link-stack/packages/bridge-common/lib/database.ts

198 lines
4.9 KiB
TypeScript
Raw Permalink Normal View History

2024-04-21 16:59:50 +02:00
import { PostgresDialect, CamelCasePlugin } from "kysely";
2025-11-13 11:18:08 +01:00
import type { GeneratedAlways, Generated, ColumnType, Selectable } from "kysely";
2024-04-30 13:13:49 +02:00
import pg from "pg";
2024-03-17 12:58:25 +01:00
import { KyselyAuth } from "@auth/kysely-adapter";
2024-04-30 13:13:49 +02:00
const { Pool, types } = pg;
2024-04-21 16:59:50 +02:00
2024-04-24 21:44:05 +02:00
type Timestamp = ColumnType<Date, Date | string>;
2025-11-13 11:18:08 +01:00
types.setTypeParser(types.builtins.TIMESTAMPTZ, (val) => new Date(val).toISOString());
2024-04-24 21:44:05 +02:00
2024-04-21 16:59:50 +02:00
type GraphileJob = {
taskIdentifier: string;
payload: Record<string, any>;
priority: number;
maxAttempts: number;
key: string;
queueName: string;
};
export const addGraphileJob = async (jobInfo: GraphileJob) => {
2024-04-21 20:47:55 +02:00
// await db.insertInto("graphile_worker.jobs").values(jobInfo).execute();
2024-04-21 16:59:50 +02:00
};
2024-03-17 12:58:25 +01:00
2024-04-23 10:31:26 +02:00
export interface Database {
2024-03-17 12:58:25 +01:00
User: {
2024-03-20 17:51:21 +01:00
id: string;
2024-03-17 12:58:25 +01:00
name: string | null;
email: string;
emailVerified: Date | null;
image: string | null;
};
Account: {
id: GeneratedAlways<string>;
userId: string;
2024-03-20 17:51:21 +01:00
type: "oidc" | "oauth" | "email" | "webauthn";
2024-03-17 12:58:25 +01:00
provider: string;
providerAccountId: string;
2024-03-20 17:51:21 +01:00
refresh_token: string | undefined;
access_token: string | undefined;
expires_at: number | undefined;
token_type: Lowercase<string> | undefined;
scope: string | undefined;
id_token: string | undefined;
session_state: string | undefined;
2024-03-17 12:58:25 +01:00
};
Session: {
id: GeneratedAlways<string>;
userId: string;
sessionToken: string;
expires: Date;
};
VerificationToken: {
identifier: string;
token: string;
expires: Date;
};
2024-04-23 13:36:51 +02:00
WhatsappBot: {
2024-03-17 12:58:25 +01:00
id: GeneratedAlways<string>;
2024-04-21 08:11:24 +02:00
name: string;
2024-04-25 13:36:50 +02:00
description: string;
2024-04-21 08:11:24 +02:00
phoneNumber: string;
2024-05-07 14:16:01 +02:00
token: string;
qrCode: string;
verified: boolean;
userId: string;
2024-04-21 08:11:24 +02:00
createdBy: string;
createdAt: Date;
updatedAt: Date;
};
2024-04-25 13:36:50 +02:00
FacebookBot: {
id: GeneratedAlways<string>;
name: string | null;
description: string | null;
token: string | null;
pageAccessToken: string | null;
appSecret: string | null;
verifyToken: string | null;
pageId: string | null;
appId: string | null;
userId: string | null;
2024-05-07 14:16:01 +02:00
verified: Generated<boolean>;
2024-04-25 13:36:50 +02:00
createdAt: GeneratedAlways<Timestamp>;
updatedAt: GeneratedAlways<Timestamp>;
};
2024-04-21 08:11:24 +02:00
2024-04-23 13:36:51 +02:00
VoiceLine: {
2024-04-21 08:11:24 +02:00
id: GeneratedAlways<string>;
name: string;
2024-04-25 13:36:50 +02:00
description: string;
2024-04-21 08:11:24 +02:00
createdBy: string;
createdAt: Date;
updatedAt: Date;
};
SignalBot: {
id: GeneratedAlways<string>;
name: string;
2024-04-25 13:36:50 +02:00
description: string;
2024-04-21 08:11:24 +02:00
phoneNumber: string;
2024-06-05 15:12:48 +02:00
qrCode: string;
token: string;
verified: boolean;
2024-04-21 08:11:24 +02:00
createdAt: Date;
updatedAt: Date;
2024-03-17 12:58:25 +01:00
};
2024-04-23 13:36:51 +02:00
Webhook: {
id: GeneratedAlways<string>;
name: string;
2024-04-25 13:36:50 +02:00
description: string;
2024-04-29 17:27:25 +02:00
backendType: string;
backendId: string;
endpointUrl: string;
httpMethod: "post" | "put";
headers: Record<string, any>;
2024-04-23 13:36:51 +02:00
createdBy: string;
createdAt: Date;
updatedAt: Date;
};
2024-03-17 12:58:25 +01:00
}
2024-04-25 13:36:50 +02:00
export type FacebookBot = Selectable<Database["FacebookBot"]>;
export type SignalBot = Selectable<Database["SignalBot"]>;
export type WhatsappBot = Selectable<Database["WhatsappBot"]>;
export type VoiceLine = Selectable<Database["VoiceLine"]>;
export type Webhook = Selectable<Database["Webhook"]>;
export type User = Selectable<Database["User"]>;
2024-04-25 12:31:03 +02:00
2025-11-10 14:55:22 +01:00
// Lazy database initialization to avoid errors during build time
let _db: KyselyAuth<Database> | undefined;
function getDb(): KyselyAuth<Database> {
if (_db) {
return _db;
}
// Validate environment variables
const DATABASE_HOST = process.env.DATABASE_HOST;
const DATABASE_NAME = process.env.DATABASE_NAME;
const DATABASE_PORT = process.env.DATABASE_PORT;
const DATABASE_USER = process.env.DATABASE_USER;
const DATABASE_PASSWORD = process.env.DATABASE_PASSWORD;
2025-11-13 11:18:08 +01:00
if (
!DATABASE_HOST ||
!DATABASE_NAME ||
!DATABASE_PORT ||
!DATABASE_USER ||
!DATABASE_PASSWORD
) {
throw new Error(
"Missing required database environment variables: DATABASE_HOST, DATABASE_NAME, DATABASE_PORT, DATABASE_USER, DATABASE_PASSWORD",
);
2025-11-10 14:55:22 +01:00
}
const port = parseInt(DATABASE_PORT, 10);
if (isNaN(port) || port < 1 || port > 65535) {
2025-11-13 11:18:08 +01:00
throw new Error(
`Invalid DATABASE_PORT: ${DATABASE_PORT}. Must be a number between 1 and 65535.`,
);
2025-11-10 14:55:22 +01:00
}
_db = new KyselyAuth<Database>({
dialect: new PostgresDialect({
pool: new Pool({
host: DATABASE_HOST,
database: DATABASE_NAME,
port,
user: DATABASE_USER,
password: DATABASE_PASSWORD,
}),
}) as any,
plugins: [new CamelCasePlugin() as any],
});
return _db;
}
// Export db as a getter that lazily initializes the database
export const db = new Proxy({} as KyselyAuth<Database>, {
get(_target, prop) {
const instance = getDb();
const value = (instance as any)[prop];
// If it's a function, bind it to the actual instance to preserve 'this' context
2025-11-13 11:18:08 +01:00
if (typeof value === "function") {
2025-11-10 14:55:22 +01:00
return value.bind(instance);
}
return value;
},
2024-03-17 12:58:25 +01:00
});