diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml
new file mode 100644
index 0000000..01d2d1b
--- /dev/null
+++ b/.gitea/workflows/deploy.yml
@@ -0,0 +1,207 @@
+name: Build and Deploy
+
+on:
+ push:
+ branches:
+ - main
+ - master
+
+jobs:
+ build-and-deploy:
+ runs-on: runner
+ steps:
+ - name: Build and Deploy on Host
+ run: |
+ echo "=== Starting build and deploy ==="
+
+ # 检查并安装 Node.js 和 pnpm(容器内)
+ if ! command -v node &> /dev/null; then
+ echo "Installing Node.js in container..."
+ apk add --no-cache nodejs npm
+ fi
+
+ if ! command -v pnpm &> /dev/null; then
+ echo "Installing pnpm..."
+ npm install -g pnpm
+ fi
+
+ if ! command -v zip &> /dev/null; then
+ echo "Installing zip..."
+ apk add --no-cache zip
+ fi
+
+ if ! command -v scp &> /dev/null; then
+ echo "Installing openssh-client..."
+ apk add --no-cache openssh-client sshpass
+ fi
+
+ echo "Node version: $(node --version)"
+ echo "NPM version: $(npm --version)"
+ echo "PNPM version: $(pnpm --version)"
+
+ # 记录开始时间
+ START_TIME=$(date +%s)
+
+ WORK_DIR="/workspace/zy-client-a"
+ GITEA_REPO_PATH="/data/gitea/git/repositories/zy/zy-client-a.git"
+
+ # 初始化部署状态变量
+ DEPLOY_STATUS="success"
+ BUILT_PROJECTS=""
+
+ # 更新代码
+ if [ ! -d "$WORK_DIR/.git" ]; then
+ mkdir -p "$WORK_DIR"
+ git clone --depth 1 "file://$GITEA_REPO_PATH" "$WORK_DIR"
+ cd "$WORK_DIR"
+ OLD_HEAD=$(git rev-parse HEAD)
+ else
+ cd "$WORK_DIR"
+ OLD_HEAD=$(git rev-parse HEAD)
+ git fetch origin main
+ git reset --hard origin/main
+ fi
+
+ NEW_HEAD=$(git rev-parse HEAD)
+ CHANGED_FILES=$(git diff --name-only "$OLD_HEAD" "$NEW_HEAD" 2>/dev/null || git diff-tree --no-commit-id --name-only -r HEAD)
+
+ echo "Changed files: $CHANGED_FILES"
+ CHANGED_DIRS=$(echo "$CHANGED_FILES" | cut -d'/' -f1 | sort -u)
+
+ # 清空zip目录,确保只上传本次构建的文件
+ rm -rf zip
+ mkdir -p zip
+
+ # 构建项目
+ for dir in $CHANGED_DIRS; do
+ if [ -d "$dir" ] && [ -f "$dir/package.json" ]; then
+ echo "========================================="
+ echo "Building: $dir"
+ echo "========================================="
+
+ cd "$dir"
+
+ if ! pnpm install; then
+ echo "Error: pnpm install failed for $dir"
+ DEPLOY_STATUS="failed"
+ cd "$WORK_DIR"
+ continue
+ fi
+
+ if ! pnpm run build; then
+ echo "Error: pnpm run build failed for $dir"
+ DEPLOY_STATUS="failed"
+ cd "$WORK_DIR"
+ continue
+ fi
+
+ # 从 zip.js 提取 zip 文件名并打包 dist
+ if [ -f "zip.js" ] && [ -d "dist" ]; then
+ ZIP_NAME=$(grep -o 'zip/[^"]*\.zip' zip.js | head -1 | sed 's|zip/||')
+ if [ -n "$ZIP_NAME" ]; then
+ echo "Creating zip: $ZIP_NAME"
+ cd dist
+ if zip -r "../$ZIP_NAME" ./*; then
+ cd ..
+ mv "$ZIP_NAME" "$WORK_DIR/zip/$ZIP_NAME"
+ echo "Zip created: $WORK_DIR/zip/$ZIP_NAME"
+ BUILT_PROJECTS="${BUILT_PROJECTS}${dir}:${ZIP_NAME};"
+ else
+ echo "Error: Failed to create zip for $dir"
+ DEPLOY_STATUS="failed"
+ cd ..
+ fi
+ fi
+ fi
+
+ cd "$WORK_DIR"
+ fi
+ done
+
+ # 上传
+ if ls zip/*.zip 1> /dev/null 2>&1; then
+ echo "Uploading..."
+
+ if command -v sshpass &> /dev/null; then
+ # 直接上传,scp会自动覆盖同名文件
+ if sshpass -p '${{ secrets.SSH_REMOTE }}' scp -P 22 -o StrictHostKeyChecking=no zip/*.zip root@74.50.99.168:/opt/1panel/www/sites/aoe.jtgapp.cc/index/page/; then
+ echo "Upload completed!"
+ else
+ echo "Error: Upload failed"
+ DEPLOY_STATUS="failed"
+ fi
+ else
+ # 降级:直接使用 scp(需要配置 SSH 密钥)
+ if scp -P 22 -o StrictHostKeyChecking=no zip/*.zip root@74.50.99.168:/opt/1panel/www/sites/aoe.jtgapp.cc/index/page/; then
+ echo "Upload completed!"
+ else
+ echo "Error: Upload failed"
+ DEPLOY_STATUS="failed"
+ fi
+ fi
+ fi
+
+ # 发送 Telegram 通知
+ END_TIME=$(date +%s)
+ DURATION=$((END_TIME - START_TIME))
+ DURATION_MIN=$((DURATION / 60))
+ DURATION_SEC=$((DURATION % 60))
+
+ if [ -n "${{ secrets.TELEGRAM_BOT_TOKEN }}" ] && [ -n "${{ secrets.TELEGRAM_CHAT_ID }}" ]; then
+ echo "Sending Telegram notification..."
+
+ # 构建消息
+ if [ "$DEPLOY_STATUS" = "success" ]; then
+ MESSAGE="🚀 部署通知%0A%0A"
+ MESSAGE="${MESSAGE}✅ 部署状态: 成功%0A%0A"
+ MESSAGE="${MESSAGE}📊 基本信息:%0A"
+ MESSAGE="${MESSAGE}• 分支: ${{ github.ref_name }}%0A"
+ MESSAGE="${MESSAGE}• 提交: ${NEW_HEAD:0:8}%0A"
+ MESSAGE="${MESSAGE}• 耗时: ${DURATION_MIN}分${DURATION_SEC}秒%0A"
+ MESSAGE="${MESSAGE}• 日期: $(date '+%Y-%m-%d %H:%M:%S')%0A%0A"
+
+ if [ -n "$BUILT_PROJECTS" ]; then
+ MESSAGE="${MESSAGE}📦 部署项目:%0A"
+ IFS=';' read -ra PROJECTS <<< "$BUILT_PROJECTS"
+ for entry in "${PROJECTS[@]}"; do
+ if [ -n "$entry" ]; then
+ PROJECT_NAME=$(echo "$entry" | cut -d':' -f1)
+ ZIP_NAME=$(echo "$entry" | cut -d':' -f2)
+ MESSAGE="${MESSAGE}• ${PROJECT_NAME}: ${ZIP_NAME}%0A"
+ fi
+ done
+ fi
+ else
+ MESSAGE="🚀 部署通知%0A%0A"
+ MESSAGE="${MESSAGE}❌ 部署状态: 失败%0A%0A"
+ MESSAGE="${MESSAGE}📊 基本信息:%0A"
+ MESSAGE="${MESSAGE}• 分支: ${{ github.ref_name }}%0A"
+ MESSAGE="${MESSAGE}• 提交: ${NEW_HEAD:0:8}%0A"
+ MESSAGE="${MESSAGE}• 耗时: ${DURATION_MIN}分${DURATION_SEC}秒%0A"
+ MESSAGE="${MESSAGE}• 日期: $(date '+%Y-%m-%d %H:%M:%S')%0A%0A"
+ MESSAGE="${MESSAGE}⚠️ 请检查构建日志获取详细错误信息"
+ fi
+
+ # 发送到 Telegram(最多重试3次)
+ for i in 1 2 3; do
+ RESPONSE=$(wget -qO- --post-data="chat_id=${{ secrets.TELEGRAM_CHAT_ID }}&text=${MESSAGE}&parse_mode=HTML" \
+ "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" 2>&1)
+
+ if echo "$RESPONSE" | grep -q '"ok":true'; then
+ echo "Telegram notification sent successfully"
+ break
+ else
+ echo "Failed to send Telegram notification (attempt $i/3)"
+ if [ $i -lt 3 ]; then
+ sleep 3
+ fi
+ fi
+ done
+ else
+ echo "Skipping Telegram notification (secrets not configured)"
+ fi
+
+ # 如果部署失败,退出码为1
+ if [ "$DEPLOY_STATUS" = "failed" ]; then
+ exit 1
+ fi