75 lines
3.0 KiB
TypeScript
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;
|
|
}
|