chore: drop migration leftovers, refresh env examples + secret-leak scope

- remove MONGODB_URI from .env.example (Atlas migration done; deleted from
  Vercel cloud env too)
- trim .env.deploy.example to vars actually consumed by deploy scripts
  (Upstash creds were only needed by the now-deleted migration script)
- README config table: drop ENV / SOURCE_COMMIT / SCHEDULE_CHECK_APP_TIME
  (never read by code; Java-era leftovers)
- check-secret-leaks: drop MONGODB_URI; add UPSTASH/KV/CRON tokens; widen
  scan roots to include api/
- add scripts/list-upstash-keys.js read-only ops helper
This commit is contained in:
2026-05-10 00:23:16 +07:00
parent 49726f14c1
commit eb0f79be82
5 changed files with 58 additions and 21 deletions
+4 -11
View File
@@ -1,18 +1,11 @@
# Operator-only file: holds the credentials the deploy + migrate scripts need.
# Operator-only file: holds the credentials the deploy scripts need.
# Copy to .env.deploy and fill in. NEVER commit .env.deploy.
# Telegram (must match values set as Vercel env vars)
# Telegram (must match values set as Vercel env vars).
# Used by: scripts/register-webhook.js, scripts/set-bot-description.js
TELEGRAM_BOT_TOKEN=
TELEGRAM_WEBHOOK_SECRET=
# Where Telegram should send webhook updates after Phase 6 cutover.
# Where Telegram should send webhook updates.
# Format: https://<your-vercel-url>/api/webhook
# Variable name kept as WORKER_URL for register-webhook.js compatibility.
WORKER_URL=https://store-scraper-bot.vercel.app/api/webhook
# Upstash Redis credentials — required by migrate-atlas-to-upstash.js.
# If the Vercel Marketplace Upstash integration is set up, the bot also
# accepts KV_REST_API_URL / KV_REST_API_TOKEN as fallbacks (see upstash.js).
UPSTASH_REDIS_REST_URL=
UPSTASH_REDIS_REST_TOKEN=
KEY_PREFIX=store-scraper-bot:
-3
View File
@@ -19,6 +19,3 @@ CRON_SECRET=generate_another_random_string_at_least_32_chars
ADMIN_IDS=123456789,987654321
APP_CACHE_SECONDS=600
NUM_DAYS_WARNING_NOT_UPDATED=30
# One-shot migration only (Phase 5: legacy Atlas → Upstash). Remove after migration done.
MONGODB_URI=mongodb+srv://user:pass@cluster.mongodb.net/dbname
+2 -5
View File
@@ -36,13 +36,10 @@ Vercel env vars:
| `UPSTASH_REDIS_REST_TOKEN` | Upstash REST token (or `KV_REST_API_TOKEN` fallback) |
| `KEY_PREFIX` | Namespace for all Redis keys (default `store-scraper-bot:`) |
| `CRON_SECRET` | ≥32 chars random; required by Vercel Cron handler |
| `ENV` | `DEVELOPMENT` or `PRODUCTION` |
| `SOURCE_COMMIT` | Optional; shown on startup |
| `APP_CACHE_SECONDS` | Cache TTL for upstream API responses (default 600) |
| `NUM_DAYS_WARNING_NOT_UPDATED` | Threshold for daily warning (default 30) |
| `SCHEDULE_CHECK_APP_TIME` | Cron expression in Vietnam timezone (default `0 7 * * *`) |
| `NUM_DAYS_WARNING_NOT_UPDATED` | Default warning threshold in days (default 30; per-group override via `/setdayswarning`) |
Operator-only `.env.deploy` (used by `npm run register`) — see `.env.deploy.example`.
Operator-only `.env.deploy` (used by `npm run register` + `npm run describe`) — see `.env.deploy.example`.
## Run
+9 -2
View File
@@ -4,8 +4,15 @@
import { readdirSync, readFileSync, statSync } from 'node:fs';
import { join } from 'node:path';
const SECRETS = ['MONGODB_URI', 'TELEGRAM_BOT_TOKEN', 'TELEGRAM_WEBHOOK_SECRET', 'ADMIN_IDS'];
const ROOTS = ['src', 'scripts'];
const SECRETS = [
'TELEGRAM_BOT_TOKEN',
'TELEGRAM_WEBHOOK_SECRET',
'UPSTASH_REDIS_REST_TOKEN',
'KV_REST_API_TOKEN',
'CRON_SECRET',
'ADMIN_IDS',
];
const ROOTS = ['src', 'scripts', 'api'];
function* walk(dir) {
let entries;
+43
View File
@@ -0,0 +1,43 @@
#!/usr/bin/env node
// One-shot inspector: lists every Redis key the bot can see.
// Run via: node --env-file=.env.deploy scripts/list-upstash-keys.js
// Read-only.
import { Redis } from '@upstash/redis';
const url = process.env.UPSTASH_REDIS_REST_URL ?? process.env.KV_REST_API_URL;
const token = process.env.UPSTASH_REDIS_REST_TOKEN ?? process.env.KV_REST_API_TOKEN;
const prefix = process.env.KEY_PREFIX ?? 'store-scraper-bot:';
if (!url || !token) {
console.error('Upstash credentials not found in env');
process.exit(1);
}
const redis = new Redis({ url, token });
let cursor = 0;
const keys = [];
do {
const [next, batch] = await redis.scan(cursor, { match: '*', count: 100 });
cursor = Number(next);
keys.push(...batch);
} while (cursor !== 0);
keys.sort();
const inPrefix = keys.filter((k) => k.startsWith(prefix));
const orphan = keys.filter((k) => !k.startsWith(prefix));
console.log(`KEY_PREFIX: ${prefix}`);
console.log(`Total keys: ${keys.length}`);
console.log(`In-prefix: ${inPrefix.length}`);
console.log(`Orphan: ${orphan.length}`);
console.log();
console.log('In-prefix keys:');
for (const k of inPrefix) console.log(` ${k}`);
if (orphan.length) {
console.log();
console.log('Orphan keys (NOT under KEY_PREFIX):');
for (const k of orphan) console.log(` ${k}`);
}