@odla-ai/chapter 0.38.0 → 0.39.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +20 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +20 -8
- package/dist/index.js.map +1 -1
- package/dist/worker/index.cjs +78 -38
- package/dist/worker/index.cjs.map +1 -1
- package/dist/worker/index.js +78 -38
- package/dist/worker/index.js.map +1 -1
- package/package.json +1 -1
package/dist/worker/index.cjs
CHANGED
|
@@ -1225,10 +1225,10 @@ async function syncApplicationToCrm(deps, opts) {
|
|
|
1225
1225
|
return recordId;
|
|
1226
1226
|
}
|
|
1227
1227
|
async function backfillCrm(deps) {
|
|
1228
|
-
const
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1228
|
+
const data = await deps.db.query({
|
|
1229
|
+
applications: { $: { order: { createdAt: "desc" }, limit: 1e3 } },
|
|
1230
|
+
$users: { $: { limit: 1e3 } }
|
|
1231
|
+
});
|
|
1232
1232
|
const seen = /* @__PURE__ */ new Set();
|
|
1233
1233
|
let synced = 0;
|
|
1234
1234
|
const errors = [];
|
|
@@ -1243,8 +1243,8 @@ async function backfillCrm(deps) {
|
|
|
1243
1243
|
errors.push({ email: key, error: err instanceof Error ? err.message : String(err) });
|
|
1244
1244
|
}
|
|
1245
1245
|
};
|
|
1246
|
-
for (const a of
|
|
1247
|
-
for (const u of
|
|
1246
|
+
for (const a of data.applications ?? []) await run(a, str(a.status));
|
|
1247
|
+
for (const u of data.$users ?? []) {
|
|
1248
1248
|
if (u.deleted === true) continue;
|
|
1249
1249
|
await run({ email: u.email, firstName: str(u.name) });
|
|
1250
1250
|
}
|
|
@@ -3373,9 +3373,21 @@ function webhookMutationId(eventId) {
|
|
|
3373
3373
|
function findApplicationRef(obj) {
|
|
3374
3374
|
const metaOf = (v) => v && typeof v === "object" ? v.metadata ?? {} : {};
|
|
3375
3375
|
const pick = (m) => typeof m.applicationId === "string" ? m.applicationId : void 0;
|
|
3376
|
-
const
|
|
3376
|
+
const candidates = [
|
|
3377
|
+
metaOf(obj),
|
|
3378
|
+
metaOf(obj.subscription_details),
|
|
3379
|
+
metaOf(obj.parent?.subscription_details)
|
|
3380
|
+
];
|
|
3381
|
+
const applicationId = candidates.map(pick).find(Boolean);
|
|
3382
|
+
const appId = candidates.map((metadata) => typeof metadata.odlaAppId === "string" ? metadata.odlaAppId : void 0).find(Boolean);
|
|
3383
|
+
const environment = candidates.map((metadata) => typeof metadata.odlaEnvironment === "string" ? metadata.odlaEnvironment : void 0).find(Boolean);
|
|
3377
3384
|
const customerId = typeof obj.customer === "string" ? obj.customer : void 0;
|
|
3378
|
-
return {
|
|
3385
|
+
return {
|
|
3386
|
+
...applicationId ? { applicationId } : {},
|
|
3387
|
+
...customerId ? { customerId } : {},
|
|
3388
|
+
...appId ? { appId } : {},
|
|
3389
|
+
...environment ? { environment } : {}
|
|
3390
|
+
};
|
|
3379
3391
|
}
|
|
3380
3392
|
function findGiftRef(obj) {
|
|
3381
3393
|
const metaOf = (value) => value && typeof value === "object" ? value.metadata ?? {} : {};
|
|
@@ -3466,7 +3478,13 @@ function createStripeProvider(config) {
|
|
|
3466
3478
|
return {
|
|
3467
3479
|
async createSubscription(input) {
|
|
3468
3480
|
const sk = requireSecret(secretKey);
|
|
3469
|
-
const meta = {
|
|
3481
|
+
const meta = {
|
|
3482
|
+
applicationId: input.applicationId,
|
|
3483
|
+
groupId: input.groupId,
|
|
3484
|
+
email: input.email,
|
|
3485
|
+
odlaAppId: input.appId,
|
|
3486
|
+
odlaEnvironment: input.environment
|
|
3487
|
+
};
|
|
3470
3488
|
let customerId = input.existingCustomerId;
|
|
3471
3489
|
if (!customerId) {
|
|
3472
3490
|
const cust = await stripeCall(
|
|
@@ -3986,6 +4004,8 @@ async function startSubscription(req, env, ctx) {
|
|
|
3986
4004
|
let result;
|
|
3987
4005
|
try {
|
|
3988
4006
|
result = await provider.createSubscription({
|
|
4007
|
+
appId: env.ODLA_APP_ID,
|
|
4008
|
+
environment: env.ODLA_ENV,
|
|
3989
4009
|
applicationId,
|
|
3990
4010
|
groupId: String(group.id),
|
|
3991
4011
|
email: String(app.email ?? ""),
|
|
@@ -4015,6 +4035,10 @@ async function ingestWebhook(req, env, ctx) {
|
|
|
4015
4035
|
if (!ingest.ok) return json({ error: "invalid signature" }, 400);
|
|
4016
4036
|
const { eventId, event } = ingest;
|
|
4017
4037
|
if (event.kind === "ignored") return json({ ok: true, ignored: event.type });
|
|
4038
|
+
const identifiedEnvironment = event.appId !== void 0 || event.environment !== void 0;
|
|
4039
|
+
if (env.ODLA_APP_ID && identifiedEnvironment && (event.appId !== env.ODLA_APP_ID || event.environment !== env.ODLA_ENV)) {
|
|
4040
|
+
return json({ ok: true, matched: false });
|
|
4041
|
+
}
|
|
4018
4042
|
if (event.kind === "gift_paid" || event.kind === "gift_revoked") {
|
|
4019
4043
|
const intent = await firstRow2(db, "giftPurchaseIntents", { where: { id: event.giftIntentId }, limit: 1 });
|
|
4020
4044
|
if (!intent || intent.chapterId !== ctx.chapter.id || intent.environment !== env.ODLA_ENV) {
|
|
@@ -4369,14 +4393,16 @@ var handleAdminPeople = async (req, url, env, ctx) => {
|
|
|
4369
4393
|
if (gate9 instanceof Response) return gate9;
|
|
4370
4394
|
const { db } = gate9;
|
|
4371
4395
|
const sk = await getVaultSecret(db, "clerk_secret_key");
|
|
4372
|
-
const [
|
|
4373
|
-
db.query({
|
|
4374
|
-
|
|
4396
|
+
const [data, roleList] = await Promise.all([
|
|
4397
|
+
db.query({
|
|
4398
|
+
applications: { $: { order: { createdAt: "desc" }, limit: 200 } },
|
|
4399
|
+
$users: { $: { limit: 200 } }
|
|
4400
|
+
}),
|
|
4375
4401
|
sk ? clerkListUsers(sk).catch(() => []) : Promise.resolve([])
|
|
4376
4402
|
]);
|
|
4377
4403
|
const clerkByUserId = new Map(roleList.map((u) => [u.id, u]));
|
|
4378
4404
|
const people = /* @__PURE__ */ new Map();
|
|
4379
|
-
for (const u of
|
|
4405
|
+
for (const u of data.$users ?? []) {
|
|
4380
4406
|
if (u.deleted === true) continue;
|
|
4381
4407
|
const email = typeof u.email === "string" ? u.email : "";
|
|
4382
4408
|
if (!email) continue;
|
|
@@ -4391,7 +4417,7 @@ var handleAdminPeople = async (req, url, env, ctx) => {
|
|
|
4391
4417
|
application: null
|
|
4392
4418
|
});
|
|
4393
4419
|
}
|
|
4394
|
-
for (const a of
|
|
4420
|
+
for (const a of data.applications ?? []) {
|
|
4395
4421
|
const key = String(a.email ?? "").toLowerCase();
|
|
4396
4422
|
if (!key) continue;
|
|
4397
4423
|
const name = `${a.firstName ?? ""} ${a.lastName ?? ""}`.trim();
|
|
@@ -4596,13 +4622,19 @@ var handleAdminDashboard = async (req, url, env, ctx) => {
|
|
|
4596
4622
|
const now = Date.now();
|
|
4597
4623
|
const d7 = now - 7 * 864e5;
|
|
4598
4624
|
const d30 = now - 30 * 864e5;
|
|
4599
|
-
const
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4625
|
+
const data = await db.query({
|
|
4626
|
+
applications: { $: { order: { createdAt: "desc" }, limit: 1e3 } },
|
|
4627
|
+
meetings: {
|
|
4628
|
+
$: {
|
|
4629
|
+
where: { status: "scheduled" },
|
|
4630
|
+
order: { startAt: "asc" },
|
|
4631
|
+
limit: 500
|
|
4632
|
+
}
|
|
4633
|
+
},
|
|
4634
|
+
crm_record: { $: { where: { type: "person" }, limit: 1e3 } },
|
|
4635
|
+
groups: { $: { where: { id: ctx.chapter.id }, limit: 1 } }
|
|
4636
|
+
});
|
|
4637
|
+
const apps = rows3(data.applications);
|
|
4606
4638
|
const applications = {
|
|
4607
4639
|
total: apps.length,
|
|
4608
4640
|
last7: apps.filter((a) => a.createdAt >= d7).length,
|
|
@@ -4614,7 +4646,7 @@ var handleAdminDashboard = async (req, url, env, ctx) => {
|
|
|
4614
4646
|
pipeline[s] = 0;
|
|
4615
4647
|
pipelineDelta[s] = 0;
|
|
4616
4648
|
}
|
|
4617
|
-
for (const r of rows3(
|
|
4649
|
+
for (const r of rows3(data.crm_record)) {
|
|
4618
4650
|
const s = r.stage;
|
|
4619
4651
|
if (s in pipeline) {
|
|
4620
4652
|
pipeline[s] = (pipeline[s] ?? 0) + 1;
|
|
@@ -4622,7 +4654,7 @@ var handleAdminDashboard = async (req, url, env, ctx) => {
|
|
|
4622
4654
|
if (Number.isFinite(sc) && sc >= d7) pipelineDelta[s] = (pipelineDelta[s] ?? 0) + 1;
|
|
4623
4655
|
}
|
|
4624
4656
|
}
|
|
4625
|
-
const meetings = rows3(
|
|
4657
|
+
const meetings = rows3(data.meetings);
|
|
4626
4658
|
const appById = new Map(apps.map((a) => [a.id, a]));
|
|
4627
4659
|
const upcoming = meetings.filter((m) => m.startAt >= now - 36e5);
|
|
4628
4660
|
const calls = { upcoming: upcoming.length, needsAttention: meetings.filter((m) => m.drift && m.drift !== "none").length };
|
|
@@ -4638,7 +4670,7 @@ var handleAdminDashboard = async (req, url, env, ctx) => {
|
|
|
4638
4670
|
email: a?.email ?? null
|
|
4639
4671
|
};
|
|
4640
4672
|
});
|
|
4641
|
-
const group =
|
|
4673
|
+
const group = data.groups?.[0];
|
|
4642
4674
|
const timezone = resolveScheduling(group?.schedulingJson).timezone;
|
|
4643
4675
|
let revenue = { billingReady: false };
|
|
4644
4676
|
let revenueSeries = null;
|
|
@@ -4672,16 +4704,18 @@ var handleAdminBilling = async (req, url, env, ctx) => {
|
|
|
4672
4704
|
const db = got;
|
|
4673
4705
|
const sk = await getVaultSecret(db, "stripe_secret_key");
|
|
4674
4706
|
if (!sk) return json({ billingReady: false, rows: [], summary: null });
|
|
4675
|
-
const [
|
|
4676
|
-
db.query({
|
|
4677
|
-
|
|
4678
|
-
|
|
4707
|
+
const [data, subsRes] = await Promise.all([
|
|
4708
|
+
db.query({
|
|
4709
|
+
applications: { $: { order: { createdAt: "desc" }, limit: 200 } },
|
|
4710
|
+
groups: { $: { where: { id: ctx.chapter.id }, limit: 1 } }
|
|
4711
|
+
}),
|
|
4712
|
+
stripeCall(sk, "GET", "/v1/subscriptions", { limit: 100, status: "all" })
|
|
4679
4713
|
]);
|
|
4680
|
-
const testMode = String(
|
|
4714
|
+
const testMode = String(data.groups?.[0]?.stripePublishableKey ?? "").startsWith("pk_test");
|
|
4681
4715
|
if (!subsRes.ok) return json({ error: "billing lookup failed upstream" }, 502);
|
|
4682
4716
|
const subById = new Map(rows3(subsRes.body.data).map((s) => [s.id, s]));
|
|
4683
4717
|
const billingRows = [];
|
|
4684
|
-
for (const a of rows3(
|
|
4718
|
+
for (const a of rows3(data.applications)) {
|
|
4685
4719
|
if (!a.stripeCustomerId && !a.stripeSubscriptionId) continue;
|
|
4686
4720
|
const sub = a.stripeSubscriptionId ? subById.get(a.stripeSubscriptionId) : void 0;
|
|
4687
4721
|
const items = sub?.items?.data ?? [];
|
|
@@ -5021,21 +5055,27 @@ var handleAdminComms = async (req, url, env, ctx) => {
|
|
|
5021
5055
|
if (got instanceof Response) return got;
|
|
5022
5056
|
const db = got;
|
|
5023
5057
|
const appId = m[1];
|
|
5024
|
-
const
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5058
|
+
const data = await db.query({
|
|
5059
|
+
emailLog: {
|
|
5060
|
+
$: {
|
|
5061
|
+
where: { applicationId: appId },
|
|
5062
|
+
order: { sentAt: "desc" },
|
|
5063
|
+
limit: 100
|
|
5064
|
+
}
|
|
5065
|
+
},
|
|
5066
|
+
meetings: { $: { where: { applicationId: appId } } },
|
|
5067
|
+
applications: { $: { where: { id: appId }, limit: 1 } }
|
|
5068
|
+
});
|
|
5069
|
+
const app = data.applications?.[0] ?? null;
|
|
5030
5070
|
const group = await loadGroup2(db, str2(app?.groupId, ctx.chapter.id));
|
|
5031
5071
|
const vars = app ? { firstName: str2(app.firstName), lastName: str2(app.lastName), email: str2(app.email), phone: str2(app.phone), state: str2(app.state), adminUrl: `${url.origin}/admin/`, membersUrl: `${url.origin}/members/` } : null;
|
|
5032
|
-
const emails = (
|
|
5072
|
+
const emails = (data.emailLog ?? []).filter((r) => r.template !== "adminNotification").map((r) => {
|
|
5033
5073
|
let mailBody = typeof r.body === "string" ? r.body : null;
|
|
5034
5074
|
if (!mailBody && group && vars) mailBody = renderTemplateBody(emailGroupFrom(group), String(r.template), vars);
|
|
5035
5075
|
const channel = r.error ? "email (failed)" : r.redirected ? "email (dev-redirected)" : r.transport === "log-only" ? "email (not delivered)" : "email";
|
|
5036
5076
|
return { kind: "email", channel, label: String(r.template), subject: str2(r.subject), to: r.to ?? null, body: mailBody, at: r.sentAt, error: r.error ?? null };
|
|
5037
5077
|
});
|
|
5038
|
-
const calendar = (
|
|
5078
|
+
const calendar = (data.meetings ?? []).map((m2) => ({
|
|
5039
5079
|
kind: "calendar",
|
|
5040
5080
|
channel: "Google Calendar",
|
|
5041
5081
|
label: m2.status === "cancelled" ? "Invitation (call later cancelled)" : "Meeting invitation",
|