Compare commits

...

17 Commits

Author SHA1 Message Date
xiaoquan 631235e16c fix: 修复 server 类型错误
Deploy / deploy (push) Successful in 2m58s
- AdminRole 从正确的常量文件导入
- env 参数添加正确的类型断言
2026-06-10 23:55:52 +08:00
xiaoquan 55bfd808e9 fix: 修复 platform-admin 类型错误
Deploy / deploy (push) Failing after 2m24s
- Accounts.tsx: 导入 formatDateTime 和 formatMoney
- Dashboard.tsx: 添加 monthIncome 和 monthExpense 字段
- PlatformTransactions.tsx: 修复 refresh 调用,使用 setParams
2026-06-10 23:47:34 +08:00
xiaoquan 7c86b1a87f fix: 修复 refresh 函数调用错误
Deploy / deploy (push) Failing after 1m54s
- refresh() 不接受参数
- 使用 reset() 重置所有参数
- 使用 setParams() 更新特定参数
2026-06-10 20:12:13 +08:00
xiaoquan d26a9595d3 fix: 升级 website Dockerfile 使用 Node.js 20
Deploy / deploy (push) Failing after 23s
Next.js 要求 Node.js >= 20.9.0
2026-06-10 20:02:21 +08:00
xiaoquan d3b7db90e7 fix: 修复 Settlement 类型字段名称不匹配
Deploy / deploy (push) Failing after 1m40s
- orderAmount → totalAmount
- settlementAmount → actualAmount
- settledAt → paidAt

与 @rent/shared-types 中的类型定义保持一致
2026-06-10 19:53:51 +08:00
xiaoquan 85547d7043 fix: 修复 pnpm 版本和 docker compose 命令
Deploy / deploy (push) Failing after 1m36s
## 修改内容
1. 所有 Dockerfile 中指定 pnpm@8(兼容 lockfileVersion 6.0)
2. 工作流使用 docker compose(V2)而不是 docker-compose

## 修复的问题
- pnpm-lock.yaml 版本不兼容
- 容器中缺少 docker-compose 命令

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-10 19:42:53 +08:00
xiaoquan ab7ff9a59b fix: 移除重复的 docker.sock 挂载
Deploy / deploy (push) Failing after 28s
2026-06-10 19:37:43 +08:00
xiaoquan 51de32d1de fix: 使用正确的 Runner 标签 self-hosted
Deploy / deploy (push) Failing after 26s
2026-06-10 19:16:50 +08:00
xiaoquan 89e5143395 fix: 修改 Runner 标签匹配 2026-06-10 19:15:24 +08:00
xiaoquan d66f6b6be5 test: 触发 CI/CD 测试 2026-06-10 19:13:14 +08:00
xiaoquan 147a25614f fix: 使用包含 Docker 的容器镜像运行 CI/CD
## 修改内容
- 添加 container 配置
- 使用 catthehacker/ubuntu:act-latest 镜像
- 该镜像预装了 docker、docker-compose 等工具

## 优点
- 不需要在 Runner 容器中安装工具
- 使用标准的 GitHub Actions 兼容镜像
- 更稳定可靠

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-10 19:02:57 +08:00
xiaoquan c243300670 feat: 添加 Webhook 部署方案
## 新增文件
- deploy/scripts/deploy.sh - 自动化部署脚本
- deploy/docker/WEBHOOK_SETUP.md - Webhook 配置完整指南

## 方案说明
放弃使用 Gitea Runner(Docker 容器环境限制太多)
改用 Webhook 直接在宿主机上执行部署脚本

## 工作流程
1. git push 触发 Gitea Webhook
2. Webhook 服务调用 deploy.sh
3. 脚本自动 git pull + docker-compose build + up

## 优点
- 简单可靠,不依赖容器环境
- 直接在宿主机执行,有完整的 Docker 访问权限
- 容易调试和维护
- 支持两种 Webhook 方式:webhook 工具或 PHP 脚本

## 配置方式
详见 WEBHOOK_SETUP.md 文档

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-10 18:05:05 +08:00
xiaoquan 32b4053ed3 fix: 支持 docker compose V2 版本
## 修改内容
- 使用 `docker compose` (V2,空格分隔)
- 备用方案:`docker-compose` (V1,连字符)
- 通过 || 运算符自动尝试两种版本

