@protobase/server 0.45.13 → 0.45.15
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/better-auth/auth-mail/auth-emails.d.ts +1 -0
- package/dist/better-auth/auth-mail/auth-emails.js +2 -1
- package/dist/better-auth/auth-mail/no-account-email.d.ts +1 -0
- package/dist/better-auth/auth-mail/no-account-email.js +32 -0
- package/dist/better-auth/authenticator.js +13 -7
- package/dist/better-auth/create-auth.d.ts +30 -1
- package/dist/better-auth/create-auth.js +15 -2
- package/dist/better-auth/no-account-mail.js +24 -0
- package/dist/better-auth/token-standing.js +21 -12
- package/dist/better-auth/user-deletion.d.ts +4 -0
- package/dist/better-auth/user-deletion.js +50 -0
- package/dist/better-auth/users.d.ts +1 -1
- package/dist/better-auth/users.js +3 -1
- package/dist/create-admin.d.ts +4 -1
- package/dist/create-admin.js +9 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.js +7 -1
- package/dist/notifications/caller.js +16 -0
- package/dist/notifications/device-input.js +80 -0
- package/dist/notifications/device-store-of.d.ts +13 -0
- package/dist/notifications/device-store-of.js +13 -0
- package/dist/notifications/device-store.d.ts +25 -0
- package/dist/notifications/device-store.js +22 -0
- package/dist/notifications/device.d.ts +30 -0
- package/dist/notifications/device.js +5 -0
- package/dist/notifications/options.d.ts +20 -0
- package/dist/notifications/options.js +43 -0
- package/dist/notifications/paths.d.ts +6 -0
- package/dist/notifications/paths.js +7 -0
- package/dist/notifications/plugin.d.ts +10 -0
- package/dist/notifications/plugin.js +13 -0
- package/dist/notifications/postgres-store.d.ts +15 -0
- package/dist/notifications/postgres-store.js +110 -0
- package/dist/notifications/push-hosts.js +18 -0
- package/dist/notifications/routes.d.ts +6 -0
- package/dist/notifications/routes.js +57 -0
- package/dist/notifications/schema-sql.js +40 -0
- package/dist/notifications/schema.d.ts +17 -0
- package/dist/notifications/schema.js +59 -0
- package/dist/package.js +1 -1
- package/dist/project-config.d.ts +3 -0
- package/dist/project-config.js +3 -1
- package/dist/types.d.ts +9 -0
- package/package.json +4 -4
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { accountExistsEmail } from "./account-exists-email.js";
|
|
2
2
|
import { invitationEmail } from "./invitation-email.js";
|
|
3
|
+
import { noAccountEmail } from "./no-account-email.js";
|
|
3
4
|
import { resetPasswordEmail } from "./reset-password-email.js";
|
|
4
5
|
import { signInCodeEmail } from "./sign-in-code-email.js";
|
|
5
6
|
import { signUpCodeEmail } from "./sign-up-code-email.js";
|
|
6
7
|
import { staffSignInEmail } from "./staff-sign-in-email.js";
|
|
7
8
|
import { twoFactorCodeEmail } from "./two-factor-code-email.js";
|
|
8
|
-
export { accountExistsEmail, invitationEmail, resetPasswordEmail, signInCodeEmail, signUpCodeEmail, staffSignInEmail, twoFactorCodeEmail };
|
|
9
|
+
export { accountExistsEmail, invitationEmail, noAccountEmail, resetPasswordEmail, signInCodeEmail, signUpCodeEmail, staffSignInEmail, twoFactorCodeEmail };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import "../../index.js";
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { defineEmail } from "../../mail/define-email.js";
|
|
2
|
+
import { EmailButton, EmailLayout, EmailNote } from "../../mail/email-layout.js";
|
|
3
|
+
import { signInPath } from "@protobase/schema";
|
|
4
|
+
import { Text } from "react-email";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
//#region src/better-auth/auth-mail/no-account-email.tsx
|
|
7
|
+
const NoAccountEmail = ({ app }) => /* @__PURE__ */ jsxs(EmailLayout, {
|
|
8
|
+
app,
|
|
9
|
+
preview: `No ${app.name} account uses this address`,
|
|
10
|
+
heading: "No account for this address",
|
|
11
|
+
children: [
|
|
12
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
13
|
+
"Someone asked for a code to sign in to ",
|
|
14
|
+
app.name,
|
|
15
|
+
" with this address. No account uses it, so no code was sent."
|
|
16
|
+
] }),
|
|
17
|
+
/* @__PURE__ */ jsx(Text, { children: "If you have an account, sign in again with the address you signed up with." }),
|
|
18
|
+
/* @__PURE__ */ jsxs(EmailButton, {
|
|
19
|
+
href: new URL(signInPath(), app.url).href,
|
|
20
|
+
children: ["Sign in to ", app.name]
|
|
21
|
+
}),
|
|
22
|
+
/* @__PURE__ */ jsx(EmailNote, { children: "If this was not you, ignore this email: nothing was changed." })
|
|
23
|
+
]
|
|
24
|
+
});
|
|
25
|
+
/** The email to an address that asked for a sign-in code while no account uses it: no code, a hint to try another address. */
|
|
26
|
+
const noAccountEmail = defineEmail({
|
|
27
|
+
name: "no-account",
|
|
28
|
+
subject: ({ app }) => `No ${app.name} account uses this address`,
|
|
29
|
+
component: NoAccountEmail
|
|
30
|
+
});
|
|
31
|
+
//#endregion
|
|
32
|
+
export { noAccountEmail };
|
|
@@ -44,8 +44,12 @@ const betterAuthAuthenticator = (options) => {
|
|
|
44
44
|
});
|
|
45
45
|
const verify = async (request) => {
|
|
46
46
|
const { payload, session } = await sessions(request);
|
|
47
|
-
if (auth)
|
|
48
|
-
|
|
47
|
+
if (!auth) return session;
|
|
48
|
+
const signIn = await standing(await auth.$context, payload, session);
|
|
49
|
+
return signIn ? {
|
|
50
|
+
...session,
|
|
51
|
+
signIn
|
|
52
|
+
} : session;
|
|
49
53
|
};
|
|
50
54
|
if (!organizations) return verify;
|
|
51
55
|
return async (request) => {
|
|
@@ -65,11 +69,13 @@ const betterAuthAuthenticator = (options) => {
|
|
|
65
69
|
const dpopReplayStore = () => replayStore ??= options.auth ? options.auth.$context.then(({ internalAdapter }) => createDpopReplayStore(internalAdapter)) : Promise.resolve(createInMemoryDpopReplayStore());
|
|
66
70
|
const ownStanding = (context, payload, session) => checkTokenStanding(context, tokenClaims(payload, session.user.roles, organizations ? session.tenant : void 0), organizations);
|
|
67
71
|
const own = sessionOf({ audience: options.audience ?? issuer }, ownStanding);
|
|
68
|
-
const grantStanding = (context, payload, session) =>
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
72
|
+
const grantStanding = async (context, payload, session) => {
|
|
73
|
+
await checkMcpGrant(context.adapter, {
|
|
74
|
+
userId: payload.sub,
|
|
75
|
+
clientId: session.client?.id,
|
|
76
|
+
issuedAt: payload.iat
|
|
77
|
+
});
|
|
78
|
+
};
|
|
73
79
|
return Object.assign(withTrustedApps(own, issuer, options.apps ?? []), { forAudience: (audience) => sessionOf({
|
|
74
80
|
audience,
|
|
75
81
|
typ: "at+jwt",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { StaffSignIn } from "./staff-log.js";
|
|
2
2
|
import { RoleDefinitions, RolesInput } from "./organizations/role-definitions.js";
|
|
3
3
|
import { ConnectedApp } from "./mcp/connected-apps.js";
|
|
4
|
+
import { UserDeletionListener } from "./user-deletion.js";
|
|
4
5
|
import "./auth-base-path.js";
|
|
5
6
|
import { Mailer } from "../mail/smtp-mailer.js";
|
|
6
7
|
import { OperatorProvider } from "./operator-provider.js";
|
|
@@ -232,6 +233,18 @@ export declare const createAuth: (options: CreateAuthOptions) => import("better-
|
|
|
232
233
|
};
|
|
233
234
|
};
|
|
234
235
|
plugins: ({
|
|
236
|
+
id: "protobase-user-deletion";
|
|
237
|
+
hooks: {
|
|
238
|
+
before: {
|
|
239
|
+
matcher: (context: import("better-auth").HookEndpointContext) => boolean;
|
|
240
|
+
handler: import("better-call").Middleware<import("better-call").MiddlewareOptions, (inputContext: import("better-call").MiddlewareInputContext<import("better-call").MiddlewareOptions>) => Promise<void>>;
|
|
241
|
+
}[];
|
|
242
|
+
after: {
|
|
243
|
+
matcher: (context: import("better-auth").HookEndpointContext) => boolean;
|
|
244
|
+
handler: import("better-call").Middleware<import("better-call").MiddlewareOptions, (inputContext: import("better-call").MiddlewareInputContext<import("better-call").MiddlewareOptions>) => Promise<void>>;
|
|
245
|
+
}[];
|
|
246
|
+
};
|
|
247
|
+
} | {
|
|
235
248
|
id: "admin";
|
|
236
249
|
version: string;
|
|
237
250
|
init(): {
|
|
@@ -3105,6 +3118,14 @@ export declare const createAuth: (options: CreateAuthOptions) => import("better-
|
|
|
3105
3118
|
INVALID_OTP: import("better-auth").RawError<"INVALID_OTP">;
|
|
3106
3119
|
TOO_MANY_ATTEMPTS: import("better-auth").RawError<"TOO_MANY_ATTEMPTS">;
|
|
3107
3120
|
};
|
|
3121
|
+
} | {
|
|
3122
|
+
id: "protobase-no-account-mail";
|
|
3123
|
+
hooks: {
|
|
3124
|
+
after: {
|
|
3125
|
+
matcher: (ctx: import("better-auth").HookEndpointContext) => boolean;
|
|
3126
|
+
handler: import("better-call").Middleware<import("better-call").MiddlewareOptions, (inputContext: import("better-call").MiddlewareInputContext<import("better-call").MiddlewareOptions>) => Promise<void>>;
|
|
3127
|
+
}[];
|
|
3128
|
+
};
|
|
3108
3129
|
} | {
|
|
3109
3130
|
id: "protobase-sign-up";
|
|
3110
3131
|
endpoints: {
|
|
@@ -3731,7 +3752,7 @@ export declare const createAuth: (options: CreateAuthOptions) => import("better-
|
|
|
3731
3752
|
userId: string;
|
|
3732
3753
|
expiresAt: Date;
|
|
3733
3754
|
token: string;
|
|
3734
|
-
ipAddress
|
|
3755
|
+
ipAddress? /** The app keeps each user's metadata, in the column `users.metadata` names. */ : string | null | undefined;
|
|
3735
3756
|
userAgent?: string | null | undefined;
|
|
3736
3757
|
};
|
|
3737
3758
|
user: Record<string, any> & {
|
|
@@ -8717,6 +8738,14 @@ export declare const createAuth: (options: CreateAuthOptions) => import("better-
|
|
|
8717
8738
|
* names. Only `createAdmin` with a `mobile` section uses it; `handler` and `api` never trust a Capacitor origin.
|
|
8718
8739
|
*/
|
|
8719
8740
|
handleMobile: (request: Request) => Promise<Response>;
|
|
8741
|
+
/**
|
|
8742
|
+
* What runs when a user is deleted: `set` a named listener that removes what the app keeps of the user elsewhere,
|
|
8743
|
+
* such as their devices; `run` is for a deletion outside Better Auth's endpoints (`deleteUser`).
|
|
8744
|
+
*/
|
|
8745
|
+
userDeletion: {
|
|
8746
|
+
set: (name: string, listener: UserDeletionListener) => undefined;
|
|
8747
|
+
run: (userId: string) => Promise<void>;
|
|
8748
|
+
};
|
|
8720
8749
|
/** Issuer and audience of the tokens: `baseURL`, on every origin. */
|
|
8721
8750
|
issuer: string;
|
|
8722
8751
|
/** Every origin the app is served on, `baseURL`'s first. */
|
|
@@ -12,6 +12,7 @@ import { platformMailer } from "../mail/platform-mailer.js";
|
|
|
12
12
|
import { readOperatorSettings, resolveOperator } from "./operator-provider.js";
|
|
13
13
|
import { defaultInvitationUrl } from "./invitations/invitation-link-hook.js";
|
|
14
14
|
import { lastAdminGuardPlugin } from "./last-admin-guard.js";
|
|
15
|
+
import { userDeletion } from "./user-deletion.js";
|
|
15
16
|
import { revokeMcpGrants } from "./mcp/revoke-grants.js";
|
|
16
17
|
import { coreNames, pluginNames } from "./snake-case-names.js";
|
|
17
18
|
import { lastOrganizationField, sessionOrganizationHooks } from "./organizations/session-organization.js";
|
|
@@ -28,6 +29,7 @@ import { platformSignInFor, platformSignInProvider, readPlatformSignIn, resolveP
|
|
|
28
29
|
import { appSignInPolicy } from "./app-sign-in-policy.js";
|
|
29
30
|
import { assertNoEndpointKeyClashes } from "./endpoint-key-clashes.js";
|
|
30
31
|
import { emailInDomains, resolveSamlProviders, samlDisabledPaths, samlGuardPlugin, samlPlugin } from "./saml-sign-in.js";
|
|
32
|
+
import { noAccountMailPlugin } from "./no-account-mail.js";
|
|
31
33
|
import { emailCodeOptions, twoFactorCodeOptions, unusedEmailCodePaths } from "./sign-in-mail.js";
|
|
32
34
|
import { signUpPaths, signUpPlugin } from "./sign-up/sign-up-plugin.js";
|
|
33
35
|
import { confirmedOnCreate, confirmedOnUpdate, emailConfirmedField } from "./email-confirmed.js";
|
|
@@ -62,7 +64,7 @@ const organizationPaths = ["/organization/set-active", "/organization/accept-inv
|
|
|
62
64
|
* The options `createAuth` gives Better Auth, with the roles, default role, mailer, operator provider and platform
|
|
63
65
|
* sign-in provider already resolved.
|
|
64
66
|
*/
|
|
65
|
-
const betterAuthOptions = (options, { origins = [], passkeyDomain, roles, definitions, defaultRole, appName = defaultAppName, mailer, signUpRole, operator, platformSignIn, saml = [], organizations, mcp, context = noContext, signInPolicy = defaultSignInPolicy, mobile }) => {
|
|
67
|
+
const betterAuthOptions = (options, { origins = [], passkeyDomain, roles, definitions, defaultRole, appName = defaultAppName, mailer, signUpRole, operator, platformSignIn, saml = [], organizations, mcp, context = noContext, signInPolicy = defaultSignInPolicy, mobile, deletion }) => {
|
|
66
68
|
const secure = options.baseURL.startsWith("https://");
|
|
67
69
|
const signIn = platformSignInFor(platformSignIn, Object.keys(options.socialProviders ?? {}));
|
|
68
70
|
const hosts = passkeyHosts([options.baseURL, ...origins]);
|
|
@@ -161,7 +163,7 @@ const betterAuthOptions = (options, { origins = [], passkeyDomain, roles, defini
|
|
|
161
163
|
formerDomain: hosts[0],
|
|
162
164
|
origins: appOrigins(options.baseURL, origins)
|
|
163
165
|
}),
|
|
164
|
-
...mail ? [emailOTP(emailCodeOptions(mail))] : [],
|
|
166
|
+
...mail ? [emailOTP(emailCodeOptions(mail)), noAccountMailPlugin(mail)] : [],
|
|
165
167
|
...mail && signUpRole ? [signUpPlugin({
|
|
166
168
|
role: signUpRole,
|
|
167
169
|
mail,
|
|
@@ -182,6 +184,7 @@ const betterAuthOptions = (options, { origins = [], passkeyDomain, roles, defini
|
|
|
182
184
|
}),
|
|
183
185
|
rolesPlugin({ definitions }),
|
|
184
186
|
lastAdminGuardPlugin(),
|
|
187
|
+
...deletion ? [deletion.plugin] : [],
|
|
185
188
|
...organizations ? [organizationPlugin({
|
|
186
189
|
resolved: organizations,
|
|
187
190
|
secret: options.secret,
|
|
@@ -314,8 +317,10 @@ const createAuth = (options) => {
|
|
|
314
317
|
let instance;
|
|
315
318
|
const context = () => instance?.$context ?? Promise.reject(/* @__PURE__ */ new Error("The auth instance is not made yet"));
|
|
316
319
|
const mobile = mobileRequests();
|
|
320
|
+
const deletion = userDeletion();
|
|
317
321
|
const authOptions = betterAuthOptions(options, {
|
|
318
322
|
mobile,
|
|
323
|
+
deletion,
|
|
319
324
|
origins,
|
|
320
325
|
passkeyDomain,
|
|
321
326
|
roles,
|
|
@@ -341,6 +346,14 @@ const createAuth = (options) => {
|
|
|
341
346
|
* names. Only `createAdmin` with a `mobile` section uses it; `handler` and `api` never trust a Capacitor origin.
|
|
342
347
|
*/
|
|
343
348
|
handleMobile: (request) => auth.handler(mobile.grant(request)),
|
|
349
|
+
/**
|
|
350
|
+
* What runs when a user is deleted: `set` a named listener that removes what the app keeps of the user elsewhere,
|
|
351
|
+
* such as their devices; `run` is for a deletion outside Better Auth's endpoints (`deleteUser`).
|
|
352
|
+
*/
|
|
353
|
+
userDeletion: {
|
|
354
|
+
set: deletion.set,
|
|
355
|
+
run: deletion.run
|
|
356
|
+
},
|
|
344
357
|
/** Issuer and audience of the tokens: `baseURL`, on every origin. */
|
|
345
358
|
issuer: options.baseURL,
|
|
346
359
|
/** Every origin the app is served on, `baseURL`'s first. */
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { sendAuthEmail } from "./auth-mail/send-auth-email.js";
|
|
2
|
+
import { APIError, createAuthMiddleware } from "better-auth/api";
|
|
3
|
+
//#region src/better-auth/no-account-mail.ts
|
|
4
|
+
/**
|
|
5
|
+
* Mails an address that asked for a sign-in code while no account uses it, so its owner knows to try another address.
|
|
6
|
+
* The email OTP plugin answers such a request as it answers one for an account, and mails nothing; this hook sends the
|
|
7
|
+
* no-account email after it, so the answer still tells nobody which addresses have an account.
|
|
8
|
+
*/
|
|
9
|
+
const noAccountMailPlugin = (mail) => ({
|
|
10
|
+
id: "protobase-no-account-mail",
|
|
11
|
+
hooks: { after: [{
|
|
12
|
+
matcher: (ctx) => ctx.path === "/email-otp/send-verification-otp",
|
|
13
|
+
handler: createAuthMiddleware(async (ctx) => {
|
|
14
|
+
if (ctx.context.returned instanceof APIError) return;
|
|
15
|
+
const { email, type } = ctx.body;
|
|
16
|
+
if (type !== "sign-in") return;
|
|
17
|
+
const address = email.toLowerCase();
|
|
18
|
+
if (await ctx.context.internalAdapter.findUserByEmail(address)) return;
|
|
19
|
+
await ctx.context.runInBackgroundOrAwait(sendAuthEmail(mail, "noAccountEmail", address, {}, ctx.context.baseURL));
|
|
20
|
+
})
|
|
21
|
+
}] }
|
|
22
|
+
});
|
|
23
|
+
//#endregion
|
|
24
|
+
export { noAccountMailPlugin };
|
|
@@ -3,7 +3,16 @@ import { noGlobalRole } from "./organizations/role-definitions.js";
|
|
|
3
3
|
import { parseRoles } from "./parse-roles.js";
|
|
4
4
|
import { effectiveRoles, globalRoles } from "./organizations/app-roles.js";
|
|
5
5
|
import { findMember, findOrganization } from "./organizations/store.js";
|
|
6
|
+
import { isStaffSession } from "./staff-checks.js";
|
|
6
7
|
//#region src/better-auth/token-standing.ts
|
|
8
|
+
/**
|
|
9
|
+
* The sign-in a session row stands for: staff started it when the admin plugin's `impersonatedBy` names someone. A row
|
|
10
|
+
* read without that field gives `undefined`, so a caller who cannot be told apart from staff is never taken for the user.
|
|
11
|
+
*/
|
|
12
|
+
const signInOf = (session, id) => "impersonatedBy" in session && session.impersonatedBy !== void 0 ? {
|
|
13
|
+
session: id,
|
|
14
|
+
staff: isStaffSession(session)
|
|
15
|
+
} : void 0;
|
|
7
16
|
const tokenClaims = (payload, roles, tenant) => ({
|
|
8
17
|
userId: payload.sub,
|
|
9
18
|
...typeof payload.sid === "string" && { sessionId: payload.sid },
|
|
@@ -26,20 +35,19 @@ const heldWhere = async ({ adapter }, user, organizations, organizationId) => {
|
|
|
26
35
|
/**
|
|
27
36
|
* Whether a token still stands, read from the store on every request: its user exists and is not banned, its session
|
|
28
37
|
* is not signed out or expired, and each role it carries is one the user still holds there. A token that fails is a
|
|
29
|
-
* 401, so the client asks its session for a new one, which carries the roles as they are now or is refused.
|
|
38
|
+
* 401, so the client asks its session for a new one, which carries the roles as they are now or is refused. Answers the
|
|
39
|
+
* sign-in the token's session stands for (`signInOf`), `undefined` for a token without one.
|
|
30
40
|
*/
|
|
31
41
|
const checkTokenStanding = async (context, claims, organizations, now = /* @__PURE__ */ new Date()) => {
|
|
32
42
|
const { adapter } = context;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if (!session || session.userId !== claims.userId || session.expiresAt <= now) throw revoked();
|
|
42
|
-
}
|
|
43
|
+
const session = claims.sessionId === void 0 ? void 0 : await adapter.findOne({
|
|
44
|
+
model: "session",
|
|
45
|
+
where: [{
|
|
46
|
+
field: "id",
|
|
47
|
+
value: claims.sessionId
|
|
48
|
+
}]
|
|
49
|
+
});
|
|
50
|
+
if (claims.sessionId !== void 0 && (!session || session.userId !== claims.userId || session.expiresAt <= now)) throw revoked();
|
|
43
51
|
const user = await adapter.findOne({
|
|
44
52
|
model: "user",
|
|
45
53
|
where: [{
|
|
@@ -50,6 +58,7 @@ const checkTokenStanding = async (context, claims, organizations, now = /* @__PU
|
|
|
50
58
|
if (!user || bannedNow(user, now)) throw revoked();
|
|
51
59
|
const held = await heldWhere(context, user, organizations, claims.organizationId);
|
|
52
60
|
if (!held || claims.roles.some((role) => !held.includes(role))) throw revoked();
|
|
61
|
+
return session ? signInOf(session, claims.sessionId) : void 0;
|
|
53
62
|
};
|
|
54
63
|
//#endregion
|
|
55
|
-
export { checkTokenStanding, tokenClaims };
|
|
64
|
+
export { checkTokenStanding, signInOf, tokenClaims };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import "./organizations/role-definitions.js";
|
|
2
|
+
import { parseRoles } from "./parse-roles.js";
|
|
3
|
+
import { createAuthMiddleware, getSessionFromCtx, isAPIError } from "better-auth/api";
|
|
4
|
+
//#region src/better-auth/user-deletion.ts
|
|
5
|
+
const removePath = "/admin/remove-user";
|
|
6
|
+
/**
|
|
7
|
+
* What runs when a user is deleted, by an admin (`/admin/remove-user`) or by `deleteUser` (the CLI): each listener
|
|
8
|
+
* before anything is deleted, so one that throws stops the deletion with the user, their sessions and accounts in
|
|
9
|
+
* place, and once more after the user is gone, for what was added in between.
|
|
10
|
+
*/
|
|
11
|
+
const userDeletion = () => {
|
|
12
|
+
const listeners = /* @__PURE__ */ new Map();
|
|
13
|
+
const run = async (userId) => {
|
|
14
|
+
for (const listener of listeners.values()) await listener(userId);
|
|
15
|
+
};
|
|
16
|
+
const allowedRemoval = async (ctx) => {
|
|
17
|
+
const userId = ctx.body?.userId;
|
|
18
|
+
if (typeof userId !== "string" || userId === "") return void 0;
|
|
19
|
+
const session = await getSessionFromCtx(ctx);
|
|
20
|
+
if (!session || !parseRoles(session.user.role).includes("admin") || session.user.id === userId) return void 0;
|
|
21
|
+
return await ctx.context.internalAdapter.findUserById(userId) ? userId : void 0;
|
|
22
|
+
};
|
|
23
|
+
return {
|
|
24
|
+
plugin: {
|
|
25
|
+
id: "protobase-user-deletion",
|
|
26
|
+
hooks: {
|
|
27
|
+
before: [{
|
|
28
|
+
matcher: (context) => context.path === removePath,
|
|
29
|
+
handler: createAuthMiddleware(async (ctx) => {
|
|
30
|
+
const userId = await allowedRemoval(ctx);
|
|
31
|
+
if (userId !== void 0) await run(userId);
|
|
32
|
+
})
|
|
33
|
+
}],
|
|
34
|
+
after: [{
|
|
35
|
+
matcher: (context) => context.path === removePath,
|
|
36
|
+
handler: createAuthMiddleware(async (ctx) => {
|
|
37
|
+
const userId = ctx.body?.userId;
|
|
38
|
+
if (isAPIError(ctx.context.returned) || typeof userId !== "string" || userId === "") return;
|
|
39
|
+
await run(userId);
|
|
40
|
+
})
|
|
41
|
+
}]
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
run,
|
|
45
|
+
/** Sets the listener of `name`, in place of the one it had: an app built again does not add another. */
|
|
46
|
+
set: (name, listener) => void listeners.set(name, listener)
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
//#endregion
|
|
50
|
+
export { userDeletion };
|
|
@@ -42,7 +42,7 @@ export declare const createUser: (auth: AdminAuth, input: NewUser) => Promise<{
|
|
|
42
42
|
role: string;
|
|
43
43
|
}>;
|
|
44
44
|
export declare const listUsers: (auth: AdminAuth) => Promise<StoredUser[]>;
|
|
45
|
-
/** Deletes the user with their sessions and accounts; refuses to delete the last active admin. */
|
|
45
|
+
/** Deletes the user with their sessions and accounts, and what `auth.userDeletion` removes; refuses to delete the last active admin. */
|
|
46
46
|
export declare const deleteUser: (auth: AdminAuth, email: string) => Promise<void>;
|
|
47
47
|
/** Sets the role: one of the configured roles, or several separated by commas; refuses to demote the last active admin. */
|
|
48
48
|
export declare const setUserRole: (auth: AdminAuth, input: {
|
|
@@ -80,13 +80,15 @@ const listUsers = async (auth) => (await allUsers(auth)).map((user) => ({
|
|
|
80
80
|
banned: Boolean(user.banned),
|
|
81
81
|
createdAt: new Date(user.createdAt).toISOString()
|
|
82
82
|
}));
|
|
83
|
-
/** Deletes the user with their sessions and accounts; refuses to delete the last active admin. */
|
|
83
|
+
/** Deletes the user with their sessions and accounts, and what `auth.userDeletion` removes; refuses to delete the last active admin. */
|
|
84
84
|
const deleteUser = async (auth, email) => {
|
|
85
85
|
const user = await findUser(auth, email);
|
|
86
86
|
await refuseLastAdmin(auth, user, "delete");
|
|
87
87
|
const { internalAdapter } = await auth.$context;
|
|
88
|
+
await auth.userDeletion.run(user.id);
|
|
88
89
|
await internalAdapter.deleteUserSessions(user.id);
|
|
89
90
|
await internalAdapter.deleteUser(user.id);
|
|
91
|
+
await auth.userDeletion.run(user.id);
|
|
90
92
|
};
|
|
91
93
|
/** Sets the role: one of the configured roles, or several separated by commas; refuses to demote the last active admin. */
|
|
92
94
|
const setUserRole = async (auth, input) => {
|
package/dist/create-admin.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { Admin, Authenticator, PageSource, ViewSource } from "./types.js";
|
|
|
8
8
|
import { CommentUsers } from "./comments/comment-users.js";
|
|
9
9
|
import { HistoryOptions } from "./history/options.js";
|
|
10
10
|
import { WebhooksOptions } from "./webhooks/table/options.js";
|
|
11
|
+
import { NotificationsOptions } from "./notifications/options.js";
|
|
11
12
|
import { SubscriptionDefinition } from "./webhooks/subscription.js";
|
|
12
13
|
import { FilesOptions } from "./files/options.js";
|
|
13
14
|
import { Difficulty } from "./forms/challenge.js";
|
|
@@ -46,6 +47,8 @@ export type CreateAdminInput = {
|
|
|
46
47
|
subscriptions?: SubscriptionDefinition[];
|
|
47
48
|
/** Webhook subscriptions made in the admin app, from `extends: [webhooks()]` (see `WebhooksOptions`). */
|
|
48
49
|
webhooks?: WebhooksOptions;
|
|
50
|
+
/** Each user's devices and the endpoints that register them, from `extends: [notifications()]` (see `NotificationsOptions`). */
|
|
51
|
+
notifications?: NotificationsOptions;
|
|
49
52
|
/**
|
|
50
53
|
* The app's iOS and Android build (see `MobileConfig`): the API answers its Capacitor origins through CORS, and sign-in
|
|
51
54
|
* at `/api/auth` trusts the Capacitor origin a request names, without CORS, as the app calls it through native HTTP.
|
|
@@ -85,5 +88,5 @@ export type CreateAdminInput = {
|
|
|
85
88
|
upgradeWebSocket?: UpgradeWebSocket;
|
|
86
89
|
};
|
|
87
90
|
/** The admin REST API as a Hono app. It uses only Web APIs, so it runs on Node, Workers and anywhere else Hono does. */
|
|
88
|
-
export declare const createAdmin: ({ resources: appResources, views: appViews, pages, userMenu, bottomBars, db, authenticate, auth, functions, files: filesOptions, events, history: historyOptions, jobs, subscriptions, webhooks: webhooksOptions, mobile, app: identity, icons, bundlePlugins, fieldTypes, options, formsDifficulty, commentUsers: givenCommentUsers, verifyToken, upgradeWebSocket }: CreateAdminInput) => Admin;
|
|
91
|
+
export declare const createAdmin: ({ resources: appResources, views: appViews, pages, userMenu, bottomBars, db, authenticate, auth, functions, files: filesOptions, events, history: historyOptions, jobs, subscriptions, webhooks: webhooksOptions, notifications, mobile, app: identity, icons, bundlePlugins, fieldTypes, options, formsDifficulty, commentUsers: givenCommentUsers, verifyToken, upgradeWebSocket }: CreateAdminInput) => Admin;
|
|
89
92
|
//#endregion
|
package/dist/create-admin.js
CHANGED
|
@@ -51,6 +51,9 @@ import { toApiFunction } from "./functions/define-function.js";
|
|
|
51
51
|
import { createWebhooks } from "./webhooks/webhooks.js";
|
|
52
52
|
import { webhookTable } from "./webhooks/table/webhook-table.js";
|
|
53
53
|
import { subscriptionUrlCheck, subscriptionWriteCheck } from "./webhooks/table/validate-hook.js";
|
|
54
|
+
import { deviceStore, removeDevicesWithUsers } from "./notifications/device-store-of.js";
|
|
55
|
+
import { devicesPath } from "./notifications/paths.js";
|
|
56
|
+
import { deviceRoutes } from "./notifications/routes.js";
|
|
54
57
|
import { visitorsBrowse, withAnonymous } from "./anonymous.js";
|
|
55
58
|
import { checkBottomBars, checkShell } from "./check-shell.js";
|
|
56
59
|
import { checkOwnerFields } from "./check-owners.js";
|
|
@@ -94,7 +97,7 @@ const declaredRoles = (auth, roles) => {
|
|
|
94
97
|
return names.length > 0 ? [...new Set(names)] : void 0;
|
|
95
98
|
};
|
|
96
99
|
/** The admin REST API as a Hono app. It uses only Web APIs, so it runs on Node, Workers and anywhere else Hono does. */
|
|
97
|
-
const createAdmin = ({ resources: appResources, views: appViews = [], pages = [], userMenu, bottomBars = [], db, authenticate, auth, functions = {}, files: filesOptions, events, history: historyOptions, jobs = [], subscriptions = [], webhooks: webhooksOptions, mobile, app: identity, icons, bundlePlugins = [], fieldTypes = {}, options = {}, formsDifficulty, commentUsers: givenCommentUsers, verifyToken, upgradeWebSocket }) => {
|
|
100
|
+
const createAdmin = ({ resources: appResources, views: appViews = [], pages = [], userMenu, bottomBars = [], db, authenticate, auth, functions = {}, files: filesOptions, events, history: historyOptions, jobs = [], subscriptions = [], webhooks: webhooksOptions, notifications, mobile, app: identity, icons, bundlePlugins = [], fieldTypes = {}, options = {}, formsDifficulty, commentUsers: givenCommentUsers, verifyToken, upgradeWebSocket }) => {
|
|
98
101
|
const basePath = options.basePath ?? "/api/v1";
|
|
99
102
|
const appModels = appResources.map((resource) => resource.toModel());
|
|
100
103
|
const withComments = appModels.some((model) => model.comments);
|
|
@@ -361,6 +364,11 @@ const createAdmin = ({ resources: appResources, views: appViews = [], pages = []
|
|
|
361
364
|
}));
|
|
362
365
|
app.route("/", batchRoute(deps, authenticate, meta));
|
|
363
366
|
app.route("/", fileRoutes(deps, authenticate));
|
|
367
|
+
if (notifications) {
|
|
368
|
+
const devices = deviceStore(db, notifications);
|
|
369
|
+
if (auth) removeDevicesWithUsers(auth, devices);
|
|
370
|
+
app.route(devicesPath(systemPath), guarded((routes) => void routes.route("/", deviceRoutes(devices))));
|
|
371
|
+
}
|
|
364
372
|
if (forms) app.route(`${systemPath}/forms`, formRoutes(deps, forms));
|
|
365
373
|
const feed = tableFeed({
|
|
366
374
|
db,
|
package/dist/index.d.ts
CHANGED
|
@@ -18,6 +18,9 @@ import { Admin, AuditEvent, AuditQueue, Authenticator, PipelineHook, ScanGuardMo
|
|
|
18
18
|
import { CommentUsers } from "./comments/comment-users.js";
|
|
19
19
|
import { HistoryOptions } from "./history/options.js";
|
|
20
20
|
import { WebhooksOptions } from "./webhooks/table/options.js";
|
|
21
|
+
import { ApnsEnvironment, Device, DeviceKind, NewDevice } from "./notifications/device.js";
|
|
22
|
+
import { DeviceStore } from "./notifications/device-store.js";
|
|
23
|
+
import { DeviceColumn, NotificationsOptions } from "./notifications/options.js";
|
|
21
24
|
import { EventData, RunContext, SubscriptionDefinition, SubscriptionEvent, SubscriptionOptions, subscription } from "./webhooks/subscription.js";
|
|
22
25
|
import { Sharp, renderVariant, thumbHash } from "./files/images.js";
|
|
23
26
|
import { FileProvider, FileStore, StoredObject } from "./files/store.js";
|
|
@@ -86,6 +89,12 @@ import { checkWebhooksSchema, webhooksMigration } from "./webhooks/table/schema.
|
|
|
86
89
|
import { checkComments, commentsMigration } from "./comments/comments-sql.js";
|
|
87
90
|
import { CheckAiInput, checkAi } from "./ai/check-ai.js";
|
|
88
91
|
import { CommentItem } from "./comments/comment-item.js";
|
|
92
|
+
import { notifications } from "./notifications/plugin.js";
|
|
93
|
+
import { checkNotificationsSchema, notificationsMigration } from "./notifications/schema.js";
|
|
94
|
+
import { deviceStore, removeDevicesWithUsers } from "./notifications/device-store-of.js";
|
|
95
|
+
import { DeviceStoreError, postgresDeviceStore } from "./notifications/postgres-store.js";
|
|
96
|
+
import { deviceIdParam, devicesPath } from "./notifications/paths.js";
|
|
97
|
+
import { maxDevicesPerUser } from "./notifications/routes.js";
|
|
89
98
|
import { MemberInput, NewOrganization, addAppRoleToOwners, addMember, createOrganization, listOrganizations, removeMember, setMemberRoles } from "./better-auth/organizations/host.js";
|
|
90
99
|
import { AssistantBackend, assistantProtocolRoutes } from "./assistant/protocol-routes.js";
|
|
91
100
|
import { fileConversationStore } from "./assistant/file-conversation-store.js";
|
|
@@ -137,4 +146,4 @@ import { checkJobsSchema, jobsSchema, jobsSchemaMigration } from "./jobs/schema.
|
|
|
137
146
|
import { MobileRelease, ReleaseFile, ServedReleaseFile, composeRelease, releaseManifest } from "./mobile-release/release.js";
|
|
138
147
|
import { MobileReleaseAppInput, OfferedRelease, mobileReleaseApp } from "./mobile-release/release-app.js";
|
|
139
148
|
import { mobileSigningKeyVariable, readSigningKey } from "./mobile-release/signing-key.js";
|
|
140
|
-
export { type Admin, type AdminAuth, type AdminOptions, type ApiFunction, type ApiKeys, type ApiKeysOptions, type AppIcons, type AppIdentity, type AppPoolConfig, type ApplyOptions, type ApplyResult, type ApprovalAnswer, type ApprovalRequest, type AssistantBackend, type AssistantOptions, type AssistantSettings, type AssistantTool, type AuditEvent, type AuditQueue, type Authenticator, type BatchOperation, type BatchResult, type BetterAuthAuthenticator, type BetterAuthAuthenticatorOptions, type Caller, type CallerOptions, type CapturedRow, type CascadingKey, type ChatMessage, type ChatTextPart, type CheckAiInput, type CleanupEntry, type CleanupReport, type CleanupSchedule, type CoalescedChange, type CommentItem, type CommentUsers, type Completion, type CompletionOptions, ConnectionsBusy, type Conversation, type ConversationRecord, type ConversationStore, type ConversationsOptions, type CorsOptions, type CorsOrigins, type CreateAdminInput, type CreateAuthOptions, type DrainResult, type EmailComponent, type EmailOptions, type EmailTemplate, type EncryptionStatus, type EncryptionTools, type EncryptionToolsInput, type EnqueueOptions, type ErrorReporter, type ErrorReportingOptions, type ErrorReportingSettings, type EventData, type EventsOptions, FatalJobError, type FieldApplied, type FieldStatus, type FileCleanupInput, type FileLimits, type FileObject, type FileProvider, type FileRef, type FileStore, type FileVariantObject, type FilesOptions, type FunctionApp, type FunctionCaller, type FunctionContext, type FunctionEnv, type FunctionHandler, type FunctionOptions, type FunctionRecords, type FunctionSocket, type FunctionSource, type HistoryOptions, HttpProblem, type IssueTokenOptions, type JobContext, type JobDefinition, type JobOptions, type JobsRuntime, type JwtOptions, type JwtVerifyOptions, type KeyFunctionContext, type LinkedAccount, type ListParams, type LocalFilesOptions, type MailMessage, type Mailer, type MapLayer, type MapOptions, type MapSettings, type McpGateInput, type McpHandlerInput, type McpRoutesInput, type McpServerInput, type McpToolsInput, type McpWrites, type MemberInput, MissingCredentials, type MobileConfig, type MobileRelease, type MobileReleaseAppInput, ModelError, type ModelSettings, type NewOrganization, type NewUser, type OfferedRelease, type OperatorProvider, type OrganizationRole, type OrganizationsOptions, type PgModule, type PipelineHook, type PlatformSignIn, type PluginOption, type PluginRef, type PngIcon, type ProjectConfig, type ProtectedFunctionOptions, type PublicFunctionContext, type PublicFunctionEnv, type PublicFunctionHandler, type PublicFunctionOptions, type QueryOptions, type QueryResult, type ReadableResources, type ReasoningDetail, type ReasoningEffort, type RebindOption, type RecordKeyInput, type RecordPage, type ReleaseFile, type RenderedEmail, type ResourceChange, type ResourceSource, type RoleDefinition, type RolesInput, type RunContext, type RuntimeOptions, type S3Bucket, type S3FilesOptions, type S3ListPage, type S3Object, type ScanGuardMode, type ScheduledDelete, type ServedReleaseFile, type Session, type SessionOrganization, type Sharp, type SignInPolicy, type SignInRule, type SignatureOptions, type SocketEvents, type SourceOption, type SourceRef, type StaffAccessRule, type StaffSignIn, type StandardSchema, type StandardWebhookOptions, type StartJobsOptions, type StoreFileInput, type StoredMember, type StoredObject, type StoredOrganization, type StoredUser, type StrayValues, type SubscriptionDefinition, type SubscriptionEvent, type SubscriptionOptions, type TenantValue, type ToolAnnotations, type ToolCall, type ToolContext, type ToolRecord, type ToolRecords, type ToolSpec, type TrustedApp, type TurnOptions, type UnreadableEntry, type User, type UserCreatedHook, type ViewSource, type WebhooksOptions, type WidgetOptions, type WriteEvent, type WriteOperation, type WriteRecordToolsInput, addAppRoleToOwners, addMember, apiKeyCaller, apiKeySettings, apiKeyVariables, appPool, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaMigration, authStoreTables, backgroundSettled, badRequest, batchWriteTool, beforeSignInPluginsVariable, betterAuthAuthenticator, betterAuthTokens, capSocketMessages, changesMigration, checkAi, checkAuthSchema, checkChanges, checkComments, checkComputed, checkJobsSchema, checkQueryRunner, checkSourceNames, checkViewQueries, checkWebhooksSchema, coalesce, commentsMigration, composeRelease, computedMigration, configExports, consoleAuditQueue, conversationKey, createAdmin, createAuth, createConversations, createEncryptionTools, createFileCleanup, createOrganization, createRecordTool, createUser, defaultAppName, defaultSignInPolicy, defineConfig, defineEmail, defineFunction, defineJob, defineTool, deleteRecordTool, deleteUser, errorReportingSettings, errorReportingVariable, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, formatUri, functionApp, getRecordTool, goTruePath, handleDroppedConnections, hasUsers, honorSocketMessageCaps, imageSize, isBackgroundWork, isCaptured, isJobDefinition, isOAuthWellKnownPath, isSchedule, issueToken, jobDefinitions, jobsDrainApp, jobsSchema, jobsSchemaMigration, jwtAuthenticator, jwtVerifier, limitedFileStore, listOrganizations, listRecordsTool, listResourcesTool, listUsers, localFileStore, localFiles, longQueryPool, longQueryTimeoutMs, mapSettings, mapStyleUrl, mapStyleVariable, maxTokenTtlSeconds, maxWidgetPropsBytes, mcpGate, mcpHandler, mcpRoutes, mcpServer, mcpToolContext, mcpTools, mergeConfig, migrationCommand, mobileReleaseApp, mobileSigningKeyVariable, newWebhookSecret, normalizeRoles, notFound, oauthWellKnownPaths, objectKey, parseColumns, parseRoles, passwordLength, pluginOptions, pluginsBeforeSignIn, pluginsFor, pluginsVariable, pngIcons, poolWaitMs, processor, queryRunnerMigration, readDbConnections, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readRecordTools, readSigningKey, readWriteQueryTool, readableResources, realtimeLimits, recordOf, releaseManifest, removeMember, renderVariant, requestApproval, requireEnv, resourceChange, roleChoices, runInBackground, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, s3FileStore, s3Files, seesAssistant, seesRuntime, sessionOf, setMemberRoles, setUserBanned, setUserRole, shownAppName, signInPolicyMethods, socketMessageCapOf, sourceOptions, sourcesFor, sourcesVariable, startErrorReporting, startJobs, storageCleanupSchedule, storageProvider, storeFile, storeUsage, streamChatCompletion, subscription, supabasePath, thumbHash, unauthorized, updateRecordTool, variantPath, verifySignature, verifyStandardWebhook, waitUntil, wantsLongQueries, webManifestPath, webauthnPath, webhooks, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps, writeRecordTools };
|
|
149
|
+
export { type Admin, type AdminAuth, type AdminOptions, type ApiFunction, type ApiKeys, type ApiKeysOptions, type ApnsEnvironment, type AppIcons, type AppIdentity, type AppPoolConfig, type ApplyOptions, type ApplyResult, type ApprovalAnswer, type ApprovalRequest, type AssistantBackend, type AssistantOptions, type AssistantSettings, type AssistantTool, type AuditEvent, type AuditQueue, type Authenticator, type BatchOperation, type BatchResult, type BetterAuthAuthenticator, type BetterAuthAuthenticatorOptions, type Caller, type CallerOptions, type CapturedRow, type CascadingKey, type ChatMessage, type ChatTextPart, type CheckAiInput, type CleanupEntry, type CleanupReport, type CleanupSchedule, type CoalescedChange, type CommentItem, type CommentUsers, type Completion, type CompletionOptions, ConnectionsBusy, type Conversation, type ConversationRecord, type ConversationStore, type ConversationsOptions, type CorsOptions, type CorsOrigins, type CreateAdminInput, type CreateAuthOptions, type Device, type DeviceColumn, type DeviceKind, type DeviceStore, DeviceStoreError, type DrainResult, type EmailComponent, type EmailOptions, type EmailTemplate, type EncryptionStatus, type EncryptionTools, type EncryptionToolsInput, type EnqueueOptions, type ErrorReporter, type ErrorReportingOptions, type ErrorReportingSettings, type EventData, type EventsOptions, FatalJobError, type FieldApplied, type FieldStatus, type FileCleanupInput, type FileLimits, type FileObject, type FileProvider, type FileRef, type FileStore, type FileVariantObject, type FilesOptions, type FunctionApp, type FunctionCaller, type FunctionContext, type FunctionEnv, type FunctionHandler, type FunctionOptions, type FunctionRecords, type FunctionSocket, type FunctionSource, type HistoryOptions, HttpProblem, type IssueTokenOptions, type JobContext, type JobDefinition, type JobOptions, type JobsRuntime, type JwtOptions, type JwtVerifyOptions, type KeyFunctionContext, type LinkedAccount, type ListParams, type LocalFilesOptions, type MailMessage, type Mailer, type MapLayer, type MapOptions, type MapSettings, type McpGateInput, type McpHandlerInput, type McpRoutesInput, type McpServerInput, type McpToolsInput, type McpWrites, type MemberInput, MissingCredentials, type MobileConfig, type MobileRelease, type MobileReleaseAppInput, ModelError, type ModelSettings, type NewDevice, type NewOrganization, type NewUser, type NotificationsOptions, type OfferedRelease, type OperatorProvider, type OrganizationRole, type OrganizationsOptions, type PgModule, type PipelineHook, type PlatformSignIn, type PluginOption, type PluginRef, type PngIcon, type ProjectConfig, type ProtectedFunctionOptions, type PublicFunctionContext, type PublicFunctionEnv, type PublicFunctionHandler, type PublicFunctionOptions, type QueryOptions, type QueryResult, type ReadableResources, type ReasoningDetail, type ReasoningEffort, type RebindOption, type RecordKeyInput, type RecordPage, type ReleaseFile, type RenderedEmail, type ResourceChange, type ResourceSource, type RoleDefinition, type RolesInput, type RunContext, type RuntimeOptions, type S3Bucket, type S3FilesOptions, type S3ListPage, type S3Object, type ScanGuardMode, type ScheduledDelete, type ServedReleaseFile, type Session, type SessionOrganization, type Sharp, type SignInPolicy, type SignInRule, type SignatureOptions, type SocketEvents, type SourceOption, type SourceRef, type StaffAccessRule, type StaffSignIn, type StandardSchema, type StandardWebhookOptions, type StartJobsOptions, type StoreFileInput, type StoredMember, type StoredObject, type StoredOrganization, type StoredUser, type StrayValues, type SubscriptionDefinition, type SubscriptionEvent, type SubscriptionOptions, type TenantValue, type ToolAnnotations, type ToolCall, type ToolContext, type ToolRecord, type ToolRecords, type ToolSpec, type TrustedApp, type TurnOptions, type UnreadableEntry, type User, type UserCreatedHook, type ViewSource, type WebhooksOptions, type WidgetOptions, type WriteEvent, type WriteOperation, type WriteRecordToolsInput, addAppRoleToOwners, addMember, apiKeyCaller, apiKeySettings, apiKeyVariables, appPool, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaMigration, authStoreTables, backgroundSettled, badRequest, batchWriteTool, beforeSignInPluginsVariable, betterAuthAuthenticator, betterAuthTokens, capSocketMessages, changesMigration, checkAi, checkAuthSchema, checkChanges, checkComments, checkComputed, checkJobsSchema, checkNotificationsSchema, checkQueryRunner, checkSourceNames, checkViewQueries, checkWebhooksSchema, coalesce, commentsMigration, composeRelease, computedMigration, configExports, consoleAuditQueue, conversationKey, createAdmin, createAuth, createConversations, createEncryptionTools, createFileCleanup, createOrganization, createRecordTool, createUser, defaultAppName, defaultSignInPolicy, defineConfig, defineEmail, defineFunction, defineJob, defineTool, deleteRecordTool, deleteUser, deviceIdParam, deviceStore, devicesPath, errorReportingSettings, errorReportingVariable, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, formatUri, functionApp, getRecordTool, goTruePath, handleDroppedConnections, hasUsers, honorSocketMessageCaps, imageSize, isBackgroundWork, isCaptured, isJobDefinition, isOAuthWellKnownPath, isSchedule, issueToken, jobDefinitions, jobsDrainApp, jobsSchema, jobsSchemaMigration, jwtAuthenticator, jwtVerifier, limitedFileStore, listOrganizations, listRecordsTool, listResourcesTool, listUsers, localFileStore, localFiles, longQueryPool, longQueryTimeoutMs, mapSettings, mapStyleUrl, mapStyleVariable, maxDevicesPerUser, maxTokenTtlSeconds, maxWidgetPropsBytes, mcpGate, mcpHandler, mcpRoutes, mcpServer, mcpToolContext, mcpTools, mergeConfig, migrationCommand, mobileReleaseApp, mobileSigningKeyVariable, newWebhookSecret, normalizeRoles, notFound, notifications, notificationsMigration, oauthWellKnownPaths, objectKey, parseColumns, parseRoles, passwordLength, pluginOptions, pluginsBeforeSignIn, pluginsFor, pluginsVariable, pngIcons, poolWaitMs, postgresDeviceStore, processor, queryRunnerMigration, readDbConnections, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readRecordTools, readSigningKey, readWriteQueryTool, readableResources, realtimeLimits, recordOf, releaseManifest, removeDevicesWithUsers, removeMember, renderVariant, requestApproval, requireEnv, resourceChange, roleChoices, runInBackground, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, s3FileStore, s3Files, seesAssistant, seesRuntime, sessionOf, setMemberRoles, setUserBanned, setUserRole, shownAppName, signInPolicyMethods, socketMessageCapOf, sourceOptions, sourcesFor, sourcesVariable, startErrorReporting, startJobs, storageCleanupSchedule, storageProvider, storeFile, storeUsage, streamChatCompletion, subscription, supabasePath, thumbHash, unauthorized, updateRecordTool, variantPath, verifySignature, verifyStandardWebhook, waitUntil, wantsLongQueries, webManifestPath, webauthnPath, webhooks, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps, writeRecordTools };
|
package/dist/index.js
CHANGED
|
@@ -56,6 +56,10 @@ import { recordOf, resourceChange } from "./changes/resource-change.js";
|
|
|
56
56
|
import { defineFunction } from "./functions/define-function.js";
|
|
57
57
|
import { newWebhookSecret } from "./webhooks/standard-webhooks.js";
|
|
58
58
|
import { subscription } from "./webhooks/subscription.js";
|
|
59
|
+
import { DeviceStoreError, postgresDeviceStore } from "./notifications/postgres-store.js";
|
|
60
|
+
import { deviceStore, removeDevicesWithUsers } from "./notifications/device-store-of.js";
|
|
61
|
+
import { deviceIdParam, devicesPath } from "./notifications/paths.js";
|
|
62
|
+
import { maxDevicesPerUser } from "./notifications/routes.js";
|
|
59
63
|
import { errorReportingSettings, errorReportingVariable } from "./error-reporting/error-reporting-settings.js";
|
|
60
64
|
import { storageCleanupSchedule } from "./files/cleanup-schedule.js";
|
|
61
65
|
import { limitedFileStore, storeUsage } from "./files/limits.js";
|
|
@@ -86,6 +90,8 @@ import { checkComputed, computedMigration } from "./computed/computed-schema.js"
|
|
|
86
90
|
import { webhooks } from "./webhooks/table/plugin.js";
|
|
87
91
|
import { checkWebhooksSchema, webhooksMigration } from "./webhooks/table/schema.js";
|
|
88
92
|
import { checkAi } from "./ai/check-ai.js";
|
|
93
|
+
import { notifications } from "./notifications/plugin.js";
|
|
94
|
+
import { checkNotificationsSchema, notificationsMigration } from "./notifications/schema.js";
|
|
89
95
|
import { maxWidgetPropsBytes, widget } from "./assistant/widget.js";
|
|
90
96
|
import { checkQueryRunner, queryRunnerMigration } from "./assistant/query-runner/query-runner.js";
|
|
91
97
|
import { startErrorReporting } from "./error-reporting/error-reporter.js";
|
|
@@ -104,4 +110,4 @@ import { jobsDrainApp } from "./jobs/drain-route.js";
|
|
|
104
110
|
import { composeRelease, releaseManifest } from "./mobile-release/release.js";
|
|
105
111
|
import { mobileReleaseApp } from "./mobile-release/release-app.js";
|
|
106
112
|
import { mobileSigningKeyVariable, readSigningKey } from "./mobile-release/signing-key.js";
|
|
107
|
-
export { ConnectionsBusy, FatalJobError, HttpProblem, MissingCredentials, ModelError, addAppRoleToOwners, addMember, apiKeyCaller, apiKeySettings, apiKeyVariables, appPool, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaMigration, authStoreTables, backgroundSettled, badRequest, batchWriteTool, beforeSignInPluginsVariable, betterAuthAuthenticator, betterAuthTokens, capSocketMessages, changesMigration, checkAi, checkAuthSchema, checkChanges, checkComments, checkComputed, checkJobsSchema, checkQueryRunner, checkSourceNames, checkViewQueries, checkWebhooksSchema, coalesce, commentsMigration, composeRelease, computedMigration, configExports, consoleAuditQueue, conversationKey, createAdmin, createAuth, createConversations, createEncryptionTools, createFileCleanup, createOrganization, createRecordTool, createUser, defaultAppName, defaultSignInPolicy, defineConfig, defineEmail, defineFunction, defineJob, defineTool, deleteRecordTool, deleteUser, errorReportingSettings, errorReportingVariable, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, formatUri, functionApp, getRecordTool, goTruePath, handleDroppedConnections, hasUsers, honorSocketMessageCaps, imageSize, isBackgroundWork, isCaptured, isJobDefinition, isOAuthWellKnownPath, isSchedule, issueToken, jobDefinitions, jobsDrainApp, jobsSchema, jobsSchemaMigration, jwtAuthenticator, jwtVerifier, limitedFileStore, listOrganizations, listRecordsTool, listResourcesTool, listUsers, localFileStore, localFiles, longQueryPool, longQueryTimeoutMs, mapSettings, mapStyleUrl, mapStyleVariable, maxTokenTtlSeconds, maxWidgetPropsBytes, mcpGate, mcpHandler, mcpRoutes, mcpServer, mcpToolContext, mcpTools, mergeConfig, migrationCommand, mobileReleaseApp, mobileSigningKeyVariable, newWebhookSecret, normalizeRoles, notFound, oauthWellKnownPaths, objectKey, parseColumns, parseRoles, passwordLength, pluginOptions, pluginsBeforeSignIn, pluginsFor, pluginsVariable, pngIcons, poolWaitMs, processor, queryRunnerMigration, readDbConnections, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readRecordTools, readSigningKey, readWriteQueryTool, readableResources, realtimeLimits, recordOf, releaseManifest, removeMember, renderVariant, requestApproval, requireEnv, resourceChange, roleChoices, runInBackground, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, s3FileStore, s3Files, seesAssistant, seesRuntime, sessionOf, setMemberRoles, setUserBanned, setUserRole, shownAppName, signInPolicyMethods, socketMessageCapOf, sourceOptions, sourcesFor, sourcesVariable, startErrorReporting, startJobs, storageCleanupSchedule, storageProvider, storeFile, storeUsage, streamChatCompletion, subscription, supabasePath, thumbHash, unauthorized, updateRecordTool, variantPath, verifySignature, verifyStandardWebhook, waitUntil, wantsLongQueries, webManifestPath, webauthnPath, webhooks, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps, writeRecordTools };
|
|
113
|
+
export { ConnectionsBusy, DeviceStoreError, FatalJobError, HttpProblem, MissingCredentials, ModelError, addAppRoleToOwners, addMember, apiKeyCaller, apiKeySettings, apiKeyVariables, appPool, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaMigration, authStoreTables, backgroundSettled, badRequest, batchWriteTool, beforeSignInPluginsVariable, betterAuthAuthenticator, betterAuthTokens, capSocketMessages, changesMigration, checkAi, checkAuthSchema, checkChanges, checkComments, checkComputed, checkJobsSchema, checkNotificationsSchema, checkQueryRunner, checkSourceNames, checkViewQueries, checkWebhooksSchema, coalesce, commentsMigration, composeRelease, computedMigration, configExports, consoleAuditQueue, conversationKey, createAdmin, createAuth, createConversations, createEncryptionTools, createFileCleanup, createOrganization, createRecordTool, createUser, defaultAppName, defaultSignInPolicy, defineConfig, defineEmail, defineFunction, defineJob, defineTool, deleteRecordTool, deleteUser, deviceIdParam, deviceStore, devicesPath, errorReportingSettings, errorReportingVariable, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, formatUri, functionApp, getRecordTool, goTruePath, handleDroppedConnections, hasUsers, honorSocketMessageCaps, imageSize, isBackgroundWork, isCaptured, isJobDefinition, isOAuthWellKnownPath, isSchedule, issueToken, jobDefinitions, jobsDrainApp, jobsSchema, jobsSchemaMigration, jwtAuthenticator, jwtVerifier, limitedFileStore, listOrganizations, listRecordsTool, listResourcesTool, listUsers, localFileStore, localFiles, longQueryPool, longQueryTimeoutMs, mapSettings, mapStyleUrl, mapStyleVariable, maxDevicesPerUser, maxTokenTtlSeconds, maxWidgetPropsBytes, mcpGate, mcpHandler, mcpRoutes, mcpServer, mcpToolContext, mcpTools, mergeConfig, migrationCommand, mobileReleaseApp, mobileSigningKeyVariable, newWebhookSecret, normalizeRoles, notFound, notifications, notificationsMigration, oauthWellKnownPaths, objectKey, parseColumns, parseRoles, passwordLength, pluginOptions, pluginsBeforeSignIn, pluginsFor, pluginsVariable, pngIcons, poolWaitMs, postgresDeviceStore, processor, queryRunnerMigration, readDbConnections, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readRecordTools, readSigningKey, readWriteQueryTool, readableResources, realtimeLimits, recordOf, releaseManifest, removeDevicesWithUsers, removeMember, renderVariant, requestApproval, requireEnv, resourceChange, roleChoices, runInBackground, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, s3FileStore, s3Files, seesAssistant, seesRuntime, sessionOf, setMemberRoles, setUserBanned, setUserRole, shownAppName, signInPolicyMethods, socketMessageCapOf, sourceOptions, sourcesFor, sourcesVariable, startErrorReporting, startJobs, storageCleanupSchedule, storageProvider, storeFile, storeUsage, streamChatCompletion, subscription, supabasePath, thumbHash, unauthorized, updateRecordTool, variantPath, verifySignature, verifyStandardWebhook, waitUntil, wantsLongQueries, webManifestPath, webauthnPath, webhooks, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps, writeRecordTools };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { forbidden } from "../problem.js";
|
|
2
|
+
//#region src/notifications/caller.ts
|
|
3
|
+
/**
|
|
4
|
+
* The id of the user whose devices a request may register and remove: only someone signed in as themselves. Refused:
|
|
5
|
+
* visitors, outside apps acting for the user (an MCP client), and every session whose authenticator does not mark it as
|
|
6
|
+
* the user's own sign-in (`Session.signIn`), staff signed in as the user included.
|
|
7
|
+
*/
|
|
8
|
+
const ownDeviceUser = (session) => {
|
|
9
|
+
const refuse = () => forbidden("own-sign-in-only", "Only someone signed in as themselves can change their devices");
|
|
10
|
+
if (session.anonymous || session.client || session.signIn?.staff !== false) throw refuse();
|
|
11
|
+
const userId = String(session.user.id);
|
|
12
|
+
if (userId === "" || session.user.id === void 0 || session.user.id === null) throw refuse();
|
|
13
|
+
return userId;
|
|
14
|
+
};
|
|
15
|
+
//#endregion
|
|
16
|
+
export { ownDeviceUser };
|