feat(db): add read replicas
Build and Push Image / build-backend (push) Successful in 1m17s
Build and Push Image / build-frontend (push) Successful in 3m15s
Build and Push Image / push (push) Successful in 53s

This commit is contained in:
2026-08-30 16:19:13 +00:00 Unverified
parent 41b24bd7c2
commit a83015384c
5 changed files with 64 additions and 65 deletions
+1 -1
View File
@@ -68,4 +68,4 @@ COPY --from=builder /home/container/.next/static ./.next/static
COPY --from=builder /home/container/.version ./.version COPY --from=builder /home/container/.version ./.version
ENV HOSTNAME=0.0.0.0 ENV HOSTNAME=0.0.0.0
CMD ["bun", "server.js"] CMD ["bun", "server.js"]
+1 -1
View File
@@ -26,6 +26,6 @@ export async function retrieveMobileUpload(accessKey: TAccessKey) {
type: object.type!, type: object.type!,
}), }),
); );
fd.set("name", object.name!) fd.set("name", object.name!);
return fd; return fd;
} }
+7 -1
View File
@@ -1,4 +1,10 @@
import { drizzle } from "drizzle-orm/bun-sql"; import { drizzle } from "drizzle-orm/bun-sql";
import * as schema from "./schema"; import * as schema from "./schema";
import { withReplicas } from "drizzle-orm/pg-core";
export const db = drizzle(process.env.DATABASE_URL!, { schema }); export const db = process.env.REPLICA_DATABASE_URL
? withReplicas(drizzle(process.env.DATABASE_URL!, { schema }), [
drizzle(process.env.REPLICA_DATABASE_URL!, { schema }),
drizzle(process.env.REPLICA_DATABASE_URL!, { schema }),
])
: drizzle(process.env.DATABASE_URL!, { schema });
+1 -1
View File
@@ -56,7 +56,7 @@ export const sse = sseEndpointMap({
}, },
slip_sync: { slip_sync: {
complete: z.custom<TTrackingKey>(), complete: z.custom<TTrackingKey>(),
connected: z.custom<TTrackingKey>() connected: z.custom<TTrackingKey>(),
}, },
}); });
export const adminSseList: (keyof typeof sse)[] = ["log"]; export const adminSseList: (keyof typeof sse)[] = ["log"];
+54 -61
View File
@@ -155,35 +155,32 @@ describe("parseTLV", () => {
]); ]);
}); });
test.each([ test.each(["0", "000", "0001x00"])(
"0", "rejects an incomplete header in %p",
"000", (payload) => {
"0001x00", expect(() => parseTLV(payload)).toThrow(
])("rejects an incomplete header in %p", (payload) => { /Malformed TLV payload: incomplete headers at index/,
expect(() => parseTLV(payload)).toThrow( );
/Malformed TLV payload: incomplete headers at index/, },
); );
});
test.each([ test.each(["AA00", "0A00", " 100"])(
"AA00", "rejects invalid tags in %p",
"0A00", (payload) => {
" 100", expect(() => parseTLV(payload)).toThrow(
])("rejects invalid tags in %p", (payload) => { /Malformed TLV payload: invalid tag/,
expect(() => parseTLV(payload)).toThrow( );
/Malformed TLV payload: invalid tag/, },
); );
});
test.each([ test.each(["00AA", "000A", "00 1"])(
"00AA", "rejects invalid lengths in %p",
"000A", (payload) => {
"00 1", expect(() => parseTLV(payload)).toThrow(
])("rejects invalid lengths in %p", (payload) => { /Malformed TLV payload: invalid length/,
expect(() => parseTLV(payload)).toThrow( );
/Malformed TLV payload: invalid length/, },
); );
});
test("rejects a value shorter than its declared length", () => { test("rejects a value shorter than its declared length", () => {
expect(() => parseTLV("0005abc")).toThrow( expect(() => parseTLV("0005abc")).toThrow(
@@ -351,11 +348,12 @@ describe("decodeEMVCo", () => {
{ type: "primitive", tag: 58, value: "THA" }, { type: "primitive", tag: 58, value: "THA" },
], ],
}, },
] satisfies { name: string; objects: DataObject[] }[])("rejects $name", ({ ] satisfies { name: string; objects: DataObject[] }[])(
objects, "rejects $name",
}) => { ({ objects }) => {
expect(() => decodeEMVCo(objects)).toThrow(); expect(() => decodeEMVCo(objects)).toThrow();
}); },
);
}); });
describe("encodeEMVCo", () => { describe("encodeEMVCo", () => {
@@ -703,20 +701,19 @@ describe("encodePromptPay", () => {
{ type: "mobile", tag: 1, value: "0066812345678" }, { type: "mobile", tag: 1, value: "0066812345678" },
{ type: "nationalId", tag: 2, value: "1234567890123" }, { type: "nationalId", tag: 2, value: "1234567890123" },
{ type: "ewallet", tag: 3, value: "123456789012345" }, { type: "ewallet", tag: 3, value: "123456789012345" },
] as const)("encodes a canonical $type identifier", ({ ] as const)(
type, "encodes a canonical $type identifier",
tag, ({ type, tag, value }) => {
value, expect(encodePromptPay({ type, value })).toEqual([
}) => { {
expect(encodePromptPay({ type, value })).toEqual([ type: "primitive",
{ tag: 0,
type: "primitive", value: "A000000677010111",
tag: 0, },
value: "A000000677010111", { type: "primitive", tag, value },
}, ]);
{ type: "primitive", tag, value }, },
]); );
});
test("rejects noncanonical identifier values", () => { test("rejects noncanonical identifier values", () => {
expect(() => expect(() =>
@@ -885,21 +882,17 @@ describe("generatePromptPayPayload", () => {
expect(parsed.emvco.merchantCategoryCode).toBe("0000"); expect(parsed.emvco.merchantCategoryCode).toBe("0000");
}); });
test.each([ test.each([0, -1, Number.NaN, Number.POSITIVE_INFINITY, "", "1.234"])(
0, "rejects invalid amount %p",
-1, (amount) => {
Number.NaN, expect(() =>
Number.POSITIVE_INFINITY, generatePromptPayPayload({
"", identifier: { type: "mobile", value: "0801234567" },
"1.234", amount,
])("rejects invalid amount %p", (amount) => { }),
expect(() => ).toThrow();
generatePromptPayPayload({ },
identifier: { type: "mobile", value: "0801234567" }, );
amount,
}),
).toThrow();
});
test("rejects invalid merchant category codes", () => { test("rejects invalid merchant category codes", () => {
expect(() => expect(() =>