@percepta/create 3.1.5 → 3.2.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 (74) hide show
  1. package/dist/index.js +53 -46
  2. package/dist/index.js.map +1 -1
  3. package/dist/{init-OeK4Yk6_.js → init-CtCp7Tv2.js} +3 -3
  4. package/dist/init-CtCp7Tv2.js.map +1 -0
  5. package/dist/{status-DC8mvHZj.js → status-CKe4aKso.js} +2 -2
  6. package/dist/{status-DC8mvHZj.js.map → status-CKe4aKso.js.map} +1 -1
  7. package/dist/{sync-C5Pd32VM.js → sync-D1vkoofl.js} +2 -2
  8. package/dist/{sync-C5Pd32VM.js.map → sync-D1vkoofl.js.map} +1 -1
  9. package/dist/{upstream-F6m8zRBQ.js → upstream-D-LH_1z4.js} +2 -2
  10. package/dist/{upstream-F6m8zRBQ.js.map → upstream-D-LH_1z4.js.map} +1 -1
  11. package/package.json +2 -2
  12. package/template-versions.json +1 -1
  13. package/templates/monorepo/.github/workflows/access-control.yml +38 -0
  14. package/templates/monorepo/README.md +41 -2
  15. package/templates/monorepo/access/README.md +39 -0
  16. package/templates/monorepo/access/bootstrap-grants.yaml.example +9 -0
  17. package/templates/monorepo/access/dev-grants.yaml.example +19 -0
  18. package/templates/monorepo/access/dev-groups.yaml.example +8 -0
  19. package/templates/monorepo/access/reconcile.yaml.example +11 -0
  20. package/templates/monorepo/auth/README.md +26 -0
  21. package/templates/monorepo/auth/drizzle.config.ts +13 -0
  22. package/templates/monorepo/auth/package.json +32 -0
  23. package/templates/monorepo/auth/scripts/setup-database.ts +57 -0
  24. package/templates/monorepo/auth/src/auth.ts +77 -0
  25. package/templates/monorepo/auth/src/config/database.ts +31 -0
  26. package/templates/monorepo/auth/src/drizzle/db.ts +9 -0
  27. package/templates/monorepo/auth/src/drizzle/migrations/0000_shared_auth.sql +89 -0
  28. package/templates/monorepo/auth/src/drizzle/migrations/meta/_journal.json +13 -0
  29. package/templates/{webapp → monorepo/auth}/src/drizzle/schema/auth/accounts.ts +1 -6
  30. package/templates/{webapp → monorepo/auth}/src/drizzle/schema/auth/sessions.ts +1 -5
  31. package/templates/{webapp → monorepo/auth}/src/drizzle/schema/auth/verifications.ts +0 -4
  32. package/templates/monorepo/auth/src/drizzle/schema/groups.ts +16 -0
  33. package/templates/monorepo/auth/src/drizzle/schema/index.ts +5 -0
  34. package/templates/monorepo/auth/src/drizzle/schema/users.ts +6 -0
  35. package/templates/monorepo/auth/src/index.ts +1 -0
  36. package/templates/monorepo/auth/src/scim/README.md +6 -0
  37. package/templates/monorepo/auth/tsconfig.json +12 -0
  38. package/templates/monorepo/package.json.template +18 -6
  39. package/templates/monorepo/pnpm-workspace.yaml +1 -0
  40. package/templates/webapp/AGENTS.md +13 -6
  41. package/templates/webapp/README.md +34 -18
  42. package/templates/webapp/agent-skills/access-control.md +301 -0
  43. package/templates/webapp/agent-skills/database.md +1 -1
  44. package/templates/webapp/docker-compose.yml +16 -0
  45. package/templates/webapp/env.example.template +9 -0
  46. package/templates/webapp/next.config.ts +1 -0
  47. package/templates/webapp/package.json.template +8 -4
  48. package/templates/webapp/scripts/seed.ts +87 -36
  49. package/templates/webapp/scripts/setup-database.ts +7 -1
  50. package/templates/webapp/scripts/start.sh +0 -9
  51. package/templates/webapp/src/access/access.manifest.ts +15 -0
  52. package/templates/webapp/src/access/schema.zed +7 -0
  53. package/templates/webapp/src/app/(app)/admin/_lib/PrincipalRoleTable.tsx +113 -0
  54. package/templates/webapp/src/app/(app)/admin/_lib/accessAdmin.ts +85 -0
  55. package/templates/webapp/src/app/(app)/admin/groups/page.tsx +117 -0
  56. package/templates/webapp/src/app/(app)/admin/users/page.tsx +79 -0
  57. package/templates/webapp/src/app/(app)/layout.tsx +16 -2
  58. package/templates/webapp/src/app/(app)/page.tsx +1 -12
  59. package/templates/webapp/src/app/(auth)/auth/signin/page.tsx +2 -5
  60. package/templates/webapp/src/app/(auth)/auth/signup/page.tsx +2 -5
  61. package/templates/webapp/src/config/getEnvConfig.ts +8 -0
  62. package/templates/webapp/src/drizzle/db.ts +3 -4
  63. package/templates/webapp/src/drizzle/migrations/0000_eager_grandmaster.sql +1 -57
  64. package/templates/webapp/src/drizzle/migrations/meta/0000_snapshot.json +1 -347
  65. package/templates/webapp/src/drizzle/schema/index.ts +3 -4
  66. package/templates/webapp/src/lib/auth/index.ts +6 -81
  67. package/templates/webapp/src/server/api/root.ts +4 -1
  68. package/templates/webapp/src/server/api/routers/access.ts +13 -0
  69. package/templates/webapp/src/server/trpc.ts +42 -8
  70. package/templates/webapp/src/services/DatabaseService.ts +4 -5
  71. package/templates/webapp/src/services/access/AppAccessControl.ts +39 -0
  72. package/dist/init-OeK4Yk6_.js.map +0 -1
  73. package/templates/webapp/scripts/create-user.ts +0 -47
  74. package/templates/webapp/src/drizzle/schema/auth/users.ts +0 -38
