A once-daily global aviation briefing built with Next.js, shadcn/ui, Firecrawl, Gemini, and Vercel Blob.
npm install
npm run devThe app uses the bundled preview edition when external services are not configured.
Copy .env.example to .env.local and provide:
FIRECRAWL_API_KEYGEMINI_API_KEYCRON_SECRETBLOB_READ_WRITE_TOKENfrom a Vercel Blob storeRESEND_API_KEYwith full access for contact-form delivery and newsletter signupsNEXT_PUBLIC_CLERK_PUBLISHABLE_KEYandCLERK_SECRET_KEYfrom ClerkSTRIPE_SECRET_KEY,STRIPE_PRO_PRICE_ID,STRIPE_PRO_YEARLY_PRICE_ID, andSTRIPE_WEBHOOK_SECRETfor the monthly and yearly recurring GBP Pro prices
Set role to admin in a user's Clerk public metadata to allow that user to open /admin and manage posts, subscriptions, and accounts.
Send checkout.session.completed, customer.subscription.updated, customer.subscription.deleted, invoice.paid, and invoice.payment_failed to /api/stripe/webhook, and enable the Stripe Customer Portal. The webhook uses the private Vercel Blob store for durable event deduplication, so production billing requires that store.
Activate Stripe Tax, add the jurisdictions where the business is registered to collect tax, and assign the Pro product the appropriate digital-news/service tax code. Checkout calculates tax from the customer's location, collects tax IDs, and adds tax on top of the exclusive monthly and yearly prices.
Pro access follows active, trialing, and past_due subscriptions. past_due is the payment-recovery grace period and lasts until Stripe's retry policy moves the subscription to another status. unpaid customers lose Pro content access but keep Customer Portal access to recover billing. Complimentary access can also be granted in /admin, and Clerk Billing features with the slug pro are accepted. Preferences, bookmarks, and saved searches use the existing private Vercel Blob store.
Vercel calls /api/cron daily at 06:00 UTC. The endpoint inspects up to three article links per publisher, sends the qualifying batch to Gemini, validates the result, stores avtldr/stories.json, and revalidates the homepage. An edition is published only when 12–16 stories qualify; otherwise, the previous complete edition remains live. Each run also stores its filtering and selection counts in avtldr/refresh-diagnostics.json.
Authenticated maintenance modes:
# Generate and return a complete candidate edition without replacing live content.
curl -H "Authorization: Bearer $CRON_SECRET" "$SITE_URL/api/cron?dryRun=1"
# Read the most recently persisted refresh diagnostics.
curl -H "Authorization: Bearer $CRON_SECRET" "$SITE_URL/api/cron?diagnostics=1"npm test
npm run lint
npm run build