create-authhero 0.18.0 → 0.19.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.
|
@@ -1,29 +1,65 @@
|
|
|
1
1
|
import { Context } from "hono";
|
|
2
2
|
import { HTTPException } from "hono/http-exception";
|
|
3
3
|
import { swaggerUI } from "@hono/swagger-ui";
|
|
4
|
+
import { init, AuthHeroConfig, fetchAll } from "authhero";
|
|
4
5
|
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
createSyncHooks,
|
|
7
|
+
createTenantsOpenAPIRouter,
|
|
8
|
+
createProtectSyncedMiddleware,
|
|
8
9
|
} from "@authhero/multi-tenancy";
|
|
10
|
+
import { DataAdapters } from "@authhero/adapter-interfaces";
|
|
9
11
|
|
|
10
12
|
// Control plane tenant ID - the tenant that manages all other tenants
|
|
11
13
|
const CONTROL_PLANE_TENANT_ID = "control_plane";
|
|
12
14
|
|
|
13
|
-
export default function createApp(
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
) {
|
|
15
|
+
export default function createApp(config: AuthHeroConfig & { dataAdapter: DataAdapters }) {
|
|
16
|
+
// Create sync hooks for syncing entities from control plane to child tenants
|
|
17
|
+
const { entityHooks, tenantHooks } = createSyncHooks({
|
|
18
|
+
controlPlaneTenantId: CONTROL_PLANE_TENANT_ID,
|
|
19
|
+
getChildTenantIds: async () => {
|
|
20
|
+
const allTenants = await fetchAll<{ id: string }>(
|
|
21
|
+
(params) => config.dataAdapter.tenants.list(params),
|
|
22
|
+
"tenants",
|
|
23
|
+
{ cursorField: "id", pageSize: 100 },
|
|
24
|
+
);
|
|
25
|
+
return allTenants
|
|
26
|
+
.filter((t) => t.id !== CONTROL_PLANE_TENANT_ID)
|
|
27
|
+
.map((t) => t.id);
|
|
28
|
+
},
|
|
29
|
+
getAdapters: async () => config.dataAdapter,
|
|
30
|
+
getControlPlaneAdapters: async () => config.dataAdapter,
|
|
31
|
+
sync: {
|
|
32
|
+
resourceServers: true,
|
|
33
|
+
roles: true,
|
|
34
|
+
connections: true,
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Create tenants router
|
|
39
|
+
const tenantsRouter = createTenantsOpenAPIRouter(
|
|
40
|
+
{
|
|
41
|
+
accessControl: {
|
|
42
|
+
controlPlaneTenantId: CONTROL_PLANE_TENANT_ID,
|
|
43
|
+
requireOrganizationMatch: false,
|
|
44
|
+
defaultPermissions: ["tenant:admin"],
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
{ tenants: tenantHooks },
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
// Initialize AuthHero with sync hooks and tenant routes
|
|
18
51
|
const { app } = init({
|
|
19
52
|
...config,
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
53
|
+
entityHooks,
|
|
54
|
+
managementApiExtensions: [
|
|
55
|
+
...(config.managementApiExtensions || []),
|
|
56
|
+
{ path: "/tenants", router: tenantsRouter },
|
|
57
|
+
],
|
|
25
58
|
});
|
|
26
59
|
|
|
60
|
+
// Add middleware to protect synced entities from modification on child tenants
|
|
61
|
+
app.use("/api/v2/*", createProtectSyncedMiddleware());
|
|
62
|
+
|
|
27
63
|
app
|
|
28
64
|
.onError((err, ctx) => {
|
|
29
65
|
if (err instanceof HTTPException) {
|
|
@@ -3,7 +3,7 @@ import { Kysely } from "kysely";
|
|
|
3
3
|
import createAdapters from "@authhero/kysely-adapter";
|
|
4
4
|
import createApp from "./app";
|
|
5
5
|
import { Env } from "./types";
|
|
6
|
-
import { AuthHeroConfig } from "
|
|
6
|
+
import { AuthHeroConfig } from "authhero";
|
|
7
7
|
|
|
8
8
|
// ──────────────────────────────────────────────────────────────────────────────
|
|
9
9
|
// OPTIONAL: Uncomment to enable Cloudflare adapters (Analytics Engine, etc.)
|
package/dist/create-authhero.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command as A } from "commander";
|
|
3
3
|
import m from "inquirer";
|
|
4
|
-
import
|
|
4
|
+
import n from "fs";
|
|
5
5
|
import i from "path";
|
|
6
6
|
import { spawn as b } from "child_process";
|
|
7
7
|
const D = new A(), c = {
|
|
@@ -106,6 +106,7 @@ const D = new A(), c = {
|
|
|
106
106
|
"@authhero/multi-tenancy": "latest",
|
|
107
107
|
"@hono/swagger-ui": "^0.5.0",
|
|
108
108
|
"@hono/zod-openapi": "^0.19.0",
|
|
109
|
+
authhero: "latest",
|
|
109
110
|
hono: "^4.6.0",
|
|
110
111
|
kysely: "latest",
|
|
111
112
|
"kysely-d1": "latest"
|
|
@@ -122,9 +123,9 @@ const D = new A(), c = {
|
|
|
122
123
|
}
|
|
123
124
|
};
|
|
124
125
|
function j(o, e) {
|
|
125
|
-
|
|
126
|
-
const a = i.join(o,
|
|
127
|
-
|
|
126
|
+
n.readdirSync(o).forEach((s) => {
|
|
127
|
+
const a = i.join(o, s), t = i.join(e, s);
|
|
128
|
+
n.lstatSync(a).isDirectory() ? (n.mkdirSync(t, { recursive: !0 }), j(a, t)) : n.copyFileSync(a, t);
|
|
128
129
|
});
|
|
129
130
|
}
|
|
130
131
|
function I() {
|
|
@@ -163,7 +164,7 @@ main().catch(console.error);
|
|
|
163
164
|
}
|
|
164
165
|
function x(o) {
|
|
165
166
|
const e = i.join(o, ".github", "workflows");
|
|
166
|
-
|
|
167
|
+
n.mkdirSync(e, { recursive: !0 });
|
|
167
168
|
const r = `name: Unit tests
|
|
168
169
|
|
|
169
170
|
on: push
|
|
@@ -185,7 +186,7 @@ jobs:
|
|
|
185
186
|
|
|
186
187
|
- run: npm run type-check
|
|
187
188
|
- run: npm test
|
|
188
|
-
`,
|
|
189
|
+
`, s = `name: Deploy to Dev
|
|
189
190
|
|
|
190
191
|
on:
|
|
191
192
|
push:
|
|
@@ -250,7 +251,7 @@ jobs:
|
|
|
250
251
|
apiToken: \${{ secrets.PROD_CLOUDFLARE_API_TOKEN }}
|
|
251
252
|
command: deploy --env production
|
|
252
253
|
`;
|
|
253
|
-
|
|
254
|
+
n.writeFileSync(i.join(e, "unit-tests.yml"), r), n.writeFileSync(i.join(e, "deploy-dev.yml"), s), n.writeFileSync(i.join(e, "release.yml"), a), console.log("\\n📦 GitHub CI workflows created!");
|
|
254
255
|
}
|
|
255
256
|
function C(o) {
|
|
256
257
|
const e = {
|
|
@@ -261,34 +262,34 @@ function C(o) {
|
|
|
261
262
|
"@semantic-release/github"
|
|
262
263
|
]
|
|
263
264
|
};
|
|
264
|
-
|
|
265
|
+
n.writeFileSync(
|
|
265
266
|
i.join(o, ".releaserc.json"),
|
|
266
267
|
JSON.stringify(e, null, 2)
|
|
267
268
|
);
|
|
268
|
-
const r = i.join(o, "package.json"),
|
|
269
|
-
|
|
270
|
-
...
|
|
269
|
+
const r = i.join(o, "package.json"), s = JSON.parse(n.readFileSync(r, "utf-8"));
|
|
270
|
+
s.devDependencies = {
|
|
271
|
+
...s.devDependencies,
|
|
271
272
|
"semantic-release": "^24.0.0"
|
|
272
|
-
},
|
|
273
|
-
...
|
|
273
|
+
}, s.scripts = {
|
|
274
|
+
...s.scripts,
|
|
274
275
|
test: 'echo "No tests yet"',
|
|
275
276
|
"type-check": "tsc --noEmit"
|
|
276
|
-
},
|
|
277
|
+
}, n.writeFileSync(r, JSON.stringify(s, null, 2));
|
|
277
278
|
}
|
|
278
279
|
function v(o, e) {
|
|
279
|
-
return new Promise((r,
|
|
280
|
+
return new Promise((r, s) => {
|
|
280
281
|
const a = b(o, [], {
|
|
281
282
|
cwd: e,
|
|
282
283
|
shell: !0,
|
|
283
284
|
stdio: "inherit"
|
|
284
285
|
});
|
|
285
286
|
a.on("close", (t) => {
|
|
286
|
-
t === 0 ? r() :
|
|
287
|
-
}), a.on("error",
|
|
287
|
+
t === 0 ? r() : s(new Error(`Command failed with exit code ${t}`));
|
|
288
|
+
}), a.on("error", s);
|
|
288
289
|
});
|
|
289
290
|
}
|
|
290
291
|
function S(o, e, r) {
|
|
291
|
-
return new Promise((
|
|
292
|
+
return new Promise((s, a) => {
|
|
292
293
|
const t = b(o, [], {
|
|
293
294
|
cwd: e,
|
|
294
295
|
shell: !0,
|
|
@@ -296,7 +297,7 @@ function S(o, e, r) {
|
|
|
296
297
|
env: { ...process.env, ...r }
|
|
297
298
|
});
|
|
298
299
|
t.on("close", (f) => {
|
|
299
|
-
f === 0 ?
|
|
300
|
+
f === 0 ? s() : a(new Error(`Command failed with exit code ${f}`));
|
|
300
301
|
}), t.on("error", a);
|
|
301
302
|
});
|
|
302
303
|
}
|
|
@@ -311,8 +312,8 @@ D.version("1.0.0").description("Create a new AuthHero project").argument("[proje
|
|
|
311
312
|
console.log(`
|
|
312
313
|
🔐 Welcome to AuthHero!
|
|
313
314
|
`);
|
|
314
|
-
let
|
|
315
|
-
|
|
315
|
+
let s = o;
|
|
316
|
+
s || (r ? (s = "auth-server", console.log(`Using default project name: ${s}`)) : s = (await m.prompt([
|
|
316
317
|
{
|
|
317
318
|
type: "input",
|
|
318
319
|
name: "projectName",
|
|
@@ -321,8 +322,8 @@ D.version("1.0.0").description("Create a new AuthHero project").argument("[proje
|
|
|
321
322
|
validate: (p) => p !== "" || "Project name cannot be empty"
|
|
322
323
|
}
|
|
323
324
|
])).projectName);
|
|
324
|
-
const a = i.join(process.cwd(),
|
|
325
|
-
|
|
325
|
+
const a = i.join(process.cwd(), s);
|
|
326
|
+
n.existsSync(a) && (console.error(`❌ Project "${s}" already exists.`), process.exit(1));
|
|
326
327
|
let t;
|
|
327
328
|
e.template ? (["local", "cloudflare-simple", "cloudflare-multitenant"].includes(
|
|
328
329
|
e.template
|
|
@@ -356,19 +357,19 @@ D.version("1.0.0").description("Create a new AuthHero project").argument("[proje
|
|
|
356
357
|
}
|
|
357
358
|
])).setupType;
|
|
358
359
|
const f = c[t];
|
|
359
|
-
|
|
360
|
+
n.mkdirSync(a, { recursive: !0 }), n.writeFileSync(
|
|
360
361
|
i.join(a, "package.json"),
|
|
361
|
-
JSON.stringify(f.packageJson(
|
|
362
|
+
JSON.stringify(f.packageJson(s), null, 2)
|
|
362
363
|
);
|
|
363
364
|
const k = i.join(
|
|
364
365
|
import.meta.url.replace("file://", "").replace("/create-authhero.js", ""),
|
|
365
366
|
f.templateDir
|
|
366
367
|
);
|
|
367
|
-
if (
|
|
368
|
+
if (n.existsSync(k) ? j(k, a) : (console.error(`❌ Template directory not found: ${k}`), process.exit(1)), t === "cloudflare-simple" || t === "cloudflare-multitenant") {
|
|
368
369
|
const l = i.join(a, "wrangler.toml"), p = i.join(a, "wrangler.local.toml");
|
|
369
|
-
|
|
370
|
+
n.existsSync(l) && n.copyFileSync(l, p);
|
|
370
371
|
const d = i.join(a, ".dev.vars.example"), u = i.join(a, ".dev.vars");
|
|
371
|
-
|
|
372
|
+
n.existsSync(d) && n.copyFileSync(d, u), console.log(
|
|
372
373
|
"📁 Created wrangler.local.toml and .dev.vars for local development"
|
|
373
374
|
);
|
|
374
375
|
}
|
|
@@ -382,11 +383,11 @@ D.version("1.0.0").description("Create a new AuthHero project").argument("[proje
|
|
|
382
383
|
}
|
|
383
384
|
])).includeGithubCi), y && (x(a), C(a))), t === "local") {
|
|
384
385
|
const l = I();
|
|
385
|
-
|
|
386
|
+
n.writeFileSync(i.join(a, "src/seed.ts"), l);
|
|
386
387
|
}
|
|
387
388
|
console.log(
|
|
388
389
|
`
|
|
389
|
-
✅ Project "${
|
|
390
|
+
✅ Project "${s}" has been created with ${f.name} setup!
|
|
390
391
|
`
|
|
391
392
|
);
|
|
392
393
|
let h;
|
|
@@ -487,7 +488,7 @@ D.version("1.0.0").description("Create a new AuthHero project").argument("[proje
|
|
|
487
488
|
`
|
|
488
489
|
), await v(`${l} run dev`, a)), r && !d && (console.log(`
|
|
489
490
|
✅ Setup complete!`), console.log(`
|
|
490
|
-
To start the development server:`), console.log(` cd ${
|
|
491
|
+
To start the development server:`), console.log(` cd ${s}`), console.log(" npm run dev"), (t === "cloudflare-simple" || t === "cloudflare-multitenant") && console.log(
|
|
491
492
|
`
|
|
492
493
|
Server will be available at: https://localhost:3000`
|
|
493
494
|
));
|
|
@@ -496,7 +497,7 @@ Server will be available at: https://localhost:3000`
|
|
|
496
497
|
❌ An error occurred:`, p), process.exit(1);
|
|
497
498
|
}
|
|
498
499
|
}
|
|
499
|
-
h || (console.log("Next steps:"), console.log(` cd ${
|
|
500
|
+
h || (console.log("Next steps:"), console.log(` cd ${s}`), t === "local" ? (console.log(" npm install"), console.log(" npm run migrate"), console.log(
|
|
500
501
|
" ADMIN_EMAIL=admin@example.com ADMIN_PASSWORD=yourpassword npm run seed"
|
|
501
502
|
), console.log(" npm run dev")) : (t === "cloudflare-simple" || t === "cloudflare-multitenant") && (console.log(" npm install"), console.log(
|
|
502
503
|
" npm run migrate # or npm run db:migrate:remote for production"
|