Production readiness (Sprint 1):
- workers/api/wrangler.toml: real KV namespace IDs for CACHE + SUBSCRIPTIONS (prod + preview),
AUTH_SECRET + GTS_CLIENT_SECRET set via wrangler secret put on Cloudflare, ALLOWED_ORIGINS env var
- kv-backed rate limiter replacing in-memory Map (rl:${ip}:${path}:${bucket} in KV)
- env-driven CORS allowlist (ALLOWED_ORIGINS), removed hardcoded localhost from production code
- GTS OAuth callback CSRF: signed state nonce stored in KV, consumed once on callback,
removed from CSRF_EXEMPT_PATHS, 4 new auth.state tests
- .github/workflows/deploy.yml: PR -> preview, main -> production via cloudflare/wrangler-action
- DEPLOYMENT.md, scripts/deploy/setup-cloudflare.sh
Frontend (/twitter):
- Full X-style hero: left rail, protocol pills with live counts (filter by network),
quality controls (threshold slider, non-crypto toggle), composer with Mastodon->Nostr bridge,
FeedList flat-scrolling, sign-in CTAs throughout, right sidebar network list + trending
- ToastProvider replacing alert() in AuthProvider
- BottomNav for mobile (Feed/Twitter/News/Daily/Profile)
- /status page: SSR health dashboard with auto-refresh, per-protocol latency
Backend:
- /api/preferences: GET/POST with session auth, persist threshold + non-crypto + niches/platforms,
11 endpoint tests + sanitizer unit tests
- GTS callback: state nonce verified against KV before token exchange
- KV-backed rate limiter with fail-open on KV errors
Tests: 81 backend tests pass (76->81)
Deployed: degenfeed-api (prod) and degenfeed-api-preview (preview) on Cloudflare
Health: Nostr/Lens/Bluesky/Mastodon/Threads/Reddit OK, RSS 403, Farcaster timeout
41 lines
1.1 KiB
JSON
41 lines
1.1 KiB
JSON
{
|
|
"$schema": "https://biomejs.dev/schemas/2.5.2/schema.json",
|
|
"vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": true },
|
|
"files": {
|
|
"ignoreUnknown": true,
|
|
"includes": ["**"]
|
|
},
|
|
"formatter": {
|
|
"enabled": true,
|
|
"indentStyle": "space",
|
|
"indentWidth": 2,
|
|
"lineWidth": 100,
|
|
"lineEnding": "lf"
|
|
},
|
|
"assist": { "actions": { "source": { "organizeImports": "on" } } },
|
|
"linter": {
|
|
"enabled": true,
|
|
"rules": {
|
|
"preset": "recommended",
|
|
"correctness": { "noUnusedVariables": "warn", "noUnusedImports": "warn" },
|
|
"style": { "useImportType": "warn", "useExportType": "warn" },
|
|
"suspicious": { "noExplicitAny": "warn" },
|
|
"a11y": { "useKeyWithClickEvents": "warn" }
|
|
}
|
|
},
|
|
"javascript": {
|
|
"formatter": { "quoteStyle": "single", "semicolons": "always", "trailingCommas": "all" }
|
|
},
|
|
"json": {
|
|
"formatter": { "indentWidth": 2, "trailingCommas": "none" }
|
|
},
|
|
"css": {
|
|
"parser": { "tailwindDirectives": true }
|
|
},
|
|
"overrides": [
|
|
{
|
|
"includes": ["apps/web/src/app/globals.css"],
|
|
"linter": { "enabled": false }
|
|
}
|
|
]
|
|
}
|