feat(water-log): run Smartsheet sync via Gitea Actions cron + persist secrets on deploy
Deploy to route.crispygoat.com / deploy (push) Successful in 4m20s
Deploy to route.crispygoat.com / deploy (push) Successful in 4m20s
- New .gitea/workflows/smartsheet-cron.yml: hits POST /api/water-log/smartsheet-sync every 15min with SMARTSHEET_CRON_SECRET bearer. Replaces the no-op vercel.json cron entry (ignored on self-hosted Gitea). - deploy.yml now writes SMARTSHEET_TOKEN_ENC_KEY and SMARTSHEET_CRON_SECRET to the runtime .env on every deploy so we don't silently lose them. - Dropped the unused cron entry from vercel.json.
This commit is contained in:
@@ -68,6 +68,8 @@ jobs:
|
|||||||
MINIO_BUCKET_WATER_LOGS: ${{ secrets.MINIO_BUCKET_WATER_LOGS }}
|
MINIO_BUCKET_WATER_LOGS: ${{ secrets.MINIO_BUCKET_WATER_LOGS }}
|
||||||
MINIMAX_API_KEY: ${{ secrets.MINIMAX_API_KEY }}
|
MINIMAX_API_KEY: ${{ secrets.MINIMAX_API_KEY }}
|
||||||
MINIMAX_BASE_URL: ${{ secrets.MINIMAX_BASE_URL }}
|
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
|
run: npm run build
|
||||||
|
|
||||||
- name: Deploy
|
- name: Deploy
|
||||||
@@ -108,6 +110,8 @@ jobs:
|
|||||||
MINIMAX_API_KEY: ${{ secrets.MINIMAX_API_KEY }}
|
MINIMAX_API_KEY: ${{ secrets.MINIMAX_API_KEY }}
|
||||||
MINIMAX_BASE_URL: ${{ secrets.MINIMAX_BASE_URL }}
|
MINIMAX_BASE_URL: ${{ secrets.MINIMAX_BASE_URL }}
|
||||||
CRON_SECRET: ${{ secrets.CRON_SECRET }}
|
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 }}
|
SERVER_SSH_KEY: ${{ secrets.SERVER_SSH_KEY }}
|
||||||
run: |
|
run: |
|
||||||
set -e
|
set -e
|
||||||
@@ -173,6 +177,8 @@ jobs:
|
|||||||
printf 'MINIMAX_API_KEY=%s\n' "$MINIMAX_API_KEY"
|
printf 'MINIMAX_API_KEY=%s\n' "$MINIMAX_API_KEY"
|
||||||
printf 'MINIMAX_BASE_URL=%s\n' "$MINIMAX_BASE_URL"
|
printf 'MINIMAX_BASE_URL=%s\n' "$MINIMAX_BASE_URL"
|
||||||
printf 'CRON_SECRET=%s\n' "$CRON_SECRET"
|
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"
|
} > "$ENV_FILE"
|
||||||
|
|
||||||
# Upload env file and sync build output
|
# Upload env file and sync build output
|
||||||
|
|||||||
@@ -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"
|
||||||
@@ -747,3 +747,19 @@ c.commit()
|
|||||||
Gitea is on **SQLite** here (`/home/git/data/gitea.db`), not the
|
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
|
`HOST = 127.0.0.1:3306` listed in app.ini (that's stale config from
|
||||||
when the DB was MySQL).
|
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.
|
||||||
|
|||||||
@@ -32,10 +32,6 @@
|
|||||||
{
|
{
|
||||||
"path": "/api/cron/send-scheduled",
|
"path": "/api/cron/send-scheduled",
|
||||||
"schedule": "0 9 * * *"
|
"schedule": "0 9 * * *"
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/api/water-log/smartsheet-sync",
|
|
||||||
"schedule": "*/15 * * * *"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"headers": [
|
"headers": [
|
||||||
|
|||||||
Reference in New Issue
Block a user