| import { env } from "$env/dynamic/private"; |
| import { GridFSBucket, MongoClient } from "mongodb"; |
| import type { Conversation } from "$lib/types/Conversation"; |
| import type { SharedConversation } from "$lib/types/SharedConversation"; |
| import type { AbortedGeneration } from "$lib/types/AbortedGeneration"; |
| import type { Settings } from "$lib/types/Settings"; |
| import type { User } from "$lib/types/User"; |
| import type { MessageEvent } from "$lib/types/MessageEvent"; |
| import type { Session } from "$lib/types/Session"; |
| import type { Assistant } from "$lib/types/Assistant"; |
| import type { Report } from "$lib/types/Report"; |
| import type { ConversationStats } from "$lib/types/ConversationStats"; |
| import type { MigrationResult } from "$lib/types/MigrationResult"; |
| import type { Semaphore } from "$lib/types/Semaphore"; |
| import type { AssistantStats } from "$lib/types/AssistantStats"; |
| import { logger } from "$lib/server/logger"; |
| import { building } from "$app/environment"; |
|
|
| export const CONVERSATION_STATS_COLLECTION = "conversations.stats"; |
|
|
| export class Database { |
| private client: MongoClient; |
|
|
| private static instance: Database; |
|
|
| private constructor() { |
| if (!env.MONGODB_URL) { |
| throw new Error( |
| "Please specify the MONGODB_URL environment variable inside .env.local. Set it to mongodb://localhost:27017 if you are running MongoDB locally, or to a MongoDB Atlas free instance for example." |
| ); |
| } |
|
|
| this.client = new MongoClient(env.MONGODB_URL, { |
| directConnection: env.MONGODB_DIRECT_CONNECTION === "true", |
| }); |
|
|
| this.client.connect().catch((err) => { |
| logger.error("Connection error", err); |
| process.exit(1); |
| }); |
| this.client.db(env.MONGODB_DB_NAME + (import.meta.env.MODE === "test" ? "-test" : "")); |
| this.client.on("open", () => this.initDatabase()); |
|
|
| |
| process.on("SIGINT", async () => { |
| await this.client.close(true); |
|
|
| |
| setTimeout(() => { |
| process.exit(0); |
| }, 100); |
| }); |
| } |
|
|
| public static getInstance(): Database { |
| if (!Database.instance) { |
| Database.instance = new Database(); |
| } |
|
|
| return Database.instance; |
| } |
|
|
| |
| |
| |
| public getClient(): MongoClient { |
| return this.client; |
| } |
|
|
| |
| |
| |
| public getCollections() { |
| const db = this.client.db( |
| env.MONGODB_DB_NAME + (import.meta.env.MODE === "test" ? "-test" : "") |
| ); |
|
|
| const conversations = db.collection<Conversation>("conversations"); |
| const conversationStats = db.collection<ConversationStats>(CONVERSATION_STATS_COLLECTION); |
| const assistants = db.collection<Assistant>("assistants"); |
| const assistantStats = db.collection<AssistantStats>("assistants.stats"); |
| const reports = db.collection<Report>("reports"); |
| const sharedConversations = db.collection<SharedConversation>("sharedConversations"); |
| const abortedGenerations = db.collection<AbortedGeneration>("abortedGenerations"); |
| const settings = db.collection<Settings>("settings"); |
| const users = db.collection<User>("users"); |
| const sessions = db.collection<Session>("sessions"); |
| const messageEvents = db.collection<MessageEvent>("messageEvents"); |
| const bucket = new GridFSBucket(db, { bucketName: "files" }); |
| const migrationResults = db.collection<MigrationResult>("migrationResults"); |
| const semaphores = db.collection<Semaphore>("semaphores"); |
|
|
| return { |
| conversations, |
| conversationStats, |
| assistants, |
| assistantStats, |
| reports, |
| sharedConversations, |
| abortedGenerations, |
| settings, |
| users, |
| sessions, |
| messageEvents, |
| bucket, |
| migrationResults, |
| semaphores, |
| }; |
| } |
|
|
| |
| |
| |
| |
| private initDatabase() { |
| const { |
| conversations, |
| conversationStats, |
| assistants, |
| assistantStats, |
| reports, |
| sharedConversations, |
| abortedGenerations, |
| settings, |
| users, |
| sessions, |
| messageEvents, |
| semaphores, |
| } = this.getCollections(); |
|
|
| conversations |
| .createIndex( |
| { sessionId: 1, updatedAt: -1 }, |
| { partialFilterExpression: { sessionId: { $exists: true } } } |
| ) |
| .catch(logger.error); |
| conversations |
| .createIndex( |
| { userId: 1, updatedAt: -1 }, |
| { partialFilterExpression: { userId: { $exists: true } } } |
| ) |
| .catch(logger.error); |
| conversations |
| .createIndex( |
| { "message.id": 1, "message.ancestors": 1 }, |
| { partialFilterExpression: { userId: { $exists: true } } } |
| ) |
| .catch(logger.error); |
| |
| |
| conversations.createIndex({ "messages.createdAt": 1 }, { sparse: true }).catch(logger.error); |
| |
| conversationStats |
| .createIndex( |
| { |
| type: 1, |
| "date.field": 1, |
| "date.span": 1, |
| "date.at": 1, |
| distinct: 1, |
| }, |
| { unique: true } |
| ) |
| .catch(logger.error); |
| |
| conversationStats |
| .createIndex({ |
| type: 1, |
| "date.field": 1, |
| "date.at": 1, |
| }) |
| .catch(logger.error); |
| abortedGenerations |
| .createIndex({ updatedAt: 1 }, { expireAfterSeconds: 30 }) |
| .catch(logger.error); |
| abortedGenerations.createIndex({ conversationId: 1 }, { unique: true }).catch(logger.error); |
| sharedConversations.createIndex({ hash: 1 }, { unique: true }).catch(logger.error); |
| settings.createIndex({ sessionId: 1 }, { unique: true, sparse: true }).catch(logger.error); |
| settings.createIndex({ userId: 1 }, { unique: true, sparse: true }).catch(logger.error); |
| settings.createIndex({ assistants: 1 }).catch(logger.error); |
| users.createIndex({ hfUserId: 1 }, { unique: true }).catch(logger.error); |
| users.createIndex({ sessionId: 1 }, { unique: true, sparse: true }).catch(logger.error); |
| |
| users.createIndex({ username: 1 }).catch(logger.error); |
| messageEvents.createIndex({ createdAt: 1 }, { expireAfterSeconds: 60 }).catch(logger.error); |
| sessions.createIndex({ expiresAt: 1 }, { expireAfterSeconds: 0 }).catch(logger.error); |
| sessions.createIndex({ sessionId: 1 }, { unique: true }).catch(logger.error); |
| assistants.createIndex({ createdById: 1, userCount: -1 }).catch(logger.error); |
| assistants.createIndex({ userCount: 1 }).catch(logger.error); |
| assistants.createIndex({ featured: 1, userCount: -1 }).catch(logger.error); |
| assistants.createIndex({ modelId: 1, userCount: -1 }).catch(logger.error); |
| assistants.createIndex({ searchTokens: 1 }).catch(logger.error); |
| assistants.createIndex({ last24HoursCount: 1 }).catch(logger.error); |
| assistantStats |
| |
| .createIndex({ "date.span": 1, "date.at": 1, assistantId: 1 }, { unique: true }) |
| .catch(logger.error); |
| reports.createIndex({ assistantId: 1 }).catch(logger.error); |
| reports.createIndex({ createdBy: 1, assistantId: 1 }).catch(logger.error); |
|
|
| |
| semaphores.createIndex({ key: 1 }, { unique: true }).catch(logger.error); |
| semaphores.createIndex({ createdAt: 1 }, { expireAfterSeconds: 60 }).catch(logger.error); |
| } |
| } |
|
|
| export const collections = building |
| ? ({} as unknown as ReturnType<typeof Database.prototype.getCollections>) |
| : Database.getInstance().getCollections(); |
|
|