authhero 0.2.1 → 0.2.3
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/CHANGELOG.md +15 -0
- package/dist/authhero.cjs +1 -1
- package/dist/authhero.iife.js +1 -1
- package/dist/authhero.mjs +5918 -2174
- package/package.json +3 -2
- package/src/hooks/index.ts +29 -0
- package/src/hooks/link-users.ts +31 -0
- package/src/hooks/webhooks.ts +77 -0
- package/src/index.ts +9 -2
- package/src/management-app.ts +55 -0
- package/src/middlewares/register-component.ts +36 -0
- package/src/routes/management-api/tenants.ts +224 -0
- package/src/routes/oauth2/well-known.ts +2 -3
- package/src/types/Bindings.ts +5 -0
- package/src/types/Query.ts +42 -0
- package/src/types/Variables.ts +3 -1
- package/src/types/index.ts +2 -2
- package/src/utils/parse-sort.ts +18 -0
- package/src/utils/users.ts +130 -0
- package/src/bun.ts +0 -16
- package/src/types/JWKS.ts +0 -37
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "authhero",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"main": "dist/authhero.cjs",
|
|
5
5
|
"types": "dist/authhero.d.ts",
|
|
6
6
|
"module": "dist/authhero.mjs",
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@hono/zod-openapi": "^0.14.5",
|
|
22
|
-
"hono": "^4.4.10"
|
|
22
|
+
"hono": "^4.4.10",
|
|
23
|
+
"@authhero/adapter-interfaces": "^0.9.2"
|
|
23
24
|
},
|
|
24
25
|
"scripts": {
|
|
25
26
|
"dev": "bun --watch src/bun.ts",
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { DataAdapters, User } from "@authhero/adapter-interfaces";
|
|
2
|
+
import { linkUsersHook } from "./link-users";
|
|
3
|
+
import { postUserRegistrationWebhook } from "./webhooks";
|
|
4
|
+
import { Context } from "hono";
|
|
5
|
+
import { Bindings, Variables } from "../types";
|
|
6
|
+
|
|
7
|
+
function createUserHooks(
|
|
8
|
+
ctx: Context<{ Bindings: Bindings; Variables: Variables }>,
|
|
9
|
+
data: DataAdapters,
|
|
10
|
+
) {
|
|
11
|
+
return async (tenant_id: string, user: User) => {
|
|
12
|
+
// Check for existing user with the same email and if so link the users
|
|
13
|
+
let result = await linkUsersHook(data)(tenant_id, user);
|
|
14
|
+
// Invoke post-user-registration webhooks
|
|
15
|
+
await postUserRegistrationWebhook(ctx, data)(tenant_id, result);
|
|
16
|
+
|
|
17
|
+
return result;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function addDataHooks(
|
|
22
|
+
ctx: Context<{ Bindings: Bindings; Variables: Variables }>,
|
|
23
|
+
data: DataAdapters,
|
|
24
|
+
): DataAdapters {
|
|
25
|
+
return {
|
|
26
|
+
...data,
|
|
27
|
+
users: { ...data.users, create: createUserHooks(ctx, data) },
|
|
28
|
+
};
|
|
29
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { DataAdapters, User } from "@authhero/adapter-interfaces";
|
|
2
|
+
import { getPrimaryUserByEmail } from "../utils/users";
|
|
3
|
+
|
|
4
|
+
export function linkUsersHook(data: DataAdapters) {
|
|
5
|
+
return async (tenant_id: string, user: User): Promise<User> => {
|
|
6
|
+
// If the user does not have an email or the email is not verified, return the user
|
|
7
|
+
if (!user.email || !user.email_verified) {
|
|
8
|
+
return data.users.create(tenant_id, user);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Search for a user with the same email
|
|
12
|
+
const primaryUser = await getPrimaryUserByEmail({
|
|
13
|
+
userAdapter: data.users,
|
|
14
|
+
tenant_id,
|
|
15
|
+
email: user.email,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// If no user with the same email exists, return the user
|
|
19
|
+
if (!primaryUser) {
|
|
20
|
+
return data.users.create(tenant_id, user);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
await data.users.create(tenant_id, {
|
|
24
|
+
...user,
|
|
25
|
+
linked_to: primaryUser.user_id,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// TODO: add the new user to the identities
|
|
29
|
+
return primaryUser;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DataAdapters,
|
|
3
|
+
Hook,
|
|
4
|
+
LogTypes,
|
|
5
|
+
User,
|
|
6
|
+
} from "@authhero/adapter-interfaces";
|
|
7
|
+
// import { createLogMessage } from "../utils/create-log-message";
|
|
8
|
+
import { Context } from "hono";
|
|
9
|
+
import { Variables, Bindings } from "../types";
|
|
10
|
+
|
|
11
|
+
async function invokeHooks(
|
|
12
|
+
ctx: Context<{ Bindings: Bindings; Variables: Variables }>,
|
|
13
|
+
hooks: Hook[],
|
|
14
|
+
data: any,
|
|
15
|
+
) {
|
|
16
|
+
for await (const hook of hooks) {
|
|
17
|
+
const response = await fetch(hook.url, {
|
|
18
|
+
method: "POST",
|
|
19
|
+
headers: {
|
|
20
|
+
"Content-Type": "application/json",
|
|
21
|
+
},
|
|
22
|
+
body: JSON.stringify(data),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
if (!response.ok) {
|
|
26
|
+
// const log = createLogMessage(ctx, {
|
|
27
|
+
// type: LogTypes.FAILED_LOGIN_INCORRECT_PASSWORD,
|
|
28
|
+
// description: "Invalid password",
|
|
29
|
+
// });
|
|
30
|
+
// await data.logs.create(ctx.var.tenant_id, log);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function postUserRegistrationWebhook(
|
|
36
|
+
ctx: Context<{ Bindings: Bindings; Variables: Variables }>,
|
|
37
|
+
data: DataAdapters,
|
|
38
|
+
) {
|
|
39
|
+
return async (tenant_id: string, user: User): Promise<User> => {
|
|
40
|
+
const { hooks } = await data.hooks.list(tenant_id, {
|
|
41
|
+
q: "trigger_id:post-user-registration",
|
|
42
|
+
page: 0,
|
|
43
|
+
per_page: 100,
|
|
44
|
+
include_totals: false,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
await invokeHooks(ctx, hooks, {
|
|
48
|
+
tenant_id,
|
|
49
|
+
user,
|
|
50
|
+
trigger_id: "post-user-registration",
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
return user;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function postUserLoginWebhook(
|
|
58
|
+
ctx: Context<{ Bindings: Bindings; Variables: Variables }>,
|
|
59
|
+
data: DataAdapters,
|
|
60
|
+
) {
|
|
61
|
+
return async (tenant_id: string, user: User): Promise<User> => {
|
|
62
|
+
const { hooks } = await data.hooks.list(tenant_id, {
|
|
63
|
+
q: "trigger_id:post-user-login",
|
|
64
|
+
page: 0,
|
|
65
|
+
per_page: 100,
|
|
66
|
+
include_totals: false,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
await invokeHooks(ctx, hooks, {
|
|
70
|
+
tenant_id,
|
|
71
|
+
user,
|
|
72
|
+
trigger_id: "post-user-login",
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
return user;
|
|
76
|
+
};
|
|
77
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -2,16 +2,23 @@ import { OpenAPIHono } from "@hono/zod-openapi";
|
|
|
2
2
|
import { Context } from "hono";
|
|
3
3
|
import { Bindings, Variables } from "./types";
|
|
4
4
|
import { wellKnownRoutes } from "./routes/oauth2";
|
|
5
|
+
import createManagementApi from "./management-app";
|
|
6
|
+
import { DataAdapters } from "@authhero/adapter-interfaces";
|
|
5
7
|
|
|
6
|
-
export interface AuthHeroConfig {
|
|
8
|
+
export interface AuthHeroConfig {
|
|
9
|
+
dataAdapter: DataAdapters;
|
|
10
|
+
}
|
|
7
11
|
|
|
8
|
-
export function init() {
|
|
12
|
+
export function init(options: AuthHeroConfig) {
|
|
9
13
|
const rootApp = new OpenAPIHono<{ Bindings: Bindings }>();
|
|
10
14
|
|
|
11
15
|
rootApp.get("/", (ctx: Context) => {
|
|
12
16
|
return ctx.text("Hello, authhero!");
|
|
13
17
|
});
|
|
14
18
|
|
|
19
|
+
const managementApp = createManagementApi(options);
|
|
20
|
+
rootApp.route("/api/v2", managementApp);
|
|
21
|
+
|
|
15
22
|
/**
|
|
16
23
|
* The oauth routes
|
|
17
24
|
*/
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { OpenAPIHono } from "@hono/zod-openapi";
|
|
2
|
+
import { Bindings, Variables } from "./types";
|
|
3
|
+
import { addDataHooks } from "./hooks";
|
|
4
|
+
// import { brandingRoutes } from "./routes/management-api/branding";
|
|
5
|
+
// import { domainRoutes } from "./routes/management-api/domains";
|
|
6
|
+
// import { userRoutes } from "./routes/management-api/users";
|
|
7
|
+
// import { keyRoutes } from "./routes/management-api/keys";
|
|
8
|
+
// import { usersByEmailRoutes } from "./routes/management-api/users-by-email";
|
|
9
|
+
// import { applicationRoutes } from "./routes/management-api/applications";
|
|
10
|
+
import { tenantRoutes } from "./routes/management-api/tenants";
|
|
11
|
+
// import { logRoutes } from "./routes/management-api/logs";
|
|
12
|
+
// import { hooksRoutes } from "./routes/management-api/hooks";
|
|
13
|
+
// import { connectionRoutes } from "./routes/management-api/connections";
|
|
14
|
+
import { registerComponent } from "./middlewares/register-component";
|
|
15
|
+
import { DataAdapters } from "@authhero/adapter-interfaces";
|
|
16
|
+
|
|
17
|
+
export interface CreateAuthParams {
|
|
18
|
+
dataAdapter: DataAdapters;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default function create(params: CreateAuthParams) {
|
|
22
|
+
const app = new OpenAPIHono<{
|
|
23
|
+
Bindings: Bindings;
|
|
24
|
+
Variables: Variables;
|
|
25
|
+
}>();
|
|
26
|
+
|
|
27
|
+
app.use(async (ctx, next) => {
|
|
28
|
+
ctx.env.data = addDataHooks(ctx, params.dataAdapter);
|
|
29
|
+
return next();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const managementApp = app
|
|
33
|
+
// .route("/api/v2/branding", brandingRoutes)
|
|
34
|
+
// .route("/api/v2/domains", domainRoutes)
|
|
35
|
+
// .route("/api/v2/users", userRoutes)
|
|
36
|
+
// .route("/api/v2/keys/signing", keyRoutes)
|
|
37
|
+
// .route("/api/v2/users-by-email", usersByEmailRoutes)
|
|
38
|
+
// .route("/api/v2/applications", applicationRoutes)
|
|
39
|
+
.route("/api/v2/tenants", tenantRoutes);
|
|
40
|
+
// .route("/api/v2/logs", logRoutes)
|
|
41
|
+
// .route("/api/v2/hooks", hooksRoutes)
|
|
42
|
+
// .route("/api/v2/connections", connectionRoutes);
|
|
43
|
+
|
|
44
|
+
registerComponent(managementApp);
|
|
45
|
+
|
|
46
|
+
managementApp.doc("/api/v2/spec", {
|
|
47
|
+
openapi: "3.0.0",
|
|
48
|
+
info: {
|
|
49
|
+
version: "1.0.0",
|
|
50
|
+
title: "Management api",
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
return managementApp;
|
|
55
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { OpenAPIHono } from "@hono/zod-openapi";
|
|
2
|
+
import { Context, Next } from "hono";
|
|
3
|
+
import { Bindings, Variables } from "../types";
|
|
4
|
+
|
|
5
|
+
let inititated = false;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* This registers the security scheme for the application. As it uses an environment variable, it can only be registered once the first request arrives.
|
|
9
|
+
* @param app
|
|
10
|
+
*/
|
|
11
|
+
export function registerComponent(
|
|
12
|
+
app: OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>,
|
|
13
|
+
) {
|
|
14
|
+
app.use(async (ctx: Context<{ Bindings: Bindings }>, next: Next) => {
|
|
15
|
+
if (!inititated) {
|
|
16
|
+
app.openAPIRegistry.registerComponent("securitySchemes", "Bearer", {
|
|
17
|
+
type: "oauth2",
|
|
18
|
+
scheme: "bearer",
|
|
19
|
+
flows: {
|
|
20
|
+
implicit: {
|
|
21
|
+
authorizationUrl: `${ctx.env.AUTH_URL}/authorize`,
|
|
22
|
+
scopes: {
|
|
23
|
+
openid: "Basic user information",
|
|
24
|
+
email: "User email",
|
|
25
|
+
profile: "User profile information",
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
inititated = true;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return await next();
|
|
35
|
+
});
|
|
36
|
+
}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { OpenAPIHono, createRoute, z } from "@hono/zod-openapi";
|
|
2
|
+
import { HTTPException } from "hono/http-exception";
|
|
3
|
+
import { Bindings } from "../../types";
|
|
4
|
+
import { querySchema } from "../../types";
|
|
5
|
+
import { parseSort } from "../../utils/parse-sort";
|
|
6
|
+
// import authenticationMiddleware from "../../middlewares/authentication";
|
|
7
|
+
import {
|
|
8
|
+
tenantInsertSchema,
|
|
9
|
+
tenantSchema,
|
|
10
|
+
totalsSchema,
|
|
11
|
+
} from "@authhero/adapter-interfaces";
|
|
12
|
+
|
|
13
|
+
const tenantsWithTotalsSchema = totalsSchema.extend({
|
|
14
|
+
tenants: z.array(tenantSchema),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
export const tenantRoutes = new OpenAPIHono<{ Bindings: Bindings }>()
|
|
18
|
+
// --------------------------------
|
|
19
|
+
// GET /tenants
|
|
20
|
+
// --------------------------------
|
|
21
|
+
.openapi(
|
|
22
|
+
createRoute({
|
|
23
|
+
tags: ["tenants"],
|
|
24
|
+
method: "get",
|
|
25
|
+
path: "/",
|
|
26
|
+
request: {
|
|
27
|
+
query: querySchema,
|
|
28
|
+
},
|
|
29
|
+
// middleware: [authenticationMiddleware({ scopes: ["auth:read"] })],
|
|
30
|
+
security: [
|
|
31
|
+
{
|
|
32
|
+
Bearer: ["auth:read"],
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
responses: {
|
|
36
|
+
200: {
|
|
37
|
+
content: {
|
|
38
|
+
"tenant/json": {
|
|
39
|
+
schema: z.union([z.array(tenantSchema), tenantsWithTotalsSchema]),
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
description: "List of tenants",
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
}),
|
|
46
|
+
async (ctx) => {
|
|
47
|
+
const { page, per_page, include_totals, sort, q } =
|
|
48
|
+
ctx.req.valid("query");
|
|
49
|
+
|
|
50
|
+
const result = await ctx.env.data.tenants.list({
|
|
51
|
+
page,
|
|
52
|
+
per_page,
|
|
53
|
+
include_totals,
|
|
54
|
+
sort: parseSort(sort),
|
|
55
|
+
q,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
if (include_totals) {
|
|
59
|
+
return ctx.json(result);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return ctx.json(result.tenants);
|
|
63
|
+
},
|
|
64
|
+
)
|
|
65
|
+
// --------------------------------
|
|
66
|
+
// GET /tenants/:id
|
|
67
|
+
// --------------------------------
|
|
68
|
+
.openapi(
|
|
69
|
+
createRoute({
|
|
70
|
+
tags: ["tenants"],
|
|
71
|
+
method: "get",
|
|
72
|
+
path: "/{id}",
|
|
73
|
+
request: {
|
|
74
|
+
params: z.object({
|
|
75
|
+
id: z.string(),
|
|
76
|
+
}),
|
|
77
|
+
},
|
|
78
|
+
// middleware: [authenticationMiddleware({ scopes: ["auth:read"] })],
|
|
79
|
+
security: [
|
|
80
|
+
{
|
|
81
|
+
Bearer: ["auth:read"],
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
responses: {
|
|
85
|
+
200: {
|
|
86
|
+
content: {
|
|
87
|
+
"tenant/json": {
|
|
88
|
+
schema: tenantSchema,
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
description: "A tenant",
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
}),
|
|
95
|
+
async (ctx) => {
|
|
96
|
+
const { id } = ctx.req.valid("param");
|
|
97
|
+
|
|
98
|
+
const tenant = await ctx.env.data.tenants.get(id);
|
|
99
|
+
|
|
100
|
+
if (!tenant) {
|
|
101
|
+
throw new HTTPException(404);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
console.log("tenant", tenant);
|
|
105
|
+
|
|
106
|
+
return ctx.json(tenant);
|
|
107
|
+
},
|
|
108
|
+
)
|
|
109
|
+
// --------------------------------
|
|
110
|
+
// DELETE /tenants/:id
|
|
111
|
+
// --------------------------------
|
|
112
|
+
.openapi(
|
|
113
|
+
createRoute({
|
|
114
|
+
tags: ["tenants"],
|
|
115
|
+
method: "delete",
|
|
116
|
+
path: "/{id}",
|
|
117
|
+
request: {
|
|
118
|
+
params: z.object({
|
|
119
|
+
id: z.string(),
|
|
120
|
+
}),
|
|
121
|
+
},
|
|
122
|
+
// middleware: [authenticationMiddleware({ scopes: ["auth:write"] })],
|
|
123
|
+
security: [
|
|
124
|
+
{
|
|
125
|
+
Bearer: ["auth:write"],
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
responses: {
|
|
129
|
+
200: {
|
|
130
|
+
description: "Status",
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
}),
|
|
134
|
+
async (ctx) => {
|
|
135
|
+
const { id } = ctx.req.valid("param");
|
|
136
|
+
|
|
137
|
+
await ctx.env.data.tenants.remove(id);
|
|
138
|
+
|
|
139
|
+
return ctx.text("OK");
|
|
140
|
+
},
|
|
141
|
+
)
|
|
142
|
+
// --------------------------------
|
|
143
|
+
// PATCH /tenants/:id
|
|
144
|
+
// --------------------------------
|
|
145
|
+
.openapi(
|
|
146
|
+
createRoute({
|
|
147
|
+
tags: ["tenants"],
|
|
148
|
+
method: "patch",
|
|
149
|
+
path: "/{id}",
|
|
150
|
+
request: {
|
|
151
|
+
body: {
|
|
152
|
+
content: {
|
|
153
|
+
"application/json": {
|
|
154
|
+
schema: z.object(tenantInsertSchema.shape).partial(),
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
params: z.object({
|
|
159
|
+
id: z.string(),
|
|
160
|
+
}),
|
|
161
|
+
},
|
|
162
|
+
// middleware: [authenticationMiddleware({ scopes: ["auth:write"] })],
|
|
163
|
+
security: [
|
|
164
|
+
{
|
|
165
|
+
Bearer: ["auth:write"],
|
|
166
|
+
},
|
|
167
|
+
],
|
|
168
|
+
responses: {
|
|
169
|
+
200: {
|
|
170
|
+
description: "Status",
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
}),
|
|
174
|
+
async (ctx) => {
|
|
175
|
+
const { id } = ctx.req.valid("param");
|
|
176
|
+
const body = ctx.req.valid("json");
|
|
177
|
+
|
|
178
|
+
await ctx.env.data.tenants.update(id, body);
|
|
179
|
+
|
|
180
|
+
return ctx.text("OK");
|
|
181
|
+
},
|
|
182
|
+
)
|
|
183
|
+
// --------------------------------
|
|
184
|
+
// POST /tenants
|
|
185
|
+
// --------------------------------
|
|
186
|
+
.openapi(
|
|
187
|
+
createRoute({
|
|
188
|
+
tags: ["tenants"],
|
|
189
|
+
method: "post",
|
|
190
|
+
path: "/",
|
|
191
|
+
request: {
|
|
192
|
+
body: {
|
|
193
|
+
content: {
|
|
194
|
+
"application/json": {
|
|
195
|
+
schema: z.object(tenantInsertSchema.shape),
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
// middleware: [authenticationMiddleware({ scopes: ["auth:write"] })],
|
|
201
|
+
security: [
|
|
202
|
+
{
|
|
203
|
+
Bearer: ["auth:write"],
|
|
204
|
+
},
|
|
205
|
+
],
|
|
206
|
+
responses: {
|
|
207
|
+
200: {
|
|
208
|
+
content: {
|
|
209
|
+
"tenant/json": {
|
|
210
|
+
schema: tenantSchema,
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
description: "An tenant",
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
}),
|
|
217
|
+
async (ctx) => {
|
|
218
|
+
const body = ctx.req.valid("json");
|
|
219
|
+
|
|
220
|
+
const tenant = await ctx.env.data.tenants.create(body);
|
|
221
|
+
|
|
222
|
+
return ctx.json(tenant, { status: 201 });
|
|
223
|
+
},
|
|
224
|
+
);
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
|
-
Bindings,
|
|
3
2
|
jwksKeySchema,
|
|
4
|
-
// jwksSchema,
|
|
5
3
|
openIDConfigurationSchema,
|
|
6
|
-
} from "
|
|
4
|
+
} from "@authhero/adapter-interfaces";
|
|
5
|
+
import { Bindings } from "../../types";
|
|
7
6
|
import { OpenAPIHono, createRoute } from "@hono/zod-openapi";
|
|
8
7
|
|
|
9
8
|
export const wellKnownRoutes = new OpenAPIHono<{ Bindings: Bindings }>()
|
package/src/types/Bindings.ts
CHANGED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { z } from "@hono/zod-openapi";
|
|
2
|
+
|
|
3
|
+
export const querySchema = z.object({
|
|
4
|
+
page: z
|
|
5
|
+
.string()
|
|
6
|
+
.min(0)
|
|
7
|
+
.optional()
|
|
8
|
+
.default("0")
|
|
9
|
+
.transform((p) => parseInt(p, 10))
|
|
10
|
+
.openapi({
|
|
11
|
+
description: "The page number where 0 is the first page",
|
|
12
|
+
}),
|
|
13
|
+
per_page: z
|
|
14
|
+
.string()
|
|
15
|
+
.min(1)
|
|
16
|
+
.optional()
|
|
17
|
+
.default("10")
|
|
18
|
+
.transform((p) => parseInt(p, 10))
|
|
19
|
+
.openapi({
|
|
20
|
+
description: "The number of items per page",
|
|
21
|
+
}),
|
|
22
|
+
include_totals: z
|
|
23
|
+
.string()
|
|
24
|
+
.optional()
|
|
25
|
+
.default("false")
|
|
26
|
+
.transform((it) => it === "true")
|
|
27
|
+
.openapi({
|
|
28
|
+
description:
|
|
29
|
+
"If the total number of items should be included in the response",
|
|
30
|
+
}),
|
|
31
|
+
sort: z
|
|
32
|
+
.string()
|
|
33
|
+
.regex(/^.+:(-1|1)$/)
|
|
34
|
+
.optional()
|
|
35
|
+
.openapi({
|
|
36
|
+
description:
|
|
37
|
+
"A property that should have the format 'string:-1' or 'string:1'",
|
|
38
|
+
}),
|
|
39
|
+
q: z.string().optional().openapi({
|
|
40
|
+
description: "A lucene query string used to filter the results",
|
|
41
|
+
}),
|
|
42
|
+
});
|
package/src/types/Variables.ts
CHANGED
package/src/types/index.ts
CHANGED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function parseSort(sort?: string):
|
|
2
|
+
| undefined
|
|
3
|
+
| {
|
|
4
|
+
sort_by: string;
|
|
5
|
+
sort_order: "asc" | "desc";
|
|
6
|
+
} {
|
|
7
|
+
if (!sort) {
|
|
8
|
+
return undefined;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const [sort_by = "", orderString] = sort.split(":");
|
|
12
|
+
const sort_order = orderString === "1" ? "asc" : "desc";
|
|
13
|
+
|
|
14
|
+
return {
|
|
15
|
+
sort_by,
|
|
16
|
+
sort_order,
|
|
17
|
+
};
|
|
18
|
+
}
|