diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index ba394de..537b526 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -68,6 +68,8 @@ jobs: MINIO_BUCKET_WATER_LOGS: ${{ secrets.MINIO_BUCKET_WATER_LOGS }} MINIMAX_API_KEY: ${{ secrets.MINIMAX_API_KEY }} MINIMAX_BASE_URL: ${{ secrets.MINIMAX_BASE_URL }} + SMARTSHEET_TOKEN_ENC_KEY: ${{ secrets.SMARTSHEET_TOKEN_ENC_KEY }} + SMARTSHEET_CRON_SECRET: ${{ secrets.SMARTSHEET_CRON_SECRET }} run: npm run build - name: Deploy @@ -108,6 +110,8 @@ jobs: MINIMAX_API_KEY: ${{ secrets.MINIMAX_API_KEY }} MINIMAX_BASE_URL: ${{ secrets.MINIMAX_BASE_URL }} CRON_SECRET: ${{ secrets.CRON_SECRET }} + SMARTSHEET_TOKEN_ENC_KEY: ${{ secrets.SMARTSHEET_TOKEN_ENC_KEY }} + SMARTSHEET_CRON_SECRET: ${{ secrets.SMARTSHEET_CRON_SECRET }} SERVER_SSH_KEY: ${{ secrets.SERVER_SSH_KEY }} run: | set -e @@ -173,6 +177,8 @@ jobs: printf 'MINIMAX_API_KEY=%s\n' "$MINIMAX_API_KEY" printf 'MINIMAX_BASE_URL=%s\n' "$MINIMAX_BASE_URL" printf 'CRON_SECRET=%s\n' "$CRON_SECRET" + printf 'SMARTSHEET_TOKEN_ENC_KEY=%s\n' "$SMARTSHEET_TOKEN_ENC_KEY" + printf 'SMARTSHEET_CRON_SECRET=%s\n' "$SMARTSHEET_CRON_SECRET" } > "$ENV_FILE" # Upload env file and sync build output diff --git a/.gitea/workflows/smartsheet-cron.yml b/.gitea/workflows/smartsheet-cron.yml new file mode 100644 index 0000000..65a2fe1 --- /dev/null +++ b/.gitea/workflows/smartsheet-cron.yml @@ -0,0 +1,40 @@ +name: Smartsheet sync cron + +# Replaces the `vercel.json` cron entry (ignored on self-hosted Gitea). +# Drains the Smartsheet sync queue every 15 minutes via the running +# Next.js app's POST /api/water-log/smartsheet-sync endpoint. + +on: + schedule: + # Every 15 minutes. Gitea Actions uses standard 5-field cron syntax; + # `0,15,30,45 * * * *` is the explicit form to avoid `*/15` + # parser quirks across older Gitea versions. + - cron: "0,15,30,45 * * * *" + workflow_dispatch: + +jobs: + drain-sync-queue: + runs-on: ubuntu-latest + steps: + - name: Hit /api/water-log/smartsheet-sync + env: + SMARTSHEET_CRON_SECRET: ${{ secrets.SMARTSHEET_CRON_SECRET }} + SITE_URL: ${{ secrets.NEXT_PUBLIC_SITE_URL }} + run: | + set -euo pipefail + if [ -z "$SMARTSHEET_CRON_SECRET" ]; then + echo "SMARTSHEET_CRON_SECRET secret not set — skipping." + exit 0 + fi + if [ -z "$SITE_URL" ]; then + echo "NEXT_PUBLIC_SITE_URL secret not set — skipping." + exit 0 + fi + echo "POST $SITE_URL/api/water-log/smartsheet-sync" + curl -fsS --max-time 60 -X POST \ + -H "Authorization: Bearer $SMARTSHEET_CRON_SECRET" \ + -H "Content-Type: application/json" \ + -H "User-Agent: gitea-smartsheet-cron/1" \ + "${SITE_URL%/}/api/water-log/smartsheet-sync" \ + --data '{}' \ + -w "\n[http %{http_code} in %{time_total}s]\n" diff --git a/MEMORY.md b/MEMORY.md index 24e4371..e912f7c 100644 --- a/MEMORY.md +++ b/MEMORY.md @@ -747,3 +747,19 @@ c.commit() Gitea is on **SQLite** here (`/home/git/data/gitea.db`), not the `HOST = 127.0.0.1:3306` listed in app.ini (that's stale config from when the DB was MySQL). + +## 2026-07: Smartsheet cron now runs via Gitea Actions, not vercel.json + +The `*/15` cron entry in `vercel.json` is silently ignored because +the host is self-hosted Gitea, not Vercel. Replaced with +`.gitea/workflows/smartsheet-cron.yml`, schedule `0,15,30,45 * * * *`. + +`deploy.yml` now writes `SMARTSHEET_TOKEN_ENC_KEY` and +`SMARTSHEET_CRON_SECRET` into the runtime `.env`. Without this, +any future deploy would silently drop these secrets and break +runtime sync (encrypted token storage, cron auth). + +If the cron ever returns 401, check `SMARTSHEET_CRON_SECRET` +matches between Gitea repo secrets and the live `.env`. The +runtime route at `POST /api/water-log/smartsheet-sync` falls +back to `CRON_SECRET` if `SMARTSHEET_CRON_SECRET` is unset. diff --git a/vercel.json b/vercel.json index 0cc645b..07720d9 100644 --- a/vercel.json +++ b/vercel.json @@ -32,10 +32,6 @@ { "path": "/api/cron/send-scheduled", "schedule": "0 9 * * *" - }, - { - "path": "/api/water-log/smartsheet-sync", - "schedule": "*/15 * * * *" } ], "headers": [