@kevinmarrec/create-app 0.6.7 → 0.8.0

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.
Files changed (70) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +1 -3
  3. package/package.json +22 -18
  4. package/template/.github/workflows/ci.yml +3 -3
  5. package/template/.scripts/dev.ts +8 -0
  6. package/template/.vscode/settings.json +1 -1
  7. package/template/client/.env +1 -0
  8. package/template/{frontend → client}/package.json +11 -9
  9. package/template/{frontend → client}/src/App.vue +9 -2
  10. package/template/client/src/composables/auth.ts +39 -0
  11. package/template/client/src/composables/content.ts +12 -0
  12. package/template/{frontend → client}/src/composables/index.ts +1 -0
  13. package/template/{frontend → client}/src/lib/orpc.ts +2 -2
  14. package/template/compose.yaml +5 -5
  15. package/template/knip.config.ts +11 -6
  16. package/template/package.json +10 -10
  17. package/template/server/.env.development +4 -0
  18. package/template/{backend → server}/package.json +7 -7
  19. package/template/{backend → server}/src/auth/index.ts +3 -1
  20. package/template/{backend → server}/src/database/drizzle/config.ts +2 -1
  21. package/template/server/src/database/index.ts +20 -0
  22. package/template/server/src/database/migrations/0000_init.sql +50 -0
  23. package/template/{backend → server}/src/database/migrations/meta/0000_snapshot.json +92 -105
  24. package/template/server/src/database/migrations/meta/_journal.json +13 -0
  25. package/template/server/src/database/schema/accounts.ts +20 -0
  26. package/template/server/src/database/schema/sessions.ts +15 -0
  27. package/template/server/src/database/schema/users.ts +12 -0
  28. package/template/server/src/database/schema/verifications.ts +11 -0
  29. package/template/{backend → server}/src/env.d.ts +2 -2
  30. package/template/{backend → server}/src/main.ts +20 -16
  31. package/template/server/src/orpc/context.ts +10 -0
  32. package/template/server/src/orpc/handler.ts +34 -0
  33. package/template/server/src/orpc/index.ts +18 -0
  34. package/template/server/src/orpc/middlewares/auth.ts +23 -0
  35. package/template/server/src/orpc/router/index.ts +19 -0
  36. package/template/server/src/utils/cors.ts +26 -0
  37. package/template/tsconfig.json +2 -2
  38. package/template/backend/.env.development +0 -4
  39. package/template/backend/src/database/index.ts +0 -23
  40. package/template/backend/src/database/migrations/0000_fluffy_salo.sql +0 -49
  41. package/template/backend/src/database/migrations/meta/_journal.json +0 -13
  42. package/template/backend/src/database/schema/accounts.ts +0 -20
  43. package/template/backend/src/database/schema/sessions.ts +0 -15
  44. package/template/backend/src/database/schema/users.ts +0 -12
  45. package/template/backend/src/database/schema/verifications.ts +0 -11
  46. package/template/backend/src/orpc/index.ts +0 -65
  47. package/template/backend/src/orpc/middlewares/auth.ts +0 -33
  48. package/template/backend/src/orpc/router/auth.ts +0 -54
  49. package/template/backend/src/orpc/router/index.ts +0 -9
  50. package/template/frontend/.env.development +0 -1
  51. package/template/frontend/src/composables/auth.ts +0 -31
  52. package/template/scripts/dev.ts +0 -8
  53. /package/template/{gitignore → .gitignore} +0 -0
  54. /package/template/{npmrc → .npmrc} +0 -0
  55. /package/template/{frontend → client}/index.html +0 -0
  56. /package/template/{frontend → client}/public/favicon.svg +0 -0
  57. /package/template/{frontend → client}/public/robots.txt +0 -0
  58. /package/template/{frontend → client}/src/components/.gitkeep +0 -0
  59. /package/template/{frontend → client}/src/env.d.ts +0 -0
  60. /package/template/{frontend → client}/src/locales/en.yml +0 -0
  61. /package/template/{frontend → client}/src/locales/fr.yml +0 -0
  62. /package/template/{frontend → client}/src/main.ts +0 -0
  63. /package/template/{frontend → client}/tsconfig.json +0 -0
  64. /package/template/{frontend → client}/uno.config.ts +0 -0
  65. /package/template/{frontend → client}/vite.config.ts +0 -0
  66. /package/template/{frontend → client}/wrangler.json +0 -0
  67. /package/template/{backend → server}/src/database/schema/index.ts +0 -0
  68. /package/template/{backend → server}/src/orpc/middlewares/index.ts +0 -0
  69. /package/template/{backend → server}/src/utils/logger.ts +0 -0
  70. /package/template/{backend → server}/tsconfig.json +0 -0
