@protobase/server 0.62.4 → 0.63.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/better-auth/auth-settings.d.ts +4 -1
- package/dist/better-auth/create-auth.d.ts +14 -2
- package/dist/better-auth/create-auth.js +8 -0
- package/dist/better-auth/organizations/organization-role.js +13 -0
- package/dist/better-auth/status-route.js +8 -1
- package/dist/calls/call-hub.js +28 -7
- package/dist/calls/call-member.d.ts +5 -1
- package/dist/calls/call-reads.d.ts +7 -2
- package/dist/calls/call-reads.js +6 -4
- package/dist/calls/call-store.d.ts +7 -0
- package/dist/calls/call-store.js +35 -24
- package/dist/calls/call-sweep.js +8 -6
- package/dist/calls/calls-access.js +7 -2
- package/dist/calls/calls-runtime.d.ts +26 -5
- package/dist/calls/calls-runtime.js +84 -19
- package/dist/calls/calls-service.js +4 -2
- package/dist/calls/calls-sql.d.ts +1 -1
- package/dist/calls/calls-sql.js +56 -5
- package/dist/calls/calls-store.js +2 -1
- package/dist/calls/client-message.js +27 -2
- package/dist/calls/guest-checks-sql.d.ts +14 -0
- package/dist/calls/guest-checks-sql.js +43 -0
- package/dist/calls/guest-limits.js +49 -0
- package/dist/calls/guest-links-sql.d.ts +16 -0
- package/dist/calls/guest-links-sql.js +78 -0
- package/dist/calls/guest-lobby-sql.d.ts +17 -0
- package/dist/calls/guest-lobby-sql.js +81 -0
- package/dist/calls/guest-name.js +10 -0
- package/dist/calls/guest-routes.js +97 -0
- package/dist/calls/guest-socket.js +58 -0
- package/dist/calls/guest-store.d.ts +48 -0
- package/dist/calls/guest-store.js +20 -0
- package/dist/calls/guest-tokens.js +17 -0
- package/dist/calls/member-relay.js +63 -5
- package/dist/calls/organization-guests-route.js +38 -0
- package/dist/calls/record-organization.js +24 -0
- package/dist/create-admin.js +23 -2
- package/dist/functions/define-function.d.ts +10 -0
- package/dist/functions/own-mailbox.js +9 -0
- package/dist/functions/routes.js +5 -2
- package/dist/functions/upgrade-caller.js +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +3 -2
- package/dist/jobs/job-types.d.ts +1 -1
- package/dist/jobs/runtime.d.ts +1 -1
- package/dist/mail/composer-message.js +27 -0
- package/dist/mail/define-email.d.ts +7 -4
- package/dist/mail/define-email.js +3 -2
- package/dist/mail/{smtp-mailer.d.ts → mail-message.d.ts} +9 -1
- package/dist/mail/smtp-mailer.js +2 -8
- package/dist/package.js +1 -1
- package/dist/personal-mail/gmail.js +43 -0
- package/dist/personal-mail/linked-accounts.js +36 -0
- package/dist/personal-mail/personal-mail-error.d.ts +22 -0
- package/dist/personal-mail/personal-mail-error.js +25 -0
- package/dist/personal-mail/personal-mail-provider.d.ts +25 -0
- package/dist/personal-mail/personal-mail-providers.d.ts +8 -0
- package/dist/personal-mail/personal-mail-providers.js +16 -0
- package/dist/personal-mail/personal-mailer.js +26 -0
- package/dist/personal-mail/rfc822-message.js +15 -0
- package/package.json +4 -4
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { HttpProblem, forbidden, notFound, unauthorized } from "../problem.js";
|
|
2
|
+
import { socketAcceptor } from "../functions/accept-socket.js";
|
|
3
|
+
import { allowedOrigin } from "../functions/upgrade-caller.js";
|
|
4
|
+
import { guestLimits } from "./guest-limits.js";
|
|
5
|
+
import { guestName } from "./guest-name.js";
|
|
6
|
+
import { guestTokenPrefix, linkTokenPrefix, newToken, tokenHash } from "./guest-tokens.js";
|
|
7
|
+
import { guestSocket } from "./guest-socket.js";
|
|
8
|
+
import { Hono } from "hono";
|
|
9
|
+
//#region src/calls/guest-routes.ts
|
|
10
|
+
const noStore = { "Cache-Control": "no-store" };
|
|
11
|
+
const tooMany = () => new HttpProblem(429, "too-many-requests", "Too Many Requests", "Too many tries: wait a minute", {}, { "retry-after": "60" });
|
|
12
|
+
const noLobby = () => notFound("This link lets nobody into a call");
|
|
13
|
+
const bearer = (c) => c.req.header("authorization")?.match(/^Bearer (.+)$/)?.[1];
|
|
14
|
+
/**
|
|
15
|
+
* The guests' routes, mounted at `guestCallsPath` before the API's authenticator, which takes none of their tokens,
|
|
16
|
+
* as no route but these takes them:
|
|
17
|
+
* - `POST /lobby` with `{ link, name }`: the guest waits in the lobby of the link's call; `201 { guest, token }`.
|
|
18
|
+
* - `GET /lobby` with `Authorization: Bearer <token>`: `{ state }`, with `call` and `publish` once let in.
|
|
19
|
+
* - `DELETE /lobby`: the waiting guest leaves.
|
|
20
|
+
* - `GET /socket`: the guest's call socket (see `guestSocket`).
|
|
21
|
+
* Each address may enter `entriesPerMinute` times a minute, and is answered 429 for the rest of the minute after
|
|
22
|
+
* `refusalsPerMinute` unknown or refused tokens; each guest asks after their wait `pollsPerMinute` times at most.
|
|
23
|
+
*/
|
|
24
|
+
const guestRoutes = ({ calls, addressOf, origins, upgradeWebSocket, onError, report }) => {
|
|
25
|
+
const app = new Hono();
|
|
26
|
+
app.onError(onError);
|
|
27
|
+
const limits = guestLimits();
|
|
28
|
+
const address = (c) => addressOf(c.req.raw) ?? "no-trusted-address";
|
|
29
|
+
const open = (c) => {
|
|
30
|
+
const at = address(c);
|
|
31
|
+
if (limits.blocked(at)) throw tooMany();
|
|
32
|
+
return at;
|
|
33
|
+
};
|
|
34
|
+
const guestToken = (c, at) => {
|
|
35
|
+
const hash = tokenHash(guestTokenPrefix, bearer(c));
|
|
36
|
+
if (!hash) {
|
|
37
|
+
limits.refused(at);
|
|
38
|
+
throw unauthorized("Send the token the lobby gave you");
|
|
39
|
+
}
|
|
40
|
+
if (!limits.poll(hash.toString("hex"))) throw tooMany();
|
|
41
|
+
return hash;
|
|
42
|
+
};
|
|
43
|
+
app.post("/lobby", async (c) => {
|
|
44
|
+
const at = open(c);
|
|
45
|
+
const body = await c.req.json().catch(() => void 0);
|
|
46
|
+
if (!limits.enter(at)) throw tooMany();
|
|
47
|
+
const name = guestName(body?.name);
|
|
48
|
+
const link = tokenHash(linkTokenPrefix, body?.link);
|
|
49
|
+
if (!link) {
|
|
50
|
+
limits.refused(at);
|
|
51
|
+
throw noLobby();
|
|
52
|
+
}
|
|
53
|
+
const token = newToken(guestTokenPrefix);
|
|
54
|
+
const entered = await (await calls.runtime()).guests().enter({
|
|
55
|
+
link,
|
|
56
|
+
name,
|
|
57
|
+
token: tokenHash(guestTokenPrefix, token)
|
|
58
|
+
});
|
|
59
|
+
if ("refused" in entered) {
|
|
60
|
+
limits.refused(at);
|
|
61
|
+
throw noLobby();
|
|
62
|
+
}
|
|
63
|
+
return c.json({
|
|
64
|
+
guest: entered.guest,
|
|
65
|
+
token
|
|
66
|
+
}, 201, noStore);
|
|
67
|
+
});
|
|
68
|
+
app.get("/lobby", async (c) => {
|
|
69
|
+
const at = open(c);
|
|
70
|
+
const hash = guestToken(c, at);
|
|
71
|
+
const state = await (await calls.runtime()).guests().state(hash);
|
|
72
|
+
if (!state) {
|
|
73
|
+
limits.refused(at);
|
|
74
|
+
throw unauthorized("This token lets nobody into a call");
|
|
75
|
+
}
|
|
76
|
+
return c.json(state, 200, noStore);
|
|
77
|
+
});
|
|
78
|
+
app.delete("/lobby", async (c) => {
|
|
79
|
+
const at = open(c);
|
|
80
|
+
await (await calls.runtime()).guests().leave(guestToken(c, at));
|
|
81
|
+
return c.body(null, 204);
|
|
82
|
+
});
|
|
83
|
+
app.get("/socket", async (c) => {
|
|
84
|
+
const at = open(c);
|
|
85
|
+
if (!allowedOrigin(c.req.raw, origins)) throw forbidden("foreign-origin", "This page’s origin may not open a socket to this app");
|
|
86
|
+
const runtime = await calls.runtime();
|
|
87
|
+
const acceptor = socketAcceptor({
|
|
88
|
+
c,
|
|
89
|
+
upgradeWebSocket,
|
|
90
|
+
report
|
|
91
|
+
});
|
|
92
|
+
return acceptor.answer("guest call", acceptor.accept(guestSocket(runtime, () => limits.refused(at))));
|
|
93
|
+
});
|
|
94
|
+
return app;
|
|
95
|
+
};
|
|
96
|
+
//#endregion
|
|
97
|
+
export { guestRoutes };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { forbidden } from "../problem.js";
|
|
2
|
+
import { parseClientMessage } from "./client-message.js";
|
|
3
|
+
import { guestTokenPrefix, tokenHash } from "./guest-tokens.js";
|
|
4
|
+
//#region src/calls/guest-socket.ts
|
|
5
|
+
/** How long a guest's socket may stay open before its first message names the guest, in milliseconds. */
|
|
6
|
+
const guestFirstMessageMs = 1e4;
|
|
7
|
+
const refusedCloseCode = 1008;
|
|
8
|
+
const maxMessageBytes = 307200;
|
|
9
|
+
/**
|
|
10
|
+
* A guest's call socket: open to anyone, it lets in only a guest whose first message, their `join` or `resume`, carries
|
|
11
|
+
* the token of a guest a member let in who may still be in the call. From then on it is a member's socket, as `guest:<id>` in their link's call,
|
|
12
|
+
* checked every minute: removed, their link revoked or expired, guests turned off or the call ended closes it. A
|
|
13
|
+
* socket that names no such guest, or none in time, closes with 1008 and `refused` (the address is counted).
|
|
14
|
+
*/
|
|
15
|
+
const guestSocket = (runtime, refused) => {
|
|
16
|
+
let socket;
|
|
17
|
+
let relay;
|
|
18
|
+
let guest;
|
|
19
|
+
let timer;
|
|
20
|
+
const refuse = () => {
|
|
21
|
+
refused();
|
|
22
|
+
socket.send(JSON.stringify({
|
|
23
|
+
type: "left",
|
|
24
|
+
reason: "refused"
|
|
25
|
+
}));
|
|
26
|
+
socket.close(refusedCloseCode, "Not let into this call");
|
|
27
|
+
};
|
|
28
|
+
const first = async (data) => {
|
|
29
|
+
clearTimeout(timer);
|
|
30
|
+
const message = parseClientMessage(data);
|
|
31
|
+
const hash = message && (message.type === "join" || message.type === "resume") ? tokenHash(guestTokenPrefix, message.guest) : void 0;
|
|
32
|
+
const row = hash && await runtime.guests().byToken(hash);
|
|
33
|
+
if (!row || row.admitted !== true || !await runtime.guests().stillAllowed(row.id)) return refuse();
|
|
34
|
+
guest = row.id;
|
|
35
|
+
relay = await runtime.relayGuest(row);
|
|
36
|
+
await relay.onOpen?.(socket);
|
|
37
|
+
await relay.onMessage?.(data, socket);
|
|
38
|
+
};
|
|
39
|
+
return {
|
|
40
|
+
maxMessageBytes,
|
|
41
|
+
onOpen: (opened) => {
|
|
42
|
+
socket = opened;
|
|
43
|
+
timer = setTimeout(refuse, guestFirstMessageMs);
|
|
44
|
+
timer.unref?.();
|
|
45
|
+
},
|
|
46
|
+
onMessage: (data) => relay ? relay.onMessage?.(data, socket) : first(data),
|
|
47
|
+
onClose: (code, reason) => {
|
|
48
|
+
clearTimeout(timer);
|
|
49
|
+
return relay?.onClose?.(code, reason);
|
|
50
|
+
},
|
|
51
|
+
recheck: async () => {
|
|
52
|
+
if (guest === void 0) return;
|
|
53
|
+
if (!await runtime.guests().stillAllowed(guest)) throw forbidden("guest-not-allowed", "This guest may no longer be in the call");
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
//#endregion
|
|
58
|
+
export { guestFirstMessageMs, guestSocket };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { GuestRow } from "./guest-checks-sql.js";
|
|
2
|
+
import { CreateLinkInput } from "./guest-links-sql.js";
|
|
3
|
+
import { DecideInput, LobbyState } from "./guest-lobby-sql.js";
|
|
4
|
+
import { CallGuestLink, CallGuestRefusal, CallLobbyGuest } from "@protobase/schema/internal";
|
|
5
|
+
import { Db } from "@protobase/query";
|
|
6
|
+
//#region src/calls/guest-store.d.ts
|
|
7
|
+
/** Where guest links, the lobby's guests and the organizations' switch are kept: the `calls` schema. */
|
|
8
|
+
export type GuestStore = {
|
|
9
|
+
createLink: (input: CreateLinkInput) => Promise<{
|
|
10
|
+
link: CallGuestLink;
|
|
11
|
+
token: string;
|
|
12
|
+
} | {
|
|
13
|
+
refused: CallGuestRefusal;
|
|
14
|
+
}>;
|
|
15
|
+
revokeLink: (input: {
|
|
16
|
+
call: string;
|
|
17
|
+
link: string;
|
|
18
|
+
member: string;
|
|
19
|
+
}) => Promise<boolean>;
|
|
20
|
+
decide: (input: DecideInput) => Promise<{
|
|
21
|
+
refused?: CallGuestRefusal;
|
|
22
|
+
}>;
|
|
23
|
+
lobbies: (calls: readonly string[]) => Promise<Map<string, {
|
|
24
|
+
guests: CallLobbyGuest[];
|
|
25
|
+
links: CallGuestLink[];
|
|
26
|
+
}>>;
|
|
27
|
+
enter: (input: {
|
|
28
|
+
link: Buffer;
|
|
29
|
+
name: string;
|
|
30
|
+
token: Buffer;
|
|
31
|
+
}) => Promise<{
|
|
32
|
+
guest: string;
|
|
33
|
+
} | {
|
|
34
|
+
refused: 'gone';
|
|
35
|
+
}>;
|
|
36
|
+
state: (token: Buffer) => Promise<LobbyState | undefined>;
|
|
37
|
+
leave: (token: Buffer) => Promise<void>;
|
|
38
|
+
byToken: (token: Buffer) => Promise<GuestRow | undefined>;
|
|
39
|
+
stillAllowed: (guest: string) => Promise<boolean>;
|
|
40
|
+
/** The calls whose guests turning guests off took out. */
|
|
41
|
+
setOrganization: (input: {
|
|
42
|
+
organization: string;
|
|
43
|
+
allowed: boolean;
|
|
44
|
+
by: string;
|
|
45
|
+
}) => Promise<string[]>;
|
|
46
|
+
organizationAllowed: (organization: string) => Promise<boolean>;
|
|
47
|
+
};
|
|
48
|
+
//#endregion
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { guestByToken, guestStillAllowed } from "./guest-checks-sql.js";
|
|
2
|
+
import { createLink, organizationGuests, revokeLink, setOrganizationGuests } from "./guest-links-sql.js";
|
|
3
|
+
import { decideGuest, enterLobby, leaveLobby, lobbies, lobbyState } from "./guest-lobby-sql.js";
|
|
4
|
+
//#region src/calls/guest-store.ts
|
|
5
|
+
/** The `calls` schema of the app's database; writes under the API's statement timeout. */
|
|
6
|
+
const sqlGuestStore = (db, timeoutMs, organizationOf) => ({
|
|
7
|
+
createLink: (input) => createLink(db, timeoutMs, organizationOf, input),
|
|
8
|
+
revokeLink: (input) => revokeLink(db, timeoutMs, input),
|
|
9
|
+
decide: (input) => decideGuest(db, timeoutMs, input),
|
|
10
|
+
lobbies: (calls) => lobbies(db, calls),
|
|
11
|
+
enter: (input) => enterLobby(db, timeoutMs, input),
|
|
12
|
+
state: (token) => lobbyState(db, token),
|
|
13
|
+
leave: (token) => leaveLobby(db, token),
|
|
14
|
+
byToken: (token) => guestByToken(db, token),
|
|
15
|
+
stillAllowed: (guest) => guestStillAllowed(db, guest),
|
|
16
|
+
setOrganization: (input) => setOrganizationGuests(db, timeoutMs, input),
|
|
17
|
+
organizationAllowed: (organization) => organizationGuests(db, organization)
|
|
18
|
+
});
|
|
19
|
+
//#endregion
|
|
20
|
+
export { sqlGuestStore };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
2
|
+
//#region src/calls/guest-tokens.ts
|
|
3
|
+
/** What starts a guest link's token, and a guest's: neither is ever a session token, a JWT or an API key. */
|
|
4
|
+
const linkTokenPrefix = "pbgl_";
|
|
5
|
+
const guestTokenPrefix = "pbg_";
|
|
6
|
+
/** A new token, 32 random bytes in base64url after its prefix. */
|
|
7
|
+
const newToken = (prefix) => `${prefix}${randomBytes(32).toString("base64url")}`;
|
|
8
|
+
/** The SHA-256 a token is stored and looked up by; `undefined` for a value that is no token of this kind. */
|
|
9
|
+
const tokenHash = (prefix, token) => {
|
|
10
|
+
if (typeof token !== "string" || !token.startsWith(prefix) || token.length !== prefix.length + 43) return;
|
|
11
|
+
return createHash("sha256").update(token).digest();
|
|
12
|
+
};
|
|
13
|
+
/** The member a guest is in their call: no `calls.join` takes a member of this form. */
|
|
14
|
+
const guestMember = (guest) => `guest:${guest}`;
|
|
15
|
+
const isGuestMember = (member) => member.startsWith("guest:");
|
|
16
|
+
//#endregion
|
|
17
|
+
export { guestMember, guestTokenPrefix, isGuestMember, linkTokenPrefix, newToken, tokenHash };
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { memberEnd } from "./close-codes.js";
|
|
2
1
|
import { parseClientMessage } from "./client-message.js";
|
|
2
|
+
import { memberEnd } from "./close-codes.js";
|
|
3
3
|
//#region src/calls/member-relay.ts
|
|
4
4
|
/** The close code of a socket whose caller Protobase refused: the member leaves the call too. */
|
|
5
5
|
const refusedCloseCode = 1008;
|
|
6
6
|
const mayPublish = (rights, tracks) => rights.publish === true || Array.isArray(rights.publish) && tracks.every((track) => rights.publish.includes(track.source));
|
|
7
|
+
const guestSources = (rights, asked) => [...new Set(asked)].filter((source) => rights.publish === true || Array.isArray(rights.publish) && rights.publish.includes(source));
|
|
8
|
+
const isGuestMessage = (message) => message.type.startsWith("guest-");
|
|
7
9
|
const refusals = {
|
|
8
10
|
removed: "You were removed from this call",
|
|
9
11
|
full: "This call is full",
|
|
@@ -15,7 +17,7 @@ const refusals = {
|
|
|
15
17
|
* may not make, and tells the call and the plugin how the socket ended. The plugin only ever sees well-formed messages
|
|
16
18
|
* of a member Protobase let in.
|
|
17
19
|
*/
|
|
18
|
-
const memberRelay = ({ door, backend, record, member, rights, maxMembers, maxMinutes, report, user, ring }) => {
|
|
20
|
+
const memberRelay = ({ door, backend, record, member, rights, maxMembers, maxMinutes, report, user, ring, invites = false, name, guest }) => {
|
|
19
21
|
let browser;
|
|
20
22
|
let session;
|
|
21
23
|
let inCall;
|
|
@@ -49,14 +51,20 @@ const memberRelay = ({ door, backend, record, member, rights, maxMembers, maxMin
|
|
|
49
51
|
muted: false
|
|
50
52
|
}))
|
|
51
53
|
} : void 0;
|
|
54
|
+
const more = {
|
|
55
|
+
...user !== void 0 && { user },
|
|
56
|
+
...ring && !resume && { ring },
|
|
57
|
+
...invites && { invites },
|
|
58
|
+
...name !== void 0 && { name },
|
|
59
|
+
...guest && { guest }
|
|
60
|
+
};
|
|
52
61
|
const admitted = await door.admit({
|
|
53
62
|
record,
|
|
54
63
|
member,
|
|
55
64
|
maxMembers,
|
|
56
65
|
maxMinutes,
|
|
57
66
|
...resume && { resume },
|
|
58
|
-
...
|
|
59
|
-
...ring && !resume && { ring }
|
|
67
|
+
...more
|
|
60
68
|
}, {
|
|
61
69
|
send,
|
|
62
70
|
close
|
|
@@ -80,9 +88,59 @@ const memberRelay = ({ door, backend, record, member, rights, maxMembers, maxMin
|
|
|
80
88
|
await session.receive(message);
|
|
81
89
|
await admitted.listen(session.roster ?? (() => {}));
|
|
82
90
|
};
|
|
91
|
+
const guests = async (message, call) => {
|
|
92
|
+
const actions = door.guests;
|
|
93
|
+
if (!invites || !actions) return send({
|
|
94
|
+
type: "guest-refused",
|
|
95
|
+
reason: "not-allowed"
|
|
96
|
+
});
|
|
97
|
+
switch (message.type) {
|
|
98
|
+
case "guest-link": {
|
|
99
|
+
const made = await actions.link({
|
|
100
|
+
call,
|
|
101
|
+
member,
|
|
102
|
+
publish: guestSources(rights, message.publish),
|
|
103
|
+
...message.expires_at && { expiresAt: new Date(message.expires_at) }
|
|
104
|
+
});
|
|
105
|
+
if ("refused" in made) return send({
|
|
106
|
+
type: "guest-refused",
|
|
107
|
+
reason: made.refused
|
|
108
|
+
});
|
|
109
|
+
return send({
|
|
110
|
+
type: "guest-link",
|
|
111
|
+
link: made.link,
|
|
112
|
+
token: made.token
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
case "guest-link-revoke": return send(await actions.revoke({
|
|
116
|
+
call,
|
|
117
|
+
link: message.link,
|
|
118
|
+
member
|
|
119
|
+
}) ? { type: "guest-done" } : {
|
|
120
|
+
type: "guest-refused",
|
|
121
|
+
reason: "gone"
|
|
122
|
+
});
|
|
123
|
+
case "guest-decide": {
|
|
124
|
+
const decided = await actions.decide({
|
|
125
|
+
call,
|
|
126
|
+
guest: message.guest,
|
|
127
|
+
member,
|
|
128
|
+
admit: message.admit
|
|
129
|
+
});
|
|
130
|
+
return send(decided.refused ? {
|
|
131
|
+
type: "guest-refused",
|
|
132
|
+
reason: decided.refused
|
|
133
|
+
} : { type: "guest-done" });
|
|
134
|
+
}
|
|
135
|
+
case "guest-remove":
|
|
136
|
+
await actions.remove(call, message.guest);
|
|
137
|
+
return send({ type: "guest-done" });
|
|
138
|
+
}
|
|
139
|
+
};
|
|
83
140
|
const relay = async (message) => {
|
|
84
141
|
if (message.type === "join" || message.type === "resume") return enter(message);
|
|
85
|
-
if (!session) return close(refusedCloseCode, "Join or resume the call first");
|
|
142
|
+
if (!session || !inCall) return close(refusedCloseCode, "Join or resume the call first");
|
|
143
|
+
if (isGuestMessage(message)) return guests(message, inCall.call);
|
|
86
144
|
if (message.type === "publish" && !mayPublish(rights, message.tracks)) return send({
|
|
87
145
|
type: "publish-refused",
|
|
88
146
|
reason: "The member may not publish these tracks"
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { badRequest, forbidden } from "../problem.js";
|
|
2
|
+
import { Hono } from "hono";
|
|
3
|
+
//#region src/calls/organization-guests-route.ts
|
|
4
|
+
const organizationOf = (session) => {
|
|
5
|
+
if (session.anonymous || session.client || session.organization === void 0) throw forbidden("organization-required", "Sign in and work in an organization first");
|
|
6
|
+
return String(session.organization.id);
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Whether the calls of the organization the user works in take guests, mounted behind the API's authenticator: `GET /`
|
|
10
|
+
* `{ allowed }` for any member, `PUT /` with `{ allowed }` for its owner or an admin. Turning guests off revokes every
|
|
11
|
+
* guest link of the organization's calls and takes their guests out at once.
|
|
12
|
+
*/
|
|
13
|
+
const organizationGuestsRoute = ({ calls, roleIn }) => {
|
|
14
|
+
const app = new Hono();
|
|
15
|
+
app.get("/", async (c) => {
|
|
16
|
+
const session = c.get("session");
|
|
17
|
+
const organization = organizationOf(session);
|
|
18
|
+
if (!await roleIn(organization, String(session.user.id))) throw forbidden("not-a-member", "You are not a member of this organization");
|
|
19
|
+
return c.json({ allowed: await (await calls.runtime()).guests().organizationAllowed(organization) }, 200, { "Cache-Control": "no-store" });
|
|
20
|
+
});
|
|
21
|
+
app.put("/", async (c) => {
|
|
22
|
+
const session = c.get("session");
|
|
23
|
+
const organization = organizationOf(session);
|
|
24
|
+
const role = await roleIn(organization, String(session.user.id));
|
|
25
|
+
if (role !== "owner" && role !== "admin") throw forbidden("organization-admin-required", "Only an owner or admin of the organization turns guest links on or off");
|
|
26
|
+
const body = await c.req.json().catch(() => void 0);
|
|
27
|
+
if (typeof body?.allowed !== "boolean") throw badRequest("allowed-required", "Send { allowed: true } or { allowed: false }");
|
|
28
|
+
await (await calls.runtime()).setOrganizationGuests({
|
|
29
|
+
organization,
|
|
30
|
+
allowed: body.allowed,
|
|
31
|
+
by: String(session.user.id)
|
|
32
|
+
});
|
|
33
|
+
return c.json({ allowed: body.allowed });
|
|
34
|
+
});
|
|
35
|
+
return app;
|
|
36
|
+
};
|
|
37
|
+
//#endregion
|
|
38
|
+
export { organizationGuestsRoute };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { decodeKey } from "@protobase/schema/internal";
|
|
2
|
+
import { sql } from "kysely";
|
|
3
|
+
//#region src/calls/record-organization.ts
|
|
4
|
+
/**
|
|
5
|
+
* The organization a call's record is in, read from the record's own tenant field with no access check: `null` for a
|
|
6
|
+
* record of a resource without a tenant, or one that is gone, which no organization's switch covers. A key of a relation
|
|
7
|
+
* to a resource with a key of several fields is read as none either, so its calls take no guests.
|
|
8
|
+
*/
|
|
9
|
+
const recordOrganization = (registry) => async (trx, record) => {
|
|
10
|
+
const entry = registry.find(record.resource);
|
|
11
|
+
const tenant = entry?.model.tenant;
|
|
12
|
+
if (!entry || !tenant) return null;
|
|
13
|
+
const { model } = entry;
|
|
14
|
+
const decoded = decodeKey(record.key, entry.keyTypes);
|
|
15
|
+
const values = Array.isArray(decoded) ? decoded : [decoded];
|
|
16
|
+
if (values.length !== model.primaryKey.length) return null;
|
|
17
|
+
const table = model.table.schema ? sql.id(model.table.schema, model.table.name) : sql.id(model.table.name);
|
|
18
|
+
const where = sql.join(model.primaryKey.map((name, index) => sql`${sql.id(model.fields[name].column)} = ${values[index]}`), sql` and `);
|
|
19
|
+
const { rows } = await sql`select ${sql.id(model.fields[tenant].column)} as tenant from ${table} where ${where}`.execute(trx);
|
|
20
|
+
const found = rows[0]?.tenant;
|
|
21
|
+
return found === void 0 || found === null ? null : String(found);
|
|
22
|
+
};
|
|
23
|
+
//#endregion
|
|
24
|
+
export { recordOrganization };
|
package/dist/create-admin.js
CHANGED
|
@@ -61,6 +61,10 @@ import { webhookTable } from "./webhooks/table/webhook-table.js";
|
|
|
61
61
|
import { subscriptionUrlCheck, subscriptionWriteCheck } from "./webhooks/table/validate-hook.js";
|
|
62
62
|
import { callsService } from "./calls/calls-service.js";
|
|
63
63
|
import { ringRoutes } from "./calls/ring-routes.js";
|
|
64
|
+
import { upgradeAuthenticator } from "./functions/upgrade-caller.js";
|
|
65
|
+
import { guestRoutes } from "./calls/guest-routes.js";
|
|
66
|
+
import { organizationGuestsRoute } from "./calls/organization-guests-route.js";
|
|
67
|
+
import { organizationRole } from "./better-auth/organizations/organization-role.js";
|
|
64
68
|
import { deviceStore, removeDevicesWithUsers } from "./notifications/device-store-of.js";
|
|
65
69
|
import { historyStore, removeHistoryWithUsers } from "./notifications/history-store-of.js";
|
|
66
70
|
import { devicesPath, historyPath } from "./notifications/paths.js";
|
|
@@ -91,7 +95,6 @@ import { fieldDecrypter } from "./encryption/decrypt-field.js";
|
|
|
91
95
|
import { createEncryptionRuntime } from "./encryption/runtime.js";
|
|
92
96
|
import { functionRoutes } from "./functions/routes.js";
|
|
93
97
|
import { supabaseFunctionRoutes } from "./functions/supabase-routes.js";
|
|
94
|
-
import { upgradeAuthenticator } from "./functions/upgrade-caller.js";
|
|
95
98
|
import { localeSetting } from "./locale/locale-option.js";
|
|
96
99
|
import { requestLocale } from "./locale/request-locale.js";
|
|
97
100
|
import { mapSettings } from "./maps/map-settings.js";
|
|
@@ -112,7 +115,8 @@ import { createScanGuard } from "./scan-guard.js";
|
|
|
112
115
|
import { failedJobs, failedJobsView } from "./jobs/failed-jobs.js";
|
|
113
116
|
import { Hono } from "hono";
|
|
114
117
|
import { accountPage, accountPageName, checkPages, settingsResources, withSettingsFields } from "@protobase/layout";
|
|
115
|
-
import { ringsPath, statusPath } from "@protobase/schema/internal";
|
|
118
|
+
import { guestCallsPath, ringsPath, statusPath } from "@protobase/schema/internal";
|
|
119
|
+
import { getIP } from "better-auth/api";
|
|
116
120
|
//#region src/create-admin.ts
|
|
117
121
|
const declaredRoles = (auth, roles) => {
|
|
118
122
|
const names = [...auth?.roles ?? [], ...Object.keys(roles?.definitions ?? {})];
|
|
@@ -270,6 +274,7 @@ const createAdmin = ({ resources: appResources, views: appViews = [], pages = []
|
|
|
270
274
|
notices: changes,
|
|
271
275
|
channels: () => channels,
|
|
272
276
|
notifications: Boolean(notifications),
|
|
277
|
+
organizations: Boolean(auth?.organizations),
|
|
273
278
|
...auth?.origins[0] && { origin: auth.origins[0] },
|
|
274
279
|
...statusTables && { doNotDisturb: (ringDb, userIds) => onDoNotDisturb(ringDb, statusTables.statuses, userIds) }
|
|
275
280
|
}) }
|
|
@@ -414,6 +419,7 @@ const createAdmin = ({ resources: appResources, views: appViews = [], pages = []
|
|
|
414
419
|
records: (session) => callerRecords(deps, session),
|
|
415
420
|
channels,
|
|
416
421
|
decryptField,
|
|
422
|
+
...auth && auth.personalMail.length > 0 && { personalMailer: auth.personalMailer },
|
|
417
423
|
onError,
|
|
418
424
|
wake: () => wakeDispatch(deps),
|
|
419
425
|
upgradeWebSocket,
|
|
@@ -469,6 +475,21 @@ const createAdmin = ({ resources: appResources, views: appViews = [], pages = []
|
|
|
469
475
|
}
|
|
470
476
|
if (statusTables) app.route(statusPath(systemPath), guarded((routes) => void routes.route("/", statusRoutes(db, statusTables))));
|
|
471
477
|
if (deps.calls) app.route(ringsPath(systemPath), guarded((routes) => void routes.route("/", ringRoutes(deps.calls))));
|
|
478
|
+
if (deps.calls && auth) {
|
|
479
|
+
const addressOf = (request) => getIP(request, auth.options);
|
|
480
|
+
app.route(guestCallsPath(systemPath), guestRoutes({
|
|
481
|
+
calls: deps.calls,
|
|
482
|
+
addressOf,
|
|
483
|
+
origins: auth.origins,
|
|
484
|
+
onError,
|
|
485
|
+
...upgradeWebSocket && { upgradeWebSocket },
|
|
486
|
+
report
|
|
487
|
+
}));
|
|
488
|
+
if (auth.organizations) app.route(guestCallsPath(systemPath, "organization"), guarded((routes) => void routes.route("/", organizationGuestsRoute({
|
|
489
|
+
calls: deps.calls,
|
|
490
|
+
roleIn: organizationRole(auth)
|
|
491
|
+
}))));
|
|
492
|
+
}
|
|
472
493
|
if (forms) app.route(`${systemPath}/forms`, formRoutes(deps, forms));
|
|
473
494
|
const feed = tableFeed({
|
|
474
495
|
db,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DecryptField } from "../encryption/decrypt-field-types.js";
|
|
2
|
+
import { Mailer } from "../mail/mail-message.js";
|
|
2
3
|
import { Session } from "../types.js";
|
|
3
4
|
import { Caller } from "../api-keys/resolve-caller.js";
|
|
4
5
|
import { SocketEvents } from "./function-socket.js";
|
|
@@ -43,6 +44,15 @@ export type FunctionContext = PublicFunctionContext & FunctionCaller & {
|
|
|
43
44
|
* the audit does. Not on a public function's context: a public function that needs a value enqueues a job.
|
|
44
45
|
*/
|
|
45
46
|
decryptField: DecryptField;
|
|
47
|
+
/**
|
|
48
|
+
* Mail from the caller's own mailbox, through the mail provider they linked (`auth.personalMail`), attachments and
|
|
49
|
+
* all: `personalMail.send({ to, subject, text, html, attachments })`, or an email template's `send(to, data,
|
|
50
|
+
* context.personalMail, { attachments })`. Rejects with a `PersonalMailError`, sending nothing, when they linked no
|
|
51
|
+
* mailbox (`not_linked`) or the link no longer works (`link_expired`); thrown on, it answers as a 409 problem with
|
|
52
|
+
* that `code`. Only someone signed in as themselves sends from their mailbox: staff signed in as them, or an outside
|
|
53
|
+
* app acting for them, is refused with a 403 problem. Not on a public function's context.
|
|
54
|
+
*/
|
|
55
|
+
personalMail: Mailer;
|
|
46
56
|
};
|
|
47
57
|
/** What a function gets at `/api/sb/functions/v1/<name>`, where supabase-js calls it: the caller its keys and token make. */
|
|
48
58
|
export type KeyFunctionContext = {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ownSignInUser } from "../notifications/caller.js";
|
|
2
|
+
//#region src/functions/own-mailbox.ts
|
|
3
|
+
/**
|
|
4
|
+
* The caller's own mailbox: mail goes out from it only for someone signed in as themselves. Staff signed in as them,
|
|
5
|
+
* an outside app acting for them and a visitor are refused with a 403 problem, sending nothing.
|
|
6
|
+
*/
|
|
7
|
+
const ownMailbox = (session, personalMailer) => ({ send: async (message) => personalMailer(ownSignInUser(session, "send mail from their own mailbox")).send(message) });
|
|
8
|
+
//#endregion
|
|
9
|
+
export { ownMailbox };
|
package/dist/functions/routes.js
CHANGED
|
@@ -2,9 +2,10 @@ import { forbidden, notFound } from "../problem.js";
|
|
|
2
2
|
import { namingTimeouts } from "../statement-timeout.js";
|
|
3
3
|
import { auditOrigin } from "../audit/origin.js";
|
|
4
4
|
import { invokeFunction } from "./define-function.js";
|
|
5
|
+
import { socketAcceptor } from "./accept-socket.js";
|
|
5
6
|
import { runInBackground } from "../background-work.js";
|
|
6
7
|
import { callsAccess } from "../calls/calls-access.js";
|
|
7
|
-
import {
|
|
8
|
+
import { ownMailbox } from "./own-mailbox.js";
|
|
8
9
|
import { functionGate, requireGate } from "./function-gate.js";
|
|
9
10
|
import { sameCaller } from "./same-caller.js";
|
|
10
11
|
import { Hono } from "hono";
|
|
@@ -15,6 +16,7 @@ const relativeRequest = (request, prefix) => {
|
|
|
15
16
|
url.pathname = url.pathname.slice(prefix.length) || "/";
|
|
16
17
|
return new Request(url, request);
|
|
17
18
|
};
|
|
19
|
+
const noPersonalMail = () => ({ send: () => Promise.reject(/* @__PURE__ */ new Error("Personal mail is off: name the mail providers people may link in auth.personalMail, such as [\"gmail\"]")) });
|
|
18
20
|
const isUpgrade = (request) => request.headers.get("upgrade")?.toLowerCase() === "websocket";
|
|
19
21
|
/**
|
|
20
22
|
* Every function at `<basePath>/<name>` and below, any method. A protected function authenticates the request first, so
|
|
@@ -24,7 +26,7 @@ const isUpgrade = (request) => request.headers.get("upgrade")?.toLowerCase() ===
|
|
|
24
26
|
* A function may answer an upgrade with a socket, only once it returned. While it is open, its caller is checked again
|
|
25
27
|
* every minute: a protected function's always, a public one's once `caller()` resolved.
|
|
26
28
|
*/
|
|
27
|
-
const functionRoutes = ({ basePath, functions, authenticate, access, db, records, channels, decryptField, onError, wake, upgradeWebSocket, upgradeAuthenticate = authenticate, calls, report }) => {
|
|
29
|
+
const functionRoutes = ({ basePath, functions, authenticate, access, db, records, channels, decryptField, personalMailer = noPersonalMail, onError, wake, upgradeWebSocket, upgradeAuthenticate = authenticate, calls, report }) => {
|
|
28
30
|
const app = new Hono().basePath(basePath);
|
|
29
31
|
app.onError(onError);
|
|
30
32
|
const sessionOf = (request) => isUpgrade(request) ? upgradeAuthenticate(request) : authenticate(request);
|
|
@@ -99,6 +101,7 @@ const functionRoutes = ({ basePath, functions, authenticate, access, db, records
|
|
|
99
101
|
...caller,
|
|
100
102
|
caller: async () => caller,
|
|
101
103
|
decryptField: decryptField(reader),
|
|
104
|
+
personalMail: ownMailbox(caller.session, personalMailer),
|
|
102
105
|
acceptWebSocket: acceptor.accept,
|
|
103
106
|
calls: callsAccess(calls, async () => caller)
|
|
104
107
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { RealtimeOptions, relayed, sqlSends } from "./realtime/relay/realtime-op
|
|
|
5
5
|
import { EventsOptions } from "./changes/events-options.js";
|
|
6
6
|
import { DecryptField, FieldDecrypter, FieldReader, RecordKey } from "./encryption/decrypt-field-types.js";
|
|
7
7
|
import { StandardSchema, readJson } from "./functions/read-json.js";
|
|
8
|
-
import { MailMessage, Mailer } from "./mail/
|
|
8
|
+
import { MailAttachment, MailMessage, Mailer } from "./mail/mail-message.js";
|
|
9
9
|
import { JsonObject, JsonValue } from "./notifications/json-value.js";
|
|
10
10
|
import { HistoryPosition, NewNotification, StoredNotification, historyIdPattern, historyLimits } from "./notifications/history.js";
|
|
11
11
|
import { PushMessage, PushMessageError, Urgency, WebPushFields, maxTtlSeconds, pushMessage, urgencies } from "./notifications/message.js";
|
|
@@ -175,9 +175,11 @@ import { defineJob, isJobDefinition, jobDefinitions } from "./jobs/define-job.js
|
|
|
175
175
|
import { FatalJobError } from "./jobs/run-job.js";
|
|
176
176
|
import { ArgumentsPayload, EnqueueJob, JobDeclaration, JobName, JobPayload, JobRun, enqueueJob } from "./jobs/declared-job.js";
|
|
177
177
|
import { isSchedule } from "./jobs/schedule.js";
|
|
178
|
+
import { PersonalMailError, PersonalMailErrorCode } from "./personal-mail/personal-mail-error.js";
|
|
179
|
+
import { PersonalMailProviderId } from "./personal-mail/personal-mail-providers.js";
|
|
178
180
|
import { EmailComponent, EmailOptions, EmailTemplate, RenderedEmail, defineEmail } from "./mail/define-email.js";
|
|
179
181
|
import { DrainResult, JobsRuntime, StartJobsOptions, startJobs } from "./jobs/runtime.js";
|
|
180
182
|
import { jobsDrainApp } from "./jobs/drain-route.js";
|
|
181
183
|
import { checkJobsSchema, jobsSchema, jobsSchemaMigration } from "./jobs/schema.js";
|
|
182
184
|
import { AdminAuth, CreateAuthOptions, UserCreatedHook, normalizeRoles } from "./better-auth/create-auth.js";
|
|
183
|
-
export { type Admin, type AdminAuth, type AdminOptions, type AggregateParams, type ApiFunction, type ApiKeys, type ApiKeysOptions, type ApnsEnvironment, type AppIcons, type AppIdentity, type AppPoolConfig, type ApplyOptions, type ApplyResult, type ApprovalAnswer, type ApprovalRequest, type ArgumentsPayload, type AssistantBackend, type AssistantOptions, type AssistantSettings, type AssistantTool, type AuditEvent, type AuditQueue, type AuthHooks, type AuthSettings, type AuthenticateSettings, type Authenticator, type BaseToolContext, type BatchOperation, type BatchResult, type BetterAuthAuthenticator, type BetterAuthAuthenticatorOptions, type CallBackend, type CallMember, type Caller, type CallerOptions, type CallsAccess, type CallsOptions, type CapturedRow, type CascadingKey, type ChannelBroadcast, type ChannelSender, type ChatMessage, type ChatTextPart, type CheckAiInput, type CleanupEntry, type CleanupReport, type CleanupSchedule, type CoalescedChange, type CommentItem, type CommentToolsInput, type CommentUsers, type Completion, type CompletionOptions, ConnectionsBusy, type Conversation, type ConversationRecord, type ConversationStore, type ConversationsOptions, type CorsOptions, type CorsOrigins, type CreateAdminInput, type CreateAuthOptions, type DecryptField, type Device, type DeviceColumn, type DeviceKind, type DeviceStore, DeviceStoreError, type DrainResult, type EmailComponent, type EmailOptions, type EmailTemplate, type EncryptionStatus, type EncryptionTools, type EncryptionToolsInput, type EnqueueJob, type EnqueueOptions, type ErrorReporter, type ErrorReportingOptions, type ErrorReportingSettings, type EventData, type EventsOptions, FatalJobError, type FieldApplied, type FieldDecrypter, type FieldReader, type FieldStatus, type FileCleanupInput, type FileLimits, type FileObject, type FileProvider, type FileProviderSettings, 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 HistoryColumn, type HistoryOptions, type HistoryPosition, type HistoryStore, HistoryStoreError, type HookContext, HttpProblem, type IssueTokenOptions, type JobContext, type JobDeclaration, type JobDefinition, type JobName, type JobOptions, type JobPayload, type JobRun, type JobsRuntime, type JsonObject, type JsonValue, type JwtOptions, type JwtVerifyOptions, type KeyFunctionContext, type LinkedAccount, type ListParams, type LiveKitBackend, type LocalFilesOptions, type MailMessage, type Mailer, type MapLayer, type MapOptions, type MapSettings, type McpApprovals, type McpGateInput, type McpHandlerInput, type McpRoutesInput, type McpServerInput, type McpToolsInput, type McpWrites, type MemberInput, type Membership, MissingCredentials, type MobileConfig, ModelError, type ModelSettings, type NewDevice, type NewNotification, type NewOrganization, type NewUser, type NotificationRecipients, NotificationSendError, type NotificationsOptions, type OperatorProvider, type OrganizationRole, type OrganizationsOptions, type PgModule, type PipelineHook, type PlatformSignIn, type PluginOption, type PluginRef, type PngIcon, type ProtectedFunctionOptions, type PublicFunctionContext, type PublicFunctionEnv, type PublicFunctionHandler, type PublicFunctionOptions, type PushAnswer, type PushMessage, PushMessageError, type PushSender, type QueryOptions, type QueryResult, type ReadableResources, type RealtimeOptions, type RealtimePrune, type ReasoningDetail, type ReasoningEffort, type RebindOption, type RecordGroups, type RecordKey, type RecordKeyInput, type RecordPage, 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 SendNotification, 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 StatusOptions, type StoreFileInput, type StoredMember, type StoredNotification, type StoredObject, type StoredOrganization, type StoredUser, type StrayValues, type SubscriptionDefinition, type SubscriptionEvent, type SubscriptionOptions, type TenantValue, type ToolAnnotations, type ToolCall, type ToolComment, type ToolCommentPage, type ToolComments, type ToolContext, type ToolRecord, type ToolRecords, type ToolSpec, type TrustedApp, type TurnOptions, type UnreadableEntry, type Urgency, type User, type UserCreatedHook, type VapidSettings, type ViewSource, type WebPushFields, type WebPushOptions, type WebhooksOptions, type WidgetOptions, type WriteEvent, type WriteOperation, type WriteRecordToolsInput, addAppRoleToOwners, addCommentTool, addMember, apiKeyCaller, apiKeySettings, apiKeyVariables, appNameOver, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaDrift, authSchemaMigration, authStoreTables, backgroundSettled, badRequest, batchWriteTool, beforeSignInPluginsVariable, betterAuthTokens, callsMigration, capSocketMessages, changesMigration, channelSendsMigration, checkAi, checkAuthSchema, checkCalls, checkChanges, checkChannelSends, checkComments, checkComputed, checkJobsSchema, checkNotificationsSchema, checkQueryRunner, checkRealtimeRelay, checkResourcesOutsideAuthStore, checkSourceNames, checkStatusSchema, checkViewQueries, checkWebhooksSchema, coalesce, commentTools, commentsMigration, computedMigration, configExports, consoleAuditQueue, conversationKey, countRecordsTool, createAdmin, createConversations, createEncryptionTools, createFileCleanup, createOrganization, createRecordTool, createUser, defaultAppName, defaultHistoryPage, defaultSignInPolicy, defineEmail, defineFunction, defineJob, defineTool, deleteRecordTool, deleteUser, describeResourceTool, deviceIdParam, deviceStore, devicesPath, enqueueJob, errorReportingSettings, errorReportingVariable, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, formatUri, functionApp, generateVapidKeys, getRecordTool, goTruePath, handleDroppedConnections, hasUsers, historyIdParam, historyIdPattern, historyLimits, historyPath, historyStore, honorSocketMessageCaps, imageSize, isBackgroundWork, isCaptured, isJobDefinition, isOAuthWellKnownPath, isSchedule, issueToken, jobDefinitions, jobsDrainApp, jobsSchema, jobsSchemaMigration, jwtAuthenticator, jwtVerifier, limitedFileStore, listCommentsTool, listOrganizations, listRecordsTool, listResourcesTool, listUsers, localFileStore, longQueryTimeoutMs, mapSettings, mapStyleUrl, mapStyleVariable, maxDevicesPerUser, maxHistoryPage, maxMessageBytes, maxRecipients, maxTokenTtlSeconds, maxTtlSeconds, maxUnreadCount, maxWidgetPropsBytes, mcpApprovals, mcpGate, mcpHandler, mcpIcons, mcpInstructions, mcpRoutes, mcpServer, mcpToolContext, mcpTools, migrationCommand, newWebhookSecret, normalizeRoles, notFound, notificationSenders, notificationsMigration, oauthWellKnownPaths, objectKey, onDoNotDisturb, parseColumns, parseRoles, passwordLength, pluginOptions, pluginsBeforeSignIn, pluginsFor, pluginsVariable, pngIcons, poolWaitMs, postgresDeviceStore, postgresHistoryStore, processor, pushMessage, queryRunnerMigration, readDbConnections, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readRecordTools, readVapidSettings, readWriteQueryTool, readableResources, realtimeLimits, realtimeRelayMigration, recordOf, refusalOf, refused, relayed, removeDevicesWithUsers, removeHistoryWithUsers, removeMember, renderVariant, requestApproval, requireEnv, resourceChange, roleChoices, runInBackground, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, s3FileStore, seesAssistant, seesRuntime, sendNotification, sessionOf, setMemberRoles, setUserBanned, setUserRole, shownAppName, signInPolicyMethods, socketMessageCapOf, sourceOptions, sourcesFor, sourcesVariable, sqlSends, startErrorReporting, startJobs, statusMigration, statusNames, storageCleanupSchedule, storageProvider, storeFile, storeUsage, streamChatCompletion, subscription, supabasePath, thumbHash, unauthorized, updateRecordTool, urgencies, vapidPrivateKeyVariable, vapidPublicKeyVariable, vapidSubjectVariable, variantPath, verifySignature, verifyStandardWebhook, waitUntil, wantsLongQueries, webManifestPath, webPushSender, webauthnPath, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps, writeRecordTools };
|
|
185
|
+
export { type Admin, type AdminAuth, type AdminOptions, type AggregateParams, type ApiFunction, type ApiKeys, type ApiKeysOptions, type ApnsEnvironment, type AppIcons, type AppIdentity, type AppPoolConfig, type ApplyOptions, type ApplyResult, type ApprovalAnswer, type ApprovalRequest, type ArgumentsPayload, type AssistantBackend, type AssistantOptions, type AssistantSettings, type AssistantTool, type AuditEvent, type AuditQueue, type AuthHooks, type AuthSettings, type AuthenticateSettings, type Authenticator, type BaseToolContext, type BatchOperation, type BatchResult, type BetterAuthAuthenticator, type BetterAuthAuthenticatorOptions, type CallBackend, type CallMember, type Caller, type CallerOptions, type CallsAccess, type CallsOptions, type CapturedRow, type CascadingKey, type ChannelBroadcast, type ChannelSender, type ChatMessage, type ChatTextPart, type CheckAiInput, type CleanupEntry, type CleanupReport, type CleanupSchedule, type CoalescedChange, type CommentItem, type CommentToolsInput, type CommentUsers, type Completion, type CompletionOptions, ConnectionsBusy, type Conversation, type ConversationRecord, type ConversationStore, type ConversationsOptions, type CorsOptions, type CorsOrigins, type CreateAdminInput, type CreateAuthOptions, type DecryptField, type Device, type DeviceColumn, type DeviceKind, type DeviceStore, DeviceStoreError, type DrainResult, type EmailComponent, type EmailOptions, type EmailTemplate, type EncryptionStatus, type EncryptionTools, type EncryptionToolsInput, type EnqueueJob, type EnqueueOptions, type ErrorReporter, type ErrorReportingOptions, type ErrorReportingSettings, type EventData, type EventsOptions, FatalJobError, type FieldApplied, type FieldDecrypter, type FieldReader, type FieldStatus, type FileCleanupInput, type FileLimits, type FileObject, type FileProvider, type FileProviderSettings, 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 HistoryColumn, type HistoryOptions, type HistoryPosition, type HistoryStore, HistoryStoreError, type HookContext, HttpProblem, type IssueTokenOptions, type JobContext, type JobDeclaration, type JobDefinition, type JobName, type JobOptions, type JobPayload, type JobRun, type JobsRuntime, type JsonObject, type JsonValue, type JwtOptions, type JwtVerifyOptions, type KeyFunctionContext, type LinkedAccount, type ListParams, type LiveKitBackend, type LocalFilesOptions, type MailAttachment, type MailMessage, type Mailer, type MapLayer, type MapOptions, type MapSettings, type McpApprovals, type McpGateInput, type McpHandlerInput, type McpRoutesInput, type McpServerInput, type McpToolsInput, type McpWrites, type MemberInput, type Membership, MissingCredentials, type MobileConfig, ModelError, type ModelSettings, type NewDevice, type NewNotification, type NewOrganization, type NewUser, type NotificationRecipients, NotificationSendError, type NotificationsOptions, type OperatorProvider, type OrganizationRole, type OrganizationsOptions, PersonalMailError, type PersonalMailErrorCode, type PersonalMailProviderId, type PgModule, type PipelineHook, type PlatformSignIn, type PluginOption, type PluginRef, type PngIcon, type ProtectedFunctionOptions, type PublicFunctionContext, type PublicFunctionEnv, type PublicFunctionHandler, type PublicFunctionOptions, type PushAnswer, type PushMessage, PushMessageError, type PushSender, type QueryOptions, type QueryResult, type ReadableResources, type RealtimeOptions, type RealtimePrune, type ReasoningDetail, type ReasoningEffort, type RebindOption, type RecordGroups, type RecordKey, type RecordKeyInput, type RecordPage, 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 SendNotification, 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 StatusOptions, type StoreFileInput, type StoredMember, type StoredNotification, type StoredObject, type StoredOrganization, type StoredUser, type StrayValues, type SubscriptionDefinition, type SubscriptionEvent, type SubscriptionOptions, type TenantValue, type ToolAnnotations, type ToolCall, type ToolComment, type ToolCommentPage, type ToolComments, type ToolContext, type ToolRecord, type ToolRecords, type ToolSpec, type TrustedApp, type TurnOptions, type UnreadableEntry, type Urgency, type User, type UserCreatedHook, type VapidSettings, type ViewSource, type WebPushFields, type WebPushOptions, type WebhooksOptions, type WidgetOptions, type WriteEvent, type WriteOperation, type WriteRecordToolsInput, addAppRoleToOwners, addCommentTool, addMember, apiKeyCaller, apiKeySettings, apiKeyVariables, appNameOver, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaDrift, authSchemaMigration, authStoreTables, backgroundSettled, badRequest, batchWriteTool, beforeSignInPluginsVariable, betterAuthTokens, callsMigration, capSocketMessages, changesMigration, channelSendsMigration, checkAi, checkAuthSchema, checkCalls, checkChanges, checkChannelSends, checkComments, checkComputed, checkJobsSchema, checkNotificationsSchema, checkQueryRunner, checkRealtimeRelay, checkResourcesOutsideAuthStore, checkSourceNames, checkStatusSchema, checkViewQueries, checkWebhooksSchema, coalesce, commentTools, commentsMigration, computedMigration, configExports, consoleAuditQueue, conversationKey, countRecordsTool, createAdmin, createConversations, createEncryptionTools, createFileCleanup, createOrganization, createRecordTool, createUser, defaultAppName, defaultHistoryPage, defaultSignInPolicy, defineEmail, defineFunction, defineJob, defineTool, deleteRecordTool, deleteUser, describeResourceTool, deviceIdParam, deviceStore, devicesPath, enqueueJob, errorReportingSettings, errorReportingVariable, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, formatUri, functionApp, generateVapidKeys, getRecordTool, goTruePath, handleDroppedConnections, hasUsers, historyIdParam, historyIdPattern, historyLimits, historyPath, historyStore, honorSocketMessageCaps, imageSize, isBackgroundWork, isCaptured, isJobDefinition, isOAuthWellKnownPath, isSchedule, issueToken, jobDefinitions, jobsDrainApp, jobsSchema, jobsSchemaMigration, jwtAuthenticator, jwtVerifier, limitedFileStore, listCommentsTool, listOrganizations, listRecordsTool, listResourcesTool, listUsers, localFileStore, longQueryTimeoutMs, mapSettings, mapStyleUrl, mapStyleVariable, maxDevicesPerUser, maxHistoryPage, maxMessageBytes, maxRecipients, maxTokenTtlSeconds, maxTtlSeconds, maxUnreadCount, maxWidgetPropsBytes, mcpApprovals, mcpGate, mcpHandler, mcpIcons, mcpInstructions, mcpRoutes, mcpServer, mcpToolContext, mcpTools, migrationCommand, newWebhookSecret, normalizeRoles, notFound, notificationSenders, notificationsMigration, oauthWellKnownPaths, objectKey, onDoNotDisturb, parseColumns, parseRoles, passwordLength, pluginOptions, pluginsBeforeSignIn, pluginsFor, pluginsVariable, pngIcons, poolWaitMs, postgresDeviceStore, postgresHistoryStore, processor, pushMessage, queryRunnerMigration, readDbConnections, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readRecordTools, readVapidSettings, readWriteQueryTool, readableResources, realtimeLimits, realtimeRelayMigration, recordOf, refusalOf, refused, relayed, removeDevicesWithUsers, removeHistoryWithUsers, removeMember, renderVariant, requestApproval, requireEnv, resourceChange, roleChoices, runInBackground, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, s3FileStore, seesAssistant, seesRuntime, sendNotification, sessionOf, setMemberRoles, setUserBanned, setUserRole, shownAppName, signInPolicyMethods, socketMessageCapOf, sourceOptions, sourcesFor, sourcesVariable, sqlSends, startErrorReporting, startJobs, statusMigration, statusNames, storageCleanupSchedule, storageProvider, storeFile, storeUsage, streamChatCompletion, subscription, supabasePath, thumbHash, unauthorized, updateRecordTool, urgencies, vapidPrivateKeyVariable, vapidPublicKeyVariable, vapidSubjectVariable, variantPath, verifySignature, verifyStandardWebhook, waitUntil, wantsLongQueries, webManifestPath, webPushSender, webauthnPath, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps, writeRecordTools };
|