import { Db, Document, Filter, MongoClient } from "mongodb"; import { Collection } from "./types"; const DATABASE_URL = "mongodb://mongoadmin:mongoadmin@localhost:27017/?replicaSet=rs0&readPreference=primaryPreferred&directConnection=true"; const DATABASE_NAME = "openvidu"; /** Statuses of the entities that are still open, per collection. */ const OPEN_STATUSES: Partial> = { [Collection.ROOMS]: ["active"], [Collection.PARTICIPANTS]: ["active", "joining"], [Collection.TRACKS]: ["published"], [Collection.TRACK_SUBSCRIPTIONS]: ["requested", "subscribed"] }; /** A document of the analytics data model, whose ids are strings. */ export interface AnalyticsDocument extends Document { _id: string; } /** A query on documents whose ids are strings. */ export type Query = Filter; export class MongoService { private static instance: MongoService; private client: MongoClient; private db: Db; private constructor() { this.client = new MongoClient(DATABASE_URL, { serverSelectionTimeoutMS: 30000 }); this.db = this.client.db(DATABASE_NAME); } public static getInstance(): MongoService { if (!MongoService.instance) { MongoService.instance = new MongoService(); } return MongoService.instance; } public async disconnect(): Promise { await this.client.close(); } public database(): Db { return this.db; } public async findEntity(collection: Collection, filter: Query): Promise { return this.db.collection(collection).findOne(filter); } public async findEntities(collection: Collection, filter: Query): Promise { return this.db .collection(collection) .find(filter) .toArray(); } public async count(collection: Collection, filter: Query): Promise { return this.db.collection(collection).countDocuments(filter); } /** Documents of the collection matching the filter that are still open. */ public async countOpen(collection: Collection, filter: Query): Promise { return this.count(collection, { ...filter, status: { $in: OPEN_STATUSES[collection] ?? [] } }); } /** Inserts a document as the writer would have left it, for the cases that need a state no failure can produce. */ public async insertEntity(collection: Collection, doc: AnalyticsDocument): Promise { await this.db.collection(collection).insertOne(doc); } public async findEvents(filter: Query): Promise { return this.db.collection(Collection.EVENTS).find(filter).sort({ ts: 1 }).toArray(); } /** The synthetic close event the reconciler inserted for an entity. */ public async findSyntheticEvent(entityId: string): Promise { return this.db.collection(Collection.EVENTS).findOne({ _id: `AE_SYN_${entityId}`, synthetic: true }); } public async schemaMeta(id: string): Promise { return this.db.collection(Collection.SCHEMA_META).findOne({ _id: id }); } public async collectionNames(): Promise { return (await this.db.listCollections({}, { nameOnly: true }).toArray()).map((info) => info.name); } }