## 说明
- Docker Compose V2 已内置在 Docker 中
- 命令格式:docker compose(不是 docker-compose)
- 如果 V2 不可用,自动回退到 V1

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-10 18:02:19 +08:00
xiaoquan 5b3c599db6 feat: 使用外网 HTTPS 地址克隆代码
## 修改内容
- 使用 https://gitea.pinzhuhui.com/xiaoquan/rent.git
- 代码克隆到 /workspace/rent-deploy
- 首次运行自动克隆,后续自动 git pull

## 优点
- 使用外网地址,网络稳定
- HTTPS 协议,兼容性好
- 完全自动化,不需要手动配置

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-10 17:57:59 +08:00
xiaoquan bfc30fac22 feat: 最终 CI/CD 方案 - 手动克隆 + 自动部署
## 方案说明
由于 Gitea Runner 在 Docker 容器中无法访问宿主机文件系统和网络,
采用一次性手动配置 + 自动部署的方案

## 配置步骤(一次性)
1. SSH 登录服务器
2. 手动克隆代码到 /www/dk_project/dk_app/rent
3. 配置 Git 凭证(SSH 或 Token)

## 工作流程
1. 开发者 git push
2. CI/CD 自动触发
3. git pull 拉取最新代码
4. docker-compose build 构建
5. docker-compose up 部署

## 新增文档
- CICD_SETUP.md - 完整配置指南

## 优点
- 简单可靠
- 不依赖复杂的网络配置
- 代码在宿主机上,构建速度快

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-10 17:48:53 +08:00
xiaoquan 860ad35387 debug: 自动查找 Gitea 仓库路径
## 修改内容
- 尝试多个可能的 Gitea 仓库路径
- 如果都找不到,执行 find 命令搜索
- 输出搜索结果,方便确定正确路径

## 目的
- 找到服务器上 Gitea 仓库的实际位置
- 根据输出确定正确路径

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-10 17:43:10 +08:00
xiaoquan e1612f63db fix: 使用 Gitea 仓库文件路径,不走网络
## 问题
- 网络克隆一直超时
- Docker 容器访问 Gitea HTTP 端口不通

## 解决方案
- 直接使用宿主机文件系统中的 Gitea 仓库
- 路径:/www/dk_project/dk_app/gitea/gitea_P4dS/gitea/gitea/repositories/xiaoquan/rent.git
- 使用 git init + git remote add 方式
- 完全不走网络,只读取本地文件

