feat: add JWT auth with db-backed users
Some checks failed
Build & Release / build (push) Has been cancelled
Some checks failed
Build & Release / build (push) Has been cancelled
- users table created on startup via migrate() - POST /api/auth/setup to create first user (blocked once any user exists) - POST /api/auth/login returns httpOnly JWT cookie (7d expiry) - POST /api/auth/logout clears cookie - GET /api/auth/me for auth check - All /api/customers routes require valid JWT - Frontend shows login form when unauthenticated - Fix type errors in k8s, do, and pgbouncer services
This commit is contained in:
@@ -30,7 +30,7 @@ export async function createDatabaseUser(name: string): Promise<{ name: string;
|
||||
const res = await doFetch(`/databases/${config.doDbClusterId}/users`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ name }),
|
||||
});
|
||||
}) as { user: { name: string; password: string } };
|
||||
return res.user;
|
||||
}
|
||||
|
||||
@@ -43,6 +43,6 @@ export async function deleteDatabase(name: string) {
|
||||
}
|
||||
|
||||
export async function getDatabaseHost(): Promise<string> {
|
||||
const res = await doFetch(`/databases/${config.doDbClusterId}`);
|
||||
const res = await doFetch(`/databases/${config.doDbClusterId}`) as { database: { connection: { host: string } } };
|
||||
return res.database.connection.host;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ export async function removeCustomerFromPool(slug: string) {
|
||||
|
||||
async function addDbEntry(slug: string) {
|
||||
const cm = await getConfigMap(NAMESPACE, "pgbouncer-config");
|
||||
const ini = cm["pgbouncer.ini"];
|
||||
const ini = cm["pgbouncer.ini"] ?? "";
|
||||
const newLine = ` ${slug} = host=${DO_HOST} port=${DO_PORT} dbname=${slug} user=${slug} pool_mode=session pool_size=3`;
|
||||
const updated = ini.replace("[pgbouncer]", `${newLine}\n [pgbouncer]`);
|
||||
await patchConfigMap(NAMESPACE, "pgbouncer-config", { "pgbouncer.ini": updated });
|
||||
@@ -30,21 +30,21 @@ async function addDbEntry(slug: string) {
|
||||
|
||||
async function removeDbEntry(slug: string) {
|
||||
const cm = await getConfigMap(NAMESPACE, "pgbouncer-config");
|
||||
const ini = cm["pgbouncer.ini"];
|
||||
const ini = cm["pgbouncer.ini"] ?? "";
|
||||
const updated = ini.split("\n").filter((l) => !l.includes(`dbname=${slug}`)).join("\n");
|
||||
await patchConfigMap(NAMESPACE, "pgbouncer-config", { "pgbouncer.ini": updated });
|
||||
}
|
||||
|
||||
async function addUserEntry(slug: string, password: string) {
|
||||
const secret = await getSecret(NAMESPACE, "pgbouncer-userlist");
|
||||
const userlist = secret["userlist.txt"];
|
||||
const userlist = secret["userlist.txt"] ?? "";
|
||||
const updated = `${userlist}\n"${slug}" "${password}"`;
|
||||
await patchSecret(NAMESPACE, "pgbouncer-userlist", { "userlist.txt": updated });
|
||||
}
|
||||
|
||||
async function removeUserEntry(slug: string) {
|
||||
const secret = await getSecret(NAMESPACE, "pgbouncer-userlist");
|
||||
const userlist = secret["userlist.txt"];
|
||||
const userlist = secret["userlist.txt"] ?? "";
|
||||
const updated = userlist.split("\n").filter((l) => !l.startsWith(`"${slug}"`)).join("\n");
|
||||
await patchSecret(NAMESPACE, "pgbouncer-userlist", { "userlist.txt": updated });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user