feat: add PostgreSQL backend
Mirrors the node:sqlite sub-backend structure with full migration support. Uses native pg types (UUID, JSONB, TIMESTAMPTZ) and $1/$2 parameterisation via internal ? placeholders converted at execution time. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
import type { Abode } from "../types/Abode.js";
|
||||
import type { ApikeyPermissions, ClientApikey } from "../types/Apikey.js";
|
||||
import type { Resident, ResidentFlags } from "../types/Resident.js";
|
||||
import type { ClientUser, PartialUser, UserFlags } from "../types/User.js";
|
||||
|
||||
export function pgToDate(d: Date | string): string {
|
||||
return d instanceof Date ? d.toISOString() : new Date(d).toISOString();
|
||||
}
|
||||
|
||||
const defaultUserFlags: UserFlags = {};
|
||||
export function pgToUserFlags(flags: unknown): UserFlags {
|
||||
const out = { ...defaultUserFlags };
|
||||
if (typeof flags !== "object" || !flags || Array.isArray(flags)) return out;
|
||||
const f = flags as Record<string, unknown>;
|
||||
if (f.admin === true) out.admin = true;
|
||||
return out;
|
||||
}
|
||||
|
||||
export function pgToPartialUser(user: {
|
||||
uid: string;
|
||||
name: string;
|
||||
flags: unknown;
|
||||
created_at: Date | string;
|
||||
updated_at: Date | string;
|
||||
}): PartialUser {
|
||||
return {
|
||||
uid: user.uid,
|
||||
name: user.name,
|
||||
flags: pgToUserFlags(user.flags),
|
||||
created_at: pgToDate(user.created_at),
|
||||
updated_at: pgToDate(user.updated_at),
|
||||
};
|
||||
}
|
||||
export function pgToClientUser(user: {
|
||||
uid: string;
|
||||
email: string;
|
||||
name: string;
|
||||
flags: unknown;
|
||||
created_at: Date | string;
|
||||
updated_at: Date | string;
|
||||
}): ClientUser {
|
||||
return {
|
||||
...pgToPartialUser(user),
|
||||
email: user.email,
|
||||
};
|
||||
}
|
||||
|
||||
export function pgToAbode(abode: {
|
||||
aid: string;
|
||||
name: string;
|
||||
created_at: Date | string;
|
||||
created_by: string | null;
|
||||
updated_at: Date | string;
|
||||
updated_by: string | null;
|
||||
}): Abode {
|
||||
return {
|
||||
aid: abode.aid,
|
||||
name: abode.name,
|
||||
created_at: pgToDate(abode.created_at),
|
||||
created_by: abode.created_by,
|
||||
updated_at: pgToDate(abode.updated_at),
|
||||
updated_by: abode.updated_by,
|
||||
};
|
||||
}
|
||||
|
||||
const defaultResidentFlags: ResidentFlags = {};
|
||||
export function pgToResidentFlags(flags: unknown): ResidentFlags {
|
||||
const out = { ...defaultResidentFlags };
|
||||
if (typeof flags !== "object" || !flags || Array.isArray(flags)) return out;
|
||||
const f = flags as Record<string, unknown>;
|
||||
if (f.admin === true) out.admin = true;
|
||||
return out;
|
||||
}
|
||||
|
||||
export function pgToResident(resident: {
|
||||
uid: string;
|
||||
aid: string;
|
||||
flags: unknown;
|
||||
created_at: Date | string;
|
||||
created_by: string | null;
|
||||
updated_at: Date | string;
|
||||
updated_by: string | null;
|
||||
}): Resident {
|
||||
return {
|
||||
uid: resident.uid,
|
||||
aid: resident.aid,
|
||||
flags: pgToResidentFlags(resident.flags),
|
||||
created_at: pgToDate(resident.created_at),
|
||||
created_by: resident.created_by,
|
||||
updated_at: pgToDate(resident.updated_at),
|
||||
updated_by: resident.updated_by,
|
||||
};
|
||||
}
|
||||
|
||||
const defaultApikeyPermissions: ApikeyPermissions = {};
|
||||
export function pgToApikeyPermissions(permissions: unknown): ApikeyPermissions {
|
||||
const out = { ...defaultApikeyPermissions };
|
||||
if (
|
||||
typeof permissions !== "object" ||
|
||||
!permissions ||
|
||||
Array.isArray(permissions)
|
||||
)
|
||||
return out;
|
||||
const p = permissions as Record<string, unknown>;
|
||||
if (p.admin === true) out.admin = true;
|
||||
if (p.all === true) out.all = true;
|
||||
for (const key of ["users", "residents", "abodes"] as const) {
|
||||
if (p[key] === "r" || p[key] === "rw") out[key] = p[key] as "r" | "rw";
|
||||
}
|
||||
for (const key of ["restrict_users", "restrict_abodes"] as const) {
|
||||
if (
|
||||
Array.isArray(p[key]) &&
|
||||
(p[key] as unknown[]).every((x) => typeof x === "string")
|
||||
) {
|
||||
out[key] = p[key] as string[];
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
export function pgToClientApikey(apikey: {
|
||||
uid: string;
|
||||
kid: string;
|
||||
name: string;
|
||||
permissions: unknown;
|
||||
created_at: Date | string;
|
||||
expires_at: Date | string | null;
|
||||
}): ClientApikey {
|
||||
return {
|
||||
uid: apikey.uid,
|
||||
kid: apikey.kid,
|
||||
name: apikey.name,
|
||||
permissions: pgToApikeyPermissions(apikey.permissions),
|
||||
created_at: pgToDate(apikey.created_at),
|
||||
expires_at: apikey.expires_at ? pgToDate(apikey.expires_at) : null,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user