@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.js
CHANGED
|
@@ -1198,10 +1198,10 @@ async function syncApplicationToCrm(deps, opts) {
|
|
|
1198
1198
|
return recordId;
|
|
1199
1199
|
}
|
|
1200
1200
|
async function backfillCrm(deps) {
|
|
1201
|
-
const
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1201
|
+
const data = await deps.db.query({
|
|
1202
|
+
applications: { $: { order: { createdAt: "desc" }, limit: 1e3 } },
|
|
1203
|
+
$users: { $: { limit: 1e3 } }
|
|
1204
|
+
});
|
|
1205
1205
|
const seen = /* @__PURE__ */ new Set();
|
|
1206
1206
|
let synced = 0;
|
|
1207
1207
|
const errors = [];
|
|
@@ -1216,8 +1216,8 @@ async function backfillCrm(deps) {
|
|
|
1216
1216
|
errors.push({ email: key, error: err instanceof Error ? err.message : String(err) });
|
|
1217
1217
|
}
|
|
1218
1218
|
};
|
|
1219
|
-
for (const a of
|
|
1220
|
-
for (const u of
|
|
1219
|
+
for (const a of data.applications ?? []) await run(a, str(a.status));
|
|
1220
|
+
for (const u of data.$users ?? []) {
|
|
1221
1221
|
if (u.deleted === true) continue;
|
|
1222
1222
|
await run({ email: u.email, firstName: str(u.name) });
|
|
1223
1223
|
}
|
|
@@ -3346,9 +3346,21 @@ function webhookMutationId(eventId) {
|
|
|
3346
3346
|
function findApplicationRef(obj) {
|
|
3347
3347
|
const metaOf = (v) => v && typeof v === "object" ? v.metadata ?? {} : {};
|
|
3348
3348
|
const pick = (m) => typeof m.applicationId === "string" ? m.applicationId : void 0;
|
|
3349
|
-
const
|
|
3349
|
+
const candidates = [
|
|
3350
|
+
metaOf(obj),
|
|
3351
|
+
metaOf(obj.subscription_details),
|
|
3352
|
+
metaOf(obj.parent?.subscription_details)
|
|
3353
|
+
];
|
|
3354
|
+
const applicationId = candidates.map(pick).find(Boolean);
|
|
3355
|
+
const appId = candidates.map((metadata) => typeof metadata.odlaAppId === "string" ? metadata.odlaAppId : void 0).find(Boolean);
|
|
3356
|
+
const environment = candidates.map((metadata) => typeof metadata.odlaEnvironment === "string" ? metadata.odlaEnvironment : void 0).find(Boolean);
|
|
3350
3357
|
const customerId = typeof obj.customer === "string" ? obj.customer : void 0;
|
|
3351
|
-
return {
|
|
3358
|
+
return {
|
|
3359
|
+
...applicationId ? { applicationId } : {},
|
|
3360
|
+
...customerId ? { customerId } : {},
|
|
3361
|
+
...appId ? { appId } : {},
|
|
3362
|
+
...environment ? { environment } : {}
|
|
3363
|
+
};
|
|
3352
3364
|
}
|
|
3353
3365
|
function findGiftRef(obj) {
|
|
3354
3366
|
const metaOf = (value) => value && typeof value === "object" ? value.metadata ?? {} : {};
|
|
@@ -3439,7 +3451,13 @@ function createStripeProvider(config) {
|
|
|
3439
3451
|
return {
|
|
3440
3452
|
async createSubscription(input) {
|
|
3441
3453
|
const sk = requireSecret(secretKey);
|
|
3442
|
-
const meta = {
|
|
3454
|
+
const meta = {
|
|
3455
|
+
applicationId: input.applicationId,
|
|
3456
|
+
groupId: input.groupId,
|
|
3457
|
+
email: input.email,
|
|
3458
|
+
odlaAppId: input.appId,
|
|
3459
|
+
odlaEnvironment: input.environment
|
|
3460
|
+
};
|
|
3443
3461
|
let customerId = input.existingCustomerId;
|
|
3444
3462
|
if (!customerId) {
|
|
3445
3463
|
const cust = await stripeCall(
|
|
@@ -3959,6 +3977,8 @@ async function startSubscription(req, env, ctx) {
|
|
|
3959
3977
|
let result;
|
|
3960
3978
|
try {
|
|
3961
3979
|
result = await provider.createSubscription({
|
|
3980
|
+
appId: env.ODLA_APP_ID,
|
|
3981
|
+
environment: env.ODLA_ENV,
|
|
3962
3982
|
applicationId,
|
|
3963
3983
|
groupId: String(group.id),
|
|
3964
3984
|
email: String(app.email ?? ""),
|
|
@@ -3988,6 +4008,10 @@ async function ingestWebhook(req, env, ctx) {
|
|
|
3988
4008
|
if (!ingest.ok) return json({ error: "invalid signature" }, 400);
|
|
3989
4009
|
const { eventId, event } = ingest;
|
|
3990
4010
|
if (event.kind === "ignored") return json({ ok: true, ignored: event.type });
|
|
4011
|
+
const identifiedEnvironment = event.appId !== void 0 || event.environment !== void 0;
|
|
4012
|
+
if (env.ODLA_APP_ID && identifiedEnvironment && (event.appId !== env.ODLA_APP_ID || event.environment !== env.ODLA_ENV)) {
|
|
4013
|
+
return json({ ok: true, matched: false });
|
|
4014
|
+
}
|
|
3991
4015
|
if (event.kind === "gift_paid" || event.kind === "gift_revoked") {
|
|
3992
4016
|
const intent = await firstRow2(db, "giftPurchaseIntents", { where: { id: event.giftIntentId }, limit: 1 });
|
|
3993
4017
|
if (!intent || intent.chapterId !== ctx.chapter.id || intent.environment !== env.ODLA_ENV) {
|
|
@@ -4342,14 +4366,16 @@ var handleAdminPeople = async (req, url, env, ctx) => {
|
|
|
4342
4366
|
if (gate9 instanceof Response) return gate9;
|
|
4343
4367
|
const { db } = gate9;
|
|
4344
4368
|
const sk = await getVaultSecret(db, "clerk_secret_key");
|
|
4345
|
-
const [
|
|
4346
|
-
db.query({
|
|
4347
|
-
|
|
4369
|
+
const [data, roleList] = await Promise.all([
|
|
4370
|
+
db.query({
|
|
4371
|
+
applications: { $: { order: { createdAt: "desc" }, limit: 200 } },
|
|
4372
|
+
$users: { $: { limit: 200 } }
|
|
4373
|
+
}),
|
|
4348
4374
|
sk ? clerkListUsers(sk).catch(() => []) : Promise.resolve([])
|
|
4349
4375
|
]);
|
|
4350
4376
|
const clerkByUserId = new Map(roleList.map((u) => [u.id, u]));
|
|
4351
4377
|
const people = /* @__PURE__ */ new Map();
|
|
4352
|
-
for (const u of
|
|
4378
|
+
for (const u of data.$users ?? []) {
|
|
4353
4379
|
if (u.deleted === true) continue;
|
|
4354
4380
|
const email = typeof u.email === "string" ? u.email : "";
|
|
4355
4381
|
if (!email) continue;
|
|
@@ -4364,7 +4390,7 @@ var handleAdminPeople = async (req, url, env, ctx) => {
|
|
|
4364
4390
|
application: null
|
|
4365
4391
|
});
|
|
4366
4392
|
}
|
|
4367
|
-
for (const a of
|
|
4393
|
+
for (const a of data.applications ?? []) {
|
|
4368
4394
|
const key = String(a.email ?? "").toLowerCase();
|
|
4369
4395
|
if (!key) continue;
|
|
4370
4396
|
const name = `${a.firstName ?? ""} ${a.lastName ?? ""}`.trim();
|
|
@@ -4569,13 +4595,19 @@ var handleAdminDashboard = async (req, url, env, ctx) => {
|
|
|
4569
4595
|
const now = Date.now();
|
|
4570
4596
|
const d7 = now - 7 * 864e5;
|
|
4571
4597
|
const d30 = now - 30 * 864e5;
|
|
4572
|
-
const
|
|
4573
|
-
|
|
4574
|
-
|
|
4575
|
-
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
|
|
4598
|
+
const data = await db.query({
|
|
4599
|
+
applications: { $: { order: { createdAt: "desc" }, limit: 1e3 } },
|
|
4600
|
+
meetings: {
|
|
4601
|
+
$: {
|
|
4602
|
+
where: { status: "scheduled" },
|
|
4603
|
+
order: { startAt: "asc" },
|
|
4604
|
+
limit: 500
|
|
4605
|
+
}
|
|
4606
|
+
},
|
|
4607
|
+
crm_record: { $: { where: { type: "person" }, limit: 1e3 } },
|
|
4608
|
+
groups: { $: { where: { id: ctx.chapter.id }, limit: 1 } }
|
|
4609
|
+
});
|
|
4610
|
+
const apps = rows3(data.applications);
|
|
4579
4611
|
const applications = {
|
|
4580
4612
|
total: apps.length,
|
|
4581
4613
|
last7: apps.filter((a) => a.createdAt >= d7).length,
|
|
@@ -4587,7 +4619,7 @@ var handleAdminDashboard = async (req, url, env, ctx) => {
|
|
|
4587
4619
|
pipeline[s] = 0;
|
|
4588
4620
|
pipelineDelta[s] = 0;
|
|
4589
4621
|
}
|
|
4590
|
-
for (const r of rows3(
|
|
4622
|
+
for (const r of rows3(data.crm_record)) {
|
|
4591
4623
|
const s = r.stage;
|
|
4592
4624
|
if (s in pipeline) {
|
|
4593
4625
|
pipeline[s] = (pipeline[s] ?? 0) + 1;
|
|
@@ -4595,7 +4627,7 @@ var handleAdminDashboard = async (req, url, env, ctx) => {
|
|
|
4595
4627
|
if (Number.isFinite(sc) && sc >= d7) pipelineDelta[s] = (pipelineDelta[s] ?? 0) + 1;
|
|
4596
4628
|
}
|
|
4597
4629
|
}
|
|
4598
|
-
const meetings = rows3(
|
|
4630
|
+
const meetings = rows3(data.meetings);
|
|
4599
4631
|
const appById = new Map(apps.map((a) => [a.id, a]));
|
|
4600
4632
|
const upcoming = meetings.filter((m) => m.startAt >= now - 36e5);
|
|
4601
4633
|
const calls = { upcoming: upcoming.length, needsAttention: meetings.filter((m) => m.drift && m.drift !== "none").length };
|
|
@@ -4611,7 +4643,7 @@ var handleAdminDashboard = async (req, url, env, ctx) => {
|
|
|
4611
4643
|
email: a?.email ?? null
|
|
4612
4644
|
};
|
|
4613
4645
|
});
|
|
4614
|
-
const group =
|
|
4646
|
+
const group = data.groups?.[0];
|
|
4615
4647
|
const timezone = resolveScheduling(group?.schedulingJson).timezone;
|
|
4616
4648
|
let revenue = { billingReady: false };
|
|
4617
4649
|
let revenueSeries = null;
|
|
@@ -4645,16 +4677,18 @@ var handleAdminBilling = async (req, url, env, ctx) => {
|
|
|
4645
4677
|
const db = got;
|
|
4646
4678
|
const sk = await getVaultSecret(db, "stripe_secret_key");
|
|
4647
4679
|
if (!sk) return json({ billingReady: false, rows: [], summary: null });
|
|
4648
|
-
const [
|
|
4649
|
-
db.query({
|
|
4650
|
-
|
|
4651
|
-
|
|
4680
|
+
const [data, subsRes] = await Promise.all([
|
|
4681
|
+
db.query({
|
|
4682
|
+
applications: { $: { order: { createdAt: "desc" }, limit: 200 } },
|
|
4683
|
+
groups: { $: { where: { id: ctx.chapter.id }, limit: 1 } }
|
|
4684
|
+
}),
|
|
4685
|
+
stripeCall(sk, "GET", "/v1/subscriptions", { limit: 100, status: "all" })
|
|
4652
4686
|
]);
|
|
4653
|
-
const testMode = String(
|
|
4687
|
+
const testMode = String(data.groups?.[0]?.stripePublishableKey ?? "").startsWith("pk_test");
|
|
4654
4688
|
if (!subsRes.ok) return json({ error: "billing lookup failed upstream" }, 502);
|
|
4655
4689
|
const subById = new Map(rows3(subsRes.body.data).map((s) => [s.id, s]));
|
|
4656
4690
|
const billingRows = [];
|
|
4657
|
-
for (const a of rows3(
|
|
4691
|
+
for (const a of rows3(data.applications)) {
|
|
4658
4692
|
if (!a.stripeCustomerId && !a.stripeSubscriptionId) continue;
|
|
4659
4693
|
const sub = a.stripeSubscriptionId ? subById.get(a.stripeSubscriptionId) : void 0;
|
|
4660
4694
|
const items = sub?.items?.data ?? [];
|
|
@@ -4994,21 +5028,27 @@ var handleAdminComms = async (req, url, env, ctx) => {
|
|
|
4994
5028
|
if (got instanceof Response) return got;
|
|
4995
5029
|
const db = got;
|
|
4996
5030
|
const appId = m[1];
|
|
4997
|
-
const
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
|
|
5031
|
+
const data = await db.query({
|
|
5032
|
+
emailLog: {
|
|
5033
|
+
$: {
|
|
5034
|
+
where: { applicationId: appId },
|
|
5035
|
+
order: { sentAt: "desc" },
|
|
5036
|
+
limit: 100
|
|
5037
|
+
}
|
|
5038
|
+
},
|
|
5039
|
+
meetings: { $: { where: { applicationId: appId } } },
|
|
5040
|
+
applications: { $: { where: { id: appId }, limit: 1 } }
|
|
5041
|
+
});
|
|
5042
|
+
const app = data.applications?.[0] ?? null;
|
|
5003
5043
|
const group = await loadGroup2(db, str2(app?.groupId, ctx.chapter.id));
|
|
5004
5044
|
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;
|
|
5005
|
-
const emails = (
|
|
5045
|
+
const emails = (data.emailLog ?? []).filter((r) => r.template !== "adminNotification").map((r) => {
|
|
5006
5046
|
let mailBody = typeof r.body === "string" ? r.body : null;
|
|
5007
5047
|
if (!mailBody && group && vars) mailBody = renderTemplateBody(emailGroupFrom(group), String(r.template), vars);
|
|
5008
5048
|
const channel = r.error ? "email (failed)" : r.redirected ? "email (dev-redirected)" : r.transport === "log-only" ? "email (not delivered)" : "email";
|
|
5009
5049
|
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 };
|
|
5010
5050
|
});
|
|
5011
|
-
const calendar = (
|
|
5051
|
+
const calendar = (data.meetings ?? []).map((m2) => ({
|
|
5012
5052
|
kind: "calendar",
|
|
5013
5053
|
channel: "Google Calendar",
|
|
5014
5054
|
label: m2.status === "cancelled" ? "Invitation (call later cancelled)" : "Meeting invitation",
|