Show domains for change to production and redeploy in deployments page (#56)
* Display URL for change to production dialog box * Refactor database method for domains to service class * Handle error in resolver instead of service class * Return entity from service class for add operation * Do not fetch branches if repo not available --------- Co-authored-by: neeraj <neeraj.rtly@gmail.com>
This commit is contained in:
parent
7e522aa45d
commit
afd522654c
@ -1,4 +1,4 @@
|
|||||||
import { DataSource, DeepPartial, FindManyOptions, FindOneOptions } from 'typeorm';
|
import { DataSource, DeepPartial, FindManyOptions, FindOneOptions, FindOptionsWhere } from 'typeorm';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import debug from 'debug';
|
import debug from 'debug';
|
||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
@ -41,7 +41,7 @@ export class Database {
|
|||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
async createUser (data: DeepPartial<User>): Promise<User> {
|
async addUser (data: DeepPartial<User>): Promise<User> {
|
||||||
const userRepository = this.dataSource.getRepository(User);
|
const userRepository = this.dataSource.getRepository(User);
|
||||||
const user = await userRepository.save(data);
|
const user = await userRepository.save(data);
|
||||||
|
|
||||||
@ -161,7 +161,7 @@ export class Database {
|
|||||||
return domains;
|
return domains;
|
||||||
}
|
}
|
||||||
|
|
||||||
async createDeployement (data: DeepPartial<Deployment>): Promise<Deployment> {
|
async addDeployement (data: DeepPartial<Deployment>): Promise<Deployment> {
|
||||||
const deploymentRepository = this.dataSource.getRepository(Deployment);
|
const deploymentRepository = this.dataSource.getRepository(Deployment);
|
||||||
const deployment = await deploymentRepository.save(data);
|
const deployment = await deploymentRepository.save(data);
|
||||||
|
|
||||||
@ -216,11 +216,7 @@ export class Database {
|
|||||||
const projectMemberRepository = this.dataSource.getRepository(ProjectMember);
|
const projectMemberRepository = this.dataSource.getRepository(ProjectMember);
|
||||||
const updateResult = await projectMemberRepository.update({ id: Number(projectMemberId) }, data);
|
const updateResult = await projectMemberRepository.update({ id: Number(projectMemberId) }, data);
|
||||||
|
|
||||||
if (updateResult.affected) {
|
return Boolean(updateResult.affected);
|
||||||
return updateResult.affected > 0;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async addProjectMember (data: DeepPartial<ProjectMember>): Promise<ProjectMember> {
|
async addProjectMember (data: DeepPartial<ProjectMember>): Promise<ProjectMember> {
|
||||||
@ -241,11 +237,7 @@ export class Database {
|
|||||||
const environmentVariableRepository = this.dataSource.getRepository(EnvironmentVariable);
|
const environmentVariableRepository = this.dataSource.getRepository(EnvironmentVariable);
|
||||||
const updateResult = await environmentVariableRepository.update({ id: Number(environmentVariableId) }, update);
|
const updateResult = await environmentVariableRepository.update({ id: Number(environmentVariableId) }, update);
|
||||||
|
|
||||||
if (updateResult.affected) {
|
return Boolean(updateResult.affected);
|
||||||
return updateResult.affected > 0;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteEnvironmentVariable (environmentVariableId: string): Promise<boolean> {
|
async deleteEnvironmentVariable (environmentVariableId: string): Promise<boolean> {
|
||||||
@ -302,11 +294,7 @@ export class Database {
|
|||||||
const deploymentRepository = this.dataSource.getRepository(Deployment);
|
const deploymentRepository = this.dataSource.getRepository(Deployment);
|
||||||
const updateResult = await deploymentRepository.update({ id: deploymentId }, updates);
|
const updateResult = await deploymentRepository.update({ id: deploymentId }, updates);
|
||||||
|
|
||||||
if (updateResult.affected) {
|
return Boolean(updateResult.affected);
|
||||||
return updateResult.affected > 0;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async addProject (userId: string, projectDetails: DeepPartial<Project>): Promise<Project> {
|
async addProject (userId: string, projectDetails: DeepPartial<Project>): Promise<Project> {
|
||||||
@ -336,11 +324,7 @@ export class Database {
|
|||||||
const projectRepository = this.dataSource.getRepository(Project);
|
const projectRepository = this.dataSource.getRepository(Project);
|
||||||
const updateResult = await projectRepository.update({ id: projectId }, updates);
|
const updateResult = await projectRepository.update({ id: projectId }, updates);
|
||||||
|
|
||||||
if (updateResult.affected) {
|
return Boolean(updateResult.affected);
|
||||||
return updateResult.affected > 0;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteProjectById (projectId: string): Promise<boolean> {
|
async deleteProjectById (projectId: string): Promise<boolean> {
|
||||||
@ -371,70 +355,28 @@ export class Database {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async rollbackDeploymentById (projectId: string, deploymentId: string): Promise<boolean> {
|
async addDomain (data: DeepPartial<Domain>): Promise<Domain> {
|
||||||
const deploymentRepository = this.dataSource.getRepository(Deployment);
|
|
||||||
|
|
||||||
// TODO: Implement transactions
|
|
||||||
const oldCurrentDeployment = await deploymentRepository.findOne({
|
|
||||||
relations: {
|
|
||||||
domain: true
|
|
||||||
},
|
|
||||||
where: {
|
|
||||||
project: {
|
|
||||||
id: projectId
|
|
||||||
},
|
|
||||||
isCurrent: true
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const oldCurrentDeploymentUpdate = await deploymentRepository.update({ project: { id: projectId }, isCurrent: true }, { isCurrent: false, domain: null });
|
|
||||||
|
|
||||||
const newCurrentDeploymentUpdate = await deploymentRepository.update({ id: deploymentId }, { isCurrent: true, domain: oldCurrentDeployment?.domain });
|
|
||||||
|
|
||||||
if (oldCurrentDeploymentUpdate.affected && newCurrentDeploymentUpdate.affected) {
|
|
||||||
return oldCurrentDeploymentUpdate.affected > 0 && newCurrentDeploymentUpdate.affected > 0;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async addDomainByProjectId (projectId: string, domainDetails: { name: string }): Promise<Domain[]> {
|
|
||||||
const domainRepository = this.dataSource.getRepository(Domain);
|
const domainRepository = this.dataSource.getRepository(Domain);
|
||||||
const projectRepository = this.dataSource.getRepository(Project);
|
const newDomain = await domainRepository.save(data);
|
||||||
|
|
||||||
const currentProject = await projectRepository.findOneBy({
|
return newDomain;
|
||||||
id: projectId
|
|
||||||
});
|
|
||||||
|
|
||||||
if (currentProject === null) {
|
|
||||||
throw new Error(`Project with ${projectId} not found`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const primaryDomainDetails = {
|
async getDomain (options: FindOneOptions<Domain>): Promise<Domain | null> {
|
||||||
...domainDetails,
|
const domainRepository = this.dataSource.getRepository(Domain);
|
||||||
branch: currentProject.prodBranch,
|
const domain = await domainRepository.findOne(options);
|
||||||
project: currentProject
|
|
||||||
};
|
|
||||||
|
|
||||||
const primaryDomain = domainRepository.create(primaryDomainDetails as DeepPartial<Domain>);
|
return domain;
|
||||||
const savedPrimaryDomain = await domainRepository.save(primaryDomain);
|
|
||||||
|
|
||||||
const domainArr = domainDetails.name.split('www.');
|
|
||||||
|
|
||||||
const redirectedDomainDetails = {
|
|
||||||
name: domainArr.length > 1 ? domainArr[1] : `www.${domainArr[0]}`,
|
|
||||||
branch: currentProject.prodBranch,
|
|
||||||
project: currentProject,
|
|
||||||
redirectTo: savedPrimaryDomain
|
|
||||||
};
|
|
||||||
|
|
||||||
const redirectedDomain = domainRepository.create(redirectedDomainDetails as DeepPartial<Domain>);
|
|
||||||
const savedRedirectedDomain = await domainRepository.save(redirectedDomain);
|
|
||||||
|
|
||||||
return [savedPrimaryDomain, savedRedirectedDomain];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getDomainsByProjectId (projectId: string): Promise<Domain[]> {
|
async updateDomainById (domainId: string, updates: DeepPartial<Domain>): Promise<boolean> {
|
||||||
|
const domainRepository = this.dataSource.getRepository(Domain);
|
||||||
|
const updateResult = await domainRepository.update({ id: Number(domainId) }, updates);
|
||||||
|
|
||||||
|
return Boolean(updateResult.affected);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getDomainsByProjectId (projectId: string, filter?: FindOptionsWhere<Domain>): Promise<Domain[]> {
|
||||||
const domainRepository = this.dataSource.getRepository(Domain);
|
const domainRepository = this.dataSource.getRepository(Domain);
|
||||||
|
|
||||||
const domains = await domainRepository.find({
|
const domains = await domainRepository.find({
|
||||||
@ -444,68 +386,11 @@ export class Database {
|
|||||||
where: {
|
where: {
|
||||||
project: {
|
project: {
|
||||||
id: projectId
|
id: projectId
|
||||||
}
|
},
|
||||||
|
...filter
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return domains;
|
return domains;
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateDomainById (domainId: string, data: DeepPartial<Domain>): Promise<boolean> {
|
|
||||||
const domainRepository = this.dataSource.getRepository(Domain);
|
|
||||||
|
|
||||||
const domain = await domainRepository.findOne({
|
|
||||||
where: {
|
|
||||||
id: Number(domainId)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const newDomain: DeepPartial<Domain> = {
|
|
||||||
...data
|
|
||||||
};
|
|
||||||
|
|
||||||
if (domain === null) {
|
|
||||||
throw new Error(`Error finding domain with id ${domainId}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const domainsRedirectedFrom = await domainRepository.find({
|
|
||||||
where: {
|
|
||||||
project: {
|
|
||||||
id: domain.projectId
|
|
||||||
},
|
|
||||||
redirectToId: domain.id
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// If there are domains redirecting to current domain, only branch of current domain can be updated
|
|
||||||
if (domainsRedirectedFrom.length > 0 && data.branch === domain.branch) {
|
|
||||||
throw new Error('Remove all redirects to this domain before updating');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.redirectToId) {
|
|
||||||
const redirectedDomain = await domainRepository.findOne({
|
|
||||||
where: {
|
|
||||||
id: Number(data.redirectToId)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (redirectedDomain === null) {
|
|
||||||
throw new Error('Could not find Domain to redirect to');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (redirectedDomain.redirectToId) {
|
|
||||||
throw new Error('Unable to redirect to the domain because it is already redirecting elsewhere. Redirects cannot be chained.');
|
|
||||||
}
|
|
||||||
|
|
||||||
newDomain.redirectTo = redirectedDomain;
|
|
||||||
}
|
|
||||||
|
|
||||||
const updateResult = await domainRepository.update({ id: Number(domainId) }, newDomain);
|
|
||||||
|
|
||||||
if (updateResult.affected) {
|
|
||||||
return updateResult.affected > 0;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -55,5 +55,5 @@ export class Domain {
|
|||||||
updatedAt!: Date;
|
updatedAt!: Date;
|
||||||
|
|
||||||
@DeleteDateColumn()
|
@DeleteDateColumn()
|
||||||
deletedAt?: Date;
|
deletedAt!: Date | null;
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,7 @@ export class Project {
|
|||||||
updatedAt!: Date;
|
updatedAt!: Date;
|
||||||
|
|
||||||
@DeleteDateColumn()
|
@DeleteDateColumn()
|
||||||
deletedAt?: Date;
|
deletedAt!: Date | null;
|
||||||
|
|
||||||
@OneToMany(() => Deployment, (deployment) => deployment.project)
|
@OneToMany(() => Deployment, (deployment) => deployment.project)
|
||||||
deployments!: Deployment[];
|
deployments!: Deployment[];
|
||||||
|
@ -47,5 +47,5 @@ export class ProjectMember {
|
|||||||
updatedAt!: Date;
|
updatedAt!: Date;
|
||||||
|
|
||||||
@DeleteDateColumn()
|
@DeleteDateColumn()
|
||||||
deletedAt?: Date;
|
deletedAt!: Date | null;
|
||||||
}
|
}
|
||||||
|
@ -43,5 +43,5 @@ export class UserOrganization {
|
|||||||
updatedAt!: Date;
|
updatedAt!: Date;
|
||||||
|
|
||||||
@DeleteDateColumn()
|
@DeleteDateColumn()
|
||||||
deletedAt?: Date;
|
deletedAt!: Date | null;
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
import debug from 'debug';
|
import debug from 'debug';
|
||||||
import assert from 'assert';
|
import { DeepPartial, FindOptionsWhere } from 'typeorm';
|
||||||
import { DeepPartial } from 'typeorm';
|
|
||||||
|
|
||||||
import { OAuthApp } from '@octokit/oauth-app';
|
import { OAuthApp } from '@octokit/oauth-app';
|
||||||
|
|
||||||
import { Service } from './service';
|
import { Service } from './service';
|
||||||
import { Database } from './database';
|
import { Database } from './database';
|
||||||
import { isUserOwner } from './utils';
|
|
||||||
import { Permission } from './entity/ProjectMember';
|
import { Permission } from './entity/ProjectMember';
|
||||||
import { Domain } from './entity/Domain';
|
import { Domain } from './entity/Domain';
|
||||||
import { Project } from './entity/Project';
|
import { Project } from './entity/Project';
|
||||||
@ -51,8 +49,8 @@ export const createResolvers = async (db: Database, app: OAuthApp, service: Serv
|
|||||||
return service.searchProjects(context.userId, searchText);
|
return service.searchProjects(context.userId, searchText);
|
||||||
},
|
},
|
||||||
|
|
||||||
domains: async (_:any, { projectId }: { projectId: string }) => {
|
domains: async (_:any, { projectId, filter }: { projectId: string, filter?: FindOptionsWhere<Domain> }) => {
|
||||||
return service.getDomainsByProjectId(projectId);
|
return service.getDomainsByProjectId(projectId, filter);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -60,20 +58,7 @@ export const createResolvers = async (db: Database, app: OAuthApp, service: Serv
|
|||||||
Mutation: {
|
Mutation: {
|
||||||
removeProjectMember: async (_: any, { projectMemberId }: { projectMemberId: string }, context: any) => {
|
removeProjectMember: async (_: any, { projectMemberId }: { projectMemberId: string }, context: any) => {
|
||||||
try {
|
try {
|
||||||
const member = await db.getProjectMemberById(projectMemberId);
|
return await service.removeProjectMember(context.userId, projectMemberId);
|
||||||
|
|
||||||
if (member.member.id === context.userId) {
|
|
||||||
throw new Error('Invalid operation: cannot remove self');
|
|
||||||
}
|
|
||||||
|
|
||||||
const memberProject = member.project;
|
|
||||||
assert(memberProject);
|
|
||||||
|
|
||||||
if (isUserOwner(String(context.userId), String(memberProject.owner.id))) {
|
|
||||||
return await db.removeProjectMemberById(projectMemberId);
|
|
||||||
} else {
|
|
||||||
throw new Error('Invalid operation: not authorized');
|
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log(err);
|
log(err);
|
||||||
return false;
|
return false;
|
||||||
@ -86,7 +71,12 @@ export const createResolvers = async (db: Database, app: OAuthApp, service: Serv
|
|||||||
permissions: Permission[]
|
permissions: Permission[]
|
||||||
}
|
}
|
||||||
}) => {
|
}) => {
|
||||||
return service.updateProjectMember(projectMemberId, data);
|
try {
|
||||||
|
return await service.updateProjectMember(projectMemberId, data);
|
||||||
|
} catch (err) {
|
||||||
|
log(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
addProjectMember: async (_: any, { projectId, data }: {
|
addProjectMember: async (_: any, { projectId, data }: {
|
||||||
@ -96,48 +86,97 @@ export const createResolvers = async (db: Database, app: OAuthApp, service: Serv
|
|||||||
permissions: Permission[]
|
permissions: Permission[]
|
||||||
}
|
}
|
||||||
}) => {
|
}) => {
|
||||||
return service.addProjectMember(projectId, data);
|
try {
|
||||||
|
return Boolean(await service.addProjectMember(projectId, data));
|
||||||
|
} catch (err) {
|
||||||
|
log(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
addEnvironmentVariables: async (_: any, { projectId, data }: { projectId: string, data: { environments: string[], key: string, value: string}[] }) => {
|
addEnvironmentVariables: async (_: any, { projectId, data }: { projectId: string, data: { environments: string[], key: string, value: string}[] }) => {
|
||||||
return service.addEnvironmentVariables(projectId, data);
|
try {
|
||||||
|
return Boolean(await service.addEnvironmentVariables(projectId, data));
|
||||||
|
} catch (err) {
|
||||||
|
log(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
updateEnvironmentVariable: async (_: any, { environmentVariableId, data }: { environmentVariableId: string, data : DeepPartial<EnvironmentVariable>}) => {
|
updateEnvironmentVariable: async (_: any, { environmentVariableId, data }: { environmentVariableId: string, data : DeepPartial<EnvironmentVariable>}) => {
|
||||||
return service.updateEnvironmentVariable(environmentVariableId, data);
|
try {
|
||||||
|
return await service.updateEnvironmentVariable(environmentVariableId, data);
|
||||||
|
} catch (err) {
|
||||||
|
log(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
removeEnvironmentVariable: async (_: any, { environmentVariableId }: { environmentVariableId: string}) => {
|
removeEnvironmentVariable: async (_: any, { environmentVariableId }: { environmentVariableId: string}) => {
|
||||||
return service.removeEnvironmentVariable(environmentVariableId);
|
try {
|
||||||
|
return await service.removeEnvironmentVariable(environmentVariableId);
|
||||||
|
} catch (err) {
|
||||||
|
log(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
updateDeploymentToProd: async (_: any, { deploymentId }: { deploymentId: string }) => {
|
updateDeploymentToProd: async (_: any, { deploymentId }: { deploymentId: string }) => {
|
||||||
return service.updateDeploymentToProd(deploymentId);
|
try {
|
||||||
|
return await service.updateDeploymentToProd(deploymentId);
|
||||||
|
} catch (err) {
|
||||||
|
log(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
addProject: async (_: any, { data }: { data: DeepPartial<Project> }, context: any) => {
|
addProject: async (_: any, { data }: { data: DeepPartial<Project> }, context: any) => {
|
||||||
return service.addProject(context.userId, data);
|
try {
|
||||||
|
return Boolean(await service.addProject(context.userId, data));
|
||||||
|
} catch (err) {
|
||||||
|
log(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
updateProject: async (_: any, { projectId, projectDetails }: { projectId: string, projectDetails: DeepPartial<Project> }) => {
|
updateProject: async (_: any, { projectId, projectDetails }: { projectId: string, projectDetails: DeepPartial<Project> }) => {
|
||||||
return service.updateProject(projectId, projectDetails);
|
try {
|
||||||
|
return await service.updateProject(projectId, projectDetails);
|
||||||
|
} catch (err) {
|
||||||
|
log(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
redeployToProd: async (_: any, { deploymentId }: { deploymentId: string }, context: any) => {
|
redeployToProd: async (_: any, { deploymentId }: { deploymentId: string }, context: any) => {
|
||||||
return service.redeployToProd(context.userId, deploymentId);
|
try {
|
||||||
|
return await service.redeployToProd(context.userId, deploymentId);
|
||||||
|
} catch (err) {
|
||||||
|
log(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
deleteProject: async (_: any, { projectId }: { projectId: string }) => {
|
deleteProject: async (_: any, { projectId }: { projectId: string }) => {
|
||||||
return service.deleteProject(projectId);
|
try {
|
||||||
|
return await service.deleteProject(projectId);
|
||||||
|
} catch (err) {
|
||||||
|
log(err); return false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
deleteDomain: async (_: any, { domainId }: { domainId: string }) => {
|
deleteDomain: async (_: any, { domainId }: { domainId: string }) => {
|
||||||
return service.deleteDomain(domainId);
|
try {
|
||||||
|
return await service.deleteDomain(domainId);
|
||||||
|
} catch (err) {
|
||||||
|
log(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
rollbackDeployment: async (_: any, { projectId, deploymentId }: {deploymentId: string, projectId: string }) => {
|
rollbackDeployment: async (_: any, { projectId, deploymentId }: {deploymentId: string, projectId: string }) => {
|
||||||
try {
|
try {
|
||||||
return await db.rollbackDeploymentById(projectId, deploymentId);
|
return await service.rollbackDeployment(projectId, deploymentId);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log(err);
|
log(err);
|
||||||
return false;
|
return false;
|
||||||
@ -146,7 +185,7 @@ export const createResolvers = async (db: Database, app: OAuthApp, service: Serv
|
|||||||
|
|
||||||
addDomain: async (_: any, { projectId, domainDetails }: { projectId: string, domainDetails: { name: string } }) => {
|
addDomain: async (_: any, { projectId, domainDetails }: { projectId: string, domainDetails: { name: string } }) => {
|
||||||
try {
|
try {
|
||||||
return await db.addDomainByProjectId(projectId, domainDetails);
|
return Boolean(await service.addDomain(projectId, domainDetails));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log(err);
|
log(err);
|
||||||
return false;
|
return false;
|
||||||
@ -155,7 +194,7 @@ export const createResolvers = async (db: Database, app: OAuthApp, service: Serv
|
|||||||
|
|
||||||
updateDomain: async (_: any, { domainId, domainDetails }: { domainId: string, domainDetails: DeepPartial<Domain>}) => {
|
updateDomain: async (_: any, { domainId, domainDetails }: { domainId: string, domainDetails: DeepPartial<Domain>}) => {
|
||||||
try {
|
try {
|
||||||
return await db.updateDomainById(domainId, domainDetails);
|
return await service.updateDomain(domainId, domainDetails);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log(err);
|
log(err);
|
||||||
return false;
|
return false;
|
||||||
|
@ -167,6 +167,10 @@ input UpdateProjectMemberInput {
|
|||||||
permissions: [Permission]
|
permissions: [Permission]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input FilterDomainsInput {
|
||||||
|
branch: String
|
||||||
|
}
|
||||||
|
|
||||||
type Query {
|
type Query {
|
||||||
user: User!
|
user: User!
|
||||||
organizations: [Organization!]
|
organizations: [Organization!]
|
||||||
@ -177,7 +181,7 @@ type Query {
|
|||||||
environmentVariables(projectId: String!): [EnvironmentVariable!]
|
environmentVariables(projectId: String!): [EnvironmentVariable!]
|
||||||
projectMembers(projectId: String!): [ProjectMember!]
|
projectMembers(projectId: String!): [ProjectMember!]
|
||||||
searchProjects(searchText: String!): [Project!]
|
searchProjects(searchText: String!): [Project!]
|
||||||
domains(projectId: String!): [Domain!]
|
domains(projectId: String!, filter: FilterDomainsInput): [Domain]
|
||||||
}
|
}
|
||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import debug from 'debug';
|
import assert from 'assert';
|
||||||
import { customAlphabet } from 'nanoid';
|
import { customAlphabet } from 'nanoid';
|
||||||
import { lowercase, numbers } from 'nanoid-dictionary';
|
import { lowercase, numbers } from 'nanoid-dictionary';
|
||||||
|
import { DeepPartial, FindOptionsWhere } from 'typeorm';
|
||||||
|
|
||||||
import { Database } from './database';
|
import { Database } from './database';
|
||||||
import { Deployment, Environment } from './entity/Deployment';
|
import { Deployment, Environment } from './entity/Deployment';
|
||||||
@ -10,9 +11,7 @@ import { Organization } from './entity/Organization';
|
|||||||
import { Project } from './entity/Project';
|
import { Project } from './entity/Project';
|
||||||
import { Permission, ProjectMember } from './entity/ProjectMember';
|
import { Permission, ProjectMember } from './entity/ProjectMember';
|
||||||
import { User } from './entity/User';
|
import { User } from './entity/User';
|
||||||
import { DeepPartial } from 'typeorm';
|
import { isUserOwner } from './utils';
|
||||||
|
|
||||||
const log = debug('snowball:service');
|
|
||||||
|
|
||||||
const nanoid = customAlphabet(lowercase + numbers, 8);
|
const nanoid = customAlphabet(lowercase + numbers, 8);
|
||||||
|
|
||||||
@ -66,26 +65,20 @@ export class Service {
|
|||||||
return dbProjects;
|
return dbProjects;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getDomainsByProjectId (projectId: string): Promise<Domain[]> {
|
async getDomainsByProjectId (projectId: string, filter?: FindOptionsWhere<Domain>): Promise<Domain[]> {
|
||||||
const dbDomains = await this.db.getDomainsByProjectId(projectId);
|
const dbDomains = await this.db.getDomainsByProjectId(projectId, filter);
|
||||||
return dbDomains;
|
return dbDomains;
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateProjectMember (projectMemberId: string, data: {permissions: Permission[]}): Promise<boolean> {
|
async updateProjectMember (projectMemberId: string, data: {permissions: Permission[]}): Promise<boolean> {
|
||||||
try {
|
return this.db.updateProjectMemberById(projectMemberId, data);
|
||||||
return await this.db.updateProjectMemberById(projectMemberId, data);
|
|
||||||
} catch (err) {
|
|
||||||
log(err);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async addProjectMember (projectId: string,
|
async addProjectMember (projectId: string,
|
||||||
data: {
|
data: {
|
||||||
email: string,
|
email: string,
|
||||||
permissions: Permission[]
|
permissions: Permission[]
|
||||||
}): Promise<boolean> {
|
}): Promise<ProjectMember> {
|
||||||
try {
|
|
||||||
// TODO: Send invitation
|
// TODO: Send invitation
|
||||||
let user = await this.db.getUser({
|
let user = await this.db.getUser({
|
||||||
where: {
|
where: {
|
||||||
@ -94,7 +87,7 @@ export class Service {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
user = await this.db.createUser({
|
user = await this.db.addUser({
|
||||||
email: data.email
|
email: data.email
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -110,15 +103,27 @@ export class Service {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return Boolean(newProjectMember);
|
return newProjectMember;
|
||||||
} catch (err) {
|
}
|
||||||
log(err);
|
|
||||||
return false;
|
async removeProjectMember (userId: string, projectMemberId: string): Promise<boolean> {
|
||||||
|
const member = await this.db.getProjectMemberById(projectMemberId);
|
||||||
|
|
||||||
|
if (String(member.member.id) === userId) {
|
||||||
|
throw new Error('Invalid operation: cannot remove self');
|
||||||
|
}
|
||||||
|
|
||||||
|
const memberProject = member.project;
|
||||||
|
assert(memberProject);
|
||||||
|
|
||||||
|
if (isUserOwner(String(userId), String(memberProject.owner.id))) {
|
||||||
|
return this.db.removeProjectMemberById(projectMemberId);
|
||||||
|
} else {
|
||||||
|
throw new Error('Invalid operation: not authorized');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async addEnvironmentVariables (projectId: string, data: { environments: string[], key: string, value: string}[]): Promise<boolean> {
|
async addEnvironmentVariables (projectId: string, data: { environments: string[], key: string, value: string}[]): Promise<EnvironmentVariable[]> {
|
||||||
try {
|
|
||||||
const formattedEnvironmentVariables = data.map((environmentVariable) => {
|
const formattedEnvironmentVariables = data.map((environmentVariable) => {
|
||||||
return environmentVariable.environments.map((environment) => {
|
return environmentVariable.environments.map((environment) => {
|
||||||
return ({
|
return ({
|
||||||
@ -133,72 +138,63 @@ export class Service {
|
|||||||
}).flat();
|
}).flat();
|
||||||
|
|
||||||
const savedEnvironmentVariables = await this.db.addEnvironmentVariables(formattedEnvironmentVariables);
|
const savedEnvironmentVariables = await this.db.addEnvironmentVariables(formattedEnvironmentVariables);
|
||||||
return savedEnvironmentVariables.length > 0;
|
return savedEnvironmentVariables;
|
||||||
} catch (err) {
|
|
||||||
log(err);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateEnvironmentVariable (environmentVariableId: string, data : DeepPartial<EnvironmentVariable>): Promise<boolean> {
|
async updateEnvironmentVariable (environmentVariableId: string, data : DeepPartial<EnvironmentVariable>): Promise<boolean> {
|
||||||
try {
|
return this.db.updateEnvironmentVariable(environmentVariableId, data);
|
||||||
return await this.db.updateEnvironmentVariable(environmentVariableId, data);
|
|
||||||
} catch (err) {
|
|
||||||
log(err);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async removeEnvironmentVariable (environmentVariableId: string): Promise<boolean> {
|
async removeEnvironmentVariable (environmentVariableId: string): Promise<boolean> {
|
||||||
try {
|
return this.db.deleteEnvironmentVariable(environmentVariableId);
|
||||||
return await this.db.deleteEnvironmentVariable(environmentVariableId);
|
|
||||||
} catch (err) {
|
|
||||||
log(err);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateDeploymentToProd (deploymentId: string): Promise<boolean> {
|
async updateDeploymentToProd (deploymentId: string): Promise<boolean> {
|
||||||
try {
|
const deployment = await this.db.getDeployment({ where: { id: deploymentId }, relations: { project: true } });
|
||||||
return await this.db.updateDeploymentById(deploymentId, {
|
|
||||||
environment: Environment.Production
|
if (!deployment) {
|
||||||
});
|
throw new Error('Deployment does not exist');
|
||||||
} catch (err) {
|
|
||||||
log(err);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async addProject (userId: string, data: DeepPartial<Project>): Promise<boolean> {
|
const prodBranchDomains = await this.db.getDomainsByProjectId(deployment.project.id, { branch: deployment.project.prodBranch });
|
||||||
try {
|
|
||||||
await this.db.addProject(userId, data);
|
const oldDeployment = await this.db.getDeployment({
|
||||||
return true;
|
where: {
|
||||||
} catch (err) {
|
domain: {
|
||||||
log(err);
|
id: prodBranchDomains[0].id
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (oldDeployment) {
|
||||||
|
await this.db.updateDeploymentById(oldDeployment.id, {
|
||||||
|
domain: null,
|
||||||
|
isCurrent: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateResult = await this.db.updateDeploymentById(deploymentId, {
|
||||||
|
environment: Environment.Production,
|
||||||
|
domain: prodBranchDomains[0],
|
||||||
|
isCurrent: true
|
||||||
|
});
|
||||||
|
|
||||||
|
return updateResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
async addProject (userId: string, data: DeepPartial<Project>): Promise<Project> {
|
||||||
|
return this.db.addProject(userId, data);
|
||||||
|
}
|
||||||
|
|
||||||
async updateProject (projectId: string, data: DeepPartial<Project>): Promise<boolean> {
|
async updateProject (projectId: string, data: DeepPartial<Project>): Promise<boolean> {
|
||||||
try {
|
return this.db.updateProjectById(projectId, data);
|
||||||
return await this.db.updateProjectById(projectId, data);
|
|
||||||
} catch (err) {
|
|
||||||
log(err);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteProject (projectId: string): Promise<boolean> {
|
async deleteProject (projectId: string): Promise<boolean> {
|
||||||
try {
|
return this.db.deleteProjectById(projectId);
|
||||||
return await this.db.deleteProjectById(projectId);
|
|
||||||
} catch (err) {
|
|
||||||
log(err);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteDomain (domainId: string): Promise<boolean> {
|
async deleteDomain (domainId: string): Promise<boolean> {
|
||||||
try {
|
|
||||||
const domainsRedirectedFrom = await this.db.getDomains({
|
const domainsRedirectedFrom = await this.db.getDomains({
|
||||||
where: {
|
where: {
|
||||||
redirectToId: Number(domainId)
|
redirectToId: Number(domainId)
|
||||||
@ -209,15 +205,10 @@ export class Service {
|
|||||||
throw new Error('Cannot delete domain since it has redirects from other domains');
|
throw new Error('Cannot delete domain since it has redirects from other domains');
|
||||||
}
|
}
|
||||||
|
|
||||||
return await this.db.deleteDomainById(domainId);
|
return this.db.deleteDomainById(domainId);
|
||||||
} catch (err) {
|
|
||||||
log(err);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async redeployToProd (userId: string, deploymentId: string): Promise<Deployment| boolean> {
|
async redeployToProd (userId: string, deploymentId: string): Promise<Deployment| boolean> {
|
||||||
try {
|
|
||||||
const deployment = await this.db.getDeployment({
|
const deployment = await this.db.getDeployment({
|
||||||
relations: {
|
relations: {
|
||||||
project: true,
|
project: true,
|
||||||
@ -247,12 +238,117 @@ export class Service {
|
|||||||
updatedDeployment.url = `${updatedDeployment.id}-${updatedDeployment.project.subDomain}`;
|
updatedDeployment.url = `${updatedDeployment.id}-${updatedDeployment.project.subDomain}`;
|
||||||
|
|
||||||
const oldDeployment = await this.db.updateDeploymentById(deploymentId, { domain: null, isCurrent: false });
|
const oldDeployment = await this.db.updateDeploymentById(deploymentId, { domain: null, isCurrent: false });
|
||||||
const newDeployement = await this.db.createDeployement(updatedDeployment);
|
const newDeployement = await this.db.addDeployement(updatedDeployment);
|
||||||
|
|
||||||
return oldDeployment && Boolean(newDeployement);
|
return oldDeployment && Boolean(newDeployement);
|
||||||
} catch (err) {
|
}
|
||||||
log(err);
|
|
||||||
return false;
|
async rollbackDeployment (projectId: string, deploymentId: string): Promise<boolean> {
|
||||||
}
|
// TODO: Implement transactions
|
||||||
|
const oldCurrentDeployment = await this.db.getDeployment({
|
||||||
|
relations: {
|
||||||
|
domain: true
|
||||||
|
},
|
||||||
|
where: {
|
||||||
|
project: {
|
||||||
|
id: projectId
|
||||||
|
},
|
||||||
|
isCurrent: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!oldCurrentDeployment) {
|
||||||
|
throw new Error('Current deployement doesnot exist');
|
||||||
|
}
|
||||||
|
|
||||||
|
const oldCurrentDeploymentUpdate = await this.db.updateDeploymentById(oldCurrentDeployment.id, { isCurrent: false, domain: null });
|
||||||
|
|
||||||
|
const newCurrentDeploymentUpdate = await this.db.updateDeploymentById(deploymentId, { isCurrent: true, domain: oldCurrentDeployment?.domain });
|
||||||
|
|
||||||
|
return newCurrentDeploymentUpdate && oldCurrentDeploymentUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
async addDomain (projectId: string, domainDetails: { name: string }): Promise<{
|
||||||
|
primaryDomain: Domain,
|
||||||
|
redirectedDomain: Domain
|
||||||
|
}> {
|
||||||
|
const currentProject = await this.db.getProjectById(projectId);
|
||||||
|
|
||||||
|
if (currentProject === null) {
|
||||||
|
throw new Error(`Project with ${projectId} not found`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const primaryDomainDetails = {
|
||||||
|
...domainDetails,
|
||||||
|
branch: currentProject.prodBranch,
|
||||||
|
project: currentProject
|
||||||
|
};
|
||||||
|
|
||||||
|
const savedPrimaryDomain = await this.db.addDomain(primaryDomainDetails);
|
||||||
|
|
||||||
|
const domainArr = domainDetails.name.split('www.');
|
||||||
|
|
||||||
|
const redirectedDomainDetails = {
|
||||||
|
name: domainArr.length > 1 ? domainArr[1] : `www.${domainArr[0]}`,
|
||||||
|
branch: currentProject.prodBranch,
|
||||||
|
project: currentProject,
|
||||||
|
redirectTo: savedPrimaryDomain
|
||||||
|
};
|
||||||
|
|
||||||
|
const savedRedirectedDomain = await this.db.addDomain(redirectedDomainDetails);
|
||||||
|
|
||||||
|
return { primaryDomain: savedPrimaryDomain, redirectedDomain: savedRedirectedDomain };
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateDomain (domainId: string, domainDetails: DeepPartial<Domain>): Promise<boolean> {
|
||||||
|
const domain = await this.db.getDomain({
|
||||||
|
where: {
|
||||||
|
id: Number(domainId)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (domain === null) {
|
||||||
|
throw new Error(`Error finding domain with id ${domainId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const newDomain = {
|
||||||
|
...domainDetails
|
||||||
|
};
|
||||||
|
|
||||||
|
const domainsRedirectedFrom = await this.db.getDomains({
|
||||||
|
where: {
|
||||||
|
project: {
|
||||||
|
id: domain.projectId
|
||||||
|
},
|
||||||
|
redirectToId: domain.id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// If there are domains redirecting to current domain, only branch of current domain can be updated
|
||||||
|
if (domainsRedirectedFrom.length > 0 && domainDetails.branch === domain.branch) {
|
||||||
|
throw new Error('Remove all redirects to this domain before updating');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (domainDetails.redirectToId) {
|
||||||
|
const redirectedDomain = await this.db.getDomain({
|
||||||
|
where: {
|
||||||
|
id: Number(domainDetails.redirectToId)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (redirectedDomain === null) {
|
||||||
|
throw new Error('Could not find Domain to redirect to');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (redirectedDomain.redirectToId) {
|
||||||
|
throw new Error('Unable to redirect to the domain because it is already redirecting elsewhere. Redirects cannot be chained.');
|
||||||
|
}
|
||||||
|
|
||||||
|
newDomain.redirectTo = redirectedDomain;
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateResult = await this.db.updateDomainById(domainId, newDomain);
|
||||||
|
|
||||||
|
return updateResult;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { Project } from 'gql-client';
|
import { Project, Domain } from 'gql-client';
|
||||||
|
|
||||||
import { Button, Typography } from '@material-tailwind/react';
|
import { Button, Typography } from '@material-tailwind/react';
|
||||||
|
|
||||||
@ -22,6 +22,7 @@ const DeploymentsTabPanel = ({ project }: { project: Project }) => {
|
|||||||
|
|
||||||
const [filterValue, setFilterValue] = useState(DEFAULT_FILTER_VALUE);
|
const [filterValue, setFilterValue] = useState(DEFAULT_FILTER_VALUE);
|
||||||
const [deployments, setDeployments] = useState<DeploymentDetails[]>([]);
|
const [deployments, setDeployments] = useState<DeploymentDetails[]>([]);
|
||||||
|
const [prodBranchDomains, setProdBranchDomains] = useState<Domain[]>([]);
|
||||||
|
|
||||||
const fetchDeployments = async () => {
|
const fetchDeployments = async () => {
|
||||||
const { deployments } = await client.getDeployments(project.id);
|
const { deployments } = await client.getDeployments(project.id);
|
||||||
@ -35,8 +36,16 @@ const DeploymentsTabPanel = ({ project }: { project: Project }) => {
|
|||||||
setDeployments(updatedDeployments);
|
setDeployments(updatedDeployments);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const fetchProductionBranchDomains = useCallback(async () => {
|
||||||
|
const { domains } = await client.getDomains(project.id, {
|
||||||
|
branch: project.prodBranch,
|
||||||
|
});
|
||||||
|
setProdBranchDomains(domains);
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchDeployments();
|
fetchDeployments();
|
||||||
|
fetchProductionBranchDomains();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const currentDeployment = useMemo(() => {
|
const currentDeployment = useMemo(() => {
|
||||||
@ -93,6 +102,7 @@ const DeploymentsTabPanel = ({ project }: { project: Project }) => {
|
|||||||
currentDeployment={currentDeployment!}
|
currentDeployment={currentDeployment!}
|
||||||
onUpdate={onUpdateDeploymenToProd}
|
onUpdate={onUpdateDeploymenToProd}
|
||||||
project={project}
|
project={project}
|
||||||
|
prodBranchDomains={prodBranchDomains}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
@ -31,6 +31,11 @@ const OverviewTabPanel = ({ project }: OverviewProps) => {
|
|||||||
const fetchRepoActivity = async () => {
|
const fetchRepoActivity = async () => {
|
||||||
const [owner, repo] = project.repository.split('/');
|
const [owner, repo] = project.repository.split('/');
|
||||||
|
|
||||||
|
if (!repo) {
|
||||||
|
// Do not fetch branches if repo not available
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Get all branches in project repo
|
// Get all branches in project repo
|
||||||
const result = await octokit.rest.repos.listBranches({
|
const result = await octokit.rest.repos.listBranches({
|
||||||
owner,
|
owner,
|
||||||
|
@ -10,7 +10,7 @@ import {
|
|||||||
ChipProps,
|
ChipProps,
|
||||||
} from '@material-tailwind/react';
|
} from '@material-tailwind/react';
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
import { Environment, Project } from 'gql-client';
|
import { Environment, Project, Domain } from 'gql-client';
|
||||||
|
|
||||||
import { relativeTimeMs } from '../../../../utils/time';
|
import { relativeTimeMs } from '../../../../utils/time';
|
||||||
import ConfirmDialog from '../../../shared/ConfirmDialog';
|
import ConfirmDialog from '../../../shared/ConfirmDialog';
|
||||||
@ -24,6 +24,7 @@ interface DeployDetailsCardProps {
|
|||||||
currentDeployment: DeploymentDetails;
|
currentDeployment: DeploymentDetails;
|
||||||
onUpdate: () => Promise<void>;
|
onUpdate: () => Promise<void>;
|
||||||
project: Project;
|
project: Project;
|
||||||
|
prodBranchDomains: Domain[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const STATUS_COLORS: { [key in Status]: ChipProps['color'] } = {
|
const STATUS_COLORS: { [key in Status]: ChipProps['color'] } = {
|
||||||
@ -37,6 +38,7 @@ const DeploymentDetailsCard = ({
|
|||||||
currentDeployment,
|
currentDeployment,
|
||||||
onUpdate,
|
onUpdate,
|
||||||
project,
|
project,
|
||||||
|
prodBranchDomains,
|
||||||
}: DeployDetailsCardProps) => {
|
}: DeployDetailsCardProps) => {
|
||||||
const client = useGQLClient();
|
const client = useGQLClient();
|
||||||
|
|
||||||
@ -167,12 +169,14 @@ const DeploymentDetailsCard = ({
|
|||||||
<Typography variant="small">
|
<Typography variant="small">
|
||||||
The new deployment will be associated with these domains:
|
The new deployment will be associated with these domains:
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="small" color="blue">
|
{prodBranchDomains.length > 0 &&
|
||||||
^ saugatt.com
|
prodBranchDomains.map((value) => {
|
||||||
</Typography>
|
return (
|
||||||
<Typography variant="small" color="blue">
|
<Typography variant="small" color="blue" key={value.id}>
|
||||||
^ www.saugatt.com
|
^ {value.name}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
</ConfirmDialog>
|
</ConfirmDialog>
|
||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
@ -195,12 +199,11 @@ const DeploymentDetailsCard = ({
|
|||||||
<Typography variant="small">
|
<Typography variant="small">
|
||||||
These domains will point to your new deployment:
|
These domains will point to your new deployment:
|
||||||
</Typography>
|
</Typography>
|
||||||
|
{deployment.domain?.name && (
|
||||||
<Typography variant="small" color="blue">
|
<Typography variant="small" color="blue">
|
||||||
^ saugatt.com
|
{deployment.domain?.name}
|
||||||
</Typography>
|
|
||||||
<Typography variant="small" color="blue">
|
|
||||||
^ www.saugatt.com
|
|
||||||
</Typography>
|
</Typography>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</ConfirmDialog>
|
</ConfirmDialog>
|
||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { ApolloClient, DefaultOptions, InMemoryCache, NormalizedCacheObject } from '@apollo/client';
|
import { ApolloClient, DefaultOptions, InMemoryCache, NormalizedCacheObject } from '@apollo/client';
|
||||||
|
|
||||||
import { getUser, getOrganizations, getDeployments, getProjectMembers, searchProjects, getEnvironmentVariables, getProject, getDomains, getProjectsInOrganization } from './queries';
|
import { getUser, getOrganizations, getDeployments, getProjectMembers, searchProjects, getEnvironmentVariables, getProject, getDomains, getProjectsInOrganization } from './queries';
|
||||||
import { AddEnvironmentVariableInput, AddEnvironmentVariablesResponse, GetDeploymentsResponse, GetEnvironmentVariablesResponse, GetOrganizationsResponse, GetProjectMembersResponse, SearchProjectsResponse, GetUserResponse, UpdateDeploymentToProdResponse, GetProjectResponse, UpdateProjectResponse, UpdateProjectInput, RedeployToProdResponse, DeleteProjectResponse, GetProjectsInOrganizationResponse, RollbackDeploymentResponse, AddDomainInput, AddDomainResponse, GetDomainsResponse, UpdateDomainInput, UpdateDomainResponse, AuthenticateGitHubResponse, UnauthenticateGitHubResponse, UpdateEnvironmentVariableResponse, UpdateEnvironmentVariableInput, RemoveEnvironmentVariableResponse, UpdateProjectMemberInput, RemoveProjectMemberResponse, UpdateProjectMemberResponse, DeleteDomainResponse, AddProjectMemberInput, AddProjectMemberResponse, AddProjectInput, AddProjectResponse } from './types';
|
import { AddEnvironmentVariableInput, AddEnvironmentVariablesResponse, GetDeploymentsResponse, GetEnvironmentVariablesResponse, GetOrganizationsResponse, GetProjectMembersResponse, SearchProjectsResponse, GetUserResponse, UpdateDeploymentToProdResponse, GetProjectResponse, UpdateProjectResponse, UpdateProjectInput, RedeployToProdResponse, DeleteProjectResponse, GetProjectsInOrganizationResponse, RollbackDeploymentResponse, AddDomainInput, AddDomainResponse, GetDomainsResponse, UpdateDomainInput, UpdateDomainResponse, AuthenticateGitHubResponse, UnauthenticateGitHubResponse, UpdateEnvironmentVariableResponse, UpdateEnvironmentVariableInput, RemoveEnvironmentVariableResponse, UpdateProjectMemberInput, RemoveProjectMemberResponse, UpdateProjectMemberResponse, DeleteDomainResponse, AddProjectMemberInput, AddProjectMemberResponse, AddProjectInput, AddProjectResponse, FilterDomainInput } from './types';
|
||||||
import { removeProjectMember, addEnvironmentVariables, updateDeploymentToProd, updateProjectMutation, redeployToProd, deleteProject, addDomain, rollbackDeployment, updateDomainMutation, authenticateGitHub, unauthenticateGitHub, updateEnvironmentVariable, removeEnvironmentVariable, updateProjectMember, deleteDomain, addProjectMember, addProject } from './mutations';
|
import { removeProjectMember, addEnvironmentVariables, updateDeploymentToProd, updateProjectMutation, redeployToProd, deleteProject, addDomain, rollbackDeployment, updateDomainMutation, authenticateGitHub, unauthenticateGitHub, updateEnvironmentVariable, removeEnvironmentVariable, updateProjectMember, deleteDomain, addProjectMember, addProject } from './mutations';
|
||||||
|
|
||||||
export interface GraphQLConfig {
|
export interface GraphQLConfig {
|
||||||
@ -286,11 +286,12 @@ export class GQLClient {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getDomains (projectId: string): Promise<GetDomainsResponse> {
|
async getDomains (projectId: string, filter?: FilterDomainInput): Promise<GetDomainsResponse> {
|
||||||
const { data } = await this.client.query({
|
const { data } = await this.client.query({
|
||||||
query: getDomains,
|
query: getDomains,
|
||||||
variables: {
|
variables: {
|
||||||
projectId
|
projectId,
|
||||||
|
filter
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -199,8 +199,8 @@ query ($searchText: String!) {
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const getDomains = gql`
|
export const getDomains = gql`
|
||||||
query ($projectId: String!) {
|
query ($projectId: String!, $filter: FilterDomainsInput) {
|
||||||
domains(projectId: $projectId) {
|
domains(projectId: $projectId, filter: $filter) {
|
||||||
branch
|
branch
|
||||||
createdAt
|
createdAt
|
||||||
redirectTo {
|
redirectTo {
|
||||||
|
@ -272,6 +272,10 @@ export type AddDomainInput = {
|
|||||||
name: string
|
name: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type FilterDomainInput = {
|
||||||
|
branch: string
|
||||||
|
}
|
||||||
|
|
||||||
export type AddDomainResponse = {
|
export type AddDomainResponse = {
|
||||||
addDomain: true
|
addDomain: true
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user