mirror of
https://gitee.com/lylinux/DjangoBlog.git
synced 2025-12-06 11:19:14 +08:00
插件能力&&升级依赖
This commit is contained in:
209
README.md
209
README.md
@@ -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>
|
||||
|
||||
[](https://github.com/liangliangyy/DjangoBlog/actions/workflows/django.yml) [](https://github.com/liangliangyy/DjangoBlog/actions/workflows/codeql-analysis.yml) [](https://codecov.io/gh/liangliangyy/DjangoBlog) []()
|
||||
---
|
||||
|
||||
## 主要功能:
|
||||
- 文章,页面,分类目录,标签的添加,删除,编辑等。文章、评论及页面支持`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)留下你的网址,让更多的人看到。您的回复将会是我继续更新维护下去的动力。
|
||||
|
||||
@@ -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):
|
||||
"""文章分类"""
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
default_app_config = 'djangoblog.apps.DjangoblogAppConfig'
|
||||
|
||||
11
djangoblog/apps.py
Normal file
11
djangoblog/apps.py
Normal 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()
|
||||
19
djangoblog/plugin_manage/loader.py
Normal file
19
djangoblog/plugin_manage/loader.py
Normal 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)
|
||||
@@ -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'
|
||||
]
|
||||
@@ -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>
|
||||
|
||||
---
|
||||
|
||||
[](https://github.com/liangliangyy/DjangoBlog/actions/workflows/django.yml) [](https://github.com/liangliangyy/DjangoBlog/actions/workflows/codeql-analysis.yml) [](https://codecov.io/gh/liangliangyy/DjangoBlog) []()
|
||||
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
114
docs/docker-en.md
Normal file
@@ -0,0 +1,114 @@
|
||||
# Deploying DjangoBlog with Docker
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
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.
|
||||
159
docs/docker.md
159
docs/docker.md
@@ -1,59 +1,114 @@
|
||||
# 使用docker部署
|
||||
# 使用 Docker 部署 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
141
docs/k8s-en.md
Normal 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
141
docs/k8s.md
Normal 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 的部署!
|
||||
1
plugins/seo_optimizer/__init__.py
Normal file
1
plugins/seo_optimizer/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# This file makes this a Python package
|
||||
142
plugins/seo_optimizer/plugin.py
Normal file
142
plugins/seo_optimizer/plugin.py
Normal 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()
|
||||
BIN
requirements.txt
BIN
requirements.txt
Binary file not shown.
@@ -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">
|
||||
|
||||
@@ -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]>
|
||||
|
||||
Reference in New Issue
Block a user