Skip to content

access-dlsu/leapify

Repository files navigation

Leapify

The backend for DLSU CSO LEAP event websites. Leapify ships as:

Mode When to use
Standalone Worker You want a zero-code deploy — just configure secrets and run wrangler deploy
npm module You have your own Worker / Next.js / SvelteKit app and want to mount Leapify inside it

Both modes share exactly the same routes, auth, caching, and email logic.


Mode 1 — Standalone Worker

Deploy to Cloudflare

No code required. Clone, configure, deploy.

git clone https://github.com/access-dlsu/leapify
cd leapify
npm install
cp wrangler.toml.example wrangler.toml

Edit wrangler.toml with your D1 database ID and KV namespace ID, then set your secrets:

# CORS — comma-separated allowed origins
wrangler secret put ALLOWED_ORIGINS
# → "https://yoursite.com,https://www.yoursite.com"

# Firebase
wrangler secret put FIREBASE_PROJECT_ID
wrangler secret put FIREBASE_WEB_API_KEY

# Google Forms
wrangler secret put GFORMS_SERVICE_ACCOUNT_JSON
wrangler secret put GFORMS_WEBHOOK_SECRET

# Contentful
wrangler secret put CONTENTFUL_SPACE_ID
wrangler secret put CONTENTFUL_ACCESS_TOKEN
wrangler secret put CONTENTFUL_ENVIRONMENT

# Amazon SES (primary email)
wrangler secret put SES_REGION
wrangler secret put SES_ACCESS_KEY_ID
wrangler secret put SES_SECRET_ACCESS_KEY
wrangler secret put SES_FROM_ADDRESS

# Resend (optional email fallback — omit to disable)
wrangler secret put RESEND_API_KEY
wrangler secret put RESEND_FROM_ADDRESS

# Internal route security
wrangler secret put INTERNAL_API_SECRET

Build and deploy:

npm run build
wrangler deploy

Verify:

curl https://your-worker.your-subdomain.workers.dev/health
# → { "status": "ok", "providers": { "ses": true, "resend": false } }

wrangler.toml.example has full comments for every binding. See .dev.vars.example for local dev.


Mode 2 — npm module

Install into your own project and mount the handler:

npm install leapify

Server layer (Cloudflare Worker / Pages Function):

import { createLeapify } from 'leapify'

export default createLeapify({
  allowedOrigins: ['https://yourdomain.com'],
})

Browser / client components:

import { createLeapifyClient, getLeapifyToken } from 'leapify/client'
import { auth } from '@/lib/firebase'

const api = createLeapifyClient(
  process.env.NEXT_PUBLIC_API_URL!,
  () => getLeapifyToken(auth.currentUser),
)

const events = await api.getEvents()   // → LeapEvent[]
const config = await api.getConfig()   // → SiteConfig
const me     = await api.getMe()       // → UserProfile | null

Full setup and per-endpoint examples → Frontend Integration Guide


How It Works

Leapify exposes /api/ endpoints that your frontend consumes. The backend handles Firebase Auth, Cloudflare D1 (database), Contentful (CMS), and transactional email (Amazon SES primary / Resend fallback) — all credentials live in .env / wrangler.toml, never in browser code.

  • /api/* — restricted to your site's origin (allowedOrigins CORS gate)
  • /health — publicly accessible for uptime monitoring; reports which email providers are configured

Tech Stack

Layer Technology Purpose
Framework Hono Edge-optimized, <1ms cold start
ORM Drizzle + D1 Type-safe SQL on Cloudflare's serverless SQLite
Validation Zod Runtime schema validation
Cache Cloudflare KV + CDN JWT caching, slot availability, edge response cache
CMS Contentful Headless CMS for all event/FAQ/site content
Email Amazon SES + CF Queues Async transactional email — SES primary, Resend fallback
Auth Firebase Auth Google Sign-In, restricted to @dlsu.edu.ph
Testing Vitest + CF pool Unit + integration tests on real CF runtime

Infrastructure

Service Quota Role
Cloudflare D1 5M reads/day, 100k writes/day (Pro) Primary relational database
Cloudflare KV 10M reads/day (Pro) JWT cache + slot availability
Cloudflare Queues Async job dispatch
Contentful 100k API calls/mo, 50GB CDN (Free) All CMS content
Firebase Auth Usage-based Identity + JWT issuance
Amazon SES ~62k emails/mo free tier; $0.10/1k after Primary transactional email
Resend 50k emails/mo (Pro) Fallback transactional email

Caching Strategy

Three Cloudflare cache tiers keep D1/Firebase within quota at 30k concurrent users:

Tier TTL What it caches
CF CDN Edge 7 days GET /events list (ETag-gated)
CF KV 3,600s Firebase JWT tokens
CF KV 5s Slot availability per event

Auth

All users must sign in with @dlsu.edu.ph Google accounts. The backend rejects any other domain with 403 DOMAIN_RESTRICTED.

Role Token Access
guest None Public endpoints only
user Firebase JWT (@dlsu.edu.ph) Protected user endpoints
admin JWT + admin: true claim Admin mutation endpoints

Resilience at Scale

Firebase Auth (~50 QPS limit): JWTs are cached in Cloudflare KV with a 3,600s TTL — >90% of requests skip Firebase entirely.

Email (200–500ms send latency): All email jobs are pushed to a Cloudflare Queue. The HTTP response returns immediately (202 Accepted); the consumer worker sends via Amazon SES (primary). If SES returns a non-retryable error and RESEND_API_KEY is set, the job is retried via Resend before landing in the DLQ. If RESEND_API_KEY is not set, fallback is skipped and the job goes straight to the DLQ after SES exhausts its retries.

Why SES? SES supports 14 emails/second by default (vs. Resend's 10/s), has a native suppression list, and is significantly cheaper at high volumes ($0.10 per 1k). Resend is an optional fallback — configure it for a deliverability dashboard and better incident visibility, or omit RESEND_API_KEY to run SES-only.


Deployment

Leapify targets Cloudflare Workers (standalone) or Cloudflare Pages Functions (colocated). Configure wrangler.toml with D1, KV, Queue bindings and Worker secrets.

Standalone:

npm run build && wrangler deploy

npm module (inside your own repo):

wrangler deploy   # your consumer worker that imports leapify

See wrangler.toml.example and .dev.vars.example for the full config shape.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors