Gitea 工作流新增 demo 环境部署
Some checks failed
CI — Docker Build & Push / Build & Push Image (push) Failing after 11m24s

This commit is contained in:
2026-04-23 11:49:34 +09:00
parent d6eb1bc57e
commit 774fedaa7a
4 changed files with 426 additions and 45 deletions

View File

@@ -24,7 +24,8 @@
"Bash(git -C /d/3.Project/HTY1024/ai-app-database add .gitea/workflows/)",
"Bash(git -C /d/3.Project/HTY1024/ai-app-database commit -m 'fix\\(ci\\): 将 Secret 名从 GITEA_TOKEN 改为 REGISTRY_TOKEN:*)",
"Bash(git -C /d/3.Project/HTY1024/ai-app-database log --oneline -3)",
"Bash(git -C /d/3.Project/HTY1024/ai-app-database add scripts/ .gitattributes)"
"Bash(git -C /d/3.Project/HTY1024/ai-app-database add scripts/ .gitattributes)",
"Bash(git -C /d/3.Project/HTY1024/ai-app-database log --oneline -4)"
]
}
}

View File

@@ -0,0 +1,214 @@
# ═══════════════════════════════════════════════════════════════
# Demo 部署工作流 — CI 成功后自动部署 / 可手动触发
#
# 触发条件:
# 1. "CI — Docker Build & Push" 在 main/master 上成功完成后自动触发
# 2. workflow_dispatch 手动触发(可指定镜像标签)
#
# 运行环境:
# - self-hosted runner与 Gitea 同一服务器)
# - Docker 部署,外置 MySQL & Caddy
#
# 前置配置Gitea → 仓库 → 设置 → Secrets
# REGISTRY_TOKEN — 具有 package:read 权限的 Gitea Access Token
# DEMO_SECRET_KEY — Flask SECRET_KEY随机字符串≥32位
# DEMO_ADMIN_PASS — Demo 管理员密码(首次部署时设置)
#
# 服务器前置条件:
# - 已安装 Docker + docker compose或 docker-compose
# - 已安装并运行 Caddy配置了 sites 目录 include
# - runner 用户在 docker 组 或 使用 sudo见下方 SUDO_DOCKER 变量)
# ═══════════════════════════════════════════════════════════════
name: Deploy — Demo Environment
on:
# CI 工作流在 main/master 完成后触发
workflow_run:
workflows:
- "CI — Docker Build & Push"
branches:
- main
- master
types:
- completed
# 手动触发,支持指定镜像标签
workflow_dispatch:
inputs:
image_tag:
description: '镜像标签(默认: latest'
required: false
default: 'latest'
env:
REGISTRY: git.hty1024.com
# Demo 部署目录(需与服务器实际路径一致)
DEPLOY_DIR: /opt/resource-library-demo
# Caddy 站点配置目录(需在 Caddyfile 中通过 import 引用)
CADDY_SITES_DIR: /etc/caddy/sites
# 应用对外端口Caddy 反向代理到此端口)
APP_PORT: 5000
# Demo 域名
DOMAIN: prl.hty1024.com
# 外置 MySQL 连接串
DB_URL: mysql+pymysql://resource_library:BWRVzzCwzuuP_1Hj@pma.hty1024.com:31000/resource_library
jobs:
deploy-demo:
name: Deploy to Demo
runs-on: self-hosted
# workflow_run 触发时,仅在 CI 成功时继续
if: >
github.event_name == 'workflow_dispatch' ||
(github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success')
steps:
# ── 1. 检出代码(获取 compose 文件等) ──────────────────────
- name: 检出代码
uses: actions/checkout@v4
# ── 2. 确定目标镜像标签 ──────────────────────────────────────
- name: 确定镜像标签
id: image
run: |
REPO=$(echo "${{ github.repository }}" | tr '[:upper:]' '[:lower:]')
IMAGE_NAME="${{ env.REGISTRY }}/${REPO}"
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
TAG="${{ github.event.inputs.image_tag }}"
else
TAG="latest"
fi
echo "name=${IMAGE_NAME}" >> $GITHUB_OUTPUT
echo "tag=${TAG}" >> $GITHUB_OUTPUT
echo "full=${IMAGE_NAME}:${TAG}" >> $GITHUB_OUTPUT
echo "镜像: ${IMAGE_NAME}:${TAG}"
# ── 3. 登录 Gitea 镜像仓库 ──────────────────────────────────
- name: 登录 Gitea 镜像仓库
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ gitea.actor }}
password: ${{ secrets.REGISTRY_TOKEN }}
# ── 4. 拉取最新镜像 ──────────────────────────────────────────
- name: 拉取镜像
run: |
docker pull ${{ steps.image.outputs.full }}
echo "镜像摘要: $(docker inspect ${{ steps.image.outputs.full }} --format='{{.Id}}')"
# ── 5. 创建部署目录 ──────────────────────────────────────────
- name: 准备部署目录
run: |
mkdir -p ${{ env.DEPLOY_DIR }}
# ── 6. 写入环境配置文件 ──────────────────────────────────────
- name: 写入 .env 文件
run: |
cat > ${{ env.DEPLOY_DIR }}/.env.demo << 'ENVEOF'
# 自动生成 — 请勿手动编辑,由 CI/CD 工作流维护
DATABASE_URL=${{ env.DB_URL }}
SECRET_KEY=${{ secrets.DEMO_SECRET_KEY }}
APP_PORT=${{ env.APP_PORT }}
FLASK_ENV=production
ENVEOF
chmod 600 ${{ env.DEPLOY_DIR }}/.env.demo
echo "env 文件已写入"
# ── 7. 复制 compose 文件到部署目录 ───────────────────────────
- name: 复制 Compose 文件
run: |
cp docker-compose.external-db.yml ${{ env.DEPLOY_DIR }}/docker-compose.yml
# ── 8. 更新 compose 文件中的镜像引用 ─────────────────────────
- name: 更新镜像标签
run: |
cd ${{ env.DEPLOY_DIR }}
# 将 compose 文件中的镜像替换为当前目标镜像
sed -i "s|image:.*resource-library.*|image: ${{ steps.image.outputs.full }}|g" docker-compose.yml
echo "已更新镜像引用为: ${{ steps.image.outputs.full }}"
# ── 9. 部署 / 更新容器 ──────────────────────────────────────
- name: 部署容器
run: |
cd ${{ env.DEPLOY_DIR }}
# 检测 compose 命令
if docker compose version &>/dev/null 2>&1; then
COMPOSE="docker compose"
else
COMPOSE="docker-compose"
fi
$COMPOSE \
--env-file .env.demo \
--project-name resource-library-demo \
up -d --remove-orphans --pull never
echo "容器已启动"
$COMPOSE --project-name resource-library-demo ps
# ── 10. 等待服务就绪 ─────────────────────────────────────────
- name: 健康检查
run: |
max_wait=90
elapsed=0
url="http://127.0.0.1:${{ env.APP_PORT }}/auth/login"
echo "等待服务就绪(最长 ${max_wait}s…"
while [ "$elapsed" -lt "$max_wait" ]; do
if curl -fsSL --max-time 5 "$url" > /dev/null 2>&1; then
echo "服务健康!(${elapsed}s"
exit 0
fi
sleep 5
elapsed=$((elapsed + 5))
echo " 已等待 ${elapsed}s…"
done
echo "健康检查超时,查看日志:"
docker logs --tail 50 resource-library-demo-app-1 2>/dev/null || true
exit 1
# ── 11. 配置 Caddy 站点 ──────────────────────────────────────
- name: 配置 Caddy
run: |
# 确保 Caddy sites 目录存在
mkdir -p ${{ env.CADDY_SITES_DIR }}
# 写入站点配置(仅在文件内容变更时才重载)
CADDY_CONF="${{ env.CADDY_SITES_DIR }}/${{ env.DOMAIN }}.caddy"
NEW_CONF=$(cat docker/caddy/${{ env.DOMAIN }}.caddy)
OLD_CONF=$(cat "$CADDY_CONF" 2>/dev/null || echo "")
if [ "$NEW_CONF" != "$OLD_CONF" ]; then
cp docker/caddy/${{ env.DOMAIN }}.caddy "$CADDY_CONF"
echo "Caddy 配置已更新,重载…"
# 优先使用 systemctl退而使用 caddy reload
if systemctl is-active --quiet caddy; then
systemctl reload caddy || caddy reload --config /etc/caddy/Caddyfile
elif caddy validate --config /etc/caddy/Caddyfile &>/dev/null; then
caddy reload --config /etc/caddy/Caddyfile
else
echo "警告:无法重载 Caddy请手动执行 systemctl reload caddy"
fi
else
echo "Caddy 配置未变更,跳过重载"
fi
# ── 12. 输出部署摘要 ─────────────────────────────────────────
- name: 输出部署摘要
run: |
echo "## 🚀 Demo 环境部署成功" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| 项目 | 值 |" >> $GITHUB_STEP_SUMMARY
echo "|------|-----|" >> $GITHUB_STEP_SUMMARY
echo "| 访问地址 | https://${{ env.DOMAIN }} |" >> $GITHUB_STEP_SUMMARY
echo "| 镜像 | \`${{ steps.image.outputs.full }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| 触发方式 | ${{ github.event_name }} |" >> $GITHUB_STEP_SUMMARY
echo "| 部署时间 | $(date -u '+%Y-%m-%d %H:%M:%S UTC') |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Demo 地址:** https://${{ env.DOMAIN }}" >> $GITHUB_STEP_SUMMARY

