59 lines
2.1 KiB
TypeScript
59 lines
2.1 KiB
TypeScript
import type { ResultSetHeader, RowDataPacket } from "mysql2/promise";
|
|
import { BILL_BODY_LIMIT } from "./body";
|
|
import { withTransaction, getPool } from "./db";
|
|
import { isUuid } from "./ids";
|
|
|
|
export class BillStoreError extends Error {
|
|
readonly status: number;
|
|
|
|
constructor(status: number, message: string) {
|
|
super(message);
|
|
this.status = status;
|
|
}
|
|
}
|
|
|
|
export async function storePublishedBill(token: string, raw: string): Promise<void> {
|
|
if (!isUuid(token)) throw new BillStoreError(400, "invalid token");
|
|
if (Buffer.byteLength(raw, "utf8") > BILL_BODY_LIMIT) throw new BillStoreError(413, "body too large");
|
|
let parsed: unknown;
|
|
try {
|
|
parsed = JSON.parse(raw);
|
|
} catch {
|
|
throw new BillStoreError(400, "invalid json");
|
|
}
|
|
const isSnapshot = parsed !== null && typeof parsed === "object" && !Array.isArray(parsed);
|
|
await withTransaction(async (conn) => {
|
|
await conn.query(
|
|
`INSERT INTO bill_snapshots (token, payload_json, updated_at)
|
|
VALUES (?, ?, UTC_TIMESTAMP(3))
|
|
ON DUPLICATE KEY UPDATE payload_json = VALUES(payload_json), updated_at = UTC_TIMESTAMP(3)`,
|
|
[token, raw],
|
|
);
|
|
if (isSnapshot) {
|
|
await conn.query<ResultSetHeader>(
|
|
`UPDATE invoices SET snapshot_json = ? WHERE public_token = ? AND deleted_at IS NULL`,
|
|
[raw, token],
|
|
);
|
|
}
|
|
});
|
|
}
|
|
|
|
/** Invoice snapshot first, then the legacy store. */
|
|
export async function readBillPayload(token: string): Promise<string | null> {
|
|
if (!isUuid(token)) return null;
|
|
const pool = getPool();
|
|
const [invoices] = await pool.query<RowDataPacket[]>(
|
|
`SELECT snapshot_json FROM invoices WHERE public_token = ? AND deleted_at IS NULL LIMIT 1`,
|
|
[token],
|
|
);
|
|
const snapshot = invoices[0]?.snapshot_json;
|
|
if (typeof snapshot === "string" && snapshot.length > 0) return snapshot;
|
|
const [legacy] = await pool.query<RowDataPacket[]>(
|
|
`SELECT payload_json FROM bill_snapshots WHERE token = ? LIMIT 1`,
|
|
[token],
|
|
);
|
|
const payload = legacy[0]?.payload_json;
|
|
if (typeof payload === "string" && payload.length > 0) return payload;
|
|
return null;
|
|
}
|