@odla-ai/chapter 0.42.1 → 0.42.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -1
- package/dist/{chunk-JXFYOUDL.js → chunk-4HS2JPE2.js} +11 -1
- package/dist/chunk-4HS2JPE2.js.map +1 -0
- package/dist/{chunk-MMDWXTJU.js → chunk-7MEVZEL2.js} +49 -3
- package/dist/chunk-7MEVZEL2.js.map +1 -0
- package/dist/{chunk-S7CND3R5.js → chunk-VWDEHUNI.js} +2 -2
- package/dist/{copy-context-BNUzbL5g.d.ts → copy-context-Bh1EZvCg.d.ts} +10 -2
- package/dist/index.cjs +98 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +41 -4
- package/dist/index.d.ts +41 -4
- package/dist/index.js +98 -10
- package/dist/index.js.map +1 -1
- package/dist/ui/admin/index.d.ts +14 -3
- package/dist/ui/admin/index.js +4 -2
- package/dist/ui/index.d.ts +2 -2
- package/dist/ui/index.js +5 -3
- package/dist/ui/member/index.d.ts +2 -2
- package/dist/ui/member/index.js +2 -2
- package/dist/worker/index.cjs +551 -156
- package/dist/worker/index.cjs.map +1 -1
- package/dist/worker/index.d.cts +56 -3
- package/dist/worker/index.d.ts +56 -3
- package/dist/worker/index.js +527 -132
- package/dist/worker/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-JXFYOUDL.js.map +0 -1
- package/dist/chunk-MMDWXTJU.js.map +0 -1
- /package/dist/{chunk-S7CND3R5.js.map → chunk-VWDEHUNI.js.map} +0 -0
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,384 @@ 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
|
+
var NetworkTargetTransportError = class extends Error {
|
|
3312
|
+
code = "binding_unavailable";
|
|
3313
|
+
binding;
|
|
3314
|
+
constructor(binding) {
|
|
3315
|
+
super(`service binding "${binding}" is unavailable`);
|
|
3316
|
+
this.name = "NetworkTargetTransportError";
|
|
3317
|
+
this.binding = binding;
|
|
3318
|
+
}
|
|
3319
|
+
};
|
|
3320
|
+
function resolveNetworkTargetTransport(env, target) {
|
|
3321
|
+
if (!target.binding) {
|
|
3322
|
+
return {
|
|
3323
|
+
kind: "url",
|
|
3324
|
+
fetch: (destination, init) => globalThis.fetch(destination, init)
|
|
3325
|
+
};
|
|
3326
|
+
}
|
|
3327
|
+
const candidate = env[target.binding];
|
|
3328
|
+
if (typeof candidate !== "object" || candidate === null || !("fetch" in candidate) || typeof candidate.fetch !== "function") {
|
|
3329
|
+
throw new NetworkTargetTransportError(target.binding);
|
|
3330
|
+
}
|
|
3331
|
+
const binding = candidate;
|
|
3332
|
+
return {
|
|
3333
|
+
kind: "binding",
|
|
3334
|
+
binding: target.binding,
|
|
3335
|
+
// Keep the receiver attached: Cloudflare service bindings require it.
|
|
3336
|
+
fetch: (destination, init) => binding.fetch(new Request(destination, init))
|
|
3337
|
+
};
|
|
3338
|
+
}
|
|
3339
|
+
function fetchNetworkTarget(env, target, destination, init) {
|
|
3340
|
+
return resolveNetworkTargetTransport(env, target).fetch(destination, init);
|
|
3341
|
+
}
|
|
3342
|
+
|
|
3343
|
+
// src/worker-network-admission-state.ts
|
|
3344
|
+
import { getRecord } from "@odla-ai/crm";
|
|
3345
|
+
var MAX_ASSIGNMENTS = 2e3;
|
|
3346
|
+
var admissionRows = (value) => Array.isArray(value) ? value : [];
|
|
3347
|
+
function assignmentId(targetId, personId) {
|
|
3348
|
+
return `leader-admission:${targetId}:${personId}`;
|
|
3349
|
+
}
|
|
3350
|
+
function deliveryId(targetId, environment) {
|
|
3351
|
+
return `leader-admission-delivery:${targetId}:${environment}`;
|
|
3352
|
+
}
|
|
3353
|
+
async function loadLeaderAdmissionAssignments(db, targetId) {
|
|
3354
|
+
const result = await db.query({ leaderAdmissionAssignments: { $: {
|
|
3355
|
+
where: { targetId },
|
|
3356
|
+
order: { updatedAt: "desc" },
|
|
3357
|
+
limit: MAX_ASSIGNMENTS
|
|
3358
|
+
} } });
|
|
3359
|
+
return admissionRows(result.leaderAdmissionAssignments);
|
|
3360
|
+
}
|
|
3361
|
+
async function loadLeaderAdmissionDelivery(db, targetId, environment) {
|
|
3362
|
+
const id = deliveryId(targetId, environment);
|
|
3363
|
+
const result = await db.query({ leaderAdmissionDeliveries: { $: { where: { id }, limit: 1 } } });
|
|
3364
|
+
return admissionRows(result.leaderAdmissionDeliveries)[0] ?? null;
|
|
3365
|
+
}
|
|
3366
|
+
function personAdmissionEmail(ctx, record4) {
|
|
3367
|
+
if (record4.type !== "person") return null;
|
|
3368
|
+
const field = ctx.chapter.crm.config.types.person?.emailField;
|
|
3369
|
+
if (!field) return null;
|
|
3370
|
+
const raw = record4.fields[field] ?? record4.primaryEmail;
|
|
3371
|
+
if (typeof raw !== "string") return null;
|
|
3372
|
+
const normalized = normalizeAdmissionEmail(raw);
|
|
3373
|
+
return isValidEmail(normalized) ? normalized : null;
|
|
3374
|
+
}
|
|
3375
|
+
async function grantInput(db, ctx, assignment) {
|
|
3376
|
+
const person = await getRecord({ crm: ctx.chapter.crm, db }, assignment.personId);
|
|
3377
|
+
const email = person ? personAdmissionEmail(ctx, person) : null;
|
|
3378
|
+
if (!email) throw new Error(`person ${assignment.personId} has no valid authoritative email`);
|
|
3379
|
+
return {
|
|
3380
|
+
id: assignment.grantId,
|
|
3381
|
+
email,
|
|
3382
|
+
decision: assignment.decision,
|
|
3383
|
+
...assignment.note ? { note: assignment.note } : {},
|
|
3384
|
+
...assignment.expiresAt !== void 0 ? { expiresAt: assignment.expiresAt } : {}
|
|
3385
|
+
};
|
|
3386
|
+
}
|
|
3387
|
+
async function persistLeaderAdmissionDesiredSet(opts) {
|
|
3388
|
+
const now = Date.now();
|
|
3389
|
+
const current = await loadLeaderAdmissionAssignments(opts.db, opts.target.id);
|
|
3390
|
+
const id = assignmentId(opts.target.id, opts.personId);
|
|
3391
|
+
const existing = current.find((row) => row.id === id);
|
|
3392
|
+
const digest = await signupControlDigest({
|
|
3393
|
+
targetId: opts.target.id,
|
|
3394
|
+
personId: opts.personId,
|
|
3395
|
+
decision: opts.decision,
|
|
3396
|
+
note: opts.note ?? null,
|
|
3397
|
+
expiresAt: opts.expiresAt ?? null,
|
|
3398
|
+
active: opts.active
|
|
3399
|
+
});
|
|
3400
|
+
const mutationReceiptId = `leader-admission-mutation:${opts.target.id}:${opts.mutationId}`;
|
|
3401
|
+
const priorMutation2 = await opts.db.query({ leaderAdmissionMutations: { $: {
|
|
3402
|
+
where: { id: mutationReceiptId },
|
|
3403
|
+
limit: 1
|
|
3404
|
+
} } });
|
|
3405
|
+
const receipt = admissionRows(priorMutation2.leaderAdmissionMutations)[0];
|
|
3406
|
+
if (receipt) {
|
|
3407
|
+
if (receipt.requestDigest !== digest || receipt.personId !== opts.personId) {
|
|
3408
|
+
throw new Error("mutation_conflict");
|
|
3409
|
+
}
|
|
3410
|
+
const saved = await loadLeaderAdmissionDelivery(opts.db, opts.target.id, opts.env.ODLA_ENV);
|
|
3411
|
+
if (!saved) throw new Error("delivery_missing");
|
|
3412
|
+
return { delivery: saved, duplicate: true };
|
|
3413
|
+
}
|
|
3414
|
+
if (existing?.lastMutationId === opts.mutationId) {
|
|
3415
|
+
if (existing.lastRequestDigest !== digest) throw new Error("mutation_conflict");
|
|
3416
|
+
const saved = await loadLeaderAdmissionDelivery(opts.db, opts.target.id, opts.env.ODLA_ENV);
|
|
3417
|
+
if (!saved) throw new Error("delivery_missing");
|
|
3418
|
+
return { delivery: saved, duplicate: true };
|
|
3419
|
+
}
|
|
3420
|
+
const assignment = {
|
|
3421
|
+
id,
|
|
3422
|
+
targetId: opts.target.id,
|
|
3423
|
+
personId: opts.personId,
|
|
3424
|
+
grantId: existing?.grantId ?? crypto.randomUUID().replaceAll("-", ""),
|
|
3425
|
+
decision: opts.decision,
|
|
3426
|
+
...opts.note ? { note: opts.note } : {},
|
|
3427
|
+
...opts.expiresAt !== void 0 ? { expiresAt: opts.expiresAt } : {},
|
|
3428
|
+
active: opts.active,
|
|
3429
|
+
lastMutationId: opts.mutationId,
|
|
3430
|
+
lastRequestDigest: digest,
|
|
3431
|
+
createdAt: existing?.createdAt ?? now,
|
|
3432
|
+
createdBy: existing?.createdBy ?? opts.actor,
|
|
3433
|
+
updatedAt: now,
|
|
3434
|
+
updatedBy: opts.actor
|
|
3435
|
+
};
|
|
3436
|
+
const desired = current.filter((row) => row.id !== id && row.active).concat(assignment.active ? [assignment] : []);
|
|
3437
|
+
const grants = await Promise.all(desired.map((row) => grantInput(opts.db, opts.ctx, row)));
|
|
3438
|
+
const previous = await loadLeaderAdmissionDelivery(opts.db, opts.target.id, opts.env.ODLA_ENV);
|
|
3439
|
+
const revision = (previous?.revision ?? 0) + 1;
|
|
3440
|
+
const envelope = await createAdmissionGrantEnvelope({
|
|
3441
|
+
schemaVersion: 1,
|
|
3442
|
+
revision,
|
|
3443
|
+
generatedAt: now,
|
|
3444
|
+
source: { appId: opts.ctx.chapter.id, actor: opts.actor },
|
|
3445
|
+
target: { chapterId: opts.target.id, environment: opts.env.ODLA_ENV },
|
|
3446
|
+
grants
|
|
3447
|
+
});
|
|
3448
|
+
const delivery = {
|
|
3449
|
+
id: deliveryId(opts.target.id, opts.env.ODLA_ENV),
|
|
3450
|
+
targetId: opts.target.id,
|
|
3451
|
+
environment: opts.env.ODLA_ENV,
|
|
3452
|
+
revision,
|
|
3453
|
+
digest: envelope.digest,
|
|
3454
|
+
envelope,
|
|
3455
|
+
status: "saved",
|
|
3456
|
+
attempts: 0,
|
|
3457
|
+
updatedAt: now
|
|
3458
|
+
};
|
|
3459
|
+
const ops = [
|
|
3460
|
+
{ t: "update", ns: "leaderAdmissionAssignments", id, attrs: assignment },
|
|
3461
|
+
{ t: "update", ns: "leaderAdmissionDeliveries", id: delivery.id, attrs: delivery },
|
|
3462
|
+
{ t: "update", ns: "leaderAdmissionMutations", id: mutationReceiptId, attrs: {
|
|
3463
|
+
id: mutationReceiptId,
|
|
3464
|
+
targetId: opts.target.id,
|
|
3465
|
+
personId: opts.personId,
|
|
3466
|
+
requestDigest: digest,
|
|
3467
|
+
revision,
|
|
3468
|
+
createdAt: now
|
|
3469
|
+
} }
|
|
3470
|
+
];
|
|
3471
|
+
const retract = [
|
|
3472
|
+
...!opts.note && existing?.note !== void 0 ? ["note"] : [],
|
|
3473
|
+
...opts.expiresAt === void 0 && existing?.expiresAt !== void 0 ? ["expiresAt"] : []
|
|
3474
|
+
];
|
|
3475
|
+
if (retract.length) ops.push({ t: "retract", ns: "leaderAdmissionAssignments", id, attrs: retract });
|
|
3476
|
+
const tx = await opts.db.transact(ops, {
|
|
3477
|
+
mutationId: `leader-admission:${opts.target.id}:${opts.mutationId}`,
|
|
3478
|
+
guards: previous ? [{ ns: "leaderAdmissionDeliveries", id: previous.id, exists: true, equals: { revision: previous.revision } }] : [{ ns: "leaderAdmissionDeliveries", id: delivery.id, exists: false }]
|
|
3479
|
+
});
|
|
3480
|
+
return { delivery, duplicate: tx.duplicate };
|
|
3481
|
+
}
|
|
3482
|
+
|
|
3483
|
+
// src/worker-routes-network-admission.ts
|
|
3484
|
+
var PATH = "/api/admin/network/admissions";
|
|
3485
|
+
var RETRY_PATH = "/api/admin/network/admissions/retry";
|
|
3486
|
+
var REVOKE_PATH = "/api/admin/network/admissions/revoke";
|
|
3487
|
+
var MUTATION_ID = /^[A-Za-z0-9_-]{16,128}$/;
|
|
3488
|
+
var MAX_NOTE2 = 2e3;
|
|
3489
|
+
var reject4 = (status, code, error) => json({ ok: false, code, error }, status);
|
|
3490
|
+
async function gate2(req, env, ctx) {
|
|
3491
|
+
const db = ctx.makeDb(env);
|
|
3492
|
+
const user = await ctx.verifyUser(req, env);
|
|
3493
|
+
if (!user) return { response: reject4(401, "unauthorized", "unauthorized") };
|
|
3494
|
+
if (!await ctx.isAdmin(db, user)) return { response: reject4(403, "forbidden", "forbidden") };
|
|
3495
|
+
return { db, user };
|
|
3496
|
+
}
|
|
3497
|
+
function targetFor(ctx, id) {
|
|
3498
|
+
return ctx.chapter.network.targets.find((target) => target.id === id && target.admissionGrants);
|
|
3499
|
+
}
|
|
3500
|
+
async function deliverSaved(db, env, ctx, target, delivery) {
|
|
3501
|
+
if (delivery.status === "acknowledged") return delivery;
|
|
3502
|
+
const secret = await getVaultSecret(db, target.secretName);
|
|
3503
|
+
const attemptedAt = Date.now();
|
|
3504
|
+
const attempts = delivery.attempts + 1;
|
|
3505
|
+
let patch;
|
|
3506
|
+
if (!secret) {
|
|
3507
|
+
patch = {
|
|
3508
|
+
status: delivery.attempts > 0 ? "retrying" : "pending",
|
|
3509
|
+
attempts,
|
|
3510
|
+
lastAttemptAt: attemptedAt,
|
|
3511
|
+
lastError: "edge secret is missing",
|
|
3512
|
+
updatedAt: attemptedAt
|
|
3513
|
+
};
|
|
3514
|
+
} else {
|
|
3515
|
+
try {
|
|
3516
|
+
const receipt = await deliverAdmissionGrants({
|
|
3517
|
+
secret,
|
|
3518
|
+
sender: ctx.chapter.id,
|
|
3519
|
+
targetUrl: target.url,
|
|
3520
|
+
envelope: delivery.envelope,
|
|
3521
|
+
fetch: ((input, init) => fetchNetworkTarget(env, target, new URL(String(input)), init ?? {}))
|
|
3522
|
+
});
|
|
3523
|
+
patch = {
|
|
3524
|
+
status: "acknowledged",
|
|
3525
|
+
attempts,
|
|
3526
|
+
lastAttemptAt: attemptedAt,
|
|
3527
|
+
acknowledgedAt: receipt.acknowledgedAt,
|
|
3528
|
+
updatedAt: attemptedAt
|
|
3529
|
+
};
|
|
3530
|
+
} catch (error) {
|
|
3531
|
+
const permanent = error instanceof AdmissionGrantDeliveryError && error.status >= 400 && error.status < 500;
|
|
3532
|
+
patch = {
|
|
3533
|
+
status: permanent ? "rejected" : delivery.attempts > 0 ? "retrying" : "pending",
|
|
3534
|
+
attempts,
|
|
3535
|
+
lastAttemptAt: attemptedAt,
|
|
3536
|
+
lastError: error instanceof Error ? error.message.slice(0, 2e3) : "delivery failed",
|
|
3537
|
+
updatedAt: attemptedAt
|
|
3538
|
+
};
|
|
3539
|
+
}
|
|
3540
|
+
}
|
|
3541
|
+
const ops = [{
|
|
3542
|
+
t: "update",
|
|
3543
|
+
ns: "leaderAdmissionDeliveries",
|
|
3544
|
+
id: delivery.id,
|
|
3545
|
+
attrs: patch
|
|
3546
|
+
}];
|
|
3547
|
+
if (patch.status === "acknowledged" && delivery.lastError) {
|
|
3548
|
+
ops.push({ t: "retract", ns: "leaderAdmissionDeliveries", id: delivery.id, attrs: ["lastError"] });
|
|
3549
|
+
}
|
|
3550
|
+
await db.transact(ops, {
|
|
3551
|
+
mutationId: `leader-admission-attempt:${delivery.targetId}:${delivery.revision}:${attempts}:${patch.status}`
|
|
3552
|
+
});
|
|
3553
|
+
return { ...delivery, ...patch };
|
|
3554
|
+
}
|
|
3555
|
+
async function parseBody(req) {
|
|
3556
|
+
try {
|
|
3557
|
+
return await req.json();
|
|
3558
|
+
} catch {
|
|
3559
|
+
return null;
|
|
3560
|
+
}
|
|
3561
|
+
}
|
|
3562
|
+
var handleAdminNetworkAdmissions = async (req, url, env, ctx) => {
|
|
3563
|
+
if (![PATH, RETRY_PATH, REVOKE_PATH].includes(url.pathname)) return null;
|
|
3564
|
+
const auth = await gate2(req, env, ctx);
|
|
3565
|
+
if ("response" in auth) return auth.response;
|
|
3566
|
+
const db = auth.db;
|
|
3567
|
+
if (url.pathname === PATH && req.method === "GET") {
|
|
3568
|
+
const targetId2 = url.searchParams.get("targetId") ?? "";
|
|
3569
|
+
const target2 = targetFor(ctx, targetId2);
|
|
3570
|
+
if (!target2) return reject4(404, "unknown_target", "admission target is not configured");
|
|
3571
|
+
return json({
|
|
3572
|
+
ok: true,
|
|
3573
|
+
target: { id: target2.id, name: target2.name },
|
|
3574
|
+
assignments: await loadLeaderAdmissionAssignments(db, target2.id),
|
|
3575
|
+
delivery: await loadLeaderAdmissionDelivery(db, target2.id, env.ODLA_ENV)
|
|
3576
|
+
});
|
|
3577
|
+
}
|
|
3578
|
+
if (req.method !== "POST") return null;
|
|
3579
|
+
const body = await parseBody(req);
|
|
3580
|
+
if (!body) return reject4(422, "invalid_request", "request body must be JSON");
|
|
3581
|
+
const targetId = typeof body.targetId === "string" ? body.targetId : "";
|
|
3582
|
+
const target = targetFor(ctx, targetId);
|
|
3583
|
+
if (!target) return reject4(404, "unknown_target", "admission target is not configured");
|
|
3584
|
+
if (url.pathname === RETRY_PATH) {
|
|
3585
|
+
const delivery = await loadLeaderAdmissionDelivery(db, target.id, env.ODLA_ENV);
|
|
3586
|
+
if (!delivery) return reject4(404, "not_found", "no saved admission delivery exists");
|
|
3587
|
+
return json({ ok: true, delivery: await deliverSaved(db, env, ctx, target, delivery) });
|
|
3588
|
+
}
|
|
3589
|
+
const personId = typeof body.personId === "string" ? body.personId : "";
|
|
3590
|
+
const mutationId = typeof body.mutationId === "string" ? body.mutationId : "";
|
|
3591
|
+
if (!personId || !MUTATION_ID.test(mutationId)) {
|
|
3592
|
+
return reject4(422, "invalid_request", "personId and a stable mutationId are required");
|
|
3593
|
+
}
|
|
3594
|
+
const person = await getRecord2({ crm: ctx.chapter.crm, db: auth.db }, personId);
|
|
3595
|
+
if (!person) return reject4(404, "person_not_found", "person not found");
|
|
3596
|
+
if (!personAdmissionEmail(ctx, person)) {
|
|
3597
|
+
return reject4(422, "invalid_email", "person has no valid authoritative email");
|
|
3598
|
+
}
|
|
3599
|
+
const decision = body.decision ?? "waive_interview";
|
|
3600
|
+
if (decision !== "waive_interview" && decision !== "require_interview") {
|
|
3601
|
+
return reject4(422, "invalid_decision", "decision must be waive_interview or require_interview");
|
|
3602
|
+
}
|
|
3603
|
+
const note = typeof body.note === "string" ? body.note.trim() : "";
|
|
3604
|
+
if (note.length > MAX_NOTE2) return reject4(422, "invalid_note", "note is too long");
|
|
3605
|
+
const expiresAt = body.expiresAt == null ? void 0 : Number(body.expiresAt);
|
|
3606
|
+
if (expiresAt !== void 0 && (!Number.isFinite(expiresAt) || expiresAt <= Date.now())) {
|
|
3607
|
+
return reject4(422, "invalid_expiry", "expiresAt must be a future epoch timestamp");
|
|
3608
|
+
}
|
|
3609
|
+
try {
|
|
3610
|
+
const saved = await persistLeaderAdmissionDesiredSet({
|
|
3611
|
+
db,
|
|
3612
|
+
ctx,
|
|
3613
|
+
env,
|
|
3614
|
+
target,
|
|
3615
|
+
personId,
|
|
3616
|
+
decision,
|
|
3617
|
+
...note ? { note } : {},
|
|
3618
|
+
...expiresAt !== void 0 ? { expiresAt } : {},
|
|
3619
|
+
active: url.pathname !== REVOKE_PATH,
|
|
3620
|
+
mutationId,
|
|
3621
|
+
actor: auth.user.email ?? auth.user.userId
|
|
3622
|
+
});
|
|
3623
|
+
const delivery = await deliverSaved(db, env, ctx, target, saved.delivery);
|
|
3624
|
+
return json({ ok: true, duplicate: saved.duplicate, delivery }, delivery.status === "acknowledged" ? 200 : 202);
|
|
3625
|
+
} catch (error) {
|
|
3626
|
+
if (error instanceof Error && error.message === "mutation_conflict") {
|
|
3627
|
+
return reject4(409, "mutation_conflict", "mutationId was already used for another request");
|
|
3628
|
+
}
|
|
3629
|
+
if (error instanceof Error && /valid authoritative email/.test(error.message)) {
|
|
3630
|
+
return reject4(422, "invalid_email", error.message);
|
|
3631
|
+
}
|
|
3632
|
+
return reject4(409, "revision_conflict", "admission assignments changed; reload and retry");
|
|
3633
|
+
}
|
|
3634
|
+
};
|
|
3635
|
+
async function retryLeaderAdmissionDeliveries(env, ctx, limit = 100) {
|
|
3636
|
+
const db = ctx.makeDb(env);
|
|
3637
|
+
const result = await db.query({ leaderAdmissionDeliveries: { $: { limit } } });
|
|
3638
|
+
const pending = admissionRows(result.leaderAdmissionDeliveries).filter((delivery) => delivery.status !== "acknowledged" && delivery.status !== "rejected").slice(0, Math.max(1, Math.min(limit, 500)));
|
|
3639
|
+
const delivered = [];
|
|
3640
|
+
for (const item of pending) {
|
|
3641
|
+
const target = targetFor(ctx, item.targetId);
|
|
3642
|
+
if (target) delivered.push(await deliverSaved(db, env, ctx, target, item));
|
|
3643
|
+
}
|
|
3644
|
+
return delivered;
|
|
3645
|
+
}
|
|
3646
|
+
|
|
3259
3647
|
// src/worker-routes-schedule.ts
|
|
3260
3648
|
import { computeBookableSlots, initCalendar } from "@odla-ai/calendar";
|
|
3261
3649
|
function errCode(err) {
|
|
@@ -4517,16 +4905,16 @@ var crmDeps2 = (db, ctx) => ({
|
|
|
4517
4905
|
});
|
|
4518
4906
|
var handleAdminCrmSync = async (req, url, env, ctx) => {
|
|
4519
4907
|
if (req.method !== "POST" || url.pathname !== "/api/admin/crm/sync") return null;
|
|
4520
|
-
const
|
|
4521
|
-
if (
|
|
4522
|
-
const result = await backfillCrm(crmDeps2(
|
|
4908
|
+
const gate10 = await adminGate(req, env, ctx);
|
|
4909
|
+
if (gate10 instanceof Response) return gate10;
|
|
4910
|
+
const result = await backfillCrm(crmDeps2(gate10.db, ctx));
|
|
4523
4911
|
return json({ ok: true, ...result });
|
|
4524
4912
|
};
|
|
4525
4913
|
var handleAdminPeople = async (req, url, env, ctx) => {
|
|
4526
4914
|
if (req.method !== "GET" || url.pathname !== "/api/admin/people") return null;
|
|
4527
|
-
const
|
|
4528
|
-
if (
|
|
4529
|
-
const { db } =
|
|
4915
|
+
const gate10 = await adminGate(req, env, ctx);
|
|
4916
|
+
if (gate10 instanceof Response) return gate10;
|
|
4917
|
+
const { db } = gate10;
|
|
4530
4918
|
const sk = await getVaultSecret(db, "clerk_secret_key");
|
|
4531
4919
|
const [data, roleList] = await Promise.all([
|
|
4532
4920
|
db.query({
|
|
@@ -4579,9 +4967,9 @@ var handleAdminPeople = async (req, url, env, ctx) => {
|
|
|
4579
4967
|
};
|
|
4580
4968
|
var handleAdminPeopleAccess = async (req, url, env, ctx) => {
|
|
4581
4969
|
if (req.method !== "GET" || url.pathname !== "/api/admin/people/access") return null;
|
|
4582
|
-
const
|
|
4583
|
-
if (
|
|
4584
|
-
const { db } =
|
|
4970
|
+
const gate10 = await adminGate(req, env, ctx);
|
|
4971
|
+
if (gate10 instanceof Response) return gate10;
|
|
4972
|
+
const { db } = gate10;
|
|
4585
4973
|
const targetId = url.searchParams.get("userId") ?? "";
|
|
4586
4974
|
if (!targetId.startsWith("user_")) return json({ error: "invalid userId" }, 400);
|
|
4587
4975
|
const sk = await getVaultSecret(db, "clerk_secret_key");
|
|
@@ -4592,9 +4980,9 @@ var handleAdminPeopleAccess = async (req, url, env, ctx) => {
|
|
|
4592
4980
|
};
|
|
4593
4981
|
var handleAdminPeopleRole = async (req, url, env, ctx) => {
|
|
4594
4982
|
if (req.method !== "POST" || url.pathname !== "/api/admin/people/role") return null;
|
|
4595
|
-
const
|
|
4596
|
-
if (
|
|
4597
|
-
const { db, actor } =
|
|
4983
|
+
const gate10 = await adminGate(req, env, ctx);
|
|
4984
|
+
if (gate10 instanceof Response) return gate10;
|
|
4985
|
+
const { db, actor } = gate10;
|
|
4598
4986
|
let body;
|
|
4599
4987
|
try {
|
|
4600
4988
|
body = await req.json();
|
|
@@ -4741,7 +5129,7 @@ function subAnnualCents(sub) {
|
|
|
4741
5129
|
}
|
|
4742
5130
|
|
|
4743
5131
|
// src/worker-routes-admin-dashboard.ts
|
|
4744
|
-
async function
|
|
5132
|
+
async function gate3(req, env, ctx) {
|
|
4745
5133
|
const rawDb = ctx.makeDb(env);
|
|
4746
5134
|
const u = await ctx.verifyUser(req, env);
|
|
4747
5135
|
if (!u) return json({ error: "unauthorized" }, 401);
|
|
@@ -4751,7 +5139,7 @@ async function gate2(req, env, ctx) {
|
|
|
4751
5139
|
var rows3 = (v) => Array.isArray(v) ? v : [];
|
|
4752
5140
|
var handleAdminDashboard = async (req, url, env, ctx) => {
|
|
4753
5141
|
if (req.method !== "GET" || url.pathname !== "/api/admin/dashboard") return null;
|
|
4754
|
-
const got = await
|
|
5142
|
+
const got = await gate3(req, env, ctx);
|
|
4755
5143
|
if (got instanceof Response) return got;
|
|
4756
5144
|
const db = got;
|
|
4757
5145
|
const now = Date.now();
|
|
@@ -4834,7 +5222,7 @@ var handleAdminDashboard = async (req, url, env, ctx) => {
|
|
|
4834
5222
|
};
|
|
4835
5223
|
var handleAdminBilling = async (req, url, env, ctx) => {
|
|
4836
5224
|
if (req.method !== "GET" || url.pathname !== "/api/admin/billing") return null;
|
|
4837
|
-
const got = await
|
|
5225
|
+
const got = await gate3(req, env, ctx);
|
|
4838
5226
|
if (got instanceof Response) return got;
|
|
4839
5227
|
const db = got;
|
|
4840
5228
|
const sk = await getVaultSecret(db, "stripe_secret_key");
|
|
@@ -4894,7 +5282,7 @@ var handleAdminBilling = async (req, url, env, ctx) => {
|
|
|
4894
5282
|
|
|
4895
5283
|
// src/worker-routes-admin-lifecycle.ts
|
|
4896
5284
|
import { computeBookableSlots as computeBookableSlots2, initCalendar as initCalendar3 } from "@odla-ai/calendar";
|
|
4897
|
-
async function
|
|
5285
|
+
async function gate4(req, env, ctx) {
|
|
4898
5286
|
const rawDb = ctx.makeDb(env);
|
|
4899
5287
|
const u = await ctx.verifyUser(req, env);
|
|
4900
5288
|
if (!u) return json({ error: "unauthorized" }, 401);
|
|
@@ -4920,7 +5308,7 @@ async function loadGroup(db, id) {
|
|
|
4920
5308
|
var handleAdminMeetingReschedule = async (req, url, env, ctx) => {
|
|
4921
5309
|
const m = url.pathname.match(/^\/api\/admin\/meetings\/([0-9a-f-]+)\/reschedule$/);
|
|
4922
5310
|
if (req.method !== "POST" || !m) return null;
|
|
4923
|
-
const got = await
|
|
5311
|
+
const got = await gate4(req, env, ctx);
|
|
4924
5312
|
if (got instanceof Response) return got;
|
|
4925
5313
|
const db = got;
|
|
4926
5314
|
const body = await readJson(req);
|
|
@@ -4961,7 +5349,7 @@ var handleAdminMeetingReschedule = async (req, url, env, ctx) => {
|
|
|
4961
5349
|
var handleAdminMeetingCancel = async (req, url, env, ctx) => {
|
|
4962
5350
|
const m = url.pathname.match(/^\/api\/admin\/meetings\/([0-9a-f-]+)\/cancel$/);
|
|
4963
5351
|
if (req.method !== "POST" || !m) return null;
|
|
4964
|
-
const got = await
|
|
5352
|
+
const got = await gate4(req, env, ctx);
|
|
4965
5353
|
if (got instanceof Response) return got;
|
|
4966
5354
|
const db = got;
|
|
4967
5355
|
const { meetings } = await db.query({ meetings: { $: { where: { id: m[1] }, limit: 1 } } });
|
|
@@ -4984,7 +5372,7 @@ var handleAdminMeetingCancel = async (req, url, env, ctx) => {
|
|
|
4984
5372
|
var handleAdminApprove = async (req, url, env, ctx) => {
|
|
4985
5373
|
const m = url.pathname.match(/^\/api\/admin\/applications\/([0-9a-f-]+)\/approve$/);
|
|
4986
5374
|
if (req.method !== "POST" || !m) return null;
|
|
4987
|
-
const got = await
|
|
5375
|
+
const got = await gate4(req, env, ctx);
|
|
4988
5376
|
if (got instanceof Response) return got;
|
|
4989
5377
|
const db = got;
|
|
4990
5378
|
const id = m[1];
|
|
@@ -4997,7 +5385,7 @@ var handleAdminApprove = async (req, url, env, ctx) => {
|
|
|
4997
5385
|
var handleAdminRefund = async (req, url, env, ctx) => {
|
|
4998
5386
|
const m = url.pathname.match(/^\/api\/admin\/applications\/([0-9a-f-]+)\/refund$/);
|
|
4999
5387
|
if (req.method !== "POST" || !m) return null;
|
|
5000
|
-
const got = await
|
|
5388
|
+
const got = await gate4(req, env, ctx);
|
|
5001
5389
|
if (got instanceof Response) return got;
|
|
5002
5390
|
const db = got;
|
|
5003
5391
|
const app = await loadApp(db, m[1]);
|
|
@@ -5029,7 +5417,7 @@ var handleAdminRefund = async (req, url, env, ctx) => {
|
|
|
5029
5417
|
var handleAdminApplicationPatch = async (req, url, env, ctx) => {
|
|
5030
5418
|
const m = url.pathname.match(/^\/api\/admin\/applications\/([0-9a-f-]+)$/);
|
|
5031
5419
|
if (req.method !== "PATCH" || !m) return null;
|
|
5032
|
-
const got = await
|
|
5420
|
+
const got = await gate4(req, env, ctx);
|
|
5033
5421
|
if (got instanceof Response) return got;
|
|
5034
5422
|
const db = got;
|
|
5035
5423
|
const id = m[1];
|
|
@@ -5057,7 +5445,7 @@ var handleAdminApplicationPatch = async (req, url, env, ctx) => {
|
|
|
5057
5445
|
};
|
|
5058
5446
|
|
|
5059
5447
|
// src/worker-routes-admin-comms.ts
|
|
5060
|
-
async function
|
|
5448
|
+
async function gate5(req, env, ctx) {
|
|
5061
5449
|
const rawDb = ctx.makeDb(env);
|
|
5062
5450
|
const u = await ctx.verifyUser(req, env);
|
|
5063
5451
|
if (!u) return json({ error: "unauthorized" }, 401);
|
|
@@ -5079,7 +5467,7 @@ var notifyDeps = (db, env) => ({
|
|
|
5079
5467
|
});
|
|
5080
5468
|
var handleAdminGroupEmail = async (req, url, env, ctx) => {
|
|
5081
5469
|
if (url.pathname !== "/api/admin/group/email" || req.method !== "GET" && req.method !== "PUT") return null;
|
|
5082
|
-
const got = await
|
|
5470
|
+
const got = await gate5(req, env, ctx);
|
|
5083
5471
|
if (got instanceof Response) return got;
|
|
5084
5472
|
const db = got;
|
|
5085
5473
|
const group = await loadGroup2(db, ctx.chapter.id);
|
|
@@ -5141,7 +5529,7 @@ var handleAdminGroupEmail = async (req, url, env, ctx) => {
|
|
|
5141
5529
|
};
|
|
5142
5530
|
var handleAdminEmailLog = async (req, url, env, ctx) => {
|
|
5143
5531
|
if (req.method !== "GET" || url.pathname !== "/api/admin/email/log") return null;
|
|
5144
|
-
const got = await
|
|
5532
|
+
const got = await gate5(req, env, ctx);
|
|
5145
5533
|
if (got instanceof Response) return got;
|
|
5146
5534
|
const { emailLog } = await got.query({ emailLog: { $: { order: { sentAt: "desc" }, limit: 50 } } });
|
|
5147
5535
|
const sends = (emailLog ?? []).map((r) => ({
|
|
@@ -5158,7 +5546,7 @@ var handleAdminEmailLog = async (req, url, env, ctx) => {
|
|
|
5158
5546
|
};
|
|
5159
5547
|
var handleAdminEmailTest = async (req, url, env, ctx) => {
|
|
5160
5548
|
if (req.method !== "POST" || url.pathname !== "/api/admin/email/test") return null;
|
|
5161
|
-
const got = await
|
|
5549
|
+
const got = await gate5(req, env, ctx);
|
|
5162
5550
|
if (got instanceof Response) return got;
|
|
5163
5551
|
const db = got;
|
|
5164
5552
|
let body;
|
|
@@ -5186,7 +5574,7 @@ var handleAdminEmailTest = async (req, url, env, ctx) => {
|
|
|
5186
5574
|
var handleAdminComms = async (req, url, env, ctx) => {
|
|
5187
5575
|
const m = url.pathname.match(/^\/api\/admin\/people\/([0-9a-fA-F-]+)\/comms$/);
|
|
5188
5576
|
if (req.method !== "GET" || !m) return null;
|
|
5189
|
-
const got = await
|
|
5577
|
+
const got = await gate5(req, env, ctx);
|
|
5190
5578
|
if (got instanceof Response) return got;
|
|
5191
5579
|
const db = got;
|
|
5192
5580
|
const appId = m[1];
|
|
@@ -5224,26 +5612,12 @@ var handleAdminComms = async (req, url, env, ctx) => {
|
|
|
5224
5612
|
};
|
|
5225
5613
|
|
|
5226
5614
|
// 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
|
-
}
|
|
5615
|
+
import { getRecord as getRecord3, typeSummary } from "@odla-ai/crm";
|
|
5616
|
+
import { signFederatedRequest as signFederatedRequest3, verifyFederatedRequest as verifyFederatedRequest5 } from "@odla-ai/db";
|
|
5243
5617
|
|
|
5244
5618
|
// src/worker-network-push.ts
|
|
5245
5619
|
import { recordDeliveryAttempt } from "@odla-ai/crm";
|
|
5246
|
-
import { signFederatedRequest } from "@odla-ai/db";
|
|
5620
|
+
import { signFederatedRequest as signFederatedRequest2 } from "@odla-ai/db";
|
|
5247
5621
|
async function pushNetworkRecord(db, env, ctx, target, record4) {
|
|
5248
5622
|
const secret = await getVaultSecret(db, target.secretName);
|
|
5249
5623
|
const crmDeps3 = {
|
|
@@ -5277,7 +5651,7 @@ async function pushNetworkRecord(db, env, ctx, target, record4) {
|
|
|
5277
5651
|
const payloadBody = JSON.stringify(payload);
|
|
5278
5652
|
const destination = new URL("/api/network/shared", target.url);
|
|
5279
5653
|
try {
|
|
5280
|
-
const signed = await
|
|
5654
|
+
const signed = await signFederatedRequest2({
|
|
5281
5655
|
secret,
|
|
5282
5656
|
sender: ctx.chapter.id,
|
|
5283
5657
|
method: "POST",
|
|
@@ -5336,7 +5710,7 @@ async function pushNetworkRecord(db, env, ctx, target, record4) {
|
|
|
5336
5710
|
}
|
|
5337
5711
|
|
|
5338
5712
|
// src/worker-routes-network.ts
|
|
5339
|
-
async function
|
|
5713
|
+
async function gate6(req, env, ctx) {
|
|
5340
5714
|
const db = ctx.makeDb(env);
|
|
5341
5715
|
const user = await ctx.verifyUser(req, env);
|
|
5342
5716
|
if (!user) return { response: json({ error: "unauthorized" }, 401) };
|
|
@@ -5345,7 +5719,7 @@ async function gate5(req, env, ctx) {
|
|
|
5345
5719
|
}
|
|
5346
5720
|
var handleAdminNetworkTargets = async (req, url, env, ctx) => {
|
|
5347
5721
|
if (req.method !== "GET" || url.pathname !== "/api/admin/network/targets") return null;
|
|
5348
|
-
const got = await
|
|
5722
|
+
const got = await gate6(req, env, ctx);
|
|
5349
5723
|
if ("response" in got) return got.response;
|
|
5350
5724
|
return json({
|
|
5351
5725
|
targets: ctx.chapter.network.targets.map(({
|
|
@@ -5377,26 +5751,50 @@ var handleNetworkSnapshot = async (req, url, env, ctx) => {
|
|
|
5377
5751
|
const types = await Promise.all(
|
|
5378
5752
|
Object.keys(ctx.chapter.crm.config.types).map((type) => typeSummary({ crm: ctx.chapter.crm, db }, type))
|
|
5379
5753
|
);
|
|
5754
|
+
const activeConfiguration = ctx.chapter.signupControl ? await loadActiveSignupControl(db, ctx.chapter.id, env.ODLA_ENV) : null;
|
|
5380
5755
|
const snapshot = {
|
|
5381
5756
|
version: 1,
|
|
5382
5757
|
site: { id: ctx.chapter.id, name: ctx.chapter.name, mode: ctx.chapter.mode },
|
|
5383
5758
|
generatedAt: Date.now(),
|
|
5759
|
+
configuration: {
|
|
5760
|
+
activeRevision: activeConfiguration?.revision ?? null,
|
|
5761
|
+
...activeConfiguration ? { digest: activeConfiguration.digest } : {}
|
|
5762
|
+
},
|
|
5384
5763
|
types
|
|
5385
5764
|
};
|
|
5386
5765
|
return json(snapshot);
|
|
5387
5766
|
};
|
|
5388
5767
|
async function snapshotOne(db, env, ctx, target) {
|
|
5768
|
+
const transportKind = target.binding ? "binding" : "url";
|
|
5769
|
+
const base = {
|
|
5770
|
+
id: target.id,
|
|
5771
|
+
name: target.name,
|
|
5772
|
+
url: target.url,
|
|
5773
|
+
configuration: { activeRevision: null },
|
|
5774
|
+
publicSite: { available: null }
|
|
5775
|
+
};
|
|
5389
5776
|
const secret = await getVaultSecret(db, target.secretName);
|
|
5390
|
-
if (!secret) return {
|
|
5777
|
+
if (!secret) return {
|
|
5778
|
+
...base,
|
|
5779
|
+
available: false,
|
|
5780
|
+
transport: {
|
|
5781
|
+
kind: transportKind,
|
|
5782
|
+
...target.binding ? { binding: target.binding } : {},
|
|
5783
|
+
reachable: null,
|
|
5784
|
+
error: { code: "edge_secret_missing", message: "edge secret is missing" }
|
|
5785
|
+
},
|
|
5786
|
+
error: "edge secret is missing"
|
|
5787
|
+
};
|
|
5391
5788
|
const destination = new URL("/api/network/snapshot", target.url);
|
|
5392
5789
|
try {
|
|
5393
|
-
const
|
|
5790
|
+
const transport = resolveNetworkTargetTransport(env, target);
|
|
5791
|
+
const headers = await signFederatedRequest3({
|
|
5394
5792
|
secret,
|
|
5395
5793
|
sender: ctx.chapter.id,
|
|
5396
5794
|
method: "GET",
|
|
5397
5795
|
url: destination
|
|
5398
5796
|
});
|
|
5399
|
-
const response = await
|
|
5797
|
+
const response = await transport.fetch(destination, {
|
|
5400
5798
|
headers,
|
|
5401
5799
|
signal: AbortSignal.timeout(1e4)
|
|
5402
5800
|
});
|
|
@@ -5404,34 +5802,53 @@ async function snapshotOne(db, env, ctx, target) {
|
|
|
5404
5802
|
if (!response.ok || !body || !("version" in body) || body.version !== 1 || !("site" in body) || body.site.id !== target.id || !Array.isArray(body.types)) {
|
|
5405
5803
|
const upstream = body && "error" in body && typeof body.error === "string" ? body.error : void 0;
|
|
5406
5804
|
return {
|
|
5407
|
-
|
|
5408
|
-
name: target.name,
|
|
5409
|
-
url: target.url,
|
|
5805
|
+
...base,
|
|
5410
5806
|
available: false,
|
|
5807
|
+
transport: {
|
|
5808
|
+
kind: transport.kind,
|
|
5809
|
+
...transport.binding ? { binding: transport.binding } : {},
|
|
5810
|
+
reachable: true
|
|
5811
|
+
},
|
|
5812
|
+
publicSite: { available: transport.kind === "url" ? true : null },
|
|
5411
5813
|
error: upstream ?? (response.ok ? "invalid follower snapshot" : `follower returned ${response.status}`)
|
|
5412
5814
|
};
|
|
5413
5815
|
}
|
|
5414
5816
|
return {
|
|
5415
|
-
|
|
5416
|
-
name: target.name,
|
|
5417
|
-
url: target.url,
|
|
5817
|
+
...base,
|
|
5418
5818
|
available: true,
|
|
5819
|
+
transport: {
|
|
5820
|
+
kind: transport.kind,
|
|
5821
|
+
...transport.binding ? { binding: transport.binding } : {},
|
|
5822
|
+
reachable: true
|
|
5823
|
+
},
|
|
5824
|
+
configuration: body.configuration ?? { activeRevision: null },
|
|
5825
|
+
publicSite: { available: transport.kind === "url" ? true : null },
|
|
5419
5826
|
generatedAt: body.generatedAt,
|
|
5420
5827
|
types: body.types
|
|
5421
5828
|
};
|
|
5422
5829
|
} catch (error) {
|
|
5830
|
+
const bindingError = error instanceof NetworkTargetTransportError;
|
|
5831
|
+
const message = error instanceof Error ? error.message : "snapshot failed";
|
|
5423
5832
|
return {
|
|
5424
|
-
|
|
5425
|
-
name: target.name,
|
|
5426
|
-
url: target.url,
|
|
5833
|
+
...base,
|
|
5427
5834
|
available: false,
|
|
5428
|
-
|
|
5835
|
+
transport: {
|
|
5836
|
+
kind: transportKind,
|
|
5837
|
+
...target.binding ? { binding: target.binding } : {},
|
|
5838
|
+
reachable: false,
|
|
5839
|
+
error: {
|
|
5840
|
+
code: bindingError ? "binding_unavailable" : target.binding ? "binding_request_failed" : "url_request_failed",
|
|
5841
|
+
message
|
|
5842
|
+
}
|
|
5843
|
+
},
|
|
5844
|
+
publicSite: { available: target.binding ? null : false },
|
|
5845
|
+
error: message
|
|
5429
5846
|
};
|
|
5430
5847
|
}
|
|
5431
5848
|
}
|
|
5432
5849
|
var handleAdminNetworkRollup = async (req, url, env, ctx) => {
|
|
5433
5850
|
if (req.method !== "GET" || url.pathname !== "/api/admin/network/rollup") return null;
|
|
5434
|
-
const got = await
|
|
5851
|
+
const got = await gate6(req, env, ctx);
|
|
5435
5852
|
if ("response" in got) return got.response;
|
|
5436
5853
|
const targets = await Promise.all(
|
|
5437
5854
|
ctx.chapter.network.targets.map((target) => snapshotOne(got.db, env, ctx, target))
|
|
@@ -5458,7 +5875,7 @@ var handleAdminNetworkRollup = async (req, url, env, ctx) => {
|
|
|
5458
5875
|
};
|
|
5459
5876
|
var handleAdminNetworkPush = async (req, url, env, ctx) => {
|
|
5460
5877
|
if (req.method !== "POST" || url.pathname !== "/api/admin/network/push") return null;
|
|
5461
|
-
const got = await
|
|
5878
|
+
const got = await gate6(req, env, ctx);
|
|
5462
5879
|
if ("response" in got) return got.response;
|
|
5463
5880
|
let body;
|
|
5464
5881
|
try {
|
|
@@ -5473,7 +5890,7 @@ var handleAdminNetworkPush = async (req, url, env, ctx) => {
|
|
|
5473
5890
|
if (requested.size !== body.targetIds.length) return json({ error: "targetIds must contain unique strings" }, 400);
|
|
5474
5891
|
const targets = ctx.chapter.network.targets.filter((target) => requested.has(target.id));
|
|
5475
5892
|
if (targets.length !== requested.size) return json({ error: "one or more targetIds are not configured" }, 400);
|
|
5476
|
-
const record4 = await
|
|
5893
|
+
const record4 = await getRecord3({ crm: ctx.chapter.crm, db: got.db }, body.recordId);
|
|
5477
5894
|
if (!record4) return json({ error: "record not found" }, 404);
|
|
5478
5895
|
const results = await Promise.all(
|
|
5479
5896
|
targets.map((target) => pushNetworkRecord(got.db, env, ctx, target, record4))
|
|
@@ -5482,7 +5899,7 @@ var handleAdminNetworkPush = async (req, url, env, ctx) => {
|
|
|
5482
5899
|
};
|
|
5483
5900
|
|
|
5484
5901
|
// src/worker-routes-network-records-follower.ts
|
|
5485
|
-
import { CRM_NS, CrmInputError, getRecord as
|
|
5902
|
+
import { CRM_NS, CrmInputError, getRecord as getRecord4, listRecords, setStage as setStage2, updateRecord as updateRecord3 } from "@odla-ai/crm";
|
|
5486
5903
|
import { verifyFederatedRequest as verifyFederatedRequest6 } from "@odla-ai/db";
|
|
5487
5904
|
|
|
5488
5905
|
// src/worker-routes-network-records-common.ts
|
|
@@ -5492,7 +5909,7 @@ var MAX_OFFSET = 1e4;
|
|
|
5492
5909
|
var MAX_SEARCH = 200;
|
|
5493
5910
|
var MAX_BODY = 32768;
|
|
5494
5911
|
var RECORD_ID = /^[a-zA-Z0-9][a-zA-Z0-9:_-]{0,191}$/;
|
|
5495
|
-
var
|
|
5912
|
+
var MUTATION_ID2 = /^[a-zA-Z0-9][a-zA-Z0-9:._-]{7,127}$/;
|
|
5496
5913
|
function bodyTooLarge(body) {
|
|
5497
5914
|
return new TextEncoder().encode(body).byteLength > MAX_BODY;
|
|
5498
5915
|
}
|
|
@@ -5610,7 +6027,7 @@ var handleNetworkRecords = async (req, url, env, ctx) => {
|
|
|
5610
6027
|
const hasFields = Boolean(fields && typeof fields === "object" && !Array.isArray(fields));
|
|
5611
6028
|
const hasStage = typeof input?.stage === "string";
|
|
5612
6029
|
const actor = input?.actor;
|
|
5613
|
-
if (!input || !IDENTIFIER.test(input.type) || !RECORD_ID.test(input.recordId) || !Number.isSafeInteger(input.expectedRevision) || input.expectedRevision < 0 || !
|
|
6030
|
+
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
6031
|
return json({ error: "invalid network mutation", code: "invalid_mutation" }, 422);
|
|
5615
6032
|
}
|
|
5616
6033
|
const editable = reader.editableFields[input.type];
|
|
@@ -5629,7 +6046,7 @@ var handleNetworkRecords = async (req, url, env, ctx) => {
|
|
|
5629
6046
|
if (prior.fingerprint !== fingerprint) {
|
|
5630
6047
|
return json({ error: "mutationId was already used for different input", code: "mutation_conflict" }, 409);
|
|
5631
6048
|
}
|
|
5632
|
-
const replayed = await
|
|
6049
|
+
const replayed = await getRecord4({ crm: ctx.chapter.crm, db }, input.recordId);
|
|
5633
6050
|
if (!replayed || replayed.type !== input.type) return json({ error: "record not found", code: "not_found" }, 404);
|
|
5634
6051
|
const result = {
|
|
5635
6052
|
version: 1,
|
|
@@ -5639,7 +6056,7 @@ var handleNetworkRecords = async (req, url, env, ctx) => {
|
|
|
5639
6056
|
};
|
|
5640
6057
|
return json(result);
|
|
5641
6058
|
}
|
|
5642
|
-
const record4 = await
|
|
6059
|
+
const record4 = await getRecord4({ crm: ctx.chapter.crm, db }, input.recordId);
|
|
5643
6060
|
if (!record4 || record4.type !== input.type) return json({ error: "record not found", code: "not_found" }, 404);
|
|
5644
6061
|
if (record4.updatedAt !== input.expectedRevision) {
|
|
5645
6062
|
return json({ error: "record changed since it was reviewed", code: "stale_revision" }, 409);
|
|
@@ -5724,15 +6141,15 @@ var handleNetworkRecords = async (req, url, env, ctx) => {
|
|
|
5724
6141
|
};
|
|
5725
6142
|
|
|
5726
6143
|
// src/worker-routes-network-records-admin.ts
|
|
5727
|
-
import { signFederatedRequest as
|
|
5728
|
-
async function
|
|
6144
|
+
import { signFederatedRequest as signFederatedRequest4 } from "@odla-ai/db";
|
|
6145
|
+
async function gate7(req, env, ctx) {
|
|
5729
6146
|
const db = ctx.makeDb(env);
|
|
5730
6147
|
const user = await ctx.verifyUser(req, env);
|
|
5731
6148
|
if (!user) return { response: json({ error: "unauthorized" }, 401) };
|
|
5732
6149
|
if (!await ctx.isAdmin(db, user)) return { response: json({ error: "forbidden" }, 403) };
|
|
5733
6150
|
return { db, user };
|
|
5734
6151
|
}
|
|
5735
|
-
function
|
|
6152
|
+
function targetFor2(ctx, id) {
|
|
5736
6153
|
return ctx.chapter.network.targets.find((target) => target.id === id);
|
|
5737
6154
|
}
|
|
5738
6155
|
async function readTargetRecords(db, env, ctx, target, query) {
|
|
@@ -5741,7 +6158,7 @@ async function readTargetRecords(db, env, ctx, target, query) {
|
|
|
5741
6158
|
const destination = new URL("/api/network/records", target.url);
|
|
5742
6159
|
destination.search = query.toString();
|
|
5743
6160
|
try {
|
|
5744
|
-
const headers = await
|
|
6161
|
+
const headers = await signFederatedRequest4({ secret, sender: ctx.chapter.id, method: "GET", url: destination });
|
|
5745
6162
|
const response = await fetchNetworkTarget(env, target, destination, {
|
|
5746
6163
|
headers,
|
|
5747
6164
|
signal: AbortSignal.timeout(1e4)
|
|
@@ -5777,7 +6194,7 @@ async function mutateTargetRecord(db, env, ctx, target, input) {
|
|
|
5777
6194
|
const destination = new URL("/api/network/records", target.url);
|
|
5778
6195
|
const body = JSON.stringify(input);
|
|
5779
6196
|
try {
|
|
5780
|
-
const headers = await
|
|
6197
|
+
const headers = await signFederatedRequest4({
|
|
5781
6198
|
secret,
|
|
5782
6199
|
sender: ctx.chapter.id,
|
|
5783
6200
|
method: "PATCH",
|
|
@@ -5809,7 +6226,7 @@ var handleAdminNetworkRecords = async (req, url, env, ctx) => {
|
|
|
5809
6226
|
if (req.method === "PATCH" && Number(req.headers.get("content-length") ?? 0) > MAX_BODY) {
|
|
5810
6227
|
return json({ error: "mutation body is too large", code: "body_too_large" }, 413);
|
|
5811
6228
|
}
|
|
5812
|
-
const got = await
|
|
6229
|
+
const got = await gate7(req, env, ctx);
|
|
5813
6230
|
if ("response" in got) return got.response;
|
|
5814
6231
|
if (req.method === "PATCH") {
|
|
5815
6232
|
let input;
|
|
@@ -5822,10 +6239,10 @@ var handleAdminNetworkRecords = async (req, url, env, ctx) => {
|
|
|
5822
6239
|
} catch {
|
|
5823
6240
|
return json({ error: "invalid JSON body", code: "invalid_json" }, 422);
|
|
5824
6241
|
}
|
|
5825
|
-
const target2 =
|
|
6242
|
+
const target2 = targetFor2(ctx, input?.targetId);
|
|
5826
6243
|
const hasFields = Boolean(input?.fields && typeof input.fields === "object" && !Array.isArray(input.fields));
|
|
5827
6244
|
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 || !
|
|
6245
|
+
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
6246
|
return json({ error: "invalid network mutation", code: "invalid_mutation" }, 422);
|
|
5830
6247
|
}
|
|
5831
6248
|
return mutateTargetRecord(got.db, env, ctx, target2, {
|
|
@@ -5840,7 +6257,7 @@ var handleAdminNetworkRecords = async (req, url, env, ctx) => {
|
|
|
5840
6257
|
}
|
|
5841
6258
|
const targetId = url.searchParams.get("targetId") ?? "";
|
|
5842
6259
|
const type = url.searchParams.get("type") ?? "";
|
|
5843
|
-
const target =
|
|
6260
|
+
const target = targetFor2(ctx, targetId);
|
|
5844
6261
|
if (!target || !IDENTIFIER.test(type)) return json({ error: "configured targetId and record type are required" }, 400);
|
|
5845
6262
|
const limit = integer(url.searchParams.get("limit"), 25, 1, MAX_PAGE);
|
|
5846
6263
|
const offset = integer(url.searchParams.get("offset"), 0, 0, MAX_OFFSET);
|
|
@@ -5857,8 +6274,8 @@ var handleAdminNetworkRecords = async (req, url, env, ctx) => {
|
|
|
5857
6274
|
// src/worker-routes-network-notes.ts
|
|
5858
6275
|
var IDENTIFIER2 = /^[a-z][a-zA-Z0-9_]*$/;
|
|
5859
6276
|
var RECORD_ID2 = /^[A-Za-z0-9][A-Za-z0-9_.:-]{0,255}$/;
|
|
5860
|
-
var
|
|
5861
|
-
async function
|
|
6277
|
+
var MAX_NOTE3 = 4e3;
|
|
6278
|
+
async function gate8(req, env, ctx) {
|
|
5862
6279
|
const db = ctx.makeDb(env);
|
|
5863
6280
|
const user = await ctx.verifyUser(req, env);
|
|
5864
6281
|
if (!user) return { response: json({ error: "unauthorized" }, 401) };
|
|
@@ -5892,7 +6309,7 @@ function asNote(row) {
|
|
|
5892
6309
|
}
|
|
5893
6310
|
var handleAdminNetworkNotes = async (req, url, env, ctx) => {
|
|
5894
6311
|
if (url.pathname !== "/api/admin/network/notes" || req.method !== "GET" && req.method !== "POST") return null;
|
|
5895
|
-
const got = await
|
|
6312
|
+
const got = await gate8(req, env, ctx);
|
|
5896
6313
|
if ("response" in got) return got.response;
|
|
5897
6314
|
const identity = noteIdentity(url, ctx);
|
|
5898
6315
|
if (!identity) return json({ error: "valid targetId, type, and recordId are required" }, 400);
|
|
@@ -5909,7 +6326,7 @@ var handleAdminNetworkNotes = async (req, url, env, ctx) => {
|
|
|
5909
6326
|
return json({ notes: (result.networkNotes ?? []).map((row) => asNote(row)) });
|
|
5910
6327
|
}
|
|
5911
6328
|
const contentLength = Number(req.headers.get("content-length") ?? 0);
|
|
5912
|
-
if (contentLength >
|
|
6329
|
+
if (contentLength > MAX_NOTE3 * 2) return json({ error: "note is too large" }, 413);
|
|
5913
6330
|
let input;
|
|
5914
6331
|
try {
|
|
5915
6332
|
input = await req.json();
|
|
@@ -5917,7 +6334,7 @@ var handleAdminNetworkNotes = async (req, url, env, ctx) => {
|
|
|
5917
6334
|
return json({ error: "invalid JSON body" }, 400);
|
|
5918
6335
|
}
|
|
5919
6336
|
const body = typeof input.body === "string" ? input.body.trim() : "";
|
|
5920
|
-
if (!body || body.length >
|
|
6337
|
+
if (!body || body.length > MAX_NOTE3) return json({ error: `note must be 1\u2013${MAX_NOTE3} characters` }, 400);
|
|
5921
6338
|
const id = crypto.randomUUID();
|
|
5922
6339
|
const note = {
|
|
5923
6340
|
id,
|
|
@@ -5942,15 +6359,15 @@ var handleAdminNetworkNotes = async (req, url, env, ctx) => {
|
|
|
5942
6359
|
import {
|
|
5943
6360
|
addActivity,
|
|
5944
6361
|
CRM_NS as CRM_NS2,
|
|
5945
|
-
getRecord as
|
|
6362
|
+
getRecord as getRecord5
|
|
5946
6363
|
} from "@odla-ai/crm";
|
|
5947
|
-
import { signFederatedRequest as
|
|
6364
|
+
import { signFederatedRequest as signFederatedRequest5, verifyFederatedRequest as verifyFederatedRequest7 } from "@odla-ai/db";
|
|
5948
6365
|
var IDENTIFIER3 = /^[a-z][a-zA-Z0-9_]*$/;
|
|
5949
6366
|
var RECORD_ID3 = /^[A-Za-z0-9][A-Za-z0-9_.:-]{0,255}$/;
|
|
5950
|
-
var
|
|
5951
|
-
var
|
|
6367
|
+
var MUTATION_ID3 = /^[A-Za-z0-9_-]{16,128}$/;
|
|
6368
|
+
var MAX_NOTE4 = 4e3;
|
|
5952
6369
|
var NETWORK_META = "chapterNetwork";
|
|
5953
|
-
async function
|
|
6370
|
+
async function gate9(req, env, ctx) {
|
|
5954
6371
|
const db = ctx.makeDb(env);
|
|
5955
6372
|
const user = await ctx.verifyUser(req, env);
|
|
5956
6373
|
if (!user) return { response: json({ error: "unauthorized" }, 401) };
|
|
@@ -5996,7 +6413,7 @@ function sourceLabel(sourceId) {
|
|
|
5996
6413
|
}
|
|
5997
6414
|
var handleNetworkSharedNotes = async (req, url, env, ctx) => {
|
|
5998
6415
|
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) >
|
|
6416
|
+
if (req.method === "POST" && Number(req.headers.get("content-length") ?? 0) > MAX_NOTE4 * 2) {
|
|
6000
6417
|
return json({ error: "note is too large" }, 413);
|
|
6001
6418
|
}
|
|
6002
6419
|
const db = ctx.makeDb(env);
|
|
@@ -6014,7 +6431,7 @@ var handleNetworkSharedNotes = async (req, url, env, ctx) => {
|
|
|
6014
6431
|
if (!IDENTIFIER3.test(recordType) || !RECORD_ID3.test(recordId) || !reader?.sharedNotes.includes(recordType)) {
|
|
6015
6432
|
return json({ error: "shared notes are not enabled for this reader and record type" }, 403);
|
|
6016
6433
|
}
|
|
6017
|
-
const record4 = await
|
|
6434
|
+
const record4 = await getRecord5({ crm: ctx.chapter.crm, db }, recordId);
|
|
6018
6435
|
if (!record4 || record4.type !== recordType) return json({ error: "record not found" }, 404);
|
|
6019
6436
|
if (req.method === "GET") {
|
|
6020
6437
|
return json({
|
|
@@ -6031,8 +6448,8 @@ var handleNetworkSharedNotes = async (req, url, env, ctx) => {
|
|
|
6031
6448
|
}
|
|
6032
6449
|
const body = typeof input.body === "string" ? input.body.trim() : "";
|
|
6033
6450
|
const mutationId = typeof input.mutationId === "string" ? input.mutationId : "";
|
|
6034
|
-
if (!body || body.length >
|
|
6035
|
-
return json({ error: `body must be 1\u2013${
|
|
6451
|
+
if (!body || body.length > MAX_NOTE4 || !MUTATION_ID3.test(mutationId)) {
|
|
6452
|
+
return json({ error: `body must be 1\u2013${MAX_NOTE4} characters and mutationId must be valid` }, 400);
|
|
6036
6453
|
}
|
|
6037
6454
|
const createdAt = Date.now();
|
|
6038
6455
|
const id = `network:${verified.sender}:${mutationId}`;
|
|
@@ -6069,17 +6486,17 @@ var handleNetworkSharedNotes = async (req, url, env, ctx) => {
|
|
|
6069
6486
|
};
|
|
6070
6487
|
return json({ version: 1, site: { id: ctx.chapter.id, name: ctx.chapter.name }, note, duplicate: result.duplicate }, 201);
|
|
6071
6488
|
};
|
|
6072
|
-
function
|
|
6489
|
+
function targetFor3(ctx, id) {
|
|
6073
6490
|
return ctx.chapter.network.targets.find((target) => target.id === id);
|
|
6074
6491
|
}
|
|
6075
6492
|
var handleAdminNetworkSharedNotes = async (req, url, env, ctx) => {
|
|
6076
6493
|
if (url.pathname !== "/api/admin/network/shared-notes" || req.method !== "GET" && req.method !== "POST") return null;
|
|
6077
|
-
const got = await
|
|
6494
|
+
const got = await gate9(req, env, ctx);
|
|
6078
6495
|
if ("response" in got) return got.response;
|
|
6079
6496
|
const targetId = url.searchParams.get("targetId") ?? "";
|
|
6080
6497
|
const recordType = url.searchParams.get("type") ?? "";
|
|
6081
6498
|
const recordId = url.searchParams.get("recordId") ?? "";
|
|
6082
|
-
const target =
|
|
6499
|
+
const target = targetFor3(ctx, targetId);
|
|
6083
6500
|
if (!target || !target.sharedNotes.includes(recordType) || !IDENTIFIER3.test(recordType) || !RECORD_ID3.test(recordId)) {
|
|
6084
6501
|
return json({ error: "configured targetId, type, and recordId are required" }, 400);
|
|
6085
6502
|
}
|
|
@@ -6092,7 +6509,7 @@ var handleAdminNetworkSharedNotes = async (req, url, env, ctx) => {
|
|
|
6092
6509
|
return json({ error: "invalid JSON body" }, 400);
|
|
6093
6510
|
}
|
|
6094
6511
|
const note = typeof input.body === "string" ? input.body.trim() : "";
|
|
6095
|
-
if (!note || note.length >
|
|
6512
|
+
if (!note || note.length > MAX_NOTE4) return json({ error: `note must be 1\u2013${MAX_NOTE4} characters` }, 400);
|
|
6096
6513
|
body = JSON.stringify({ body: note, mutationId: crypto.randomUUID() });
|
|
6097
6514
|
}
|
|
6098
6515
|
const secret = await getVaultSecret(got.db, target.secretName);
|
|
@@ -6100,7 +6517,7 @@ var handleAdminNetworkSharedNotes = async (req, url, env, ctx) => {
|
|
|
6100
6517
|
const destination = new URL("/api/network/shared-notes", target.url);
|
|
6101
6518
|
destination.search = new URLSearchParams({ type: recordType, recordId }).toString();
|
|
6102
6519
|
try {
|
|
6103
|
-
const headers = await
|
|
6520
|
+
const headers = await signFederatedRequest5({
|
|
6104
6521
|
secret,
|
|
6105
6522
|
sender: ctx.chapter.id,
|
|
6106
6523
|
method: req.method,
|
|
@@ -6692,11 +7109,11 @@ var handleAdminBuilderLibrary = async (req, url, env, ctx) => {
|
|
|
6692
7109
|
};
|
|
6693
7110
|
|
|
6694
7111
|
// src/signup-control-delivery.ts
|
|
6695
|
-
import { signFederatedRequest as
|
|
7112
|
+
import { signFederatedRequest as signFederatedRequest6 } from "@odla-ai/db";
|
|
6696
7113
|
async function deliverSignupControl(options) {
|
|
6697
7114
|
const destination = new URL(SIGNUP_CONTROL_PATH, options.targetUrl);
|
|
6698
7115
|
const body = JSON.stringify(options.envelope);
|
|
6699
|
-
const signed = await
|
|
7116
|
+
const signed = await signFederatedRequest6({
|
|
6700
7117
|
secret: options.secret,
|
|
6701
7118
|
sender: options.sender,
|
|
6702
7119
|
method: "POST",
|
|
@@ -6717,11 +7134,11 @@ async function deliverSignupControl(options) {
|
|
|
6717
7134
|
}
|
|
6718
7135
|
|
|
6719
7136
|
// src/member-content-delivery.ts
|
|
6720
|
-
import { signFederatedRequest as
|
|
7137
|
+
import { signFederatedRequest as signFederatedRequest7 } from "@odla-ai/db";
|
|
6721
7138
|
async function deliverMemberContent(options) {
|
|
6722
7139
|
const destination = new URL(MEMBER_CONTENT_CONTROL_PATH, options.targetUrl);
|
|
6723
7140
|
const body = JSON.stringify(options.envelope);
|
|
6724
|
-
const signed = await
|
|
7141
|
+
const signed = await signFederatedRequest7({
|
|
6725
7142
|
secret: options.secret,
|
|
6726
7143
|
sender: options.sender,
|
|
6727
7144
|
method: "POST",
|
|
@@ -6742,11 +7159,11 @@ async function deliverMemberContent(options) {
|
|
|
6742
7159
|
}
|
|
6743
7160
|
|
|
6744
7161
|
// src/builder-library-delivery.ts
|
|
6745
|
-
import { signFederatedRequest as
|
|
7162
|
+
import { signFederatedRequest as signFederatedRequest8 } from "@odla-ai/db";
|
|
6746
7163
|
async function deliverBuilderLibrary(options) {
|
|
6747
7164
|
const destination = new URL(BUILDER_LIBRARY_CONTROL_PATH, options.targetUrl);
|
|
6748
7165
|
const body = JSON.stringify(options.envelope);
|
|
6749
|
-
const signed = await
|
|
7166
|
+
const signed = await signFederatedRequest8({ secret: options.secret, sender: options.sender, method: "POST", url: destination, body });
|
|
6750
7167
|
const response = await (options.fetch ?? globalThis.fetch)(destination, { method: "POST", headers: { ...signed, "content-type": "application/json" }, body });
|
|
6751
7168
|
const result = await response.json();
|
|
6752
7169
|
if (!response.ok || !("ok" in result) || result.ok !== true) {
|
|
@@ -6756,31 +7173,6 @@ async function deliverBuilderLibrary(options) {
|
|
|
6756
7173
|
return result;
|
|
6757
7174
|
}
|
|
6758
7175
|
|
|
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
7176
|
// src/signup-control-theme.ts
|
|
6785
7177
|
var SIGNUP_CONTROL_THEME_MARKER = "<!--odla:signup-control-theme-->";
|
|
6786
7178
|
var SIGNUP_CONTROL_THEME_STYLE_ID = "odla-signup-control-theme";
|
|
@@ -6940,6 +7332,7 @@ var BUILTIN_ROUTES = [
|
|
|
6940
7332
|
handleAdminPeopleRole,
|
|
6941
7333
|
handleAdminCrmSync,
|
|
6942
7334
|
handleAdminAdmissionGrants,
|
|
7335
|
+
handleAdminNetworkAdmissions,
|
|
6943
7336
|
handleAdminClerkPrivateProfileSync,
|
|
6944
7337
|
// Aggregation
|
|
6945
7338
|
handleAdminDashboard,
|
|
@@ -6984,6 +7377,7 @@ function chapterWorker(options) {
|
|
|
6984
7377
|
};
|
|
6985
7378
|
}
|
|
6986
7379
|
export {
|
|
7380
|
+
AdmissionGrantDeliveryError,
|
|
6987
7381
|
chapterWorker,
|
|
6988
7382
|
createWorkerContext,
|
|
6989
7383
|
deliverAdmissionGrants,
|
|
@@ -6991,6 +7385,7 @@ export {
|
|
|
6991
7385
|
deliverMemberContent,
|
|
6992
7386
|
deliverSignupControl,
|
|
6993
7387
|
renderActiveSignupControlHtml,
|
|
7388
|
+
retryLeaderAdmissionDeliveries,
|
|
6994
7389
|
withActiveSignupControlTheme
|
|
6995
7390
|
};
|
|
6996
7391
|
//# sourceMappingURL=index.js.map
|