@odla-ai/chapter 0.42.1 → 0.42.2
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/README.md +13 -1
- package/dist/{chunk-MMDWXTJU.js → chunk-HWUTCXBV.js} +44 -1
- package/dist/chunk-HWUTCXBV.js.map +1 -0
- package/dist/{copy-context-BNUzbL5g.d.ts → copy-context-BBPsYe24.d.ts} +9 -1
- package/dist/index.cjs +88 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -2
- package/dist/index.d.ts +10 -2
- package/dist/index.js +88 -10
- package/dist/index.js.map +1 -1
- package/dist/ui/admin/index.d.ts +14 -3
- package/dist/ui/admin/index.js +3 -1
- package/dist/ui/index.d.ts +2 -2
- package/dist/ui/index.js +3 -1
- package/dist/ui/member/index.d.ts +2 -2
- package/dist/worker/index.cjs +477 -144
- package/dist/worker/index.cjs.map +1 -1
- package/dist/worker/index.d.cts +55 -2
- package/dist/worker/index.d.ts +55 -2
- package/dist/worker/index.js +453 -120
- package/dist/worker/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-MMDWXTJU.js.map +0 -1
package/dist/worker/index.js
CHANGED
|
@@ -607,6 +607,11 @@ function validateAdmissionGrantDocument(input) {
|
|
|
607
607
|
}
|
|
608
608
|
};
|
|
609
609
|
}
|
|
610
|
+
async function createAdmissionGrantEnvelope(document) {
|
|
611
|
+
const checked = validateAdmissionGrantDocument(document);
|
|
612
|
+
if (!checked.ok) throw new TypeError(`Invalid admission grant document: ${checked.errors.join("; ")}`);
|
|
613
|
+
return { document: checked.value, digest: await signupControlDigest(checked.value) };
|
|
614
|
+
}
|
|
610
615
|
var rows = (value) => Array.isArray(value) ? value : [];
|
|
611
616
|
function asGrant(row) {
|
|
612
617
|
if (typeof row.id !== "string" || typeof row.emailNormalized !== "string") return null;
|
|
@@ -3110,12 +3115,17 @@ import { verifyFederatedRequest as verifyFederatedRequest4 } from "@odla-ai/db";
|
|
|
3110
3115
|
var reject3 = (reason, status, errors) => json({ ok: false, reason, ...errors ? { errors } : {} }, status);
|
|
3111
3116
|
var handleAdmissionGrants = async (req, url, env, ctx) => {
|
|
3112
3117
|
if (req.method !== "POST" || url.pathname !== ADMISSION_GRANTS_PATH) return null;
|
|
3113
|
-
const
|
|
3114
|
-
|
|
3118
|
+
const claimedSender = req.headers.get("x-odla-federation-sender") ?? "";
|
|
3119
|
+
const networkReader = ctx.chapter.network.readers.find((reader) => reader.id === claimedSender && reader.admissionGrants);
|
|
3120
|
+
const signupAuthority = ctx.chapter.signupControl?.sourceId === claimedSender ? ctx.chapter.signupControl : void 0;
|
|
3121
|
+
if (!networkReader && !signupAuthority) return json({ error: "not found" }, 404);
|
|
3115
3122
|
const db = ctx.makeDb(env);
|
|
3116
|
-
const secret = await getVaultSecret(
|
|
3123
|
+
const secret = await getVaultSecret(
|
|
3124
|
+
db,
|
|
3125
|
+
networkReader ? "network_share_secret" : signupAuthority.secretName
|
|
3126
|
+
);
|
|
3117
3127
|
if (!secret) return json({ error: "unauthorized" }, 401);
|
|
3118
|
-
const verified = await verifyFederatedRequest4(req, { secret, senders: [
|
|
3128
|
+
const verified = await verifyFederatedRequest4(req, { secret, senders: [claimedSender] });
|
|
3119
3129
|
if (!verified.ok) return json({ error: "unauthorized", reason: verified.reason }, 401);
|
|
3120
3130
|
let input;
|
|
3121
3131
|
try {
|
|
@@ -3127,11 +3137,11 @@ var handleAdmissionGrants = async (req, url, env, ctx) => {
|
|
|
3127
3137
|
const checked = validateAdmissionGrantDocument(input.document);
|
|
3128
3138
|
if (!checked.ok) return reject3("invalid_document", 400, checked.errors);
|
|
3129
3139
|
const document = checked.value;
|
|
3130
|
-
if (document.source.appId !==
|
|
3140
|
+
if (document.source.appId !== claimedSender) return reject3("wrong_source", 403);
|
|
3131
3141
|
if (document.target.chapterId !== ctx.chapter.id) return reject3("wrong_target", 409);
|
|
3132
3142
|
if (document.target.environment !== env.ODLA_ENV) return reject3("wrong_environment", 409);
|
|
3133
3143
|
if (!digest || await signupControlDigest(document) !== digest) return reject3("invalid_digest", 400);
|
|
3134
|
-
const latest = await loadLatestAdmissionRevision(db,
|
|
3144
|
+
const latest = await loadLatestAdmissionRevision(db, claimedSender, ctx.chapter.id, env.ODLA_ENV);
|
|
3135
3145
|
if (latest && document.revision < latest.revision) return reject3("stale_revision", 409);
|
|
3136
3146
|
if (latest && document.revision === latest.revision) {
|
|
3137
3147
|
if (latest.digest !== digest) return reject3("conflicting_revision", 409);
|
|
@@ -3256,6 +3266,365 @@ var handleAdminAdmissionGrants = async (req, url, env, ctx) => {
|
|
|
3256
3266
|
return null;
|
|
3257
3267
|
};
|
|
3258
3268
|
|
|
3269
|
+
// src/worker-routes-network-admission.ts
|
|
3270
|
+
import { getRecord as getRecord2 } from "@odla-ai/crm";
|
|
3271
|
+
|
|
3272
|
+
// src/admission-grants-delivery.ts
|
|
3273
|
+
import { signFederatedRequest } from "@odla-ai/db";
|
|
3274
|
+
var AdmissionGrantDeliveryError = class extends Error {
|
|
3275
|
+
constructor(status, reason) {
|
|
3276
|
+
super(`admission grants rejected (${status}): ${reason}`);
|
|
3277
|
+
this.status = status;
|
|
3278
|
+
this.reason = reason;
|
|
3279
|
+
this.name = "AdmissionGrantDeliveryError";
|
|
3280
|
+
}
|
|
3281
|
+
status;
|
|
3282
|
+
reason;
|
|
3283
|
+
};
|
|
3284
|
+
async function deliverAdmissionGrants(options) {
|
|
3285
|
+
const destination = new URL(ADMISSION_GRANTS_PATH, options.targetUrl);
|
|
3286
|
+
const body = JSON.stringify(options.envelope);
|
|
3287
|
+
const signed = await signFederatedRequest({
|
|
3288
|
+
secret: options.secret,
|
|
3289
|
+
sender: options.sender,
|
|
3290
|
+
method: "POST",
|
|
3291
|
+
url: destination,
|
|
3292
|
+
body
|
|
3293
|
+
});
|
|
3294
|
+
const response = await (options.fetch ?? globalThis.fetch)(destination, {
|
|
3295
|
+
method: "POST",
|
|
3296
|
+
headers: { ...signed, "content-type": "application/json" },
|
|
3297
|
+
body
|
|
3298
|
+
});
|
|
3299
|
+
const result = await response.json();
|
|
3300
|
+
if (!response.ok || !("ok" in result) || result.ok !== true) {
|
|
3301
|
+
const failure = result;
|
|
3302
|
+
throw new AdmissionGrantDeliveryError(
|
|
3303
|
+
response.status,
|
|
3304
|
+
failure.reason ?? failure.error ?? "unknown"
|
|
3305
|
+
);
|
|
3306
|
+
}
|
|
3307
|
+
return result;
|
|
3308
|
+
}
|
|
3309
|
+
|
|
3310
|
+
// src/worker-network-fetch.ts
|
|
3311
|
+
function targetFetcher(env, target) {
|
|
3312
|
+
if (!target.binding) return null;
|
|
3313
|
+
const candidate = env[target.binding];
|
|
3314
|
+
if (typeof candidate !== "object" || candidate === null || !("fetch" in candidate) || typeof candidate.fetch !== "function") {
|
|
3315
|
+
throw new Error(`service binding "${target.binding}" is unavailable`);
|
|
3316
|
+
}
|
|
3317
|
+
return candidate;
|
|
3318
|
+
}
|
|
3319
|
+
function fetchNetworkTarget(env, target, destination, init) {
|
|
3320
|
+
const binding = targetFetcher(env, target);
|
|
3321
|
+
return binding ? binding.fetch(new Request(destination, init)) : globalThis.fetch(destination, init);
|
|
3322
|
+
}
|
|
3323
|
+
|
|
3324
|
+
// src/worker-network-admission-state.ts
|
|
3325
|
+
import { getRecord } from "@odla-ai/crm";
|
|
3326
|
+
var MAX_ASSIGNMENTS = 2e3;
|
|
3327
|
+
var admissionRows = (value) => Array.isArray(value) ? value : [];
|
|
3328
|
+
function assignmentId(targetId, personId) {
|
|
3329
|
+
return `leader-admission:${targetId}:${personId}`;
|
|
3330
|
+
}
|
|
3331
|
+
function deliveryId(targetId, environment) {
|
|
3332
|
+
return `leader-admission-delivery:${targetId}:${environment}`;
|
|
3333
|
+
}
|
|
3334
|
+
async function loadLeaderAdmissionAssignments(db, targetId) {
|
|
3335
|
+
const result = await db.query({ leaderAdmissionAssignments: { $: {
|
|
3336
|
+
where: { targetId },
|
|
3337
|
+
order: { updatedAt: "desc" },
|
|
3338
|
+
limit: MAX_ASSIGNMENTS
|
|
3339
|
+
} } });
|
|
3340
|
+
return admissionRows(result.leaderAdmissionAssignments);
|
|
3341
|
+
}
|
|
3342
|
+
async function loadLeaderAdmissionDelivery(db, targetId, environment) {
|
|
3343
|
+
const id = deliveryId(targetId, environment);
|
|
3344
|
+
const result = await db.query({ leaderAdmissionDeliveries: { $: { where: { id }, limit: 1 } } });
|
|
3345
|
+
return admissionRows(result.leaderAdmissionDeliveries)[0] ?? null;
|
|
3346
|
+
}
|
|
3347
|
+
function personAdmissionEmail(ctx, record4) {
|
|
3348
|
+
if (record4.type !== "person") return null;
|
|
3349
|
+
const field = ctx.chapter.crm.config.types.person?.emailField;
|
|
3350
|
+
if (!field) return null;
|
|
3351
|
+
const raw = record4.fields[field] ?? record4.primaryEmail;
|
|
3352
|
+
if (typeof raw !== "string") return null;
|
|
3353
|
+
const normalized = normalizeAdmissionEmail(raw);
|
|
3354
|
+
return isValidEmail(normalized) ? normalized : null;
|
|
3355
|
+
}
|
|
3356
|
+
async function grantInput(db, ctx, assignment) {
|
|
3357
|
+
const person = await getRecord({ crm: ctx.chapter.crm, db }, assignment.personId);
|
|
3358
|
+
const email = person ? personAdmissionEmail(ctx, person) : null;
|
|
3359
|
+
if (!email) throw new Error(`person ${assignment.personId} has no valid authoritative email`);
|
|
3360
|
+
return {
|
|
3361
|
+
id: assignment.grantId,
|
|
3362
|
+
email,
|
|
3363
|
+
decision: assignment.decision,
|
|
3364
|
+
...assignment.note ? { note: assignment.note } : {},
|
|
3365
|
+
...assignment.expiresAt !== void 0 ? { expiresAt: assignment.expiresAt } : {}
|
|
3366
|
+
};
|
|
3367
|
+
}
|
|
3368
|
+
async function persistLeaderAdmissionDesiredSet(opts) {
|
|
3369
|
+
const now = Date.now();
|
|
3370
|
+
const current = await loadLeaderAdmissionAssignments(opts.db, opts.target.id);
|
|
3371
|
+
const id = assignmentId(opts.target.id, opts.personId);
|
|
3372
|
+
const existing = current.find((row) => row.id === id);
|
|
3373
|
+
const digest = await signupControlDigest({
|
|
3374
|
+
targetId: opts.target.id,
|
|
3375
|
+
personId: opts.personId,
|
|
3376
|
+
decision: opts.decision,
|
|
3377
|
+
note: opts.note ?? null,
|
|
3378
|
+
expiresAt: opts.expiresAt ?? null,
|
|
3379
|
+
active: opts.active
|
|
3380
|
+
});
|
|
3381
|
+
const mutationReceiptId = `leader-admission-mutation:${opts.target.id}:${opts.mutationId}`;
|
|
3382
|
+
const priorMutation2 = await opts.db.query({ leaderAdmissionMutations: { $: {
|
|
3383
|
+
where: { id: mutationReceiptId },
|
|
3384
|
+
limit: 1
|
|
3385
|
+
} } });
|
|
3386
|
+
const receipt = admissionRows(priorMutation2.leaderAdmissionMutations)[0];
|
|
3387
|
+
if (receipt) {
|
|
3388
|
+
if (receipt.requestDigest !== digest || receipt.personId !== opts.personId) {
|
|
3389
|
+
throw new Error("mutation_conflict");
|
|
3390
|
+
}
|
|
3391
|
+
const saved = await loadLeaderAdmissionDelivery(opts.db, opts.target.id, opts.env.ODLA_ENV);
|
|
3392
|
+
if (!saved) throw new Error("delivery_missing");
|
|
3393
|
+
return { delivery: saved, duplicate: true };
|
|
3394
|
+
}
|
|
3395
|
+
if (existing?.lastMutationId === opts.mutationId) {
|
|
3396
|
+
if (existing.lastRequestDigest !== digest) throw new Error("mutation_conflict");
|
|
3397
|
+
const saved = await loadLeaderAdmissionDelivery(opts.db, opts.target.id, opts.env.ODLA_ENV);
|
|
3398
|
+
if (!saved) throw new Error("delivery_missing");
|
|
3399
|
+
return { delivery: saved, duplicate: true };
|
|
3400
|
+
}
|
|
3401
|
+
const assignment = {
|
|
3402
|
+
id,
|
|
3403
|
+
targetId: opts.target.id,
|
|
3404
|
+
personId: opts.personId,
|
|
3405
|
+
grantId: existing?.grantId ?? crypto.randomUUID().replaceAll("-", ""),
|
|
3406
|
+
decision: opts.decision,
|
|
3407
|
+
...opts.note ? { note: opts.note } : {},
|
|
3408
|
+
...opts.expiresAt !== void 0 ? { expiresAt: opts.expiresAt } : {},
|
|
3409
|
+
active: opts.active,
|
|
3410
|
+
lastMutationId: opts.mutationId,
|
|
3411
|
+
lastRequestDigest: digest,
|
|
3412
|
+
createdAt: existing?.createdAt ?? now,
|
|
3413
|
+
createdBy: existing?.createdBy ?? opts.actor,
|
|
3414
|
+
updatedAt: now,
|
|
3415
|
+
updatedBy: opts.actor
|
|
3416
|
+
};
|
|
3417
|
+
const desired = current.filter((row) => row.id !== id && row.active).concat(assignment.active ? [assignment] : []);
|
|
3418
|
+
const grants = await Promise.all(desired.map((row) => grantInput(opts.db, opts.ctx, row)));
|
|
3419
|
+
const previous = await loadLeaderAdmissionDelivery(opts.db, opts.target.id, opts.env.ODLA_ENV);
|
|
3420
|
+
const revision = (previous?.revision ?? 0) + 1;
|
|
3421
|
+
const envelope = await createAdmissionGrantEnvelope({
|
|
3422
|
+
schemaVersion: 1,
|
|
3423
|
+
revision,
|
|
3424
|
+
generatedAt: now,
|
|
3425
|
+
source: { appId: opts.ctx.chapter.id, actor: opts.actor },
|
|
3426
|
+
target: { chapterId: opts.target.id, environment: opts.env.ODLA_ENV },
|
|
3427
|
+
grants
|
|
3428
|
+
});
|
|
3429
|
+
const delivery = {
|
|
3430
|
+
id: deliveryId(opts.target.id, opts.env.ODLA_ENV),
|
|
3431
|
+
targetId: opts.target.id,
|
|
3432
|
+
environment: opts.env.ODLA_ENV,
|
|
3433
|
+
revision,
|
|
3434
|
+
digest: envelope.digest,
|
|
3435
|
+
envelope,
|
|
3436
|
+
status: "saved",
|
|
3437
|
+
attempts: 0,
|
|
3438
|
+
updatedAt: now
|
|
3439
|
+
};
|
|
3440
|
+
const ops = [
|
|
3441
|
+
{ t: "update", ns: "leaderAdmissionAssignments", id, attrs: assignment },
|
|
3442
|
+
{ t: "update", ns: "leaderAdmissionDeliveries", id: delivery.id, attrs: delivery },
|
|
3443
|
+
{ t: "update", ns: "leaderAdmissionMutations", id: mutationReceiptId, attrs: {
|
|
3444
|
+
id: mutationReceiptId,
|
|
3445
|
+
targetId: opts.target.id,
|
|
3446
|
+
personId: opts.personId,
|
|
3447
|
+
requestDigest: digest,
|
|
3448
|
+
revision,
|
|
3449
|
+
createdAt: now
|
|
3450
|
+
} }
|
|
3451
|
+
];
|
|
3452
|
+
const retract = [
|
|
3453
|
+
...!opts.note && existing?.note !== void 0 ? ["note"] : [],
|
|
3454
|
+
...opts.expiresAt === void 0 && existing?.expiresAt !== void 0 ? ["expiresAt"] : []
|
|
3455
|
+
];
|
|
3456
|
+
if (retract.length) ops.push({ t: "retract", ns: "leaderAdmissionAssignments", id, attrs: retract });
|
|
3457
|
+
const tx = await opts.db.transact(ops, {
|
|
3458
|
+
mutationId: `leader-admission:${opts.target.id}:${opts.mutationId}`,
|
|
3459
|
+
guards: previous ? [{ ns: "leaderAdmissionDeliveries", id: previous.id, exists: true, equals: { revision: previous.revision } }] : [{ ns: "leaderAdmissionDeliveries", id: delivery.id, exists: false }]
|
|
3460
|
+
});
|
|
3461
|
+
return { delivery, duplicate: tx.duplicate };
|
|
3462
|
+
}
|
|
3463
|
+
|
|
3464
|
+
// src/worker-routes-network-admission.ts
|
|
3465
|
+
var PATH = "/api/admin/network/admissions";
|
|
3466
|
+
var RETRY_PATH = "/api/admin/network/admissions/retry";
|
|
3467
|
+
var REVOKE_PATH = "/api/admin/network/admissions/revoke";
|
|
3468
|
+
var MUTATION_ID = /^[A-Za-z0-9_-]{16,128}$/;
|
|
3469
|
+
var MAX_NOTE2 = 2e3;
|
|
3470
|
+
var reject4 = (status, code, error) => json({ ok: false, code, error }, status);
|
|
3471
|
+
async function gate2(req, env, ctx) {
|
|
3472
|
+
const db = ctx.makeDb(env);
|
|
3473
|
+
const user = await ctx.verifyUser(req, env);
|
|
3474
|
+
if (!user) return { response: reject4(401, "unauthorized", "unauthorized") };
|
|
3475
|
+
if (!await ctx.isAdmin(db, user)) return { response: reject4(403, "forbidden", "forbidden") };
|
|
3476
|
+
return { db, user };
|
|
3477
|
+
}
|
|
3478
|
+
function targetFor(ctx, id) {
|
|
3479
|
+
return ctx.chapter.network.targets.find((target) => target.id === id && target.admissionGrants);
|
|
3480
|
+
}
|
|
3481
|
+
async function deliverSaved(db, env, ctx, target, delivery) {
|
|
3482
|
+
if (delivery.status === "acknowledged") return delivery;
|
|
3483
|
+
const secret = await getVaultSecret(db, target.secretName);
|
|
3484
|
+
const attemptedAt = Date.now();
|
|
3485
|
+
const attempts = delivery.attempts + 1;
|
|
3486
|
+
let patch;
|
|
3487
|
+
if (!secret) {
|
|
3488
|
+
patch = {
|
|
3489
|
+
status: delivery.attempts > 0 ? "retrying" : "pending",
|
|
3490
|
+
attempts,
|
|
3491
|
+
lastAttemptAt: attemptedAt,
|
|
3492
|
+
lastError: "edge secret is missing",
|
|
3493
|
+
updatedAt: attemptedAt
|
|
3494
|
+
};
|
|
3495
|
+
} else {
|
|
3496
|
+
try {
|
|
3497
|
+
const receipt = await deliverAdmissionGrants({
|
|
3498
|
+
secret,
|
|
3499
|
+
sender: ctx.chapter.id,
|
|
3500
|
+
targetUrl: target.url,
|
|
3501
|
+
envelope: delivery.envelope,
|
|
3502
|
+
fetch: ((input, init) => fetchNetworkTarget(env, target, new URL(String(input)), init ?? {}))
|
|
3503
|
+
});
|
|
3504
|
+
patch = {
|
|
3505
|
+
status: "acknowledged",
|
|
3506
|
+
attempts,
|
|
3507
|
+
lastAttemptAt: attemptedAt,
|
|
3508
|
+
acknowledgedAt: receipt.acknowledgedAt,
|
|
3509
|
+
updatedAt: attemptedAt
|
|
3510
|
+
};
|
|
3511
|
+
} catch (error) {
|
|
3512
|
+
const permanent = error instanceof AdmissionGrantDeliveryError && error.status >= 400 && error.status < 500;
|
|
3513
|
+
patch = {
|
|
3514
|
+
status: permanent ? "rejected" : delivery.attempts > 0 ? "retrying" : "pending",
|
|
3515
|
+
attempts,
|
|
3516
|
+
lastAttemptAt: attemptedAt,
|
|
3517
|
+
lastError: error instanceof Error ? error.message.slice(0, 2e3) : "delivery failed",
|
|
3518
|
+
updatedAt: attemptedAt
|
|
3519
|
+
};
|
|
3520
|
+
}
|
|
3521
|
+
}
|
|
3522
|
+
const ops = [{
|
|
3523
|
+
t: "update",
|
|
3524
|
+
ns: "leaderAdmissionDeliveries",
|
|
3525
|
+
id: delivery.id,
|
|
3526
|
+
attrs: patch
|
|
3527
|
+
}];
|
|
3528
|
+
if (patch.status === "acknowledged" && delivery.lastError) {
|
|
3529
|
+
ops.push({ t: "retract", ns: "leaderAdmissionDeliveries", id: delivery.id, attrs: ["lastError"] });
|
|
3530
|
+
}
|
|
3531
|
+
await db.transact(ops, {
|
|
3532
|
+
mutationId: `leader-admission-attempt:${delivery.targetId}:${delivery.revision}:${attempts}:${patch.status}`
|
|
3533
|
+
});
|
|
3534
|
+
return { ...delivery, ...patch };
|
|
3535
|
+
}
|
|
3536
|
+
async function parseBody(req) {
|
|
3537
|
+
try {
|
|
3538
|
+
return await req.json();
|
|
3539
|
+
} catch {
|
|
3540
|
+
return null;
|
|
3541
|
+
}
|
|
3542
|
+
}
|
|
3543
|
+
var handleAdminNetworkAdmissions = async (req, url, env, ctx) => {
|
|
3544
|
+
if (![PATH, RETRY_PATH, REVOKE_PATH].includes(url.pathname)) return null;
|
|
3545
|
+
const auth = await gate2(req, env, ctx);
|
|
3546
|
+
if ("response" in auth) return auth.response;
|
|
3547
|
+
const db = auth.db;
|
|
3548
|
+
if (url.pathname === PATH && req.method === "GET") {
|
|
3549
|
+
const targetId2 = url.searchParams.get("targetId") ?? "";
|
|
3550
|
+
const target2 = targetFor(ctx, targetId2);
|
|
3551
|
+
if (!target2) return reject4(404, "unknown_target", "admission target is not configured");
|
|
3552
|
+
return json({
|
|
3553
|
+
ok: true,
|
|
3554
|
+
target: { id: target2.id, name: target2.name },
|
|
3555
|
+
assignments: await loadLeaderAdmissionAssignments(db, target2.id),
|
|
3556
|
+
delivery: await loadLeaderAdmissionDelivery(db, target2.id, env.ODLA_ENV)
|
|
3557
|
+
});
|
|
3558
|
+
}
|
|
3559
|
+
if (req.method !== "POST") return null;
|
|
3560
|
+
const body = await parseBody(req);
|
|
3561
|
+
if (!body) return reject4(422, "invalid_request", "request body must be JSON");
|
|
3562
|
+
const targetId = typeof body.targetId === "string" ? body.targetId : "";
|
|
3563
|
+
const target = targetFor(ctx, targetId);
|
|
3564
|
+
if (!target) return reject4(404, "unknown_target", "admission target is not configured");
|
|
3565
|
+
if (url.pathname === RETRY_PATH) {
|
|
3566
|
+
const delivery = await loadLeaderAdmissionDelivery(db, target.id, env.ODLA_ENV);
|
|
3567
|
+
if (!delivery) return reject4(404, "not_found", "no saved admission delivery exists");
|
|
3568
|
+
return json({ ok: true, delivery: await deliverSaved(db, env, ctx, target, delivery) });
|
|
3569
|
+
}
|
|
3570
|
+
const personId = typeof body.personId === "string" ? body.personId : "";
|
|
3571
|
+
const mutationId = typeof body.mutationId === "string" ? body.mutationId : "";
|
|
3572
|
+
if (!personId || !MUTATION_ID.test(mutationId)) {
|
|
3573
|
+
return reject4(422, "invalid_request", "personId and a stable mutationId are required");
|
|
3574
|
+
}
|
|
3575
|
+
const person = await getRecord2({ crm: ctx.chapter.crm, db: auth.db }, personId);
|
|
3576
|
+
if (!person) return reject4(404, "person_not_found", "person not found");
|
|
3577
|
+
if (!personAdmissionEmail(ctx, person)) {
|
|
3578
|
+
return reject4(422, "invalid_email", "person has no valid authoritative email");
|
|
3579
|
+
}
|
|
3580
|
+
const decision = body.decision ?? "waive_interview";
|
|
3581
|
+
if (decision !== "waive_interview" && decision !== "require_interview") {
|
|
3582
|
+
return reject4(422, "invalid_decision", "decision must be waive_interview or require_interview");
|
|
3583
|
+
}
|
|
3584
|
+
const note = typeof body.note === "string" ? body.note.trim() : "";
|
|
3585
|
+
if (note.length > MAX_NOTE2) return reject4(422, "invalid_note", "note is too long");
|
|
3586
|
+
const expiresAt = body.expiresAt == null ? void 0 : Number(body.expiresAt);
|
|
3587
|
+
if (expiresAt !== void 0 && (!Number.isFinite(expiresAt) || expiresAt <= Date.now())) {
|
|
3588
|
+
return reject4(422, "invalid_expiry", "expiresAt must be a future epoch timestamp");
|
|
3589
|
+
}
|
|
3590
|
+
try {
|
|
3591
|
+
const saved = await persistLeaderAdmissionDesiredSet({
|
|
3592
|
+
db,
|
|
3593
|
+
ctx,
|
|
3594
|
+
env,
|
|
3595
|
+
target,
|
|
3596
|
+
personId,
|
|
3597
|
+
decision,
|
|
3598
|
+
...note ? { note } : {},
|
|
3599
|
+
...expiresAt !== void 0 ? { expiresAt } : {},
|
|
3600
|
+
active: url.pathname !== REVOKE_PATH,
|
|
3601
|
+
mutationId,
|
|
3602
|
+
actor: auth.user.email ?? auth.user.userId
|
|
3603
|
+
});
|
|
3604
|
+
const delivery = await deliverSaved(db, env, ctx, target, saved.delivery);
|
|
3605
|
+
return json({ ok: true, duplicate: saved.duplicate, delivery }, delivery.status === "acknowledged" ? 200 : 202);
|
|
3606
|
+
} catch (error) {
|
|
3607
|
+
if (error instanceof Error && error.message === "mutation_conflict") {
|
|
3608
|
+
return reject4(409, "mutation_conflict", "mutationId was already used for another request");
|
|
3609
|
+
}
|
|
3610
|
+
if (error instanceof Error && /valid authoritative email/.test(error.message)) {
|
|
3611
|
+
return reject4(422, "invalid_email", error.message);
|
|
3612
|
+
}
|
|
3613
|
+
return reject4(409, "revision_conflict", "admission assignments changed; reload and retry");
|
|
3614
|
+
}
|
|
3615
|
+
};
|
|
3616
|
+
async function retryLeaderAdmissionDeliveries(env, ctx, limit = 100) {
|
|
3617
|
+
const db = ctx.makeDb(env);
|
|
3618
|
+
const result = await db.query({ leaderAdmissionDeliveries: { $: { limit } } });
|
|
3619
|
+
const pending = admissionRows(result.leaderAdmissionDeliveries).filter((delivery) => delivery.status !== "acknowledged" && delivery.status !== "rejected").slice(0, Math.max(1, Math.min(limit, 500)));
|
|
3620
|
+
const delivered = [];
|
|
3621
|
+
for (const item of pending) {
|
|
3622
|
+
const target = targetFor(ctx, item.targetId);
|
|
3623
|
+
if (target) delivered.push(await deliverSaved(db, env, ctx, target, item));
|
|
3624
|
+
}
|
|
3625
|
+
return delivered;
|
|
3626
|
+
}
|
|
3627
|
+
|
|
3259
3628
|
// src/worker-routes-schedule.ts
|
|
3260
3629
|
import { computeBookableSlots, initCalendar } from "@odla-ai/calendar";
|
|
3261
3630
|
function errCode(err) {
|
|
@@ -4517,16 +4886,16 @@ var crmDeps2 = (db, ctx) => ({
|
|
|
4517
4886
|
});
|
|
4518
4887
|
var handleAdminCrmSync = async (req, url, env, ctx) => {
|
|
4519
4888
|
if (req.method !== "POST" || url.pathname !== "/api/admin/crm/sync") return null;
|
|
4520
|
-
const
|
|
4521
|
-
if (
|
|
4522
|
-
const result = await backfillCrm(crmDeps2(
|
|
4889
|
+
const gate10 = await adminGate(req, env, ctx);
|
|
4890
|
+
if (gate10 instanceof Response) return gate10;
|
|
4891
|
+
const result = await backfillCrm(crmDeps2(gate10.db, ctx));
|
|
4523
4892
|
return json({ ok: true, ...result });
|
|
4524
4893
|
};
|
|
4525
4894
|
var handleAdminPeople = async (req, url, env, ctx) => {
|
|
4526
4895
|
if (req.method !== "GET" || url.pathname !== "/api/admin/people") return null;
|
|
4527
|
-
const
|
|
4528
|
-
if (
|
|
4529
|
-
const { db } =
|
|
4896
|
+
const gate10 = await adminGate(req, env, ctx);
|
|
4897
|
+
if (gate10 instanceof Response) return gate10;
|
|
4898
|
+
const { db } = gate10;
|
|
4530
4899
|
const sk = await getVaultSecret(db, "clerk_secret_key");
|
|
4531
4900
|
const [data, roleList] = await Promise.all([
|
|
4532
4901
|
db.query({
|
|
@@ -4579,9 +4948,9 @@ var handleAdminPeople = async (req, url, env, ctx) => {
|
|
|
4579
4948
|
};
|
|
4580
4949
|
var handleAdminPeopleAccess = async (req, url, env, ctx) => {
|
|
4581
4950
|
if (req.method !== "GET" || url.pathname !== "/api/admin/people/access") return null;
|
|
4582
|
-
const
|
|
4583
|
-
if (
|
|
4584
|
-
const { db } =
|
|
4951
|
+
const gate10 = await adminGate(req, env, ctx);
|
|
4952
|
+
if (gate10 instanceof Response) return gate10;
|
|
4953
|
+
const { db } = gate10;
|
|
4585
4954
|
const targetId = url.searchParams.get("userId") ?? "";
|
|
4586
4955
|
if (!targetId.startsWith("user_")) return json({ error: "invalid userId" }, 400);
|
|
4587
4956
|
const sk = await getVaultSecret(db, "clerk_secret_key");
|
|
@@ -4592,9 +4961,9 @@ var handleAdminPeopleAccess = async (req, url, env, ctx) => {
|
|
|
4592
4961
|
};
|
|
4593
4962
|
var handleAdminPeopleRole = async (req, url, env, ctx) => {
|
|
4594
4963
|
if (req.method !== "POST" || url.pathname !== "/api/admin/people/role") return null;
|
|
4595
|
-
const
|
|
4596
|
-
if (
|
|
4597
|
-
const { db, actor } =
|
|
4964
|
+
const gate10 = await adminGate(req, env, ctx);
|
|
4965
|
+
if (gate10 instanceof Response) return gate10;
|
|
4966
|
+
const { db, actor } = gate10;
|
|
4598
4967
|
let body;
|
|
4599
4968
|
try {
|
|
4600
4969
|
body = await req.json();
|
|
@@ -4741,7 +5110,7 @@ function subAnnualCents(sub) {
|
|
|
4741
5110
|
}
|
|
4742
5111
|
|
|
4743
5112
|
// src/worker-routes-admin-dashboard.ts
|
|
4744
|
-
async function
|
|
5113
|
+
async function gate3(req, env, ctx) {
|
|
4745
5114
|
const rawDb = ctx.makeDb(env);
|
|
4746
5115
|
const u = await ctx.verifyUser(req, env);
|
|
4747
5116
|
if (!u) return json({ error: "unauthorized" }, 401);
|
|
@@ -4751,7 +5120,7 @@ async function gate2(req, env, ctx) {
|
|
|
4751
5120
|
var rows3 = (v) => Array.isArray(v) ? v : [];
|
|
4752
5121
|
var handleAdminDashboard = async (req, url, env, ctx) => {
|
|
4753
5122
|
if (req.method !== "GET" || url.pathname !== "/api/admin/dashboard") return null;
|
|
4754
|
-
const got = await
|
|
5123
|
+
const got = await gate3(req, env, ctx);
|
|
4755
5124
|
if (got instanceof Response) return got;
|
|
4756
5125
|
const db = got;
|
|
4757
5126
|
const now = Date.now();
|
|
@@ -4834,7 +5203,7 @@ var handleAdminDashboard = async (req, url, env, ctx) => {
|
|
|
4834
5203
|
};
|
|
4835
5204
|
var handleAdminBilling = async (req, url, env, ctx) => {
|
|
4836
5205
|
if (req.method !== "GET" || url.pathname !== "/api/admin/billing") return null;
|
|
4837
|
-
const got = await
|
|
5206
|
+
const got = await gate3(req, env, ctx);
|
|
4838
5207
|
if (got instanceof Response) return got;
|
|
4839
5208
|
const db = got;
|
|
4840
5209
|
const sk = await getVaultSecret(db, "stripe_secret_key");
|
|
@@ -4894,7 +5263,7 @@ var handleAdminBilling = async (req, url, env, ctx) => {
|
|
|
4894
5263
|
|
|
4895
5264
|
// src/worker-routes-admin-lifecycle.ts
|
|
4896
5265
|
import { computeBookableSlots as computeBookableSlots2, initCalendar as initCalendar3 } from "@odla-ai/calendar";
|
|
4897
|
-
async function
|
|
5266
|
+
async function gate4(req, env, ctx) {
|
|
4898
5267
|
const rawDb = ctx.makeDb(env);
|
|
4899
5268
|
const u = await ctx.verifyUser(req, env);
|
|
4900
5269
|
if (!u) return json({ error: "unauthorized" }, 401);
|
|
@@ -4920,7 +5289,7 @@ async function loadGroup(db, id) {
|
|
|
4920
5289
|
var handleAdminMeetingReschedule = async (req, url, env, ctx) => {
|
|
4921
5290
|
const m = url.pathname.match(/^\/api\/admin\/meetings\/([0-9a-f-]+)\/reschedule$/);
|
|
4922
5291
|
if (req.method !== "POST" || !m) return null;
|
|
4923
|
-
const got = await
|
|
5292
|
+
const got = await gate4(req, env, ctx);
|
|
4924
5293
|
if (got instanceof Response) return got;
|
|
4925
5294
|
const db = got;
|
|
4926
5295
|
const body = await readJson(req);
|
|
@@ -4961,7 +5330,7 @@ var handleAdminMeetingReschedule = async (req, url, env, ctx) => {
|
|
|
4961
5330
|
var handleAdminMeetingCancel = async (req, url, env, ctx) => {
|
|
4962
5331
|
const m = url.pathname.match(/^\/api\/admin\/meetings\/([0-9a-f-]+)\/cancel$/);
|
|
4963
5332
|
if (req.method !== "POST" || !m) return null;
|
|
4964
|
-
const got = await
|
|
5333
|
+
const got = await gate4(req, env, ctx);
|
|
4965
5334
|
if (got instanceof Response) return got;
|
|
4966
5335
|
const db = got;
|
|
4967
5336
|
const { meetings } = await db.query({ meetings: { $: { where: { id: m[1] }, limit: 1 } } });
|
|
@@ -4984,7 +5353,7 @@ var handleAdminMeetingCancel = async (req, url, env, ctx) => {
|
|
|
4984
5353
|
var handleAdminApprove = async (req, url, env, ctx) => {
|
|
4985
5354
|
const m = url.pathname.match(/^\/api\/admin\/applications\/([0-9a-f-]+)\/approve$/);
|
|
4986
5355
|
if (req.method !== "POST" || !m) return null;
|
|
4987
|
-
const got = await
|
|
5356
|
+
const got = await gate4(req, env, ctx);
|
|
4988
5357
|
if (got instanceof Response) return got;
|
|
4989
5358
|
const db = got;
|
|
4990
5359
|
const id = m[1];
|
|
@@ -4997,7 +5366,7 @@ var handleAdminApprove = async (req, url, env, ctx) => {
|
|
|
4997
5366
|
var handleAdminRefund = async (req, url, env, ctx) => {
|
|
4998
5367
|
const m = url.pathname.match(/^\/api\/admin\/applications\/([0-9a-f-]+)\/refund$/);
|
|
4999
5368
|
if (req.method !== "POST" || !m) return null;
|
|
5000
|
-
const got = await
|
|
5369
|
+
const got = await gate4(req, env, ctx);
|
|
5001
5370
|
if (got instanceof Response) return got;
|
|
5002
5371
|
const db = got;
|
|
5003
5372
|
const app = await loadApp(db, m[1]);
|
|
@@ -5029,7 +5398,7 @@ var handleAdminRefund = async (req, url, env, ctx) => {
|
|
|
5029
5398
|
var handleAdminApplicationPatch = async (req, url, env, ctx) => {
|
|
5030
5399
|
const m = url.pathname.match(/^\/api\/admin\/applications\/([0-9a-f-]+)$/);
|
|
5031
5400
|
if (req.method !== "PATCH" || !m) return null;
|
|
5032
|
-
const got = await
|
|
5401
|
+
const got = await gate4(req, env, ctx);
|
|
5033
5402
|
if (got instanceof Response) return got;
|
|
5034
5403
|
const db = got;
|
|
5035
5404
|
const id = m[1];
|
|
@@ -5057,7 +5426,7 @@ var handleAdminApplicationPatch = async (req, url, env, ctx) => {
|
|
|
5057
5426
|
};
|
|
5058
5427
|
|
|
5059
5428
|
// src/worker-routes-admin-comms.ts
|
|
5060
|
-
async function
|
|
5429
|
+
async function gate5(req, env, ctx) {
|
|
5061
5430
|
const rawDb = ctx.makeDb(env);
|
|
5062
5431
|
const u = await ctx.verifyUser(req, env);
|
|
5063
5432
|
if (!u) return json({ error: "unauthorized" }, 401);
|
|
@@ -5079,7 +5448,7 @@ var notifyDeps = (db, env) => ({
|
|
|
5079
5448
|
});
|
|
5080
5449
|
var handleAdminGroupEmail = async (req, url, env, ctx) => {
|
|
5081
5450
|
if (url.pathname !== "/api/admin/group/email" || req.method !== "GET" && req.method !== "PUT") return null;
|
|
5082
|
-
const got = await
|
|
5451
|
+
const got = await gate5(req, env, ctx);
|
|
5083
5452
|
if (got instanceof Response) return got;
|
|
5084
5453
|
const db = got;
|
|
5085
5454
|
const group = await loadGroup2(db, ctx.chapter.id);
|
|
@@ -5141,7 +5510,7 @@ var handleAdminGroupEmail = async (req, url, env, ctx) => {
|
|
|
5141
5510
|
};
|
|
5142
5511
|
var handleAdminEmailLog = async (req, url, env, ctx) => {
|
|
5143
5512
|
if (req.method !== "GET" || url.pathname !== "/api/admin/email/log") return null;
|
|
5144
|
-
const got = await
|
|
5513
|
+
const got = await gate5(req, env, ctx);
|
|
5145
5514
|
if (got instanceof Response) return got;
|
|
5146
5515
|
const { emailLog } = await got.query({ emailLog: { $: { order: { sentAt: "desc" }, limit: 50 } } });
|
|
5147
5516
|
const sends = (emailLog ?? []).map((r) => ({
|
|
@@ -5158,7 +5527,7 @@ var handleAdminEmailLog = async (req, url, env, ctx) => {
|
|
|
5158
5527
|
};
|
|
5159
5528
|
var handleAdminEmailTest = async (req, url, env, ctx) => {
|
|
5160
5529
|
if (req.method !== "POST" || url.pathname !== "/api/admin/email/test") return null;
|
|
5161
|
-
const got = await
|
|
5530
|
+
const got = await gate5(req, env, ctx);
|
|
5162
5531
|
if (got instanceof Response) return got;
|
|
5163
5532
|
const db = got;
|
|
5164
5533
|
let body;
|
|
@@ -5186,7 +5555,7 @@ var handleAdminEmailTest = async (req, url, env, ctx) => {
|
|
|
5186
5555
|
var handleAdminComms = async (req, url, env, ctx) => {
|
|
5187
5556
|
const m = url.pathname.match(/^\/api\/admin\/people\/([0-9a-fA-F-]+)\/comms$/);
|
|
5188
5557
|
if (req.method !== "GET" || !m) return null;
|
|
5189
|
-
const got = await
|
|
5558
|
+
const got = await gate5(req, env, ctx);
|
|
5190
5559
|
if (got instanceof Response) return got;
|
|
5191
5560
|
const db = got;
|
|
5192
5561
|
const appId = m[1];
|
|
@@ -5224,26 +5593,12 @@ var handleAdminComms = async (req, url, env, ctx) => {
|
|
|
5224
5593
|
};
|
|
5225
5594
|
|
|
5226
5595
|
// src/worker-routes-network.ts
|
|
5227
|
-
import { getRecord, typeSummary } from "@odla-ai/crm";
|
|
5228
|
-
import { signFederatedRequest as
|
|
5229
|
-
|
|
5230
|
-
// src/worker-network-fetch.ts
|
|
5231
|
-
function targetFetcher(env, target) {
|
|
5232
|
-
if (!target.binding) return null;
|
|
5233
|
-
const candidate = env[target.binding];
|
|
5234
|
-
if (typeof candidate !== "object" || candidate === null || !("fetch" in candidate) || typeof candidate.fetch !== "function") {
|
|
5235
|
-
throw new Error(`service binding "${target.binding}" is unavailable`);
|
|
5236
|
-
}
|
|
5237
|
-
return candidate;
|
|
5238
|
-
}
|
|
5239
|
-
function fetchNetworkTarget(env, target, destination, init) {
|
|
5240
|
-
const binding = targetFetcher(env, target);
|
|
5241
|
-
return binding ? binding.fetch(new Request(destination, init)) : globalThis.fetch(destination, init);
|
|
5242
|
-
}
|
|
5596
|
+
import { getRecord as getRecord3, typeSummary } from "@odla-ai/crm";
|
|
5597
|
+
import { signFederatedRequest as signFederatedRequest3, verifyFederatedRequest as verifyFederatedRequest5 } from "@odla-ai/db";
|
|
5243
5598
|
|
|
5244
5599
|
// src/worker-network-push.ts
|
|
5245
5600
|
import { recordDeliveryAttempt } from "@odla-ai/crm";
|
|
5246
|
-
import { signFederatedRequest } from "@odla-ai/db";
|
|
5601
|
+
import { signFederatedRequest as signFederatedRequest2 } from "@odla-ai/db";
|
|
5247
5602
|
async function pushNetworkRecord(db, env, ctx, target, record4) {
|
|
5248
5603
|
const secret = await getVaultSecret(db, target.secretName);
|
|
5249
5604
|
const crmDeps3 = {
|
|
@@ -5277,7 +5632,7 @@ async function pushNetworkRecord(db, env, ctx, target, record4) {
|
|
|
5277
5632
|
const payloadBody = JSON.stringify(payload);
|
|
5278
5633
|
const destination = new URL("/api/network/shared", target.url);
|
|
5279
5634
|
try {
|
|
5280
|
-
const signed = await
|
|
5635
|
+
const signed = await signFederatedRequest2({
|
|
5281
5636
|
secret,
|
|
5282
5637
|
sender: ctx.chapter.id,
|
|
5283
5638
|
method: "POST",
|
|
@@ -5336,7 +5691,7 @@ async function pushNetworkRecord(db, env, ctx, target, record4) {
|
|
|
5336
5691
|
}
|
|
5337
5692
|
|
|
5338
5693
|
// src/worker-routes-network.ts
|
|
5339
|
-
async function
|
|
5694
|
+
async function gate6(req, env, ctx) {
|
|
5340
5695
|
const db = ctx.makeDb(env);
|
|
5341
5696
|
const user = await ctx.verifyUser(req, env);
|
|
5342
5697
|
if (!user) return { response: json({ error: "unauthorized" }, 401) };
|
|
@@ -5345,7 +5700,7 @@ async function gate5(req, env, ctx) {
|
|
|
5345
5700
|
}
|
|
5346
5701
|
var handleAdminNetworkTargets = async (req, url, env, ctx) => {
|
|
5347
5702
|
if (req.method !== "GET" || url.pathname !== "/api/admin/network/targets") return null;
|
|
5348
|
-
const got = await
|
|
5703
|
+
const got = await gate6(req, env, ctx);
|
|
5349
5704
|
if ("response" in got) return got.response;
|
|
5350
5705
|
return json({
|
|
5351
5706
|
targets: ctx.chapter.network.targets.map(({
|
|
@@ -5390,7 +5745,7 @@ async function snapshotOne(db, env, ctx, target) {
|
|
|
5390
5745
|
if (!secret) return { id: target.id, name: target.name, url: target.url, available: false, error: "edge secret is missing" };
|
|
5391
5746
|
const destination = new URL("/api/network/snapshot", target.url);
|
|
5392
5747
|
try {
|
|
5393
|
-
const headers = await
|
|
5748
|
+
const headers = await signFederatedRequest3({
|
|
5394
5749
|
secret,
|
|
5395
5750
|
sender: ctx.chapter.id,
|
|
5396
5751
|
method: "GET",
|
|
@@ -5431,7 +5786,7 @@ async function snapshotOne(db, env, ctx, target) {
|
|
|
5431
5786
|
}
|
|
5432
5787
|
var handleAdminNetworkRollup = async (req, url, env, ctx) => {
|
|
5433
5788
|
if (req.method !== "GET" || url.pathname !== "/api/admin/network/rollup") return null;
|
|
5434
|
-
const got = await
|
|
5789
|
+
const got = await gate6(req, env, ctx);
|
|
5435
5790
|
if ("response" in got) return got.response;
|
|
5436
5791
|
const targets = await Promise.all(
|
|
5437
5792
|
ctx.chapter.network.targets.map((target) => snapshotOne(got.db, env, ctx, target))
|
|
@@ -5458,7 +5813,7 @@ var handleAdminNetworkRollup = async (req, url, env, ctx) => {
|
|
|
5458
5813
|
};
|
|
5459
5814
|
var handleAdminNetworkPush = async (req, url, env, ctx) => {
|
|
5460
5815
|
if (req.method !== "POST" || url.pathname !== "/api/admin/network/push") return null;
|
|
5461
|
-
const got = await
|
|
5816
|
+
const got = await gate6(req, env, ctx);
|
|
5462
5817
|
if ("response" in got) return got.response;
|
|
5463
5818
|
let body;
|
|
5464
5819
|
try {
|
|
@@ -5473,7 +5828,7 @@ var handleAdminNetworkPush = async (req, url, env, ctx) => {
|
|
|
5473
5828
|
if (requested.size !== body.targetIds.length) return json({ error: "targetIds must contain unique strings" }, 400);
|
|
5474
5829
|
const targets = ctx.chapter.network.targets.filter((target) => requested.has(target.id));
|
|
5475
5830
|
if (targets.length !== requested.size) return json({ error: "one or more targetIds are not configured" }, 400);
|
|
5476
|
-
const record4 = await
|
|
5831
|
+
const record4 = await getRecord3({ crm: ctx.chapter.crm, db: got.db }, body.recordId);
|
|
5477
5832
|
if (!record4) return json({ error: "record not found" }, 404);
|
|
5478
5833
|
const results = await Promise.all(
|
|
5479
5834
|
targets.map((target) => pushNetworkRecord(got.db, env, ctx, target, record4))
|
|
@@ -5482,7 +5837,7 @@ var handleAdminNetworkPush = async (req, url, env, ctx) => {
|
|
|
5482
5837
|
};
|
|
5483
5838
|
|
|
5484
5839
|
// src/worker-routes-network-records-follower.ts
|
|
5485
|
-
import { CRM_NS, CrmInputError, getRecord as
|
|
5840
|
+
import { CRM_NS, CrmInputError, getRecord as getRecord4, listRecords, setStage as setStage2, updateRecord as updateRecord3 } from "@odla-ai/crm";
|
|
5486
5841
|
import { verifyFederatedRequest as verifyFederatedRequest6 } from "@odla-ai/db";
|
|
5487
5842
|
|
|
5488
5843
|
// src/worker-routes-network-records-common.ts
|
|
@@ -5492,7 +5847,7 @@ var MAX_OFFSET = 1e4;
|
|
|
5492
5847
|
var MAX_SEARCH = 200;
|
|
5493
5848
|
var MAX_BODY = 32768;
|
|
5494
5849
|
var RECORD_ID = /^[a-zA-Z0-9][a-zA-Z0-9:_-]{0,191}$/;
|
|
5495
|
-
var
|
|
5850
|
+
var MUTATION_ID2 = /^[a-zA-Z0-9][a-zA-Z0-9:._-]{7,127}$/;
|
|
5496
5851
|
function bodyTooLarge(body) {
|
|
5497
5852
|
return new TextEncoder().encode(body).byteLength > MAX_BODY;
|
|
5498
5853
|
}
|
|
@@ -5610,7 +5965,7 @@ var handleNetworkRecords = async (req, url, env, ctx) => {
|
|
|
5610
5965
|
const hasFields = Boolean(fields && typeof fields === "object" && !Array.isArray(fields));
|
|
5611
5966
|
const hasStage = typeof input?.stage === "string";
|
|
5612
5967
|
const actor = input?.actor;
|
|
5613
|
-
if (!input || !IDENTIFIER.test(input.type) || !RECORD_ID.test(input.recordId) || !Number.isSafeInteger(input.expectedRevision) || input.expectedRevision < 0 || !
|
|
5968
|
+
if (!input || !IDENTIFIER.test(input.type) || !RECORD_ID.test(input.recordId) || !Number.isSafeInteger(input.expectedRevision) || input.expectedRevision < 0 || !MUTATION_ID2.test(input.mutationId) || !actor || typeof actor.id !== "string" || !RECORD_ID.test(actor.id) || hasFields === hasStage) {
|
|
5614
5969
|
return json({ error: "invalid network mutation", code: "invalid_mutation" }, 422);
|
|
5615
5970
|
}
|
|
5616
5971
|
const editable = reader.editableFields[input.type];
|
|
@@ -5629,7 +5984,7 @@ var handleNetworkRecords = async (req, url, env, ctx) => {
|
|
|
5629
5984
|
if (prior.fingerprint !== fingerprint) {
|
|
5630
5985
|
return json({ error: "mutationId was already used for different input", code: "mutation_conflict" }, 409);
|
|
5631
5986
|
}
|
|
5632
|
-
const replayed = await
|
|
5987
|
+
const replayed = await getRecord4({ crm: ctx.chapter.crm, db }, input.recordId);
|
|
5633
5988
|
if (!replayed || replayed.type !== input.type) return json({ error: "record not found", code: "not_found" }, 404);
|
|
5634
5989
|
const result = {
|
|
5635
5990
|
version: 1,
|
|
@@ -5639,7 +5994,7 @@ var handleNetworkRecords = async (req, url, env, ctx) => {
|
|
|
5639
5994
|
};
|
|
5640
5995
|
return json(result);
|
|
5641
5996
|
}
|
|
5642
|
-
const record4 = await
|
|
5997
|
+
const record4 = await getRecord4({ crm: ctx.chapter.crm, db }, input.recordId);
|
|
5643
5998
|
if (!record4 || record4.type !== input.type) return json({ error: "record not found", code: "not_found" }, 404);
|
|
5644
5999
|
if (record4.updatedAt !== input.expectedRevision) {
|
|
5645
6000
|
return json({ error: "record changed since it was reviewed", code: "stale_revision" }, 409);
|
|
@@ -5724,15 +6079,15 @@ var handleNetworkRecords = async (req, url, env, ctx) => {
|
|
|
5724
6079
|
};
|
|
5725
6080
|
|
|
5726
6081
|
// src/worker-routes-network-records-admin.ts
|
|
5727
|
-
import { signFederatedRequest as
|
|
5728
|
-
async function
|
|
6082
|
+
import { signFederatedRequest as signFederatedRequest4 } from "@odla-ai/db";
|
|
6083
|
+
async function gate7(req, env, ctx) {
|
|
5729
6084
|
const db = ctx.makeDb(env);
|
|
5730
6085
|
const user = await ctx.verifyUser(req, env);
|
|
5731
6086
|
if (!user) return { response: json({ error: "unauthorized" }, 401) };
|
|
5732
6087
|
if (!await ctx.isAdmin(db, user)) return { response: json({ error: "forbidden" }, 403) };
|
|
5733
6088
|
return { db, user };
|
|
5734
6089
|
}
|
|
5735
|
-
function
|
|
6090
|
+
function targetFor2(ctx, id) {
|
|
5736
6091
|
return ctx.chapter.network.targets.find((target) => target.id === id);
|
|
5737
6092
|
}
|
|
5738
6093
|
async function readTargetRecords(db, env, ctx, target, query) {
|
|
@@ -5741,7 +6096,7 @@ async function readTargetRecords(db, env, ctx, target, query) {
|
|
|
5741
6096
|
const destination = new URL("/api/network/records", target.url);
|
|
5742
6097
|
destination.search = query.toString();
|
|
5743
6098
|
try {
|
|
5744
|
-
const headers = await
|
|
6099
|
+
const headers = await signFederatedRequest4({ secret, sender: ctx.chapter.id, method: "GET", url: destination });
|
|
5745
6100
|
const response = await fetchNetworkTarget(env, target, destination, {
|
|
5746
6101
|
headers,
|
|
5747
6102
|
signal: AbortSignal.timeout(1e4)
|
|
@@ -5777,7 +6132,7 @@ async function mutateTargetRecord(db, env, ctx, target, input) {
|
|
|
5777
6132
|
const destination = new URL("/api/network/records", target.url);
|
|
5778
6133
|
const body = JSON.stringify(input);
|
|
5779
6134
|
try {
|
|
5780
|
-
const headers = await
|
|
6135
|
+
const headers = await signFederatedRequest4({
|
|
5781
6136
|
secret,
|
|
5782
6137
|
sender: ctx.chapter.id,
|
|
5783
6138
|
method: "PATCH",
|
|
@@ -5809,7 +6164,7 @@ var handleAdminNetworkRecords = async (req, url, env, ctx) => {
|
|
|
5809
6164
|
if (req.method === "PATCH" && Number(req.headers.get("content-length") ?? 0) > MAX_BODY) {
|
|
5810
6165
|
return json({ error: "mutation body is too large", code: "body_too_large" }, 413);
|
|
5811
6166
|
}
|
|
5812
|
-
const got = await
|
|
6167
|
+
const got = await gate7(req, env, ctx);
|
|
5813
6168
|
if ("response" in got) return got.response;
|
|
5814
6169
|
if (req.method === "PATCH") {
|
|
5815
6170
|
let input;
|
|
@@ -5822,10 +6177,10 @@ var handleAdminNetworkRecords = async (req, url, env, ctx) => {
|
|
|
5822
6177
|
} catch {
|
|
5823
6178
|
return json({ error: "invalid JSON body", code: "invalid_json" }, 422);
|
|
5824
6179
|
}
|
|
5825
|
-
const target2 =
|
|
6180
|
+
const target2 = targetFor2(ctx, input?.targetId);
|
|
5826
6181
|
const hasFields = Boolean(input?.fields && typeof input.fields === "object" && !Array.isArray(input.fields));
|
|
5827
6182
|
const hasStage = typeof input?.stage === "string";
|
|
5828
|
-
if (!target2 || !IDENTIFIER.test(input.type) || !RECORD_ID.test(input.recordId) || !Number.isSafeInteger(input.expectedRevision) || input.expectedRevision < 0 || !
|
|
6183
|
+
if (!target2 || !IDENTIFIER.test(input.type) || !RECORD_ID.test(input.recordId) || !Number.isSafeInteger(input.expectedRevision) || input.expectedRevision < 0 || !MUTATION_ID2.test(input.mutationId) || hasFields === hasStage) {
|
|
5829
6184
|
return json({ error: "invalid network mutation", code: "invalid_mutation" }, 422);
|
|
5830
6185
|
}
|
|
5831
6186
|
return mutateTargetRecord(got.db, env, ctx, target2, {
|
|
@@ -5840,7 +6195,7 @@ var handleAdminNetworkRecords = async (req, url, env, ctx) => {
|
|
|
5840
6195
|
}
|
|
5841
6196
|
const targetId = url.searchParams.get("targetId") ?? "";
|
|
5842
6197
|
const type = url.searchParams.get("type") ?? "";
|
|
5843
|
-
const target =
|
|
6198
|
+
const target = targetFor2(ctx, targetId);
|
|
5844
6199
|
if (!target || !IDENTIFIER.test(type)) return json({ error: "configured targetId and record type are required" }, 400);
|
|
5845
6200
|
const limit = integer(url.searchParams.get("limit"), 25, 1, MAX_PAGE);
|
|
5846
6201
|
const offset = integer(url.searchParams.get("offset"), 0, 0, MAX_OFFSET);
|
|
@@ -5857,8 +6212,8 @@ var handleAdminNetworkRecords = async (req, url, env, ctx) => {
|
|
|
5857
6212
|
// src/worker-routes-network-notes.ts
|
|
5858
6213
|
var IDENTIFIER2 = /^[a-z][a-zA-Z0-9_]*$/;
|
|
5859
6214
|
var RECORD_ID2 = /^[A-Za-z0-9][A-Za-z0-9_.:-]{0,255}$/;
|
|
5860
|
-
var
|
|
5861
|
-
async function
|
|
6215
|
+
var MAX_NOTE3 = 4e3;
|
|
6216
|
+
async function gate8(req, env, ctx) {
|
|
5862
6217
|
const db = ctx.makeDb(env);
|
|
5863
6218
|
const user = await ctx.verifyUser(req, env);
|
|
5864
6219
|
if (!user) return { response: json({ error: "unauthorized" }, 401) };
|
|
@@ -5892,7 +6247,7 @@ function asNote(row) {
|
|
|
5892
6247
|
}
|
|
5893
6248
|
var handleAdminNetworkNotes = async (req, url, env, ctx) => {
|
|
5894
6249
|
if (url.pathname !== "/api/admin/network/notes" || req.method !== "GET" && req.method !== "POST") return null;
|
|
5895
|
-
const got = await
|
|
6250
|
+
const got = await gate8(req, env, ctx);
|
|
5896
6251
|
if ("response" in got) return got.response;
|
|
5897
6252
|
const identity = noteIdentity(url, ctx);
|
|
5898
6253
|
if (!identity) return json({ error: "valid targetId, type, and recordId are required" }, 400);
|
|
@@ -5909,7 +6264,7 @@ var handleAdminNetworkNotes = async (req, url, env, ctx) => {
|
|
|
5909
6264
|
return json({ notes: (result.networkNotes ?? []).map((row) => asNote(row)) });
|
|
5910
6265
|
}
|
|
5911
6266
|
const contentLength = Number(req.headers.get("content-length") ?? 0);
|
|
5912
|
-
if (contentLength >
|
|
6267
|
+
if (contentLength > MAX_NOTE3 * 2) return json({ error: "note is too large" }, 413);
|
|
5913
6268
|
let input;
|
|
5914
6269
|
try {
|
|
5915
6270
|
input = await req.json();
|
|
@@ -5917,7 +6272,7 @@ var handleAdminNetworkNotes = async (req, url, env, ctx) => {
|
|
|
5917
6272
|
return json({ error: "invalid JSON body" }, 400);
|
|
5918
6273
|
}
|
|
5919
6274
|
const body = typeof input.body === "string" ? input.body.trim() : "";
|
|
5920
|
-
if (!body || body.length >
|
|
6275
|
+
if (!body || body.length > MAX_NOTE3) return json({ error: `note must be 1\u2013${MAX_NOTE3} characters` }, 400);
|
|
5921
6276
|
const id = crypto.randomUUID();
|
|
5922
6277
|
const note = {
|
|
5923
6278
|
id,
|
|
@@ -5942,15 +6297,15 @@ var handleAdminNetworkNotes = async (req, url, env, ctx) => {
|
|
|
5942
6297
|
import {
|
|
5943
6298
|
addActivity,
|
|
5944
6299
|
CRM_NS as CRM_NS2,
|
|
5945
|
-
getRecord as
|
|
6300
|
+
getRecord as getRecord5
|
|
5946
6301
|
} from "@odla-ai/crm";
|
|
5947
|
-
import { signFederatedRequest as
|
|
6302
|
+
import { signFederatedRequest as signFederatedRequest5, verifyFederatedRequest as verifyFederatedRequest7 } from "@odla-ai/db";
|
|
5948
6303
|
var IDENTIFIER3 = /^[a-z][a-zA-Z0-9_]*$/;
|
|
5949
6304
|
var RECORD_ID3 = /^[A-Za-z0-9][A-Za-z0-9_.:-]{0,255}$/;
|
|
5950
|
-
var
|
|
5951
|
-
var
|
|
6305
|
+
var MUTATION_ID3 = /^[A-Za-z0-9_-]{16,128}$/;
|
|
6306
|
+
var MAX_NOTE4 = 4e3;
|
|
5952
6307
|
var NETWORK_META = "chapterNetwork";
|
|
5953
|
-
async function
|
|
6308
|
+
async function gate9(req, env, ctx) {
|
|
5954
6309
|
const db = ctx.makeDb(env);
|
|
5955
6310
|
const user = await ctx.verifyUser(req, env);
|
|
5956
6311
|
if (!user) return { response: json({ error: "unauthorized" }, 401) };
|
|
@@ -5996,7 +6351,7 @@ function sourceLabel(sourceId) {
|
|
|
5996
6351
|
}
|
|
5997
6352
|
var handleNetworkSharedNotes = async (req, url, env, ctx) => {
|
|
5998
6353
|
if (url.pathname !== "/api/network/shared-notes" || req.method !== "GET" && req.method !== "POST") return null;
|
|
5999
|
-
if (req.method === "POST" && Number(req.headers.get("content-length") ?? 0) >
|
|
6354
|
+
if (req.method === "POST" && Number(req.headers.get("content-length") ?? 0) > MAX_NOTE4 * 2) {
|
|
6000
6355
|
return json({ error: "note is too large" }, 413);
|
|
6001
6356
|
}
|
|
6002
6357
|
const db = ctx.makeDb(env);
|
|
@@ -6014,7 +6369,7 @@ var handleNetworkSharedNotes = async (req, url, env, ctx) => {
|
|
|
6014
6369
|
if (!IDENTIFIER3.test(recordType) || !RECORD_ID3.test(recordId) || !reader?.sharedNotes.includes(recordType)) {
|
|
6015
6370
|
return json({ error: "shared notes are not enabled for this reader and record type" }, 403);
|
|
6016
6371
|
}
|
|
6017
|
-
const record4 = await
|
|
6372
|
+
const record4 = await getRecord5({ crm: ctx.chapter.crm, db }, recordId);
|
|
6018
6373
|
if (!record4 || record4.type !== recordType) return json({ error: "record not found" }, 404);
|
|
6019
6374
|
if (req.method === "GET") {
|
|
6020
6375
|
return json({
|
|
@@ -6031,8 +6386,8 @@ var handleNetworkSharedNotes = async (req, url, env, ctx) => {
|
|
|
6031
6386
|
}
|
|
6032
6387
|
const body = typeof input.body === "string" ? input.body.trim() : "";
|
|
6033
6388
|
const mutationId = typeof input.mutationId === "string" ? input.mutationId : "";
|
|
6034
|
-
if (!body || body.length >
|
|
6035
|
-
return json({ error: `body must be 1\u2013${
|
|
6389
|
+
if (!body || body.length > MAX_NOTE4 || !MUTATION_ID3.test(mutationId)) {
|
|
6390
|
+
return json({ error: `body must be 1\u2013${MAX_NOTE4} characters and mutationId must be valid` }, 400);
|
|
6036
6391
|
}
|
|
6037
6392
|
const createdAt = Date.now();
|
|
6038
6393
|
const id = `network:${verified.sender}:${mutationId}`;
|
|
@@ -6069,17 +6424,17 @@ var handleNetworkSharedNotes = async (req, url, env, ctx) => {
|
|
|
6069
6424
|
};
|
|
6070
6425
|
return json({ version: 1, site: { id: ctx.chapter.id, name: ctx.chapter.name }, note, duplicate: result.duplicate }, 201);
|
|
6071
6426
|
};
|
|
6072
|
-
function
|
|
6427
|
+
function targetFor3(ctx, id) {
|
|
6073
6428
|
return ctx.chapter.network.targets.find((target) => target.id === id);
|
|
6074
6429
|
}
|
|
6075
6430
|
var handleAdminNetworkSharedNotes = async (req, url, env, ctx) => {
|
|
6076
6431
|
if (url.pathname !== "/api/admin/network/shared-notes" || req.method !== "GET" && req.method !== "POST") return null;
|
|
6077
|
-
const got = await
|
|
6432
|
+
const got = await gate9(req, env, ctx);
|
|
6078
6433
|
if ("response" in got) return got.response;
|
|
6079
6434
|
const targetId = url.searchParams.get("targetId") ?? "";
|
|
6080
6435
|
const recordType = url.searchParams.get("type") ?? "";
|
|
6081
6436
|
const recordId = url.searchParams.get("recordId") ?? "";
|
|
6082
|
-
const target =
|
|
6437
|
+
const target = targetFor3(ctx, targetId);
|
|
6083
6438
|
if (!target || !target.sharedNotes.includes(recordType) || !IDENTIFIER3.test(recordType) || !RECORD_ID3.test(recordId)) {
|
|
6084
6439
|
return json({ error: "configured targetId, type, and recordId are required" }, 400);
|
|
6085
6440
|
}
|
|
@@ -6092,7 +6447,7 @@ var handleAdminNetworkSharedNotes = async (req, url, env, ctx) => {
|
|
|
6092
6447
|
return json({ error: "invalid JSON body" }, 400);
|
|
6093
6448
|
}
|
|
6094
6449
|
const note = typeof input.body === "string" ? input.body.trim() : "";
|
|
6095
|
-
if (!note || note.length >
|
|
6450
|
+
if (!note || note.length > MAX_NOTE4) return json({ error: `note must be 1\u2013${MAX_NOTE4} characters` }, 400);
|
|
6096
6451
|
body = JSON.stringify({ body: note, mutationId: crypto.randomUUID() });
|
|
6097
6452
|
}
|
|
6098
6453
|
const secret = await getVaultSecret(got.db, target.secretName);
|
|
@@ -6100,7 +6455,7 @@ var handleAdminNetworkSharedNotes = async (req, url, env, ctx) => {
|
|
|
6100
6455
|
const destination = new URL("/api/network/shared-notes", target.url);
|
|
6101
6456
|
destination.search = new URLSearchParams({ type: recordType, recordId }).toString();
|
|
6102
6457
|
try {
|
|
6103
|
-
const headers = await
|
|
6458
|
+
const headers = await signFederatedRequest5({
|
|
6104
6459
|
secret,
|
|
6105
6460
|
sender: ctx.chapter.id,
|
|
6106
6461
|
method: req.method,
|
|
@@ -6692,11 +7047,11 @@ var handleAdminBuilderLibrary = async (req, url, env, ctx) => {
|
|
|
6692
7047
|
};
|
|
6693
7048
|
|
|
6694
7049
|
// src/signup-control-delivery.ts
|
|
6695
|
-
import { signFederatedRequest as
|
|
7050
|
+
import { signFederatedRequest as signFederatedRequest6 } from "@odla-ai/db";
|
|
6696
7051
|
async function deliverSignupControl(options) {
|
|
6697
7052
|
const destination = new URL(SIGNUP_CONTROL_PATH, options.targetUrl);
|
|
6698
7053
|
const body = JSON.stringify(options.envelope);
|
|
6699
|
-
const signed = await
|
|
7054
|
+
const signed = await signFederatedRequest6({
|
|
6700
7055
|
secret: options.secret,
|
|
6701
7056
|
sender: options.sender,
|
|
6702
7057
|
method: "POST",
|
|
@@ -6717,11 +7072,11 @@ async function deliverSignupControl(options) {
|
|
|
6717
7072
|
}
|
|
6718
7073
|
|
|
6719
7074
|
// src/member-content-delivery.ts
|
|
6720
|
-
import { signFederatedRequest as
|
|
7075
|
+
import { signFederatedRequest as signFederatedRequest7 } from "@odla-ai/db";
|
|
6721
7076
|
async function deliverMemberContent(options) {
|
|
6722
7077
|
const destination = new URL(MEMBER_CONTENT_CONTROL_PATH, options.targetUrl);
|
|
6723
7078
|
const body = JSON.stringify(options.envelope);
|
|
6724
|
-
const signed = await
|
|
7079
|
+
const signed = await signFederatedRequest7({
|
|
6725
7080
|
secret: options.secret,
|
|
6726
7081
|
sender: options.sender,
|
|
6727
7082
|
method: "POST",
|
|
@@ -6742,11 +7097,11 @@ async function deliverMemberContent(options) {
|
|
|
6742
7097
|
}
|
|
6743
7098
|
|
|
6744
7099
|
// src/builder-library-delivery.ts
|
|
6745
|
-
import { signFederatedRequest as
|
|
7100
|
+
import { signFederatedRequest as signFederatedRequest8 } from "@odla-ai/db";
|
|
6746
7101
|
async function deliverBuilderLibrary(options) {
|
|
6747
7102
|
const destination = new URL(BUILDER_LIBRARY_CONTROL_PATH, options.targetUrl);
|
|
6748
7103
|
const body = JSON.stringify(options.envelope);
|
|
6749
|
-
const signed = await
|
|
7104
|
+
const signed = await signFederatedRequest8({ secret: options.secret, sender: options.sender, method: "POST", url: destination, body });
|
|
6750
7105
|
const response = await (options.fetch ?? globalThis.fetch)(destination, { method: "POST", headers: { ...signed, "content-type": "application/json" }, body });
|
|
6751
7106
|
const result = await response.json();
|
|
6752
7107
|
if (!response.ok || !("ok" in result) || result.ok !== true) {
|
|
@@ -6756,31 +7111,6 @@ async function deliverBuilderLibrary(options) {
|
|
|
6756
7111
|
return result;
|
|
6757
7112
|
}
|
|
6758
7113
|
|
|
6759
|
-
// src/admission-grants-delivery.ts
|
|
6760
|
-
import { signFederatedRequest as signFederatedRequest8 } from "@odla-ai/db";
|
|
6761
|
-
async function deliverAdmissionGrants(options) {
|
|
6762
|
-
const destination = new URL(ADMISSION_GRANTS_PATH, options.targetUrl);
|
|
6763
|
-
const body = JSON.stringify(options.envelope);
|
|
6764
|
-
const signed = await signFederatedRequest8({
|
|
6765
|
-
secret: options.secret,
|
|
6766
|
-
sender: options.sender,
|
|
6767
|
-
method: "POST",
|
|
6768
|
-
url: destination,
|
|
6769
|
-
body
|
|
6770
|
-
});
|
|
6771
|
-
const response = await (options.fetch ?? globalThis.fetch)(destination, {
|
|
6772
|
-
method: "POST",
|
|
6773
|
-
headers: { ...signed, "content-type": "application/json" },
|
|
6774
|
-
body
|
|
6775
|
-
});
|
|
6776
|
-
const result = await response.json();
|
|
6777
|
-
if (!response.ok || !("ok" in result) || result.ok !== true) {
|
|
6778
|
-
const failure = result;
|
|
6779
|
-
throw new Error(`admission grants rejected (${response.status}): ${failure.reason ?? failure.error ?? "unknown"}`);
|
|
6780
|
-
}
|
|
6781
|
-
return result;
|
|
6782
|
-
}
|
|
6783
|
-
|
|
6784
7114
|
// src/signup-control-theme.ts
|
|
6785
7115
|
var SIGNUP_CONTROL_THEME_MARKER = "<!--odla:signup-control-theme-->";
|
|
6786
7116
|
var SIGNUP_CONTROL_THEME_STYLE_ID = "odla-signup-control-theme";
|
|
@@ -6940,6 +7270,7 @@ var BUILTIN_ROUTES = [
|
|
|
6940
7270
|
handleAdminPeopleRole,
|
|
6941
7271
|
handleAdminCrmSync,
|
|
6942
7272
|
handleAdminAdmissionGrants,
|
|
7273
|
+
handleAdminNetworkAdmissions,
|
|
6943
7274
|
handleAdminClerkPrivateProfileSync,
|
|
6944
7275
|
// Aggregation
|
|
6945
7276
|
handleAdminDashboard,
|
|
@@ -6984,6 +7315,7 @@ function chapterWorker(options) {
|
|
|
6984
7315
|
};
|
|
6985
7316
|
}
|
|
6986
7317
|
export {
|
|
7318
|
+
AdmissionGrantDeliveryError,
|
|
6987
7319
|
chapterWorker,
|
|
6988
7320
|
createWorkerContext,
|
|
6989
7321
|
deliverAdmissionGrants,
|
|
@@ -6991,6 +7323,7 @@ export {
|
|
|
6991
7323
|
deliverMemberContent,
|
|
6992
7324
|
deliverSignupControl,
|
|
6993
7325
|
renderActiveSignupControlHtml,
|
|
7326
|
+
retryLeaderAdmissionDeliveries,
|
|
6994
7327
|
withActiveSignupControlTheme
|
|
6995
7328
|
};
|
|
6996
7329
|
//# sourceMappingURL=index.js.map
|