create-questpie 2.0.1 → 2.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -6
- package/dist/index.mjs +139 -24
- package/package.json +5 -3
- package/skills/questpie/AGENTS.md +2670 -0
- package/skills/questpie/SKILL.md +260 -0
- package/skills/questpie/references/auth.md +121 -0
- package/skills/questpie/references/business-logic.md +550 -0
- package/skills/questpie/references/codegen-plugin-api.md +382 -0
- package/skills/questpie/references/crud-api.md +378 -0
- package/skills/questpie/references/data-modeling.md +493 -0
- package/skills/questpie/references/extend.md +557 -0
- package/skills/questpie/references/field-types.md +386 -0
- package/skills/questpie/references/infrastructure-adapters.md +545 -0
- package/skills/questpie/references/multi-tenancy.md +364 -0
- package/skills/questpie/references/production.md +475 -0
- package/skills/questpie/references/query-operators.md +125 -0
- package/skills/questpie/references/quickstart.md +564 -0
- package/skills/questpie/references/rules.md +389 -0
- package/skills/questpie/references/tanstack-query.md +520 -0
- package/skills/questpie-admin/AGENTS.md +1508 -0
- package/skills/questpie-admin/SKILL.md +436 -0
- package/skills/questpie-admin/references/blocks.md +331 -0
- package/skills/questpie-admin/references/custom-ui.md +305 -0
- package/skills/questpie-admin/references/views.md +449 -0
- package/templates/tanstack-start/AGENTS.md +17 -13
- package/templates/tanstack-start/CLAUDE.md +15 -12
- package/templates/tanstack-start/README.md +19 -13
- package/templates/tanstack-start/env.example +1 -1
- package/templates/tanstack-start/package.json +20 -6
- package/templates/tanstack-start/src/lib/env.ts +1 -1
- package/templates/tanstack-start/src/lib/query-client.ts +10 -1
- package/templates/tanstack-start/src/questpie/server/config/admin.ts +27 -30
- package/templates/tanstack-start/src/routeTree.gen.ts +138 -0
- package/templates/tanstack-start/src/routes/__root.tsx +0 -2
- package/templates/tanstack-start/src/routes/admin/$.tsx +12 -1
- package/templates/tanstack-start/src/routes/admin/index.tsx +12 -5
- package/templates/tanstack-start/src/routes/admin.tsx +8 -1
- package/templates/tanstack-start/src/tanstack-start.d.ts +1 -0
- package/templates/tanstack-start/src/vite-env.d.ts +1 -0
- package/templates/tanstack-start/vite.config.ts +1 -3
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
# Infrastructure Adapters Reference
|
|
2
|
+
|
|
3
|
+
All adapter configurations for QUESTPIE production infrastructure.
|
|
4
|
+
|
|
5
|
+
## Database
|
|
6
|
+
|
|
7
|
+
PostgreSQL with Drizzle ORM. Configured in `questpie.config.ts`:
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { runtimeConfig } from "questpie";
|
|
11
|
+
|
|
12
|
+
export default runtimeConfig({
|
|
13
|
+
db: {
|
|
14
|
+
url: process.env.DATABASE_URL || "postgres://localhost/myapp",
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Field-to-Column Mapping
|
|
20
|
+
|
|
21
|
+
| QUESTPIE Field | Drizzle Column Type |
|
|
22
|
+
| -------------- | ------------------- |
|
|
23
|
+
| `f.text()` | `varchar` / `text` |
|
|
24
|
+
| `f.number()` | `integer` |
|
|
25
|
+
| `f.boolean()` | `boolean` |
|
|
26
|
+
| `f.date()` | `date` |
|
|
27
|
+
| `f.datetime()` | `timestamp` |
|
|
28
|
+
| `f.select()` | `varchar` |
|
|
29
|
+
| `f.json()` | `jsonb` |
|
|
30
|
+
| `f.object()` | `jsonb` |
|
|
31
|
+
| `.array()` | `jsonb` |
|
|
32
|
+
| `f.relation()` | `varchar` (FK) |
|
|
33
|
+
|
|
34
|
+
### Raw Access
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
handler: async ({ db }) => {
|
|
38
|
+
// Raw SQL
|
|
39
|
+
const result = await db.execute(sql`SELECT COUNT(*) FROM posts`);
|
|
40
|
+
|
|
41
|
+
// Drizzle query builder
|
|
42
|
+
const rows = await db.select().from(table).where(eq(table.id, id));
|
|
43
|
+
};
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Indexes
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import { uniqueIndex, index } from "drizzle-orm/pg-core";
|
|
50
|
+
|
|
51
|
+
collection("posts")
|
|
52
|
+
.fields(({ f }) => ({ slug: f.text().required() }))
|
|
53
|
+
.indexes(({ table }) => [
|
|
54
|
+
uniqueIndex("posts_slug_unique").on(table.slug),
|
|
55
|
+
index("posts_status_idx").on(table.status),
|
|
56
|
+
]);
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Storage
|
|
60
|
+
|
|
61
|
+
File storage via [Flydrive](https://flydrive.dev/).
|
|
62
|
+
|
|
63
|
+
### Local (Development)
|
|
64
|
+
|
|
65
|
+
Default adapter. Files stored on local filesystem:
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
export default runtimeConfig({
|
|
69
|
+
storage: {
|
|
70
|
+
basePath: "/api",
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### S3-Compatible (Production)
|
|
76
|
+
|
|
77
|
+
Works with AWS S3, MinIO, DigitalOcean Spaces, Cloudflare R2:
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
import { S3Driver } from "flydrive/drivers/s3";
|
|
81
|
+
|
|
82
|
+
export default runtimeConfig({
|
|
83
|
+
storage: {
|
|
84
|
+
basePath: "/api",
|
|
85
|
+
driver: new S3Driver({
|
|
86
|
+
bucket: process.env.S3_BUCKET,
|
|
87
|
+
region: process.env.S3_REGION,
|
|
88
|
+
accessKeyId: process.env.S3_ACCESS_KEY,
|
|
89
|
+
secretAccessKey: process.env.S3_SECRET_KEY,
|
|
90
|
+
}),
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Upload Fields
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
avatar: f.upload({
|
|
99
|
+
to: "assets", // target upload collection
|
|
100
|
+
mimeTypes: ["image/*"], // allowed MIME types
|
|
101
|
+
maxSize: 5_000_000, // max file size in bytes (5MB)
|
|
102
|
+
}),
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Client Upload
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
const asset = await client.collections.assets.upload(file, {
|
|
109
|
+
onProgress: (percent) => console.log(`${percent}%`),
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const assets = await client.collections.assets.uploadMany(files, {
|
|
113
|
+
onProgress: (percent) => console.log(`${percent}%`),
|
|
114
|
+
});
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Queue
|
|
118
|
+
|
|
119
|
+
Background jobs via [pg-boss](https://github.com/timgit/pg-boss). Jobs stored in PostgreSQL -- no external queue service needed.
|
|
120
|
+
|
|
121
|
+
### Configuration
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
import { pgBossAdapter, runtimeConfig } from "questpie";
|
|
125
|
+
|
|
126
|
+
export default runtimeConfig({
|
|
127
|
+
queue: {
|
|
128
|
+
adapter: pgBossAdapter({
|
|
129
|
+
connectionString: process.env.DATABASE_URL,
|
|
130
|
+
}),
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Publishing Jobs
|
|
136
|
+
|
|
137
|
+
The `queue` context object is fully typed:
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
handler: async ({ queue }) => {
|
|
141
|
+
await queue.sendConfirmation.publish({
|
|
142
|
+
appointmentId: "abc",
|
|
143
|
+
customerId: "def",
|
|
144
|
+
});
|
|
145
|
+
};
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Realtime
|
|
149
|
+
|
|
150
|
+
SSE-based live updates.
|
|
151
|
+
|
|
152
|
+
### pgNotify (Single Instance)
|
|
153
|
+
|
|
154
|
+
Uses PostgreSQL `LISTEN/NOTIFY`. Best for single-server deployments:
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
import { pgNotifyAdapter, runtimeConfig } from "questpie";
|
|
158
|
+
|
|
159
|
+
export default runtimeConfig({
|
|
160
|
+
realtime: {
|
|
161
|
+
adapter: pgNotifyAdapter({
|
|
162
|
+
connectionString: process.env.DATABASE_URL,
|
|
163
|
+
}),
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Redis Streams (Multi-Instance)
|
|
169
|
+
|
|
170
|
+
Required for horizontal scaling across multiple server instances:
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
import { redisStreamsAdapter, runtimeConfig } from "questpie";
|
|
174
|
+
|
|
175
|
+
export default runtimeConfig({
|
|
176
|
+
realtime: {
|
|
177
|
+
adapter: redisStreamsAdapter({
|
|
178
|
+
url: process.env.REDIS_URL,
|
|
179
|
+
}),
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### When to Use Which
|
|
185
|
+
|
|
186
|
+
| Adapter | Use Case |
|
|
187
|
+
| --------------------- | ----------------------------------------------------- |
|
|
188
|
+
| `pgNotifyAdapter` | Single server, development, simple deployments |
|
|
189
|
+
| `redisStreamsAdapter` | Multiple servers, horizontal scaling, high throughput |
|
|
190
|
+
|
|
191
|
+
## Email
|
|
192
|
+
|
|
193
|
+
Transactional email with typed templates.
|
|
194
|
+
|
|
195
|
+
### SMTP (Production)
|
|
196
|
+
|
|
197
|
+
```ts
|
|
198
|
+
import { SmtpAdapter, runtimeConfig } from "questpie";
|
|
199
|
+
|
|
200
|
+
export default runtimeConfig({
|
|
201
|
+
email: {
|
|
202
|
+
adapter: new SmtpAdapter({
|
|
203
|
+
transport: {
|
|
204
|
+
host: process.env.SMTP_HOST,
|
|
205
|
+
port: parseInt(process.env.SMTP_PORT || "587"),
|
|
206
|
+
secure: true,
|
|
207
|
+
},
|
|
208
|
+
}),
|
|
209
|
+
},
|
|
210
|
+
});
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Console (Development)
|
|
214
|
+
|
|
215
|
+
Logs emails to console instead of sending:
|
|
216
|
+
|
|
217
|
+
```ts
|
|
218
|
+
import { ConsoleAdapter, runtimeConfig } from "questpie";
|
|
219
|
+
|
|
220
|
+
export default runtimeConfig({
|
|
221
|
+
email: {
|
|
222
|
+
adapter: new ConsoleAdapter({ logHtml: false }),
|
|
223
|
+
},
|
|
224
|
+
});
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Environment-Based Switching
|
|
228
|
+
|
|
229
|
+
```ts
|
|
230
|
+
email: {
|
|
231
|
+
adapter:
|
|
232
|
+
process.env.NODE_ENV === "development"
|
|
233
|
+
? new ConsoleAdapter({ logHtml: false })
|
|
234
|
+
: new SmtpAdapter({
|
|
235
|
+
transport: {
|
|
236
|
+
host: process.env.SMTP_HOST,
|
|
237
|
+
port: parseInt(process.env.SMTP_PORT || "587"),
|
|
238
|
+
secure: true,
|
|
239
|
+
},
|
|
240
|
+
}),
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Defining Templates
|
|
245
|
+
|
|
246
|
+
Templates go in the `emails/` directory:
|
|
247
|
+
|
|
248
|
+
```ts
|
|
249
|
+
// emails/welcome.ts
|
|
250
|
+
import { email } from "questpie";
|
|
251
|
+
import z from "zod";
|
|
252
|
+
|
|
253
|
+
export default email({
|
|
254
|
+
name: "welcome",
|
|
255
|
+
schema: z.object({
|
|
256
|
+
userName: z.string(),
|
|
257
|
+
loginUrl: z.string(),
|
|
258
|
+
}),
|
|
259
|
+
handler: ({ input }) => ({
|
|
260
|
+
subject: `Welcome, ${input.userName}!`,
|
|
261
|
+
html: `<h1>Welcome!</h1><p><a href="${input.loginUrl}">Sign in here</a></p>`,
|
|
262
|
+
}),
|
|
263
|
+
});
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Sending
|
|
267
|
+
|
|
268
|
+
```ts
|
|
269
|
+
handler: async ({ email }) => {
|
|
270
|
+
await email.sendTemplate({
|
|
271
|
+
template: "welcome",
|
|
272
|
+
input: { userName: "John", loginUrl: "https://app.example.com/login" },
|
|
273
|
+
to: "john@example.com",
|
|
274
|
+
});
|
|
275
|
+
};
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
## KV Store
|
|
279
|
+
|
|
280
|
+
Key-value storage for caching, rate limiting, ephemeral data.
|
|
281
|
+
|
|
282
|
+
### Custom Adapter
|
|
283
|
+
|
|
284
|
+
```ts
|
|
285
|
+
export default runtimeConfig({
|
|
286
|
+
kv: {
|
|
287
|
+
adapter: myKvAdapter,
|
|
288
|
+
defaultTtl: 3600,
|
|
289
|
+
},
|
|
290
|
+
});
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### In-Memory Default
|
|
294
|
+
|
|
295
|
+
```ts
|
|
296
|
+
export default runtimeConfig({
|
|
297
|
+
kv: {
|
|
298
|
+
defaultTtl: 3600,
|
|
299
|
+
},
|
|
300
|
+
});
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### API
|
|
304
|
+
|
|
305
|
+
```ts
|
|
306
|
+
handler: async ({ kv }) => {
|
|
307
|
+
// Set with TTL (seconds)
|
|
308
|
+
await kv.set("session:abc", JSON.stringify(data), { ttl: 3600 });
|
|
309
|
+
|
|
310
|
+
// Get
|
|
311
|
+
const value = await kv.get("session:abc");
|
|
312
|
+
|
|
313
|
+
// Delete
|
|
314
|
+
await kv.delete("session:abc");
|
|
315
|
+
};
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
## Logger
|
|
319
|
+
|
|
320
|
+
Structured logging via [Pino](https://getpino.io).
|
|
321
|
+
|
|
322
|
+
### Usage
|
|
323
|
+
|
|
324
|
+
```ts
|
|
325
|
+
handler: async ({ logger }) => {
|
|
326
|
+
logger.info("Processing request");
|
|
327
|
+
logger.error({ err: error }, "Request failed");
|
|
328
|
+
logger.debug({ userId, action }, "User action");
|
|
329
|
+
};
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### Log Levels
|
|
333
|
+
|
|
334
|
+
| Level | Usage |
|
|
335
|
+
| ------- | --------------------- |
|
|
336
|
+
| `trace` | Detailed debugging |
|
|
337
|
+
| `debug` | Development debugging |
|
|
338
|
+
| `info` | Normal operations |
|
|
339
|
+
| `warn` | Potential issues |
|
|
340
|
+
| `error` | Errors |
|
|
341
|
+
| `fatal` | Critical failures |
|
|
342
|
+
|
|
343
|
+
### Structured Data
|
|
344
|
+
|
|
345
|
+
Pass objects as the first argument:
|
|
346
|
+
|
|
347
|
+
```ts
|
|
348
|
+
logger.info(
|
|
349
|
+
{
|
|
350
|
+
appointmentId: "abc",
|
|
351
|
+
action: "created",
|
|
352
|
+
barberId: "def",
|
|
353
|
+
},
|
|
354
|
+
"Appointment created",
|
|
355
|
+
);
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
## OpenAPI
|
|
359
|
+
|
|
360
|
+
Auto-generates OpenAPI 3.1 spec from your schema.
|
|
361
|
+
|
|
362
|
+
### Setup
|
|
363
|
+
|
|
364
|
+
```bash
|
|
365
|
+
bun add @questpie/openapi
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
```ts
|
|
369
|
+
// src/questpie/server/modules.ts
|
|
370
|
+
import { adminModule } from "@questpie/admin/server";
|
|
371
|
+
import { openApiModule } from "@questpie/openapi";
|
|
372
|
+
|
|
373
|
+
export default [adminModule, openApiModule] as const;
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
Configure it in `config/openapi.ts`:
|
|
377
|
+
|
|
378
|
+
```ts
|
|
379
|
+
import { openApiConfig } from "@questpie/openapi";
|
|
380
|
+
|
|
381
|
+
export default openApiConfig({
|
|
382
|
+
info: { title: "My API", version: "1.0.0" },
|
|
383
|
+
});
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
Then run codegen:
|
|
387
|
+
|
|
388
|
+
```bash
|
|
389
|
+
bunx questpie generate
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
### Configuration Options
|
|
393
|
+
|
|
394
|
+
```ts
|
|
395
|
+
openApiConfig({
|
|
396
|
+
info: {
|
|
397
|
+
title: "My API",
|
|
398
|
+
version: "1.0.0",
|
|
399
|
+
description: "Backend for my app",
|
|
400
|
+
},
|
|
401
|
+
servers: [{ url: "https://api.example.com", description: "Production" }],
|
|
402
|
+
basePath: "/api",
|
|
403
|
+
exclude: {
|
|
404
|
+
collections: ["_internal_logs"],
|
|
405
|
+
globals: ["_cache"],
|
|
406
|
+
},
|
|
407
|
+
auth: true, // include auth endpoints
|
|
408
|
+
search: true, // include search endpoints
|
|
409
|
+
scalar: {
|
|
410
|
+
theme: "purple", // Scalar UI theme
|
|
411
|
+
},
|
|
412
|
+
specPath: "openapi.json", // custom spec route
|
|
413
|
+
docsPath: "docs", // custom docs route
|
|
414
|
+
});
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
### Routes
|
|
418
|
+
|
|
419
|
+
| Route | Description |
|
|
420
|
+
| ----------------------- | -------------------------------- |
|
|
421
|
+
| `GET /api/openapi.json` | OpenAPI 3.1 JSON spec |
|
|
422
|
+
| `GET /api/docs` | Scalar interactive API reference |
|
|
423
|
+
|
|
424
|
+
### What Gets Documented
|
|
425
|
+
|
|
426
|
+
| Source | Documented As |
|
|
427
|
+
| ----------- | ------------------------------------------------------- |
|
|
428
|
+
| Collections | CRUD endpoints (`GET`, `POST`, `PUT`, `DELETE`) |
|
|
429
|
+
| Globals | Read/update endpoints (`GET`, `PUT`) |
|
|
430
|
+
| Routes | App route endpoints (`/api/{path}`) |
|
|
431
|
+
| Auth | Sign-in, sign-up, session endpoints (when `auth: true`) |
|
|
432
|
+
| Search | Search endpoints (when `search: true`) |
|
|
433
|
+
|
|
434
|
+
### Manual Route Approach
|
|
435
|
+
|
|
436
|
+
Instead of the module, create route files directly:
|
|
437
|
+
|
|
438
|
+
```ts
|
|
439
|
+
// routes/openapi.json.ts
|
|
440
|
+
import { openApiRoute } from "@questpie/openapi";
|
|
441
|
+
|
|
442
|
+
export default openApiRoute({
|
|
443
|
+
info: { title: "My API", version: "1.0.0" },
|
|
444
|
+
});
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
```ts
|
|
448
|
+
// routes/docs.ts
|
|
449
|
+
import { docsRoute } from "@questpie/openapi";
|
|
450
|
+
|
|
451
|
+
export default docsRoute({
|
|
452
|
+
scalar: { theme: "purple" },
|
|
453
|
+
});
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
### Programmatic Access
|
|
457
|
+
|
|
458
|
+
```ts
|
|
459
|
+
import { generateOpenApiSpec } from "@questpie/openapi";
|
|
460
|
+
|
|
461
|
+
const spec = generateOpenApiSpec(app, {
|
|
462
|
+
info: { title: "My API", version: "1.0.0" },
|
|
463
|
+
});
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
## Migrations CLI
|
|
467
|
+
|
|
468
|
+
| Command | Description |
|
|
469
|
+
| -------------------------------- | ------------------------------------------------ |
|
|
470
|
+
| `bunx questpie push` | Direct schema sync (dev only, no migration file) |
|
|
471
|
+
| `bunx questpie migrate:generate` | Generate migration from schema diff |
|
|
472
|
+
| `bunx questpie migrate:up` | Run pending migrations |
|
|
473
|
+
| `bunx questpie migrate:down` | Rollback last migration |
|
|
474
|
+
| `bunx questpie migrate:fresh` | Drop all and re-run (DESTRUCTIVE) |
|
|
475
|
+
| `bunx questpie migrate:reset` | Reset migration tracking |
|
|
476
|
+
| `bunx questpie seed` | Run seed files |
|
|
477
|
+
|
|
478
|
+
## Complete Production Config Example
|
|
479
|
+
|
|
480
|
+
```ts
|
|
481
|
+
import {
|
|
482
|
+
runtimeConfig,
|
|
483
|
+
pgBossAdapter,
|
|
484
|
+
pgNotifyAdapter,
|
|
485
|
+
SmtpAdapter,
|
|
486
|
+
} from "questpie";
|
|
487
|
+
import { S3Driver } from "flydrive/drivers/s3";
|
|
488
|
+
|
|
489
|
+
export default runtimeConfig({
|
|
490
|
+
db: {
|
|
491
|
+
url: process.env.DATABASE_URL!,
|
|
492
|
+
},
|
|
493
|
+
storage: {
|
|
494
|
+
basePath: "/api",
|
|
495
|
+
driver: new S3Driver({
|
|
496
|
+
bucket: process.env.S3_BUCKET!,
|
|
497
|
+
region: process.env.S3_REGION!,
|
|
498
|
+
accessKeyId: process.env.S3_ACCESS_KEY!,
|
|
499
|
+
secretAccessKey: process.env.S3_SECRET_KEY!,
|
|
500
|
+
}),
|
|
501
|
+
},
|
|
502
|
+
queue: {
|
|
503
|
+
adapter: pgBossAdapter({
|
|
504
|
+
connectionString: process.env.DATABASE_URL!,
|
|
505
|
+
}),
|
|
506
|
+
},
|
|
507
|
+
realtime: {
|
|
508
|
+
adapter: pgNotifyAdapter({
|
|
509
|
+
connectionString: process.env.DATABASE_URL!,
|
|
510
|
+
}),
|
|
511
|
+
},
|
|
512
|
+
email: {
|
|
513
|
+
adapter: new SmtpAdapter({
|
|
514
|
+
transport: {
|
|
515
|
+
host: process.env.SMTP_HOST!,
|
|
516
|
+
port: parseInt(process.env.SMTP_PORT || "587"),
|
|
517
|
+
secure: true,
|
|
518
|
+
},
|
|
519
|
+
}),
|
|
520
|
+
},
|
|
521
|
+
kv: {
|
|
522
|
+
adapter: myKvAdapter,
|
|
523
|
+
defaultTtl: 3600,
|
|
524
|
+
},
|
|
525
|
+
cli: {
|
|
526
|
+
migrations: { directory: "./src/migrations" },
|
|
527
|
+
seeds: { directory: "./src/seeds" },
|
|
528
|
+
},
|
|
529
|
+
});
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
## Environment Variables Summary
|
|
533
|
+
|
|
534
|
+
| Variable | Service | Required |
|
|
535
|
+
| -------------------- | ----------------------------- | ------------------- |
|
|
536
|
+
| `DATABASE_URL` | Database, Queue, Realtime | Yes |
|
|
537
|
+
| `APP_URL` | Auth, Email links | Yes |
|
|
538
|
+
| `BETTER_AUTH_SECRET` | Auth sessions | Yes (production) |
|
|
539
|
+
| `REDIS_URL` | KV, Realtime (multi-instance) | No |
|
|
540
|
+
| `S3_BUCKET` | Storage | No (if using local) |
|
|
541
|
+
| `S3_REGION` | Storage | No |
|
|
542
|
+
| `S3_ACCESS_KEY` | Storage | No |
|
|
543
|
+
| `S3_SECRET_KEY` | Storage | No |
|
|
544
|
+
| `SMTP_HOST` | Email | No |
|
|
545
|
+
| `SMTP_PORT` | Email | No |
|