This commit is contained in:
Daniel Hougaard
2024-02-23 01:34:15 +01:00
parent 744c510a51
commit 9473de2212
4 changed files with 139 additions and 0 deletions

View File

@@ -17,6 +17,14 @@ const projectWithEnv = ProjectsSchema.merge(
})
);
const slugSchema = z
.string()
.min(5)
.max(36)
.refine((v) => slugify(v) === v, {
message: "Slug must be a valid slug"
});
export const registerProjectRouter = async (server: FastifyZodProvider) => {
/* Get project key */
server.route({
@@ -169,4 +177,51 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
return { project };
}
});
server.route({
method: "DELETE",
url: "/:slug",
schema: {
params: z.object({
slug: slugSchema.describe("The slug of the project to delete.")
}),
response: {
200: z.void()
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
await server.services.project.deleteProjectBySlug({
actorId: req.permission.id,
actorOrgId: req.permission.orgId,
actor: req.permission.type,
slug: req.params.slug
});
}
});
server.route({
method: "GET",
url: "/:slug",
schema: {
params: z.object({
slug: slugSchema.describe("The slug of the project to get.")
}),
response: {
200: projectWithEnv
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const project = await server.services.project.getProjectBySlug({
actorId: req.permission.id,
actorOrgId: req.permission.orgId,
actor: req.permission.type,
slug: req.params.slug
});
return project;
}
});
};

View File

@@ -160,6 +160,44 @@ export const projectDALFactory = (db: TDbClient) => {
}
};
const findProjectBySlug = async (slug: string) => {
try {
const projects = await db(TableName.ProjectMembership)
.where(`${TableName.Project}.slug`, slug)
.join(TableName.Project, `${TableName.ProjectMembership}.projectId`, `${TableName.Project}.id`)
.join(TableName.Environment, `${TableName.Environment}.projectId`, `${TableName.Project}.id`)
.select(
selectAllTableCols(TableName.Project),
db.ref("id").withSchema(TableName.Project).as("_id"),
db.ref("id").withSchema(TableName.Environment).as("envId"),
db.ref("slug").withSchema(TableName.Environment).as("envSlug"),
db.ref("name").withSchema(TableName.Environment).as("envName")
)
.orderBy([
{ column: `${TableName.Project}.name`, order: "asc" },
{ column: `${TableName.Environment}.position`, order: "asc" }
]);
return sqlNestRelationships({
data: projects,
key: "id",
parentMapper: ({ _id, ...el }) => ({ _id, ...ProjectsSchema.parse(el) }),
childrenMapper: [
{
key: "envId",
label: "environments" as const,
mapper: ({ envId, envSlug, envName: name }) => ({
id: envId,
slug: envSlug,
name
})
}
]
})?.[0];
} catch (error) {
throw new DatabaseError({ error, name: "Find project by slug" });
}
};
const checkProjectUpgradeStatus = async (projectId: string) => {
const project = await projectOrm.findById(projectId);
const upgradeInProgress =
@@ -179,6 +217,7 @@ export const projectDALFactory = (db: TDbClient) => {
findAllProjectsByIdentity,
findProjectGhostUser,
findProjectById,
findProjectBySlug,
checkProjectUpgradeStatus
};
};

View File

@@ -32,7 +32,9 @@ import { assignWorkspaceKeysToMembers, createProjectKey } from "./project-fns";
import { TProjectQueueFactory } from "./project-queue";
import {
TCreateProjectDTO,
TDeleteProjectBySlugDTO,
TDeleteProjectDTO,
TGetProjectBySlugDTO,
TGetProjectDTO,
TUpdateProjectDTO,
TUpgradeProjectDTO
@@ -329,6 +331,27 @@ export const projectServiceFactory = ({
return deletedProject;
};
const deleteProjectBySlug = async ({ actor, actorId, actorOrgId, slug }: TDeleteProjectBySlugDTO) => {
const project = await projectDAL.findOne({ slug });
const { permission } = await permissionService.getProjectPermission(actor, actorId, project.id, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Project);
const deletedProject = await projectDAL.transaction(async (tx) => {
const delProject = await projectDAL.deleteById(project.id, tx);
const projectGhostUser = await projectMembershipDAL.findProjectGhostUser(project.id).catch(() => null);
// Delete the org membership for the ghost user if it's found.
if (projectGhostUser) {
await userDAL.deleteById(projectGhostUser.id, tx);
}
return delProject;
});
return deletedProject;
};
const getProjects = async (actorId: string) => {
const workspaces = await projectDAL.findAllProjects(actorId);
return workspaces;
@@ -339,6 +362,12 @@ export const projectServiceFactory = ({
return projectDAL.findProjectById(projectId);
};
const getProjectBySlug = async ({ actorId, actorOrgId, slug, actor }: TGetProjectBySlugDTO) => {
const project = await projectDAL.findProjectBySlug(slug);
await permissionService.getProjectPermission(actor, actorId, project.id, actorOrgId);
return project;
};
const updateProject = async ({ projectId, actor, actorId, actorOrgId, update }: TUpdateProjectDTO) => {
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Settings);
@@ -417,8 +446,10 @@ export const projectServiceFactory = ({
deleteProject,
getProjects,
updateProject,
deleteProjectBySlug,
getProjectUpgradeStatus,
getAProject,
getProjectBySlug,
toggleAutoCapitalization,
updateName,
upgradeProject

View File

@@ -19,6 +19,13 @@ export type TDeleteProjectDTO = {
projectId: string;
};
export type TDeleteProjectBySlugDTO = {
slug: string;
actor: ActorType;
actorId: string;
actorOrgId?: string;
};
export type TGetProjectDTO = {
actor: ActorType;
actorId: string;
@@ -26,6 +33,13 @@ export type TGetProjectDTO = {
projectId: string;
};
export type TGetProjectBySlugDTO = {
slug: string;
actor: ActorType;
actorId: string;
actorOrgId?: string;
};
export type TUpdateProjectDTO = {
update: {
name?: string;