mirror of
https://github.com/tiennm99/store-scraper-bot.git
synced 2026-10-17 02:14:40 +00:00
feat: migrate to Vercel + Upstash with KEY_PREFIX namespacing
Phases 1-5 of consolidate-vercel-upstash plan. Replaces Cloudflare Workers + KV with Vercel serverless functions + Upstash Redis. Inlines app-store-scraper / google-play-scraper npm libs (drops the store-scraper.vercel.app HTTP roundtrip). KEY_PREFIX (default 'store-scraper-bot:') namespaces all Redis keys so the Upstash DB can be safely shared with other Vercel projects. - vercel.json + .vercelignore + Vercel-aware package.json scripts - api/webhook.js + api/cron.js Vercel functions (with shared src/app-builder.js); cron auth fails closed when CRON_SECRET unset - src/repository/upstash.js replaces kv.js; all 4 repos take a handle bundling client + prefix - scripts/migrate-atlas-to-upstash.js writes legacy Java Atlas state directly to Upstash with --dry-run + --include-cache flags - .env.example refreshed for the new env surface Phases 6 (Vercel deploy + webhook cutover) and 7 (Docker + wrangler cleanup) remain operator-driven post-deploy.
This commit is contained in:
1 parent
cf17071679
commit
73762fa0a0
33 files changed
+4011
-488
No files matched your search
+32
@@ -0,0 +1,32 @@
|
||||
// Daily check entry. Triggered by Vercel Cron at 00:00 UTC = 07:00 Asia/Saigon
|
||||
// (schedule lives in vercel.json). Replaces the prior Cloudflare Worker
|
||||
// `scheduled` handler. Validates the Authorization: Bearer $CRON_SECRET header
|
||||
// to prevent random POSTs from triggering the daily check.
|
||||
|
||||
import { buildApp } from '../src/app-builder.js';
|
||||
import { runDailyCheck } from '../src/scheduler/scheduler.js';
|
||||
|
||||
export const config = { runtime: 'nodejs', maxDuration: 60 };
|
||||
|
||||
export default async function handler(req) {
|
||||
// Fail closed if CRON_SECRET is unset — otherwise the comparison would be
|
||||
// `Bearer undefined`, which an attacker could replay as a literal string
|
||||
// and bypass auth.
|
||||
const expected = process.env.CRON_SECRET;
|
||||
const auth = req.headers.get('authorization');
|
||||
if (!expected || auth !== `Bearer ${expected}`) {
|
||||
return new Response('Unauthorized', { status: 401 });
|
||||
}
|
||||
|
||||
let app;
|
||||
try {
|
||||
app = buildApp(process.env);
|
||||
} catch (err) {
|
||||
console.log(JSON.stringify({ level: 'error', msg: 'config error', err: err.message }));
|
||||
return new Response('Server misconfigured', { status: 500 });
|
||||
}
|
||||
|
||||
// Cron runs synchronously up to maxDuration; no waitUntil needed.
|
||||
await runDailyCheck(app.config, app.store, app.sender, app.appleScraper, app.googleScraper);
|
||||
return new Response('OK');
|
||||
}
|
||||
Reference in new issue
Block a user