create-authhero 0.41.0 → 0.41.1
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 +75 -71
- package/dist/local/src/index.ts +84 -3
- package/package.json +1 -1
package/dist/create-authhero.js
CHANGED
|
@@ -2,18 +2,18 @@
|
|
|
2
2
|
import { Command as R } from "commander";
|
|
3
3
|
import m from "inquirer";
|
|
4
4
|
import i from "fs";
|
|
5
|
-
import
|
|
6
|
-
import { fileURLToPath as
|
|
7
|
-
import { spawn as
|
|
5
|
+
import s from "path";
|
|
6
|
+
import { fileURLToPath as O } from "url";
|
|
7
|
+
import { spawn as U } from "child_process";
|
|
8
8
|
const T = new R(), p = {
|
|
9
9
|
local: {
|
|
10
10
|
name: "Local (SQLite)",
|
|
11
11
|
description: "Local development setup with SQLite database - great for getting started",
|
|
12
12
|
templateDir: "local",
|
|
13
|
-
packageJson: (
|
|
13
|
+
packageJson: (a, e, o, r, n) => {
|
|
14
14
|
const t = r ? "workspace:*" : "latest";
|
|
15
15
|
return {
|
|
16
|
-
name:
|
|
16
|
+
name: a,
|
|
17
17
|
version: "1.0.0",
|
|
18
18
|
type: "module",
|
|
19
19
|
scripts: {
|
|
@@ -50,10 +50,10 @@ const T = new R(), p = {
|
|
|
50
50
|
name: "Cloudflare Workers (D1)",
|
|
51
51
|
description: "Cloudflare Workers setup with D1 database",
|
|
52
52
|
templateDir: "cloudflare",
|
|
53
|
-
packageJson: (
|
|
53
|
+
packageJson: (a, e, o, r, n) => {
|
|
54
54
|
const t = r ? "workspace:*" : "latest";
|
|
55
55
|
return {
|
|
56
|
-
name:
|
|
56
|
+
name: a,
|
|
57
57
|
version: "1.0.0",
|
|
58
58
|
type: "module",
|
|
59
59
|
scripts: {
|
|
@@ -99,10 +99,10 @@ const T = new R(), p = {
|
|
|
99
99
|
name: "AWS SST (Lambda + DynamoDB)",
|
|
100
100
|
description: "Serverless AWS deployment with Lambda, DynamoDB, and SST",
|
|
101
101
|
templateDir: "aws-sst",
|
|
102
|
-
packageJson: (
|
|
102
|
+
packageJson: (a, e, o, r, n) => {
|
|
103
103
|
const t = r ? "workspace:*" : "latest";
|
|
104
104
|
return {
|
|
105
|
-
name:
|
|
105
|
+
name: a,
|
|
106
106
|
version: "1.0.0",
|
|
107
107
|
type: "module",
|
|
108
108
|
scripts: {
|
|
@@ -137,14 +137,14 @@ const T = new R(), p = {
|
|
|
137
137
|
seedFile: "seed.ts"
|
|
138
138
|
}
|
|
139
139
|
};
|
|
140
|
-
function P(
|
|
141
|
-
i.readdirSync(
|
|
142
|
-
const n =
|
|
140
|
+
function P(a, e) {
|
|
141
|
+
i.readdirSync(a).forEach((r) => {
|
|
142
|
+
const n = s.join(a, r), t = s.join(e, r);
|
|
143
143
|
i.lstatSync(n).isDirectory() ? (i.mkdirSync(t, { recursive: !0 }), P(n, t)) : i.copyFileSync(n, t);
|
|
144
144
|
});
|
|
145
145
|
}
|
|
146
|
-
function L(
|
|
147
|
-
const n =
|
|
146
|
+
function L(a, e = !1, o = "authhero-local", r) {
|
|
147
|
+
const n = a ? "control_plane" : "main", t = a ? "Control Plane" : "Main", c = [
|
|
148
148
|
"https://manage.authhero.net/auth-callback",
|
|
149
149
|
"https://local.authhero.net/auth-callback",
|
|
150
150
|
"http://localhost:5173/auth-callback",
|
|
@@ -158,7 +158,7 @@ function L(s, e = !1, o = "authhero-local", r) {
|
|
|
158
158
|
"https://local.authhero.net",
|
|
159
159
|
"http://localhost:5173",
|
|
160
160
|
"http://localhost:3000"
|
|
161
|
-
], 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], _ = e ? `
|
|
162
162
|
// Create OpenID Conformance Suite test clients and user
|
|
163
163
|
console.log("Creating conformance test clients and user...");
|
|
164
164
|
|
|
@@ -273,7 +273,7 @@ function L(s, e = !1, o = "authhero-local", r) {
|
|
|
273
273
|
return `import { SqliteDialect, Kysely } from "kysely";
|
|
274
274
|
import Database from "better-sqlite3";
|
|
275
275
|
import createAdapters from "@authhero/kysely-adapter";
|
|
276
|
-
import { seed } from "authhero";
|
|
276
|
+
import { seed${e ? ", USERNAME_PASSWORD_PROVIDER" : ""} } from "authhero";
|
|
277
277
|
|
|
278
278
|
interface ExtraClient {
|
|
279
279
|
client_id: string;
|
|
@@ -282,6 +282,7 @@ interface ExtraClient {
|
|
|
282
282
|
callbacks?: string[];
|
|
283
283
|
allowed_logout_urls?: string[];
|
|
284
284
|
web_origins?: string[];
|
|
285
|
+
auth0_conformant?: boolean;
|
|
285
286
|
}
|
|
286
287
|
|
|
287
288
|
function parseFlag(name: string): string | undefined {
|
|
@@ -333,7 +334,7 @@ async function main() {
|
|
|
333
334
|
adminPassword,
|
|
334
335
|
tenantId: "${n}",
|
|
335
336
|
tenantName: "${t}",
|
|
336
|
-
isControlPlane: ${!!
|
|
337
|
+
isControlPlane: ${!!a},
|
|
337
338
|
clientId: "default",
|
|
338
339
|
callbacks: ${JSON.stringify(f)},
|
|
339
340
|
allowedLogoutUrls: ${JSON.stringify(y)},
|
|
@@ -354,6 +355,9 @@ async function main() {
|
|
|
354
355
|
web_origins: c.web_origins ?? [],
|
|
355
356
|
connections: ["Username-Password-Authentication"],
|
|
356
357
|
client_metadata: { universal_login_version: "2" },
|
|
358
|
+
...(c.auth0_conformant !== undefined && {
|
|
359
|
+
auth0_conformant: c.auth0_conformant,
|
|
360
|
+
}),
|
|
357
361
|
});
|
|
358
362
|
console.log(\`✅ Created client "\${c.client_id}"\`);
|
|
359
363
|
}
|
|
@@ -362,7 +366,7 @@ async function main() {
|
|
|
362
366
|
await adapters.users.update(seedResult.tenantId, seedResult.userId, userProfile);
|
|
363
367
|
console.log(\`✅ Updated profile of user "\${seedResult.username}"\`);
|
|
364
368
|
}
|
|
365
|
-
${
|
|
369
|
+
${_}
|
|
366
370
|
await db.destroy();
|
|
367
371
|
}
|
|
368
372
|
|
|
@@ -372,7 +376,7 @@ main().catch((err) => {
|
|
|
372
376
|
});
|
|
373
377
|
`;
|
|
374
378
|
}
|
|
375
|
-
function $(
|
|
379
|
+
function $(a, e) {
|
|
376
380
|
const o = e ? `import fs from "fs";
|
|
377
381
|
` : "", r = e ? `
|
|
378
382
|
const adminDistPath = path.resolve(
|
|
@@ -390,7 +394,7 @@ const adminIndexPath = path.join(adminDistPath, "index.html");
|
|
|
390
394
|
.replace(/href="\\.\\//g, 'href="/admin/');
|
|
391
395
|
const configJson = JSON.stringify({
|
|
392
396
|
domain: issuer.replace(/\\/$/, ""),
|
|
393
|
-
clientId: ${
|
|
397
|
+
clientId: ${a ? "CONTROL_PLANE_CLIENT_ID," : '"default",'}
|
|
394
398
|
basePath: "/admin",
|
|
395
399
|
}).replace(/</g, "\\\\u003c");
|
|
396
400
|
configWithHandlers.adminIndexHtml = rawHtml.replace(
|
|
@@ -403,7 +407,7 @@ const adminIndexPath = path.join(adminDistPath, "index.html");
|
|
|
403
407
|
});
|
|
404
408
|
}
|
|
405
409
|
` : "";
|
|
406
|
-
return
|
|
410
|
+
return a ? `import { Context } from "hono";
|
|
407
411
|
import { swaggerUI } from "@hono/swagger-ui";
|
|
408
412
|
import { AuthHeroConfig, DataAdapters } from "authhero";
|
|
409
413
|
import { serveStatic } from "@hono/node-server/serve-static";
|
|
@@ -508,7 +512,7 @@ ${n}
|
|
|
508
512
|
}
|
|
509
513
|
`;
|
|
510
514
|
}
|
|
511
|
-
function j(
|
|
515
|
+
function j(a) {
|
|
512
516
|
return `import { D1Dialect } from "kysely-d1";
|
|
513
517
|
import { Kysely } from "kysely";
|
|
514
518
|
import createAdapters from "@authhero/kysely-adapter";
|
|
@@ -535,9 +539,9 @@ export default {
|
|
|
535
539
|
adminUsername,
|
|
536
540
|
adminPassword,
|
|
537
541
|
issuer,
|
|
538
|
-
tenantId: "${
|
|
539
|
-
tenantName: "${
|
|
540
|
-
isControlPlane: ${!!
|
|
542
|
+
tenantId: "${a ? "control_plane" : "main"}",
|
|
543
|
+
tenantName: "${a ? "Control Plane" : "Main"}",
|
|
544
|
+
isControlPlane: ${!!a},
|
|
541
545
|
clientId: "default",
|
|
542
546
|
});
|
|
543
547
|
|
|
@@ -569,11 +573,11 @@ export default {
|
|
|
569
573
|
};
|
|
570
574
|
`;
|
|
571
575
|
}
|
|
572
|
-
function H(
|
|
576
|
+
function H(a, e) {
|
|
573
577
|
const o = e ? `import adminIndexHtml from "./admin-index-html";
|
|
574
578
|
` : "", r = e ? ` adminIndexHtml,
|
|
575
579
|
` : "";
|
|
576
|
-
return
|
|
580
|
+
return a ? `import { Context } from "hono";
|
|
577
581
|
import { swaggerUI } from "@hono/swagger-ui";
|
|
578
582
|
import { AuthHeroConfig, DataAdapters } from "authhero";
|
|
579
583
|
import { initMultiTenant } from "@authhero/multi-tenancy";
|
|
@@ -654,8 +658,8 @@ ${r} });
|
|
|
654
658
|
}
|
|
655
659
|
`;
|
|
656
660
|
}
|
|
657
|
-
function
|
|
658
|
-
return
|
|
661
|
+
function M(a) {
|
|
662
|
+
return a ? `import { Context } from "hono";
|
|
659
663
|
import { swaggerUI } from "@hono/swagger-ui";
|
|
660
664
|
import { AuthHeroConfig, DataAdapters } from "authhero";
|
|
661
665
|
import { initMultiTenant } from "@authhero/multi-tenancy";
|
|
@@ -760,7 +764,7 @@ export default function createApp(config: AppConfig) {
|
|
|
760
764
|
}
|
|
761
765
|
`;
|
|
762
766
|
}
|
|
763
|
-
function
|
|
767
|
+
function F(a) {
|
|
764
768
|
return `import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
|
|
765
769
|
import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb";
|
|
766
770
|
import createAdapters from "@authhero/aws";
|
|
@@ -789,9 +793,9 @@ async function main() {
|
|
|
789
793
|
await seed(adapters, {
|
|
790
794
|
adminUsername,
|
|
791
795
|
adminPassword,
|
|
792
|
-
tenantId: "${
|
|
793
|
-
tenantName: "${
|
|
794
|
-
isControlPlane: ${!!
|
|
796
|
+
tenantId: "${a ? "control_plane" : "main"}",
|
|
797
|
+
tenantName: "${a ? "Control Plane" : "Main"}",
|
|
798
|
+
isControlPlane: ${!!a},
|
|
795
799
|
});
|
|
796
800
|
|
|
797
801
|
console.log("✅ Database seeded successfully!");
|
|
@@ -800,21 +804,21 @@ async function main() {
|
|
|
800
804
|
main().catch(console.error);
|
|
801
805
|
`;
|
|
802
806
|
}
|
|
803
|
-
function W(
|
|
804
|
-
const o =
|
|
807
|
+
function W(a, e) {
|
|
808
|
+
const o = s.join(a, "src");
|
|
805
809
|
i.writeFileSync(
|
|
806
|
-
|
|
807
|
-
F(e)
|
|
808
|
-
), i.writeFileSync(
|
|
809
|
-
a.join(o, "seed.ts"),
|
|
810
|
+
s.join(o, "app.ts"),
|
|
810
811
|
M(e)
|
|
812
|
+
), i.writeFileSync(
|
|
813
|
+
s.join(o, "seed.ts"),
|
|
814
|
+
F(e)
|
|
811
815
|
);
|
|
812
816
|
}
|
|
813
|
-
function
|
|
817
|
+
function k() {
|
|
814
818
|
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");
|
|
815
819
|
}
|
|
816
|
-
function q(
|
|
817
|
-
const e =
|
|
820
|
+
function q(a) {
|
|
821
|
+
const e = s.join(a, ".github", "workflows");
|
|
818
822
|
i.mkdirSync(e, { recursive: !0 });
|
|
819
823
|
const o = `name: Unit tests
|
|
820
824
|
|
|
@@ -902,9 +906,9 @@ jobs:
|
|
|
902
906
|
apiToken: \${{ secrets.PROD_CLOUDFLARE_API_TOKEN }}
|
|
903
907
|
command: deploy --env production
|
|
904
908
|
`;
|
|
905
|
-
i.writeFileSync(
|
|
909
|
+
i.writeFileSync(s.join(e, "unit-tests.yml"), o), i.writeFileSync(s.join(e, "deploy-dev.yml"), r), i.writeFileSync(s.join(e, "release.yml"), n), console.log("\\n📦 GitHub CI workflows created!");
|
|
906
910
|
}
|
|
907
|
-
function J(
|
|
911
|
+
function J(a) {
|
|
908
912
|
const e = {
|
|
909
913
|
branches: ["main"],
|
|
910
914
|
plugins: [
|
|
@@ -914,10 +918,10 @@ function J(s) {
|
|
|
914
918
|
]
|
|
915
919
|
};
|
|
916
920
|
i.writeFileSync(
|
|
917
|
-
|
|
921
|
+
s.join(a, ".releaserc.json"),
|
|
918
922
|
JSON.stringify(e, null, 2)
|
|
919
923
|
);
|
|
920
|
-
const o =
|
|
924
|
+
const o = s.join(a, "package.json"), r = JSON.parse(i.readFileSync(o, "utf-8"));
|
|
921
925
|
r.devDependencies = {
|
|
922
926
|
...r.devDependencies,
|
|
923
927
|
"semantic-release": "^24.0.0"
|
|
@@ -927,9 +931,9 @@ function J(s) {
|
|
|
927
931
|
"type-check": "tsc --noEmit"
|
|
928
932
|
}, i.writeFileSync(o, JSON.stringify(r, null, 2));
|
|
929
933
|
}
|
|
930
|
-
function b(
|
|
934
|
+
function b(a, e) {
|
|
931
935
|
return new Promise((o, r) => {
|
|
932
|
-
const n =
|
|
936
|
+
const n = U(a, [], {
|
|
933
937
|
cwd: e,
|
|
934
938
|
shell: !0,
|
|
935
939
|
stdio: "inherit"
|
|
@@ -939,13 +943,13 @@ function b(s, e) {
|
|
|
939
943
|
}), n.on("error", r);
|
|
940
944
|
});
|
|
941
945
|
}
|
|
942
|
-
function z(
|
|
943
|
-
const r =
|
|
946
|
+
function z(a, e, o) {
|
|
947
|
+
const r = s.join(a, "src");
|
|
944
948
|
i.writeFileSync(
|
|
945
|
-
|
|
949
|
+
s.join(r, "app.ts"),
|
|
946
950
|
H(e, o)
|
|
947
951
|
), i.writeFileSync(
|
|
948
|
-
|
|
952
|
+
s.join(r, "seed.ts"),
|
|
949
953
|
j(e)
|
|
950
954
|
);
|
|
951
955
|
}
|
|
@@ -968,12 +972,12 @@ T.version("1.0.0").description("Create a new AuthHero project").argument("[proje
|
|
|
968
972
|
).option(
|
|
969
973
|
"--workspace",
|
|
970
974
|
"use workspace:* dependencies for local monorepo development"
|
|
971
|
-
).option("-y, --yes", "skip all prompts and use defaults/provided options").action(async (
|
|
975
|
+
).option("-y, --yes", "skip all prompts and use defaults/provided options").action(async (a, e) => {
|
|
972
976
|
const o = e.yes === !0;
|
|
973
977
|
console.log(`
|
|
974
978
|
🔐 Welcome to AuthHero!
|
|
975
979
|
`);
|
|
976
|
-
let r =
|
|
980
|
+
let r = a;
|
|
977
981
|
r || (o ? (r = "auth-server", console.log(`Using default project name: ${r}`)) : r = (await m.prompt([
|
|
978
982
|
{
|
|
979
983
|
type: "input",
|
|
@@ -983,7 +987,7 @@ T.version("1.0.0").description("Create a new AuthHero project").argument("[proje
|
|
|
983
987
|
validate: (u) => u !== "" || "Project name cannot be empty"
|
|
984
988
|
}
|
|
985
989
|
])).projectName);
|
|
986
|
-
const n =
|
|
990
|
+
const n = s.join(process.cwd(), r);
|
|
987
991
|
i.existsSync(n) && (console.error(`❌ Project "${r}" already exists.`), process.exit(1));
|
|
988
992
|
let t;
|
|
989
993
|
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([
|
|
@@ -1039,7 +1043,7 @@ T.version("1.0.0").description("Create a new AuthHero project").argument("[proje
|
|
|
1039
1043
|
C && console.log("Workspace mode: enabled (using workspace:* dependencies)");
|
|
1040
1044
|
const y = p[t];
|
|
1041
1045
|
i.mkdirSync(n, { recursive: !0 }), i.writeFileSync(
|
|
1042
|
-
|
|
1046
|
+
s.join(n, "package.json"),
|
|
1043
1047
|
JSON.stringify(
|
|
1044
1048
|
y.packageJson(
|
|
1045
1049
|
r,
|
|
@@ -1052,40 +1056,40 @@ T.version("1.0.0").description("Create a new AuthHero project").argument("[proje
|
|
|
1052
1056
|
2
|
|
1053
1057
|
)
|
|
1054
1058
|
);
|
|
1055
|
-
const
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
],
|
|
1059
|
-
if (
|
|
1059
|
+
const _ = y.templateDir, S = s.dirname(O(import.meta.url)), x = [
|
|
1060
|
+
s.join(S, _),
|
|
1061
|
+
s.join(S, "..", "templates", _)
|
|
1062
|
+
], I = x.find((l) => i.existsSync(l));
|
|
1063
|
+
if (I ? P(I, n) : (console.error(
|
|
1060
1064
|
`❌ Template directory not found. Looked in:
|
|
1061
1065
|
${x.join(`
|
|
1062
1066
|
`)}`
|
|
1063
1067
|
), process.exit(1)), t === "cloudflare" && z(n, c, d), t === "cloudflare") {
|
|
1064
|
-
const l =
|
|
1068
|
+
const l = s.join(n, "wrangler.toml"), u = s.join(n, "wrangler.local.toml");
|
|
1065
1069
|
i.existsSync(l) && i.copyFileSync(l, u);
|
|
1066
|
-
const g =
|
|
1070
|
+
const g = s.join(n, ".dev.vars.example"), w = s.join(n, ".dev.vars");
|
|
1067
1071
|
i.existsSync(g) && i.copyFileSync(g, w), console.log(
|
|
1068
1072
|
"📁 Created wrangler.local.toml and .dev.vars for local development"
|
|
1069
1073
|
);
|
|
1070
1074
|
}
|
|
1071
|
-
let
|
|
1072
|
-
if (t === "cloudflare" && (e.githubCi !== void 0 ? (
|
|
1075
|
+
let A = !1;
|
|
1076
|
+
if (t === "cloudflare" && (e.githubCi !== void 0 ? (A = e.githubCi, A && console.log("Including GitHub CI workflows with semantic versioning")) : o || (A = (await m.prompt([
|
|
1073
1077
|
{
|
|
1074
1078
|
type: "confirm",
|
|
1075
1079
|
name: "includeGithubCi",
|
|
1076
1080
|
message: "Would you like to include GitHub CI with semantic versioning?",
|
|
1077
1081
|
default: !1
|
|
1078
1082
|
}
|
|
1079
|
-
])).includeGithubCi),
|
|
1083
|
+
])).includeGithubCi), A && (q(n), J(n))), t === "local") {
|
|
1080
1084
|
const l = L(
|
|
1081
1085
|
c,
|
|
1082
1086
|
f,
|
|
1083
1087
|
h,
|
|
1084
1088
|
d
|
|
1085
1089
|
);
|
|
1086
|
-
i.writeFileSync(
|
|
1090
|
+
i.writeFileSync(s.join(n, "src/seed.ts"), l);
|
|
1087
1091
|
const u = $(c, d);
|
|
1088
|
-
i.writeFileSync(
|
|
1092
|
+
i.writeFileSync(s.join(n, "src/app.ts"), u);
|
|
1089
1093
|
}
|
|
1090
1094
|
if (t === "aws-sst" && W(n, c), f) {
|
|
1091
1095
|
const l = {
|
|
@@ -1107,7 +1111,7 @@ T.version("1.0.0").description("Create a new AuthHero project").argument("[proje
|
|
|
1107
1111
|
}
|
|
1108
1112
|
};
|
|
1109
1113
|
i.writeFileSync(
|
|
1110
|
-
|
|
1114
|
+
s.join(n, "conformance-config.json"),
|
|
1111
1115
|
JSON.stringify(l, null, 2)
|
|
1112
1116
|
), console.log(
|
|
1113
1117
|
"📝 Created conformance-config.json for OpenID Conformance Suite"
|
|
@@ -1174,10 +1178,10 @@ T.version("1.0.0").description("Create a new AuthHero project").argument("[proje
|
|
|
1174
1178
|
message: "Would you like to start the development server?",
|
|
1175
1179
|
default: !0
|
|
1176
1180
|
}
|
|
1177
|
-
])).shouldStart, g && (t === "cloudflare" ? D() : t === "aws-sst" ?
|
|
1181
|
+
])).shouldStart, g && (t === "cloudflare" ? D() : t === "aws-sst" ? k() : N(), console.log(`🚀 Starting development server...
|
|
1178
1182
|
`), await b(`${l} run dev`, n)), o && !g && (console.log(`
|
|
1179
1183
|
✅ Setup complete!`), console.log(`
|
|
1180
|
-
To start the development server:`), console.log(` cd ${r}`), console.log(" npm run dev"), t === "cloudflare" ? D() : t === "aws-sst" ?
|
|
1184
|
+
To start the development server:`), console.log(` cd ${r}`), console.log(" npm run dev"), t === "cloudflare" ? D() : t === "aws-sst" ? k() : N());
|
|
1181
1185
|
} catch (u) {
|
|
1182
1186
|
console.error(`
|
|
1183
1187
|
❌ An error occurred:`, u), process.exit(1);
|
package/dist/local/src/index.ts
CHANGED
|
@@ -4,6 +4,76 @@ import { Kysely } from "kysely";
|
|
|
4
4
|
import Database from "better-sqlite3";
|
|
5
5
|
import createAdapters from "@authhero/kysely-adapter";
|
|
6
6
|
import createApp from "./app";
|
|
7
|
+
import fs from "fs";
|
|
8
|
+
import path from "path";
|
|
9
|
+
import { execFileSync } from "child_process";
|
|
10
|
+
import https from "https";
|
|
11
|
+
|
|
12
|
+
// Generate self-signed certificates for local HTTPS if they don't exist
|
|
13
|
+
const certDir = path.join(process.cwd(), ".certs");
|
|
14
|
+
const keyPath = path.join(certDir, "localhost-key.pem");
|
|
15
|
+
const certPath = path.join(certDir, "localhost.pem");
|
|
16
|
+
|
|
17
|
+
function ensureCertificates() {
|
|
18
|
+
if (!fs.existsSync(certDir)) {
|
|
19
|
+
fs.mkdirSync(certDir, { recursive: true });
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (!fs.existsSync(keyPath) || !fs.existsSync(certPath)) {
|
|
23
|
+
console.log("🔑 Generating self-signed certificates for local HTTPS...");
|
|
24
|
+
|
|
25
|
+
// Try mkcert first (if installed), otherwise fall back to openssl
|
|
26
|
+
try {
|
|
27
|
+
execFileSync("which", ["mkcert"], { stdio: "ignore" });
|
|
28
|
+
execFileSync(
|
|
29
|
+
"mkcert",
|
|
30
|
+
["-key-file", keyPath, "-cert-file", certPath, "localhost", "127.0.0.1"],
|
|
31
|
+
{ stdio: "inherit" },
|
|
32
|
+
);
|
|
33
|
+
console.log("✅ Certificates generated with mkcert");
|
|
34
|
+
} catch {
|
|
35
|
+
// Fall back to openssl
|
|
36
|
+
try {
|
|
37
|
+
execFileSync(
|
|
38
|
+
"openssl",
|
|
39
|
+
[
|
|
40
|
+
"req",
|
|
41
|
+
"-x509",
|
|
42
|
+
"-newkey",
|
|
43
|
+
"rsa:2048",
|
|
44
|
+
"-keyout",
|
|
45
|
+
keyPath,
|
|
46
|
+
"-out",
|
|
47
|
+
certPath,
|
|
48
|
+
"-days",
|
|
49
|
+
"365",
|
|
50
|
+
"-nodes",
|
|
51
|
+
"-subj",
|
|
52
|
+
"/CN=localhost",
|
|
53
|
+
],
|
|
54
|
+
{ stdio: "inherit" },
|
|
55
|
+
);
|
|
56
|
+
console.log("✅ Self-signed certificates generated with openssl");
|
|
57
|
+
console.log(
|
|
58
|
+
"⚠️ You may need to trust the certificate in your browser",
|
|
59
|
+
);
|
|
60
|
+
} catch (err) {
|
|
61
|
+
console.error(
|
|
62
|
+
"❌ Failed to generate certificates. Please install mkcert or openssl",
|
|
63
|
+
);
|
|
64
|
+
console.error(
|
|
65
|
+
" Install mkcert: brew install mkcert && mkcert -install",
|
|
66
|
+
);
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
key: fs.readFileSync(keyPath),
|
|
74
|
+
cert: fs.readFileSync(certPath),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
7
77
|
|
|
8
78
|
// Initialize SQLite database
|
|
9
79
|
let db: Kysely<any>;
|
|
@@ -34,15 +104,19 @@ const app = createApp({
|
|
|
34
104
|
"https://manage.authhero.net",
|
|
35
105
|
"https://local.authhero.net",
|
|
36
106
|
"http://localhost:5173",
|
|
107
|
+
"https://localhost:5173",
|
|
37
108
|
],
|
|
38
109
|
});
|
|
39
110
|
|
|
40
111
|
// Start the server
|
|
41
112
|
const port = Number(process.env.PORT) || 3000;
|
|
42
|
-
const issuer = process.env.ISSUER || `
|
|
113
|
+
const issuer = process.env.ISSUER || `https://localhost:${port}/`;
|
|
114
|
+
|
|
115
|
+
// Get or generate certificates
|
|
116
|
+
const { key, cert } = ensureCertificates();
|
|
43
117
|
|
|
44
|
-
console.log(`🔐 AuthHero server running at
|
|
45
|
-
console.log(`📚 API documentation available at
|
|
118
|
+
console.log(`🔐 AuthHero server running at https://localhost:${port}`);
|
|
119
|
+
console.log(`📚 API documentation available at https://localhost:${port}/docs`);
|
|
46
120
|
console.log(`🌐 Portal available at https://local.authhero.net`);
|
|
47
121
|
|
|
48
122
|
serve({
|
|
@@ -53,4 +127,11 @@ serve({
|
|
|
53
127
|
});
|
|
54
128
|
},
|
|
55
129
|
port,
|
|
130
|
+
// Bind to all IPv4 interfaces explicitly. Node's default (`::`) is
|
|
131
|
+
// supposed to be dual-stack but on Docker Desktop Mac the docker bridge
|
|
132
|
+
// gateway IPv4 (e.g. 192.168.65.254) often isn't reachable that way,
|
|
133
|
+
// causing connections from inside the suite container to be refused.
|
|
134
|
+
hostname: "0.0.0.0",
|
|
135
|
+
createServer: https.createServer,
|
|
136
|
+
serverOptions: { key, cert },
|
|
56
137
|
});
|