P0: mim-api, live conversion paths, and CWV sprint to ~2.56s LCP.

Ship VMID 7811 API (assistance, contact, Stripe checkout), wire donate and forms to API with accessible success UI, static LCP shell with deferred React mount, and perf gate tooling (P0 72/2600ms; world-class 85/2500ms tracked separately).

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
defiQUG
2026-06-15 23:42:45 -07:00
co-authored by Cursor
parent f0180bd3b8
commit 6c0de165d3
26 changed files with 4212 additions and 5370 deletions
+19
View File
@@ -0,0 +1,19 @@
# mim-api — VMID 7811 (mim-api-1 @ 192.168.11.36:3001)
PORT=3001
NODE_ENV=production
MIM_PUBLIC_URL=https://mim4u.org
MIM_DATA_DIR=/var/lib/mim-api/data
# Stripe (required for live donations)
STRIPE_SECRET_KEY=sk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...
# Receipt / intake notifications (optional — logs if unset)
SMTP_HOST=
SMTP_PORT=587
SMTP_USER=
SMTP_PASS=
MIM_NOTIFY_EMAIL=[email protected]
# CORS (comma-separated origins; default mim4u.org)
MIM_CORS_ORIGINS=https://mim4u.org,https://www.mim4u.org
+21
View File
@@ -0,0 +1,21 @@
{
"name": "mim-api",
"private": true,
"version": "1.0.0",
"type": "module",
"description": "Miracles in Motion API — Stripe, assistance intake, analytics (VMID 7811)",
"main": "src/server.js",
"scripts": {
"start": "node src/server.js",
"dev": "node --watch src/server.js",
"test": "node --test test/server.test.js"
},
"engines": {
"node": ">=18"
},
"dependencies": {
"express": "^4.21.2",
"nodemailer": "^6.10.0",
"stripe": "^17.7.0"
}
}
+288
View File
@@ -0,0 +1,288 @@
/**
* MIM4U API — VMID 7811
* Routes: health, events, Stripe checkout/intent/webhook, assistance, contact, donations log
*/
import express from 'express'
import fs from 'node:fs'
import path from 'node:path'
import crypto from 'node:crypto'
import nodemailer from 'nodemailer'
import Stripe from 'stripe'
const PORT = Number(process.env.PORT || 3001)
const DATA_DIR = process.env.MIM_DATA_DIR || path.join(process.cwd(), 'data')
const PUBLIC_URL = (process.env.MIM_PUBLIC_URL || 'https://mim4u.org').replace(/\/$/, '')
const NOTIFY_EMAIL = process.env.MIM_NOTIFY_EMAIL || '[email protected]'
const CORS_ORIGINS = (process.env.MIM_CORS_ORIGINS || 'https://mim4u.org,https://www.mim4u.org')
.split(',')
.map((s) => s.trim())
.filter(Boolean)
const stripeKey = process.env.STRIPE_SECRET_KEY
const stripe = stripeKey ? new Stripe(stripeKey) : null
fs.mkdirSync(DATA_DIR, { recursive: true })
function appendJsonl(file, record) {
const line = `${JSON.stringify({ ...record, ts: new Date().toISOString() })}\n`
fs.appendFileSync(path.join(DATA_DIR, file), line, 'utf8')
}
function mailTransport() {
const { SMTP_HOST, SMTP_USER, SMTP_PASS, SMTP_PORT } = process.env
if (!SMTP_HOST) return null
return nodemailer.createTransport({
host: SMTP_HOST,
port: Number(SMTP_PORT || 587),
secure: Number(SMTP_PORT) === 465,
auth: SMTP_USER ? { user: SMTP_USER, pass: SMTP_PASS } : undefined,
})
}
async function notify(subject, text, { to } = {}) {
appendJsonl('notifications.jsonl', { subject, text, to })
const transport = mailTransport()
if (!transport) {
console.log(`[notify] ${subject}: ${text.slice(0, 200)}`)
return
}
await transport.sendMail({
from: NOTIFY_EMAIL,
to: to || NOTIFY_EMAIL,
subject,
text,
})
}
function cors(req, res, next) {
const origin = req.headers.origin
if (origin && (CORS_ORIGINS.includes(origin) || CORS_ORIGINS.includes('*'))) {
res.setHeader('Access-Control-Allow-Origin', origin)
}
res.setHeader('Access-Control-Allow-Methods', 'GET,POST,OPTIONS')
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Stripe-Signature')
if (req.method === 'OPTIONS') return res.sendStatus(204)
next()
}
function requireStripe(_req, res, next) {
if (!stripe) {
return res.status(503).json({ error: 'Stripe not configured on API host (STRIPE_SECRET_KEY)' })
}
next()
}
function validateAssistance(body) {
const errors = []
if (!body?.requestType) errors.push('requestType required')
if (!body?.studentInfo?.firstName?.trim()) errors.push('studentInfo.firstName required')
if (!body?.studentInfo?.lastName?.trim()) errors.push('studentInfo.lastName required')
if (!body?.studentInfo?.school?.trim()) errors.push('studentInfo.school required')
if (!body?.contactInfo?.parentName?.trim()) errors.push('contactInfo.parentName required')
if (!body?.contactInfo?.phone?.trim()) errors.push('contactInfo.phone required')
if (!body?.contactInfo?.email?.trim()) errors.push('contactInfo.email required')
if (!body?.contactInfo?.relationship?.trim()) errors.push('contactInfo.relationship required')
if (!body?.details?.trim()) errors.push('details required')
if (body?.website) errors.push('spam detected')
return errors
}
const app = express()
app.disable('x-powered-by')
app.use(cors)
app.get('/health', (_req, res) => {
res.type('text/plain').send('ok')
})
app.get('/api/health', (_req, res) => {
res.json({
ok: true,
service: 'mim-api',
stripe: Boolean(stripe),
dataDir: DATA_DIR,
version: '1.0.0',
})
})
app.use('/api/events', express.json({ limit: '32kb' }))
app.post('/api/events', (req, res) => {
appendJsonl('events.jsonl', req.body || {})
res.status(204).end()
})
app.use('/api/assistance-requests', express.json({ limit: '128kb' }))
const postAssistanceRequest = async (req, res) => {
const errors = validateAssistance(req.body)
if (errors.length) return res.status(400).json({ error: 'Validation failed', fields: errors })
const id = `asst_${crypto.randomUUID()}`
const record = { id, ...req.body, ip: req.ip, userAgent: req.headers['user-agent'] }
appendJsonl('assistance-requests.jsonl', record)
try {
await notify(
`[MIM4U] Assistance request ${id}`,
`New assistance request (${req.body.requestType})\nContact: ${req.body.contactInfo.parentName} <${req.body.contactInfo.email}>\nPhone: ${req.body.contactInfo.phone}\nDetails: ${req.body.details?.slice(0, 500)}`,
)
} catch (e) {
console.error('assistance notify failed', e)
}
res.status(201).json({ ok: true, id, message: 'Request received. We will contact you within 24–48 hours.' })
}
app.post('/api/assistance-requests', postAssistanceRequest)
app.post('/api/assistance', postAssistanceRequest)
app.use('/api/contact', express.json({ limit: '64kb' }))
app.post('/api/contact', async (req, res) => {
const { type, name, email, message, website, ...rest } = req.body || {}
if (website) return res.status(400).json({ error: 'Invalid submission' })
if (!type) return res.status(400).json({ error: 'type required' })
if (!email?.trim() && type !== 'story') return res.status(400).json({ error: 'email required' })
if (type === 'story' && !message?.trim() && !rest.story?.trim()) {
return res.status(400).json({ error: 'story text required' })
}
const id = `contact_${crypto.randomUUID()}`
appendJsonl('contact.jsonl', { id, type, name, email, message, ...rest })
try {
await notify(`[MIM4U] ${type} form ${id}`, JSON.stringify({ name, email, message, ...rest }, null, 2))
} catch (e) {
console.error('contact notify failed', e)
}
res.status(201).json({ ok: true, id, message: 'Thank you — we will be in touch soon.' })
})
app.use('/api/donations', express.json({ limit: '32kb' }))
app.post('/api/donations', (req, res) => {
appendJsonl('donations-offline.jsonl', req.body || {})
res.status(201).json({ ok: true })
})
app.use('/api/create-payment-intent', express.json({ limit: '32kb' }))
app.post('/api/create-payment-intent', requireStripe, async (req, res) => {
const amount = Number(req.body?.amount)
if (!Number.isFinite(amount) || amount < 100) {
return res.status(400).json({ error: 'amount must be at least 100 cents' })
}
try {
const intent = await stripe.paymentIntents.create({
amount: Math.round(amount),
currency: 'usd',
metadata: {
recurring: String(Boolean(req.body?.recurring)),
donorEmail: req.body?.customer?.email || '',
donorName: req.body?.customer?.name || '',
},
receipt_email: req.body?.customer?.email || undefined,
})
res.json({ client_secret: intent.client_secret })
} catch (e) {
console.error('payment intent error', e)
res.status(502).json({ error: e.message || 'Stripe error' })
}
})
app.use('/api/create-checkout-session', express.json({ limit: '32kb' }))
app.post('/api/create-checkout-session', requireStripe, async (req, res) => {
const amountUsd = Number(req.body?.amount)
if (!Number.isFinite(amountUsd) || amountUsd < 1) {
return res.status(400).json({ error: 'amount must be at least $1' })
}
const cents = Math.round(amountUsd * 100)
const recurring = Boolean(req.body?.recurring)
const email = req.body?.email?.trim()
const name = req.body?.name?.trim()
try {
const session = await stripe.checkout.sessions.create({
mode: recurring ? 'subscription' : 'payment',
success_url: `${PUBLIC_URL}/#/donate?status=success&session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${PUBLIC_URL}/#/donate?status=cancelled`,
customer_email: email || undefined,
line_items: [
recurring
? {
price_data: {
currency: 'usd',
product_data: { name: 'Monthly donation — Miracles in Motion Foundation' },
unit_amount: cents,
recurring: { interval: 'month' },
},
quantity: 1,
}
: {
price_data: {
currency: 'usd',
product_data: { name: 'Donation — Miracles in Motion Foundation' },
unit_amount: cents,
},
quantity: 1,
},
],
metadata: {
donorName: name || '',
anonymous: String(Boolean(req.body?.anonymous)),
},
})
res.json({ url: session.url, sessionId: session.id })
} catch (e) {
console.error('checkout session error', e)
res.status(502).json({ error: e.message || 'Stripe error' })
}
})
app.post(
'/api/webhooks/stripe',
express.raw({ type: 'application/json' }),
async (req, res) => {
if (!stripe) return res.status(503).send('Stripe not configured')
const sig = req.headers['stripe-signature']
const secret = process.env.STRIPE_WEBHOOK_SECRET
let event
try {
event = secret
? stripe.webhooks.constructEvent(req.body, sig, secret)
: JSON.parse(req.body.toString())
} catch (e) {
console.error('webhook verify failed', e.message)
return res.status(400).send(`Webhook Error: ${e.message}`)
}
if (event.type === 'checkout.session.completed') {
const session = event.data.object
appendJsonl('donations.jsonl', {
sessionId: session.id,
amountTotal: session.amount_total,
customerEmail: session.customer_details?.email,
metadata: session.metadata,
})
const donorEmail = session.customer_details?.email
const amountUsd = ((session.amount_total || 0) / 100).toFixed(2)
try {
await notify(
`[MIM4U] Donation received ${session.id}`,
`Amount: ${amountUsd} USD\nEmail: ${donorEmail || 'n/a'}`,
)
if (donorEmail) {
await notify(
'Thank you for your gift — Miracles in Motion Foundation',
`Dear friend,\n\nThank you for your generous donation of $${amountUsd} to Miracles in Motion Foundation.\n\nYour gift supports outreach, emergency assistance, and compassionate care in Los Angeles County.\n\nEIN ${process.env.MIM_EIN || '33-4887159'}\nhttps://mim4u.org\n\nWith gratitude,\nMiracles in Motion Foundation`,
{ to: donorEmail },
)
}
} catch (e) {
console.error('donation notify failed', e)
}
}
res.json({ received: true })
},
)
app.use((_req, res) => res.status(404).json({ error: 'Not found' }))
app.listen(PORT, '0.0.0.0', () => {
console.log(`mim-api listening on :${PORT} (stripe=${Boolean(stripe)}) data=${DATA_DIR}`)
})
+30
View File
@@ -0,0 +1,30 @@
import { describe, it } from 'node:test'
import assert from 'node:assert/strict'
/** Inline validation mirror — keep in sync with server.js validateAssistance */
function validateAssistance(body) {
const errors = []
if (!body?.requestType) errors.push('requestType required')
if (!body?.studentInfo?.firstName?.trim()) errors.push('studentInfo.firstName required')
if (!body?.studentInfo?.lastName?.trim()) errors.push('studentInfo.lastName required')
if (!body?.studentInfo?.school?.trim()) errors.push('studentInfo.school required')
if (!body?.contactInfo?.parentName?.trim()) errors.push('contactInfo.parentName required')
if (!body?.contactInfo?.phone?.trim()) errors.push('contactInfo.phone required')
if (!body?.contactInfo?.email?.trim()) errors.push('contactInfo.email required')
if (!body?.contactInfo?.relationship?.trim()) errors.push('contactInfo.relationship required')
if (!body?.details?.trim()) errors.push('details required')
if (body?.website) errors.push('spam detected')
return errors
}
describe('assistance validation shape', () => {
it('requires core fields', () => {
const errors = validateAssistance({})
assert.ok(errors.length > 0)
})
it('rejects honeypot', () => {
const errors = validateAssistance({ website: 'spam' })
assert.ok(errors.includes('spam detected'))
})
})