197
README.md
View File

@@ -2,6 +2,8 @@
基于 Flask + MySQL 的个人多媒体资料管理系统。支持文本、图片、音频、视频的上传、URL 下载、磁力下载及在线预览。
**Demo 地址:** [https://prl.hty1024.com](https://prl.hty1024.com)
## 功能特性
| 功能 | 说明 |
@@ -11,61 +13,150 @@
| 本地上传 | 拖拽或点击上传,带实时进度条 |
| URL 下载 | 后台异步下载远程文件,实时轮询进度 |
| 磁力下载 | 调用 aria2c 在后台下载磁力链接资源 |
| 在线预览 | 文本高亮/图片查看/HTML5 音视频播放器 |
| 在线预览 | 文本高亮 / 图片查看 / HTML5 音视频播放器 |
| 安全 | bcrypt 加盐哈希密码、CSRF 防护、SQL 注入防护、XSS 防护 |
## 快速开始
### 1. 环境准备
### 1. 本地开发
- Python 3.10+
- MySQL 8.0+
- (可选)[aria2c](https://aria2.github.io/) — 用于磁力下载
### 2. 安装依赖
**环境要求:** Python 3.10+、MySQL 8.0+、(可选)[aria2c](https://aria2.github.io/)
```bash
# 安装依赖
pip install -r requirements.txt
```
### 3. 配置数据库
创建 MySQL 数据库:
```sql
CREATE DATABASE resource_library CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
```
复制并修改环境配置:
```bash
# 配置环境
cp .env.example .env
```
# 编辑 .env设置 DATABASE_URL 和 SECRET_KEY
编辑 `.env`
```env
SECRET_KEY=your-random-secret-key
DATABASE_URL=mysql+pymysql://root:yourpassword@localhost:3306/resource_library
```
### 4. 初始化数据库
```bash
# 初始化数据库
python init_db.py
# 自定义管理员账号
# python init_db.py --admin-user admin --admin-pass YourPass123 --admin-email admin@example.com
```
# 自定义管理员:python init_db.py --admin-user admin --admin-pass YourPass123
### 5. 启动服务
```bash
# 启动
python run.py
```
访问 `http://localhost:5000`使用默认管理员账号登录
- 用户名:`admin`
- 密码:`Admin@123456`
访问 `http://localhost:5000`,默认管理员:用户名 `admin`,密码 `Admin@123456`
---
### 2. Docker 部署(一键脚本)
> 适用于 Ubuntu / Debian / CentOS / RHEL / Alpine / Arch 等主流 Linux 发行版
```bash
git clone <仓库地址>
cd ai-app-database
bash scripts/deploy.sh
```
脚本会引导完成所有配置,支持两种部署模式:
| 模式 | 说明 |
|------|------|
| **新建 MySQL**(默认) | 用 Docker 启动 MySQL适合全新服务器 |
| **使用已有 MySQL** | 连接外部数据库,适合已有数据库的场景 |
更新服务:
```bash
bash scripts/update.sh
# 支持参数:--yes跳过确认、--tag <镜像标签>
```
---
### 3. 手动 Docker Compose 部署
**方式 A新建 MySQL完整栈**
```bash
cp .env.docker.example .env.docker
# 编辑 .env.docker设置密码和 SECRET_KEY
docker compose --env-file .env.docker up -d
# 含 Nginxdocker compose --env-file .env.docker --profile nginx up -d
```
**方式 B外置 MySQL**
```bash
cp .env.external-db.example .env.external-db
# 编辑 .env.external-db填写 DATABASE_URL 等
docker compose -f docker-compose.external-db.yml --env-file .env.external-db up -d
```
---
## CI/CD 自动化
### Gitea Actions 工作流
| 工作流 | 触发条件 | 说明 |
|--------|----------|------|
| `ci.yml` | push 到 main / master / develop | 构建镜像并推送至 Gitea 镜像仓库 |
| `release.yml` | push `v*` tag如 v1.2.3 | 构建正式版镜像,生成多级标签 |
| `deploy-demo.yml` | CI 完成后自动触发 / 手动触发 | 部署到 Demo 环境self-hosted runner |
### 镜像标签规则
| 触发 | 生成标签 |
|------|----------|
| push main | `:latest``:sha-<短commit>` |
| push develop | `:develop``:sha-<短commit>` |
| push v1.2.3 | `:1.2.3``:1.2``:1``:latest` |
| push v1.2.3-rc1 | `:1.2.3-rc1`(不推送 `:latest` |
### 配置 Secrets
在 Gitea → 仓库 → 设置 → Secrets 中添加:
| Secret 名称 | 用途 | 备注 |
|-------------|------|------|
| `REGISTRY_TOKEN` | 推送 / 拉取镜像 | 需要 `package:write` 权限 |
| `DEMO_SECRET_KEY` | Flask SECRET_KEY | 随机字符串≥32 位 |
| `DEMO_ADMIN_PASS` | Demo 管理员密码 | 首次部署后可修改 |
> **注意:** Gitea 禁止以 `GITEA_` 或 `GITHUB_` 开头的 Secret 名称。
---
## Demo 环境
| 项目 | 配置 |
|------|------|
| 访问地址 | [https://prl.hty1024.com](https://prl.hty1024.com) |
| 部署方式 | Docker + 外置 MySQL + Caddy 反向代理 |
| 运行位置 | Gitea runner 同一服务器self-hosted |
| 自动更新 | push 到 main 分支后CI 成功即自动部署 |
### Caddy 配置
Demo 环境使用外置 Caddy 提供反向代理和自动 TLS。
**1. 在主 Caddyfile 中启用 sites 目录导入:**
```caddyfile
# /etc/caddy/Caddyfile
{
email your@email.com
}
import /etc/caddy/sites/*.caddy
```
**2. 复制站点配置:**
```bash
sudo mkdir -p /etc/caddy/sites
sudo cp docker/caddy/prl.hty1024.com.caddy /etc/caddy/sites/
sudo systemctl reload caddy
```
Caddy 会自动申请并续期 Let's Encrypt TLS 证书。
---
## 项目结构
@@ -73,14 +164,31 @@ python run.py
.
├── app/
│ ├── models/ # 数据库模型 (User, Resource, SystemSetting)
│ ├── routes/ # 路由蓝图 (auth, admin, resources, main)
│ ├── routes/ # 路由蓝图 (auth, admin, resources, main, errors)
│ ├── utils/ # 工具模块 (decorators, file_handler, downloader)
│ ├── static/ # 静态文件 (CSS, JS, 上传文件)
│ ├── static/ # 静态文件 (CSS, JS)
│ │ └── uploads/ # 用户上传文件目录(自动创建)
│ └── templates/ # Jinja2 模板
├── config.py # 配置类
├── docker/
│ ├── entrypoint.sh # 容器入口脚本等待DB、初始化、启动
│ ├── nginx.conf # Nginx 反向代理配置(可选)
│ └── caddy/
│ └── prl.hty1024.com.caddy # Demo 环境 Caddy 站点配置
├── scripts/
│ ├── deploy.sh # Linux 一键部署脚本
│ └── update.sh # 一键更新脚本(含自动回滚)
├── .gitea/workflows/
│ ├── ci.yml # CI构建并推送镜像
│ ├── release.yml # Release正式版镜像
│ └── deploy-demo.yml # Demo 环境自动部署
├── docker-compose.yml # 完整栈(含 MySQL
├── docker-compose.external-db.yml # 外置数据库模式
├── .env.docker.example # 完整栈环境变量模板
├── .env.external-db.example # 外置 DB 环境变量模板
├── Dockerfile
├── config.py
├── init_db.py # 数据库初始化脚本
├── run.py # 启动入口
├── run.py # 本地开发启动入口
└── requirements.txt
```
@@ -100,5 +208,6 @@ python run.py
磁力下载需要在服务器上安装 `aria2c`
- **Linux/macOS**`apt install aria2` / `brew install aria2`
- **Linux**`apt install aria2` / `yum install aria2`
- **macOS**`brew install aria2`
- **Windows**:从 [aria2 Releases](https://github.com/aria2/aria2/releases) 下载并加入 PATH

View File

@@ -0,0 +1,57 @@
# ═══════════════════════════════════════════════════════════════
# Caddy 站点配置 — prl.hty1024.com (Demo 环境)
#
# 使用说明:
# 1. 将此文件复制到 /etc/caddy/sites/ 目录
# 2. 确保 /etc/caddy/Caddyfile 中包含:
# import /etc/caddy/sites/*.caddy
# 3. 执行 systemctl reload caddy或 caddy reload
#
# Caddy 会自动申请并续期 Let's Encrypt TLS 证书。
# ═══════════════════════════════════════════════════════════════
prl.hty1024.com {
# ── 反向代理到 Flask 应用 ────────────────────────────────────
reverse_proxy 127.0.0.1:5000 {
# 传递真实客户端 IP
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
header_up Host {host}
# 健康检查可选Caddy 会自动摘除不健康的上游)
health_uri /auth/login
health_interval 30s
health_timeout 5s
}
# ── 响应压缩 ────────────────────────────────────────────────
encode gzip
# ── 安全响应头 ──────────────────────────────────────────────
header {
# 禁止 iframe 嵌入(点击劫持防护)
X-Frame-Options "SAMEORIGIN"
# 禁止 MIME 类型嗅探
X-Content-Type-Options "nosniff"
# 强制 HTTPSHSTS6 个月)
Strict-Transport-Security "max-age=15768000; includeSubDomains"
# XSS 过滤(旧浏览器兼容)
X-XSS-Protection "1; mode=block"
# Referrer 策略
Referrer-Policy "strict-origin-when-cross-origin"
# 删除 Server 头(隐藏服务器信息)
-Server
}
# ── 访问日志 ────────────────────────────────────────────────
log {
output file /var/log/caddy/prl.hty1024.com.log {
roll_size 50mb
roll_keep 5
roll_keep_for 720h
}
format json
}
}