Bear Blog:极简主义博客平台
Bear Blog是一个基于Django构建的博客平台,专注于提供免费、无废话、超级快速的博客体验。平台遵循极简主义设计理念,不包含JavaScript、样式表或追踪器,仅专注于内容本身。
功能特性
:bullseye: 核心特点
- 极简设计:无JavaScript、无CSS框架、无追踪器,专注内容
- Markdown支持:所有文章内容使用Markdown格式编写,提供丰富的格式化选项
- 自定义域名:支持绑定自定义域名,享受个性化博客地址
- RSS订阅:内置完整的RSS/Atom订阅功能
- 数据分析:提供基本的访问统计和分析功能
- 媒体管理:集成媒体中心,支持图片、视频、音频等文件上传
📊 平台功能
- 多博客管理:单个用户可以创建和管理多个博客
- 用户权限系统:完整的用户认证和权限管理系统
- 主题定制:支持CSS自定义,可调整博客外观
- SEO优化:自动生成元标签和结构化数据
- 电子邮件订阅:内置邮件订阅系统
- Cloudflare集成:内置CDN缓存优化
🚀 性能优势
- 轻量级架构:极简的技术栈确保快速加载
- Heroku部署:云原生部署,自动扩展
- Redis缓存:使用Redis进行性能优化
- 数据库优化:精心设计的数据库索引和查询优化
安装指南
系统要求
- Python 3.9+
- PostgreSQL数据库
- Redis(可选,用于缓存)
- 虚拟环境支持
环境配置
- 克隆项目
git clone <repository-url>
cd bear-blog
- 创建虚拟环境
python -m venv venv
source venv/bin/activate # Linux/Mac
# 或 venv\Scripts\activate # Windows
- 安装依赖
pip install -r requirements.txt
- 环境变量设置
创建.env文件并配置必要变量:
SECRET_KEY=your_secret_key_here
DEBUG=True/False
DATABASE_URL=postgres://user:password@localhost/dbname
REDISCLOUD_URL=redis://localhost:6379
数据库设置
python manage.py migrate
python manage.py createsuperuser
运行开发服务器
python manage.py runserver
使用说明
创建新博客
-
注册账户
访问注册页面,填写博客标题、子域名和初始内容。 -
配置博客
# 示例:通过API创建博客(概念示例)
blog = Blog.objects.create(title="我的博客",subdomain="myblog",content="Hello World!",user=request.user
)
- 自定义设置
- 导航菜单:通过Markdown格式添加导航链接
- 样式定制:通过CSS自定义博客外观
- 高级设置:配置分析、RSS、robots.txt等
编写文章
使用Markdown语法编写文章:
# 文章标题这是文章内容,支持**粗体**、*斜体*和[链接](http://example.com)。## 二级标题
- 列表项1
- 列表项2`代码片段`和数学公式支持。
API使用
文章管理
# 创建新文章
post = Post.objects.create(blog=blog,title="新文章标题",content="文章内容...",publish=True,published_date=timezone.now()
)
访问统计
# 记录访问数据
hit = Hit.objects.create(post=post,hash_id=hashed_user_id,browser=user_agent,country=geo_data,device=device_type
)
核心代码
1. 博客模型定义
# blogs/models.py - Blog模型核心部分
class Blog(models.Model):"""博客核心数据模型,存储博客的基本信息和配置"""user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, related_name='blogs')title = models.CharField(max_length=200) # 博客标题subdomain = models.SlugField(max_length=100, unique=True, db_index=True) # 子域名domain = models.CharField(max_length=128, blank=True, null=True, db_index=True) # 自定义域名content = models.TextField(blank=True, default='Hello World!') # 博客主页内容meta_description = models.CharField(blank=True, max_length=200) # SEO描述reviewed = models.BooleanField(db_index=True, default=False) # 审核状态hidden = models.BooleanField(db_index=True, default=False) # 隐藏状态# 样式和配置custom_styles = models.TextField(blank=True) # 自定义CSSnav = models.TextField(blank=True, default='[Home](/) [Blog](/blog/)') # 导航菜单# 高级设置robots_txt = models.TextField(blank=True, default='User-agent: *\nAllow: /')date_format = models.CharField(blank=True, default='d M, Y', max_length=32)analytics_active = models.BooleanField(default=True) # 分析功能开关class Meta:indexes = [models.Index(fields=['subdomain']),models.Index(fields=['domain']),models.Index(fields=['reviewed']),models.Index(fields=['hidden']),]def __str__(self):return f'{self.title} ({self.subdomain})'
2. 文章模型
# blogs/models.py - Post模型核心部分
class Post(models.Model):"""文章数据模型,存储博客文章的所有信息"""blog = models.ForeignKey(Blog, on_delete=models.CASCADE, related_name='posts')title = models.CharField(max_length=200, db_index=True) # 文章标题slug = models.CharField(max_length=200, db_index=True) # URL标识content = models.TextField() # 文章内容(Markdown格式)# 发布状态publish = models.BooleanField(db_index=True, default=True) # 是否发布published_date = models.DateTimeField(blank=True, db_index=True) # 发布日期first_published_at = models.DateTimeField(blank=True, db_index=True, null=True) # 首次发布日期# SEO和元数据meta_description = models.CharField(max_length=200, blank=True)meta_image = models.CharField(max_length=200, blank=True)lang = models.CharField(blank=True, db_index=True, max_length=10) # 语言设置# 文章设置is_page = models.BooleanField(db_index=True, default=False) # 是否独立页面hidden = models.BooleanField(db_index=True, default=False) # 是否隐藏make_discoverable = models.BooleanField(db_index=True, default=True) # 是否可被发现# 互动数据upvotes = models.IntegerField(db_index=True, default=0) # 点赞数shadow_votes = models.IntegerField(db_index=True, default=0) # 影子投票数class Meta:ordering = ['-published_date']indexes = [models.Index(fields=['blog', 'publish', 'published_date']),models.Index(fields=['publish', 'published_date']),models.Index(fields=['slug']),]def __str__(self):return self.title
3. 访问统计中间件
# blogs/middleware.py - 请求性能监控中间件
class RequestPerformanceMiddleware:"""请求性能监控中间件,记录API响应时间和数据库查询性能"""def __init__(self, get_response):self.get_response = get_responseself.skip_methods = {'HEAD', 'OPTIONS'}self.max_metrics = 50def track_db_time(self):"""跟踪数据库查询时间的上下文管理器"""_local.db_time = 0.0def execute_wrapper(execute, sql, params, many, context):start = time.time()try:return execute(sql, params, many, context)finally:_local.db_time += time.time() - startwith connection.execute_wrapper(execute_wrapper):yielddef __call__(self, request):"""处理每个请求的性能监控"""# 跳过不需要监控的请求方法if request.method in self.skip_methods:return self.get_response(request)# 开始性能跟踪start_time = time.time()# 跟踪数据库查询时间with self.track_db_time():response = self.get_response(request)# 计算性能指标total_time = time.time() - start_timedb_time = getattr(_local, 'db_time', 0.0)# 记录性能数据到Redisif redis_client:pattern_name = self.get_pattern_name(request)if pattern_name:metric_key = f"perf:{pattern_name}"metric_data = {'total_time': total_time,'db_time': db_time,'timestamp': time.time()}redis_client.lpush(metric_key, json.dumps(metric_data))redis_client.ltrim(metric_key, 0, self.max_metrics - 1)return response
4. Markdown处理引擎
# blogs/templatetags/custom_tags.py - Markdown渲染器
def markdown(text, blog=None, post=None):"""自定义Markdown渲染函数,支持扩展语法和安全性过滤"""# 创建自定义渲染器class BearRenderer(HTMLRenderer):def __init__(self, **kwargs):super().__init__(**kwargs)def block_code(self, code, lang=None):"""处理代码块,支持语法高亮"""if not lang:return f'<pre><code>{escape(code)}</code></pre>'try:lexer = get_lexer_by_name(lang, stripall=True)formatter = HtmlFormatter()return highlight(code, lexer, formatter)except:return f'<pre><code>{escape(code)}</code></pre>'def link(self, link, text=None, title=None):"""安全地处理链接,防止XSS攻击"""if text is None:text = link# 验证链接安全性if not is_safe_url(link):return escape(text)# 构建安全的链接标签result = f'<a href="{escape(link)}"'if title:result += f' title="{escape(title)}"'result += f'>{escape(text)}</a>'return result# 创建Markdown处理器md = create_markdown(renderer=BearRenderer(escape=False),plugins=['strikethrough', 'footnotes', 'table', 'url'])# 应用排版替换text = typographic_replacements(text)# 处理内嵌LaTeX数学公式text = replace_inline_latex(text)# 渲染Markdownreturn md(text)
5. 订阅管理视图
# blogs/views/emailer.py - 邮件订阅管理
@csrf_exempt
def email_subscribe(request):"""处理邮件订阅请求,包含防垃圾邮件机制"""if is_dodgy(request):return HttpResponse("检测到可疑请求,请稍后重试。")# 解析请求地址对应的博客blog = resolve_address(request)if not blog:return not_found(request)if request.method == "POST":email = request.POST.get("email")# 验证邮箱格式match = re.match(r'^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,4})$', email)if not match:return HttpResponse("邮箱地址格式不正确。")# 防止频繁订阅攻击recent_subscriptions = Subscriber.objects.filter(blog=blog, subscribed_date__gt=timezone.now()-timezone.timedelta(minutes=2)).count()if recent_subscriptions > 10:return HttpResponse("订阅过于频繁,请稍后再试。")# 创建或获取订阅者subscriber, created = Subscriber.objects.get_or_create(blog=blog, email_address=email)if created:return HttpResponse("订阅成功!")else:return HttpResponse("您已经订阅过了。")return HttpResponse("请求错误。")def is_dodgy(request):"""检测可疑订阅请求(反垃圾邮件机制)"""# 检查隐藏字段(蜜罐)if request.POST.get("name"):return True# 验证确认码if request.POST.get("confirm") != "829389c2a9f0402b8a3600e52f2ad4e1":return True# 检查邮件域名黑名单spam_domains = ['@cleardex.io', '@example.com']email = request.POST.get('email', '')if any(email.endswith(domain) for domain in spam_domains):return Truereturn False
6. RSS订阅生成器
# blogs/views/feed.py - RSS/Atom订阅生成
def generate_feed(blog, feed_type="atom", tag=None, page=0):"""为博客生成RSS或Atom订阅源"""# 获取符合条件的文章all_posts = blog.posts.filter(publish=True, is_page=False, published_date__lte=timezone.now()).order_by('-published_date')# 按标签过滤if tag:all_posts = all_posts.filter(all_tags__icontains=tag)all_posts = [post for post in all_posts if tag in post.tags]# 分页处理first_post = page * 10last_post = page * 10 + 10all_posts = all_posts[first_post:last_post]all_posts = list(all_posts)[::-1] # 反转顺序# 创建Feed生成器fg = FeedGenerator()fg.id(blog.useful_domain)fg.author({'name': blog.subdomain, 'email': 'hidden'})fg.title(blog.title)fg.subtitle(blog.meta_description or unmark(blog.content)[:157] + '...' or blog.title)fg.link(href=f"{blog.useful_domain}/", rel='alternate')# 添加文章条目for post in all_posts:fe = fg.add_entry()fe.id(f"{blog.useful_domain}/{post.slug}/")fe.title(clean_string(post.title))fe.author({'name': blog.subdomain, 'email': 'hidden'})fe.link(href=f"{blog.useful_domain}/{post.slug}/")if post.meta_description:fe.summary(clean_string(post.meta_description))# 处理文章内容post_content = post.content.replace('{{ email-signup }}', '')fe.content(clean_string(markdown(post_content, blog, post)), type="html")fe.published(post.published_date)fe.updated(post.last_modified)# 添加标签分类for tag in post.tags:fe.category(term=tag)# 生成对应格式的订阅源if feed_type == "atom":fg.link(href=f"{blog.useful_domain}/feed/", rel='self')return fg.atom_str(pretty=True)elif feed_type == "rss":fg.link(href=f"{blog.useful_domain}/feed/?type=rss", rel='self', type='application/rss+xml')fg.link(href=f"{blog.useful_domain}", rel='self')return fg.rss_str(pretty=True)def clean_string(s):"""清理字符串中的无效XML字符"""s = re.sub(r'[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F]', '', s)s = re.sub(r'[\uFFFE\uFFFF\uFDD0-\uFDEF]', '', s)return s
这些核心代码展示了Bear Blog平台的关键功能实现,包括数据模型设计、性能优化、内容渲染和订阅管理等重要组件。平台采用Django框架构建,注重代码质量和性能表现,为博客作者提供稳定可靠的服务基础。
更多精彩内容 请关注我的个人公众号 公众号(办公AI智能小助手)
对网络安全、黑客技术感兴趣的朋友可以关注我的安全公众号(网络安全技术点滴分享)
公众号二维码

公众号二维码
