enh: refactor LS logic

This commit is contained in:
Ben Allfree 2025-01-09 03:40:28 +00:00
parent 2a0a78907b
commit be795e4e46
3 changed files with 225 additions and 126 deletions

View File

@ -459,20 +459,36 @@ var HandleLemonSqueezySale = (c) => {
} else {
log(`user ID ok`, context.user_id);
}
context.product_id = parseInt(
(context.type === "orders" ? context.data?.data?.attributes?.first_order_item?.product_id : context.data?.data?.attributes?.product_id) || "0"
);
context.product_id = context.data?.data?.attributes?.first_order_item?.product_id || context.data?.data?.attributes?.product_id || 0;
if (!context.product_id) {
throw new Error(`No product ID`);
} else {
log(`product ID ok`, context.product_id);
}
context.product_name = context.type === "orders" ? context.data?.data?.attributes?.first_order_item?.product_name : context.data?.data?.attributes?.product_name;
if (!context.product_name) {
throw new Error(`No product name`);
context.product_name = context.data?.data?.attributes?.first_order_item?.product_name || context.data?.data?.attributes?.product_name || "";
context.variant_id = context.data?.data?.attributes?.first_order_item?.variant_id || context.data?.data?.attributes?.variant_id || 0;
if (!context.variant_id) {
throw new Error(`No variant ID`);
} else {
log(`product name ok`, context.product_name);
log(`variant ID ok`, context.variant_id);
}
const FLOUNDER_ANNUAL_PV_ID = `367781-200790`;
const FLOUNDER_LIFETIME_PV_ID = `306534-441845`;
const PRO_MONTHLY_PV_ID = `159790-200788`;
const PRO_ANNUAL_PV_ID = `159791-200789`;
const FOUNDER_ANNUAL_PV_ID = `159792-200790`;
const pv_id = `${context.product_id}-${context.variant_id}`;
if (![
FLOUNDER_ANNUAL_PV_ID,
FLOUNDER_LIFETIME_PV_ID,
PRO_MONTHLY_PV_ID,
PRO_ANNUAL_PV_ID,
FOUNDER_ANNUAL_PV_ID
].includes(pv_id)) {
throw new Error(`Product and variant not found: ${pv_id}`);
}
context.subscription_id = context.data?.data?.attributes?.first_subscription_item?.subscription_id || 0;
log(`subscription ID`, context.subscription_id);
const userRec = (() => {
try {
return dao.findFirstRecordByData("users", "id", context.user_id);
@ -488,92 +504,70 @@ var HandleLemonSqueezySale = (c) => {
order_refunded: () => {
signup_canceller();
},
subscription_created: () => {
signup_finalizer();
},
subscription_expired: () => {
signup_canceller();
},
subscription_payment_refunded: () => {
signup_canceller();
}
};
const event_handler = event_name_map[context.event_name];
if (!event_handler) {
log(`unsupported event`, context.event_name);
return c.json(200, {
status: `warning: unsupported event ${context.event_name}`
});
throw new Error(`Unsupported event: ${context.event_name}`);
} else {
log(`event handler ok`, event_handler);
}
const SUBSCRIPTION_PRODUCT_IDS = [159792, 159791, 159790, 367781];
if (context.event_name === "order_created" && SUBSCRIPTION_PRODUCT_IDS.includes(context.product_id)) {
log(`ignoring order event for subscription`);
return c.json(200, {
status: `warning: order event ignored for subscription`
});
}
const product_handler_map = {
// Founder's annual
159792: () => {
[FOUNDER_ANNUAL_PV_ID]: () => {
userRec.set(`subscription`, `founder`);
userRec.set(`subscription_interval`, `year`);
},
// Founder's lifetime
159794: () => {
userRec.set(`subscription`, `founder`);
userRec.set(`subscription_interval`, `life`);
},
// Pro annual
159791: () => {
[PRO_ANNUAL_PV_ID]: () => {
userRec.set(`subscription`, `premium`);
userRec.set(`subscription_interval`, `year`);
},
// Pro monthly
159790: () => {
[PRO_MONTHLY_PV_ID]: () => {
userRec.set(`subscription`, `premium`);
userRec.set(`subscription_interval`, `month`);
},
// Flounder lifetime
306534: () => {
[FLOUNDER_LIFETIME_PV_ID]: () => {
userRec.set(`subscription`, `flounder`);
userRec.set(`subscription_interval`, `life`);
},
// Flounder annual
367781: () => {
[FLOUNDER_ANNUAL_PV_ID]: () => {
userRec.set(`subscription`, `flounder`);
userRec.set(`subscription_interval`, `year`);
}
};
const product_handler = product_handler_map[context.product_id];
const product_handler = product_handler_map[pv_id];
if (!product_handler) {
throw new Error(`No product handler for ${context.product_id}`);
throw new Error(`No product handler for ${pv_id}`);
} else {
log(`product handler ok`, product_handler);
log(`product handler ok`, pv_id);
}
const signup_finalizer = () => {
product_handler();
dao.runInTransaction((txDao) => {
log(`transaction started`);
txDao.saveRecord(userRec);
log(`saved user`);
const notify = mkNotifier(log, txDao);
const { user_id } = context;
if (!user_id) {
throw new Error(`User ID expected here`);
}
notify(`lemonbot`, `lemon_order_discord`, user_id, context);
log(`saved discord notice`);
});
log(`database updated`);
dao.saveRecord(userRec);
log(`saved user`);
const notify = mkNotifier(log, dao);
const { user_id } = context;
if (!user_id) {
throw new Error(`User ID expected here`);
}
notify(`lemonbot`, `lemon_order_discord`, user_id, context);
log(`saved discord notice`);
audit(`LS`, `Signup processed.`, context);
};
const signup_canceller = () => {
userRec.set(`subscription`, `free`);
userRec.set(`subscription_interval`, ``);
dao.runInTransaction((txDao) => {
txDao.saveRecord(userRec);
log(`saved user`);
});
log(`database updated`);
dao.saveRecord(userRec);
log(`saved user`);
audit(`LS`, `Signup cancelled.`, context);
};
event_handler();
@ -638,16 +632,110 @@ var HandleMetaUpdateAtBoot = (c) => {
};
log(`Saving form`);
form.submit();
log(`Saved form`);
};
// src/lib/handlers/mirror/api/HandleMirrorData.ts
var HandleMirrorData = (c) => {
const users = $app.dao().findRecordsByExpr("verified_users", $dbx.exp("1=1"));
const instances = $app.dao().findRecordsByExpr(
"instances",
$dbx.exp("instances.uid in (select id from verified_users)")
);
return c.json(200, { users, instances });
const dao = $app.dao();
const log = mkLog(`POST:mirror:instance`);
log(`***TOP OF POST`);
let data = new DynamicModel({
identifier: "",
externalMachineId: "",
region: ""
});
log(`***before bind`);
c.bind(data);
log(`***after bind`);
data = JSON.parse(JSON.stringify(data));
const { identifier, externalMachineId, region } = data;
log(`***vars`, JSON.stringify({ identifier, externalMachineId, region }));
if (!identifier) {
throw new BadRequestError(`Identifier is required`);
}
if (!externalMachineId) {
throw new BadRequestError(`External Machine ID is required`);
}
if (!region) {
throw new BadRequestError(`Region is required`);
}
const safeDo = (fn) => {
try {
return fn();
} catch (e) {
log(`***error`, e);
return void 0;
}
};
const subdomain = identifier.split(".")[0];
let instance;
dao.runInTransaction((tx) => {
const foundInstance = (() => {
log(`***looking for instance by id`, subdomain);
const byId = safeDo(() => tx.findRecordById("instances", subdomain));
if (byId) return byId;
log(`***looking for instance by subdomain`, subdomain);
const bySubdomain = safeDo(
() => tx.findRecordsByExpr(
"instances",
$dbx.exp("subdomain = {:subdomain}", { subdomain })
)[0]
);
if (bySubdomain) return bySubdomain;
log(`***looking for instance by cname`, identifier);
const byCname = safeDo(
() => tx.findRecordsByExpr(
"instances",
$dbx.exp("cname = {:cname}", { cname: identifier })
)[0]
);
if (byCname) return byCname;
return void 0;
})();
log(`***found instance`, foundInstance);
if (!foundInstance) return;
log(`***running machine check`);
if (!foundInstance.getString("machine")) {
log(
`***no machine is assigned, looking up requesting machine by external id`,
externalMachineId
);
const existingMachine = safeDo(
() => tx.findRecordsByExpr(
"machines",
$dbx.exp("externalId = {:externalMachineId}", {
externalMachineId
})
)[0]
);
if (existingMachine) {
log(`***found machine`, existingMachine);
foundInstance.set("machine", existingMachine.getId());
tx.saveRecord(foundInstance);
log(`***saved instance with assigned machine`, foundInstance);
} else {
log(`***no machine found, creating new machine`);
const collection = tx.findCollectionByNameOrId("machines");
const newMachine = new Record(collection, {
externalId: externalMachineId,
region
});
tx.saveRecord(newMachine);
log(`***saved new machine id: ${newMachine.getId()}`, newMachine);
foundInstance.set("machine", newMachine.getId());
tx.saveRecord(foundInstance);
log(`***saved instance with assigned machine`, foundInstance);
}
}
log(`***got here`);
instance = foundInstance;
});
log(`***instance`, instance);
if (!instance) {
throw new NotFoundError(`Instance not found`);
}
return c.json(200, instance);
};
// src/lib/util/mkNotificationProcessor.ts

View File

@ -61,10 +61,10 @@ onAfterBootstrap((e) => {
// src/lib/handlers/mirror/hooks.ts
routerAdd(
"GET",
"/api/mirror",
"POST",
"/api/mirror/instance",
(c) => {
return require(`${__hooks}/HandleMirrorData`).HandleMirrorData(c);
return require(`${__hooks}/mothership`).HandleMirrorData(c);
},
$apis.gzip(),
$apis.requireAdminAuth()

View File

@ -11,11 +11,16 @@ type LemonSqueezyDebugContext = PartialDeep<{
data: {
data: {
attributes: {
product_id: string
product_id: number
product_name: string
variant_id: number
first_order_item: {
product_id: string
product_id: number
product_name: string
variant_id: number
}
first_subscription_item: {
subscription_id: number
}
}
type: string
@ -31,6 +36,8 @@ type LemonSqueezyDebugContext = PartialDeep<{
event_name: string
user_id: string
product_id: number
variant_id: number
subscription_id: number
product_name: string
}>
@ -89,11 +96,11 @@ export const HandleLemonSqueezySale = (c: echo.Context) => {
log(`user ID ok`, context.user_id)
}
context.product_id = parseInt(
(context.type === 'orders'
? context.data?.data?.attributes?.first_order_item?.product_id
: context.data?.data?.attributes?.product_id) || '0',
)
context.product_id =
context.data?.data?.attributes?.first_order_item?.product_id ||
context.data?.data?.attributes?.product_id ||
0
if (!context.product_id) {
throw new Error(`No product ID`)
} else {
@ -101,15 +108,46 @@ export const HandleLemonSqueezySale = (c: echo.Context) => {
}
context.product_name =
context.type === 'orders'
? context.data?.data?.attributes?.first_order_item?.product_name
: context.data?.data?.attributes?.product_name
if (!context.product_name) {
throw new Error(`No product name`)
context.data?.data?.attributes?.first_order_item?.product_name ||
context.data?.data?.attributes?.product_name ||
''
context.variant_id =
context.data?.data?.attributes?.first_order_item?.variant_id ||
context.data?.data?.attributes?.variant_id ||
0
if (!context.variant_id) {
throw new Error(`No variant ID`)
} else {
log(`product name ok`, context.product_name)
log(`variant ID ok`, context.variant_id)
}
const FLOUNDER_ANNUAL_PV_ID = `367781-200790`
const FLOUNDER_LIFETIME_PV_ID = `306534-441845`
const PRO_MONTHLY_PV_ID = `159790-200788`
const PRO_ANNUAL_PV_ID = `159791-200789`
const FOUNDER_ANNUAL_PV_ID = `159792-200790`
const pv_id = `${context.product_id}-${context.variant_id}`
if (
![
FLOUNDER_ANNUAL_PV_ID,
FLOUNDER_LIFETIME_PV_ID,
PRO_MONTHLY_PV_ID,
PRO_ANNUAL_PV_ID,
FOUNDER_ANNUAL_PV_ID,
].includes(pv_id)
) {
throw new Error(`Product and variant not found: ${pv_id}`)
}
context.subscription_id =
context.data?.data?.attributes?.first_subscription_item
?.subscription_id || 0
log(`subscription ID`, context.subscription_id)
const userRec = (() => {
try {
return dao.findFirstRecordByData('users', 'id', context.user_id)
@ -126,105 +164,78 @@ export const HandleLemonSqueezySale = (c: echo.Context) => {
order_refunded: () => {
signup_canceller()
},
subscription_created: () => {
signup_finalizer()
},
subscription_expired: () => {
signup_canceller()
},
subscription_payment_refunded: () => {
signup_canceller()
},
} as const
const event_handler =
event_name_map[context.event_name as keyof typeof event_name_map]
if (!event_handler) {
log(`unsupported event`, context.event_name)
return c.json(200, {
status: `warning: unsupported event ${context.event_name}`,
})
throw new Error(`Unsupported event: ${context.event_name}`)
} else {
log(`event handler ok`, event_handler)
}
const SUBSCRIPTION_PRODUCT_IDS = [159792, 159791, 159790, 367781]
if (
context.event_name === 'order_created' &&
SUBSCRIPTION_PRODUCT_IDS.includes(context.product_id)
) {
log(`ignoring order event for subscription`)
return c.json(200, {
status: `warning: order event ignored for subscription`,
})
}
const product_handler_map = {
// Founder's annual
159792: () => {
[FOUNDER_ANNUAL_PV_ID]: () => {
userRec.set(`subscription`, `founder`)
userRec.set(`subscription_interval`, `year`)
},
// Founder's lifetime
159794: () => {
userRec.set(`subscription`, `founder`)
userRec.set(`subscription_interval`, `life`)
},
// Pro annual
159791: () => {
[PRO_ANNUAL_PV_ID]: () => {
userRec.set(`subscription`, `premium`)
userRec.set(`subscription_interval`, `year`)
},
// Pro monthly
159790: () => {
[PRO_MONTHLY_PV_ID]: () => {
userRec.set(`subscription`, `premium`)
userRec.set(`subscription_interval`, `month`)
},
// Flounder lifetime
306534: () => {
[FLOUNDER_LIFETIME_PV_ID]: () => {
userRec.set(`subscription`, `flounder`)
userRec.set(`subscription_interval`, `life`)
},
// Flounder annual
367781: () => {
[FLOUNDER_ANNUAL_PV_ID]: () => {
userRec.set(`subscription`, `flounder`)
userRec.set(`subscription_interval`, `year`)
},
} as const
const product_handler =
product_handler_map[
context.product_id as keyof typeof product_handler_map
]
product_handler_map[pv_id as keyof typeof product_handler_map]
if (!product_handler) {
throw new Error(`No product handler for ${context.product_id}`)
throw new Error(`No product handler for ${pv_id}`)
} else {
log(`product handler ok`, product_handler)
log(`product handler ok`, pv_id)
}
const signup_finalizer = () => {
product_handler()
dao.runInTransaction((txDao) => {
log(`transaction started`)
txDao.saveRecord(userRec)
log(`saved user`)
dao.saveRecord(userRec)
log(`saved user`)
const notify = mkNotifier(log, txDao)
const { user_id } = context
if (!user_id) {
throw new Error(`User ID expected here`)
}
notify(`lemonbot`, `lemon_order_discord`, user_id, context)
log(`saved discord notice`)
})
log(`database updated`)
const notify = mkNotifier(log, dao)
const { user_id } = context
if (!user_id) {
throw new Error(`User ID expected here`)
}
notify(`lemonbot`, `lemon_order_discord`, user_id, context)
log(`saved discord notice`)
audit(`LS`, `Signup processed.`, context)
}
const signup_canceller = () => {
userRec.set(`subscription`, `free`)
userRec.set(`subscription_interval`, ``)
dao.runInTransaction((txDao) => {
txDao.saveRecord(userRec)
log(`saved user`)
})
log(`database updated`)
dao.saveRecord(userRec)
log(`saved user`)
audit(`LS`, `Signup cancelled.`, context)
}