create-authhero 0.39.0 → 0.41.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.
- package/dist/create-authhero.js +225 -143
- package/package.json +2 -1
package/dist/create-authhero.js
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { Command as
|
|
2
|
+
import { Command as R } from "commander";
|
|
3
3
|
import m from "inquirer";
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
|
|
4
|
+
import i from "fs";
|
|
5
|
+
import a from "path";
|
|
6
|
+
import { fileURLToPath as U } from "url";
|
|
7
|
+
import { spawn as O } from "child_process";
|
|
8
|
+
const T = new R(), p = {
|
|
8
9
|
local: {
|
|
9
10
|
name: "Local (SQLite)",
|
|
10
11
|
description: "Local development setup with SQLite database - great for getting started",
|
|
11
12
|
templateDir: "local",
|
|
12
|
-
packageJson: (
|
|
13
|
+
packageJson: (s, e, o, r, n) => {
|
|
13
14
|
const t = r ? "workspace:*" : "latest";
|
|
14
15
|
return {
|
|
15
|
-
name:
|
|
16
|
+
name: s,
|
|
16
17
|
version: "1.0.0",
|
|
17
18
|
type: "module",
|
|
18
19
|
scripts: {
|
|
@@ -23,7 +24,7 @@ const D = new E(), p = {
|
|
|
23
24
|
},
|
|
24
25
|
dependencies: {
|
|
25
26
|
"@authhero/kysely-adapter": t,
|
|
26
|
-
...
|
|
27
|
+
...n && { "@authhero/react-admin": t },
|
|
27
28
|
"@authhero/widget": t,
|
|
28
29
|
"@hono/swagger-ui": "^0.5.0",
|
|
29
30
|
"@hono/zod-openapi": "^0.19.0",
|
|
@@ -33,7 +34,7 @@ const D = new E(), p = {
|
|
|
33
34
|
hono: "^4.6.0",
|
|
34
35
|
kysely: "latest",
|
|
35
36
|
...e && { "@authhero/multi-tenancy": t },
|
|
36
|
-
...
|
|
37
|
+
...o && { bcryptjs: "latest" }
|
|
37
38
|
},
|
|
38
39
|
devDependencies: {
|
|
39
40
|
"@types/better-sqlite3": "^7.6.0",
|
|
@@ -49,10 +50,10 @@ const D = new E(), p = {
|
|
|
49
50
|
name: "Cloudflare Workers (D1)",
|
|
50
51
|
description: "Cloudflare Workers setup with D1 database",
|
|
51
52
|
templateDir: "cloudflare",
|
|
52
|
-
packageJson: (
|
|
53
|
+
packageJson: (s, e, o, r, n) => {
|
|
53
54
|
const t = r ? "workspace:*" : "latest";
|
|
54
55
|
return {
|
|
55
|
-
name:
|
|
56
|
+
name: s,
|
|
56
57
|
version: "1.0.0",
|
|
57
58
|
type: "module",
|
|
58
59
|
scripts: {
|
|
@@ -72,7 +73,7 @@ const D = new E(), p = {
|
|
|
72
73
|
dependencies: {
|
|
73
74
|
"@authhero/drizzle": t,
|
|
74
75
|
"@authhero/kysely-adapter": t,
|
|
75
|
-
...
|
|
76
|
+
...n && { "@authhero/react-admin": t },
|
|
76
77
|
"@authhero/widget": t,
|
|
77
78
|
"@hono/swagger-ui": "^0.5.0",
|
|
78
79
|
"@hono/zod-openapi": "^0.19.0",
|
|
@@ -81,7 +82,7 @@ const D = new E(), p = {
|
|
|
81
82
|
kysely: "latest",
|
|
82
83
|
"kysely-d1": "latest",
|
|
83
84
|
...e && { "@authhero/multi-tenancy": t },
|
|
84
|
-
...
|
|
85
|
+
...o && { bcryptjs: "latest" }
|
|
85
86
|
},
|
|
86
87
|
devDependencies: {
|
|
87
88
|
"@cloudflare/workers-types": "^4.0.0",
|
|
@@ -98,10 +99,10 @@ const D = new E(), p = {
|
|
|
98
99
|
name: "AWS SST (Lambda + DynamoDB)",
|
|
99
100
|
description: "Serverless AWS deployment with Lambda, DynamoDB, and SST",
|
|
100
101
|
templateDir: "aws-sst",
|
|
101
|
-
packageJson: (
|
|
102
|
+
packageJson: (s, e, o, r, n) => {
|
|
102
103
|
const t = r ? "workspace:*" : "latest";
|
|
103
104
|
return {
|
|
104
|
-
name:
|
|
105
|
+
name: s,
|
|
105
106
|
version: "1.0.0",
|
|
106
107
|
type: "module",
|
|
107
108
|
scripts: {
|
|
@@ -113,7 +114,7 @@ const D = new E(), p = {
|
|
|
113
114
|
},
|
|
114
115
|
dependencies: {
|
|
115
116
|
"@authhero/aws": t,
|
|
116
|
-
...
|
|
117
|
+
...n && { "@authhero/react-admin": t },
|
|
117
118
|
"@authhero/widget": t,
|
|
118
119
|
"@aws-sdk/client-dynamodb": "^3.0.0",
|
|
119
120
|
"@aws-sdk/lib-dynamodb": "^3.0.0",
|
|
@@ -122,7 +123,7 @@ const D = new E(), p = {
|
|
|
122
123
|
authhero: t,
|
|
123
124
|
hono: "^4.6.0",
|
|
124
125
|
...e && { "@authhero/multi-tenancy": t },
|
|
125
|
-
...
|
|
126
|
+
...o && { bcryptjs: "latest" }
|
|
126
127
|
},
|
|
127
128
|
devDependencies: {
|
|
128
129
|
"@types/aws-lambda": "^8.10.0",
|
|
@@ -136,34 +137,42 @@ const D = new E(), p = {
|
|
|
136
137
|
seedFile: "seed.ts"
|
|
137
138
|
}
|
|
138
139
|
};
|
|
139
|
-
function
|
|
140
|
-
|
|
141
|
-
const
|
|
142
|
-
|
|
140
|
+
function P(s, e) {
|
|
141
|
+
i.readdirSync(s).forEach((r) => {
|
|
142
|
+
const n = a.join(s, r), t = a.join(e, r);
|
|
143
|
+
i.lstatSync(n).isDirectory() ? (i.mkdirSync(t, { recursive: !0 }), P(n, t)) : i.copyFileSync(n, t);
|
|
143
144
|
});
|
|
144
145
|
}
|
|
145
|
-
function
|
|
146
|
-
const
|
|
146
|
+
function L(s, e = !1, o = "authhero-local", r) {
|
|
147
|
+
const n = s ? "control_plane" : "main", t = s ? "Control Plane" : "Main", c = [
|
|
147
148
|
"https://manage.authhero.net/auth-callback",
|
|
148
149
|
"https://local.authhero.net/auth-callback",
|
|
149
150
|
"http://localhost:5173/auth-callback",
|
|
150
151
|
"http://localhost:3000/auth-callback",
|
|
151
152
|
...r ? ["http://localhost:3000/admin/auth-callback"] : []
|
|
152
153
|
], d = e ? [
|
|
153
|
-
`https://localhost.emobix.co.uk:8443/test/a/${
|
|
154
|
-
`https://localhost:8443/test/a/${
|
|
154
|
+
`https://localhost.emobix.co.uk:8443/test/a/${o}/callback`,
|
|
155
|
+
`https://localhost:8443/test/a/${o}/callback`
|
|
155
156
|
] : [], f = [...c, ...d], h = [
|
|
156
157
|
"https://manage.authhero.net",
|
|
157
158
|
"https://local.authhero.net",
|
|
158
159
|
"http://localhost:5173",
|
|
159
160
|
"http://localhost:3000"
|
|
160
|
-
], C = e ? ["https://localhost:8443/", "https://localhost.emobix.co.uk:8443/"] : [], y = [...h, ...C],
|
|
161
|
+
], C = e ? ["https://localhost:8443/", "https://localhost.emobix.co.uk:8443/"] : [], y = [...h, ...C], A = e ? `
|
|
161
162
|
// Create OpenID Conformance Suite test clients and user
|
|
162
163
|
console.log("Creating conformance test clients and user...");
|
|
163
|
-
|
|
164
|
+
|
|
165
|
+
// The OIDCC basic test plan calls /token without an audience param. AuthHero
|
|
166
|
+
// requires either an explicit audience or a tenant default_audience to mint
|
|
167
|
+
// an access token, so set one here for the conformance setup.
|
|
168
|
+
await adapters.tenants.update("${n}", {
|
|
169
|
+
default_audience: "urn:authhero:management",
|
|
170
|
+
});
|
|
171
|
+
console.log("✅ Set tenant default_audience for conformance");
|
|
172
|
+
|
|
164
173
|
const conformanceCallbacks = [
|
|
165
|
-
"https://localhost.emobix.co.uk:8443/test/a/${
|
|
166
|
-
"https://localhost:8443/test/a/${
|
|
174
|
+
"https://localhost.emobix.co.uk:8443/test/a/${o}/callback",
|
|
175
|
+
"https://localhost:8443/test/a/${o}/callback",
|
|
167
176
|
];
|
|
168
177
|
const conformanceLogoutUrls = [
|
|
169
178
|
"https://localhost:8443/",
|
|
@@ -175,7 +184,7 @@ function R(a, e = !1, n = "authhero-local", r) {
|
|
|
175
184
|
];
|
|
176
185
|
|
|
177
186
|
try {
|
|
178
|
-
await adapters.clients.create("${
|
|
187
|
+
await adapters.clients.create("${n}", {
|
|
179
188
|
client_id: "conformance-test",
|
|
180
189
|
client_secret: "conformanceTestSecret123",
|
|
181
190
|
name: "Conformance Test Client",
|
|
@@ -193,7 +202,7 @@ function R(a, e = !1, n = "authhero-local", r) {
|
|
|
193
202
|
}
|
|
194
203
|
|
|
195
204
|
try {
|
|
196
|
-
await adapters.clients.create("${
|
|
205
|
+
await adapters.clients.create("${n}", {
|
|
197
206
|
client_id: "conformance-test2",
|
|
198
207
|
client_secret: "conformanceTestSecret456",
|
|
199
208
|
name: "Conformance Test Client 2",
|
|
@@ -213,7 +222,7 @@ function R(a, e = !1, n = "authhero-local", r) {
|
|
|
213
222
|
// Create a conformance test user with ALL OIDC profile claims populated
|
|
214
223
|
// This is required for OIDCC-5.4 (VerifyScopesReturnedInUserInfoClaims) test
|
|
215
224
|
try {
|
|
216
|
-
await adapters.users.create("${
|
|
225
|
+
await adapters.users.create("${n}", {
|
|
217
226
|
user_id: \`\${USERNAME_PASSWORD_PROVIDER}|conformance-user\`,
|
|
218
227
|
email: "conformance@example.com",
|
|
219
228
|
email_verified: true,
|
|
@@ -248,7 +257,7 @@ function R(a, e = !1, n = "authhero-local", r) {
|
|
|
248
257
|
try {
|
|
249
258
|
const bcrypt = await import("bcryptjs");
|
|
250
259
|
const hashedPassword = await bcrypt.hash("ConformanceTest123!", 10);
|
|
251
|
-
await adapters.passwords.create("${
|
|
260
|
+
await adapters.passwords.create("${n}", {
|
|
252
261
|
user_id: \`\${USERNAME_PASSWORD_PROVIDER}|conformance-user\`,
|
|
253
262
|
password: hashedPassword,
|
|
254
263
|
});
|
|
@@ -266,9 +275,51 @@ import Database from "better-sqlite3";
|
|
|
266
275
|
import createAdapters from "@authhero/kysely-adapter";
|
|
267
276
|
import { seed } from "authhero";
|
|
268
277
|
|
|
278
|
+
interface ExtraClient {
|
|
279
|
+
client_id: string;
|
|
280
|
+
client_secret: string;
|
|
281
|
+
name?: string;
|
|
282
|
+
callbacks?: string[];
|
|
283
|
+
allowed_logout_urls?: string[];
|
|
284
|
+
web_origins?: string[];
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function parseFlag(name: string): string | undefined {
|
|
288
|
+
const argv = process.argv.slice(2);
|
|
289
|
+
const eqPrefix = \`--\${name}=\`;
|
|
290
|
+
for (let i = 0; i < argv.length; i++) {
|
|
291
|
+
const arg = argv[i]!;
|
|
292
|
+
if (arg === \`--\${name}\`) return argv[i + 1];
|
|
293
|
+
if (arg.startsWith(eqPrefix)) return arg.slice(eqPrefix.length);
|
|
294
|
+
}
|
|
295
|
+
return undefined;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function positionalArgs(): string[] {
|
|
299
|
+
const out: string[] = [];
|
|
300
|
+
const argv = process.argv.slice(2);
|
|
301
|
+
for (let i = 0; i < argv.length; i++) {
|
|
302
|
+
const arg = argv[i]!;
|
|
303
|
+
if (arg.startsWith("--")) {
|
|
304
|
+
if (!arg.includes("=")) i++;
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
out.push(arg);
|
|
308
|
+
}
|
|
309
|
+
return out;
|
|
310
|
+
}
|
|
311
|
+
|
|
269
312
|
async function main() {
|
|
270
|
-
const
|
|
271
|
-
const
|
|
313
|
+
const positional = positionalArgs();
|
|
314
|
+
const adminUsername = positional[0] || process.env.ADMIN_USERNAME || "admin";
|
|
315
|
+
const adminPassword = positional[1] || process.env.ADMIN_PASSWORD || "admin";
|
|
316
|
+
|
|
317
|
+
const clientsJson = parseFlag("clients");
|
|
318
|
+
const userProfileJson = parseFlag("user-profile");
|
|
319
|
+
const extraClients: ExtraClient[] = clientsJson ? JSON.parse(clientsJson) : [];
|
|
320
|
+
const userProfile: Record<string, unknown> = userProfileJson
|
|
321
|
+
? JSON.parse(userProfileJson)
|
|
322
|
+
: {};
|
|
272
323
|
|
|
273
324
|
const dialect = new SqliteDialect({
|
|
274
325
|
database: new Database("db.sqlite"),
|
|
@@ -277,32 +328,59 @@ async function main() {
|
|
|
277
328
|
const db = new Kysely<any>({ dialect });
|
|
278
329
|
const adapters = createAdapters(db);
|
|
279
330
|
|
|
280
|
-
await seed(adapters, {
|
|
331
|
+
const seedResult = await seed(adapters, {
|
|
281
332
|
adminUsername,
|
|
282
333
|
adminPassword,
|
|
283
|
-
tenantId: "${
|
|
334
|
+
tenantId: "${n}",
|
|
284
335
|
tenantName: "${t}",
|
|
285
|
-
isControlPlane: ${!!
|
|
336
|
+
isControlPlane: ${!!s},
|
|
286
337
|
clientId: "default",
|
|
287
338
|
callbacks: ${JSON.stringify(f)},
|
|
288
339
|
allowedLogoutUrls: ${JSON.stringify(y)},
|
|
289
340
|
});
|
|
290
|
-
|
|
341
|
+
|
|
342
|
+
for (const c of extraClients) {
|
|
343
|
+
const existing = await adapters.clients.get(seedResult.tenantId, c.client_id);
|
|
344
|
+
if (existing) {
|
|
345
|
+
console.log(\`Client "\${c.client_id}" already exists, skipping...\`);
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
348
|
+
await adapters.clients.create(seedResult.tenantId, {
|
|
349
|
+
client_id: c.client_id,
|
|
350
|
+
client_secret: c.client_secret,
|
|
351
|
+
name: c.name ?? c.client_id,
|
|
352
|
+
callbacks: c.callbacks ?? [],
|
|
353
|
+
allowed_logout_urls: c.allowed_logout_urls ?? [],
|
|
354
|
+
web_origins: c.web_origins ?? [],
|
|
355
|
+
connections: ["Username-Password-Authentication"],
|
|
356
|
+
client_metadata: { universal_login_version: "2" },
|
|
357
|
+
});
|
|
358
|
+
console.log(\`✅ Created client "\${c.client_id}"\`);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (Object.keys(userProfile).length > 0) {
|
|
362
|
+
await adapters.users.update(seedResult.tenantId, seedResult.userId, userProfile);
|
|
363
|
+
console.log(\`✅ Updated profile of user "\${seedResult.username}"\`);
|
|
364
|
+
}
|
|
365
|
+
${A}
|
|
291
366
|
await db.destroy();
|
|
292
367
|
}
|
|
293
368
|
|
|
294
|
-
main().catch(
|
|
369
|
+
main().catch((err) => {
|
|
370
|
+
console.error(err);
|
|
371
|
+
process.exit(1);
|
|
372
|
+
});
|
|
295
373
|
`;
|
|
296
374
|
}
|
|
297
|
-
function
|
|
298
|
-
const
|
|
375
|
+
function $(s, e) {
|
|
376
|
+
const o = e ? `import fs from "fs";
|
|
299
377
|
` : "", r = e ? `
|
|
300
378
|
const adminDistPath = path.resolve(
|
|
301
379
|
__dirname,
|
|
302
380
|
"../node_modules/@authhero/react-admin/dist",
|
|
303
381
|
);
|
|
304
382
|
const adminIndexPath = path.join(adminDistPath, "index.html");
|
|
305
|
-
` : "",
|
|
383
|
+
` : "", n = e ? `
|
|
306
384
|
// Add admin UI handler if the package is installed
|
|
307
385
|
if (fs.existsSync(adminIndexPath)) {
|
|
308
386
|
const issuer =
|
|
@@ -312,7 +390,7 @@ const adminIndexPath = path.join(adminDistPath, "index.html");
|
|
|
312
390
|
.replace(/href="\\.\\//g, 'href="/admin/');
|
|
313
391
|
const configJson = JSON.stringify({
|
|
314
392
|
domain: issuer.replace(/\\/$/, ""),
|
|
315
|
-
clientId: ${
|
|
393
|
+
clientId: ${s ? "CONTROL_PLANE_CLIENT_ID," : '"default",'}
|
|
316
394
|
basePath: "/admin",
|
|
317
395
|
}).replace(/</g, "\\\\u003c");
|
|
318
396
|
configWithHandlers.adminIndexHtml = rawHtml.replace(
|
|
@@ -325,13 +403,13 @@ const adminIndexPath = path.join(adminDistPath, "index.html");
|
|
|
325
403
|
});
|
|
326
404
|
}
|
|
327
405
|
` : "";
|
|
328
|
-
return
|
|
406
|
+
return s ? `import { Context } from "hono";
|
|
329
407
|
import { swaggerUI } from "@hono/swagger-ui";
|
|
330
408
|
import { AuthHeroConfig, DataAdapters } from "authhero";
|
|
331
409
|
import { serveStatic } from "@hono/node-server/serve-static";
|
|
332
410
|
import { initMultiTenant } from "@authhero/multi-tenancy";
|
|
333
411
|
import path from "path";
|
|
334
|
-
${
|
|
412
|
+
${o}import { fileURLToPath } from "url";
|
|
335
413
|
|
|
336
414
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
337
415
|
|
|
@@ -352,7 +430,7 @@ export default function createApp(config: AuthHeroConfig & { dataAdapter: DataAd
|
|
|
352
430
|
rewriteRequestPath: (p) => p.replace("/u/widget", ""),
|
|
353
431
|
}),
|
|
354
432
|
};
|
|
355
|
-
${
|
|
433
|
+
${n}
|
|
356
434
|
// Initialize multi-tenant AuthHero - syncs resource servers, roles, and connections by default
|
|
357
435
|
const { app } = initMultiTenant({
|
|
358
436
|
...configWithHandlers,
|
|
@@ -389,7 +467,7 @@ import { AuthHeroConfig, init } from "authhero";
|
|
|
389
467
|
import { swaggerUI } from "@hono/swagger-ui";
|
|
390
468
|
import { serveStatic } from "@hono/node-server/serve-static";
|
|
391
469
|
import path from "path";
|
|
392
|
-
${
|
|
470
|
+
${o}import { fileURLToPath } from "url";
|
|
393
471
|
|
|
394
472
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
395
473
|
|
|
@@ -406,7 +484,7 @@ export default function createApp(config: AuthHeroConfig) {
|
|
|
406
484
|
rewriteRequestPath: (p) => p.replace("/u/widget", ""),
|
|
407
485
|
}),
|
|
408
486
|
};
|
|
409
|
-
${
|
|
487
|
+
${n}
|
|
410
488
|
const { app } = init(configWithHandlers);
|
|
411
489
|
|
|
412
490
|
app
|
|
@@ -430,7 +508,7 @@ ${o}
|
|
|
430
508
|
}
|
|
431
509
|
`;
|
|
432
510
|
}
|
|
433
|
-
function
|
|
511
|
+
function j(s) {
|
|
434
512
|
return `import { D1Dialect } from "kysely-d1";
|
|
435
513
|
import { Kysely } from "kysely";
|
|
436
514
|
import createAdapters from "@authhero/kysely-adapter";
|
|
@@ -457,9 +535,9 @@ export default {
|
|
|
457
535
|
adminUsername,
|
|
458
536
|
adminPassword,
|
|
459
537
|
issuer,
|
|
460
|
-
tenantId: "${
|
|
461
|
-
tenantName: "${
|
|
462
|
-
isControlPlane: ${!!
|
|
538
|
+
tenantId: "${s ? "control_plane" : "main"}",
|
|
539
|
+
tenantName: "${s ? "Control Plane" : "Main"}",
|
|
540
|
+
isControlPlane: ${!!s},
|
|
463
541
|
clientId: "default",
|
|
464
542
|
});
|
|
465
543
|
|
|
@@ -491,15 +569,15 @@ export default {
|
|
|
491
569
|
};
|
|
492
570
|
`;
|
|
493
571
|
}
|
|
494
|
-
function
|
|
495
|
-
const
|
|
572
|
+
function H(s, e) {
|
|
573
|
+
const o = e ? `import adminIndexHtml from "./admin-index-html";
|
|
496
574
|
` : "", r = e ? ` adminIndexHtml,
|
|
497
575
|
` : "";
|
|
498
|
-
return
|
|
576
|
+
return s ? `import { Context } from "hono";
|
|
499
577
|
import { swaggerUI } from "@hono/swagger-ui";
|
|
500
578
|
import { AuthHeroConfig, DataAdapters } from "authhero";
|
|
501
579
|
import { initMultiTenant } from "@authhero/multi-tenancy";
|
|
502
|
-
${
|
|
580
|
+
${o}
|
|
503
581
|
// Control plane configuration
|
|
504
582
|
const CONTROL_PLANE_TENANT_ID = "control_plane";
|
|
505
583
|
const CONTROL_PLANE_CLIENT_ID = "default";
|
|
@@ -540,7 +618,7 @@ ${r} controlPlane: {
|
|
|
540
618
|
import { cors } from "hono/cors";
|
|
541
619
|
import { AuthHeroConfig, init } from "authhero";
|
|
542
620
|
import { swaggerUI } from "@hono/swagger-ui";
|
|
543
|
-
${
|
|
621
|
+
${o}
|
|
544
622
|
export default function createApp(config: AuthHeroConfig) {
|
|
545
623
|
const { app } = init({
|
|
546
624
|
...config,
|
|
@@ -576,8 +654,8 @@ ${r} });
|
|
|
576
654
|
}
|
|
577
655
|
`;
|
|
578
656
|
}
|
|
579
|
-
function
|
|
580
|
-
return
|
|
657
|
+
function F(s) {
|
|
658
|
+
return s ? `import { Context } from "hono";
|
|
581
659
|
import { swaggerUI } from "@hono/swagger-ui";
|
|
582
660
|
import { AuthHeroConfig, DataAdapters } from "authhero";
|
|
583
661
|
import { initMultiTenant } from "@authhero/multi-tenancy";
|
|
@@ -682,7 +760,7 @@ export default function createApp(config: AppConfig) {
|
|
|
682
760
|
}
|
|
683
761
|
`;
|
|
684
762
|
}
|
|
685
|
-
function
|
|
763
|
+
function M(s) {
|
|
686
764
|
return `import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
|
|
687
765
|
import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb";
|
|
688
766
|
import createAdapters from "@authhero/aws";
|
|
@@ -711,9 +789,9 @@ async function main() {
|
|
|
711
789
|
await seed(adapters, {
|
|
712
790
|
adminUsername,
|
|
713
791
|
adminPassword,
|
|
714
|
-
tenantId: "${
|
|
715
|
-
tenantName: "${
|
|
716
|
-
isControlPlane: ${!!
|
|
792
|
+
tenantId: "${s ? "control_plane" : "main"}",
|
|
793
|
+
tenantName: "${s ? "Control Plane" : "Main"}",
|
|
794
|
+
isControlPlane: ${!!s},
|
|
717
795
|
});
|
|
718
796
|
|
|
719
797
|
console.log("✅ Database seeded successfully!");
|
|
@@ -722,23 +800,23 @@ async function main() {
|
|
|
722
800
|
main().catch(console.error);
|
|
723
801
|
`;
|
|
724
802
|
}
|
|
725
|
-
function
|
|
726
|
-
const
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
),
|
|
731
|
-
|
|
732
|
-
|
|
803
|
+
function W(s, e) {
|
|
804
|
+
const o = a.join(s, "src");
|
|
805
|
+
i.writeFileSync(
|
|
806
|
+
a.join(o, "app.ts"),
|
|
807
|
+
F(e)
|
|
808
|
+
), i.writeFileSync(
|
|
809
|
+
a.join(o, "seed.ts"),
|
|
810
|
+
M(e)
|
|
733
811
|
);
|
|
734
812
|
}
|
|
735
|
-
function
|
|
813
|
+
function I() {
|
|
736
814
|
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");
|
|
737
815
|
}
|
|
738
|
-
function
|
|
739
|
-
const e =
|
|
740
|
-
|
|
741
|
-
const
|
|
816
|
+
function q(s) {
|
|
817
|
+
const e = a.join(s, ".github", "workflows");
|
|
818
|
+
i.mkdirSync(e, { recursive: !0 });
|
|
819
|
+
const o = `name: Unit tests
|
|
742
820
|
|
|
743
821
|
on: push
|
|
744
822
|
|
|
@@ -795,7 +873,7 @@ jobs:
|
|
|
795
873
|
with:
|
|
796
874
|
apiToken: \${{ secrets.CLOUDFLARE_API_TOKEN }}
|
|
797
875
|
command: deploy
|
|
798
|
-
`,
|
|
876
|
+
`, n = `name: Deploy to Production
|
|
799
877
|
|
|
800
878
|
on:
|
|
801
879
|
release:
|
|
@@ -824,9 +902,9 @@ jobs:
|
|
|
824
902
|
apiToken: \${{ secrets.PROD_CLOUDFLARE_API_TOKEN }}
|
|
825
903
|
command: deploy --env production
|
|
826
904
|
`;
|
|
827
|
-
|
|
905
|
+
i.writeFileSync(a.join(e, "unit-tests.yml"), o), i.writeFileSync(a.join(e, "deploy-dev.yml"), r), i.writeFileSync(a.join(e, "release.yml"), n), console.log("\\n📦 GitHub CI workflows created!");
|
|
828
906
|
}
|
|
829
|
-
function
|
|
907
|
+
function J(s) {
|
|
830
908
|
const e = {
|
|
831
909
|
branches: ["main"],
|
|
832
910
|
plugins: [
|
|
@@ -835,11 +913,11 @@ function F(a) {
|
|
|
835
913
|
"@semantic-release/github"
|
|
836
914
|
]
|
|
837
915
|
};
|
|
838
|
-
|
|
839
|
-
|
|
916
|
+
i.writeFileSync(
|
|
917
|
+
a.join(s, ".releaserc.json"),
|
|
840
918
|
JSON.stringify(e, null, 2)
|
|
841
919
|
);
|
|
842
|
-
const
|
|
920
|
+
const o = a.join(s, "package.json"), r = JSON.parse(i.readFileSync(o, "utf-8"));
|
|
843
921
|
r.devDependencies = {
|
|
844
922
|
...r.devDependencies,
|
|
845
923
|
"semantic-release": "^24.0.0"
|
|
@@ -847,41 +925,41 @@ function F(a) {
|
|
|
847
925
|
...r.scripts,
|
|
848
926
|
test: 'echo "No tests yet"',
|
|
849
927
|
"type-check": "tsc --noEmit"
|
|
850
|
-
},
|
|
928
|
+
}, i.writeFileSync(o, JSON.stringify(r, null, 2));
|
|
851
929
|
}
|
|
852
|
-
function b(
|
|
853
|
-
return new Promise((
|
|
854
|
-
const
|
|
930
|
+
function b(s, e) {
|
|
931
|
+
return new Promise((o, r) => {
|
|
932
|
+
const n = O(s, [], {
|
|
855
933
|
cwd: e,
|
|
856
934
|
shell: !0,
|
|
857
935
|
stdio: "inherit"
|
|
858
936
|
});
|
|
859
|
-
|
|
860
|
-
t === 0 ?
|
|
861
|
-
}),
|
|
937
|
+
n.on("close", (t) => {
|
|
938
|
+
t === 0 ? o() : r(new Error(`Command failed with exit code ${t}`));
|
|
939
|
+
}), n.on("error", r);
|
|
862
940
|
});
|
|
863
941
|
}
|
|
864
|
-
function
|
|
865
|
-
const r =
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
),
|
|
870
|
-
|
|
871
|
-
|
|
942
|
+
function z(s, e, o) {
|
|
943
|
+
const r = a.join(s, "src");
|
|
944
|
+
i.writeFileSync(
|
|
945
|
+
a.join(r, "app.ts"),
|
|
946
|
+
H(e, o)
|
|
947
|
+
), i.writeFileSync(
|
|
948
|
+
a.join(r, "seed.ts"),
|
|
949
|
+
j(e)
|
|
872
950
|
);
|
|
873
951
|
}
|
|
874
|
-
function
|
|
952
|
+
function D() {
|
|
875
953
|
console.log(`
|
|
876
954
|
` + "─".repeat(50)), console.log("🔐 AuthHero server running at https://localhost:3000"), console.log("🚀 Open https://localhost:3000/setup to complete initial setup"), console.log("─".repeat(50) + `
|
|
877
955
|
`);
|
|
878
956
|
}
|
|
879
|
-
function
|
|
957
|
+
function N() {
|
|
880
958
|
console.log(`
|
|
881
959
|
` + "─".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 http://localhost:3000"), console.log("📚 API documentation available at http://localhost:3000/docs"), console.log("🚀 Open http://localhost:3000/setup to complete initial setup"), console.log("─".repeat(50) + `
|
|
882
960
|
`);
|
|
883
961
|
}
|
|
884
|
-
|
|
962
|
+
T.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(
|
|
885
963
|
"--package-manager <pm>",
|
|
886
964
|
"package manager to use: npm, yarn, pnpm, or bun"
|
|
887
965
|
).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(
|
|
@@ -890,13 +968,13 @@ D.version("1.0.0").description("Create a new AuthHero project").argument("[proje
|
|
|
890
968
|
).option(
|
|
891
969
|
"--workspace",
|
|
892
970
|
"use workspace:* dependencies for local monorepo development"
|
|
893
|
-
).option("-y, --yes", "skip all prompts and use defaults/provided options").action(async (
|
|
894
|
-
const
|
|
971
|
+
).option("-y, --yes", "skip all prompts and use defaults/provided options").action(async (s, e) => {
|
|
972
|
+
const o = e.yes === !0;
|
|
895
973
|
console.log(`
|
|
896
974
|
🔐 Welcome to AuthHero!
|
|
897
975
|
`);
|
|
898
|
-
let r =
|
|
899
|
-
r || (
|
|
976
|
+
let r = s;
|
|
977
|
+
r || (o ? (r = "auth-server", console.log(`Using default project name: ${r}`)) : r = (await m.prompt([
|
|
900
978
|
{
|
|
901
979
|
type: "input",
|
|
902
980
|
name: "projectName",
|
|
@@ -905,8 +983,8 @@ D.version("1.0.0").description("Create a new AuthHero project").argument("[proje
|
|
|
905
983
|
validate: (u) => u !== "" || "Project name cannot be empty"
|
|
906
984
|
}
|
|
907
985
|
])).projectName);
|
|
908
|
-
const
|
|
909
|
-
|
|
986
|
+
const n = a.join(process.cwd(), r);
|
|
987
|
+
i.existsSync(n) && (console.error(`❌ Project "${r}" already exists.`), process.exit(1));
|
|
910
988
|
let t;
|
|
911
989
|
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([
|
|
912
990
|
{
|
|
@@ -936,7 +1014,7 @@ D.version("1.0.0").description("Create a new AuthHero project").argument("[proje
|
|
|
936
1014
|
}
|
|
937
1015
|
])).setupType;
|
|
938
1016
|
let c;
|
|
939
|
-
e.multiTenant !== void 0 ? c = e.multiTenant :
|
|
1017
|
+
e.multiTenant !== void 0 ? c = e.multiTenant : o ? c = !1 : c = (await m.prompt([
|
|
940
1018
|
{
|
|
941
1019
|
type: "confirm",
|
|
942
1020
|
name: "multiTenant",
|
|
@@ -945,7 +1023,7 @@ D.version("1.0.0").description("Create a new AuthHero project").argument("[proje
|
|
|
945
1023
|
}
|
|
946
1024
|
])).multiTenant, c && console.log("Multi-tenant mode: enabled");
|
|
947
1025
|
let d = !1;
|
|
948
|
-
(t === "local" || t === "cloudflare") && (e.adminUi !== void 0 ? d = e.adminUi :
|
|
1026
|
+
(t === "local" || t === "cloudflare") && (e.adminUi !== void 0 ? d = e.adminUi : o ? d = !0 : d = (await m.prompt([
|
|
949
1027
|
{
|
|
950
1028
|
type: "confirm",
|
|
951
1029
|
name: "adminUi",
|
|
@@ -960,8 +1038,8 @@ D.version("1.0.0").description("Create a new AuthHero project").argument("[proje
|
|
|
960
1038
|
const C = e.workspace || !1;
|
|
961
1039
|
C && console.log("Workspace mode: enabled (using workspace:* dependencies)");
|
|
962
1040
|
const y = p[t];
|
|
963
|
-
|
|
964
|
-
|
|
1041
|
+
i.mkdirSync(n, { recursive: !0 }), i.writeFileSync(
|
|
1042
|
+
a.join(n, "package.json"),
|
|
965
1043
|
JSON.stringify(
|
|
966
1044
|
y.packageJson(
|
|
967
1045
|
r,
|
|
@@ -974,38 +1052,42 @@ D.version("1.0.0").description("Create a new AuthHero project").argument("[proje
|
|
|
974
1052
|
2
|
|
975
1053
|
)
|
|
976
1054
|
);
|
|
977
|
-
const
|
|
978
|
-
|
|
979
|
-
S
|
|
980
|
-
);
|
|
981
|
-
if (
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
1055
|
+
const A = y.templateDir, S = a.dirname(U(import.meta.url)), x = [
|
|
1056
|
+
a.join(S, A),
|
|
1057
|
+
a.join(S, "..", "templates", A)
|
|
1058
|
+
], k = x.find((l) => i.existsSync(l));
|
|
1059
|
+
if (k ? P(k, n) : (console.error(
|
|
1060
|
+
`❌ Template directory not found. Looked in:
|
|
1061
|
+
${x.join(`
|
|
1062
|
+
`)}`
|
|
1063
|
+
), process.exit(1)), t === "cloudflare" && z(n, c, d), t === "cloudflare") {
|
|
1064
|
+
const l = a.join(n, "wrangler.toml"), u = a.join(n, "wrangler.local.toml");
|
|
1065
|
+
i.existsSync(l) && i.copyFileSync(l, u);
|
|
1066
|
+
const g = a.join(n, ".dev.vars.example"), w = a.join(n, ".dev.vars");
|
|
1067
|
+
i.existsSync(g) && i.copyFileSync(g, w), console.log(
|
|
986
1068
|
"📁 Created wrangler.local.toml and .dev.vars for local development"
|
|
987
1069
|
);
|
|
988
1070
|
}
|
|
989
|
-
let
|
|
990
|
-
if (t === "cloudflare" && (e.githubCi !== void 0 ? (
|
|
1071
|
+
let _ = !1;
|
|
1072
|
+
if (t === "cloudflare" && (e.githubCi !== void 0 ? (_ = e.githubCi, _ && console.log("Including GitHub CI workflows with semantic versioning")) : o || (_ = (await m.prompt([
|
|
991
1073
|
{
|
|
992
1074
|
type: "confirm",
|
|
993
1075
|
name: "includeGithubCi",
|
|
994
1076
|
message: "Would you like to include GitHub CI with semantic versioning?",
|
|
995
1077
|
default: !1
|
|
996
1078
|
}
|
|
997
|
-
])).includeGithubCi),
|
|
998
|
-
const l =
|
|
1079
|
+
])).includeGithubCi), _ && (q(n), J(n))), t === "local") {
|
|
1080
|
+
const l = L(
|
|
999
1081
|
c,
|
|
1000
1082
|
f,
|
|
1001
1083
|
h,
|
|
1002
1084
|
d
|
|
1003
1085
|
);
|
|
1004
|
-
|
|
1005
|
-
const u =
|
|
1006
|
-
|
|
1086
|
+
i.writeFileSync(a.join(n, "src/seed.ts"), l);
|
|
1087
|
+
const u = $(c, d);
|
|
1088
|
+
i.writeFileSync(a.join(n, "src/app.ts"), u);
|
|
1007
1089
|
}
|
|
1008
|
-
if (t === "aws-sst" &&
|
|
1090
|
+
if (t === "aws-sst" && W(n, c), f) {
|
|
1009
1091
|
const l = {
|
|
1010
1092
|
alias: h,
|
|
1011
1093
|
description: "AuthHero Conformance Test",
|
|
@@ -1024,21 +1106,21 @@ D.version("1.0.0").description("Create a new AuthHero project").argument("[proje
|
|
|
1024
1106
|
resourceUrl: "http://host.docker.internal:3000/userinfo"
|
|
1025
1107
|
}
|
|
1026
1108
|
};
|
|
1027
|
-
|
|
1028
|
-
|
|
1109
|
+
i.writeFileSync(
|
|
1110
|
+
a.join(n, "conformance-config.json"),
|
|
1029
1111
|
JSON.stringify(l, null, 2)
|
|
1030
1112
|
), console.log(
|
|
1031
1113
|
"📝 Created conformance-config.json for OpenID Conformance Suite"
|
|
1032
1114
|
);
|
|
1033
1115
|
}
|
|
1034
|
-
const
|
|
1116
|
+
const E = c ? "multi-tenant" : "single-tenant";
|
|
1035
1117
|
console.log(
|
|
1036
1118
|
`
|
|
1037
|
-
✅ Project "${r}" has been created with ${y.name} (${
|
|
1119
|
+
✅ Project "${r}" has been created with ${y.name} (${E}) setup!
|
|
1038
1120
|
`
|
|
1039
1121
|
);
|
|
1040
1122
|
let v;
|
|
1041
|
-
if (e.skipInstall ? v = !1 :
|
|
1123
|
+
if (e.skipInstall ? v = !1 : o ? v = !0 : v = (await m.prompt([
|
|
1042
1124
|
{
|
|
1043
1125
|
type: "confirm",
|
|
1044
1126
|
name: "shouldInstall",
|
|
@@ -1049,7 +1131,7 @@ D.version("1.0.0").description("Create a new AuthHero project").argument("[proje
|
|
|
1049
1131
|
let l;
|
|
1050
1132
|
e.packageManager ? (["npm", "yarn", "pnpm", "bun"].includes(e.packageManager) || (console.error(
|
|
1051
1133
|
`❌ Invalid package manager: ${e.packageManager}`
|
|
1052
|
-
), console.error("Valid options: npm, yarn, pnpm, bun"), process.exit(1)), l = e.packageManager) :
|
|
1134
|
+
), console.error("Valid options: npm, yarn, pnpm, bun"), process.exit(1)), l = e.packageManager) : o ? l = "pnpm" : l = (await m.prompt([
|
|
1053
1135
|
{
|
|
1054
1136
|
type: "list",
|
|
1055
1137
|
name: "packageManager",
|
|
@@ -1067,13 +1149,13 @@ D.version("1.0.0").description("Create a new AuthHero project").argument("[proje
|
|
|
1067
1149
|
`);
|
|
1068
1150
|
try {
|
|
1069
1151
|
const u = l === "pnpm" ? "pnpm install --ignore-workspace" : `${l} install`;
|
|
1070
|
-
if (await b(u,
|
|
1152
|
+
if (await b(u, n), t === "local" && (console.log(`
|
|
1071
1153
|
🔧 Building native modules...
|
|
1072
|
-
`), await b("npm rebuild better-sqlite3",
|
|
1154
|
+
`), await b("npm rebuild better-sqlite3", n)), console.log(`
|
|
1073
1155
|
✅ Dependencies installed successfully!
|
|
1074
1156
|
`), (t === "local" || t === "cloudflare") && !e.skipMigrate) {
|
|
1075
1157
|
let w;
|
|
1076
|
-
|
|
1158
|
+
o ? w = !0 : w = (await m.prompt([
|
|
1077
1159
|
{
|
|
1078
1160
|
type: "confirm",
|
|
1079
1161
|
name: "shouldMigrate",
|
|
@@ -1082,20 +1164,20 @@ D.version("1.0.0").description("Create a new AuthHero project").argument("[proje
|
|
|
1082
1164
|
}
|
|
1083
1165
|
])).shouldMigrate, w && (console.log(`
|
|
1084
1166
|
🔄 Running migrations...
|
|
1085
|
-
`), await b(`${l} run migrate`,
|
|
1167
|
+
`), await b(`${l} run migrate`, n));
|
|
1086
1168
|
}
|
|
1087
1169
|
let g;
|
|
1088
|
-
e.skipStart ||
|
|
1170
|
+
e.skipStart || o ? g = !1 : g = (await m.prompt([
|
|
1089
1171
|
{
|
|
1090
1172
|
type: "confirm",
|
|
1091
1173
|
name: "shouldStart",
|
|
1092
1174
|
message: "Would you like to start the development server?",
|
|
1093
1175
|
default: !0
|
|
1094
1176
|
}
|
|
1095
|
-
])).shouldStart, g && (t === "cloudflare" ?
|
|
1096
|
-
`), await b(`${l} run dev`,
|
|
1177
|
+
])).shouldStart, g && (t === "cloudflare" ? D() : t === "aws-sst" ? I() : N(), console.log(`🚀 Starting development server...
|
|
1178
|
+
`), await b(`${l} run dev`, n)), o && !g && (console.log(`
|
|
1097
1179
|
✅ Setup complete!`), console.log(`
|
|
1098
|
-
To start the development server:`), console.log(` cd ${r}`), console.log(" npm run dev"), t === "cloudflare" ?
|
|
1180
|
+
To start the development server:`), console.log(` cd ${r}`), console.log(" npm run dev"), t === "cloudflare" ? D() : t === "aws-sst" ? I() : N());
|
|
1099
1181
|
} catch (u) {
|
|
1100
1182
|
console.error(`
|
|
1101
1183
|
❌ An error occurred:`, u), process.exit(1);
|
|
@@ -1122,4 +1204,4 @@ Server will be available at: http://localhost:3000`), f && (console.log(`
|
|
|
1122
1204
|
For more information, visit: https://authhero.net/docs
|
|
1123
1205
|
`));
|
|
1124
1206
|
});
|
|
1125
|
-
|
|
1207
|
+
T.parse(process.argv);
|
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.
|
|
8
|
+
"version": "0.41.0",
|
|
9
9
|
"type": "module",
|
|
10
10
|
"main": "dist/create-authhero.js",
|
|
11
11
|
"bin": {
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"@rollup/plugin-node-resolve": "^15.2.3",
|
|
21
21
|
"@types/inquirer": "^9.0.7",
|
|
22
22
|
"@types/node": "^20.14.9",
|
|
23
|
+
"tsx": "^4.19.4",
|
|
23
24
|
"typescript": "^5.5.2",
|
|
24
25
|
"vite": "^5.3.2"
|
|
25
26
|
},
|