| | 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(); |
| |
|