diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 18358f4..64d7ae6 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -74,12 +74,20 @@ jobs: cp "$CONFIG_FILE" wrangler.toml fi + # 注入 Cloudflare Workers 名称 + - name: Inject Workers Name + env: + CONFIG_FILE: ${{ env.WRANGLER_CONFIG || 'wrangler.toml' }} + CLOUDFLARE_WORKERS_NAME: ${{ secrets.CLOUDFLARE_WORKERS_NAME || '2fauth-worker' }} + run: | + sed -i "0,/name =/s/name = \".*\"/name = \"$CLOUDFLARE_WORKERS_NAME\"/" "wrangler.toml" + # 注入 D1 数据库配置 - name: Inject D1 Database ID env: CONFIG_FILE: ${{ env.WRANGLER_CONFIG || 'wrangler.toml' }} CLOUDFLARE_D1_DATABASE_ID: ${{ secrets.CLOUDFLARE_D1_DATABASE_ID }} - CLOUDFLARE_D1_DATABASE_NAME: ${{ secrets.CLOUDFLARE_D1_DATABASE_NAME || '2fauth-db' }} + CLOUDFLARE_D1_DATABASE_NAME: ${{ secrets.CLOUDFLARE_D1_DATABASE_NAME || 'nodeauth-db' }} run: | sed -i '/^\[\[d1_databases\]\]/d; /^[[:space:]]*database_name[[:space:]]*=/d; /^[[:space:]]*database_id[[:space:]]*=/d; /^[[:space:]]*binding[[:space:]]*=[[:space:]]*"DB"/d' wrangler.toml if grep -q "\[env.dev\]" wrangler.toml; then @@ -93,7 +101,7 @@ jobs: with: apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - command: d1 execute ${{ secrets.CLOUDFLARE_D1_DATABASE_NAME || '2fauth-db' }} --remote --file=backend/schema.sql + command: d1 execute ${{ secrets.CLOUDFLARE_D1_DATABASE_NAME || 'nodeauth-db' }} --remote --file=backend/schema.sql # 动态生成 Secrets 列表 (仅包含已配置的变量) - name: Generate Secret List @@ -123,6 +131,8 @@ jobs: OAUTH_BAIDU_CLIENT_SECRET: ${{ secrets.OAUTH_BAIDU_CLIENT_SECRET }} OAUTH_DROPBOX_CLIENT_ID: ${{ secrets.OAUTH_DROPBOX_CLIENT_ID }} OAUTH_DROPBOX_CLIENT_SECRET: ${{ secrets.OAUTH_DROPBOX_CLIENT_SECRET }} + OAUTH_WALLETCONNECT_PROJECT_ID: ${{ secrets.OAUTH_WALLETCONNECT_PROJECT_ID }} + OAUTH_WALLETCONNECT_SELF_PROXY: ${{ secrets.OAUTH_WALLETCONNECT_SELF_PROXY }} run: | SECRETS=" @@ -141,6 +151,7 @@ jobs: "OAUTH_MICROSOFT_CLIENT_ID" "OAUTH_MICROSOFT_CLIENT_SECRET" "OAUTH_BAIDU_CLIENT_ID" "OAUTH_BAIDU_CLIENT_SECRET" "OAUTH_DROPBOX_CLIENT_ID" "OAUTH_DROPBOX_CLIENT_SECRET" + "OAUTH_WALLETCONNECT_PROJECT_ID" "OAUTH_WALLETCONNECT_SELF_PROXY" ) for VAR in "${OPTIONAL_VARS[@]}"; do @@ -195,6 +206,8 @@ jobs: OAUTH_BAIDU_CLIENT_SECRET: ${{ secrets.OAUTH_BAIDU_CLIENT_SECRET }} OAUTH_DROPBOX_CLIENT_ID: ${{ secrets.OAUTH_DROPBOX_CLIENT_ID }} OAUTH_DROPBOX_CLIENT_SECRET: ${{ secrets.OAUTH_DROPBOX_CLIENT_SECRET }} + OAUTH_WALLETCONNECT_PROJECT_ID: ${{ secrets.OAUTH_WALLETCONNECT_PROJECT_ID }} + OAUTH_WALLETCONNECT_SELF_PROXY: ${{ secrets.OAUTH_WALLETCONNECT_SELF_PROXY }} ENCRYPTION_KEY: ${{ secrets.ENCRYPTION_KEY }} JWT_SECRET: ${{ secrets.JWT_SECRET }} diff --git a/.github/workflows/rclone.yml b/.github/workflows/rclone.yml deleted file mode 100644 index d3f4278..0000000 --- a/.github/workflows/rclone.yml +++ /dev/null @@ -1,301 +0,0 @@ -name: Backup Cloudflare D1 Database And R2 Bucket via Rclone - -on: - schedule: - - cron: "0 5 * * *" - workflow_dispatch: - -jobs: - rclone: - name: Secure Backup Pipeline - runs-on: ubuntu-latest - permissions: - contents: read - - env: - # --- 全局配置 --- - TZ: 'Asia/Shanghai' - - # --- Cloudflare --- - CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} - CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - - # --- Cloudflare D1 Database --- - CLOUDFLARE_D1_DATABASE_ID: ${{ secrets.CLOUDFLARE_D1_DATABASE_ID }} - CLOUDFLARE_D1_DATABASE_NAME: ${{ secrets.CLOUDFLARE_D1_DATABASE_NAME }} - - # --- Cloudflare R2 Storage --- - CLOUDFLARE_R2_ACCESS_KEY_ID: ${{ secrets.CLOUDFLARE_R2_ACCESS_KEY_ID }} - CLOUDFLARE_R2_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_R2_SECRET_ACCESS_KEY }} - CLOUDFLARE_R2_BUCKET_NAME: ${{ secrets.CLOUDFLARE_R2_BUCKET_NAME }} - - # --- Cloudflare Zero Trust (Service Token) --- - CLOUDFLARE_ACCESS_CLIENT_ID: ${{ secrets.CLOUDFLARE_ACCESS_CLIENT_ID }} - CLOUDFLARE_ACCESS_CLIENT_SECRET: ${{ secrets.CLOUDFLARE_ACCESS_CLIENT_SECRET }} - - # --- Encryption and Retention Policy --- - BACKUP_ENCRYPTION_KEY: ${{ secrets.BACKUP_ENCRYPTION_KEY }} - RETENTION_DAYS: ${{ secrets.BACKUP_RETENTION_DAYS || 30 }} - - # --- WebDAV --- - WEBDAV_URL: ${{ secrets.WEBDAV_URL }} - WEBDAV_USER: ${{ secrets.WEBDAV_USER }} - WEBDAV_PASSWORD: ${{ secrets.WEBDAV_PASSWORD }} - WEBDAV_VENDOR: ${{ secrets.WEBDAV_VENDOR || 'other' }} - WEBDAV_BASE_PATH: ${{ secrets.WEBDAV_BASE_PATH }} - - # --- Google Drive --- - GDRIVE_TOKEN: ${{ secrets.GDRIVE_TOKEN }} - GDRIVE_CLIENT_ID: ${{ secrets.GDRIVE_CLIENT_ID }} - GDRIVE_CLIENT_SECRET: ${{ secrets.GDRIVE_CLIENT_SECRET }} - GDRIVE_ROOT_FOLDER_ID: ${{ secrets.GDRIVE_ROOT_FOLDER_ID }} - - # --- 工具版本 --- - WRANGLER_VERSION: "3.57.1" - - steps: - # STEP 1: 安装 Rclone - - name: 🛠️ Setup Tools - uses: AnimMouse/setup-rclone@02a3642a1ad72d5b765f48873141b9f26e9082f1 # v1 - with: - version: v1.66.0 - - # STEP 2: 检查配置并注入环境变量 - # 将 Rclone 配置直接写入环境变量 (内存配置),不生成实体配置文件 - - name: 🛡️ Pre-flight Check & Config Injection - id: config - run: | - # 初始化目标列表 - TARGETS="" - - # 获取仓库名称 (用于默认路径) - REPO_NAME="${GITHUB_REPOSITORY#*/}" - - # --- 1. 检查 Cloudflare 凭据 --- - if [[ -z "$CLOUDFLARE_ACCOUNT_ID" || -z "$CLOUDFLARE_API_TOKEN" ]]; then - echo "::error::Missing Cloudflare credentials!" - exit 1 - fi - - # --- 2. 配置 WebDAV --- - if [[ -n "$WEBDAV_URL" && -n "$WEBDAV_USER" && -n "$WEBDAV_PASSWORD" ]]; then - echo "✅ WebDAV Detected: Enabling..." - - TARGETS+="remote_webdav " - - # 注入 Rclone WebDAV 配置 - echo "RCLONE_CONFIG_REMOTE_WEBDAV_TYPE=webdav" >> $GITHUB_ENV - echo "RCLONE_CONFIG_REMOTE_WEBDAV_URL=$WEBDAV_URL" >> $GITHUB_ENV - echo "RCLONE_CONFIG_REMOTE_WEBDAV_VENDOR=$WEBDAV_VENDOR" >> $GITHUB_ENV - echo "RCLONE_CONFIG_REMOTE_WEBDAV_USER=$WEBDAV_USER" >> $GITHUB_ENV - - # 设置 WebDAV 默认路径 - if [[ -z "$WEBDAV_BASE_PATH" ]]; then - WEBDAV_BASE_PATH="$REPO_NAME" - echo "WEBDAV_BASE_PATH=$REPO_NAME" >> $GITHUB_ENV - echo "ℹ️ WEBDAV_BASE_PATH auto-set to: $REPO_NAME" - fi - - # 加密 WebDAV 密码 - OBSCURED=$(rclone obscure "$WEBDAV_PASSWORD") - echo "RCLONE_CONFIG_REMOTE_WEBDAV_PASS=$OBSCURED" >> $GITHUB_ENV - - # 配置 Cloudflare Access 请求头 - if [[ -n "$CLOUDFLARE_ACCESS_CLIENT_ID" && -n "$CLOUDFLARE_ACCESS_CLIENT_SECRET" ]]; then - echo "🛡️ Cloudflare Access Token Detected: Configuring headers..." - echo "RCLONE_CONFIG_REMOTE_WEBDAV_HEADERS=CF-Access-Client-Id,$CLOUDFLARE_ACCESS_CLIENT_ID,CF-Access-Client-Secret,$CLOUDFLARE_ACCESS_CLIENT_SECRET" >> $GITHUB_ENV - fi - fi - - # --- 3. 配置 Google Drive --- - if [[ -n "$GDRIVE_TOKEN" && -n "$GDRIVE_CLIENT_ID" && -n "$GDRIVE_CLIENT_SECRET" ]]; then - echo "✅ GDrive Detected: Enabling..." - TARGETS+="remote_gdrive " - - echo "RCLONE_CONFIG_REMOTE_GDRIVE_TYPE=drive" >> $GITHUB_ENV - echo "RCLONE_CONFIG_REMOTE_GDRIVE_SCOPE=drive" >> $GITHUB_ENV - echo "RCLONE_CONFIG_REMOTE_GDRIVE_TOKEN=$GDRIVE_TOKEN" >> $GITHUB_ENV - echo "RCLONE_CONFIG_REMOTE_GDRIVE_CLIENT_ID=$GDRIVE_CLIENT_ID" >> $GITHUB_ENV - echo "RCLONE_CONFIG_REMOTE_GDRIVE_CLIENT_SECRET=$GDRIVE_CLIENT_SECRET" >> $GITHUB_ENV - - if [[ -n "$GDRIVE_ROOT_FOLDER_ID" ]]; then - echo "RCLONE_CONFIG_REMOTE_GDRIVE_ROOT_FOLDER_ID=$GDRIVE_ROOT_FOLDER_ID" >> $GITHUB_ENV - echo "GDRIVE_BASE_PATH=" >> $GITHUB_ENV - else - # 若未设置 Root Folder ID,使用仓库名作为子目录 - echo "GDRIVE_BASE_PATH=$REPO_NAME/" >> $GITHUB_ENV - fi - fi - - # --- 4. 配置 Cloudflare R2 源 --- - if [[ -n "$CLOUDFLARE_R2_ACCESS_KEY_ID" && -n "$CLOUDFLARE_R2_SECRET_ACCESS_KEY" ]]; then - echo "✅ Cloudflare R2 Credentials Detected: Configuring..." - - echo "RCLONE_CONFIG_REMOTE_R2_TYPE=s3" >> $GITHUB_ENV - echo "RCLONE_CONFIG_REMOTE_R2_PROVIDER=Cloudflare" >> $GITHUB_ENV - echo "RCLONE_CONFIG_REMOTE_R2_ACCESS_KEY_ID=$CLOUDFLARE_R2_ACCESS_KEY_ID" >> $GITHUB_ENV - echo "RCLONE_CONFIG_REMOTE_R2_SECRET_ACCESS_KEY=$CLOUDFLARE_R2_SECRET_ACCESS_KEY" >> $GITHUB_ENV - echo "RCLONE_CONFIG_REMOTE_R2_ENDPOINT=https://$CLOUDFLARE_ACCOUNT_ID.r2.cloudflarestorage.com" >> $GITHUB_ENV - fi - - # --- 5. 检查是否有可用备份目标 --- - if [[ -z "$TARGETS" ]]; then - echo "::error::No backup targets configured! Aborting." - exit 1 - fi - - # 输出目标列表 - echo "BACKUP_TARGETS=$TARGETS" >> $GITHUB_ENV - - # STEP 3: 导出并处理 D1 数据库 - - name: 📦 Export & Encrypt Cloudflare D1 Database - run: | - # 安装 Wrangler - npm install -g wrangler@${{ env.WRANGLER_VERSION }} --quiet - - # 获取数据库名称 (优先使用配置名,否则通过 ID 查询) - if [[ -n "$CLOUDFLARE_D1_DATABASE_NAME" ]]; then - echo "✅ Using configured D1 Database Name: $CLOUDFLARE_D1_DATABASE_NAME" - DB_NAME="$CLOUDFLARE_D1_DATABASE_NAME" - elif [[ -n "$CLOUDFLARE_D1_DATABASE_ID" ]]; then - echo "🔍 Resolving D1 Database Name from ID: $CLOUDFLARE_D1_DATABASE_ID" - DB_NAME=$(npx wrangler d1 list --json | jq -r ".[] | select(.uuid == \"$CLOUDFLARE_D1_DATABASE_ID\") | .name") - if [[ -z "$DB_NAME" || "$DB_NAME" == "null" ]]; then - echo "::error::Could not resolve D1 Name from UUID. Please check ID or configure CLOUDFLARE_D1_DATABASE_NAME." - exit 1 - fi - echo "CLOUDFLARE_D1_DATABASE_NAME=$DB_NAME" >> $GITHUB_ENV - else - echo "::error::Neither CLOUDFLARE_D1_DATABASE_NAME nor CLOUDFLARE_D1_DATABASE_ID is configured!" - exit 1 - fi - - echo "🚀 Exporting: $DB_NAME" - npx wrangler d1 export "$DB_NAME" --remote --output=backup.sql - - echo "🗜️ Compressing..." - gzip -9 backup.sql - - # 生成带时间戳的文件名 - DATE=$(date +'%Y-%m-%d_%H-%M-%S') - - if [[ -n "$BACKUP_ENCRYPTION_KEY" ]]; then - echo "🔒 Encrypting (AES-256)..." - FILENAME="backup_${DATE}.sql.gz.enc" - openssl enc -aes-256-cbc -salt -pbkdf2 -iter 100000 \ - -in backup.sql.gz -out "$FILENAME" -pass pass:"$BACKUP_ENCRYPTION_KEY" - else - echo "⚠️ Encryption skipped (Key not set)" - FILENAME="backup_${DATE}.sql.gz" - mv backup.sql.gz "$FILENAME" - fi - - echo "FILENAME=$FILENAME" >> $GITHUB_ENV - - # STEP 4: 同步 D1 备份文件 - - name: 🔄 Sync Cloudflare D1 Database to Cloud - run: | - # 允许单次失败,避免中断整个流程 - set +e - - OVERALL_STATUS=0 - - for target in $BACKUP_TARGETS; do - echo "---------------------------------------------------" - echo "📡 Processing Remote: [${target}]" - - # --- 确定目标路径 --- - if [[ "$target" == "remote_webdav" ]]; then - # WebDAV: 使用 d1-backup 子目录,防止误删 - DEST_PATH="${target}:${WEBDAV_BASE_PATH}/d1-backup/${CLOUDFLARE_D1_DATABASE_NAME}/${FILENAME}" - PURGE_PATH="${target}:${WEBDAV_BASE_PATH}/d1-backup/${CLOUDFLARE_D1_DATABASE_NAME}/" - else - # GDrive: 使用 d1-backup 子目录 - DEST_PATH="${target}:${GDRIVE_BASE_PATH}d1-backup/${CLOUDFLARE_D1_DATABASE_NAME}/${FILENAME}" - PURGE_PATH="${target}:${GDRIVE_BASE_PATH}d1-backup/${CLOUDFLARE_D1_DATABASE_NAME}/" - fi - - # --- 1. 执行上传 --- - echo " 📤 Uploading..." - rclone --config /dev/null copyto "$FILENAME" "$DEST_PATH" --retries 3 --user-agent "Mozilla/5.0" - - if [ $? -ne 0 ]; then - echo "::error::❌ Failed to upload to $target" - OVERALL_STATUS=1 - continue # 跳过当前目标的后续步骤,继续下一个目标 - fi - - # --- 2. 清理旧备份 --- - echo " 🧹 Pruning old backups (> ${RETENTION_DAYS}d)..." - rclone --config /dev/null delete "$PURGE_PATH" --min-age ${RETENTION_DAYS}d -v - - if [ $? -ne 0 ]; then - echo "::warning::⚠️ Cleanup failed for $target, but upload was successful." - # 清理失败不算核心失败,不修改 OVERALL_STATUS - fi - - echo " ✅ Success: $target" - done - - echo "---------------------------------------------------" - - # 检查整体状态 - if [ $OVERALL_STATUS -ne 0 ]; then - echo "::error::One or more backup targets failed." - exit 1 - fi - - echo "🎉 D1 backup tasks completed successfully." - - # STEP 5: 同步 R2 存储桶 - - name: 🔄 Sync Cloudflare R2 Bucket to Cloud - # R2 配置存在且有目标时运行 (即使 D1 失败也执行) - if: ${{ !cancelled() && env.CLOUDFLARE_R2_BUCKET_NAME != '' && env.CLOUDFLARE_R2_ACCESS_KEY_ID != '' && env.BACKUP_TARGETS != '' }} - run: | - # 允许单次失败 - set +e - - OVERALL_STATUS=0 - - echo "---------------------------------------------------" - echo "📡 Syncing R2 Bucket: [$CLOUDFLARE_R2_BUCKET_NAME]" - - for target in $BACKUP_TARGETS; do - echo " ➡️ Target: $target" - - # --- 确定目标路径 --- - if [[ "$target" == "remote_webdav" ]]; then - DEST_PATH="${target}:${WEBDAV_BASE_PATH}/r2-backup/${CLOUDFLARE_R2_BUCKET_NAME}" - else - DEST_PATH="${target}:${GDRIVE_BASE_PATH}r2-backup/${CLOUDFLARE_R2_BUCKET_NAME}" - fi - - rclone --config /dev/null sync "remote_r2:$CLOUDFLARE_R2_BUCKET_NAME" "$DEST_PATH" \ - --verbose \ - --transfers 16 \ - --checkers 8 \ - --retries 3 \ - --user-agent "Mozilla/5.0" - - if [ $? -ne 0 ]; then - echo "::error::❌ Failed to sync R2 to $target" - OVERALL_STATUS=1 - else - echo " ✅ Success: $target" - fi - done - - if [ $OVERALL_STATUS -ne 0 ]; then - echo "::error::One or more R2 sync targets failed." - exit 1 - fi - - echo "🎉 R2 Sync tasks completed." - - # STEP 6: 清理本地临时文件 - - name: 🧹 Clean up local temp files - if: ${{ always() }} - run: | - echo "Cleaning up local backup files..." - rm -f backup*.sql.gz* backup.sql - echo "✅ Cleanup done." \ No newline at end of file