import debug from 'debug'; import { customAlphabet } from 'nanoid'; import { lowercase, numbers } from 'nanoid-dictionary'; import { Database } from './database'; import { Deployment, Environment } from './entity/Deployment'; import { Domain } from './entity/Domain'; import { EnvironmentVariable } from './entity/EnvironmentVariable'; import { Organization } from './entity/Organization'; import { Project } from './entity/Project'; import { Permission, ProjectMember } from './entity/ProjectMember'; import { User } from './entity/User'; import { DeepPartial } from 'typeorm'; const log = debug('snowball:service'); const nanoid = customAlphabet(lowercase + numbers, 8); export class Service { private db: Database; constructor (db: Database) { this.db = db; } async getUser (userId: number): Promise { return this.db.getUser({ where: { id: userId } }); } async getOrganizationsByUserId (userId: number): Promise { const dbOrganizations = await this.db.getOrganizationsByUserId(userId); return dbOrganizations; } async getProjectById (projectId: string): Promise { const dbProject = await this.db.getProjectById(projectId); return dbProject; } async getProjectsInOrganization (userId:string, organizationId: string): Promise { const dbProjects = await this.db.getProjectsInOrganization(userId, organizationId); return dbProjects; } async getDeployementsByProjectId (projectId: string): Promise { const dbDeployments = await this.db.getDeploymentsByProjectId(projectId); return dbDeployments; } async getEnvironmentVariablesByProjectId (projectId: string): Promise { const dbEnvironmentVariables = await this.db.getEnvironmentVariablesByProjectId(projectId); return dbEnvironmentVariables; } async getProjectMembersByProjectId (projectId: string): Promise { const dbProjectMembers = await this.db.getProjectMembersByProjectId(projectId); return dbProjectMembers; } async searchProjects (userId:string, searchText: string): Promise { const dbProjects = await this.db.getProjectsBySearchText(Number(userId), searchText); return dbProjects; } async getDomainsByProjectId (projectId: string): Promise { const dbDomains = await this.db.getDomainsByProjectId(projectId); return dbDomains; } async updateProjectMember (projectMemberId: string, data: {permissions: Permission[]}): Promise { try { return await this.db.updateProjectMemberById(projectMemberId, data); } catch (err) { log(err); return false; } } async addProjectMember (projectId: string, data: { email: string, permissions: Permission[] }): Promise { try { // TODO: Send invitation let user = await this.db.getUser({ where: { email: data.email } }); if (!user) { user = await this.db.createUser({ email: data.email }); } const newProjectMember = await this.db.addProjectMember({ project: { id: projectId }, permissions: data.permissions, isPending: true, member: { id: user.id } }); return Boolean(newProjectMember); } catch (err) { log(err); return false; } } async addEnvironmentVariables (projectId: string, data: { environments: string[], key: string, value: string}[]): Promise { try { const formattedEnvironmentVariables = data.map((environmentVariable) => { return environmentVariable.environments.map((environment) => { return ({ key: environmentVariable.key, value: environmentVariable.value, environment: environment as Environment, project: Object.assign(new Project(), { id: projectId }) }); }); }).flat(); const savedEnvironmentVariables = await this.db.addEnvironmentVariables(formattedEnvironmentVariables); return savedEnvironmentVariables.length > 0; } catch (err) { log(err); return false; } } async updateEnvironmentVariable (environmentVariableId: string, data : DeepPartial): Promise { try { return await this.db.updateEnvironmentVariable(environmentVariableId, data); } catch (err) { log(err); return false; } } async removeEnvironmentVariable (environmentVariableId: string): Promise { try { return await this.db.deleteEnvironmentVariable(environmentVariableId); } catch (err) { log(err); return false; } } async updateDeploymentToProd (deploymentId: string): Promise { try { return await this.db.updateDeploymentById(deploymentId, { environment: Environment.Production }); } catch (err) { log(err); return false; } } async addProject (userId: string, data: DeepPartial): Promise { try { await this.db.addProject(userId, data); return true; } catch (err) { log(err); return false; } } async updateProject (projectId: string, data: DeepPartial): Promise { try { return await this.db.updateProjectById(projectId, data); } catch (err) { log(err); return false; } } async deleteProject (projectId: string): Promise { try { return await this.db.deleteProjectById(projectId); } catch (err) { log(err); return false; } } async deleteDomain (domainId: string): Promise { try { const domainsRedirectedFrom = await this.db.getDomains({ where: { redirectToId: Number(domainId) } }); if (domainsRedirectedFrom.length > 0) { throw new Error('Cannot delete domain since it has redirects from other domains'); } return await this.db.deleteDomainById(domainId); } catch (err) { log(err); return false; } } async redeployToProd (userId: string, deploymentId: string): Promise { try { const deployment = await this.db.getDeployment({ relations: { project: true, domain: true, createdBy: true }, where: { id: deploymentId } }); if (deployment === null) { throw new Error('Deployment not found'); } const { createdAt, updatedAt, ...updatedDeployment } = deployment; if (updatedDeployment.environment === Environment.Production) { // TODO: Put isCurrent field in project updatedDeployment.isCurrent = true; updatedDeployment.createdBy = Object.assign(new User(), { id: Number(userId) }); } updatedDeployment.id = nanoid(); updatedDeployment.url = `${updatedDeployment.id}-${updatedDeployment.project.subDomain}`; const oldDeployment = await this.db.updateDeploymentById(deploymentId, { domain: null, isCurrent: false }); const newDeployement = await this.db.createDeployement(updatedDeployment); return oldDeployment && Boolean(newDeployement); } catch (err) { log(err); return false; } } }