create-authhero 0.34.0 โ†’ 0.36.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,45 @@ 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
107
+ .readFileSync(adminIndexPath, "utf-8")
108
+ .replace(/src="\.\/assets\//g, 'src="/admin/assets/')
109
+ .replace(/href="\.\/assets\//g, 'href="/admin/assets/');
110
+ const configScript = `<script>window.__AUTHHERO_ADMIN_CONFIG__={domain:window.location.origin,basePath:"/admin"}</script>`;
111
+ const injectedHtml = adminHtml.replace(
112
+ "</head>",
113
+ configScript + "\n</head>",
114
+ );
115
+
116
+ // Write injected HTML to CDN assets (for direct /admin/ access)
117
+ fs.writeFileSync(path.join(adminTargetDir, "index.html"), injectedHtml);
118
+
119
+ // Write as TS module for worker to import (for SPA fallback on deep links)
120
+ const srcDir = path.join(__dirname, "src");
121
+ fs.writeFileSync(
122
+ path.join(srcDir, "admin-index-html.ts"),
123
+ `export default ${JSON.stringify(injectedHtml)};\n`,
124
+ );
125
+ console.log("โœ… Admin UI assets copied and configured");
126
+ }
127
+
89
128
  console.log(`โœ… Assets copied to ${targetDir}`);
90
129
  } catch (error) {
91
130
  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,22 @@ 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() {
689
- console.log("\\n" + "โ”€".repeat(50)), console.log("๐Ÿ” AuthHero deployed to AWS!"), console.log("๐Ÿ“š Check SST output for your API URL"), console.log(
690
- "๐Ÿš€ Open your server URL /setup to complete initial setup"
691
- ), console.log("๐ŸŒ Portal available at https://local.authhero.net"), console.log("โ”€".repeat(50) + "\\n");
734
+ function I() {
735
+ console.log("\\n" + "โ”€".repeat(50)), console.log("๐Ÿ” AuthHero deployed to AWS!"), console.log("๐Ÿ“š Check SST output for your API URL"), console.log("๐Ÿš€ Open your server URL /setup to complete initial setup"), console.log("๐ŸŒ Portal available at https://local.authhero.net"), console.log("โ”€".repeat(50) + "\\n");
692
736
  }
693
- function H(o) {
694
- const e = l.join(o, ".github", "workflows");
695
- a.mkdirSync(e, { recursive: !0 });
737
+ function M(n) {
738
+ const e = i.join(n, ".github", "workflows");
739
+ s.mkdirSync(e, { recursive: !0 });
696
740
  const r = `name: Unit tests
697
741
 
698
742
  on: push
@@ -714,7 +758,7 @@ jobs:
714
758
 
715
759
  - run: npm run type-check
716
760
  - run: npm test
717
- `, n = `name: Deploy to Dev
761
+ `, a = `name: Deploy to Dev
718
762
 
719
763
  on:
720
764
  push:
@@ -750,7 +794,7 @@ jobs:
750
794
  with:
751
795
  apiToken: \${{ secrets.CLOUDFLARE_API_TOKEN }}
752
796
  command: deploy
753
- `, t = `name: Deploy to Production
797
+ `, o = `name: Deploy to Production
754
798
 
755
799
  on:
756
800
  release:
@@ -779,9 +823,9 @@ jobs:
779
823
  apiToken: \${{ secrets.PROD_CLOUDFLARE_API_TOKEN }}
780
824
  command: deploy --env production
781
825
  `;
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!");
826
+ 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
827
  }
784
- function M(o) {
828
+ function F(n) {
785
829
  const e = {
786
830
  branches: ["main"],
787
831
  plugins: [
@@ -790,158 +834,179 @@ function M(o) {
790
834
  "@semantic-release/github"
791
835
  ]
792
836
  };
793
- a.writeFileSync(
794
- l.join(o, ".releaserc.json"),
837
+ s.writeFileSync(
838
+ i.join(n, ".releaserc.json"),
795
839
  JSON.stringify(e, null, 2)
796
840
  );
797
- const r = l.join(o, "package.json"), n = JSON.parse(a.readFileSync(r, "utf-8"));
798
- n.devDependencies = {
799
- ...n.devDependencies,
841
+ const r = i.join(n, "package.json"), a = JSON.parse(s.readFileSync(r, "utf-8"));
842
+ a.devDependencies = {
843
+ ...a.devDependencies,
800
844
  "semantic-release": "^24.0.0"
801
- }, n.scripts = {
802
- ...n.scripts,
845
+ }, a.scripts = {
846
+ ...a.scripts,
803
847
  test: 'echo "No tests yet"',
804
848
  "type-check": "tsc --noEmit"
805
- }, a.writeFileSync(r, JSON.stringify(n, null, 2));
849
+ }, s.writeFileSync(r, JSON.stringify(a, null, 2));
806
850
  }
807
- function A(o, e) {
808
- return new Promise((r, n) => {
809
- const t = E(o, [], {
851
+ function b(n, e) {
852
+ return new Promise((r, a) => {
853
+ const o = E(n, [], {
810
854
  cwd: e,
811
855
  shell: !0,
812
856
  stdio: "inherit"
813
857
  });
814
- t.on("close", (s) => {
815
- s === 0 ? r() : n(new Error(`Command failed with exit code ${s}`));
816
- }), t.on("error", n);
858
+ o.on("close", (t) => {
859
+ t === 0 ? r() : a(new Error(`Command failed with exit code ${t}`));
860
+ }), o.on("error", a);
817
861
  });
818
862
  }
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"),
863
+ function W(n, e, r) {
864
+ const a = i.join(n, "src");
865
+ s.writeFileSync(
866
+ i.join(a, "app.ts"),
867
+ L(e, r)
868
+ ), s.writeFileSync(
869
+ i.join(a, "seed.ts"),
826
870
  O(e)
827
871
  );
828
872
  }
829
873
  function x() {
830
874
  console.log(`
831
- ` + "โ”€".repeat(50)), console.log("๐Ÿ” AuthHero server running at https://localhost:3000"), console.log("๐Ÿ“š API documentation available at https://localhost:3000/docs"), console.log(
832
- "๐Ÿš€ Open https://localhost:3000/setup to complete initial setup"
833
- ), console.log("๐ŸŒ Portal available at https://local.authhero.net"), console.log("โ”€".repeat(50) + `
875
+ ` + "โ”€".repeat(50)), console.log("๐Ÿ” AuthHero server running at https://localhost:3000"), console.log("๐Ÿ“š API documentation available at https://localhost:3000/docs"), console.log("๐Ÿš€ Open https://localhost:3000/setup to complete initial setup"), console.log("๐ŸŒ Portal available at https://local.authhero.net"), console.log("โ”€".repeat(50) + `
834
876
  `);
835
877
  }
836
- function _() {
878
+ function k() {
837
879
  console.log(`
838
- ` + "โ”€".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
- "๐Ÿš€ Open https://localhost:3000/setup to complete initial setup"
840
- ), console.log("๐ŸŒ Portal available at https://local.authhero.net"), console.log("โ”€".repeat(50) + `
880
+ ` + "โ”€".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("๐Ÿš€ Open https://localhost:3000/setup to complete initial setup"), console.log("๐ŸŒ Portal available at https://local.authhero.net"), console.log("โ”€".repeat(50) + `
841
881
  `);
842
882
  }
843
883
  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
884
  "--package-manager <pm>",
845
885
  "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(
886
+ ).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
887
  "--conformance-alias <alias>",
848
888
  "alias for conformance suite (default: authhero-local)"
849
889
  ).option(
850
890
  "--workspace",
851
891
  "use workspace:* dependencies for local monorepo development"
852
- ).option("-y, --yes", "skip all prompts and use defaults/provided options").action(async (o, e) => {
892
+ ).option("-y, --yes", "skip all prompts and use defaults/provided options").action(async (n, e) => {
853
893
  const r = e.yes === !0;
854
894
  console.log(`
855
895
  ๐Ÿ” Welcome to AuthHero!
856
896
  `);
857
- let n = o;
858
- n || (r ? (n = "auth-server", console.log(`Using default project name: ${n}`)) : n = (await g.prompt([
897
+ let a = n;
898
+ a || (r ? (a = "auth-server", console.log(`Using default project name: ${a}`)) : a = (await m.prompt([
859
899
  {
860
900
  type: "input",
861
901
  name: "projectName",
862
902
  message: "Project name:",
863
903
  default: "auth-server",
864
- validate: (p) => p !== "" || "Project name cannot be empty"
904
+ validate: (u) => u !== "" || "Project name cannot be empty"
865
905
  }
866
906
  ])).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([
907
+ const o = i.join(process.cwd(), a);
908
+ s.existsSync(o) && (console.error(`โŒ Project "${a}" already exists.`), process.exit(1));
909
+ let t;
910
+ 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
911
  {
872
912
  type: "list",
873
913
  name: "setupType",
874
914
  message: "Select your setup type:",
875
915
  choices: [
876
916
  {
877
- name: `${i.local.name}
878
- ${i.local.description}`,
917
+ name: `${p.local.name}
918
+ ${p.local.description}`,
879
919
  value: "local",
880
- short: i.local.name
920
+ short: p.local.name
881
921
  },
882
922
  {
883
- name: `${i.cloudflare.name}
884
- ${i.cloudflare.description}`,
923
+ name: `${p.cloudflare.name}
924
+ ${p.cloudflare.description}`,
885
925
  value: "cloudflare",
886
- short: i.cloudflare.name
926
+ short: p.cloudflare.name
887
927
  },
888
928
  {
889
- name: `${i["aws-sst"].name}
890
- ${i["aws-sst"].description}`,
929
+ name: `${p["aws-sst"].name}
930
+ ${p["aws-sst"].description}`,
891
931
  value: "aws-sst",
892
- short: i["aws-sst"].name
932
+ short: p["aws-sst"].name
893
933
  }
894
934
  ]
895
935
  }
896
936
  ])).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})`
937
+ let c;
938
+ e.multiTenant !== void 0 ? c = e.multiTenant : r ? c = !1 : c = (await m.prompt([
939
+ {
940
+ type: "confirm",
941
+ name: "multiTenant",
942
+ message: "Would you like to enable multi-tenant mode?",
943
+ default: !1
944
+ }
945
+ ])).multiTenant, c && console.log("Multi-tenant mode: enabled");
946
+ let d = !1;
947
+ (t === "local" || t === "cloudflare") && (e.adminUi !== void 0 ? d = e.adminUi : r ? d = !0 : d = (await m.prompt([
948
+ {
949
+ type: "confirm",
950
+ name: "adminUi",
951
+ message: "Would you like to include the admin UI at /admin?",
952
+ default: !0
953
+ }
954
+ ])).adminUi, d && console.log("Admin UI: enabled (available at /admin)"));
955
+ const f = e.conformance || !1, h = e.conformanceAlias || "authhero-local";
956
+ f && console.log(
957
+ `OpenID Conformance Suite: enabled (alias: ${h})`
900
958
  );
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"),
959
+ const C = e.workspace || !1;
960
+ C && console.log("Workspace mode: enabled (using workspace:* dependencies)");
961
+ const y = p[t];
962
+ s.mkdirSync(o, { recursive: !0 }), s.writeFileSync(
963
+ i.join(o, "package.json"),
906
964
  JSON.stringify(
907
- w.packageJson(n, d, m, v),
965
+ y.packageJson(
966
+ a,
967
+ c,
968
+ f,
969
+ C,
970
+ d
971
+ ),
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,62 @@ 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(
1047
- `
1048
- Open your server URL /setup to complete initial setup`
1049
- )), console.log(`
1050
- Server will be available at: https://localhost:3000`), console.log("Portal available at: https://local.authhero.net"), m && (console.log(`
1111
+ )) : t === "aws-sst" && (console.log(" npm install"), console.log(" npm run dev # Deploys to AWS in development mode"), console.log(`
1112
+ Open your server URL /setup to complete initial setup`)), console.log(`
1113
+ Server will be available at: https://localhost:3000`), console.log("Portal available at: https://local.authhero.net"), f && (console.log(`
1051
1114
  ๐Ÿงช OpenID Conformance Suite Testing:`), console.log(
1052
1115
  " 1. Clone and start the conformance suite (if not already running):"
1053
1116
  ), console.log(
1054
1117
  " git clone https://gitlab.com/openid/conformance-suite.git"
1055
1118
  ), 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
1119
  " 3. Create a test plan and use conformance-config.json for settings"
1057
- ), console.log(` 4. Use alias: ${f}`)), console.log(`
1120
+ ), console.log(` 4. Use alias: ${h}`)), console.log(`
1058
1121
  For more information, visit: https://authhero.net/docs
1059
1122
  `));
1060
1123
  });
@@ -34,7 +34,10 @@ 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
38
+ .readFileSync(adminIndexPath, "utf-8")
39
+ .replace(/src="\.\/assets\//g, 'src="/admin/assets/')
40
+ .replace(/href="\.\/assets\//g, 'href="/admin/assets/');
38
41
  const configJson = JSON.stringify({
39
42
  domain: issuer.replace(/\/$/, ""),
40
43
  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.36.0",
9
9
  "type": "module",
10
10
  "main": "dist/create-authhero.js",
11
11
  "bin": {