@vibes.diy/api-svc 2.0.3 → 2.0.6
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/create-handler.js +4 -0
- package/create-handler.js.map +1 -1
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/index.js.map +1 -1
- package/intern/ensure-chat-id.js +64 -1
- package/intern/ensure-chat-id.js.map +1 -1
- package/intern/ensure-slug-binding.d.ts +8 -1
- package/intern/ensure-slug-binding.js +71 -40
- package/intern/ensure-slug-binding.js.map +1 -1
- package/intern/grouped-vibe-import-map.d.ts +2 -22
- package/intern/grouped-vibe-import-map.js +3 -23
- package/intern/grouped-vibe-import-map.js.map +1 -1
- package/intern/pre-allocate.d.ts +12 -0
- package/intern/pre-allocate.js +56 -0
- package/intern/pre-allocate.js.map +1 -0
- package/intern/write-apps.js +16 -3
- package/intern/write-apps.js.map +1 -1
- package/models.json +19 -6
- package/package.json +11 -10
- package/public/app-documents.d.ts +7 -0
- package/public/app-documents.js +210 -0
- package/public/app-documents.js.map +1 -0
- package/public/ensure-app-settings.js +25 -2
- package/public/ensure-app-settings.js.map +1 -1
- package/public/fork-app.d.ts +5 -0
- package/public/fork-app.js +287 -0
- package/public/fork-app.js.map +1 -0
- package/public/open-chat.js +1 -0
- package/public/open-chat.js.map +1 -1
- package/public/prompt-chat-section.d.ts +5 -3
- package/public/prompt-chat-section.js +324 -41
- package/public/prompt-chat-section.js.map +1 -1
- package/public/request-flow.d.ts +5 -0
- package/public/request-flow.js +33 -0
- package/public/request-flow.js.map +1 -1
- package/svc-ws-send-provider.d.ts +1 -0
- package/svc-ws-send-provider.js +1 -0
- package/svc-ws-send-provider.js.map +1 -1
- package/types.d.ts +1 -0
- package/types.js.map +1 -1
- package/usage-report/inspect-db-report-template.d.ts +4 -0
- package/usage-report/inspect-db-report-template.js +25 -7
- package/usage-report/inspect-db-report-template.js.map +1 -1
- package/usage-report/inspect-db-report.js +25 -0
- package/usage-report/inspect-db-report.js.map +1 -1
- package/vibes-msg-evento.js +3 -1
- package/vibes-msg-evento.js.map +1 -1
- package/intern/import-map.ts-off +0 -405
- package/intern/invite-system.ts-off +0 -392
|
@@ -1,392 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
isInviteEmailToken,
|
|
3
|
-
isInviteLinkToken,
|
|
4
|
-
isReqInviteEmailPayload,
|
|
5
|
-
isReqInviteLinkPayload,
|
|
6
|
-
InviteToken,
|
|
7
|
-
ReqInviteToken,
|
|
8
|
-
ResInviteToken,
|
|
9
|
-
ReqDeleteInviteToken,
|
|
10
|
-
ResDeleteInviteToken,
|
|
11
|
-
ReqAcceptInvite,
|
|
12
|
-
ResAcceptInvite,
|
|
13
|
-
ReqListAcceptedInvites,
|
|
14
|
-
ResListAcceptedInvites,
|
|
15
|
-
ReqDeleteAccept,
|
|
16
|
-
ResDeleteAccept,
|
|
17
|
-
AcceptedClerkInfo,
|
|
18
|
-
ReqGetFPToken,
|
|
19
|
-
ResFPToken,
|
|
20
|
-
type InviteWithAccepts,
|
|
21
|
-
} from "@vibes.diy/api-types";
|
|
22
|
-
import { type } from "arktype";
|
|
23
|
-
import { VibesSqlite } from "../create-handler.js";
|
|
24
|
-
import { Result, exception2Result } from "@adviser/cement";
|
|
25
|
-
import { ReqWithVerifiedAuth } from "../check-auth.js";
|
|
26
|
-
import { sqlInviteTokens, sqlAcceptInvites, sqlUserSlugBinding, sqlAppSlugBinding } from "../sql/vibes-diy-api-schema.js";
|
|
27
|
-
import { count } from "drizzle-orm";
|
|
28
|
-
import { and, eq, or } from "drizzle-orm/sql/expressions";
|
|
29
|
-
|
|
30
|
-
export interface InviteSystemOpts {
|
|
31
|
-
db: VibesSqlite;
|
|
32
|
-
maxPerUserInviteTokens: number;
|
|
33
|
-
maxPerSlugInviteTokens: number;
|
|
34
|
-
emailValidDurationMs: number;
|
|
35
|
-
linkValidDurationMs: number;
|
|
36
|
-
nextId: () => string;
|
|
37
|
-
now?: Date;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export async function createInviteToken(
|
|
41
|
-
req: ReqWithVerifiedAuth<ReqInviteToken>,
|
|
42
|
-
opts: InviteSystemOpts
|
|
43
|
-
): Promise<Result<ResInviteToken>> {
|
|
44
|
-
const userId = req.auth.verifiedAuth.claims.userId;
|
|
45
|
-
|
|
46
|
-
// select count from sqlInviteTokens by req.invite.appSlug, req.invite.userSlug
|
|
47
|
-
const rCount = await exception2Result(() =>
|
|
48
|
-
opts.db
|
|
49
|
-
.select({ count: count() })
|
|
50
|
-
.from(sqlInviteTokens)
|
|
51
|
-
.where(and(eq(sqlInviteTokens.appSlug, req.invite.appSlug), eq(sqlInviteTokens.userSlug, req.invite.userSlug)))
|
|
52
|
-
.get()
|
|
53
|
-
);
|
|
54
|
-
if (rCount.isErr()) {
|
|
55
|
-
return Result.Err(`Failed to count invite tokens: ${rCount.Err().message}`);
|
|
56
|
-
}
|
|
57
|
-
// if count >= opts.maxPerSlugInviteTokens, return error
|
|
58
|
-
if ((rCount.Ok()?.count ?? 0) >= opts.maxPerSlugInviteTokens) {
|
|
59
|
-
return Result.Err(
|
|
60
|
-
`Max invite tokens (${opts.maxPerSlugInviteTokens}) reached for ${req.invite.appSlug}/${req.invite.userSlug}`
|
|
61
|
-
);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const now = opts.now ?? new Date();
|
|
65
|
-
const token = opts.nextId();
|
|
66
|
-
const base = {
|
|
67
|
-
type: "vibes.diy.invite-token" as const,
|
|
68
|
-
token,
|
|
69
|
-
appSlug: req.invite.appSlug,
|
|
70
|
-
userSlug: req.invite.userSlug,
|
|
71
|
-
roles: req.invite.roles,
|
|
72
|
-
ownerUserId: userId,
|
|
73
|
-
created: now,
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
let invite: InviteToken;
|
|
77
|
-
switch (true) {
|
|
78
|
-
case isReqInviteEmailPayload(req.invite):
|
|
79
|
-
// fill InviteEmailToken and insert into sqlInviteTokens
|
|
80
|
-
invite = {
|
|
81
|
-
...base,
|
|
82
|
-
style: "email",
|
|
83
|
-
email: req.invite.email,
|
|
84
|
-
validUntil: req.invite.validUntil ?? new Date(now.getTime() + opts.emailValidDurationMs),
|
|
85
|
-
};
|
|
86
|
-
break;
|
|
87
|
-
case isReqInviteLinkPayload(req.invite):
|
|
88
|
-
// fill InviteLinkToken and insert into sqlInviteTokens
|
|
89
|
-
invite = {
|
|
90
|
-
...base,
|
|
91
|
-
style: "link",
|
|
92
|
-
acceptCount: req.invite.acceptCount,
|
|
93
|
-
validUntil: req.invite.validUntil ?? new Date(now.getTime() + opts.linkValidDurationMs),
|
|
94
|
-
};
|
|
95
|
-
break;
|
|
96
|
-
default:
|
|
97
|
-
return Result.Err(`Invalid invite token style`);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const rInsert = await exception2Result(() =>
|
|
101
|
-
opts.db
|
|
102
|
-
.insert(sqlInviteTokens)
|
|
103
|
-
.values({
|
|
104
|
-
token: invite.token,
|
|
105
|
-
appSlug: invite.appSlug,
|
|
106
|
-
userSlug: invite.userSlug,
|
|
107
|
-
ownerUserId: invite.ownerUserId,
|
|
108
|
-
validUntil: invite.validUntil.toISOString(),
|
|
109
|
-
created: invite.created.toISOString(),
|
|
110
|
-
style: invite,
|
|
111
|
-
})
|
|
112
|
-
.run()
|
|
113
|
-
);
|
|
114
|
-
if (rInsert.isErr()) {
|
|
115
|
-
return Result.Err(`Failed to insert invite token: ${rInsert.Err().message}`);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
return Result.Ok({
|
|
119
|
-
type: "res.invite" as const,
|
|
120
|
-
invite,
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
export async function deleteInviteToken(
|
|
125
|
-
req: ReqWithVerifiedAuth<ReqDeleteInviteToken>,
|
|
126
|
-
opts: InviteSystemOpts
|
|
127
|
-
): Promise<Result<ResDeleteInviteToken>> {
|
|
128
|
-
const rDelete = await exception2Result(() => opts.db.delete(sqlInviteTokens).where(eq(sqlInviteTokens.token, req.token)).run());
|
|
129
|
-
if (rDelete.isErr()) {
|
|
130
|
-
return Result.Err(`Failed to delete invite token: ${rDelete.Err().message}`);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
const rDeleteAccepts = await exception2Result(() =>
|
|
134
|
-
opts.db.delete(sqlAcceptInvites).where(eq(sqlAcceptInvites.token, req.token)).run()
|
|
135
|
-
);
|
|
136
|
-
if (rDeleteAccepts.isErr()) {
|
|
137
|
-
return Result.Err(`Failed to delete accept invites: ${rDeleteAccepts.Err().message}`);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
return Result.Ok({
|
|
141
|
-
type: "res.delete-invite" as const,
|
|
142
|
-
token: req.token,
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
export async function acceptInvite(
|
|
147
|
-
req: ReqWithVerifiedAuth<ReqAcceptInvite>,
|
|
148
|
-
opts: InviteSystemOpts
|
|
149
|
-
): Promise<Result<ResAcceptInvite>> {
|
|
150
|
-
const acceptUserId = req.auth.verifiedAuth.claims.userId;
|
|
151
|
-
const now = opts.now ?? new Date();
|
|
152
|
-
|
|
153
|
-
const rToken = await exception2Result(() =>
|
|
154
|
-
opts.db.select().from(sqlInviteTokens).where(eq(sqlInviteTokens.token, req.token)).get()
|
|
155
|
-
);
|
|
156
|
-
if (rToken.isErr()) {
|
|
157
|
-
return Result.Err(`Failed to query invite token: ${rToken.Err().message}`);
|
|
158
|
-
}
|
|
159
|
-
const tokenRow = rToken.Ok();
|
|
160
|
-
if (!tokenRow) {
|
|
161
|
-
return Result.Err(`Invite token not found: ${req.token}`);
|
|
162
|
-
}
|
|
163
|
-
if (new Date(tokenRow.validUntil) < now) {
|
|
164
|
-
return Result.Err(`Invite token has expired`);
|
|
165
|
-
}
|
|
166
|
-
if (tokenRow.ownerUserId === acceptUserId) {
|
|
167
|
-
return Result.Err(`Invite owner cannot accept their own invite`);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const invite = InviteToken(tokenRow.style);
|
|
171
|
-
if (invite instanceof type.errors) {
|
|
172
|
-
return Result.Err(`Invalid stored invite token: ${invite.summary}`);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// Count existing accepts for limit check
|
|
176
|
-
const rCount = await exception2Result(() =>
|
|
177
|
-
opts.db.select({ count: count() }).from(sqlAcceptInvites).where(eq(sqlAcceptInvites.token, req.token)).get()
|
|
178
|
-
);
|
|
179
|
-
if (rCount.isErr()) {
|
|
180
|
-
return Result.Err(`Failed to count accept invites: ${rCount.Err().message}`);
|
|
181
|
-
}
|
|
182
|
-
const accepted = rCount.Ok()?.count ?? 0;
|
|
183
|
-
|
|
184
|
-
switch (true) {
|
|
185
|
-
case isInviteEmailToken(invite):
|
|
186
|
-
if (accepted >= 1) {
|
|
187
|
-
return Result.Err(`Email invite has already been accepted`);
|
|
188
|
-
}
|
|
189
|
-
break;
|
|
190
|
-
case isInviteLinkToken(invite):
|
|
191
|
-
if (accepted >= invite.acceptCount) {
|
|
192
|
-
return Result.Err(`Link invite has reached its accept limit (${invite.acceptCount})`);
|
|
193
|
-
}
|
|
194
|
-
break;
|
|
195
|
-
default:
|
|
196
|
-
return Result.Err(`Invalid invite token style`);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
const newInfo: (typeof AcceptedClerkInfo.infer)[] = [];
|
|
200
|
-
|
|
201
|
-
if (isInviteLinkToken(invite)) {
|
|
202
|
-
newInfo.push({
|
|
203
|
-
type: "accepted-clerk-info" as const,
|
|
204
|
-
email: req.auth.verifiedAuth.claims.params.email,
|
|
205
|
-
nick: req.auth.verifiedAuth.claims.params.nick ?? req.auth.verifiedAuth.claims.params.name ?? undefined,
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// unique index on (token, acceptUserId) prevents the same user accepting twice
|
|
210
|
-
const rInsert = await exception2Result(() =>
|
|
211
|
-
opts.db
|
|
212
|
-
.insert(sqlAcceptInvites)
|
|
213
|
-
.values({
|
|
214
|
-
acceptId: opts.nextId(),
|
|
215
|
-
token: req.token,
|
|
216
|
-
acceptUserId,
|
|
217
|
-
acceptedInfo: newInfo,
|
|
218
|
-
created: now.toISOString(),
|
|
219
|
-
})
|
|
220
|
-
.run()
|
|
221
|
-
);
|
|
222
|
-
if (rInsert.isErr()) {
|
|
223
|
-
return Result.Err(`Failed to record accept invite: ${rInsert.Err().message}`);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
return Result.Ok({
|
|
227
|
-
type: "res.accept-invite" as const,
|
|
228
|
-
token: req.token,
|
|
229
|
-
appSlug: invite.appSlug,
|
|
230
|
-
userSlug: invite.userSlug,
|
|
231
|
-
roles: invite.roles,
|
|
232
|
-
acceptedInfo: newInfo,
|
|
233
|
-
});
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
export async function listAcceptedInvites(
|
|
237
|
-
req: ReqWithVerifiedAuth<ReqListAcceptedInvites>,
|
|
238
|
-
opts: InviteSystemOpts
|
|
239
|
-
): Promise<Result<ResListAcceptedInvites>> {
|
|
240
|
-
const userId = req.auth.verifiedAuth.claims.userId;
|
|
241
|
-
|
|
242
|
-
if (req.slugs.length === 0) {
|
|
243
|
-
return Result.Ok({ type: "res.list-accepted-invites" as const, items: [] });
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
const rRows = await exception2Result(() =>
|
|
247
|
-
opts.db
|
|
248
|
-
.select()
|
|
249
|
-
.from(sqlInviteTokens)
|
|
250
|
-
.leftJoin(sqlAcceptInvites, eq(sqlInviteTokens.token, sqlAcceptInvites.token))
|
|
251
|
-
.where(
|
|
252
|
-
and(
|
|
253
|
-
eq(sqlInviteTokens.ownerUserId, userId),
|
|
254
|
-
or(...req.slugs.map((s) => and(eq(sqlInviteTokens.appSlug, s.appSlug), eq(sqlInviteTokens.userSlug, s.userSlug))))
|
|
255
|
-
)
|
|
256
|
-
)
|
|
257
|
-
.all()
|
|
258
|
-
);
|
|
259
|
-
if (rRows.isErr()) {
|
|
260
|
-
return Result.Err(`Failed to query invites: ${rRows.Err().message}`);
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
// Group accept rows by invite token
|
|
264
|
-
const inviteMap = new Map<
|
|
265
|
-
string,
|
|
266
|
-
{ inviteRow: typeof sqlInviteTokens.$inferSelect; acceptRows: (typeof sqlAcceptInvites.$inferSelect)[] }
|
|
267
|
-
>();
|
|
268
|
-
for (const row of rRows.Ok()) {
|
|
269
|
-
const inviteRow = row.InviteTokens;
|
|
270
|
-
if (!inviteMap.has(inviteRow.token)) {
|
|
271
|
-
inviteMap.set(inviteRow.token, { inviteRow, acceptRows: [] });
|
|
272
|
-
}
|
|
273
|
-
if (row.AcceptInvites) {
|
|
274
|
-
const entry = inviteMap.get(inviteRow.token);
|
|
275
|
-
if (entry) entry.acceptRows.push(row.AcceptInvites);
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// Build InviteWithAccepts items
|
|
280
|
-
const items: InviteWithAccepts[] = [];
|
|
281
|
-
for (const { inviteRow, acceptRows } of inviteMap.values()) {
|
|
282
|
-
const inviteParams = InviteToken(inviteRow.style);
|
|
283
|
-
if (inviteParams instanceof type.errors) continue;
|
|
284
|
-
|
|
285
|
-
const accepts = acceptRows.flatMap((ar) => {
|
|
286
|
-
const acceptedInfo = AcceptedClerkInfo(ar.acceptedInfo);
|
|
287
|
-
if (acceptedInfo instanceof type.errors) return [];
|
|
288
|
-
return [{ acceptId: ar.acceptId, token: ar.token, acceptedInfo, created: new Date(ar.created) }];
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
items.push({ inviteParams, accepts });
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
return Result.Ok({ type: "res.list-accepted-invites" as const, items });
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
export async function deleteAccept(
|
|
298
|
-
req: ReqWithVerifiedAuth<ReqDeleteAccept>,
|
|
299
|
-
opts: InviteSystemOpts
|
|
300
|
-
): Promise<Result<ResDeleteAccept>> {
|
|
301
|
-
const rDelete = await exception2Result(() =>
|
|
302
|
-
opts.db.delete(sqlAcceptInvites).where(eq(sqlAcceptInvites.acceptId, req.acceptId)).run()
|
|
303
|
-
);
|
|
304
|
-
if (rDelete.isErr()) {
|
|
305
|
-
return Result.Err(`Failed to delete accept: ${rDelete.Err().message}`);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
return Result.Ok({ type: "res.delete-accept" as const, acceptId: req.acceptId });
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
export async function getFPToken(req: ReqWithVerifiedAuth<ReqGetFPToken>, opts: InviteSystemOpts): Promise<Result<ResFPToken>> {
|
|
312
|
-
const userId = req.auth.verifiedAuth.claims.userId;
|
|
313
|
-
|
|
314
|
-
// Owner path: user owns the slug binding
|
|
315
|
-
const rDirect = await exception2Result(() =>
|
|
316
|
-
opts.db
|
|
317
|
-
.select()
|
|
318
|
-
.from(sqlUserSlugBinding)
|
|
319
|
-
.innerJoin(sqlAppSlugBinding, eq(sqlUserSlugBinding.userSlug, sqlAppSlugBinding.userSlug))
|
|
320
|
-
.where(
|
|
321
|
-
and(
|
|
322
|
-
eq(sqlUserSlugBinding.userId, userId),
|
|
323
|
-
eq(sqlUserSlugBinding.userSlug, req.userSlug),
|
|
324
|
-
eq(sqlAppSlugBinding.appSlug, req.appSlug)
|
|
325
|
-
)
|
|
326
|
-
)
|
|
327
|
-
.get()
|
|
328
|
-
);
|
|
329
|
-
if (rDirect.isErr()) {
|
|
330
|
-
return Result.Err(`Failed to query owner token: ${rDirect.Err().message}`);
|
|
331
|
-
}
|
|
332
|
-
const directRow = rDirect.Ok();
|
|
333
|
-
if (directRow) {
|
|
334
|
-
console.log("owner-token", req.DbName, req.appSlug, req.userSlug);
|
|
335
|
-
return Result.Ok({
|
|
336
|
-
type: "res.get-fp-token" as const,
|
|
337
|
-
ledger: directRow.AppSlugBindings.ledger,
|
|
338
|
-
tenant: directRow.UserSlugBindings.tenant,
|
|
339
|
-
roles: [],
|
|
340
|
-
access: "owner" as const,
|
|
341
|
-
token: req.DbName,
|
|
342
|
-
});
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
// Shared path: user accepted an invite for the given appSlug/userSlug
|
|
346
|
-
const now = opts.now ?? new Date();
|
|
347
|
-
const rShared = await exception2Result(() =>
|
|
348
|
-
opts.db
|
|
349
|
-
.select()
|
|
350
|
-
.from(sqlInviteTokens)
|
|
351
|
-
.innerJoin(sqlAcceptInvites, eq(sqlInviteTokens.token, sqlAcceptInvites.token))
|
|
352
|
-
.innerJoin(sqlUserSlugBinding, eq(sqlInviteTokens.userSlug, sqlUserSlugBinding.userSlug))
|
|
353
|
-
.innerJoin(
|
|
354
|
-
sqlAppSlugBinding,
|
|
355
|
-
and(eq(sqlInviteTokens.appSlug, sqlAppSlugBinding.appSlug), eq(sqlInviteTokens.userSlug, sqlAppSlugBinding.userSlug))
|
|
356
|
-
)
|
|
357
|
-
.where(
|
|
358
|
-
and(
|
|
359
|
-
eq(sqlInviteTokens.appSlug, req.appSlug),
|
|
360
|
-
eq(sqlInviteTokens.userSlug, req.userSlug),
|
|
361
|
-
eq(sqlAcceptInvites.acceptUserId, userId)
|
|
362
|
-
)
|
|
363
|
-
)
|
|
364
|
-
.get()
|
|
365
|
-
);
|
|
366
|
-
if (rShared.isErr()) {
|
|
367
|
-
return Result.Err(`Failed to query shared token: ${rShared.Err().message}`);
|
|
368
|
-
}
|
|
369
|
-
const sharedRow = rShared.Ok();
|
|
370
|
-
if (!sharedRow) {
|
|
371
|
-
return Result.Err(`No access found for ${req.appSlug}/${req.userSlug}`);
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
if (new Date(sharedRow.InviteTokens.validUntil) < now) {
|
|
375
|
-
return Result.Err(`Invite token has expired`);
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
const invite = InviteToken(sharedRow.InviteTokens.style);
|
|
379
|
-
if (invite instanceof type.errors) {
|
|
380
|
-
return Result.Err(`Invalid stored invite token: ${invite.summary}`);
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
console.log("shared-token", req.DbName, req.appSlug, req.userSlug);
|
|
384
|
-
return Result.Ok({
|
|
385
|
-
type: "res.get-fp-token" as const,
|
|
386
|
-
ledger: sharedRow.AppSlugBindings.ledger,
|
|
387
|
-
tenant: sharedRow.UserSlugBindings.tenant,
|
|
388
|
-
roles: invite.roles,
|
|
389
|
-
access: "shared" as const,
|
|
390
|
-
token: req.DbName,
|
|
391
|
-
});
|
|
392
|
-
}
|