Files
kinship-directory/src/lib/db.ts
T

75 lines
3.0 KiB
TypeScript

import { MongoClient, ObjectId, type Db, type Document } from "mongodb";
import type { Family, Organization, Person, Theme } from "./types";
const globalMongo = globalThis as typeof globalThis & {
mongoClient?: Promise<MongoClient>;
mongoIndexed?: boolean;
};
export async function db(): Promise<Db> {
const uri = process.env.MONGODB_URI;
if (!uri) throw new Error("MONGODB_URI is not configured");
const clientPromise = globalMongo.mongoClient ?? new MongoClient(uri).connect();
globalMongo.mongoClient = clientPromise;
const client = await clientPromise;
const database = client.db(process.env.MONGODB_DB || "kinship_directory");
if (!globalMongo.mongoIndexed) {
await Promise.all([
database.collection("users").createIndex({ email: 1 }, { unique: true }),
database.collection("families").createIndex({ organizationId: 1, familyName: 1 }),
database.collection("images").createIndex({ organizationId: 1 }),
database.collection("organizations").createIndex({ joinCode: 1 }, { unique: true }),
]);
globalMongo.mongoIndexed = true;
}
return database;
}
const stringId = (value: unknown) => value instanceof ObjectId ? value.toHexString() : String(value);
export function mapPerson(person: Document): Person {
return {
id: String(person.id),
firstName: String(person.firstName || ""),
lastName: String(person.lastName || ""),
email: person.email ? String(person.email) : undefined,
phone: person.phone ? String(person.phone) : undefined,
birthday: person.birthday ? String(person.birthday) : undefined,
photoId: person.photoId ? String(person.photoId) : undefined,
};
}
export function mapFamily(doc: Document): Family {
return {
id: stringId(doc._id),
organizationId: stringId(doc.organizationId),
familyName: String(doc.familyName),
address: doc.address ? String(doc.address) : undefined,
city: doc.city ? String(doc.city) : undefined,
state: doc.state ? String(doc.state) : undefined,
postalCode: doc.postalCode ? String(doc.postalCode) : undefined,
homePhone: doc.homePhone ? String(doc.homePhone) : undefined,
photoId: doc.photoId ? String(doc.photoId) : undefined,
adults: Array.isArray(doc.adults) ? doc.adults.map(mapPerson) : [],
children: Array.isArray(doc.children) ? doc.children.map(mapPerson) : [],
updatedAt: doc.updatedAt instanceof Date ? doc.updatedAt.toISOString() : new Date().toISOString(),
};
}
const defaultTheme: Theme = { primary: "#173f35", accent: "#df9b63", surface: "#f7f4ed", font: "warm" };
export function mapOrganization(doc: Document): Organization {
return {
id: stringId(doc._id),
name: String(doc.name),
joinCode: String(doc.joinCode),
logoId: doc.logoId ? String(doc.logoId) : undefined,
faviconId: doc.faviconId ? String(doc.faviconId) : undefined,
theme: { ...defaultTheme, ...(doc.theme || {}) },
};
}
export function objectId(value: unknown): ObjectId | null {
return typeof value === "string" && ObjectId.isValid(value) ? new ObjectId(value) : null;
}