@@ -1,146 +1,131 @@
1
1
  {
2
- "version": "6",
3
- "dialect": "sqlite",
4
- "id": "c1982d78-242a-497b-b4b3-6a4f8a375c7f",
2
+ "id": "21bfd4d4-350d-4701-b4b8-b0d991fc163e",
5
3
  "prevId": "00000000-0000-0000-0000-000000000000",
4
+ "version": "7",
5
+ "dialect": "postgresql",
6
6
  "tables": {
7
- "users": {
7
+ "public.users": {
8
8
  "name": "users",
9
+ "schema": "",
9
10
  "columns": {
10
11
  "id": {
11
12
  "name": "id",
12
13
  "type": "text",
13
14
  "primaryKey": true,
14
- "notNull": true,
15
- "autoincrement": false
15
+ "notNull": true
16
16
  },
17
17
  "name": {
18
18
  "name": "name",
19
19
  "type": "text",
20
20
  "primaryKey": false,
21
- "notNull": true,
22
- "autoincrement": false
21
+ "notNull": true
23
22
  },
24
23
  "email": {
25
24
  "name": "email",
26
25
  "type": "text",
27
26
  "primaryKey": false,
28
- "notNull": true,
29
- "autoincrement": false
27
+ "notNull": true
30
28
  },
31
29
  "email_verified": {
32
30
  "name": "email_verified",
33
- "type": "integer",
31
+ "type": "boolean",
34
32
  "primaryKey": false,
35
33
  "notNull": true,
36
- "autoincrement": false,
37
34
  "default": false
38
35
  },
39
36
  "image": {
40
37
  "name": "image",
41
38
  "type": "text",
42
39
  "primaryKey": false,
43
- "notNull": false,
44
- "autoincrement": false
40
+ "notNull": false
45
41
  },
46
42
  "created_at": {
47
43
  "name": "created_at",
48
- "type": "integer",
44
+ "type": "timestamp",
49
45
  "primaryKey": false,
50
46
  "notNull": true,
51
- "autoincrement": false
47
+ "default": "now()"
52
48
  },
53
49
  "updated_at": {
54
50
  "name": "updated_at",
55
- "type": "integer",
51
+ "type": "timestamp",
56
52
  "primaryKey": false,
57
53
  "notNull": true,
58
- "autoincrement": false
54
+ "default": "now()"
59
55
  }
60
56
  },
61
- "indexes": {
57
+ "indexes": {},
58
+ "foreignKeys": {},
59
+ "compositePrimaryKeys": {},
60
+ "uniqueConstraints": {
62
61
  "users_email_unique": {
63
62
  "name": "users_email_unique",
63
+ "nullsNotDistinct": false,
64
64
  "columns": [
65
65
  "email"
66
- ],
67
- "isUnique": true
66
+ ]
68
67
  }
69
68
  },
70
- "foreignKeys": {},
71
- "compositePrimaryKeys": {},
72
- "uniqueConstraints": {},
73
- "checkConstraints": {}
69
+ "policies": {},
70
+ "checkConstraints": {},
71
+ "isRLSEnabled": false
74
72
  },
75
- "sessions": {
73
+ "public.sessions": {
76
74
  "name": "sessions",
75
+ "schema": "",
77
76
  "columns": {
78
77
  "id": {
79
78
  "name": "id",
80
79
  "type": "text",
81
80
  "primaryKey": true,
82
- "notNull": true,
83
- "autoincrement": false
81
+ "notNull": true
84
82
  },
85
83
  "user_id": {
86
84
  "name": "user_id",
87
85
  "type": "text",
88
86
  "primaryKey": false,
89
- "notNull": true,
90
- "autoincrement": false
87
+ "notNull": true
91
88
  },
92
89
  "token": {
93
90
  "name": "token",
94
91
  "type": "text",
95
92
  "primaryKey": false,
96
- "notNull": true,
97
- "autoincrement": false
93
+ "notNull": true
98
94
  },
99
95
  "expires_at": {
100
96
  "name": "expires_at",
101
- "type": "integer",
97
+ "type": "timestamp",
102
98
  "primaryKey": false,
103
- "notNull": true,
104
- "autoincrement": false
99
+ "notNull": true
105
100
  },
106
101
  "ip_address": {
107
102
  "name": "ip_address",
108
103
  "type": "text",
109
104
  "primaryKey": false,
110
- "notNull": false,
111
- "autoincrement": false
105
+ "notNull": false
112
106
  },
113
107
  "user_agent": {
114
108
  "name": "user_agent",
115
109
  "type": "text",
116
110
  "primaryKey": false,
117
- "notNull": false,
118
- "autoincrement": false
111
+ "notNull": false
119
112
  },
120
113
  "created_at": {
121
114
  "name": "created_at",
122
- "type": "integer",
115
+ "type": "timestamp",
123
116
  "primaryKey": false,
124
117
  "notNull": true,
125
- "autoincrement": false
118
+ "default": "now()"
126
119
  },
127
120
  "updated_at": {
128
121
  "name": "updated_at",
129
- "type": "integer",
122
+ "type": "timestamp",
130
123
  "primaryKey": false,
131
124
  "notNull": true,
132
- "autoincrement": false
133
- }
134
- },
135
- "indexes": {
136
- "sessions_token_unique": {
137
- "name": "sessions_token_unique",
138
- "columns": [
139
- "token"
140
- ],
141
- "isUnique": true
125
+ "default": "now()"
142
126
  }
143
127
  },
128
+ "indexes": {},
144
129
  "foreignKeys": {
145
130
  "sessions_user_id_users_id_fk": {
146
131
  "name": "sessions_user_id_users_id_fk",
@@ -157,102 +142,102 @@
157
142
  }
158
143
  },
159
144
  "compositePrimaryKeys": {},
160
- "uniqueConstraints": {},
161
- "checkConstraints": {}
145
+ "uniqueConstraints": {
146
+ "sessions_token_unique": {
147
+ "name": "sessions_token_unique",
148
+ "nullsNotDistinct": false,
149
+ "columns": [
150
+ "token"
151
+ ]
152
+ }
153
+ },
154
+ "policies": {},
155
+ "checkConstraints": {},
156
+ "isRLSEnabled": false
162
157
  },
163
- "accounts": {
158
+ "public.accounts": {
164
159
  "name": "accounts",
160
+ "schema": "",
165
161
  "columns": {
166
162
  "id": {
167
163
  "name": "id",
168
164
  "type": "text",
169
165
  "primaryKey": true,
170
- "notNull": true,
171
- "autoincrement": false
166
+ "notNull": true
172
167
  },
173
168
  "user_id": {
174
169
  "name": "user_id",
175
170
  "type": "text",
176
171
  "primaryKey": false,
177
- "notNull": true,
178
- "autoincrement": false
172
+ "notNull": true
179
173
  },
180
174
  "account_id": {
181
175
  "name": "account_id",
182
176
  "type": "text",
183
177
  "primaryKey": false,
184
- "notNull": true,
185
- "autoincrement": false
178
+ "notNull": true
186
179
  },
187
180
  "provider_id": {
188
181
  "name": "provider_id",
189
182
  "type": "text",
190
183
  "primaryKey": false,
191
- "notNull": true,
192
- "autoincrement": false
184
+ "notNull": true
193
185
  },
194
186
  "access_token": {
195
187
  "name": "access_token",
196
188
  "type": "text",
197
189
  "primaryKey": false,
198
- "notNull": false,
199
- "autoincrement": false
190
+ "notNull": false
200
191
  },
201
192
  "refresh_token": {
202
193
  "name": "refresh_token",
203
194
  "type": "text",
204
195
  "primaryKey": false,
205
- "notNull": false,
206
- "autoincrement": false
196
+ "notNull": false
207
197
  },
208
198
  "access_token_expires_at": {
209
199
  "name": "access_token_expires_at",
210
- "type": "integer",
200
+ "type": "timestamp",
211
201
  "primaryKey": false,
212
- "notNull": false,
213
- "autoincrement": false
202
+ "notNull": false
214
203
  },
215
204
  "refresh_token_expires_at": {
216
205
  "name": "refresh_token_expires_at",
217
- "type": "integer",
206
+ "type": "timestamp",
218
207
  "primaryKey": false,
219
- "notNull": false,
220
- "autoincrement": false
208
+ "notNull": false
221
209
  },
222
210
  "scope": {
223
211
  "name": "scope",
224
212
  "type": "text",
225
213
  "primaryKey": false,
226
- "notNull": false,
227
- "autoincrement": false
214
+ "notNull": false
228
215
  },
229
216
  "id_token": {
230
217
  "name": "id_token",
231
218
  "type": "text",
232
219
  "primaryKey": false,
233
- "notNull": false,
234
- "autoincrement": false
220
+ "notNull": false
235
221
  },
236
222
  "password": {
237
223
  "name": "password",
238
224
  "type": "text",
239
225
  "primaryKey": false,
240
- "notNull": false,
241
- "autoincrement": false
226
+ "notNull": false
242
227
  },
243
228
  "created_at": {
244
229
  "name": "created_at",
245
- "type": "integer",
230
+ "type": "timestamp",
246
231
  "primaryKey": false,
247
232
  "notNull": true,
248
- "autoincrement": false
233
+ "default": "now()"
249
234
  },
250
235
  "updated_at": {
251
236
  "name": "updated_at",
252
- "type": "integer",
237
+ "type": "timestamp",
253
238
  "primaryKey": false,
254
239
  "notNull": true,
255
- "autoincrement": false
240
+ "default": "now()"
256
241
  }
257
242
  },
258
243
  "indexes": {},
@@ -273,69 +258,71 @@
273
258
  },
274
259
  "compositePrimaryKeys": {},
275
260
  "uniqueConstraints": {},
276
- "checkConstraints": {}
261
+ "policies": {},
262
+ "checkConstraints": {},
263
+ "isRLSEnabled": false
277
264
  },
278
- "verifications": {
265
+ "public.verifications": {
279
266
  "name": "verifications",
267
+ "schema": "",
280
268
  "columns": {
281
269
  "id": {
282
270
  "name": "id",
283
271
  "type": "text",
284
272
  "primaryKey": true,
285
- "notNull": true,
286
- "autoincrement": false
273
+ "notNull": true
287
274
  },
288
275
  "identifier": {
289
276
  "name": "identifier",
290
277
  "type": "text",
291
278
  "primaryKey": false,
292
- "notNull": true,
293
- "autoincrement": false
279
+ "notNull": true
294
280
  },
295
281
  "value": {
296
282
  "name": "value",
297
283
  "type": "text",
298
284
  "primaryKey": false,
299
- "notNull": true,
300
- "autoincrement": false
285
+ "notNull": true
301
286
  },
302
287
  "expires_at": {
303
288
  "name": "expires_at",
304
- "type": "integer",
289
+ "type": "timestamp",
305
290
  "primaryKey": false,
306
- "notNull": true,
307
- "autoincrement": false
291
+ "notNull": true
308
292
  },
309
293
  "created_at": {
310
294
  "name": "created_at",
311
- "type": "integer",
295
+ "type": "timestamp",
312
296
  "primaryKey": false,
313
297
  "notNull": true,
314
- "autoincrement": false
298
+ "default": "now()"
315
299
  },
316
300
  "updated_at": {
317
301
  "name": "updated_at",
318
- "type": "integer",
302
+ "type": "timestamp",
319
303
  "primaryKey": false,
320
304
  "notNull": true,
321
- "autoincrement": false
305
+ "default": "now()"
322
306
  }
323
307
  },
324
308
  "indexes": {},
325
309
  "foreignKeys": {},
326
310
  "compositePrimaryKeys": {},
327
311
  "uniqueConstraints": {},
328
- "checkConstraints": {}
312
+ "policies": {},
313
+ "checkConstraints": {},
314
+ "isRLSEnabled": false
329
315
  }
330
316
  },
331
- "views": {},
332
317
  "enums": {},
318
+ "schemas": {},
319
+ "sequences": {},
320
+ "roles": {},
321
+ "policies": {},
322
+ "views": {},
333
323
  "_meta": {
324
+ "columns": {},
334
325
  "schemas": {},
335
- "tables": {},
336
- "columns": {}
337
- },
338
- "internal": {
339
- "indexes": {}
326
+ "tables": {}
340
327
  }
341
328
  }
@@ -0,0 +1,13 @@
1
+ {
2
+ "version": "7",
3
+ "dialect": "postgresql",
4
+ "entries": [
5
+ {
6
+ "idx": 0,
7
+ "version": "7",
8
+ "when": 1761156145639,
9
+ "tag": "0000_init",
10
+ "breakpoints": true
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,20 @@
1
+ import { pgTable } from 'drizzle-orm/pg-core'
2
+
3
+ import { users } from './users'
4
+
5
+ // https://www.better-auth.com/docs/concepts/database#account
6
+ export const accounts = pgTable('accounts', t => ({
7
+ id: t.text().primaryKey(),
8
+ userId: t.text().notNull().references(() => users.id, { onDelete: 'cascade' }),
9
+ accountId: t.text().notNull(),
10
+ providerId: t.text().notNull(),
11
+ accessToken: t.text(),
12
+ refreshToken: t.text(),
13
+ accessTokenExpiresAt: t.timestamp(),
14
+ refreshTokenExpiresAt: t.timestamp(),
15
+ scope: t.text(),
16
+ idToken: t.text(),
17
+ password: t.text(),
18
+ createdAt: t.timestamp().notNull().defaultNow(),
19
+ updatedAt: t.timestamp().notNull().defaultNow().$onUpdate(() => new Date()),
20
+ }))
@@ -0,0 +1,15 @@
1
+ import { pgTable } from 'drizzle-orm/pg-core'
2
+
3
+ import { users } from './users'
4
+
5
+ // https://www.better-auth.com/docs/concepts/database#session
6
+ export const sessions = pgTable('sessions', t => ({
7
+ id: t.text().primaryKey(),
8
+ userId: t.text().notNull().references(() => users.id, { onDelete: 'cascade' }),
9
+ token: t.text().notNull().unique(),
10
+ expiresAt: t.timestamp().notNull(),
11
+ ipAddress: t.text(),
12
+ userAgent: t.text(),
13
+ createdAt: t.timestamp().notNull().defaultNow(),
14
+ updatedAt: t.timestamp().notNull().defaultNow().$onUpdate(() => new Date()),
15
+ }))
@@ -0,0 +1,12 @@
1
+ import { pgTable } from 'drizzle-orm/pg-core'
2
+
3
+ // https://www.better-auth.com/docs/concepts/database#user
4
+ export const users = pgTable('users', t => ({
5
+ id: t.text().primaryKey(),
6
+ name: t.text().notNull(),
7
+ email: t.text().notNull().unique(),
8
+ emailVerified: t.boolean().notNull().default(false),
9
+ image: t.text(),
10
+ createdAt: t.timestamp().notNull().defaultNow(),
11
+ updatedAt: t.timestamp().notNull().defaultNow().$onUpdate(() => new Date()),
12
+ }))
@@ -0,0 +1,11 @@
1
+ import { pgTable } from 'drizzle-orm/pg-core'
2
+
3
+ // https://www.better-auth.com/docs/concepts/database#verification
4
+ export const verifications = pgTable('verifications', t => ({
5
+ id: t.text().primaryKey(),
6
+ identifier: t.text().notNull(),
7
+ value: t.text().notNull(),
8
+ expiresAt: t.timestamp().notNull(),
9
+ createdAt: t.timestamp().notNull().defaultNow(),
10
+ updatedAt: t.timestamp().notNull().defaultNow().$onUpdate(() => new Date()),
11
+ }))
@@ -1,6 +1,6 @@
1
1
  interface ImportMetaEnv {
2
- readonly BETTER_AUTH_SECRET: string
3
- readonly BETTER_AUTH_URL: string
2
+ readonly ALLOWED_ORIGINS: string
3
+ readonly AUTH_SECRET: string
4
4
  readonly DATABASE_URL: string
5
5
  readonly LOG_LEVEL: string
6
6
  readonly NODE_ENV: string
@@ -1,32 +1,36 @@
1
1
  import process from 'node:process'
2
2
 
3
- import { createBetterAuth } from '@backend/auth'
4
- import { db } from '@backend/database'
5
- import { createRpcHandler } from '@backend/orpc'
6
- import { router } from '@backend/orpc/router'
7
- import { logger } from '@backend/utils/logger'
3
+ import { createBetterAuth } from '@server/auth'
4
+ import { db } from '@server/database'
5
+ import { createRpcHandler } from '@server/orpc'
6
+ import { router } from '@server/orpc/router'
7
+ import { cors } from '@server/utils/cors'
8
+ import { logger } from '@server/utils/logger'
8
9
 
9
10
  const auth = createBetterAuth({ db, logger })
10
11
  const rpcHandler = createRpcHandler(router)
11
12
 
12
- const server = Bun.serve({
13
- hostname: import.meta.env.HOST ?? '0.0.0.0',
14
- port: import.meta.env.PORT ?? 4000,
15
- async fetch(request) {
16
- const { matched, response } = await rpcHandler.handle(request, {
13
+ const routes = {
14
+ '/auth/*': cors(async (req) => {
15
+ return await auth.handler(req)
16
+ }),
17
+ '/rpc/*': cors(async (req) => {
18
+ const { matched, response } = await rpcHandler.handle(req, {
17
19
  prefix: '/rpc',
18
- context: {
19
- auth,
20
- db,
21
- logger,
22
- },
20
+ context: { auth, db, logger },
23
21
  })
24
22
 
25
23
  if (matched)
26
24
  return response
27
25
 
28
26
  return new Response('Not found', { status: 404 })
29
- },
27
+ }),
28
+ }
29
+
30
+ const server = Bun.serve({
31
+ hostname: import.meta.env.HOST ?? '0.0.0.0',
32
+ port: import.meta.env.PORT ?? 4000,
33
+ routes,
30
34
  error(error) {
31
35
  logger.error(error)
32
36
  return new Response('Internal Server Error', { status: 500 })
@@ -0,0 +1,10 @@
1
+ import type { RequestHeadersPluginContext, ResponseHeadersPluginContext } from '@orpc/server/plugins'
2
+ import type { Auth } from '@server/auth'
3
+ import type { Database } from '@server/database'
4
+ import type { Logger } from '@server/utils/logger'
5
+
6
+ export interface Context extends RequestHeadersPluginContext, ResponseHeadersPluginContext {
7
+ auth: Auth
8
+ db: Database
9
+ logger: Logger
10
+ }
@@ -0,0 +1,34 @@
1
+ import { onError, ORPCError, type Router } from '@orpc/server'
2
+ import { RPCHandler } from '@orpc/server/fetch'
3
+ import {
4
+ RequestHeadersPlugin,
5
+ ResponseHeadersPlugin,
6
+ } from '@orpc/server/plugins'
7
+ import { APIError } from 'better-auth/api'
8
+
9
+ import type { Context } from './context'
10
+
11
+ export function createRpcHandler<T extends Context>(router: Router<any, T>) {
12
+ return new RPCHandler<T>(router, {
13
+ plugins: [
14
+ new RequestHeadersPlugin(),
15
+ new ResponseHeadersPlugin(),
16
+ ],
17
+ clientInterceptors: [
18
+ onError((error, { context }) => {
19
+ if (error instanceof APIError) {
20
+ throw new ORPCError(error.body?.code ?? 'INTERNAL_SERVER_ERROR', {
21
+ status: error.statusCode,
22
+ message: error.body?.message,
23
+ })
24
+ }
25
+
26
+ if (error instanceof ORPCError) {
27
+ throw error
28
+ }
29
+
30
+ context.logger.error(error)
31
+ }),
32
+ ],
33
+ })
34
+ }
@@ -0,0 +1,18 @@
1
+ import { os } from '@orpc/server'
2
+ import { authMiddleware } from '@server/orpc/middlewares'
3
+
4
+ import type { Context } from './context'
5
+
6
+ export { createRpcHandler } from './handler'
7
+
8
+ export type { Context }
9
+
10
+ export const pub = os
11
+ .$context<Context>()
12
+ .errors({
13
+ UNAUTHORIZED: { status: 401 },
14
+ })
15
+
16
+ /** @beta */
17
+ export const authed = pub
18
+ .use(authMiddleware)
@@ -0,0 +1,23 @@
1
+ import { ORPCError, os } from '@orpc/server'
2
+ import type { Context } from '@server/orpc'
3
+
4
+ export const authMiddleware = os
5
+ .$context<Context>()
6
+ .middleware(async ({ context, next }) => {
7
+ const { response: session, headers } = await context.auth.api.getSession({
8
+ headers: context.reqHeaders,
9
+ returnHeaders: true,
10
+ })
11
+
12
+ headers.forEach((v, k) => context.resHeaders?.append(k, v))
13
+
14
+ if (!session) {
15
+ throw new ORPCError('UNAUTHORIZED')
16
+ }
17
+
18
+ return next({
19
+ context: {
20
+ user: session.user,
21
+ },
22
+ })
23
+ })
@@ -0,0 +1,19 @@
1
+ import { authed, pub } from '@server/orpc'
2
+ import * as v from 'valibot'
3
+
4
+ export type { RouterClient } from '@orpc/server'
5
+
6
+ export const router = {
7
+ public: pub
8
+ .input(v.any())
9
+ .handler(async () => {
10
+ return 'public'
11
+ }),
12
+ private: authed
13
+ .input(v.any())
14
+ .handler(async () => {
15
+ return 'private'
16
+ }),
17
+ }
18
+
19
+ export type Router = typeof router