UNPKG

@topgroup/diginext

Version:

A BUILD SERVER & CLI to deploy apps to any Kubernetes clusters.

598 lines (597 loc) 32.6 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.AppService = void 0; const lodash_1 = require("lodash"); const path_1 = __importDefault(require("path")); const const_1 = require("../config/const"); const App_1 = require("../entities/App"); const generate_deployment_name_1 = __importDefault(require("../modules/deploy/generate-deployment-name")); const dx_domain_1 = require("../modules/diginext/dx-domain"); const git_1 = require("../modules/git"); const git_provider_api_1 = __importDefault(require("../modules/git/git-provider-api")); const git_utils_1 = require("../modules/git/git-utils"); const initalizeAndCreateDefaultBranches_1 = require("../modules/git/initalizeAndCreateDefaultBranches"); const k8s_1 = __importDefault(require("../modules/k8s")); const check_quota_1 = require("../modules/workspace/check-quota"); const plugins_1 = require("../plugins"); const array_1 = require("../plugins/array"); const env_var_1 = require("../plugins/env-var"); const mask_sensitive_info_1 = require("../plugins/mask-sensitive-info"); const mongodb_1 = require("../plugins/mongodb"); const slug_1 = require("../plugins/slug"); const string_1 = require("../plugins/string"); const user_utils_1 = require("../plugins/user-utils"); const BaseService_1 = __importDefault(require("./BaseService")); class AppService extends BaseService_1.default { constructor(ownership) { super(App_1.appSchema, ownership); } async create(data, options) { // validate let project; let appDto = { ...data }; // ownership const { WorkspaceService, ProjectService, GitProviderService } = await Promise.resolve().then(() => __importStar(require("./index"))); const wsSvc = new WorkspaceService(); const projectSvc = new ProjectService(); const workspace = this.ownership.workspace || (data.workspace && mongodb_1.MongoDB.isValidObjectId(data.workspace) ? await wsSvc.findOne({ _id: data.workspace }) : undefined); if (!workspace) throw new Error(`Workspace not found.`); // check dx quota const quotaRes = await (0, check_quota_1.checkQuota)(workspace); if (!quotaRes.status) throw new Error(quotaRes.messages.join(". ")); if (quotaRes.data && quotaRes.data.isExceed) throw new Error(`You've exceeded the limit amount of apps (${quotaRes.data.type} / Max. ${quotaRes.data.limits.apps} apps).`); // validate if (!data.project) throw new Error(`Project ID or slug or instance is required.`); if (!data.name) throw new Error(`App's name is required.`); if ((0, string_1.containsSpecialCharacters)(data.name)) throw new Error(`App's name should not contain special characters.`); // find parent project of this app if (mongodb_1.MongoDB.isValidObjectId(data.project)) { project = await projectSvc.findOne({ _id: data.project }); } else if ((0, lodash_1.isString)(data.project)) { project = await projectSvc.findOne({ slug: data.project }); } else { throw new Error(`"project" is not a valid ID or slug.`); } if (!project) throw new Error(`Project "${data.project}" not found.`); appDto.projectSlug = project.slug; // check access permissions (0, user_utils_1.checkProjectPermissions)(project, this.user); // framework if (!data.framework) data.framework = { name: "none", slug: "none", repoURL: "unknown", repoSSH: "unknown" }; if (data.framework === "none") data.framework = { name: "none", slug: "none", repoURL: "unknown", repoSSH: "unknown" }; appDto.framework = data.framework; // git repo if (options.shouldCreateGitRepo) { // create repo if needed if (!data.gitProvider) throw new Error(`"gitProvider" is required.`); const gitSvc = new GitProviderService(); const gitProvider = await gitSvc.findOne({ _id: data.gitProvider }); if (!gitProvider) throw new Error(`Git provider not found.`); const repoSlug = `${project.slug}-${(0, slug_1.makeSlug)(data.name)}`.toLowerCase(); if (options === null || options === void 0 ? void 0 : options.force) { try { await git_provider_api_1.default.deleteGitRepository(gitProvider, gitProvider.org, repoSlug); } catch (e) { } } const newRepo = await git_provider_api_1.default.createGitRepository(gitProvider, { name: repoSlug, private: true }); // assign to app data: appDto.git = { repoSSH: newRepo.ssh_url, repoURL: newRepo.repo_url, provider: gitProvider.type, }; } else { if ((0, lodash_1.isString)(data.git)) { const gitData = (0, git_utils_1.parseGitRepoDataFromRepoSSH)(data.git); if (!gitData) throw new Error(`Git repository information is not valid.`); data.git = { repoSSH: data.git, repoURL: (0, git_1.getRepoURLFromRepoSSH)(gitData.providerType, gitData.fullSlug), provider: gitData.providerType, }; } if (!data.git) throw new Error(`Git info is required.`); appDto.git = data.git; } if (!appDto.ownerSlug) appDto.ownerSlug = this.user.username || this.ownership.owner.username; let newApp; try { newApp = await super.create(appDto, options); if (!newApp) throw new Error(`Unable to create new app: "${appDto.name}".`); } catch (e) { throw new Error(e.toString()); } const newAppId = newApp._id; /** * @deprecated */ // migrate app environment variables if needed (convert {Object} to {Array}) // const migratedApp = await migrateAppEnvironmentVariables(newApp); // if (migratedApp) newApp = migratedApp; // add this new app to the project info if (project) { projectSvc.ownership = this.ownership; const projectApps = [...(project.apps || []), newAppId]; const projectAppSlugs = (0, array_1.uniqueStrings)([...(project.appSlugs || []), newApp.slug]); project = await projectSvc.updateOne({ _id: project._id }, { apps: projectApps, appSlugs: projectAppSlugs }); } return newApp; } async createWithGitURL(repoSSH, gitProviderID, ownership, options) { const appDto = {}; const workspace = ownership.workspace; const owner = ownership.owner; if (options === null || options === void 0 ? void 0 : options.isDebugging) console.log("createWithGitURL() > ownership :>> ", { workspace, owner }); // parse git data const repoData = (0, git_utils_1.parseGitRepoDataFromRepoSSH)(repoSSH); if (options === null || options === void 0 ? void 0 : options.isDebugging) console.log("createWithGitURL() > repoData :>> ", repoData); if (!repoData) throw new Error(`Unable to read git repo SSH.`); const { repoSlug } = repoData; // default project const { ProjectService, GitProviderService } = await Promise.resolve().then(() => __importStar(require("./index"))); const projectSvc = new ProjectService(); projectSvc.ownership = this.ownership; let project = await projectSvc.findOne({ isDefault: true, workspace: workspace._id }, options); if (!project) project = await projectSvc.create({ name: "Default", isDefault: true, workspace: workspace._id, owner: owner._id }); // check permissions (0, user_utils_1.checkProjectPermissions)(project, this.user); // git provider const gitSvc = new GitProviderService(); const gitProvider = await gitSvc.findOne({ _id: gitProviderID }); if (options === null || options === void 0 ? void 0 : options.isDebugging) console.log("createWithGitURL() > gitProvider :>> ", gitProvider); if (!gitProvider) throw new Error(`Unable to import git repo, git provider not found.`); // new repo slug const newRepoSlug = `${project.slug}-${(0, slug_1.makeSlug)(repoSlug)}`.toLowerCase(); const newRepoSSH = `git@${gitProvider.host}:${gitProvider.org}/${newRepoSlug}.git`; // check app is existed const existingApp = await this.findOne({ "git.repoSSH": newRepoSSH, workspace: workspace._id }, options); if (options === null || options === void 0 ? void 0 : options.isDebugging) console.log("createWithGitURL() > existingApp :>> ", existingApp); if (existingApp) { // [DANGEROUS] delete existing app when `--force` is specified: if (options === null || options === void 0 ? void 0 : options.force) { await this.delete({ "git.repoSSH": newRepoSSH, workspace: workspace._id }); } else { if (options === null || options === void 0 ? void 0 : options.returnExisting) return existingApp; throw new Error(`Unable to import: app was existed with name "${existingApp.slug}" (Project: "${project.name}").`); } } // clone/pull that repo url const branch = (options === null || options === void 0 ? void 0 : options.gitBranch) || "main"; const SOURCE_CODE_DIR = `cache/${project.slug}/${newRepoSlug}/${branch}`; const APP_DIR = path_1.default.resolve(const_1.CLI_CONFIG_DIR, SOURCE_CODE_DIR); // try with "repoSSH" first, if failed, try "repoURL"... try { await (0, plugins_1.pullOrCloneGitRepo)(repoSSH, APP_DIR, branch, { isDebugging: options === null || options === void 0 ? void 0 : options.isDebugging, removeGitOnFinish: true, removeCIOnFinish: options.removeCI, }); } catch (e) { const repoURL = (0, git_utils_1.repoSshToRepoURL)(repoSSH); await (0, git_utils_1.pullOrCloneGitRepoHTTP)(repoURL, APP_DIR, branch, { isDebugging: options === null || options === void 0 ? void 0 : options.isDebugging, removeGitOnFinish: true, removeCIOnFinish: options.removeCI, useAccessToken: { type: gitProvider.method === "basic" ? "Basic" : "Bearer", value: gitProvider.access_token, }, }); } if (options === null || options === void 0 ? void 0 : options.force) { try { await git_provider_api_1.default.deleteGitRepository(gitProvider, gitProvider.org, newRepoSlug); } catch (e) { } } // create git repo const gitRepo = await git_provider_api_1.default.createGitRepository(gitProvider, { name: newRepoSlug, private: true, description: `Forked from ${repoSSH}`, }, options); // setup initial repo: default branches, locked,... await (0, initalizeAndCreateDefaultBranches_1.initalizeAndCreateDefaultBranches)({ targetDirectory: APP_DIR, repoSSH: newRepoSSH, git: gitProvider, username: owner.slug, isDebugging: options === null || options === void 0 ? void 0 : options.isDebugging, }); // prepare app data appDto.name = repoSlug; appDto.owner = owner._id; appDto.ownerSlug = owner.slug; appDto.workspace = workspace._id; appDto.workspaceSlug = workspace.slug; appDto.project = project._id; appDto.projectSlug = project.slug; appDto.gitProvider = gitProvider._id; appDto.git = { provider: gitProvider.type, repoSSH: gitRepo.ssh_url, repoURL: gitRepo.repo_url }; appDto.framework = { name: "none", slug: "none", repoURL: "unknown", repoSSH: "unknown" }; // ownership appDto.workspace = workspace._id; appDto.workspaceSlug = workspace.slug; appDto.owner = owner._id; appDto.ownerSlug = owner.slug; // save to database const newApp = await this.create(appDto, options); // add app & app slug to project projectSvc.ownership = this.ownership; await projectSvc.updateOne({ _id: project._id }, { $push: { apps: newApp._id, appSlugs: newApp.slug } }, { raw: true }); return newApp; } async find(filter, options, pagination) { var _a, _b, _c, _d, _e; // check access permissions if ((_c = (_b = (_a = this.user) === null || _a === void 0 ? void 0 : _a.allowAccess) === null || _b === void 0 ? void 0 : _b.apps) === null || _c === void 0 ? void 0 : _c.length) filter = { $or: [filter, { _id: { $in: (_e = (_d = this.user) === null || _d === void 0 ? void 0 : _d.allowAccess) === null || _e === void 0 ? void 0 : _e.apps } }] }; const { status = false } = options || {}; const apps = await super.find(filter, options, pagination); // if skip status checking, return apps if (!status) { return apps.map((app) => { if (app && app.deployEnvironment) { for (const env of Object.keys(app.deployEnvironment)) { if (app.deployEnvironment[env] && app.deployEnvironment[env].envVars) { app.deployEnvironment[env].envVars = (0, env_var_1.formatEnvVars)(app.deployEnvironment[env].envVars); } } } return app; }); } // start checking status -> get cluster info const { ClusterService } = await Promise.resolve().then(() => __importStar(require("./index"))); const clusterSvc = new ClusterService(); const clusterFilter = {}; if (filter === null || filter === void 0 ? void 0 : filter.workspace) clusterFilter.workspace = filter.workspace; const clusters = await clusterSvc.find(clusterFilter); // check app deploy environment's status in clusters const appsWithStatus = await Promise.all(apps .map(async (app) => { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o; if (app && app.deployEnvironment) { for (const env of Object.keys(app.deployEnvironment)) { if (!app.deployEnvironment[env]) app.deployEnvironment[env] = { buildTag: "" }; // format environment variables (if any) if (app.deployEnvironment[env] && app.deployEnvironment[env].envVars) { app.deployEnvironment[env].envVars = (0, env_var_1.formatEnvVars)(app.deployEnvironment[env].envVars); } // default values app.deployEnvironment[env].readyCount = 0; app.deployEnvironment[env].status = "undeployed"; if (!app.deployEnvironment[env].cluster) return app; // find cluster & namespace const clusterSlug = app.deployEnvironment[env].cluster; const cluster = clusters.find((_cluster) => _cluster.slug === clusterSlug); if (!cluster) return app; const { contextName: context } = cluster; if (!context) return app; const { namespace } = app.deployEnvironment[env]; if (!namespace) return app; // find workloads base on "main-app" label const mainAppName = await (0, generate_deployment_name_1.default)(app); let [deployOnCluster] = await k8s_1.default.getDeploys(namespace, { filterLabel: `main-app=${mainAppName}`, context, metrics: true, }); console.log(`----- ${app.name} -----`); // console.log("- mainAppName :>> ", mainAppName); // console.log("- deployOnCluster.metadata.name :>> ", deployOnCluster?.metadata?.name); console.log("- deployOnCluster.status.replicas :>> ", (_a = deployOnCluster === null || deployOnCluster === void 0 ? void 0 : deployOnCluster.status) === null || _a === void 0 ? void 0 : _a.replicas); console.log("- deployOnCluster.resources.limits :>> ", (_f = (_e = (_d = (_c = (_b = deployOnCluster === null || deployOnCluster === void 0 ? void 0 : deployOnCluster.spec) === null || _b === void 0 ? void 0 : _b.template) === null || _c === void 0 ? void 0 : _c.spec) === null || _d === void 0 ? void 0 : _d.containers) === null || _e === void 0 ? void 0 : _e[0]) === null || _f === void 0 ? void 0 : _f.resources.limits); console.log("- deployOnCluster.cpuAvg :>> ", deployOnCluster === null || deployOnCluster === void 0 ? void 0 : deployOnCluster.cpuAvg); console.log("- deployOnCluster.memoryAvg :>> ", deployOnCluster === null || deployOnCluster === void 0 ? void 0 : deployOnCluster.memoryAvg); // console.log("- deployOnCluster.status.readyReplicas :>> ", deployOnCluster?.status?.readyReplicas); // console.log("- deployOnCluster.status.availableReplicas :>> ", deployOnCluster?.status?.availableReplicas); // console.log("- deployOnCluster.status.unavailableReplicas :>> ", deployOnCluster?.status?.unavailableReplicas); app.deployEnvironment[env].resources = {}; app.deployEnvironment[env].resources.limits = (_l = (_k = (_j = (_h = (_g = deployOnCluster === null || deployOnCluster === void 0 ? void 0 : deployOnCluster.spec) === null || _g === void 0 ? void 0 : _g.template) === null || _h === void 0 ? void 0 : _h.spec) === null || _j === void 0 ? void 0 : _j.containers) === null || _k === void 0 ? void 0 : _k[0]) === null || _l === void 0 ? void 0 : _l.resources.limits; app.deployEnvironment[env].resources.usage = { cpu: deployOnCluster === null || deployOnCluster === void 0 ? void 0 : deployOnCluster.cpuAvg, memory: deployOnCluster === null || deployOnCluster === void 0 ? void 0 : deployOnCluster.memoryAvg, }; if (!deployOnCluster) { app.deployEnvironment[env].status = "undeployed"; return app; } app.deployEnvironment[env].readyCount = (_o = (_m = deployOnCluster.status.readyReplicas) !== null && _m !== void 0 ? _m : deployOnCluster.status.availableReplicas) !== null && _o !== void 0 ? _o : 0; // console.log("- app.deployEnvironment[env].readyCount :>> ", app.deployEnvironment[env].readyCount); if (deployOnCluster.status.replicas === deployOnCluster.status.availableReplicas || deployOnCluster.status.replicas === deployOnCluster.status.readyReplicas) { app.deployEnvironment[env].status = "healthy"; return app; } if (deployOnCluster.status.unavailableReplicas && deployOnCluster.status.unavailableReplicas > 0) { app.deployEnvironment[env].status = "partial_healthy"; return app; } if (deployOnCluster.status.availableReplicas === 0 || deployOnCluster.status.unavailableReplicas === deployOnCluster.status.replicas || deployOnCluster.status.readyReplicas === 0) { app.deployEnvironment[env].status = "failed"; return app; } app.deployEnvironment[env].status = "unknown"; } } return app; }) .filter((app) => typeof app !== "undefined")); return appsWithStatus; } async update(filter, data, options) { // permissions await (0, user_utils_1.checkAppPermissionsByFilter)(this, filter, this.user); // const allApps = await this.find({}); // console.log("AppService > allApps :>> ", allApps); // if (options?.isDebugging) console.log("AppService > update > filter :>>", filter); let apps = await this.find(filter, data, options); if ((0, lodash_1.isEmpty)(apps)) { console.error(`AppService > update :>>`, { filter }); throw new Error(`Apps not found.`); } // console.log("AppService > update > apps :>>", apps); const { ProjectService, WorkspaceService } = await Promise.resolve().then(() => __importStar(require("./index"))); const projectSvc = new ProjectService(this.ownership); let project; if (data.project) { // find a project of this app if (mongodb_1.MongoDB.isValidObjectId(data.project)) { project = await projectSvc.findOne({ _id: data.project }); } else if ((0, lodash_1.isString)(data.project)) { project = await projectSvc.findOne({ slug: data.project }); } else { throw new Error(`Param "project" is not a valid ID or slug.`); } if (!project) throw new Error(`Project "${data.project}" not found.`); data.projectSlug = project.slug; data.project = project._id; // add this app._id and app.slug to project.apps and project.appSlugs if (!project.apps) project.apps = []; if (!project.appSlugs) project.appSlugs = []; if (!project.apps.includes(data._id)) project.apps.push(data._id); if (!project.appSlugs.includes(data.slug)) project.appSlugs.push(data.slug); // update project apps.forEach(async (app) => { if (app._id && !project.apps.includes(app._id)) project.apps.push(app._id); if (app._id && !project.appSlugs.includes(app.slug)) project.appSlugs.push(app.slug); await projectSvc.updateOne({ _id: project._id }, { apps: project.apps, appSlugs: project.appSlugs }); }); } // delete deploy environment of this app if (data.deployEnvironment) { const [app] = apps; const wsId = app.workspace ? (mongodb_1.MongoDB.isValidObjectId(app.workspace) ? app.workspace : app.workspace._id) : undefined; if (!wsId) throw new Error(`Workspace ID is not valid.`); const wsSvc = new WorkspaceService(); const workspace = this.workspace || (await wsSvc.findOne({ _id: wsId })); if (!workspace) throw new Error(`Workspace not found.`); for (const env of Object.keys(data.deployEnvironment)) { const deployEnvironment = data.deployEnvironment[env]; if (deployEnvironment) { // TODO: Check quota based on CPU & memory (NEW) // if (Config.NODE_ENV === "production") { // const { size } = deployEnvironment; // if (size) { // const quotaRes = await checkQuota(workspace, { resourceSize: size }); // if (!quotaRes.status) throw new Error(quotaRes.messages.join(". ")); // if (quotaRes.data && quotaRes.data.isExceed) { // throw new Error(`You've exceeded the limit amount of container size.`); // } // } // } // IMPORTANT: Only update specific paths for (const key of Object.keys(deployEnvironment)) { data[`deployEnvironment.${env}.${key}`] = deployEnvironment[key]; } } } // ! CAUTION: Delete deployEnvironment before updating delete data.deployEnvironment; } // if (options?.isDebugging) console.log("AppService > update > data :>>", data); apps = await super.update(filter, data, options); // if (options?.isDebugging) console.log("AppService > update > [UPDATED] apps :>>", apps); return apps; } async updateOne(filter, data, options) { const [app] = await this.update(filter, data, options); return app; } async delete(filter, options) { // permissions await (0, user_utils_1.checkProjectAndAppPermissions)(this, filter, this.user); const app = await this.findOne(filter, { populate: ["project"] }); if (!app) throw new Error(`App not found.`); const { DeployEnvironmentService } = await Promise.resolve().then(() => __importStar(require("../services"))); const deployEnvSvc = new DeployEnvironmentService(); if (app.deployEnvironment) { // take down all deploy environments of this app Object.entries(app.deployEnvironment).map(async ([env, deployEnvironment]) => { // take down environment if (!(0, lodash_1.isEmpty)(deployEnvironment)) { await deployEnvSvc.takeDownDeployEnvironment(app, env).catch((e) => { console.error(`AppService > delete() > takeDownDeployEnvironment() :>>`, e); }); } // delete diginext domain record (if any) if (deployEnvironment.domains && deployEnvironment.domains.filter((domain) => domain.indexOf(const_1.DIGINEXT_DOMAIN) > -1).length > 0) { if (this.workspace && this.workspace.dx_key) { for (const domain of deployEnvironment.domains.filter((_domain) => _domain.indexOf(const_1.DIGINEXT_DOMAIN) > -1)) { const recordName = domain.replace(const_1.DIGINEXT_DOMAIN, ""); (0, dx_domain_1.dxDeleteDomainRecord)({ name: recordName, type: "A" }, this.workspace.dx_key).catch(console.error); } } else { console.error("AppService > delete() > Delete domain A record > No WORKSPACE or DX_KEY found."); } } }); } // remove this app ID from project.apps const { ProjectService } = await Promise.resolve().then(() => __importStar(require("../services"))); const project = await new ProjectService().updateOne({ _id: app.project._id, }, { $pull: { apps: app._id, appSlugs: app.slug }, }, { raw: true }); return super.delete(filter, options); } async softDelete(filter, options) { // permissions await (0, user_utils_1.checkProjectAndAppPermissions)(this, filter, this.user); const app = await this.findOne(filter, options); if (!app) throw new Error(`Unable to delete: App not found.`); // take down all deploy environments of this app try { await this.takeDown(app, options); } catch (e) { // ignore on error console.error(e); } return super.softDelete(filter, options); } async deleteGitRepo(filter, options) { const app = await this.findOne(filter, { populate: ["gitProvider"] }); if (!app) throw new Error(`Unable to delete: App not found.`); // permissions (0, user_utils_1.checkAppPermissions)(app, this.user); const provider = app.gitProvider; const repoData = await (0, git_utils_1.parseGitRepoDataFromRepoSSH)(app.git.repoSSH); if (!repoData) throw new Error(`Unable to read repo data of "${app.slug}" app: ${app.git.repoSSH}`); // delete git repo via API const { GitProviderService } = await Promise.resolve().then(() => __importStar(require("./index"))); const gitSvc = new GitProviderService(this.ownership); return gitSvc.deleteGitRepository(provider, repoData.repoSlug, options); } async takeDown(app, options) { // validate if (!app.deployEnvironment) throw new Error(`Unable to take down "${app.slug}" app, no deploy environtments found.`); // initialize const { DeployEnvironmentService } = await Promise.resolve().then(() => __importStar(require("../services"))); const deployEnvSvc = new DeployEnvironmentService(); // permissions (0, user_utils_1.checkAppPermissions)(app, this.user); // take down all deploy environments const deployEnvs = Object.keys(app.deployEnvironment); console.log("AppSvc.takeDown() > deployEnvs :>> ", deployEnvs); await Promise.all(deployEnvs.map((env) => deployEnvSvc.takeDownDeployEnvironment(app, env, options))); return app; } async archiveApp(app, ownership) { const { DeployEnvironmentService } = await Promise.resolve().then(() => __importStar(require("../services"))); const deployEnvSvc = new DeployEnvironmentService(); // permissions (0, user_utils_1.checkAppPermissions)(app, this.user); // take down all deploy environments const deployEnvs = Object.keys(app.deployEnvironment); await Promise.all(deployEnvs.map((env) => deployEnvSvc.takeDownDeployEnvironment(app, env))); // update database const archivedApp = await this.updateOne({ _id: app._id }, { archivedAt: new Date() }); return archivedApp; } async unarchiveApp(app, ownership) { // permissions (0, user_utils_1.checkAppPermissions)(app, this.user); // update database const unarchivedApp = await this.updateOne({ _id: app._id }, { $unset: { archivedAt: true } }, { raw: true }); return unarchivedApp; } /** * Get all users that participated in this app. */ async getParticipants(app, options) { const { BuildService, UserService } = await Promise.resolve().then(() => __importStar(require("./index"))); const buildSvc = new BuildService(); const userSvc = new UserService(); buildSvc.ownership = this.ownership; const listOwners = await buildSvc.distinct("owner", { app: app._id }); const ids = listOwners.map((item) => item.owner); return userSvc.find({ _id: { $in: ids } }, { select: mask_sensitive_info_1.basicUserFields }, options); } } exports.AppService = AppService;