create-authhero 0.34.0 โ†’ 0.35.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.
@@ -86,6 +86,41 @@ try {
86
86
  );
87
87
  }
88
88
 
89
+ // Copy admin UI files from @authhero/react-admin package
90
+ const adminSourceDir = path.join(
91
+ __dirname,
92
+ "node_modules",
93
+ "@authhero",
94
+ "react-admin",
95
+ "dist",
96
+ );
97
+
98
+ if (fs.existsSync(adminSourceDir)) {
99
+ console.log("๐Ÿ“ฆ Copying admin UI assets...");
100
+ const adminTargetDir = path.join(targetDir, "admin");
101
+ copyDirectory(adminSourceDir, adminTargetDir);
102
+
103
+ // Inject runtime config into index.html
104
+ // Uses window.location.origin so the admin UI automatically points to its own server
105
+ const adminIndexPath = path.join(adminSourceDir, "index.html");
106
+ const adminHtml = fs.readFileSync(adminIndexPath, "utf-8")
107
+ .replace(/src="\.\/assets\//g, 'src="/admin/assets/')
108
+ .replace(/href="\.\/assets\//g, 'href="/admin/assets/');
109
+ const configScript = `<script>window.__AUTHHERO_ADMIN_CONFIG__={domain:window.location.origin,basePath:"/admin"}</script>`;
110
+ const injectedHtml = adminHtml.replace("</head>", configScript + "\n</head>");
111
+
112
+ // Write injected HTML to CDN assets (for direct /admin/ access)
113
+ fs.writeFileSync(path.join(adminTargetDir, "index.html"), injectedHtml);
114
+
115
+ // Write as TS module for worker to import (for SPA fallback on deep links)
116
+ const srcDir = path.join(__dirname, "src");
117
+ fs.writeFileSync(
118
+ path.join(srcDir, "admin-index-html.ts"),
119
+ `export default ${JSON.stringify(injectedHtml)};\n`,
120
+ );
121
+ console.log("โœ… Admin UI assets copied and configured");
122
+ }
123
+
89
124
  console.log(`โœ… Assets copied to ${targetDir}`);
90
125
  } catch (error) {
91
126
  console.error("โŒ Error copying assets:", error.message);
@@ -1,18 +1,18 @@
1
1
  #!/usr/bin/env node
2
- import { Command as I } from "commander";
3
- import g from "inquirer";
4
- import a from "fs";
5
- import l from "path";
2
+ import { Command as P } from "commander";
3
+ import m from "inquirer";
4
+ import s from "fs";
5
+ import i from "path";
6
6
  import { spawn as E } from "child_process";
7
- const D = new I(), i = {
7
+ const D = new P(), p = {
8
8
  local: {
9
9
  name: "Local (SQLite)",
10
10
  description: "Local development setup with SQLite database - great for getting started",
11
11
  templateDir: "local",
12
- packageJson: (o, e, r, n) => {
13
- const t = n ? "workspace:*" : "latest";
12
+ packageJson: (n, e, r, a, o) => {
13
+ const t = a ? "workspace:*" : "latest";
14
14
  return {
15
- name: o,
15
+ name: n,
16
16
  version: "1.0.0",
17
17
  type: "module",
18
18
  scripts: {
@@ -23,6 +23,7 @@ const D = new I(), i = {
23
23
  },
24
24
  dependencies: {
25
25
  "@authhero/kysely-adapter": t,
26
+ ...o && { "@authhero/react-admin": t },
26
27
  "@authhero/widget": t,
27
28
  "@hono/swagger-ui": "^0.5.0",
28
29
  "@hono/zod-openapi": "^0.19.0",
@@ -48,10 +49,10 @@ const D = new I(), i = {
48
49
  name: "Cloudflare Workers (D1)",
49
50
  description: "Cloudflare Workers setup with D1 database",
50
51
  templateDir: "cloudflare",
51
- packageJson: (o, e, r, n) => {
52
- const t = n ? "workspace:*" : "latest";
52
+ packageJson: (n, e, r, a, o) => {
53
+ const t = a ? "workspace:*" : "latest";
53
54
  return {
54
- name: o,
55
+ name: n,
55
56
  version: "1.0.0",
56
57
  type: "module",
57
58
  scripts: {
@@ -71,6 +72,7 @@ const D = new I(), i = {
71
72
  dependencies: {
72
73
  "@authhero/drizzle": t,
73
74
  "@authhero/kysely-adapter": t,
75
+ ...o && { "@authhero/react-admin": t },
74
76
  "@authhero/widget": t,
75
77
  "@hono/swagger-ui": "^0.5.0",
76
78
  "@hono/zod-openapi": "^0.19.0",
@@ -96,10 +98,10 @@ const D = new I(), i = {
96
98
  name: "AWS SST (Lambda + DynamoDB)",
97
99
  description: "Serverless AWS deployment with Lambda, DynamoDB, and SST",
98
100
  templateDir: "aws-sst",
99
- packageJson: (o, e, r, n) => {
100
- const t = n ? "workspace:*" : "latest";
101
+ packageJson: (n, e, r, a, o) => {
102
+ const t = a ? "workspace:*" : "latest";
101
103
  return {
102
- name: o,
104
+ name: n,
103
105
  version: "1.0.0",
104
106
  type: "module",
105
107
  scripts: {
@@ -111,6 +113,7 @@ const D = new I(), i = {
111
113
  },
112
114
  dependencies: {
113
115
  "@authhero/aws": t,
116
+ ...o && { "@authhero/react-admin": t },
114
117
  "@authhero/widget": t,
115
118
  "@aws-sdk/client-dynamodb": "^3.0.0",
116
119
  "@aws-sdk/lib-dynamodb": "^3.0.0",
@@ -133,27 +136,28 @@ const D = new I(), i = {
133
136
  seedFile: "seed.ts"
134
137
  }
135
138
  };
136
- function N(o, e) {
137
- a.readdirSync(o).forEach((n) => {
138
- const t = l.join(o, n), s = l.join(e, n);
139
- a.lstatSync(t).isDirectory() ? (a.mkdirSync(s, { recursive: !0 }), N(t, s)) : a.copyFileSync(t, s);
139
+ function N(n, e) {
140
+ s.readdirSync(n).forEach((a) => {
141
+ const o = i.join(n, a), t = i.join(e, a);
142
+ s.lstatSync(o).isDirectory() ? (s.mkdirSync(t, { recursive: !0 }), N(o, t)) : s.copyFileSync(o, t);
140
143
  });
141
144
  }
142
- function P(o, e = !1, r = "authhero-local") {
143
- const n = o ? "control_plane" : "main", t = o ? "Control Plane" : "Main", s = [
145
+ function R(n, e = !1, r = "authhero-local", a) {
146
+ const o = n ? "control_plane" : "main", t = n ? "Control Plane" : "Main", c = [
144
147
  "https://manage.authhero.net/auth-callback",
145
148
  "https://local.authhero.net/auth-callback",
146
149
  "http://localhost:5173/auth-callback",
147
- "https://localhost:3000/auth-callback"
150
+ "https://localhost:3000/auth-callback",
151
+ ...a ? ["https://localhost:3000/admin/auth-callback"] : []
148
152
  ], d = e ? [
149
153
  `https://localhost.emobix.co.uk:8443/test/a/${r}/callback`,
150
154
  `https://localhost:8443/test/a/${r}/callback`
151
- ] : [], m = [...s, ...d], f = [
155
+ ] : [], f = [...c, ...d], h = [
152
156
  "https://manage.authhero.net",
153
157
  "https://local.authhero.net",
154
158
  "http://localhost:5173",
155
159
  "https://localhost:3000"
156
- ], v = e ? ["https://localhost:8443/", "https://localhost.emobix.co.uk:8443/"] : [], w = [...f, ...v], b = e ? `
160
+ ], C = e ? ["https://localhost:8443/", "https://localhost.emobix.co.uk:8443/"] : [], y = [...h, ...C], S = e ? `
157
161
  // Create OpenID Conformance Suite test clients and user
158
162
  console.log("Creating conformance test clients and user...");
159
163
 
@@ -171,7 +175,7 @@ function P(o, e = !1, r = "authhero-local") {
171
175
  ];
172
176
 
173
177
  try {
174
- await adapters.clients.create("${n}", {
178
+ await adapters.clients.create("${o}", {
175
179
  client_id: "conformance-test",
176
180
  client_secret: "conformanceTestSecret123",
177
181
  name: "Conformance Test Client",
@@ -189,7 +193,7 @@ function P(o, e = !1, r = "authhero-local") {
189
193
  }
190
194
 
191
195
  try {
192
- await adapters.clients.create("${n}", {
196
+ await adapters.clients.create("${o}", {
193
197
  client_id: "conformance-test2",
194
198
  client_secret: "conformanceTestSecret456",
195
199
  name: "Conformance Test Client 2",
@@ -209,7 +213,7 @@ function P(o, e = !1, r = "authhero-local") {
209
213
  // Create a conformance test user with ALL OIDC profile claims populated
210
214
  // This is required for OIDCC-5.4 (VerifyScopesReturnedInUserInfoClaims) test
211
215
  try {
212
- await adapters.users.create("${n}", {
216
+ await adapters.users.create("${o}", {
213
217
  user_id: \`\${USERNAME_PASSWORD_PROVIDER}|conformance-user\`,
214
218
  email: "conformance@example.com",
215
219
  email_verified: true,
@@ -244,7 +248,7 @@ function P(o, e = !1, r = "authhero-local") {
244
248
  try {
245
249
  const bcrypt = await import("bcryptjs");
246
250
  const hashedPassword = await bcrypt.hash("ConformanceTest123!", 10);
247
- await adapters.passwords.create("${n}", {
251
+ await adapters.passwords.create("${o}", {
248
252
  user_id: \`\${USERNAME_PASSWORD_PROVIDER}|conformance-user\`,
249
253
  password: hashedPassword,
250
254
  });
@@ -276,27 +280,58 @@ async function main() {
276
280
  await seed(adapters, {
277
281
  adminUsername,
278
282
  adminPassword,
279
- tenantId: "${n}",
283
+ tenantId: "${o}",
280
284
  tenantName: "${t}",
281
- isControlPlane: ${!!o},
282
- callbacks: ${JSON.stringify(m)},
283
- allowedLogoutUrls: ${JSON.stringify(w)},
285
+ isControlPlane: ${!!n},${n ? `
286
+ clientId: "default_client",` : ""}
287
+ callbacks: ${JSON.stringify(f)},
288
+ allowedLogoutUrls: ${JSON.stringify(y)},
284
289
  });
285
- ${b}
290
+ ${S}
286
291
  await db.destroy();
287
292
  }
288
293
 
289
294
  main().catch(console.error);
290
295
  `;
291
296
  }
292
- function R(o) {
293
- return o ? `import { Context } from "hono";
297
+ function U(n, e) {
298
+ const r = e ? `import fs from "fs";
299
+ ` : "", a = e ? `
300
+ const adminDistPath = path.resolve(
301
+ __dirname,
302
+ "../node_modules/@authhero/react-admin/dist",
303
+ );
304
+ const adminIndexPath = path.join(adminDistPath, "index.html");
305
+ ` : "", o = e ? `
306
+ // Add admin UI handler if the package is installed
307
+ if (fs.existsSync(adminIndexPath)) {
308
+ const issuer =
309
+ process.env.ISSUER || \`https://localhost:\${process.env.PORT || 3000}/\`;
310
+ const rawHtml = fs.readFileSync(adminIndexPath, "utf-8")
311
+ .replace(/src="\\.\\//g, 'src="/admin/')
312
+ .replace(/href="\\.\\//g, 'href="/admin/');
313
+ const configJson = JSON.stringify({
314
+ domain: issuer.replace(/\\/$/, ""),${n ? `
315
+ clientId: CONTROL_PLANE_CLIENT_ID,` : ""}
316
+ basePath: "/admin",
317
+ }).replace(/</g, "\\\\u003c");
318
+ configWithHandlers.adminIndexHtml = rawHtml.replace(
319
+ "</head>",
320
+ \`<script>window.__AUTHHERO_ADMIN_CONFIG__=\${configJson};<\/script>\\n</head>\`,
321
+ );
322
+ configWithHandlers.adminHandler = serveStatic({
323
+ root: adminDistPath,
324
+ rewriteRequestPath: (p: string) => p.replace("/admin", ""),
325
+ });
326
+ }
327
+ ` : "";
328
+ return n ? `import { Context } from "hono";
294
329
  import { swaggerUI } from "@hono/swagger-ui";
295
330
  import { AuthHeroConfig, DataAdapters } from "authhero";
296
331
  import { serveStatic } from "@hono/node-server/serve-static";
297
332
  import { initMultiTenant } from "@authhero/multi-tenancy";
298
333
  import path from "path";
299
- import { fileURLToPath } from "url";
334
+ ${r}import { fileURLToPath } from "url";
300
335
 
301
336
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
302
337
 
@@ -304,19 +339,23 @@ const widgetPath = path.resolve(
304
339
  __dirname,
305
340
  "../node_modules/@authhero/widget/dist/authhero-widget",
306
341
  );
307
-
342
+ ${a}
308
343
  // Control plane configuration
309
344
  const CONTROL_PLANE_TENANT_ID = "control_plane";
310
345
  const CONTROL_PLANE_CLIENT_ID = "default_client";
311
346
 
312
347
  export default function createApp(config: AuthHeroConfig & { dataAdapter: DataAdapters }) {
313
- // Initialize multi-tenant AuthHero - syncs resource servers, roles, and connections by default
314
- const { app } = initMultiTenant({
348
+ const configWithHandlers: AuthHeroConfig & { dataAdapter: DataAdapters } = {
315
349
  ...config,
316
350
  widgetHandler: serveStatic({
317
351
  root: widgetPath,
318
352
  rewriteRequestPath: (p) => p.replace("/u/widget", ""),
319
353
  }),
354
+ };
355
+ ${o}
356
+ // Initialize multi-tenant AuthHero - syncs resource servers, roles, and connections by default
357
+ const { app } = initMultiTenant({
358
+ ...configWithHandlers,
320
359
  controlPlane: {
321
360
  tenantId: CONTROL_PLANE_TENANT_ID,
322
361
  clientId: CONTROL_PLANE_CLIENT_ID,
@@ -350,7 +389,7 @@ import { AuthHeroConfig, init } from "authhero";
350
389
  import { swaggerUI } from "@hono/swagger-ui";
351
390
  import { serveStatic } from "@hono/node-server/serve-static";
352
391
  import path from "path";
353
- import { fileURLToPath } from "url";
392
+ ${r}import { fileURLToPath } from "url";
354
393
 
355
394
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
356
395
 
@@ -358,15 +397,17 @@ const widgetPath = path.resolve(
358
397
  __dirname,
359
398
  "../node_modules/@authhero/widget/dist/authhero-widget",
360
399
  );
361
-
400
+ ${a}
362
401
  export default function createApp(config: AuthHeroConfig) {
363
- const { app } = init({
402
+ const configWithHandlers: AuthHeroConfig = {
364
403
  ...config,
365
404
  widgetHandler: serveStatic({
366
405
  root: widgetPath,
367
406
  rewriteRequestPath: (p) => p.replace("/u/widget", ""),
368
407
  }),
369
- });
408
+ };
409
+ ${o}
410
+ const { app } = init(configWithHandlers);
370
411
 
371
412
  app
372
413
  .onError((err, ctx) => {
@@ -389,7 +430,7 @@ export default function createApp(config: AuthHeroConfig) {
389
430
  }
390
431
  `;
391
432
  }
392
- function O(o) {
433
+ function O(n) {
393
434
  return `import { D1Dialect } from "kysely-d1";
394
435
  import { Kysely } from "kysely";
395
436
  import createAdapters from "@authhero/kysely-adapter";
@@ -416,9 +457,9 @@ export default {
416
457
  adminUsername,
417
458
  adminPassword,
418
459
  issuer,
419
- tenantId: "${o ? "control_plane" : "main"}",
420
- tenantName: "${o ? "Control Plane" : "Main"}",
421
- isControlPlane: ${!!o},
460
+ tenantId: "${n ? "control_plane" : "main"}",
461
+ tenantName: "${n ? "Control Plane" : "Main"}",
462
+ isControlPlane: ${!!n},
422
463
  });
423
464
 
424
465
  return new Response(
@@ -449,12 +490,15 @@ export default {
449
490
  };
450
491
  `;
451
492
  }
452
- function U(o) {
453
- return o ? `import { Context } from "hono";
493
+ function L(n, e) {
494
+ const r = e ? `import adminIndexHtml from "./admin-index-html";
495
+ ` : "", a = e ? ` adminIndexHtml,
496
+ ` : "";
497
+ return n ? `import { Context } from "hono";
454
498
  import { swaggerUI } from "@hono/swagger-ui";
455
499
  import { AuthHeroConfig, DataAdapters } from "authhero";
456
500
  import { initMultiTenant } from "@authhero/multi-tenancy";
457
-
501
+ ${r}
458
502
  // Control plane configuration
459
503
  const CONTROL_PLANE_TENANT_ID = "control_plane";
460
504
  const CONTROL_PLANE_CLIENT_ID = "default_client";
@@ -463,7 +507,7 @@ export default function createApp(config: AuthHeroConfig & { dataAdapter: DataAd
463
507
  // Initialize multi-tenant AuthHero - syncs resource servers, roles, and connections by default
464
508
  const { app } = initMultiTenant({
465
509
  ...config,
466
- controlPlane: {
510
+ ${a} controlPlane: {
467
511
  tenantId: CONTROL_PLANE_TENANT_ID,
468
512
  clientId: CONTROL_PLANE_CLIENT_ID,
469
513
  },
@@ -495,9 +539,11 @@ export default function createApp(config: AuthHeroConfig & { dataAdapter: DataAd
495
539
  import { cors } from "hono/cors";
496
540
  import { AuthHeroConfig, init } from "authhero";
497
541
  import { swaggerUI } from "@hono/swagger-ui";
498
-
542
+ ${r}
499
543
  export default function createApp(config: AuthHeroConfig) {
500
- const { app } = init(config);
544
+ const { app } = init({
545
+ ...config,
546
+ ${a} });
501
547
 
502
548
  // Enable CORS for all origins in development
503
549
  app.use("*", cors({
@@ -529,8 +575,8 @@ export default function createApp(config: AuthHeroConfig) {
529
575
  }
530
576
  `;
531
577
  }
532
- function L(o) {
533
- return o ? `import { Context } from "hono";
578
+ function j(n) {
579
+ return n ? `import { Context } from "hono";
534
580
  import { swaggerUI } from "@hono/swagger-ui";
535
581
  import { AuthHeroConfig, DataAdapters } from "authhero";
536
582
  import { initMultiTenant } from "@authhero/multi-tenancy";
@@ -635,7 +681,7 @@ export default function createApp(config: AppConfig) {
635
681
  }
636
682
  `;
637
683
  }
638
- function j(o) {
684
+ function $(n) {
639
685
  return `import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
640
686
  import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb";
641
687
  import createAdapters from "@authhero/aws";
@@ -664,9 +710,9 @@ async function main() {
664
710
  await seed(adapters, {
665
711
  adminUsername,
666
712
  adminPassword,
667
- tenantId: "${o ? "control_plane" : "main"}",
668
- tenantName: "${o ? "Control Plane" : "Main"}",
669
- isControlPlane: ${!!o},
713
+ tenantId: "${n ? "control_plane" : "main"}",
714
+ tenantName: "${n ? "Control Plane" : "Main"}",
715
+ isControlPlane: ${!!n},
670
716
  });
671
717
 
672
718
  console.log("โœ… Database seeded successfully!");
@@ -675,24 +721,24 @@ async function main() {
675
721
  main().catch(console.error);
676
722
  `;
677
723
  }
678
- function $(o, e) {
679
- const r = l.join(o, "src");
680
- a.writeFileSync(
681
- l.join(r, "app.ts"),
682
- L(e)
683
- ), a.writeFileSync(
684
- l.join(r, "seed.ts"),
724
+ function H(n, e) {
725
+ const r = i.join(n, "src");
726
+ s.writeFileSync(
727
+ i.join(r, "app.ts"),
685
728
  j(e)
729
+ ), s.writeFileSync(
730
+ i.join(r, "seed.ts"),
731
+ $(e)
686
732
  );
687
733
  }
688
- function k() {
734
+ function I() {
689
735
  console.log("\\n" + "โ”€".repeat(50)), console.log("๐Ÿ” AuthHero deployed to AWS!"), console.log("๐Ÿ“š Check SST output for your API URL"), console.log(
690
736
  "๐Ÿš€ Open your server URL /setup to complete initial setup"
691
737
  ), console.log("๐ŸŒ Portal available at https://local.authhero.net"), console.log("โ”€".repeat(50) + "\\n");
692
738
  }
693
- function H(o) {
694
- const e = l.join(o, ".github", "workflows");
695
- a.mkdirSync(e, { recursive: !0 });
739
+ function M(n) {
740
+ const e = i.join(n, ".github", "workflows");
741
+ s.mkdirSync(e, { recursive: !0 });
696
742
  const r = `name: Unit tests
697
743
 
698
744
  on: push
@@ -714,7 +760,7 @@ jobs:
714
760
 
715
761
  - run: npm run type-check
716
762
  - run: npm test
717
- `, n = `name: Deploy to Dev
763
+ `, a = `name: Deploy to Dev
718
764
 
719
765
  on:
720
766
  push:
@@ -750,7 +796,7 @@ jobs:
750
796
  with:
751
797
  apiToken: \${{ secrets.CLOUDFLARE_API_TOKEN }}
752
798
  command: deploy
753
- `, t = `name: Deploy to Production
799
+ `, o = `name: Deploy to Production
754
800
 
755
801
  on:
756
802
  release:
@@ -779,9 +825,9 @@ jobs:
779
825
  apiToken: \${{ secrets.PROD_CLOUDFLARE_API_TOKEN }}
780
826
  command: deploy --env production
781
827
  `;
782
- a.writeFileSync(l.join(e, "unit-tests.yml"), r), a.writeFileSync(l.join(e, "deploy-dev.yml"), n), a.writeFileSync(l.join(e, "release.yml"), t), console.log("\\n๐Ÿ“ฆ GitHub CI workflows created!");
828
+ s.writeFileSync(i.join(e, "unit-tests.yml"), r), s.writeFileSync(i.join(e, "deploy-dev.yml"), a), s.writeFileSync(i.join(e, "release.yml"), o), console.log("\\n๐Ÿ“ฆ GitHub CI workflows created!");
783
829
  }
784
- function M(o) {
830
+ function F(n) {
785
831
  const e = {
786
832
  branches: ["main"],
787
833
  plugins: [
@@ -790,39 +836,39 @@ function M(o) {
790
836
  "@semantic-release/github"
791
837
  ]
792
838
  };
793
- a.writeFileSync(
794
- l.join(o, ".releaserc.json"),
839
+ s.writeFileSync(
840
+ i.join(n, ".releaserc.json"),
795
841
  JSON.stringify(e, null, 2)
796
842
  );
797
- const r = l.join(o, "package.json"), n = JSON.parse(a.readFileSync(r, "utf-8"));
798
- n.devDependencies = {
799
- ...n.devDependencies,
843
+ const r = i.join(n, "package.json"), a = JSON.parse(s.readFileSync(r, "utf-8"));
844
+ a.devDependencies = {
845
+ ...a.devDependencies,
800
846
  "semantic-release": "^24.0.0"
801
- }, n.scripts = {
802
- ...n.scripts,
847
+ }, a.scripts = {
848
+ ...a.scripts,
803
849
  test: 'echo "No tests yet"',
804
850
  "type-check": "tsc --noEmit"
805
- }, a.writeFileSync(r, JSON.stringify(n, null, 2));
851
+ }, s.writeFileSync(r, JSON.stringify(a, null, 2));
806
852
  }
807
- function A(o, e) {
808
- return new Promise((r, n) => {
809
- const t = E(o, [], {
853
+ function b(n, e) {
854
+ return new Promise((r, a) => {
855
+ const o = E(n, [], {
810
856
  cwd: e,
811
857
  shell: !0,
812
858
  stdio: "inherit"
813
859
  });
814
- t.on("close", (s) => {
815
- s === 0 ? r() : n(new Error(`Command failed with exit code ${s}`));
816
- }), t.on("error", n);
860
+ o.on("close", (t) => {
861
+ t === 0 ? r() : a(new Error(`Command failed with exit code ${t}`));
862
+ }), o.on("error", a);
817
863
  });
818
864
  }
819
- function F(o, e) {
820
- const r = l.join(o, "src");
821
- a.writeFileSync(
822
- l.join(r, "app.ts"),
823
- U(e)
824
- ), a.writeFileSync(
825
- l.join(r, "seed.ts"),
865
+ function W(n, e, r) {
866
+ const a = i.join(n, "src");
867
+ s.writeFileSync(
868
+ i.join(a, "app.ts"),
869
+ L(e, r)
870
+ ), s.writeFileSync(
871
+ i.join(a, "seed.ts"),
826
872
  O(e)
827
873
  );
828
874
  }
@@ -833,7 +879,7 @@ function x() {
833
879
  ), console.log("๐ŸŒ Portal available at https://local.authhero.net"), console.log("โ”€".repeat(50) + `
834
880
  `);
835
881
  }
836
- function _() {
882
+ function k() {
837
883
  console.log(`
838
884
  ` + "โ”€".repeat(50)), console.log("โœ… Self-signed certificates generated with openssl"), console.log("โš ๏ธ You may need to trust the certificate in your browser"), console.log("๐Ÿ” AuthHero server running at https://localhost:3000"), console.log("๐Ÿ“š API documentation available at https://localhost:3000/docs"), console.log(
839
885
  "๐Ÿš€ Open https://localhost:3000/setup to complete initial setup"
@@ -843,105 +889,124 @@ function _() {
843
889
  D.version("1.0.0").description("Create a new AuthHero project").argument("[project-name]", "name of the project").option("-t, --template <type>", "template type: local or cloudflare").option(
844
890
  "--package-manager <pm>",
845
891
  "package manager to use: npm, yarn, pnpm, or bun"
846
- ).option("--multi-tenant", "enable multi-tenant mode").option("--skip-install", "skip installing dependencies").option("--skip-migrate", "skip running database migrations").option("--skip-start", "skip starting the development server").option("--github-ci", "include GitHub CI workflows with semantic versioning").option("--conformance", "add OpenID conformance suite test clients").option(
892
+ ).option("--multi-tenant", "enable multi-tenant mode").option("--admin-ui", "include admin UI at /admin").option("--skip-install", "skip installing dependencies").option("--skip-migrate", "skip running database migrations").option("--skip-start", "skip starting the development server").option("--github-ci", "include GitHub CI workflows with semantic versioning").option("--conformance", "add OpenID conformance suite test clients").option(
847
893
  "--conformance-alias <alias>",
848
894
  "alias for conformance suite (default: authhero-local)"
849
895
  ).option(
850
896
  "--workspace",
851
897
  "use workspace:* dependencies for local monorepo development"
852
- ).option("-y, --yes", "skip all prompts and use defaults/provided options").action(async (o, e) => {
898
+ ).option("-y, --yes", "skip all prompts and use defaults/provided options").action(async (n, e) => {
853
899
  const r = e.yes === !0;
854
900
  console.log(`
855
901
  ๐Ÿ” Welcome to AuthHero!
856
902
  `);
857
- let n = o;
858
- n || (r ? (n = "auth-server", console.log(`Using default project name: ${n}`)) : n = (await g.prompt([
903
+ let a = n;
904
+ a || (r ? (a = "auth-server", console.log(`Using default project name: ${a}`)) : a = (await m.prompt([
859
905
  {
860
906
  type: "input",
861
907
  name: "projectName",
862
908
  message: "Project name:",
863
909
  default: "auth-server",
864
- validate: (p) => p !== "" || "Project name cannot be empty"
910
+ validate: (u) => u !== "" || "Project name cannot be empty"
865
911
  }
866
912
  ])).projectName);
867
- const t = l.join(process.cwd(), n);
868
- a.existsSync(t) && (console.error(`โŒ Project "${n}" already exists.`), process.exit(1));
869
- let s;
870
- e.template ? (["local", "cloudflare", "aws-sst"].includes(e.template) || (console.error(`โŒ Invalid template: ${e.template}`), console.error("Valid options: local, cloudflare, aws-sst"), process.exit(1)), s = e.template, console.log(`Using template: ${i[s].name}`)) : s = (await g.prompt([
913
+ const o = i.join(process.cwd(), a);
914
+ s.existsSync(o) && (console.error(`โŒ Project "${a}" already exists.`), process.exit(1));
915
+ let t;
916
+ e.template ? (["local", "cloudflare", "aws-sst"].includes(e.template) || (console.error(`โŒ Invalid template: ${e.template}`), console.error("Valid options: local, cloudflare, aws-sst"), process.exit(1)), t = e.template, console.log(`Using template: ${p[t].name}`)) : t = (await m.prompt([
871
917
  {
872
918
  type: "list",
873
919
  name: "setupType",
874
920
  message: "Select your setup type:",
875
921
  choices: [
876
922
  {
877
- name: `${i.local.name}
878
- ${i.local.description}`,
923
+ name: `${p.local.name}
924
+ ${p.local.description}`,
879
925
  value: "local",
880
- short: i.local.name
926
+ short: p.local.name
881
927
  },
882
928
  {
883
- name: `${i.cloudflare.name}
884
- ${i.cloudflare.description}`,
929
+ name: `${p.cloudflare.name}
930
+ ${p.cloudflare.description}`,
885
931
  value: "cloudflare",
886
- short: i.cloudflare.name
932
+ short: p.cloudflare.name
887
933
  },
888
934
  {
889
- name: `${i["aws-sst"].name}
890
- ${i["aws-sst"].description}`,
935
+ name: `${p["aws-sst"].name}
936
+ ${p["aws-sst"].description}`,
891
937
  value: "aws-sst",
892
- short: i["aws-sst"].name
938
+ short: p["aws-sst"].name
893
939
  }
894
940
  ]
895
941
  }
896
942
  ])).setupType;
897
- const d = e.multiTenant, m = e.conformance || !1, f = e.conformanceAlias || "authhero-local";
898
- m && console.log(
899
- `OpenID Conformance Suite: enabled (alias: ${f})`
943
+ let c;
944
+ e.multiTenant !== void 0 ? c = e.multiTenant : r ? c = !1 : c = (await m.prompt([
945
+ {
946
+ type: "confirm",
947
+ name: "multiTenant",
948
+ message: "Would you like to enable multi-tenant mode?",
949
+ default: !1
950
+ }
951
+ ])).multiTenant, c && console.log("Multi-tenant mode: enabled");
952
+ let d = !1;
953
+ (t === "local" || t === "cloudflare") && (e.adminUi !== void 0 ? d = e.adminUi : r ? d = !0 : d = (await m.prompt([
954
+ {
955
+ type: "confirm",
956
+ name: "adminUi",
957
+ message: "Would you like to include the admin UI at /admin?",
958
+ default: !0
959
+ }
960
+ ])).adminUi, d && console.log("Admin UI: enabled (available at /admin)"));
961
+ const f = e.conformance || !1, h = e.conformanceAlias || "authhero-local";
962
+ f && console.log(
963
+ `OpenID Conformance Suite: enabled (alias: ${h})`
900
964
  );
901
- const v = e.workspace || !1;
902
- v && console.log("Workspace mode: enabled (using workspace:* dependencies)");
903
- const w = i[s];
904
- a.mkdirSync(t, { recursive: !0 }), a.writeFileSync(
905
- l.join(t, "package.json"),
965
+ const C = e.workspace || !1;
966
+ C && console.log("Workspace mode: enabled (using workspace:* dependencies)");
967
+ const y = p[t];
968
+ s.mkdirSync(o, { recursive: !0 }), s.writeFileSync(
969
+ i.join(o, "package.json"),
906
970
  JSON.stringify(
907
- w.packageJson(n, d, m, v),
971
+ y.packageJson(a, c, f, C, d),
908
972
  null,
909
973
  2
910
974
  )
911
975
  );
912
- const b = w.templateDir, S = l.join(
976
+ const S = y.templateDir, _ = i.join(
913
977
  import.meta.url.replace("file://", "").replace("/create-authhero.js", ""),
914
- b
978
+ S
915
979
  );
916
- if (a.existsSync(S) ? N(S, t) : (console.error(`โŒ Template directory not found: ${S}`), process.exit(1)), s === "cloudflare" && F(t, d), s === "cloudflare") {
917
- const c = l.join(t, "wrangler.toml"), p = l.join(t, "wrangler.local.toml");
918
- a.existsSync(c) && a.copyFileSync(c, p);
919
- const u = l.join(t, ".dev.vars.example"), h = l.join(t, ".dev.vars");
920
- a.existsSync(u) && a.copyFileSync(u, h), console.log(
980
+ if (s.existsSync(_) ? N(_, o) : (console.error(`โŒ Template directory not found: ${_}`), process.exit(1)), t === "cloudflare" && W(o, c, d), t === "cloudflare") {
981
+ const l = i.join(o, "wrangler.toml"), u = i.join(o, "wrangler.local.toml");
982
+ s.existsSync(l) && s.copyFileSync(l, u);
983
+ const g = i.join(o, ".dev.vars.example"), w = i.join(o, ".dev.vars");
984
+ s.existsSync(g) && s.copyFileSync(g, w), console.log(
921
985
  "๐Ÿ“ Created wrangler.local.toml and .dev.vars for local development"
922
986
  );
923
987
  }
924
- let C = !1;
925
- if (s === "cloudflare" && (e.githubCi !== void 0 ? (C = e.githubCi, C && console.log("Including GitHub CI workflows with semantic versioning")) : r || (C = (await g.prompt([
988
+ let A = !1;
989
+ if (t === "cloudflare" && (e.githubCi !== void 0 ? (A = e.githubCi, A && console.log("Including GitHub CI workflows with semantic versioning")) : r || (A = (await m.prompt([
926
990
  {
927
991
  type: "confirm",
928
992
  name: "includeGithubCi",
929
993
  message: "Would you like to include GitHub CI with semantic versioning?",
930
994
  default: !1
931
995
  }
932
- ])).includeGithubCi), C && (H(t), M(t))), s === "local") {
933
- const c = P(
934
- d,
935
- m,
936
- f
996
+ ])).includeGithubCi), A && (M(o), F(o))), t === "local") {
997
+ const l = R(
998
+ c,
999
+ f,
1000
+ h,
1001
+ d
937
1002
  );
938
- a.writeFileSync(l.join(t, "src/seed.ts"), c);
939
- const p = R(d);
940
- a.writeFileSync(l.join(t, "src/app.ts"), p);
1003
+ s.writeFileSync(i.join(o, "src/seed.ts"), l);
1004
+ const u = U(c, d);
1005
+ s.writeFileSync(i.join(o, "src/app.ts"), u);
941
1006
  }
942
- if (s === "aws-sst" && $(t, d), m) {
943
- const c = {
944
- alias: f,
1007
+ if (t === "aws-sst" && H(o, c), f) {
1008
+ const l = {
1009
+ alias: h,
945
1010
  description: "AuthHero Conformance Test",
946
1011
  server: {
947
1012
  discoveryUrl: "http://host.docker.internal:3000/.well-known/openid-configuration"
@@ -958,32 +1023,32 @@ D.version("1.0.0").description("Create a new AuthHero project").argument("[proje
958
1023
  resourceUrl: "http://host.docker.internal:3000/userinfo"
959
1024
  }
960
1025
  };
961
- a.writeFileSync(
962
- l.join(t, "conformance-config.json"),
963
- JSON.stringify(c, null, 2)
1026
+ s.writeFileSync(
1027
+ i.join(o, "conformance-config.json"),
1028
+ JSON.stringify(l, null, 2)
964
1029
  ), console.log(
965
1030
  "๐Ÿ“ Created conformance-config.json for OpenID Conformance Suite"
966
1031
  );
967
1032
  }
968
- const T = d ? "multi-tenant" : "single-tenant";
1033
+ const T = c ? "multi-tenant" : "single-tenant";
969
1034
  console.log(
970
1035
  `
971
- โœ… Project "${n}" has been created with ${w.name} (${T}) setup!
1036
+ โœ… Project "${a}" has been created with ${y.name} (${T}) setup!
972
1037
  `
973
1038
  );
974
- let y;
975
- if (e.skipInstall ? y = !1 : r ? y = !0 : y = (await g.prompt([
1039
+ let v;
1040
+ if (e.skipInstall ? v = !1 : r ? v = !0 : v = (await m.prompt([
976
1041
  {
977
1042
  type: "confirm",
978
1043
  name: "shouldInstall",
979
1044
  message: "Would you like to install dependencies now?",
980
1045
  default: !0
981
1046
  }
982
- ])).shouldInstall, y) {
983
- let c;
1047
+ ])).shouldInstall, v) {
1048
+ let l;
984
1049
  e.packageManager ? (["npm", "yarn", "pnpm", "bun"].includes(e.packageManager) || (console.error(
985
1050
  `โŒ Invalid package manager: ${e.packageManager}`
986
- ), console.error("Valid options: npm, yarn, pnpm, bun"), process.exit(1)), c = e.packageManager) : r ? c = "pnpm" : c = (await g.prompt([
1051
+ ), console.error("Valid options: npm, yarn, pnpm, bun"), process.exit(1)), l = e.packageManager) : r ? l = "pnpm" : l = (await m.prompt([
987
1052
  {
988
1053
  type: "list",
989
1054
  name: "packageManager",
@@ -997,64 +1062,64 @@ D.version("1.0.0").description("Create a new AuthHero project").argument("[proje
997
1062
  default: "pnpm"
998
1063
  }
999
1064
  ])).packageManager, console.log(`
1000
- ๐Ÿ“ฆ Installing dependencies with ${c}...
1065
+ ๐Ÿ“ฆ Installing dependencies with ${l}...
1001
1066
  `);
1002
1067
  try {
1003
- const p = c === "pnpm" ? "pnpm install --ignore-workspace" : `${c} install`;
1004
- if (await A(p, t), s === "local" && (console.log(`
1068
+ const u = l === "pnpm" ? "pnpm install --ignore-workspace" : `${l} install`;
1069
+ if (await b(u, o), t === "local" && (console.log(`
1005
1070
  ๐Ÿ”ง Building native modules...
1006
- `), await A("npm rebuild better-sqlite3", t)), console.log(`
1071
+ `), await b("npm rebuild better-sqlite3", o)), console.log(`
1007
1072
  โœ… Dependencies installed successfully!
1008
- `), (s === "local" || s === "cloudflare") && !e.skipMigrate) {
1009
- let h;
1010
- r ? h = !0 : h = (await g.prompt([
1073
+ `), (t === "local" || t === "cloudflare") && !e.skipMigrate) {
1074
+ let w;
1075
+ r ? w = !0 : w = (await m.prompt([
1011
1076
  {
1012
1077
  type: "confirm",
1013
1078
  name: "shouldMigrate",
1014
1079
  message: "Would you like to run database migrations?",
1015
1080
  default: !0
1016
1081
  }
1017
- ])).shouldMigrate, h && (console.log(`
1082
+ ])).shouldMigrate, w && (console.log(`
1018
1083
  ๐Ÿ”„ Running migrations...
1019
- `), await A(`${c} run migrate`, t));
1084
+ `), await b(`${l} run migrate`, o));
1020
1085
  }
1021
- let u;
1022
- e.skipStart || r ? u = !1 : u = (await g.prompt([
1086
+ let g;
1087
+ e.skipStart || r ? g = !1 : g = (await m.prompt([
1023
1088
  {
1024
1089
  type: "confirm",
1025
1090
  name: "shouldStart",
1026
1091
  message: "Would you like to start the development server?",
1027
1092
  default: !0
1028
1093
  }
1029
- ])).shouldStart, u && (s === "cloudflare" ? x() : s === "aws-sst" ? k() : _(), console.log(`๐Ÿš€ Starting development server...
1030
- `), await A(`${c} run dev`, t)), r && !u && (console.log(`
1094
+ ])).shouldStart, g && (t === "cloudflare" ? x() : t === "aws-sst" ? I() : k(), console.log(`๐Ÿš€ Starting development server...
1095
+ `), await b(`${l} run dev`, o)), r && !g && (console.log(`
1031
1096
  โœ… Setup complete!`), console.log(`
1032
- To start the development server:`), console.log(` cd ${n}`), console.log(" npm run dev"), s === "cloudflare" ? x() : s === "aws-sst" ? k() : _());
1033
- } catch (p) {
1097
+ To start the development server:`), console.log(` cd ${a}`), console.log(" npm run dev"), t === "cloudflare" ? x() : t === "aws-sst" ? I() : k());
1098
+ } catch (u) {
1034
1099
  console.error(`
1035
- โŒ An error occurred:`, p), process.exit(1);
1100
+ โŒ An error occurred:`, u), process.exit(1);
1036
1101
  }
1037
1102
  }
1038
- y || (console.log("Next steps:"), console.log(` cd ${n}`), s === "local" ? (console.log(" npm install"), console.log(" npm run migrate"), console.log(" npm run dev"), console.log(
1103
+ v || (console.log("Next steps:"), console.log(` cd ${a}`), t === "local" ? (console.log(" npm install"), console.log(" npm run migrate"), console.log(" npm run dev"), console.log(
1039
1104
  `
1040
1105
  Open https://localhost:3000/setup to complete initial setup`
1041
- )) : s === "cloudflare" ? (console.log(" npm install"), console.log(
1106
+ )) : t === "cloudflare" ? (console.log(" npm install"), console.log(
1042
1107
  " npm run migrate # or npm run db:migrate:remote for production"
1043
1108
  ), console.log(" npm run dev # or npm run dev:remote for production"), console.log(
1044
1109
  `
1045
1110
  Open https://localhost:3000/setup to complete initial setup`
1046
- )) : s === "aws-sst" && (console.log(" npm install"), console.log(" npm run dev # Deploys to AWS in development mode"), console.log(
1111
+ )) : t === "aws-sst" && (console.log(" npm install"), console.log(" npm run dev # Deploys to AWS in development mode"), console.log(
1047
1112
  `
1048
1113
  Open your server URL /setup to complete initial setup`
1049
1114
  )), console.log(`
1050
- Server will be available at: https://localhost:3000`), console.log("Portal available at: https://local.authhero.net"), m && (console.log(`
1115
+ Server will be available at: https://localhost:3000`), console.log("Portal available at: https://local.authhero.net"), f && (console.log(`
1051
1116
  ๐Ÿงช OpenID Conformance Suite Testing:`), console.log(
1052
1117
  " 1. Clone and start the conformance suite (if not already running):"
1053
1118
  ), console.log(
1054
1119
  " git clone https://gitlab.com/openid/conformance-suite.git"
1055
1120
  ), console.log(" cd conformance-suite && mvn clean package"), console.log(" docker-compose up -d"), console.log(" 2. Open https://localhost.emobix.co.uk:8443"), console.log(
1056
1121
  " 3. Create a test plan and use conformance-config.json for settings"
1057
- ), console.log(` 4. Use alias: ${f}`)), console.log(`
1122
+ ), console.log(` 4. Use alias: ${h}`)), console.log(`
1058
1123
  For more information, visit: https://authhero.net/docs
1059
1124
  `));
1060
1125
  });
@@ -34,7 +34,9 @@ export default function createApp(config: AuthHeroConfig) {
34
34
  if (fs.existsSync(adminIndexPath)) {
35
35
  const issuer =
36
36
  process.env.ISSUER || `https://localhost:${process.env.PORT || 3000}/`;
37
- const rawHtml = fs.readFileSync(adminIndexPath, "utf-8");
37
+ const rawHtml = fs.readFileSync(adminIndexPath, "utf-8")
38
+ .replace(/src="\.\/assets\//g, 'src="/admin/assets/')
39
+ .replace(/href="\.\/assets\//g, 'href="/admin/assets/');
38
40
  const configJson = JSON.stringify({
39
41
  domain: issuer.replace(/\/$/, ""),
40
42
  basePath: "/admin",
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "type": "git",
6
6
  "url": "https://github.com/markusahlstrand/authhero"
7
7
  },
8
- "version": "0.34.0",
8
+ "version": "0.35.0",
9
9
  "type": "module",
10
10
  "main": "dist/create-authhero.js",
11
11
  "bin": {