diff --git a/server/src/database/database.service.ts b/server/src/database/database.service.ts index c5e52fae..acb6b5c7 100644 --- a/server/src/database/database.service.ts +++ b/server/src/database/database.service.ts @@ -5,7 +5,12 @@ import { GenerateAlphaNumericPassword } from 'src/utils/random' import { MongoService } from './mongo.service' import * as mongodb_uri from 'mongodb-uri' import { RegionService } from 'src/region/region.service' -import { TASK_LOCK_INIT_TIME } from 'src/constants' +import { + CN_PUBLISHED_CONF, + CN_PUBLISHED_FUNCTIONS, + CN_PUBLISHED_WEBSITE_HOSTING, + TASK_LOCK_INIT_TIME, +} from 'src/constants' import { Region } from 'src/region/entities/region' import { SystemDatabase } from '../system-database' import { @@ -18,6 +23,9 @@ import { exec } from 'node:child_process' import { promisify } from 'node:util' import { DatabaseSyncRecord } from './entities/database-sync-record' import { MongoClient, ObjectId } from 'mongodb' +import { DedicatedDatabaseService } from './dedicated-database/dedicated-database.service' +import { CloudFunction } from 'src/function/entities/cloud-function' +import { ApplicationConfiguration } from 'src/application/entities/application-configuration' const p_exec = promisify(exec) @@ -29,6 +37,7 @@ export class DatabaseService { constructor( private readonly mongoService: MongoService, private readonly regionService: RegionService, + private readonly dedicatedDatabaseService: DedicatedDatabaseService, ) {} async create(appid: string) { @@ -226,11 +235,31 @@ export class DatabaseService { async exportDatabase(appid: string, filePath: string, uid: ObjectId) { const region = await this.regionService.findByAppId(appid) - const database = await this.findOne(appid) - assert(database, 'Database not found') + const sharedDatabase = await this.findOne(appid) + const dedicatedDatabase = await this.dedicatedDatabaseService.findOne(appid) - const connectionUri = this.getControlConnectionUri(region, database) - assert(connectionUri, 'Database connection uri not found') + if (sharedDatabase && dedicatedDatabase) { + throw new Error( + `Database ${appid} found in both shared and dedicated databases.`, + ) + } + + if (!sharedDatabase && !dedicatedDatabase) { + throw new Error( + `Database ${appid} not found in both shared and dedicated databases.`, + ) + } + let connectionUri + if (sharedDatabase) { + connectionUri = this.getControlConnectionUri(region, sharedDatabase) + } else { + connectionUri = await this.dedicatedDatabaseService.getConnectionUri( + region, + dedicatedDatabase, + ) + } + + assert(connectionUri, `Database ${appid} connection uri not found`) try { await p_exec( @@ -252,16 +281,39 @@ export class DatabaseService { uid: ObjectId, ): Promise { const region = await this.regionService.findByAppId(appid) - const database = await this.findOne(appid) - assert(database, 'Database not found') - const connectionUri = this.getControlConnectionUri(region, database) - assert(connectionUri, 'Database connection uri not found') + const sharedDatabase = await this.findOne(appid) + const dedicatedDatabase = await this.dedicatedDatabaseService.findOne(appid) + + if (sharedDatabase && dedicatedDatabase) { + throw new Error( + `Database ${appid} found in both shared and dedicated databases.`, + ) + } + + if (!sharedDatabase && !dedicatedDatabase) { + throw new Error( + `Database ${appid} not found in both shared and dedicated databases.`, + ) + } + let connectionUri + if (sharedDatabase) { + connectionUri = this.getControlConnectionUri(region, sharedDatabase) + } else { + connectionUri = await this.dedicatedDatabaseService.getConnectionUri( + region, + dedicatedDatabase, + ) + } + assert(connectionUri, `Database ${appid} connection uri not found`) try { await p_exec( `mongorestore --uri='${connectionUri}' --gzip --archive='${filePath}' --nsFrom="${dbName}.*" --nsTo="${appid}.*" -v --nsInclude="${dbName}.*"`, ) + + await this.recoverFunctionsToSystemDatabase(appid, uid) + await this.db .collection('DatabaseSyncRecord') .insertOne({ uid, createdAt: new Date() }) @@ -271,4 +323,54 @@ export class DatabaseService { throw error } } + + async recoverFunctionsToSystemDatabase(appid: string, uid: ObjectId) { + const { db, client } = + (await this.dedicatedDatabaseService.findAndConnect(appid)) || + (await this.findAndConnect(appid)) + + try { + const appFunctionCollection = db.collection(CN_PUBLISHED_FUNCTIONS) + const appConfCollection = db.collection(CN_PUBLISHED_CONF) + const appWebsiteCollection = db.collection(CN_PUBLISHED_WEBSITE_HOSTING) + + const functionsExist = await this.db + .collection('CloudFunction') + .countDocuments({ appid }) + + if (functionsExist) { + this.logger.debug(`${appid} Functions already exist in system database`) + return + } + + const funcs: CloudFunction[] = await appFunctionCollection + .find({}) + .toArray() + + if (funcs.length === 0) { + this.logger.debug(` ${appid} No functions for recover.`) + return + } + + funcs.forEach((func) => { + delete func._id + func.appid = appid + func.createdBy = uid + }) + + await this.db.collection('CloudFunction').insertMany(funcs) + + // sync conf + const conf = await this.db + .collection('ApplicationConfiguration') + .findOne({ appid }) + + await appConfCollection.deleteMany({}) + await appConfCollection.insertOne(conf) + + await appWebsiteCollection.deleteMany({}) + } finally { + await client.close() + } + } }