@@ -1,22 +1,11 @@
1
1
  import type { Metadata } from "next";
2
- import { headers } from "next/headers";
3
- import { redirect } from "next/navigation";
4
- import { auth } from "../../lib/auth";
5
2
 
6
3
  export const metadata: Metadata = {
7
4
  title: "__APP_TITLE__",
8
5
  description: "__APP_TITLE__",
9
6
  };
10
7
 
11
- export default async function HomePage() {
12
- const session = await auth.api.getSession({
13
- headers: await headers(),
14
- });
15
-
16
- if (session?.user == null) {
17
- redirect("/auth/signin");
18
- }
19
-
8
+ export default function HomePage() {
20
9
  return (
21
10
  <div className="space-y-8">
22
11
  <h1 className="text-center text-2xl font-bold text-foreground">
@@ -1,8 +1,7 @@
1
1
  import type { Metadata } from "next";
2
- import { headers } from "next/headers";
3
2
  import { redirect } from "next/navigation";
4
3
  import { Suspense } from "react";
5
- import { auth } from "../../../../lib/auth";
4
+ import { getServerSession } from "../../../../lib/auth";
6
5
  import { CredentialsSignInForm } from "./CredentialsSignInForm";
7
6
 
8
7
  export const metadata: Metadata = {
@@ -10,9 +9,7 @@ export const metadata: Metadata = {
10
9
  };
11
10
 
12
11
  export default async function SignInPage() {
13
- const session = await auth.api.getSession({
14
- headers: await headers(),
15
- });
12
+ const session = await getServerSession();
16
13
 
17
14
  if (session?.user != null) {
18
15
  redirect("/");
@@ -1,8 +1,7 @@
1
1
  import type { Metadata } from "next";
2
- import { headers } from "next/headers";
3
2
  import { redirect } from "next/navigation";
4
3
  import { Suspense } from "react";
5
- import { auth } from "../../../../lib/auth";
4
+ import { getServerSession } from "../../../../lib/auth";
6
5
  import { CredentialsSignUpForm } from "./CredentialsSignUpForm";
7
6
 
8
7
  export const metadata: Metadata = {
@@ -10,9 +9,7 @@ export const metadata: Metadata = {
10
9
  };
11
10
 
12
11
  export default async function SignUpPage() {
13
- const session = await auth.api.getSession({
14
- headers: await headers(),
15
- });
12
+ const session = await getServerSession();
16
13
 
17
14
  if (session?.user != null) {
18
15
  redirect("/");
@@ -59,6 +59,14 @@ export const { getEnvConfig, schema: ENV_CONFIG_SCHEMA } = createEnvConfig(
59
59
  ENCRYPTION_SECRET_KEY: z.string().optional(),
60
60
  TRIGGER_ENDPOINT_API_KEY: z.string().optional(),
61
61
 
62
+ // Access Control (SpiceDB):
63
+ SPICEDB_ENDPOINT: z.string().default("localhost:50051"),
64
+ SPICEDB_PRESHARED_KEY: z.string().default("dev-spicedb-token"),
65
+ SPICEDB_INSECURE: z
66
+ .string()
67
+ .transform((value: string): boolean => value === "true" || value === "1")
68
+ .default(true),
69
+
62
70
  // LLM/AI providers:
63
71
  ANTHROPIC_API_KEY: z.string().optional(),
64
72
  OPENAI_API_KEY: z.string().optional(),
@@ -1,13 +1,12 @@
1
1
  import { type NodePgDatabase, drizzle } from "drizzle-orm/node-postgres";
2
2
  import { Pool } from "pg";
3
3
  import { getEnvConfig } from "../config/getEnvConfig";
4
- import * as schema from "./schema";
5
4
  import { getPgSearchPathOption } from "./searchPath";
6
5
  import { getPgSslConfig } from "./ssl";
7
6
 
8
7
  export const { client, db } = createDb();
9
8
 
10
- function createDb(): { client: Pool; db: NodePgDatabase<typeof schema> } {
9
+ function createDb(): { client: Pool; db: NodePgDatabase } {
11
10
  const {
12
11
  DATABASE_HOST: host,
13
12
  DATABASE_PORT: port,
@@ -18,7 +17,7 @@ function createDb(): { client: Pool; db: NodePgDatabase<typeof schema> } {
18
17
  DATABASE_USE_SSL: useSSL,
19
18
  } = getEnvConfig();
20
19
 
21
- const _client = new Pool({
20
+ const pool = new Pool({
22
21
  host,
23
22
  port,
24
23
  user,
@@ -28,5 +27,5 @@ function createDb(): { client: Pool; db: NodePgDatabase<typeof schema> } {
28
27
  options: getPgSearchPathOption(databaseSchema),
29
28
  });
30
29
 
31
- return { client: _client, db: drizzle(_client, { schema }) };
30
+ return { client: pool, db: drizzle(pool) };
32
31
  }
@@ -1,57 +1 @@
1
- CREATE TABLE "account" (
2
- "id" text PRIMARY KEY NOT NULL,
3
- "user_id" uuid NOT NULL,
4
- "account_id" text NOT NULL,
5
- "provider_id" text NOT NULL,
6
- "access_token" text,
7
- "refresh_token" text,
8
- "expires_at" integer,
9
- "access_token_expires_at" timestamp,
10
- "refresh_token_expires_at" timestamp,
11
- "scope" text,
12
- "id_token" text,
13
- "password" text,
14
- "created_at" timestamp NOT NULL,
15
- "updated_at" timestamp NOT NULL
16
- );
17
- --> statement-breakpoint
18
- CREATE TABLE "session" (
19
- "id" text PRIMARY KEY NOT NULL,
20
- "user_id" uuid NOT NULL,
21
- "token" text NOT NULL,
22
- "expires_at" timestamp NOT NULL,
23
- "ip_address" text,
24
- "user_agent" text,
25
- "impersonated_by" text,
26
- "created_at" timestamp NOT NULL,
27
- "updated_at" timestamp NOT NULL,
28
- CONSTRAINT "session_token_unique" UNIQUE("token")
29
- );
30
- --> statement-breakpoint
31
- CREATE TABLE "users" (
32
- "id" uuid PRIMARY KEY NOT NULL,
33
- "name" text NOT NULL,
34
- "email" text NOT NULL,
35
- "email_verified" boolean DEFAULT false NOT NULL,
36
- "image" text,
37
- "role" text DEFAULT 'user' NOT NULL,
38
- "banned" boolean DEFAULT false,
39
- "ban_reason" text,
40
- "ban_expires" timestamp,
41
- "created_at" timestamp DEFAULT now() NOT NULL,
42
- "updated_at" timestamp DEFAULT now() NOT NULL,
43
- CONSTRAINT "users_email_unique" UNIQUE("email")
44
- );
45
- --> statement-breakpoint
46
- CREATE TABLE "verification" (
47
- "id" text PRIMARY KEY NOT NULL,
48
- "identifier" text NOT NULL,
49
- "value" text NOT NULL,
50
- "expires_at" timestamp NOT NULL,
51
- "created_at" timestamp,
52
- "updated_at" timestamp
53
- );
54
- --> statement-breakpoint
55
- ALTER TABLE "account" ADD CONSTRAINT "account_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
56
- ALTER TABLE "session" ADD CONSTRAINT "session_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
57
- CREATE UNIQUE INDEX "lower_email_index" ON "users" USING btree (lower("email"));
1
+ -- App-local migrations go here. Customer-global auth tables are migrated by ../../auth.
@@ -3,353 +3,7 @@
3
3
  "prevId": "00000000-0000-0000-0000-000000000000",
4
4
  "version": "7",
5
5
  "dialect": "postgresql",
6
- "tables": {
7
- "public.account": {
8
- "name": "account",
9
- "schema": "",
10
- "columns": {
11
- "id": {
12
- "name": "id",
13
- "type": "text",
14
- "primaryKey": true,
15
- "notNull": true
16
- },
17
- "user_id": {
18
- "name": "user_id",
19
- "type": "uuid",
20
- "primaryKey": false,
21
- "notNull": true
22
- },
23
- "account_id": {
24
- "name": "account_id",
25
- "type": "text",
26
- "primaryKey": false,
27
- "notNull": true
28
- },
29
- "provider_id": {
30
- "name": "provider_id",
31
- "type": "text",
32
- "primaryKey": false,
33
- "notNull": true
34
- },
35
- "access_token": {
36
- "name": "access_token",
37
- "type": "text",
38
- "primaryKey": false,
39
- "notNull": false
40
- },
41
- "refresh_token": {
42
- "name": "refresh_token",
43
- "type": "text",
44
- "primaryKey": false,
45
- "notNull": false
46
- },
47
- "expires_at": {
48
- "name": "expires_at",
49
- "type": "integer",
50
- "primaryKey": false,
51
- "notNull": false
52
- },
53
- "access_token_expires_at": {
54
- "name": "access_token_expires_at",
55
- "type": "timestamp",
56
- "primaryKey": false,
57
- "notNull": false
58
- },
59
- "refresh_token_expires_at": {
60
- "name": "refresh_token_expires_at",
61
- "type": "timestamp",
62
- "primaryKey": false,
63
- "notNull": false
64
- },
65
- "scope": {
66
- "name": "scope",
67
- "type": "text",
68
- "primaryKey": false,
69
- "notNull": false
70
- },
71
- "id_token": {
72
- "name": "id_token",
73
- "type": "text",
74
- "primaryKey": false,
75
- "notNull": false
76
- },
77
- "password": {
78
- "name": "password",
79
- "type": "text",
80
- "primaryKey": false,
81
- "notNull": false
82
- },
83
- "created_at": {
84
- "name": "created_at",
85
- "type": "timestamp",
86
- "primaryKey": false,
87
- "notNull": true
88
- },
89
- "updated_at": {
90
- "name": "updated_at",
91
- "type": "timestamp",
92
- "primaryKey": false,
93
- "notNull": true
94
- }
95
- },
96
- "indexes": {},
97
- "foreignKeys": {
98
- "account_user_id_users_id_fk": {
99
- "name": "account_user_id_users_id_fk",
100
- "tableFrom": "account",
101
- "tableTo": "users",
102
- "columnsFrom": ["user_id"],
103
- "columnsTo": ["id"],
104
- "onDelete": "cascade",
105
- "onUpdate": "no action"
106
- }
107
- },
108
- "compositePrimaryKeys": {},
109
- "uniqueConstraints": {},
110
- "policies": {},
111
- "checkConstraints": {},
112
- "isRLSEnabled": false
113
- },
114
- "public.session": {
115
- "name": "session",
116
- "schema": "",
117
- "columns": {
118
- "id": {
119
- "name": "id",
120
- "type": "text",
121
- "primaryKey": true,
122
- "notNull": true
123
- },
124
- "user_id": {
125
- "name": "user_id",
126
- "type": "uuid",
127
- "primaryKey": false,
128
- "notNull": true
129
- },
130
- "token": {
131
- "name": "token",
132
- "type": "text",
133
- "primaryKey": false,
134
- "notNull": true
135
- },
136
- "expires_at": {
137
- "name": "expires_at",
138
- "type": "timestamp",
139
- "primaryKey": false,
140
- "notNull": true
141
- },
142
- "ip_address": {
143
- "name": "ip_address",
144
- "type": "text",
145
- "primaryKey": false,
146
- "notNull": false
147
- },
148
- "user_agent": {
149
- "name": "user_agent",
150
- "type": "text",
151
- "primaryKey": false,
152
- "notNull": false
153
- },
154
- "impersonated_by": {
155
- "name": "impersonated_by",
156
- "type": "text",
157
- "primaryKey": false,
158
- "notNull": false
159
- },
160
- "created_at": {
161
- "name": "created_at",
162
- "type": "timestamp",
163
- "primaryKey": false,
164
- "notNull": true
165
- },
166
- "updated_at": {
167
- "name": "updated_at",
168
- "type": "timestamp",
169
- "primaryKey": false,
170
- "notNull": true
171
- }
172
- },
173
- "indexes": {},
174
- "foreignKeys": {
175
- "session_user_id_users_id_fk": {
176
- "name": "session_user_id_users_id_fk",
177
- "tableFrom": "session",
178
- "tableTo": "users",
179
- "columnsFrom": ["user_id"],
180
- "columnsTo": ["id"],
181
- "onDelete": "cascade",
182
- "onUpdate": "no action"
183
- }
184
- },
185
- "compositePrimaryKeys": {},
186
- "uniqueConstraints": {
187
- "session_token_unique": {
188
- "name": "session_token_unique",
189
- "nullsNotDistinct": false,
190
- "columns": ["token"]
191
- }
192
- },
193
- "policies": {},
194
- "checkConstraints": {},
195
- "isRLSEnabled": false
196
- },
197
- "public.users": {
198
- "name": "users",
199
- "schema": "",
200
- "columns": {
201
- "id": {
202
- "name": "id",
203
- "type": "uuid",
204
- "primaryKey": true,
205
- "notNull": true
206
- },
207
- "name": {
208
- "name": "name",
209
- "type": "text",
210
- "primaryKey": false,
211
- "notNull": true
212
- },
213
- "email": {
214
- "name": "email",
215
- "type": "text",
216
- "primaryKey": false,
217
- "notNull": true
218
- },
219
- "email_verified": {
220
- "name": "email_verified",
221
- "type": "boolean",
222
- "primaryKey": false,
223
- "notNull": true,
224
- "default": false
225
- },
226
- "image": {
227
- "name": "image",
228
- "type": "text",
229
- "primaryKey": false,
230
- "notNull": false
231
- },
232
- "role": {
233
- "name": "role",
234
- "type": "text",
235
- "primaryKey": false,
236
- "notNull": true,
237
- "default": "'user'"
238
- },
239
- "banned": {
240
- "name": "banned",
241
- "type": "boolean",
242
- "primaryKey": false,
243
- "notNull": false,
244
- "default": false
245
- },
246
- "ban_reason": {
247
- "name": "ban_reason",
248
- "type": "text",
249
- "primaryKey": false,
250
- "notNull": false
251
- },
252
- "ban_expires": {
253
- "name": "ban_expires",
254
- "type": "timestamp",
255
- "primaryKey": false,
256
- "notNull": false
257
- },
258
- "created_at": {
259
- "name": "created_at",
260
- "type": "timestamp",
261
- "primaryKey": false,
262
- "notNull": true,
263
- "default": "now()"
264
- },
265
- "updated_at": {
266
- "name": "updated_at",
267
- "type": "timestamp",
268
- "primaryKey": false,
269
- "notNull": true,
270
- "default": "now()"
271
- }
272
- },
273
- "indexes": {
274
- "lower_email_index": {
275
- "name": "lower_email_index",
276
- "columns": [
277
- {
278
- "expression": "lower(\"email\")",
279
- "asc": true,
280
- "isExpression": true,
281
- "nulls": "last"
282
- }
283
- ],
284
- "isUnique": true,
285
- "concurrently": false,
286
- "method": "btree",
287
- "with": {}
288
- }
289
- },
290
- "foreignKeys": {},
291
- "compositePrimaryKeys": {},
292
- "uniqueConstraints": {
293
- "users_email_unique": {
294
- "name": "users_email_unique",
295
- "nullsNotDistinct": false,
296
- "columns": ["email"]
297
- }
298
- },
299
- "policies": {},
300
- "checkConstraints": {},
301
- "isRLSEnabled": false
302
- },
303
- "public.verification": {
304
- "name": "verification",
305
- "schema": "",
306
- "columns": {
307
- "id": {
308
- "name": "id",
309
- "type": "text",
310
- "primaryKey": true,
311
- "notNull": true
312
- },
313
- "identifier": {
314
- "name": "identifier",
315
- "type": "text",
316
- "primaryKey": false,
317
- "notNull": true
318
- },
319
- "value": {
320
- "name": "value",
321
- "type": "text",
322
- "primaryKey": false,
323
- "notNull": true
324
- },
325
- "expires_at": {
326
- "name": "expires_at",
327
- "type": "timestamp",
328
- "primaryKey": false,
329
- "notNull": true
330
- },
331
- "created_at": {
332
- "name": "created_at",
333
- "type": "timestamp",
334
- "primaryKey": false,
335
- "notNull": false
336
- },
337
- "updated_at": {
338
- "name": "updated_at",
339
- "type": "timestamp",
340
- "primaryKey": false,
341
- "notNull": false
342
- }
343
- },
344
- "indexes": {},
345
- "foreignKeys": {},
346
- "compositePrimaryKeys": {},
347
- "uniqueConstraints": {},
348
- "policies": {},
349
- "checkConstraints": {},
350
- "isRLSEnabled": false
351
- }
352
- },
6
+ "tables": {},
353
7
  "enums": {},
354
8
  "schemas": {},
355
9
  "sequences": {},
@@ -1,4 +1,3 @@
1
- export { accounts } from "./auth/accounts";
2
- export { sessions } from "./auth/sessions";
3
- export { users } from "./auth/users";
4
- export { verifications } from "./auth/verifications";
1
+ // App-local resource tables are exported from this module.
2
+ // Customer-global auth tables are exported by @__REPO_NAME__/auth/schema.
3
+ export {};
@@ -1,85 +1,10 @@
1
- import { betterAuth } from "better-auth";
2
- import { drizzleAdapter } from "better-auth/adapters/drizzle";
3
- import { admin } from "better-auth/plugins";
4
- import { getEnvConfig } from "../../config/getEnvConfig";
5
- import { db } from "../../drizzle/db";
6
- import { accounts } from "../../drizzle/schema/auth/accounts";
7
- import { sessions } from "../../drizzle/schema/auth/sessions";
8
- import { users } from "../../drizzle/schema/auth/users";
9
- import { verifications } from "../../drizzle/schema/auth/verifications";
10
- import { getLogger } from "../../services/logger/AppLogger";
1
+ import { auth } from "@__REPO_NAME__/auth";
2
+ import { headers } from "next/headers";
11
3
 
12
- // eslint-disable-next-line n/no-process-env -- detecting Next.js build phase
13
- const isBuildPhase = process.env.NEXT_PHASE === "phase-production-build";
4
+ export { auth, type BetterAuthSession } from "@__REPO_NAME__/auth";
14
5
 
15
- function getSecret(): string {
16
- if (isBuildPhase) {
17
- return "build-placeholder-not-used-at-runtime";
18
- }
19
-
20
- const secret = getEnvConfig().BETTER_AUTH_SECRET;
21
- if (secret == null) {
22
- throw new Error(
23
- "BETTER_AUTH_SECRET is required. Set it in your environment variables.",
24
- );
25
- }
26
- return secret;
27
- }
28
-
29
- function createAuth() {
30
- const config = getEnvConfig();
31
-
32
- return betterAuth({
33
- baseURL: config.BETTER_AUTH_URL,
34
- secret: getSecret(),
35
- database: drizzleAdapter(db, {
36
- provider: "pg",
37
- schema: {
38
- user: users,
39
- session: sessions,
40
- account: accounts,
41
- verification: verifications,
42
- },
43
- }),
44
- emailAndPassword: {
45
- enabled: true,
46
- },
47
- plugins: [admin()],
48
- advanced: {
49
- database: {
50
- generateId: false,
51
- },
52
- },
53
- logger: {
54
- disabled: false,
55
- },
6
+ export async function getServerSession() {
7
+ return auth.api.getSession({
8
+ headers: await headers(),
56
9
  });
57
10
  }
58
-
59
- type Auth = ReturnType<typeof createAuth>;
60
-
61
- let _auth: Auth | undefined;
62
-
63
- function getAuth(): Auth {
64
- if (_auth == null) {
65
- _auth = createAuth();
66
- getLogger().info(undefined, "Better Auth initialized");
67
- }
68
- return _auth;
69
- }
70
-
71
- /**
72
- * Lazy proxy that defers auth initialization to first access (runtime),
73
- * avoiding build-time evaluation of environment variables.
74
- */
75
- export const auth = new Proxy({} as Auth, {
76
- get(_target, prop, receiver) {
77
- // eslint-disable-next-line @typescript-eslint/no-unsafe-return
78
- return Reflect.get(getAuth(), prop, receiver);
79
- },
80
- has(_target, prop) {
81
- return Reflect.has(getAuth(), prop);
82
- },
83
- });
84
-
85
- export type BetterAuthSession = typeof auth.$Infer.Session;
@@ -1,5 +1,8 @@
1
1
  import { router } from "../trpc";
2
+ import { accessRouter } from "./routers/access";
2
3
 
3
- export const appRouter = router({});
4
+ export const appRouter = router({
5
+ access: accessRouter,
6
+ });
4
7
 
5
8
  export type AppRouter = typeof appRouter;
@@ -0,0 +1,13 @@
1
+ import { accessManifest } from "../../../access/access.manifest";
2
+ import { protectedProcedure, requirePermission, router } from "../../trpc";
3
+
4
+ export const accessRouter = router({
5
+ roleManagementStatus: protectedProcedure
6
+ .use(
7
+ requirePermission({
8
+ permission: accessManifest.system.manageRolesPermission,
9
+ resource: accessManifest.system.ref(),
10
+ }),
11
+ )
12
+ .query(() => ({ canManageRoles: true })),
13
+ });