diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 93d8f73..32ef27b 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -867,6 +867,12 @@ jobs: # 只有"换模板 / 再加一种通知"时才需要在这里给一个 Secret。 # ⚠️ 少了这一行映射,下面 upsert_env_key 拿到的永远是空值(设了 Secret 也不生效)。 WECHAT_MINI_SUBSCRIBE_TEMPLATES: ${{ secrets.WECHAT_MINI_SUBSCRIBE_TEMPLATES }} + # 可选:酷狗凭证兜底(配一次永久用,不必进小程序粘贴 Cookie)。 + # 库里 bind-cookie 绑定的值优先;这里只在"库里没有"时生效。 + # ⚠️ 同样必须同时在【本 env:】【ENV_PAYLOAD 的 export 列表】两处出现, + # 远端脚本是 set -euxo pipefail,少一处就是 unbound variable 直接失败。 + KUGOU_ADMIN_COOKIE: ${{ secrets.KUGOU_ADMIN_COOKIE }} + KUGOU_ADMIN_TOKEN: ${{ secrets.KUGOU_ADMIN_TOKEN }} run: | set -euo pipefail IMAGE_NEW="${IMAGE_NEW,,}" @@ -899,7 +905,7 @@ jobs: ssh "${SSH_OPTS[@]}" "${DEPLOY_USER}@${DEPLOY_HOST}" "whoami; docker --version; docker compose version" ENV_SH="/tmp/deploy_env_$$" - ENV_PAYLOAD="$(printf 'export IMAGE_NEW=%q\nexport IMAGE_LATEST=%q\nexport DEPLOY_PATH=%q\nexport GITEA_HOST=%q\nexport GH_PROXY_PREFIX=%q\nexport REGISTRY_PULL_USER=%q\nexport REGISTRY_PULL_TOKEN=%q\nexport IMAGE_NAME=%q\nexport NUXT_PUBLIC_SITE_URL=%q\nexport GITHUB_SERVER_URL=%q\nexport GITHUB_REPOSITORY=%q\nexport GITHUB_REF_NAME=%q\nexport OAUTH_REDIRECT_URI=%q\nexport OAUTH_STATE_SECRET=%q\nexport AGGREGATE_OAUTH_APP_ID=%q\nexport AGGREGATE_OAUTH_APP_KEY=%q\nexport AGGREGATE_OAUTH_LOGIN_TYPE=%q\nexport AGGREGATE_OAUTH_ENDPOINT=%q\nexport WECHAT_MINI_APPID=%q\nexport WECHAT_MINI_SECRET=%q\nexport WECHAT_MINI_SUBSCRIBE_TEMPLATES=%q\n' \ + ENV_PAYLOAD="$(printf 'export IMAGE_NEW=%q\nexport IMAGE_LATEST=%q\nexport DEPLOY_PATH=%q\nexport GITEA_HOST=%q\nexport GH_PROXY_PREFIX=%q\nexport REGISTRY_PULL_USER=%q\nexport REGISTRY_PULL_TOKEN=%q\nexport IMAGE_NAME=%q\nexport NUXT_PUBLIC_SITE_URL=%q\nexport GITHUB_SERVER_URL=%q\nexport GITHUB_REPOSITORY=%q\nexport GITHUB_REF_NAME=%q\nexport OAUTH_REDIRECT_URI=%q\nexport OAUTH_STATE_SECRET=%q\nexport AGGREGATE_OAUTH_APP_ID=%q\nexport AGGREGATE_OAUTH_APP_KEY=%q\nexport AGGREGATE_OAUTH_LOGIN_TYPE=%q\nexport AGGREGATE_OAUTH_ENDPOINT=%q\nexport WECHAT_MINI_APPID=%q\nexport WECHAT_MINI_SECRET=%q\nexport WECHAT_MINI_SUBSCRIBE_TEMPLATES=%q\nexport KUGOU_ADMIN_COOKIE=%q\nexport KUGOU_ADMIN_TOKEN=%q\n' \ "${IMAGE_NEW}" "${IMAGE_LATEST}" "${DEPLOY_PATH}" "${GITEA_HOST}" "${GH_PROXY_PREFIX}" \ "${REGISTRY_PULL_USER}" "${REGISTRY_PULL_TOKEN}" "${IMAGE_NAME}" "${NUXT_PUBLIC_SITE_URL}" \ "${GITHUB_SERVER_URL}" "${GITHUB_REPOSITORY}" "${GITHUB_REF_NAME}" \ @@ -907,7 +913,8 @@ jobs: "${AGGREGATE_OAUTH_APP_ID}" "${AGGREGATE_OAUTH_APP_KEY}" \ "${AGGREGATE_OAUTH_LOGIN_TYPE}" "${AGGREGATE_OAUTH_ENDPOINT}" \ "${WECHAT_MINI_APPID}" "${WECHAT_MINI_SECRET}" \ - "${WECHAT_MINI_SUBSCRIBE_TEMPLATES:-}")" + "${WECHAT_MINI_SUBSCRIBE_TEMPLATES:-}" \ + "${KUGOU_ADMIN_COOKIE:-}" "${KUGOU_ADMIN_TOKEN:-}")" printf '%s' "${ENV_PAYLOAD}" | ssh "${SSH_OPTS[@]}" "${DEPLOY_USER}@${DEPLOY_HOST}" "cat > ${ENV_SH}" echo "[Deploy] env script written to ${ENV_SH} on remote" ssh "${SSH_OPTS[@]}" "${DEPLOY_USER}@${DEPLOY_HOST}" "set -e; ls -la ${ENV_SH}; grep -q IMAGE_NEW ${ENV_SH} && echo '[Deploy] env_sh IMAGE_NEW line OK'" @@ -998,6 +1005,10 @@ jobs: # 远端脚本是 `set -euxo pipefail`,只在一处声明 → 另一端就是 unbound variable, # 整个部署会在这里挂掉(第 90 轮踩过)。这里再补 `:-` 兜底。 upsert_env_key WECHAT_MINI_SUBSCRIBE_TEMPLATES "${WECHAT_MINI_SUBSCRIBE_TEMPLATES:-}" || MERGE_OK=0 + # 酷狗凭证兜底(可选):配一次永久用;库里 bind-cookie 绑定的值优先。 + # 同样 `:-` 兜底,Secret 没配也不会因为 set -u 挂掉部署。 + upsert_env_key KUGOU_ADMIN_COOKIE "${KUGOU_ADMIN_COOKIE:-}" || MERGE_OK=0 + upsert_env_key KUGOU_ADMIN_TOKEN "${KUGOU_ADMIN_TOKEN:-}" || MERGE_OK=0 rm -f "${ENV_FILE}.tmp.$$" if [ "${MERGE_OK}" = "1" ]; then echo "[Runtime-env] 运行期配置已合并写入 ${ENV_FILE}(OAuth=${AGGREGATE_OAUTH_APP_ID:+已配置} WECHAT_MINI_APPID=${WECHAT_MINI_APPID:+已配置})" diff --git a/server/api/admin/kugou/status.get.ts b/server/api/admin/kugou/status.get.ts index 0cfa1e6..7148ab5 100644 --- a/server/api/admin/kugou/status.get.ts +++ b/server/api/admin/kugou/status.get.ts @@ -22,6 +22,8 @@ export default defineEventHandler(async (event) => { let serviceOk: boolean let serviceMessage: string let loginStatus: any = null + const envCookie = !!(process.env.KUGOU_ADMIN_COOKIE || '').trim() + const envToken = !!(process.env.KUGOU_ADMIN_TOKEN || '').trim() try { const r = await kuGouFetch('/login/status', { timestamp: Date.now() }) loginStatus = r @@ -41,9 +43,12 @@ export default defineEventHandler(async (event) => { serviceMessage, loginStatus, }, + // 环境变量兜底(KUGOU_ADMIN_COOKIE / KUGOU_ADMIN_TOKEN):配一次永久用;库里绑定的优先 bound: { - hasToken: !!settings?.kugouAdminToken, - hasCookie: !!settings?.kugouAdminCookie, + hasToken: !!settings?.kugouAdminToken || envToken, + hasCookie: !!settings?.kugouAdminCookie || envCookie, + // 让界面能说清"这份凭证从哪来",排错时很关键 + source: settings?.kugouAdminCookie || settings?.kugouAdminToken ? 'db' : envCookie || envToken ? 'env' : 'none', boundAt: settings?.kugouAdminBoundAt || null, user: settings?.kugouAdminUser || null, }, diff --git a/server/services/kugouService.ts b/server/services/kugouService.ts index 1bb19e8..5fd96f4 100644 --- a/server/services/kugouService.ts +++ b/server/services/kugouService.ts @@ -64,7 +64,14 @@ export async function kuGouFetch( 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128 Safari/537.36', } - const cookie = options?.forceCookie || settings?.kugouAdminCookie + /* + * 凭证优先级:显式传入(bind-cookie 校验用) → 库里绑定的 → **环境变量兜底**。 + * + * 加环境变量这一层是为了"配一次永久用":站长可以只在服务器 .env(或 Gitea Secrets)里 + * 放一次 KUGOU_ADMIN_COOKIE,就不必再进小程序粘贴;库里绑定的值优先,所以 + * 之后想临时换一份 Cookie,仍然可以在后台粘一次覆盖。 + */ + const cookie = options?.forceCookie || settings?.kugouAdminCookie || process.env.KUGOU_ADMIN_COOKIE || '' if (cookie) headers.Cookie = cookie const url = base + path return await ofetch(url, { @@ -73,7 +80,8 @@ export async function kuGouFetch( retry: 1, timeout: 20000, onRequest({ options }) { - const token = options?.forceToken || settings?.kugouAdminToken + const token = + options?.forceToken || settings?.kugouAdminToken || process.env.KUGOU_ADMIN_TOKEN || '' if (token && options.query) { ;(options.query as any).token = token }