-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathnuxt.config.ts
More file actions
219 lines (206 loc) · 7.95 KB
/
Copy pathnuxt.config.ts
File metadata and controls
219 lines (206 loc) · 7.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
import type { Nitro } from 'nitropack'
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
modules: [
'@nuxt/content',
'@nuxt/eslint',
'@nuxt/fonts',
'@nuxt/icon',
'@nuxt/image',
'@nuxt/ui',
'@nuxt/scripts',
'nuxt-og-image',
'nuxt-auth-utils',
],
devtools: { enabled: true },
app: {
head: {
htmlAttrs: {
lang: 'en',
},
link: [
{ rel: 'icon', type: 'image/png', href: '/favicon-96x96.png', sizes: '96x96' },
{ rel: 'icon', type: 'image/svg+xml', href: '/favicon.svg' },
{ rel: 'shortcut icon', href: '/favicon.ico' },
{ rel: 'apple-touch-icon', sizes: '180x180', href: '/apple-touch-icon.png' },
],
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ name: 'format-detection', content: 'telephone=no' },
],
},
},
css: ['@/assets/css/main.css', '@/assets/css/leaflet.css'],
// Code highlighting theme. Nuxt UI defaults to the washed-out `material-theme`
// triple; override with a single crisp, high-contrast light theme (the site
// is light-only — `ui.colorMode: false`).
mdc: {
highlight: {
theme: 'github-light',
},
},
ui: {
colorMode: false,
},
runtimeConfig: {
anthropicApiKey: '',
openaiApiKey: '',
databaseUrl: '',
// Comma-separated list of emails granted admin powers (e.g. endorsing
// credentials on behalf of users who emailed proofs to support). Set via
// `doppler secrets set NUXT_ADMIN_EMAILS=a@x.com,b@y.com`.
adminEmails: '',
// From address used for transactional mail (e.g. "CommunityFix
// <noreply@communityfix.org>"). The domain must be onboarded to
// Cloudflare Email Sending — see `wrangler email sending enable`.
emailFrom: '',
},
routeRules: {
'/umami/**': { proxy: 'https://cloud.umami.is/**' },
// The revision inbox folded into the broader dashboard.
'/inbox': { redirect: '/dashboard' },
},
// Bind the dev server to IPv4. Without this it listens on `[::1]`, and
// nuxt-og-image skips its real HTTP font fetch whenever the origin contains
// `::1` — falling back to an internal route that can't serve @nuxt/fonts'
// Vite-served `/_fonts/*.woff2`, so every OG image renders with a fallback
// font. An IPv4 origin lets the renderer fetch fonts over HTTP, matching how
// production resolves them from the Cloudflare ASSETS binding. Dev-only.
devServer: { host: '127.0.0.1' },
future: {
compatibilityVersion: 4,
},
compatibilityDate: '2025-12-22',
nitro: {
// Pin the preset explicitly so the build always emits a Worker bundle.
// Without this, Nitro can fall back to `node-server` if Workers Builds
// doesn't inject NITRO_PRESET, producing an output that wrangler can't
// deploy.
preset: 'cloudflare_module',
experimental: {
asyncContext: true,
tasks: true,
},
scheduledTasks: {
// Re-submit nodes stuck in `pending` (review workflow never completed) to
// the moderation Workflow — safety net for swallowed/failed triggers.
// Hourly; only nodes pending > 30 min are considered stuck.
'0 * * * *': ['review:reconcile-pending'],
// Recompute all trust scores daily at 3am UTC
'0 3 * * *': ['compute:trust-scores'],
// Reap expired OAuth codes/tokens + stale rate-limit windows at 3:15am UTC
'15 3 * * *': ['oauth:purge'],
// Newsletter digests at 8am UTC: weekly on Mondays, monthly on the 1st.
// Each task only mails its own frequency cohort; retried/overlapping
// invocations are absorbed by the per-user lastSentAt guard.
'0 8 * * 1': ['newsletter:weekly'],
'0 8 1 * *': ['newsletter:monthly'],
},
// Mount the `auth` namespace on the Cloudflare KV binding so the WebAuthn
// challenge round-trip survives across requests / isolates. The default
// unstorage memory driver is per-isolate and breaks the two-step register
// and authenticate flows on stateless Workers.
storage: {
auth: {
driver: 'cloudflare-kv-binding',
binding: 'KV_AUTH',
},
},
// In dev (`bun run dev`) there is no KV binding, so fall back to memory.
// Single-process dev means the in-memory challenge survives the two-step
// round-trip just fine.
devStorage: {
auth: {
driver: 'memory',
},
},
cloudflare: {
// Let Nitro generate `.output/server/wrangler.json` and pick the right
// Hyperdrive + KV bindings based on the build-time branch. CF Workers
// Builds injects WORKERS_CI_BRANCH on every build; locally / on master
// we default to the production bindings.
//
// We can't use `wrangler.jsonc` `env.<name>` blocks here because Nitro's
// cloudflare-module preset emits a "redirected" wrangler config and
// wrangler 4.x rejects redirected configs that contain env blocks.
deployConfig: true,
wrangler: {
// @nuxt/content v3 serves `queryCollection` from a SQL database at
// runtime. Under the `cloudflare` preset it defaults to a D1 binding
// named `DB` and lazily imports its bundled content dump into that D1 on
// the first request. Without the binding the Worker has no content
// database, so every markdown-backed page (/privacy, /terms,
// /whitepaper, /mcp, /guides, /guide/*) 404s in production. Wire the
// pre-provisioned content D1s, branch-aware like the other bindings.
d1_databases: [
{
binding: 'DB',
database_name:
process.env.WORKERS_CI_BRANCH && process.env.WORKERS_CI_BRANCH !== 'master'
? 'communityfix_nuxt_content_staging'
: 'communityfix_nuxt_content',
database_id:
process.env.WORKERS_CI_BRANCH && process.env.WORKERS_CI_BRANCH !== 'master'
? 'bfd9a625-c402-4713-b887-7b545e5688ae' // communityfix_nuxt_content_staging
: '9c5ab51d-ec5e-4aed-9883-85d79bea6b85', // communityfix_nuxt_content (prod)
},
],
hyperdrive: [
{
binding: 'HYPERDRIVE',
id:
process.env.WORKERS_CI_BRANCH && process.env.WORKERS_CI_BRANCH !== 'master'
? 'd9a5e6878b9342b4ac96844524a59d05' // communityfix-staging → Neon staging branch
: '9cfe49d0e805462086e8365bd604a062', // communityfix-prod → Neon production branch
},
],
kv_namespaces: [
{
binding: 'KV_AUTH',
id:
process.env.WORKERS_CI_BRANCH && process.env.WORKERS_CI_BRANCH !== 'master'
? 'b0d3e44f92ae4be0a0c57903404f62fb' // communityfix-auth-staging
: '10ae9211092f42d4a90cd17a938c360a', // communityfix-auth (prod)
},
],
send_email: [{ name: 'EMAIL' }],
workflows: [
{
binding: 'MODERATION_WORKFLOW',
class_name: 'ModerationWorkflow',
name:
process.env.WORKERS_CI_BRANCH && process.env.WORKERS_CI_BRANCH !== 'master'
? 'moderation-staging'
: 'moderation',
script_name:
process.env.WORKERS_CI_BRANCH && process.env.WORKERS_CI_BRANCH !== 'master'
? 'communityfix-moderation-staging'
: 'communityfix-moderation',
},
],
},
},
},
hooks: {
'nitro:build:before': (nitro: Nitro) => {
nitro.options.moduleSideEffects.push('reflect-metadata')
},
},
auth: {
webAuthn: true,
},
eslint: {
config: {
stylistic: true,
},
},
fonts: {
// `global: true` is required for nuxt-og-image's Takumi renderer to embed
// these fonts; without it OG images fall back to a system font.
families: [
{ name: 'Inter', weights: [400], global: true },
{ name: 'Oswald', weights: [400, 500], global: true },
],
},
})