插件能力&&升级依赖

This commit is contained in:
liangliangyy
2025-07-24 22:53:02 +08:00
parent c97e6d199d
commit b70d74f264
17 changed files with 938 additions and 265 deletions

209
README.md
View File

@@ -1,137 +1,158 @@
# DjangoBlog
🌍
*[English](/docs/README-en.md) ∙ [简体中文](README.md)*
<p align="center">
<a href="https://github.com/liangliangyy/DjangoBlog/actions/workflows/django.yml"><img src="https://github.com/liangliangyy/DjangoBlog/actions/workflows/django.yml/badge.svg" alt="Django CI"></a>
<a href="https://github.com/liangliangyy/DjangoBlog/actions/workflows/codeql-analysis.yml"><img src="https://github.com/liangliangyy/DjangoBlog/actions/workflows/codeql-analysis.yml/badge.svg" alt="CodeQL"></a>
<a href="https://codecov.io/gh/liangliangyy/DjangoBlog"><img src="https://codecov.io/gh/liangliangyy/DjangoBlog/branch/master/graph/badge.svg" alt="codecov"></a>
<a href="https://github.com/liangliangyy/DjangoBlog/blob/master/LICENSE"><img src="https://img.shields.io/github/license/liangliangyy/djangoblog.svg" alt="license"></a>
</p>
基于`python3.10``Django4.0`的博客。
<p align="center">
<b>一款功能强大、设计优雅的现代化博客系统</b>
<br>
<a href="/docs/README-en.md">English</a> • <b>简体中文</b>
</p>
[![Django CI](https://github.com/liangliangyy/DjangoBlog/actions/workflows/django.yml/badge.svg)](https://github.com/liangliangyy/DjangoBlog/actions/workflows/django.yml) [![CodeQL](https://github.com/liangliangyy/DjangoBlog/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/liangliangyy/DjangoBlog/actions/workflows/codeql-analysis.yml) [![codecov](https://codecov.io/gh/liangliangyy/DjangoBlog/branch/master/graph/badge.svg)](https://codecov.io/gh/liangliangyy/DjangoBlog) [![license](https://img.shields.io/github/license/liangliangyy/djangoblog.svg)]()
---
## 主要功能:
- 文章,页面,分类目录,标签的添加,删除,编辑等。文章、评论及页面支持`Markdown`,支持代码高亮。
- 支持文章全文搜索。
- 完整的评论功能,包括发表回复评论,以及评论的邮件提醒,支持`Markdown`
- 侧边栏功能,最新文章,最多阅读,标签云等。
- 支持Oauth登陆现已有Google,GitHub,facebook,微博,QQ登录。
- 支持`Redis`缓存,支持缓存自动刷新。
- 简单的SEO功能新建文章等会自动通知Google和百度。
- 集成了简单的图床功能。
- 集成`django-compressor`,自动压缩`css``js`
- 网站异常邮件提醒,若有未捕捉到的异常会自动发送提醒邮件。
- 集成了微信公众号功能现在可以使用微信公众号来管理你的vps了。
DjangoBlog 是一款基于 Python 3.10 和 Django 4.0 构建的高性能博客平台。它不仅提供了传统博客的所有核心功能还通过一个灵活的插件系统让您可以轻松扩展和定制您的网站。无论您是个人博主、技术爱好者还是内容创作者DjangoBlog 都旨在为您提供一个稳定、高效且易于维护的写作和发布环境。
## ✨ 特性亮点
## 安装
mysql客户端从`pymysql`修改成了`mysqlclient`,具体请参考 [pypi](https://pypi.org/project/mysqlclient/) 查看安装前的准备。
- **强大的内容管理**: 支持文章、独立页面、分类和标签的完整管理。内置强大的 Markdown 编辑器,支持代码语法高亮。
- **全文搜索**: 集成搜索引擎,提供快速、精准的文章内容搜索
- **互动评论系统**: 支持回复、邮件提醒等功能,评论内容同样支持 Markdown。
- **灵活的侧边栏**: 可自定义展示最新文章、最多阅读、标签云等模块。
- **社交化登录**: 内置 OAuth 支持,已集成 Google, GitHub, Facebook, 微博, QQ 等主流平台。
- **高性能缓存**: 原生支持 Redis 缓存,并提供自动刷新机制,确保网站高速响应。
- **SEO 友好**: 具备基础 SEO 功能,新内容发布后可自动通知 Google 和百度。
- **便捷的插件系统**: 通过创建独立的插件来扩展博客功能代码解耦易于维护。我们已经通过插件实现了文章浏览计数、SEO 优化等功能!
- **集成图床**: 内置简单的图床功能,方便图片上传和管理。
- **自动化前端**: 集成 `django-compressor`,自动压缩和优化 CSS 及 JavaScript 文件。
- **健壮的运维**: 内置网站异常邮件提醒和微信公众号管理功能。
使用pip安装 `pip install -Ur requirements.txt`
## 🛠️ 技术栈
如果你没有pip使用如下方式安装
- OS X / Linux 电脑,终端下执行:
- **后端**: Python 3.10, Django 4.0
- **数据库**: MySQL, SQLite (可配置)
- **缓存**: Redis
- **前端**: HTML5, CSS3, JavaScript
- **搜索**: Whoosh, Elasticsearch (可配置)
- **编辑器**: Markdown (mdeditor)
```
curl http://peak.telecommunity.com/dist/ez_setup.py | python
curl https://bootstrap.pypa.io/get-pip.py | python
```
## 🚀 快速开始
- Windows电脑
### 1. 环境准备
下载 http://peak.telecommunity.com/dist/ez_setup.py 和 https://raw.github.com/pypa/pip/master/contrib/get-pip.py 这两个文件,双击运行。
确保您的系统中已安装 Python 3.10+ 和 MySQL/MariaDB。
### 2. 克隆与安装
## 运行
```bash
# 克隆项目到本地
git clone https://github.com/liangliangyy/DjangoBlog.git
cd DjangoBlog
修改`djangoblog/setting.py` 修改数据库配置,如下所示:
```python
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'djangoblog',
'USER': 'root',
'PASSWORD': 'password',
'HOST': 'host',
'PORT': 3306,
}
}
# 安装依赖
pip install -r requirements.txt
```
### 创建数据库
mysql数据库中执行:
```sql
CREATE DATABASE `djangoblog` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci */;
```
### 3. 项目配置
- **数据库**:
打开 `djangoblog/settings.py` 文件,找到 `DATABASES` 配置项,修改为您的 MySQL 连接信息。
```python
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'djangoblog',
'USER': 'root',
'PASSWORD': 'your_password',
'HOST': '127.0.0.1',
'PORT': 3306,
}
}
```
在 MySQL 中创建数据库:
```sql
CREATE DATABASE `djangoblog` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
```
- **更多配置**:
关于邮件发送、OAuth 登录、缓存等更多高级配置,请参阅我们的 [详细配置文档](/docs/config.md)。
### 4. 初始化数据库
然后终端下执行:
```bash
python manage.py makemigrations
python manage.py migrate
```
### 创建超级用
终端下执行:
```bash
# 创建一个超级管理员账
python manage.py createsuperuser
```
### 创建测试数据
终端下执行:
```bash
python manage.py create_testdata
```
### 5. 运行项目
### 收集静态文件
终端下执行:  
```bash
# (可选) 生成一些测试数据
python manage.py create_testdata
# (可选) 收集和压缩静态文件
python manage.py collectstatic --noinput
python manage.py compress --force
# 启动开发服务器
python manage.py runserver
```
### 开始运行:
执行: `python manage.py runserver`
现在,在您的浏览器中访问 `http://127.0.0.1:8000/`,您应该能看到 DjangoBlog 的首页了!
## 部署
浏览器打开: http://127.0.0.1:8000/ 就可以看到效果了。
- **传统部署**: 我们为您准备了非常详细的 [服务器部署教程](https://www.lylinux.net/article/2019/8/5/58.html)。
- **Docker 部署**: 项目已全面支持 Docker。如果您熟悉容器化技术请参考 [Docker 部署文档](/docs/docker.md) 来快速启动。
- **Kubernetes 部署**: 我们也提供了完整的 [Kubernetes 部署指南](/docs/k8s.md),助您轻松上云。
## 服务器部署
## 🧩 插件系统
本地安装部署请参考 [DjangoBlog部署教程](https://www.lylinux.net/article/2019/8/5/58.html)
有详细的部署介绍.
插件系统是 DjangoBlog 的核心特色之一。它允许您在不修改核心代码的情况下,通过编写独立的插件来为您的博客添加新功能。
本项目已经支持使用docker来部署如果你有docker环境那么可以使用docker来部署具体请参考:[docker部署](/docs/docker.md)
- **工作原理**: 插件通过在预定义的“钩子”上注册回调函数来工作。例如,当一篇文章被渲染时,`after_article_body_get` 钩子会被触发,所有注册到此钩子的函数都会被执行。
- **现有插件**: `view_count`(浏览计数), `seo_optimizer`SEO优化等都是通过插件系统实现的。
- **开发您自己的插件**: 只需在 `plugins` 目录下创建一个新的文件夹,并编写您的 `plugin.py`。欢迎探索并为 DjangoBlog 社区贡献您的创意!
## 🤝 贡献指南
我们热烈欢迎任何形式的贡献!如果您有好的想法或发现了 Bug请随时提交 Issue 或 Pull Request。
## 更多配置:
[更多配置介绍](/docs/config.md)
[集成elasticsearch](/docs/es.md)
## 📄 许可证
## 问题相关
有任何问题欢迎提Issue,或者将问题描述发送至我邮箱 `liangliangyy#gmail.com`.我会尽快解答.推荐提交Issue方式.
---
## 致大家🙋‍♀️🙋‍♂️
如果本项目帮助到了你,请在[这里](https://github.com/liangliangyy/DjangoBlog/issues/214)留下你的网址,让更多的人看到。
您的回复将会是我继续更新维护下去的动力。
## 捐赠
如果您觉得本项目对您有所帮助,欢迎您请我喝杯咖啡,您的支持是我最大的动力,您可以扫描下方二维码为我付款,谢谢。
### 支付宝:
<div>
<img src="/docs/imgs/alipay.jpg" width="150" height="150" />
</div>
### 微信:
<div>
<img src="/docs/imgs/wechat.jpg" width="150" height="150" />
</div>
本项目基于 [MIT License](LICENSE) 开源。
---
感谢jetbrains
<div>
<a href="https://www.jetbrains.com/?from=DjangoBlog"><img src="/docs/imgs/pycharm_logo.png" width="150" height="150"></a>
</div>
## ❤️ 支持与赞助
如果您觉得这个项目对您有帮助,并且希望支持我继续维护和开发新功能,欢迎请我喝杯咖啡!您的每一份支持都是我前进的最大动力。
<p align="center">
<img src="/docs/imgs/alipay.jpg" width="150" alt="支付宝赞助">
<img src="/docs/imgs/wechat.jpg" width="150" alt="微信赞助">
</p>
<p align="center">
<i>(左) 支付宝 / (右) 微信</i>
</p>
## 🙏 鸣谢
特别感谢 **JetBrains** 为本项目提供的免费开源许可证。
<p align="center">
<a href="https://www.jetbrains.com/?from=DjangoBlog">
<img src="/docs/imgs/pycharm_logo.png" width="150" alt="JetBrains Logo">
</a>
</p>
---
> 如果本项目帮助到了你,请在[这里](https://github.com/liangliangyy/DjangoBlog/issues/214)留下你的网址,让更多的人看到。您的回复将会是我继续更新维护下去的动力。

View File

@@ -1,4 +1,5 @@
import logging
import re
from abc import abstractmethod
from django.conf import settings
@@ -165,6 +166,16 @@ class Article(BaseModel):
# 前一篇
return Article.objects.filter(id__lt=self.id, status='p').first()
def get_first_image_url(self):
"""
Get the first image url from article.body.
:return:
"""
match = re.search(r'!\[.*?\]\((.+?)\)', self.body)
if match:
return match.group(1)
return ""
class Category(BaseModel):
"""文章分类"""

View File

@@ -18,12 +18,18 @@ from djangoblog.utils import CommonMarkdown, sanitize_html
from djangoblog.utils import cache
from djangoblog.utils import get_current_site
from oauth.models import OAuthUser
from djangoblog.plugin_manage import hooks
logger = logging.getLogger(__name__)
register = template.Library()
@register.simple_tag(takes_context=True)
def head_meta(context):
return mark_safe(hooks.apply_filters('head_meta', '', context))
@register.simple_tag
def timeformat(data):
try:

View File

@@ -0,0 +1 @@
default_app_config = 'djangoblog.apps.DjangoblogAppConfig'

11
djangoblog/apps.py Normal file
View File

@@ -0,0 +1,11 @@
from django.apps import AppConfig
class DjangoblogAppConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'djangoblog'
def ready(self):
super().ready()
# Import and load plugins here
from .plugin_manage.loader import load_plugins
load_plugins()

View File

@@ -0,0 +1,19 @@
import os
import logging
from django.conf import settings
logger = logging.getLogger(__name__)
def load_plugins():
"""
Dynamically loads and initializes plugins from the 'plugins' directory.
This function is intended to be called when the Django app registry is ready.
"""
for plugin_name in settings.ACTIVE_PLUGINS:
plugin_path = os.path.join(settings.PLUGINS_DIR, plugin_name)
if os.path.isdir(plugin_path) and os.path.exists(os.path.join(plugin_path, 'plugin.py')):
try:
__import__(f'plugins.{plugin_name}.plugin')
logger.info(f"Successfully loaded plugin: {plugin_name}")
except ImportError as e:
logger.error(f"Failed to import plugin: {plugin_name}", exc_info=e)

View File

@@ -60,7 +60,8 @@ INSTALLED_APPS = [
'oauth',
'servermanager',
'owntracks',
'compressor'
'compressor',
'djangoblog'
]
MIDDLEWARE = [
@@ -337,14 +338,6 @@ ACTIVE_PLUGINS = [
'article_copyright',
'reading_time',
'external_links',
'view_count'
]
# 加载插件
for plugin_dir in os.listdir(PLUGINS_DIR):
plugin_path = os.path.join(PLUGINS_DIR, plugin_dir)
if os.path.isdir(plugin_path) and os.path.exists(os.path.join(plugin_path, '__init__.py')):
try:
__import__(f'plugins.{plugin_dir}.plugin')
except ImportError as e:
print("Failed to import plugin:", plugin_dir, e)
'view_count',
'seo_optimizer'
]

View File

@@ -1,122 +1,158 @@
# DjangoBlog
🌍
*[English](README-en.md) ∙ [简体中文](README.md)*
<p align="center">
<a href="https://github.com/liangliangyy/DjangoBlog/actions/workflows/django.yml"><img src="https://github.com/liangliangyy/DjangoBlog/actions/workflows/django.yml/badge.svg" alt="Django CI"></a>
<a href="https://github.com/liangliangyy/DjangoBlog/actions/workflows/codeql-analysis.yml"><img src="https://github.com/liangliangyy/DjangoBlog/actions/workflows/codeql-analysis.yml/badge.svg" alt="CodeQL"></a>
<a href="https://codecov.io/gh/liangliangyy/DjangoBlog"><img src="https://codecov.io/gh/liangliangyy/DjangoBlog/branch/master/graph/badge.svg" alt="codecov"></a>
<a href="https://github.com/liangliangyy/DjangoBlog/blob/master/LICENSE"><img src="https://img.shields.io/github/license/liangliangyy/djangoblog.svg" alt="license"></a>
</p>
A blog system based on `python3.8` and `Django4.0`.
<p align="center">
<b>A powerful, elegant, and modern blog system.</b>
<br>
<b>English</b> • <a href="/README.md">简体中文</a>
</p>
---
[![Django CI](https://github.com/liangliangyy/DjangoBlog/actions/workflows/django.yml/badge.svg)](https://github.com/liangliangyy/DjangoBlog/actions/workflows/django.yml) [![CodeQL](https://github.com/liangliangyy/DjangoBlog/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/liangliangyy/DjangoBlog/actions/workflows/codeql-analysis.yml) [![codecov](https://codecov.io/gh/liangliangyy/DjangoBlog/branch/master/graph/badge.svg)](https://codecov.io/gh/liangliangyy/DjangoBlog) [![license](https://img.shields.io/github/license/liangliangyy/djangoblog.svg)]()
DjangoBlog is a high-performance blog platform built with Python 3.10 and Django 4.0. It not only provides all the core functionalities of a traditional blog but also features a flexible plugin system, allowing you to easily extend and customize your website. Whether you are a personal blogger, a tech enthusiast, or a content creator, DjangoBlog aims to provide a stable, efficient, and easy-to-maintain environment for writing and publishing.
## ✨ Features
## Main Features:
- Articles, Pages, Categories, Tags(Add, Delete, Edit), edc. Articles and pages support `Markdown` and highlighting.
- Articles support full-text search.
- Complete comment feature, include posting reply comment and email notification. `Markdown` supporting.
- Sidebar feature: new articles, most readings, tags, etc.
- OAuth Login supported, including Google, GitHub, Facebook, Weibo, QQ.
- `Memcache` supported, with cache auto refresh.
- Simple SEO Features, notify Google and Baidu when there was a new article or other things.
- Simple picture bed feature integrated.
- `django-compressor` integrated, auto-compressed `css`, `js`.
- Website exception email notification. When there is an unhandle exception, system will send an email notification.
- Wechat official account feature integrated. Now, you can use wechat official account to manage your VPS.
- **Powerful Content Management**: Full support for managing articles, standalone pages, categories, and tags. Comes with a powerful built-in Markdown editor with syntax highlighting.
- **Full-Text Search**: Integrated search engine for fast and accurate content searching.
- **Interactive Comment System**: Supports replies, email notifications, and Markdown formatting in comments.
- **Flexible Sidebar**: Customizable modules for displaying recent articles, most viewed posts, tag cloud, and more.
- **Social Login**: Built-in OAuth support, with integrations for Google, GitHub, Facebook, Weibo, QQ, and other major platforms.
- **High-Performance Caching**: Native support for Redis caching with an automatic refresh mechanism to ensure high-speed website responses.
- **SEO Friendly**: Basic SEO features are included, with automatic notifications to Google and Baidu upon new content publication.
- **Extensible Plugin System**: Extend blog functionalities by creating standalone plugins, ensuring decoupled and maintainable code. We have already implemented features like view counting and SEO optimization through plugins!
- **Integrated Image Hosting**: A simple, built-in image hosting feature for easy uploads and management.
- **Automated Frontend**: Integrated with `django-compressor` to automatically compress and optimize CSS and JavaScript files.
- **Robust Operations**: Built-in email notifications for website exceptions and management capabilities through a WeChat Official Account.
## Installation:
Change MySQL client from `pymysql` to `mysqlclient`, more details please reference [pypi](https://pypi.org/project/mysqlclient/) , checkout preperation before installation.
## 🛠️ Tech Stack
Install via pip: `pip install -Ur requirements.txt`
- **Backend**: Python 3.10, Django 4.0
- **Database**: MySQL, SQLite (configurable)
- **Cache**: Redis
- **Frontend**: HTML5, CSS3, JavaScript
- **Search**: Whoosh, Elasticsearch (configurable)
- **Editor**: Markdown (mdeditor)
If you do NOT have `pip`, please use the following methods to install:
- OS X / Linux, run the following commands:
## 🚀 Getting Started
```
curl http://peak.telecommunity.com/dist/ez_setup.py | python
curl https://raw.github.com/pypa/pip/master/contrib/get-pip.py | python
```
### 1. Prerequisites
- Windows
Ensure you have Python 3.10+ and MySQL/MariaDB installed on your system.
Download http://peak.telecommunity.com/dist/ez_setup.py and https://raw.github.com/pypa/pip/master/contrib/get-pip.py, and run with python.
### 2. Clone & Installation
### Configuration
Most configurations are in `setting.py`, others are in backend configurations.
```bash
# Clone the project to your local machine
git clone https://github.com/liangliangyy/DjangoBlog.git
cd DjangoBlog
I set many `setting` configuration with my environment variables (such as: `SECRET_KEY`, `OAUTH`, `mysql` and some email configuration parts.) and they did NOT been submitted to the `GitHub`. You can change these in the code with your own configuration or just add them into your environment variables.
Files in `test` directory are for `travis` with automatic testing. You do not need to care about this. Or just use it, in this way to integrate `travis` for automatic testing.
In `bin` directory, we have scripts to deploy with `Nginx`+`Gunicorn`+`virtualenv`+`supervisor` on `linux` and `Nginx` configuration file. You can reference with my article
>[DjangoBlog部署教程](https://www.lylinux.net/article/2019/8/5/58.html)
More deploy detail in this article.
## Run
Modify `DjangoBlog/setting.py` with database settings, as following:
```python
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'djangoblog',
'USER': 'root',
'PASSWORD': 'password',
'HOST': 'host',
'PORT': 3306,
}
}
# Install dependencies
pip install -r requirements.txt
```
### Create database
Run the following command in MySQL shell:
```sql
CREATE DATABASE `djangoblog` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci */;
```
### 3. Project Configuration
- **Database**:
Open `djangoblog/settings.py`, locate the `DATABASES` section, and update it with your MySQL connection details.
```python
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'djangoblog',
'USER': 'root',
'PASSWORD': 'your_password',
'HOST': '127.0.0.1',
'PORT': 3306,
}
}
```
Create the database in MySQL:
```sql
CREATE DATABASE `djangoblog` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
```
- **More Configurations**:
For advanced settings such as email, OAuth, caching, and more, please refer to our [Detailed Configuration Guide](/docs/config-en.md).
### 4. Database Initialization
Run the following commands in Terminal:
```bash
python manage.py makemigrations
python manage.py migrate
```
### Create super user
Run command in terminal:
```bash
# Create a superuser account
python manage.py createsuperuser
```
### Create testing data
Run command in terminal:
```bash
python manage.py create_testdata
```
### 5. Running the Project
### Collect static files
Run command in terminal:
```bash
# (Optional) Generate some test data
python manage.py create_testdata
# (Optional) Collect and compress static files
python manage.py collectstatic --noinput
python manage.py compress --force
# Start the development server
python manage.py runserver
```
### Getting start to run server
Execute: `python manage.py runserver`
Now, open your browser and navigate to `http://127.0.0.1:8000/`. You should see the DjangoBlog homepage!
Open up a browser and visit: http://127.0.0.1:8000/ , the you will see the blog.
## Deployment
## More configurations
[More configurations details](/docs/config-en.md)
- **Traditional Deployment**: A detailed guide for server deployment is available here: [Deployment Tutorial](https://www.lylinux.net/article/2019/8/5/58.html) (in Chinese).
- **Docker Deployment**: This project fully supports Docker. If you are familiar with containerization, please refer to the [Docker Deployment Guide](/docs/docker-en.md) for a quick start.
- **Kubernetes Deployment**: We also provide a complete [Kubernetes Deployment Guide](/docs/k8s-en.md) to help you go cloud-native easily.
## About the issues
## 🧩 Plugin System
If you have any *question*, please use Issue or send problem descriptions to my email `liangliangyy#gmail.com`. I will reponse you as soon as possible. And, we recommend you to use Issue.
The plugin system is a core feature of DjangoBlog. It allows you to add new functionalities to your blog without modifying the core codebase by writing standalone plugins.
- **How it Works**: Plugins operate by registering callback functions to predefined "hooks". For instance, when an article is rendered, the `after_article_body_get` hook is triggered, and all functions registered to this hook are executed.
- **Existing Plugins**: Features like `view_count` and `seo_optimizer` are implemented through this plugin system.
- **Develop Your Own Plugin**: Simply create a new folder under the `plugins` directory and write your `plugin.py`. We welcome you to explore and contribute your creative ideas to the DjangoBlog community!
## 🤝 Contributing
We warmly welcome contributions of any kind! If you have great ideas or have found a bug, please feel free to open an issue or submit a pull request.
## 📄 License
This project is open-sourced under the [MIT License](LICENSE).
---
## To Everyone 🙋‍♀️🙋‍♂️
If this project helps you, please submit your site address [here](https://github.com/liangliangyy/DjangoBlog/issues/214) to let more people see it.
Your reply will be the driving force for me to continue to update and maintain this project.
## ❤️ Support & Sponsorship
🙏🙏🙏
If you find this project helpful and wish to support its continued maintenance and development, please consider buying me a coffee! Your support is my greatest motivation.
<p align="center">
<img src="/docs/imgs/alipay.jpg" width="150" alt="Alipay Sponsorship">
<img src="/docs/imgs/wechat.jpg" width="150" alt="WeChat Sponsorship">
</p>
<p align="center">
<i>(Left) Alipay / (Right) WeChat</i>
</p>
## 🙏 Acknowledgements
A special thanks to **JetBrains** for providing a free open-source license for this project.
<p align="center">
<a href="https://www.jetbrains.com/?from=DjangoBlog">
<img src="/docs/imgs/pycharm_logo.png" width="150" alt="JetBrains Logo">
</a>
</p>
---
> If this project has helped you, please leave your website URL [here](https://github.com/liangliangyy/DjangoBlog/issues/214) to let more people see it. Your feedback is the driving force for my continued updates and maintenance.

114
docs/docker-en.md Normal file
View File

@@ -0,0 +1,114 @@
# Deploying DjangoBlog with Docker
![Docker Pulls](https://img.shields.io/docker/pulls/liangliangyy/djangoblog)
![Docker Image Version (latest by date)](https://img.shields.io/docker/v/liangliangyy/djangoblog?sort=date)
![Docker Image Size (latest by date)](https://img.shields.io/docker/image-size/liangliangyy/djangoblog)
This project fully supports containerized deployment using Docker, providing you with a fast, consistent, and isolated runtime environment. We recommend using `docker-compose` to launch the entire blog service stack with a single command.
## 1. Prerequisites
Before you begin, please ensure you have the following software installed on your system:
- [Docker Engine](https://docs.docker.com/engine/install/)
- [Docker Compose](https://docs.docker.com/compose/install/) (Included with Docker Desktop for Mac and Windows)
## 2. Recommended Method: Using `docker-compose` (One-Click Deployment)
This is the simplest and most recommended way to deploy. It automatically creates and manages the Django application, a MySQL database, and an optional Elasticsearch service for you.
### Step 1: Start the Basic Services
From the project's root directory, run the following command:
```bash
# Build and start the containers in detached mode (includes Django app and MySQL)
docker-compose up -d --build
```
`docker-compose` will read the `docker-compose.yml` file, pull the necessary images, build the project image, and start all services.
- **Access Your Blog**: Once the services are up, you can access the blog by navigating to `http://127.0.0.1` in your browser.
- **Data Persistence**: MySQL data files will be stored in the `data/mysql` directory within the project root, ensuring that your data persists across container restarts.
### Step 2: (Optional) Enable Elasticsearch for Full-Text Search
If you want to use Elasticsearch for more powerful full-text search capabilities, you can include the `docker-compose.es.yml` configuration file:
```bash
# Build and start all services in detached mode (Django, MySQL, Elasticsearch)
docker-compose -f docker-compose.yml -f deploy/docker-compose/docker-compose.es.yml up -d --build
```
- **Data Persistence**: Elasticsearch data will be stored in the `data/elasticsearch` directory.
### Step 3: First-Time Initialization
After the containers start for the first time, you'll need to execute some initialization commands inside the application container.
```bash
# Get a shell inside the djangoblog application container (named 'web')
docker-compose exec web bash
# Inside the container, run the following commands:
# Create a superuser account (follow the prompts to set username, email, and password)
python manage.py createsuperuser
# (Optional) Create some test data
python manage.py create_testdata
# (Optional, if ES is enabled) Create the search index
python manage.py rebuild_index
# Exit the container
exit
```
## 3. Alternative Method: Using the Standalone Docker Image
If you already have an external MySQL database running, you can run the DjangoBlog application image by itself.
```bash
# Pull the latest image from Docker Hub
docker pull liangliangyy/djangoblog:latest
# Run the container and connect it to your external database
docker run -d \
-p 8000:8000 \
-e DJANGO_SECRET_KEY='your-strong-secret-key' \
-e DJANGO_MYSQL_HOST='your-mysql-host' \
-e DJANGO_MYSQL_USER='your-mysql-user' \
-e DJANGO_MYSQL_PASSWORD='your-mysql-password' \
-e DJANGO_MYSQL_DATABASE='djangoblog' \
--name djangoblog \
liangliangyy/djangoblog:latest
```
- **Access Your Blog**: After startup, visit `http://127.0.0.1:8000`.
- **Create Superuser**: `docker exec -it djangoblog python manage.py createsuperuser`
## 4. Configuration (Environment Variables)
Most of the project's configuration is managed through environment variables. You can modify them in the `docker-compose.yml` file or pass them using the `-e` flag with the `docker run` command.
| Environment Variable | Default/Example Value | Notes |
|---------------------------|--------------------------------------------------------------------------|---------------------------------------------------------------------|
| `DJANGO_SECRET_KEY` | `your-strong-secret-key` | **Must be changed to a random, complex string!** |
| `DJANGO_DEBUG` | `False` | Toggles Django's debug mode. |
| `DJANGO_MYSQL_HOST` | `mysql` | Database hostname. |
| `DJANGO_MYSQL_PORT` | `3306` | Database port. |
| `DJANGO_MYSQL_DATABASE` | `djangoblog` | Database name. |
| `DJANGO_MYSQL_USER` | `root` | Database username. |
| `DJANGO_MYSQL_PASSWORD` | `djangoblog_123` | Database password. |
| `DJANGO_REDIS_URL` | `redis:6379/0` | Redis connection URL (for caching). |
| `DJANGO_ELASTICSEARCH_HOST`| `elasticsearch:9200` | Elasticsearch host address. |
| `DJANGO_EMAIL_HOST` | `smtp.example.org` | Email server address. |
| `DJANGO_EMAIL_PORT` | `465` | Email server port. |
| `DJANGO_EMAIL_USER` | `user@example.org` | Email account username. |
| `DJANGO_EMAIL_PASSWORD` | `your-email-password` | Email account password. |
| `DJANGO_EMAIL_USE_SSL` | `True` | Whether to use SSL. |
| `DJANGO_EMAIL_USE_TLS` | `False` | Whether to use TLS. |
| `DJANGO_ADMIN_EMAIL` | `admin@example.org` | Admin email for receiving error reports. |
| `DJANGO_BAIDU_NOTIFY_URL` | `http://data.zz.baidu.com/...` | Push API from [Baidu Webmaster Tools](https://ziyuan.baidu.com/linksubmit/index). |
---
After deployment, please review and adjust these environment variables according to your needs, especially `DJANGO_SECRET_KEY` and the database and email settings.

View File

@@ -1,59 +1,114 @@
# 使用docker部署
# 使用 Docker 部署 DjangoBlog
![Docker Pulls](https://img.shields.io/docker/pulls/liangliangyy/djangoblog)
![Docker Image Version (latest by date)](https://img.shields.io/docker/v/liangliangyy/djangoblog?sort=date)
![Docker Image Size (latest by date)](https://img.shields.io/docker/image-size/liangliangyy/djangoblog)
使用docker部署支持如下两种方式
## docker镜像方式
本项目已经支持了docker部署如果你已经有了`mysql`,那么直接使用基础镜像即可,启动命令如下所示:
```shell
本项目全面支持使用 Docker 进行容器化部署,为您提供了快速、一致且隔离的运行环境。我们推荐使用 `docker-compose` 来一键启动整个博客服务栈。
## 1. 环境准备
在开始之前,请确保您的系统中已经安装了以下软件:
- [Docker Engine](https://docs.docker.com/engine/install/)
- [Docker Compose](https://docs.docker.com/compose/install/) (对于 Docker Desktop 用户,它已内置)
## 2. 推荐方式:使用 `docker-compose` (一键部署)
这是最简单、最推荐的部署方式。它会自动为您创建并管理 Django 应用、MySQL 数据库,以及可选的 Elasticsearch 服务。
### 步骤 1: 启动基础服务
在项目根目录下,执行以下命令:
```bash
# 构建并以后台模式启动容器 (包含 Django 应用和 MySQL)
docker-compose up -d --build
```
`docker-compose` 会读取 `docker-compose.yml` 文件,自动拉取所需镜像、构建项目镜像,并启动所有服务。
- **访问您的博客**: 服务启动后,在浏览器中访问 `http://127.0.0.1` 即可看到博客首页。
- **数据持久化**: MySQL 的数据文件将存储在项目根目录下的 `data/mysql` 文件夹中,确保数据在容器重启后不丢失。
### 步骤 2: (可选) 启用 Elasticsearch 全文搜索
如果您希望使用 Elasticsearch 提供更强大的全文搜索功能,可以额外加载 `docker-compose.es.yml` 配置文件:
```bash
# 构建并以后台模式启动所有服务 (Django, MySQL, Elasticsearch)
docker-compose -f docker-compose.yml -f deploy/docker-compose/docker-compose.es.yml up -d --build
```
- **数据持久化**: Elasticsearch 的数据将存储在 `data/elasticsearch` 文件夹中。
### 步骤 3: 首次运行的初始化操作
当容器首次启动后,您需要进入容器来执行一些初始化命令。
```bash
# 进入 djangoblog 应用容器
docker-compose exec web bash
# 在容器内执行以下命令:
# 创建超级管理员账户 (请按照提示设置用户名、邮箱和密码)
python manage.py createsuperuser
# (可选) 创建一些测试数据
python manage.py create_testdata
# (可选,如果启用了 ES) 创建索引
python manage.py rebuild_index
# 退出容器
exit
```
## 3. 备选方式:使用独立的 Docker 镜像
如果您已经拥有一个正在运行的外部 MySQL 数据库,您也可以只运行 DjangoBlog 的应用镜像。
```bash
# 从 Docker Hub 拉取最新镜像
docker pull liangliangyy/djangoblog:latest
docker run -d -p 8000:8000 -e DJANGO_MYSQL_HOST=mysqlhost -e DJANGO_MYSQL_PASSWORD=mysqlrootpassword -e DJANGO_MYSQL_USER=root -e DJANGO_MYSQL_DATABASE=djangoblog --name djangoblog liangliangyy/djangoblog:latest
```
启动完成后,访问 http://127.0.0.1:8000
## 使用docker-compose
如果你没有mysql等基础服务那么可以使用`docker-compose`来运行,
具体命令如下所示:
```shell
docker-compose build
docker-compose up -d
```
本方式生成的mysql数据文件在 `bin/datas/mysql` 文件夹。
等启动完成后,访问 [http://127.0.0.1](http://127.0.0.1) 即可。
### 使用es
如果你期望使用es来作为后端的搜索引擎那么可以使用如下命令来启动
```shell
docker-compose -f docker-compose.yml -f docker-compose.es.yml build
docker-compose -f docker-compose.yml -f docker-compose.es.yml up -d
```
本方式生成的es数据文件在 `bin/datas/es` 文件夹。
## 配置说明:
本项目较多配置都基于环境变量,所有的环境变量如下所示:
| 环境变量名称 | 默认值 | 备注 |
|---------------------------|----------------------------------------------------------------------------|------------------------------------------------------------------------------------------------|
| DJANGO_DEBUG | False | |
| DJANGO_SECRET_KEY | DJANGO_BLOG_CHANGE_ME | 请务必修改,建议[随机生成](https://www.random.org/passwords/?num=5&len=24&format=html&rnd=new) |
| DJANGO_MYSQL_DATABASE | djangoblog | |
| DJANGO_MYSQL_USER | root | |
| DJANGO_MYSQL_PASSWORD | djangoblog_123 | |
| DJANGO_MYSQL_HOST | 127.0.0.1 | |
| DJANGO_MYSQL_PORT | 3306 | |
| DJANGO_MEMCACHED_ENABLE | True | |
| DJANGO_MEMCACHED_LOCATION | 127.0.0.1:11211 | |
| DJANGO_BAIDU_NOTIFY_URL | http://data.zz.baidu.com/urls?site=https://www.example.org&token=CHANGE_ME | 请在[百度站长平台](https://ziyuan.baidu.com/linksubmit/index)获取接口地址 |
| DJANGO_EMAIL_TLS | False | |
| DJANGO_EMAIL_SSL | True | |
| DJANGO_EMAIL_HOST | smtp.example.org | |
| DJANGO_EMAIL_PORT | 465 | |
| DJANGO_EMAIL_USER | SMTP_USER_CHANGE_ME | |
| DJANGO_EMAIL_PASSWORD | SMTP_PASSWORD_CHANGE_ME | |
| DJANGO_ADMIN_EMAIL | admin@example.org | |
| DJANGO_WEROBOT_TOKEN | DJANGO_BLOG_CHANGE_ME
|DJANGO_ELASTICSEARCH_HOST|
第一次启动之后,使用如下命令来创建超级用户:
```shell
docker exec -it djangoblog python /code/djangoblog/manage.py createsuperuser
# 运行容器,并链接到您的外部数据库
docker run -d \
-p 8000:8000 \
-e DJANGO_SECRET_KEY='your-strong-secret-key' \
-e DJANGO_MYSQL_HOST='your-mysql-host' \
-e DJANGO_MYSQL_USER='your-mysql-user' \
-e DJANGO_MYSQL_PASSWORD='your-mysql-password' \
-e DJANGO_MYSQL_DATABASE='djangoblog' \
--name djangoblog \
liangliangyy/djangoblog:latest
```
- **访问您的博客**: 启动完成后,访问 `http://127.0.0.1:8000`
- **创建管理员**: `docker exec -it djangoblog python manage.py createsuperuser`
## 4. 配置说明 (环境变量)
本项目的大部分配置都通过环境变量来管理。您可以在 `docker-compose.yml` 文件中修改它们,或者在使用 `docker run` 命令时通过 `-e` 参数传入。
| 环境变量名称 | 默认值/示例 | 备注 |
|-------------------------|--------------------------------------------------------------------------|---------------------------------------------------------------------|
| `DJANGO_SECRET_KEY` | `your-strong-secret-key` | **请务必修改为一个随机且复杂的字符串!** |
| `DJANGO_DEBUG` | `False` | 是否开启 Django 的调试模式 |
| `DJANGO_MYSQL_HOST` | `mysql` | 数据库主机名 |
| `DJANGO_MYSQL_PORT` | `3306` | 数据库端口 |
| `DJANGO_MYSQL_DATABASE` | `djangoblog` | 数据库名称 |
| `DJANGO_MYSQL_USER` | `root` | 数据库用户名 |
| `DJANGO_MYSQL_PASSWORD` | `djangoblog_123` | 数据库密码 |
| `DJANGO_REDIS_URL` | `redis:6379/0` | Redis 连接地址 (用于缓存) |
| `DJANGO_ELASTICSEARCH_HOST` | `elasticsearch:9200` | Elasticsearch 主机地址 |
| `DJANGO_EMAIL_HOST` | `smtp.example.org` | 邮件服务器地址 |
| `DJANGO_EMAIL_PORT` | `465` | 邮件服务器端口 |
| `DJANGO_EMAIL_USER` | `user@example.org` | 邮件账户 |
| `DJANGO_EMAIL_PASSWORD` | `your-email-password` | 邮件密码 |
| `DJANGO_EMAIL_USE_SSL` | `True` | 是否使用 SSL |
| `DJANGO_EMAIL_USE_TLS` | `False` | 是否使用 TLS |
| `DJANGO_ADMIN_EMAIL` | `admin@example.org` | 接收异常报告的管理员邮箱 |
| `DJANGO_BAIDU_NOTIFY_URL` | `http://data.zz.baidu.com/...` | [百度站长平台](https://ziyuan.baidu.com/linksubmit/index) 的推送接口 |
---
部署完成后,请务必检查并根据您的实际需求调整这些环境变量,特别是 `DJANGO_SECRET_KEY` 和数据库、邮件相关的配置。

141
docs/k8s-en.md Normal file
View File

@@ -0,0 +1,141 @@
# Deploying DjangoBlog with Kubernetes
This document guides you through deploying the DjangoBlog application on a Kubernetes (K8s) cluster. We provide a complete set of `.yaml` configuration files in the `deploy/k8s` directory to deploy a full service stack, including the DjangoBlog application, Nginx, MySQL, Redis, and Elasticsearch.
## Architecture Overview
This deployment utilizes a microservices-based, cloud-native architecture:
- **Core Components**: Each core service (DjangoBlog, Nginx, MySQL, Redis, Elasticsearch) runs as a separate `Deployment`.
- **Configuration Management**: Nginx configurations and Django application environment variables are managed via `ConfigMap`. **Note: For sensitive information like passwords, using `Secret` is highly recommended.**
- **Service Discovery**: All services are exposed internally within the cluster as `ClusterIP` type `Service`, enabling communication via service names.
- **External Access**: An `Ingress` resource is used to route external HTTP traffic to the Nginx service, which acts as the single entry point for the entire blog application.
- **Data Persistence**: A `local-storage` solution based on node-local paths is used. This requires you to manually create storage directories on a specific K8s node and statically bind them using `PersistentVolume` (PV) and `PersistentVolumeClaim` (PVC).
## 1. Prerequisites
Before you begin, please ensure you have the following:
- A running Kubernetes cluster.
- The `kubectl` command-line tool configured to connect to your cluster.
- An [Nginx Ingress Controller](https://kubernetes.github.io/ingress-nginx/deploy/) installed and configured in your cluster.
- Filesystem access to one of the nodes in your cluster (defaulted to `master` in the configs) to create local storage directories.
## 2. Deployment Steps
### Step 1: Create a Namespace
We recommend deploying all DjangoBlog-related resources in a dedicated namespace for better management.
```bash
# Create a namespace named 'djangoblog'
kubectl create namespace djangoblog
```
### Step 2: Configure Persistent Storage
This setup uses Local Persistent Volumes. You need to create the data storage directories on a node within your cluster (the default is the `master` node in `pv.yaml`).
```bash
# Log in to your master node
ssh user@master-node
# Create the required storage directories
sudo mkdir -p /mnt/local-storage-db
sudo mkdir -p /mnt/local-storage-djangoblog
sudo mkdir -p /mnt/resource/
sudo mkdir -p /mnt/local-storage-elasticsearch
# Log out from the node
exit
```
**Note**: If you wish to store data on a different node or use different paths, you must modify the `nodeAffinity` and `local.path` settings in the `deploy/k8s/pv.yaml` file.
After creating the directories, apply the storage-related configurations:
```bash
# Apply the StorageClass
kubectl apply -f deploy/k8s/storageclass.yaml
# Apply the PersistentVolumes (PVs)
kubectl apply -f deploy/k8s/pv.yaml
# Apply the PersistentVolumeClaims (PVCs)
kubectl apply -f deploy/k8s/pvc.yaml
```
### Step 3: Configure the Application
Before deploying the application, you need to edit the `deploy/k8s/configmap.yaml` file to modify sensitive information and custom settings.
**It is strongly recommended to change the following fields:**
- `DJANGO_SECRET_KEY`: Change to a random, complex string.
- `DJANGO_MYSQL_PASSWORD` and `MYSQL_ROOT_PASSWORD`: Change to your own secure database password.
```bash
# Edit the ConfigMap file
vim deploy/k8s/configmap.yaml
# Apply the configuration
kubectl apply -f deploy/k8s/configmap.yaml
```
### Step 4: Deploy the Application Stack
Now, we can deploy all the core services.
```bash
# Deploy the Deployments (DjangoBlog, MySQL, Redis, Nginx, ES)
kubectl apply -f deploy/k8s/deployment.yaml
# Deploy the Services (to create internal endpoints for the Deployments)
kubectl apply -f deploy/k8s/service.yaml
```
The deployment may take some time. You can run the following command to check if all Pods are running successfully (STATUS should be `Running`):
```bash
kubectl get pods -n djangoblog -w
```
### Step 5: Expose the Application Externally
Finally, expose the Nginx service to external traffic by applying the `Ingress` rule.
```bash
# Apply the Ingress rule
kubectl apply -f deploy/k8s/gateway.yaml
```
Once deployed, you can access your blog via the external IP address of your Ingress Controller. Use the following command to find the address:
```bash
kubectl get ingress -n djangoblog
```
### Step 6: First-Time Initialization
Similar to the Docker deployment, you need to get a shell into the DjangoBlog application Pod to perform database initialization and create a superuser on the first run.
```bash
# First, get the name of a djangoblog pod
kubectl get pods -n djangoblog | grep djangoblog
# Exec into one of the Pods (replace [pod-name] with the name from the previous step)
kubectl exec -it [pod-name] -n djangoblog -- bash
# Inside the Pod, run the following commands:
# Create a superuser account (follow the prompts)
python manage.py createsuperuser
# (Optional) Create some test data
python manage.py create_testdata
# (Optional, if ES is enabled) Create the search index
python manage.py rebuild_index
# Exit the Pod
exit
```
Congratulations! You have successfully deployed DjangoBlog on your Kubernetes cluster.

141
docs/k8s.md Normal file
View File

@@ -0,0 +1,141 @@
# 使用 Kubernetes 部署 DjangoBlog
本文档将指导您如何在 Kubernetes (K8s) 集群上部署 DjangoBlog 应用。我们提供了一套完整的 `.yaml` 配置文件,位于 `deploy/k8s` 目录下,用于部署一个包含 DjangoBlog 应用、Nginx、MySQL、Redis 和 Elasticsearch 的完整服务栈。
## 架构概览
本次部署采用的是微服务化的云原生架构:
- **核心组件**: 每个核心服务 (DjangoBlog, Nginx, MySQL, Redis, Elasticsearch) 都将作为独立的 `Deployment` 运行。
- **配置管理**: Nginx 的配置文件和 Django 应用的环境变量通过 `ConfigMap` 进行管理。**注意:敏感信息(如密码)建议使用 `Secret` 进行管理。**
- **服务发现**: 所有服务都通过 `ClusterIP` 类型的 `Service` 在集群内部暴露,并通过服务名相互通信。
- **外部访问**: 使用 `Ingress` 资源将外部的 HTTP 流量路由到 Nginx 服务,作为整个博客应用的统一入口。
- **数据持久化**: 采用基于节点本地路径的 `local-storage` 方案。这需要您在指定的 K8s 节点上手动创建存储目录,并通过 `PersistentVolume` (PV) 和 `PersistentVolumeClaim` (PVC) 进行静态绑定。
## 1. 环境准备
在开始之前,请确保您已具备以下环境:
- 一个正在运行的 Kubernetes 集群。
- `kubectl` 命令行工具已配置并能够连接到您的集群。
- 集群中已安装并配置好 [Nginx Ingress Controller](https://kubernetes.github.io/ingress-nginx/deploy/)。
- 对集群中的一个节点(默认为 `master`)拥有文件系统访问权限,用于创建本地存储目录。
## 2. 部署步骤
### 步骤 1: 创建命名空间
我们建议将 DjangoBlog 相关的所有资源都部署在一个独立的命名空间中,便于管理。
```bash
# 创建一个名为 djangoblog 的命名空间
kubectl create namespace djangoblog
```
### 步骤 2: 配置持久化存储
此方案使用本地持久卷 (Local Persistent Volume)。您需要在集群的一个节点上(在 `pv.yaml` 文件中默认为 `master` 节点)创建用于数据存储的目录。
```bash
# 登录到您的 master 节点
ssh user@master-node
# 创建所需的存储目录
sudo mkdir -p /mnt/local-storage-db
sudo mkdir -p /mnt/local-storage-djangoblog
sudo mkdir -p /mnt/resource/
sudo mkdir -p /mnt/local-storage-elasticsearch
# 退出节点
exit
```
**注意**: 如果您希望将数据存储在其他节点或使用不同的路径,请务必修改 `deploy/k8s/pv.yaml` 文件中 `nodeAffinity``local.path` 的配置。
创建目录后,应用存储相关的配置文件:
```bash
# 应用 StorageClass
kubectl apply -f deploy/k8s/storageclass.yaml
# 应用 PersistentVolume (PV)
kubectl apply -f deploy/k8s/pv.yaml
# 应用 PersistentVolumeClaim (PVC)
kubectl apply -f deploy/k8s/pvc.yaml
```
### 步骤 3: 配置应用
在部署应用之前,您需要编辑 `deploy/k8s/configmap.yaml` 文件,修改其中的敏感信息和个性化配置。
**强烈建议修改以下字段:**
- `DJANGO_SECRET_KEY`: 修改为一个随机且复杂的字符串。
- `DJANGO_MYSQL_PASSWORD``MYSQL_ROOT_PASSWORD`: 修改为您自己的数据库密码。
```bash
# 编辑 ConfigMap 文件
vim deploy/k8s/configmap.yaml
# 应用配置
kubectl apply -f deploy/k8s/configmap.yaml
```
### 步骤 4: 部署应用服务栈
现在,我们可以部署所有的核心服务了。
```bash
# 部署 Deployments (DjangoBlog, MySQL, Redis, Nginx, ES)
kubectl apply -f deploy/k8s/deployment.yaml
# 部署 Services (为 Deployments 创建内部访问端点)
kubectl apply -f deploy/k8s/service.yaml
```
部署需要一些时间,您可以运行以下命令检查所有 Pod 是否都已成功运行 (STATUS 为 `Running`)
```bash
kubectl get pods -n djangoblog -w
```
### 步骤 5: 暴露应用到外部
最后,通过应用 `Ingress` 规则来将外部流量引导至我们的 Nginx 服务。
```bash
# 应用 Ingress 规则
kubectl apply -f deploy/k8s/gateway.yaml
```
部署完成后,您可以通过 Ingress Controller 的外部 IP 地址来访问您的博客。执行以下命令获取地址:
```bash
kubectl get ingress -n djangoblog
```
### 步骤 6: 首次运行的初始化操作
与 Docker 部署类似,首次运行时,您需要进入 DjangoBlog 应用的 Pod 来执行数据库初始化和创建管理员账户。
```bash
# 首先,获取 djangoblog pod 的名称
kubectl get pods -n djangoblog | grep djangoblog
# 进入其中一个 Pod (将 [pod-name] 替换为上一步获取到的名称)
kubectl exec -it [pod-name] -n djangoblog -- bash
# 在 Pod 内部执行以下命令:
# 创建超级管理员账户 (请按照提示操作)
python manage.py createsuperuser
# (可选) 创建测试数据
python manage.py create_testdata
# (可选,如果启用了 ES) 创建索引
python manage.py rebuild_index
# 退出 Pod
exit
```
至此,您已成功在 Kubernetes 集群上完成了 DjangoBlog 的部署!

View File

@@ -0,0 +1 @@
# This file makes this a Python package

View File

@@ -0,0 +1,142 @@
import json
from django.utils.html import strip_tags
from django.template.defaultfilters import truncatewords
from djangoblog.plugin_manage.base_plugin import BasePlugin
from djangoblog.plugin_manage import hooks
from blog.models import Article, Category, Tag
from djangoblog.utils import get_blog_setting
class SeoOptimizerPlugin(BasePlugin):
PLUGIN_NAME = 'SEO 优化器'
PLUGIN_DESCRIPTION = '为文章、页面等提供 SEO 优化,动态生成 meta 标签和 JSON-LD 结构化数据。'
PLUGIN_VERSION = '0.2.0'
PLUGIN_AUTHOR = 'Gemini'
def register_hooks(self):
hooks.register('head_meta', self.dispatch_seo_generation)
def _get_article_seo_data(self, context, request, blog_setting):
article = context.get('article')
if not isinstance(article, Article):
return None
description = strip_tags(article.body)[:150]
keywords = ",".join([tag.name for tag in article.tags.all()]) or blog_setting.site_keywords
meta_tags = f'''
<meta property="og:type" content="article"/>
<meta property="og:title" content="{article.title}"/>
<meta property="og:description" content="{description}"/>
<meta property="og:url" content="{request.build_absolute_uri()}"/>
<meta property="article:published_time" content="{article.pub_time.isoformat()}"/>
<meta property="article:modified_time" content="{article.last_modify_time.isoformat()}"/>
<meta property="article:author" content="{article.author.username}"/>
<meta property="article:section" content="{article.category.name}"/>
'''
for tag in article.tags.all():
meta_tags += f'<meta property="article:tag" content="{tag.name}"/>'
meta_tags += f'<meta property="og:site_name" content="{blog_setting.site_name}"/>'
structured_data = {
"@context": "https://schema.org",
"@type": "Article",
"mainEntityOfPage": {"@type": "WebPage", "@id": request.build_absolute_uri()},
"headline": article.title,
"description": description,
"image": request.build_absolute_uri(article.get_first_image_url()),
"datePublished": article.pub_time.isoformat(),
"dateModified": article.last_modify_time.isoformat(),
"author": {"@type": "Person", "name": article.author.username},
"publisher": {"@type": "Organization", "name": blog_setting.site_name}
}
if not structured_data.get("image"):
del structured_data["image"]
return {
"title": f"{article.title} | {blog_setting.site_name}",
"description": description,
"keywords": keywords,
"meta_tags": meta_tags,
"json_ld": structured_data
}
def _get_category_seo_data(self, context, request, blog_setting):
category_name = context.get('tag_name')
if not category_name:
return None
category = Category.objects.filter(name=category_name).first()
if not category:
return None
title = f"{category.name} | {blog_setting.site_name}"
description = strip_tags(category.name) or blog_setting.site_description
keywords = category.name
# BreadcrumbList structured data for category page
breadcrumb_items = [{"@type": "ListItem", "position": 1, "name": "首页", "item": request.build_absolute_uri('/')}]
breadcrumb_items.append({"@type": "ListItem", "position": 2, "name": category.name, "item": request.build_absolute_uri()})
structured_data = {
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": breadcrumb_items
}
return {
"title": title,
"description": description,
"keywords": keywords,
"meta_tags": "",
"json_ld": structured_data
}
def _get_default_seo_data(self, context, request, blog_setting):
# Homepage and other default pages
structured_data = {
"@context": "https://schema.org",
"@type": "WebSite",
"url": request.build_absolute_uri('/'),
"potentialAction": {
"@type": "SearchAction",
"target": f"{request.build_absolute_uri('/search/')}?q={{search_term_string}}",
"query-input": "required name=search_term_string"
}
}
return {
"title": f"{blog_setting.site_name} | {blog_setting.site_description}",
"description": blog_setting.site_description,
"keywords": blog_setting.site_keywords,
"meta_tags": "",
"json_ld": structured_data
}
def dispatch_seo_generation(self, metas, context):
request = context.get('request')
if not request:
return metas
view_name = request.resolver_match.view_name
blog_setting = get_blog_setting()
seo_data = None
if view_name == 'blog:detailbyid':
seo_data = self._get_article_seo_data(context, request, blog_setting)
elif view_name == 'blog:category_detail':
seo_data = self._get_category_seo_data(context, request, blog_setting)
if not seo_data:
seo_data = self._get_default_seo_data(context, request, blog_setting)
json_ld_script = f'<script type="application/ld+json">{json.dumps(seo_data.get("json_ld", {}), ensure_ascii=False, indent=4)}</script>'
return f"""
<title>{seo_data.get("title", "")}</title>
<meta name="description" content="{seo_data.get("description", "")}">
<meta name="keywords" content="{seo_data.get("keywords", "")}">
{seo_data.get("meta_tags", "")}
{json_ld_script}
"""
plugin = SeoOptimizerPlugin()

Binary file not shown.

View File

@@ -2,30 +2,6 @@
{% load blog_tags %}
{% block header %}
<title>{{ article.title }} | {{ SITE_DESCRIPTION }}</title>
<meta property="og:type" content="article"/>
<meta property="og:title" content="{{ article.title }}"/>
<meta property="og:description" content="{{ article.body|custom_markdown|striptags|truncatewords:1 }}"/>
<meta property="og:url"
content="{{ article.get_full_url }}"/>
<meta property="article:published_time" content="{% datetimeformat article.pub_time %}"/>
<meta property="article:modified_time" content="{% datetimeformat article.pub_time %}"/>
<meta property="article:author" content="{{ article.author.get_full_url }}"/>
<meta property="article:section" content="{{ article.category.name }}"/>
{% for t in article.tags.all %}
<meta property="article:tag" content="{{ t.name }}"/>
{% endfor %}
<meta property="og:site_name" content="{{ SITE_NAME }}"/>
<meta name="description" content="{{ article.body|custom_markdown|striptags|truncatewords:1 }}"/>
{% if article.tags %}
<meta name="keywords" content="{{ article.tags.all|join:"," }}"/>
{% else %}
<meta name="keywords" content="{{ SITE_KEYWORDS }}"/>
{% endif %}
{% endblock %}
{% block content %}
<div id="primary" class="site-content">

View File

@@ -18,7 +18,12 @@
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width"/>
{% block header %}
<title>{% block title %}{{ SITE_NAME }}{% endblock %}</title>
<meta name="description" content="{{ SITE_DESCRIPTION }}">
<meta name="keywords" content="{{ SITE_KEYWORDS }}">
{% endblock %}
{% load blog_tags %}
{% head_meta %}
<link rel="profile" href="http://gmpg.org/xfn/11"/>
<!--[if lt IE 9]>