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 { } else {
log(`user ID ok`, context.user_id); log(`user ID ok`, context.user_id);
} }
context.product_id = parseInt( context.product_id = context.data?.data?.attributes?.first_order_item?.product_id || context.data?.data?.attributes?.product_id || 0;
(context.type === "orders" ? context.data?.data?.attributes?.first_order_item?.product_id : context.data?.data?.attributes?.product_id) || "0"
);
if (!context.product_id) { if (!context.product_id) {
throw new Error(`No product ID`); throw new Error(`No product ID`);
} else { } else {
log(`product ID ok`, context.product_id); 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; context.product_name = context.data?.data?.attributes?.first_order_item?.product_name || context.data?.data?.attributes?.product_name || "";
if (!context.product_name) { context.variant_id = context.data?.data?.attributes?.first_order_item?.variant_id || context.data?.data?.attributes?.variant_id || 0;
throw new Error(`No product name`); if (!context.variant_id) {
throw new Error(`No variant ID`);
} else { } 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 = (() => { const userRec = (() => {
try { try {
return dao.findFirstRecordByData("users", "id", context.user_id); return dao.findFirstRecordByData("users", "id", context.user_id);
@ -488,92 +504,70 @@ var HandleLemonSqueezySale = (c) => {
order_refunded: () => { order_refunded: () => {
signup_canceller(); signup_canceller();
}, },
subscription_created: () => {
signup_finalizer();
},
subscription_expired: () => { subscription_expired: () => {
signup_canceller(); signup_canceller();
},
subscription_payment_refunded: () => {
signup_canceller();
} }
}; };
const event_handler = event_name_map[context.event_name]; const event_handler = event_name_map[context.event_name];
if (!event_handler) { if (!event_handler) {
log(`unsupported event`, context.event_name); throw new Error(`Unsupported event: ${context.event_name}`);
return c.json(200, {
status: `warning: unsupported event ${context.event_name}`
});
} else { } else {
log(`event handler ok`, event_handler); 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 = { const product_handler_map = {
// Founder's annual // Founder's annual
159792: () => { [FOUNDER_ANNUAL_PV_ID]: () => {
userRec.set(`subscription`, `founder`); userRec.set(`subscription`, `founder`);
userRec.set(`subscription_interval`, `year`); userRec.set(`subscription_interval`, `year`);
}, },
// Founder's lifetime
159794: () => {
userRec.set(`subscription`, `founder`);
userRec.set(`subscription_interval`, `life`);
},
// Pro annual // Pro annual
159791: () => { [PRO_ANNUAL_PV_ID]: () => {
userRec.set(`subscription`, `premium`); userRec.set(`subscription`, `premium`);
userRec.set(`subscription_interval`, `year`); userRec.set(`subscription_interval`, `year`);
}, },
// Pro monthly // Pro monthly
159790: () => { [PRO_MONTHLY_PV_ID]: () => {
userRec.set(`subscription`, `premium`); userRec.set(`subscription`, `premium`);
userRec.set(`subscription_interval`, `month`); userRec.set(`subscription_interval`, `month`);
}, },
// Flounder lifetime // Flounder lifetime
306534: () => { [FLOUNDER_LIFETIME_PV_ID]: () => {
userRec.set(`subscription`, `flounder`); userRec.set(`subscription`, `flounder`);
userRec.set(`subscription_interval`, `life`); userRec.set(`subscription_interval`, `life`);
}, },
// Flounder annual // Flounder annual
367781: () => { [FLOUNDER_ANNUAL_PV_ID]: () => {
userRec.set(`subscription`, `flounder`); userRec.set(`subscription`, `flounder`);
userRec.set(`subscription_interval`, `year`); 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) { if (!product_handler) {
throw new Error(`No product handler for ${context.product_id}`); throw new Error(`No product handler for ${pv_id}`);
} else { } else {
log(`product handler ok`, product_handler); log(`product handler ok`, pv_id);
} }
const signup_finalizer = () => { const signup_finalizer = () => {
product_handler(); product_handler();
dao.runInTransaction((txDao) => { dao.saveRecord(userRec);
log(`transaction started`); log(`saved user`);
txDao.saveRecord(userRec); const notify = mkNotifier(log, dao);
log(`saved user`); const { user_id } = context;
const notify = mkNotifier(log, txDao); if (!user_id) {
const { user_id } = context; throw new Error(`User ID expected here`);
if (!user_id) { }
throw new Error(`User ID expected here`); notify(`lemonbot`, `lemon_order_discord`, user_id, context);
} log(`saved discord notice`);
notify(`lemonbot`, `lemon_order_discord`, user_id, context);
log(`saved discord notice`);
});
log(`database updated`);
audit(`LS`, `Signup processed.`, context); audit(`LS`, `Signup processed.`, context);
}; };
const signup_canceller = () => { const signup_canceller = () => {
userRec.set(`subscription`, `free`); userRec.set(`subscription`, `free`);
userRec.set(`subscription_interval`, ``); userRec.set(`subscription_interval`, ``);
dao.runInTransaction((txDao) => { dao.saveRecord(userRec);
txDao.saveRecord(userRec); log(`saved user`);
log(`saved user`);
});
log(`database updated`);
audit(`LS`, `Signup cancelled.`, context); audit(`LS`, `Signup cancelled.`, context);
}; };
event_handler(); event_handler();
@ -638,16 +632,110 @@ var HandleMetaUpdateAtBoot = (c) => {
}; };
log(`Saving form`); log(`Saving form`);
form.submit(); form.submit();
log(`Saved form`);
}; };
// src/lib/handlers/mirror/api/HandleMirrorData.ts // src/lib/handlers/mirror/api/HandleMirrorData.ts
var HandleMirrorData = (c) => { var HandleMirrorData = (c) => {
const users = $app.dao().findRecordsByExpr("verified_users", $dbx.exp("1=1")); const dao = $app.dao();
const instances = $app.dao().findRecordsByExpr( const log = mkLog(`POST:mirror:instance`);
"instances", log(`***TOP OF POST`);
$dbx.exp("instances.uid in (select id from verified_users)") let data = new DynamicModel({
); identifier: "",
return c.json(200, { users, instances }); 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 // src/lib/util/mkNotificationProcessor.ts

View File

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

View File

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