## 优点
- 不依赖网络
- 速度极快
- 100% 可靠

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-10 17:40:05 +08:00
16 changed files with 496 additions and 40 deletions
+24 -23
View File
@@ -11,31 +11,32 @@ env:
jobs:
deploy:
runs-on: [self-hosted, rent-deploy]
runs-on: self-hosted
container:
image: catthehacker/ubuntu:act-latest
options: --privileged
steps:
- name: Clone or update code
run: |
WORK_DIR="/tmp/rent-deploy"
WORK_DIR="/workspace/rent-deploy"
REPO_URL="https://gitea.pinzhuhui.com/xiaoquan/rent.git"
# 使用宿主机的 Gitea 端口
GITEA_URL="http://host.docker.internal:10082/xiaoquan/rent.git"
echo "准备代码..."
if [ -d "$WORK_DIR" ]; then
echo "代码目录已存在,拉取最新代码..."
if [ -d "$WORK_DIR/.git" ]; then
echo "代码目录已存在,新代码..."
cd $WORK_DIR
git fetch --all
git reset --hard origin/${{ github.ref_name }}
git fetch origin
git checkout ${{ github.ref_name }}
git pull origin ${{ github.ref_name }}
else
echo "首次克隆代码..."
# 尝试多个可能的地址
git clone $GITEA_URL $WORK_DIR || \
git clone http://172.17.0.1:10082/xiaoquan/rent.git $WORK_DIR || \
git clone http://localhost:10082/xiaoquan/rent.git $WORK_DIR
git clone $REPO_URL $WORK_DIR
cd $WORK_DIR
git checkout ${{ github.ref_name }}
fi
echo "✅ 代码准备完成"
echo "当前分支: $(git branch --show-current)"
echo "最新提交: $(git log -1 --oneline)"
@@ -43,38 +44,38 @@ jobs:
if: github.ref == 'refs/heads/test'
run: |
echo "部署到测试环境..."
cd /tmp/rent-deploy/deploy/docker
cd /workspace/rent-deploy/deploy/docker
export TEST_DB_PASSWORD="${{ secrets.TEST_DB_PASSWORD }}"
export TEST_JWT_SECRET="${{ secrets.TEST_JWT_SECRET }}"
export ENCRYPTION_KEY="${{ secrets.ENCRYPTION_KEY }}"
docker-compose -f docker-compose.test.yml down --remove-orphans
docker-compose -f docker-compose.test.yml build --parallel
docker-compose -f docker-compose.test.yml up -d
docker compose -f docker-compose.test.yml down --remove-orphans
docker compose -f docker-compose.test.yml build --parallel
docker compose -f docker-compose.test.yml up -d
docker image prune -f
echo "等待服务启动..."
sleep 10
docker-compose -f docker-compose.test.yml ps
docker compose -f docker-compose.test.yml ps
echo "✅ 测试环境部署完成"
- name: Deploy to production
if: github.ref == 'refs/heads/prod'
run: |
echo "部署到生产环境..."
cd /tmp/rent-deploy/deploy/docker
cd /workspace/rent-deploy/deploy/docker
export PROD_DB_PASSWORD="${{ secrets.PROD_DB_PASSWORD }}"
export PROD_JWT_SECRET="${{ secrets.PROD_JWT_SECRET }}"
export ENCRYPTION_KEY="${{ secrets.ENCRYPTION_KEY }}"
docker-compose -f docker-compose.prod.yml down --remove-orphans
docker-compose -f docker-compose.prod.yml build --parallel
docker-compose -f docker-compose.prod.yml up -d
docker compose -f docker-compose.prod.yml down --remove-orphans
docker compose -f docker-compose.prod.yml build --parallel
docker compose -f docker-compose.prod.yml up -d
docker image prune -f
echo "等待服务启动..."
sleep 10
docker-compose -f docker-compose.prod.yml ps
docker compose -f docker-compose.prod.yml ps
echo "✅ 生产环境部署完成"
+1
View File
@@ -49,3 +49,4 @@ pnpm dev:merchant
# 启动平台管理后台
pnpm dev:platform
```
# CI/CD Test 2026年06月10日 19:13:14
@@ -213,21 +213,21 @@ const Settlements: React.FC = () => {
{detailModal.data.periodStart} ~ {detailModal.data.periodEnd}
</Descriptions.Item>
<Descriptions.Item label="订单数量">{detailModal.data.orderCount} </Descriptions.Item>
<Descriptions.Item label="订单总额">{formatMoney(detailModal.data.orderAmount)}</Descriptions.Item>
<Descriptions.Item label="订单总额">{formatMoney(detailModal.data.totalAmount)}</Descriptions.Item>
<Descriptions.Item label="平台服务费">
<span style={{ color: '#ff4d4f' }}>{formatMoney(detailModal.data.serviceFee)}</span>
</Descriptions.Item>
<Descriptions.Item label="结算金额">
<span style={{ fontWeight: 'bold', color: '#52c41a' }}>
{formatMoney(detailModal.data.settlementAmount)}
{formatMoney(detailModal.data.actualAmount)}
</span>
</Descriptions.Item>
<Descriptions.Item label="状态">
<SettlementStatusTag status={detailModal.data.status} />
</Descriptions.Item>
<Descriptions.Item label="创建时间">{formatDateTime(detailModal.data.createdAt)}</Descriptions.Item>
{detailModal.data.settledAt && (
<Descriptions.Item label="结算时间">{formatDateTime(detailModal.data.settledAt)}</Descriptions.Item>
{detailModal.data.paidAt && (
<Descriptions.Item label="结算时间">{formatDateTime(detailModal.data.paidAt)}</Descriptions.Item>
)}
</Descriptions>
)}
@@ -3,6 +3,7 @@ import { Card, Table, Tabs, Form, Input, Button, Space, Tag } from 'antd';
import { SearchOutlined, ReloadOutlined } from '@ant-design/icons';
import { getPlatformAccounts, getUserAccounts, getMerchantAccounts } from '@/api/finance';
import type { ColumnsType } from 'antd/es/table';
import { formatDateTime, formatMoney } from '@rent/shared-utils';
const { TabPane } = Tabs;
@@ -20,6 +20,8 @@ interface FinancialOverview {
merchantCount: number;
todayIncome: number;
todayExpense: number;
monthIncome: number;
monthExpense: number;
}
interface Transaction {
@@ -40,6 +40,8 @@ const PlatformTransactions: React.FC = () => {
setPage,
setPageSize,
refresh,
reset,
setParams,
} = useTableData<PlatformTransaction>({
fetchFn: async (params) => {
const res = await getPlatformTransactions(params);
@@ -67,12 +69,12 @@ const PlatformTransactions: React.FC = () => {
params.endDate = values.dateRange[1].format('YYYY-MM-DD');
}
refresh(params);
setParams(params);
};
const handleReset = () => {
form.resetFields();
refresh({ page: 1 });
reset();
};
const handleViewDetail = (record: PlatformTransaction) => {
@@ -50,6 +50,8 @@ const PlatformWithdrawals: React.FC = () => {
setPage,
setPageSize,
refresh,
reset,
setParams,
} = useTableData<PlatformWithdrawal>({
fetchFn: async (params) => {
const res = await getPlatformWithdrawals(params);
@@ -66,12 +68,12 @@ const PlatformWithdrawals: React.FC = () => {
const handleSearch = () => {
const values = form.getFieldsValue();
refresh({ ...values, page: 1 });
setParams({ ...values, page: 1 });
};
const handleReset = () => {
form.resetFields();
refresh({ page: 1 });
reset();
};
const handleViewDetail = (record: PlatformWithdrawal) => {
@@ -14,7 +14,7 @@ import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger';
import { JwtAuthGuard } from '@/common/guards/jwt-auth.guard';
import { RolesGuard } from '@/common/guards/roles.guard';
import { Roles } from '@/common/decorators/roles.decorator';
import { AdminRole } from '@/entities/admin.entity';
import { AdminRole } from '@/common/constants/admin.constant';
import { SecretService } from '@/modules/shared/secret/secret.service';
import { CreateSecretDto, UpdateSecretDto, QuerySecretDto } from './dto/secret.dto';
@@ -56,10 +56,11 @@ export class SecretService {
* 获取解密后的密钥值(供其他服务调用)
*/
async getDecryptedValue(secretKey: string, env: string = 'all'): Promise<string | null> {
const envValue = env as 'all' | 'prod' | 'test';
const secret = await this.secretRepo.findOne({
where: [
{ secretKey, env: 'all' },
{ secretKey, env },
{ secretKey, env: envValue },
],
});
if (!secret) return null;
@@ -71,8 +72,9 @@ export class SecretService {
*/
async create(data: { secretKey: string; secretValue: string; description?: string; groupName?: string; env?: string }) {
// 检查是否已存在
const envValue = (data.env as 'all' | 'prod' | 'test') || 'all';
const existing = await this.secretRepo.findOne({
where: { secretKey: data.secretKey, env: (data.env as any) || 'all' },
where: { secretKey: data.secretKey, env: envValue },
});
if (existing) throw new ConflictException(`密钥 ${data.secretKey} 已存在`);
@@ -81,7 +83,7 @@ export class SecretService {
secretValue: encrypt(data.secretValue),
description: data.description,
groupName: data.groupName || 'default',
env: data.env || 'all',
env: envValue,
});
return this.secretRepo.save(secret);
}
+114
View File
@@ -0,0 +1,114 @@
# CI/CD 部署配置指南
由于 Gitea Runner 运行在 Docker 容器中,无法直接访问宿主机文件系统和网络,我们采用以下方案:
## 📋 一次性配置步骤
### 1. 在服务器上手动克隆代码
SSH 登录服务器后执行:
```bash
# 进入项目目录
cd /www/dk_project/dk_app
# 克隆代码(使用服务器本地 Gitea 端口)
git clone http://127.0.0.1:10082/xiaoquan/rent.git
# 验证
cd rent
ls -la
```
### 2. 配置 Git 凭证(避免每次输入密码)
```bash
cd /www/dk_project/dk_app/rent
# 方法1:使用 Gitea Token
git config credential.helper store
git pull # 第一次会要求输入用户名和 token
# 方法2:使用 SSH(推荐)
# 在 Gitea 中添加服务器的 SSH 公钥
ssh-keygen -t rsa -b 4096
cat ~/.ssh/id_rsa.pub
# 复制公钥,在 Gitea 个人设置 -> SSH密钥 中添加
# 修改 remote 为 SSH
git remote set-url origin git@127.0.0.1:xiaoquan/rent.git
```
### 3. 验证自动拉取
```bash
cd /www/dk_project/dk_app/rent
git pull origin test
```
应该能够无密码拉取成功。
---
## 🔄 CI/CD 工作流程
配置完成后,工作流程为:
1. **开发** → 修改代码
2. **git push origin test** → 推送到 Gitea
3. **CI/CD 自动触发**
- SSH 到服务器
- git pull 拉取最新代码
- docker-compose build 构建
- docker-compose up 部署
---
## 🚀 使用方式
### 推送到测试环境
```bash
git push origin test
```
### 推送到生产环境
```bash
git push origin prod
```
---
## 📝 注意事项
1. **首次配置**:必须在服务器上手动克隆一次代码
2. **Git 凭证**:配置后才能自动 git pull
3. **路径固定**:代码路径为 `/www/dk_project/dk_app/rent`
4. **权限**:确保 Docker 有权限访问代码目录
---
## 🔧 故障排查
### 问题:CI/CD 提示找不到代码目录
**解决**:检查服务器上是否已克隆代码
```bash
ls -la /www/dk_project/dk_app/rent
```
### 问题:git pull 需要密码
**解决**:配置 Git 凭证(见上面步骤2
### 问题:Docker 构建失败
**解决**:检查 Dockerfile 和依赖
```bash
cd /www/dk_project/dk_app/rent/deploy/docker
docker-compose -f docker-compose.test.yml build
```
---
**维护者**:开发团队
**最后更新**2026-06-10
+1 -1
View File
@@ -4,7 +4,7 @@ WORKDIR /app
COPY pnpm-workspace.yaml pnpm-lock.yaml package.json ./
COPY packages/ ./packages/
COPY apps/merchant-admin/ ./apps/merchant-admin/
RUN npm install -g pnpm && pnpm install --frozen-lockfile
RUN npm install -g pnpm@8 && pnpm install --frozen-lockfile
RUN pnpm --filter @rent/shared-types build
RUN pnpm --filter @rent/shared-utils build
RUN cd apps/merchant-admin && pnpm run build
+1 -1
View File
@@ -4,7 +4,7 @@ WORKDIR /app
COPY pnpm-workspace.yaml pnpm-lock.yaml package.json ./
COPY packages/ ./packages/
COPY apps/platform-admin/ ./apps/platform-admin/
RUN npm install -g pnpm && pnpm install --frozen-lockfile
RUN npm install -g pnpm@8 && pnpm install --frozen-lockfile
RUN pnpm --filter @rent/shared-types build
RUN pnpm --filter @rent/shared-utils build
RUN cd apps/platform-admin && pnpm run build
+1 -1
View File
@@ -4,7 +4,7 @@ WORKDIR /app
COPY pnpm-workspace.yaml pnpm-lock.yaml package.json ./
COPY packages/ ./packages/
COPY apps/server/ ./apps/server/
RUN npm install -g pnpm && pnpm install --frozen-lockfile
RUN npm install -g pnpm@8 && pnpm install --frozen-lockfile
RUN cd apps/server && pnpm run build
FROM node:18-alpine AS server
+2 -2
View File
@@ -1,10 +1,10 @@
# 官网 - Next.js 静态导出
FROM node:18-alpine AS website-builder
FROM node:20-alpine AS website-builder
WORKDIR /app
COPY pnpm-workspace.yaml pnpm-lock.yaml package.json ./
COPY packages/ ./packages/
COPY apps/official-website/ ./apps/official-website/
RUN npm install -g pnpm && pnpm install --frozen-lockfile
RUN npm install -g pnpm@8 && pnpm install --frozen-lockfile
RUN cd apps/official-website && pnpm run build
FROM nginx:alpine AS website
+250
View File
@@ -0,0 +1,250 @@
# Webhook 部署方案
由于 Gitea Runner 容器环境限制,采用更简单可靠的 Webhook 方式触发部署。
## 🚀 配置步骤
### 第一步:在服务器上配置部署脚本
SSH 登录服务器后执行:
```bash
# 1. 克隆代码(如果还没有)
cd /www/dk_project/dk_app
git clone https://gitea.pinzhuhui.com/xiaoquan/rent.git
cd rent
# 2. 配置 Git 凭证
git config credential.helper store
git pull origin test # 输入用户名和 Gitea Token
# 3. 赋予部署脚本执行权限
chmod +x deploy/scripts/deploy.sh
# 4. 创建环境变量文件
cat > /root/.env.test << 'EOF'
export TEST_DB_PASSWORD="your_password"
export TEST_JWT_SECRET="your_jwt_secret"
export ENCRYPTION_KEY="your_encryption_key"
EOF
cat > /root/.env.prod << 'EOF'
export PROD_DB_PASSWORD="your_password"
export PROD_JWT_SECRET="your_jwt_secret"
export ENCRYPTION_KEY="your_encryption_key"
EOF
# 5. 测试部署脚本
/www/dk_project/dk_app/rent/deploy/scripts/deploy.sh test
```
### 第二步:配置 Webhook 服务
#### 方法1:使用 webhook (推荐)
```bash
# 安装 webhook
cd /root
wget https://github.com/adnanh/webhook/releases/download/2.8.1/webhook-linux-amd64.tar.gz
tar -xzf webhook-linux-amd64.tar.gz
mv webhook-linux-amd64/webhook /usr/local/bin/
chmod +x /usr/local/bin/webhook
# 创建 webhook 配置
mkdir -p /root/webhooks
cat > /root/webhooks/hooks.json << 'EOF'
[
{
"id": "rent-deploy-test",
"execute-command": "/www/dk_project/dk_app/rent/deploy/scripts/deploy.sh",
"command-working-directory": "/www/dk_project/dk_app/rent",
"pass-arguments-to-command": [
{
"source": "string",
"name": "test"
}
],
"trigger-rule": {
"and": [
{
"match": {
"type": "payload-hash-sha256",
"secret": "your-webhook-secret",
"parameter": {
"source": "header",
"name": "X-Gitea-Signature"
}
}
},
{
"match": {
"type": "value",
"value": "refs/heads/test",
"parameter": {
"source": "payload",
"name": "ref"
}
}
}
]
}
},
{
"id": "rent-deploy-prod",
"execute-command": "/www/dk_project/dk_app/rent/deploy/scripts/deploy.sh",
"command-working-directory": "/www/dk_project/dk_app/rent",
"pass-arguments-to-command": [
{
"source": "string",
"name": "prod"
}
],
"trigger-rule": {
"and": [
{
"match": {
"type": "payload-hash-sha256",
"secret": "your-webhook-secret",
"parameter": {
"source": "header",
"name": "X-Gitea-Signature"
}
}
},
{
"match": {
"type": "value",
"value": "refs/heads/prod",
"parameter": {
"source": "payload",
"name": "ref"
}
}
}
]
}
}
]
EOF
# 启动 webhook 服务
nohup webhook -hooks /root/webhooks/hooks.json -verbose -port 9000 > /root/webhooks/webhook.log 2>&1 &
# 设置开机自启
cat > /etc/systemd/system/webhook.service << 'EOF'
[Unit]
Description=Webhook Service
After=network.target
[Service]
Type=simple
User=root
ExecStart=/usr/local/bin/webhook -hooks /root/webhooks/hooks.json -verbose -port 9000
Restart=always
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable webhook
systemctl start webhook
```
#### 方法2:使用简单的 PHP 脚本(如果有宝塔面板)
在宝塔面板创建一个站点,添加 `webhook.php`
```php
<?php
// webhook.php
$secret = 'your-webhook-secret';
$branch = $_POST['ref'] ?? '';
// 验证签名
$signature = $_SERVER['HTTP_X_GITEA_SIGNATURE'] ?? '';
$payload = file_get_contents('php://input');
$expected = hash_hmac('sha256', $payload, $secret);
if (!hash_equals($expected, $signature)) {
http_response_code(403);
die('Invalid signature');
}
// 执行部署
if ($branch === 'refs/heads/test') {
$output = shell_exec('/www/dk_project/dk_app/rent/deploy/scripts/deploy.sh test 2>&1');
} elseif ($branch === 'refs/heads/prod') {
$output = shell_exec('/www/dk_project/dk_app/rent/deploy/scripts/deploy.sh prod 2>&1');
} else {
http_response_code(400);
die('Unknown branch');
}
echo $output;
?>
```
### 第三步:在 Gitea 中配置 Webhook
1. 登录 Gitea
2. 进入仓库 → Settings → Webhooks
3. 点击 "添加 Webhook" → "Gitea"
4. 配置:
- **目标 URL**:
- webhook 方式:`http://your-server:9000/hooks/rent-deploy-test`
- PHP 方式:`http://your-server/webhook.php`
- **HTTP 方法**: POST
- **POST Content Type**: application/json
- **密钥**: `your-webhook-secret`(与上面配置的一致)
- **触发条件**: 勾选 "推送事件"
- **分支过滤**: `test``prod`
5. 点击 "添加 Webhook"
6. 点击 "测试推送" 验证
---
## 🔄 工作流程
```
1. git push origin test
2. Gitea 触发 Webhook
3. Webhook 服务调用 deploy.sh
4. 脚本自动:
- git pull 更新代码
- docker-compose build 构建
- docker-compose up 部署
5. 完成!
```
---
## 🔍 查看日志
### webhook 方式
```bash
tail -f /root/webhooks/webhook.log
```
### 手动测试
```bash
/www/dk_project/dk_app/rent/deploy/scripts/deploy.sh test
```
---
## ✅ 优点
- ✅ 不依赖 Runner 容器
- ✅ 直接在宿主机执行
- ✅ 简单可靠
- ✅ 容易调试
---
**维护者**:开发团队
**最后更新**2026-06-10
+81
View File
@@ -0,0 +1,81 @@
#!/bin/bash
# Gitea Webhook 部署脚本
# 用法:./deploy.sh <branch>
set -e
BRANCH=${1:-test}
PROJECT_DIR="/www/dk_project/dk_app/rent"
REPO_URL="https://gitea.pinzhuhui.com/xiaoquan/rent.git"
echo "========================================="
echo "开始部署 - 分支: $BRANCH"
echo "========================================="
# 1. 初始化或更新代码
if [ ! -d "$PROJECT_DIR" ]; then
echo "首次克隆代码..."
cd /www/dk_project/dk_app
git clone $REPO_URL
cd $PROJECT_DIR
else
echo "更新代码..."
cd $PROJECT_DIR
git fetch origin
git checkout $BRANCH
git pull origin $BRANCH
fi
echo "✅ 代码更新完成"
echo "当前分支: $(git branch --show-current)"
echo "最新提交: $(git log -1 --oneline)"
# 2. 部署
cd $PROJECT_DIR/deploy/docker
if [ "$BRANCH" == "test" ]; then
echo "========================================="
echo "部署到测试环境"
echo "========================================="
# 从环境变量或密钥文件读取
source /root/.env.test 2>/dev/null || true
docker-compose -f docker-compose.test.yml down --remove-orphans
docker-compose -f docker-compose.test.yml build --parallel
docker-compose -f docker-compose.test.yml up -d
docker image prune -f
echo "等待服务启动..."
sleep 10
docker-compose -f docker-compose.test.yml ps
echo "✅ 测试环境部署完成"
elif [ "$BRANCH" == "prod" ]; then
echo "========================================="
echo "部署到生产环境"
echo "========================================="
# 从环境变量或密钥文件读取
source /root/.env.prod 2>/dev/null || true
docker-compose -f docker-compose.prod.yml down --remove-orphans
docker-compose -f docker-compose.prod.yml build --parallel
docker-compose -f docker-compose.prod.yml up -d
docker image prune -f
echo "等待服务启动..."
sleep 10
docker-compose -f docker-compose.prod.yml ps
echo "✅ 生产环境部署完成"
else
echo "❌ 未知分支: $BRANCH"
exit 1
fi
echo "========================================="
echo "部署完成!"
echo "========================================="