feat: added schemas for openapi (#452)
This commit is contained in:
@@ -158,6 +158,9 @@ export function NewCardForm({
|
|||||||
...member,
|
...member,
|
||||||
deletedAt: null,
|
deletedAt: null,
|
||||||
})) ?? [],
|
})) ?? [],
|
||||||
|
comments: [],
|
||||||
|
checklists: [],
|
||||||
|
attachments: [],
|
||||||
_filteredLabels: labelPublicIds.map((id) => ({ publicId: id })),
|
_filteredLabels: labelPublicIds.map((id) => ({ publicId: id })),
|
||||||
_filteredMembers: memberPublicIds.map((id) => ({ publicId: id })),
|
_filteredMembers: memberPublicIds.map((id) => ({ publicId: id })),
|
||||||
index: position === "start" ? 0 : list.cards.length,
|
index: position === "start" ? 0 : list.cards.length,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import * as workspaceRepo from "@kan/db/repository/workspace.repo";
|
|||||||
import { generateUID } from "@kan/shared/utils";
|
import { generateUID } from "@kan/shared/utils";
|
||||||
|
|
||||||
import { createTRPCRouter, protectedProcedure } from "../trpc";
|
import { createTRPCRouter, protectedProcedure } from "../trpc";
|
||||||
|
import { attachmentConfirmResponseSchema } from "../schemas";
|
||||||
import { assertPermission } from "../utils/permissions";
|
import { assertPermission } from "../utils/permissions";
|
||||||
import { deleteObject, generateUploadUrl } from "@kan/shared/utils";
|
import { deleteObject, generateUploadUrl } from "@kan/shared/utils";
|
||||||
|
|
||||||
@@ -110,7 +111,7 @@ export const attachmentRouter = createTRPCRouter({
|
|||||||
size: z.number().positive(),
|
size: z.number().positive(),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.output(z.custom<Awaited<ReturnType<typeof cardAttachmentRepo.create>>>())
|
.output(attachmentConfirmResponseSchema)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
const userId = ctx.user?.id;
|
const userId = ctx.user?.id;
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,13 @@ import {
|
|||||||
} from "@kan/shared/utils";
|
} from "@kan/shared/utils";
|
||||||
|
|
||||||
import { createTRPCRouter, protectedProcedure, publicProcedure } from "../trpc";
|
import { createTRPCRouter, protectedProcedure, publicProcedure } from "../trpc";
|
||||||
|
import {
|
||||||
|
boardListItemSchema,
|
||||||
|
boardDetailSchema,
|
||||||
|
boardBySlugSchema,
|
||||||
|
boardCreateResponseSchema,
|
||||||
|
boardUpdateResponseSchema,
|
||||||
|
} from "../schemas";
|
||||||
import { assertCanDelete, assertCanEdit, assertPermission } from "../utils/permissions";
|
import { assertCanDelete, assertCanEdit, assertPermission } from "../utils/permissions";
|
||||||
|
|
||||||
export const boardRouter = createTRPCRouter({
|
export const boardRouter = createTRPCRouter({
|
||||||
@@ -37,9 +44,7 @@ export const boardRouter = createTRPCRouter({
|
|||||||
archived: z.boolean().optional(),
|
archived: z.boolean().optional(),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.output(
|
.output(z.array(boardListItemSchema))
|
||||||
z.custom<Awaited<ReturnType<typeof boardRepo.getAllByWorkspaceId>>>(),
|
|
||||||
)
|
|
||||||
.query(async ({ ctx, input }) => {
|
.query(async ({ ctx, input }) => {
|
||||||
const userId = ctx.user?.id;
|
const userId = ctx.user?.id;
|
||||||
|
|
||||||
@@ -106,7 +111,7 @@ export const boardRouter = createTRPCRouter({
|
|||||||
type: z.enum(["regular", "template"]).optional(),
|
type: z.enum(["regular", "template"]).optional(),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.output(z.custom<Awaited<ReturnType<typeof boardRepo.getByPublicId>>>())
|
.output(boardDetailSchema)
|
||||||
.query(async ({ ctx, input }) => {
|
.query(async ({ ctx, input }) => {
|
||||||
const userId = ctx.user?.id;
|
const userId = ctx.user?.id;
|
||||||
|
|
||||||
@@ -246,7 +251,7 @@ export const boardRouter = createTRPCRouter({
|
|||||||
.optional(),
|
.optional(),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.output(z.custom<Awaited<ReturnType<typeof boardRepo.getBySlug>>>())
|
.output(boardBySlugSchema.nullable())
|
||||||
.query(async ({ ctx, input }) => {
|
.query(async ({ ctx, input }) => {
|
||||||
const workspace = await workspaceRepo.getBySlugWithBoards(
|
const workspace = await workspaceRepo.getBySlugWithBoards(
|
||||||
ctx.db,
|
ctx.db,
|
||||||
@@ -299,7 +304,7 @@ export const boardRouter = createTRPCRouter({
|
|||||||
sourceBoardPublicId: z.string().min(12).optional(),
|
sourceBoardPublicId: z.string().min(12).optional(),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.output(z.custom<Awaited<ReturnType<typeof boardRepo.create>>>())
|
.output(boardCreateResponseSchema)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
const userId = ctx.user?.id;
|
const userId = ctx.user?.id;
|
||||||
|
|
||||||
@@ -469,7 +474,7 @@ export const boardRouter = createTRPCRouter({
|
|||||||
isArchived: z.boolean().optional(),
|
isArchived: z.boolean().optional(),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.output(z.object({ success: z.boolean() }).or(z.custom<Awaited<ReturnType<typeof boardRepo.update>>>()))
|
.output(boardUpdateResponseSchema)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
const userId = ctx.user?.id;
|
const userId = ctx.user?.id;
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,14 @@ import * as listRepo from "@kan/db/repository/list.repo";
|
|||||||
import * as workspaceRepo from "@kan/db/repository/workspace.repo";
|
import * as workspaceRepo from "@kan/db/repository/workspace.repo";
|
||||||
|
|
||||||
import { createTRPCRouter, protectedProcedure, publicProcedure } from "../trpc";
|
import { createTRPCRouter, protectedProcedure, publicProcedure } from "../trpc";
|
||||||
|
import {
|
||||||
|
cardCreateResponseSchema,
|
||||||
|
cardUpdateResponseSchema,
|
||||||
|
cardDetailSchema,
|
||||||
|
commentResponseSchema,
|
||||||
|
commentDeleteResponseSchema,
|
||||||
|
activityItemSchema,
|
||||||
|
} from "../schemas";
|
||||||
import { mergeActivities } from "../utils/activities";
|
import { mergeActivities } from "../utils/activities";
|
||||||
import { sendMentionEmails } from "../utils/notifications";
|
import { sendMentionEmails } from "../utils/notifications";
|
||||||
import { assertCanDelete, assertCanEdit, assertPermission } from "../utils/permissions";
|
import { assertCanDelete, assertCanEdit, assertPermission } from "../utils/permissions";
|
||||||
@@ -42,7 +50,7 @@ export const cardRouter = createTRPCRouter({
|
|||||||
dueDate: z.date().nullable().optional(),
|
dueDate: z.date().nullable().optional(),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.output(z.custom<Awaited<ReturnType<typeof cardRepo.create>>>())
|
.output(cardCreateResponseSchema)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
const userId = ctx.user?.id;
|
const userId = ctx.user?.id;
|
||||||
|
|
||||||
@@ -215,7 +223,7 @@ export const cardRouter = createTRPCRouter({
|
|||||||
comment: z.string().min(1),
|
comment: z.string().min(1),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.output(z.custom<Awaited<ReturnType<typeof cardCommentRepo.create>>>())
|
.output(commentResponseSchema)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
const userId = ctx.user?.id;
|
const userId = ctx.user?.id;
|
||||||
|
|
||||||
@@ -288,7 +296,7 @@ export const cardRouter = createTRPCRouter({
|
|||||||
comment: z.string().min(1),
|
comment: z.string().min(1),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.output(z.custom<Awaited<ReturnType<typeof cardCommentRepo.update>>>())
|
.output(commentResponseSchema)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
const userId = ctx.user?.id;
|
const userId = ctx.user?.id;
|
||||||
|
|
||||||
@@ -376,7 +384,7 @@ export const cardRouter = createTRPCRouter({
|
|||||||
commentPublicId: z.string().min(12),
|
commentPublicId: z.string().min(12),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.output(z.custom<Awaited<ReturnType<typeof cardCommentRepo.softDelete>>>())
|
.output(commentDeleteResponseSchema)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
const userId = ctx.user?.id;
|
const userId = ctx.user?.id;
|
||||||
|
|
||||||
@@ -435,7 +443,7 @@ export const cardRouter = createTRPCRouter({
|
|||||||
createdBy: userId,
|
createdBy: userId,
|
||||||
});
|
});
|
||||||
|
|
||||||
return deletedComment;
|
return { publicId: input.commentPublicId };
|
||||||
}),
|
}),
|
||||||
addOrRemoveLabel: protectedProcedure
|
addOrRemoveLabel: protectedProcedure
|
||||||
.meta({
|
.meta({
|
||||||
@@ -639,25 +647,7 @@ export const cardRouter = createTRPCRouter({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.input(z.object({ cardPublicId: z.string().min(12) }))
|
.input(z.object({ cardPublicId: z.string().min(12) }))
|
||||||
.output(
|
.output(cardDetailSchema)
|
||||||
z.custom<
|
|
||||||
Omit<
|
|
||||||
NonNullable<
|
|
||||||
Awaited<ReturnType<typeof cardRepo.getWithListAndMembersByPublicId>>
|
|
||||||
>,
|
|
||||||
"attachments"
|
|
||||||
> & {
|
|
||||||
attachments: {
|
|
||||||
publicId: string;
|
|
||||||
contentType: string;
|
|
||||||
s3Key: string;
|
|
||||||
originalFilename: string | null;
|
|
||||||
size?: number | null;
|
|
||||||
url: string | null;
|
|
||||||
}[];
|
|
||||||
}
|
|
||||||
>(),
|
|
||||||
)
|
|
||||||
.query(async ({ ctx, input }) => {
|
.query(async ({ ctx, input }) => {
|
||||||
const card = await cardRepo.getWorkspaceAndCardIdByCardPublicId(
|
const card = await cardRepo.getWorkspaceAndCardIdByCardPublicId(
|
||||||
ctx.db,
|
ctx.db,
|
||||||
@@ -763,15 +753,7 @@ export const cardRouter = createTRPCRouter({
|
|||||||
)
|
)
|
||||||
.output(
|
.output(
|
||||||
z.object({
|
z.object({
|
||||||
activities: z.array(
|
activities: z.array(activityItemSchema),
|
||||||
z.custom<
|
|
||||||
NonNullable<
|
|
||||||
Awaited<
|
|
||||||
ReturnType<typeof cardActivityRepo.getPaginatedActivities>
|
|
||||||
>
|
|
||||||
>["activities"][number]
|
|
||||||
>(),
|
|
||||||
),
|
|
||||||
hasMore: z.boolean(),
|
hasMore: z.boolean(),
|
||||||
nextCursor: z.string().datetime().nullable(),
|
nextCursor: z.string().datetime().nullable(),
|
||||||
}),
|
}),
|
||||||
@@ -871,7 +853,7 @@ export const cardRouter = createTRPCRouter({
|
|||||||
dueDate: z.date().nullable().optional(),
|
dueDate: z.date().nullable().optional(),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.output(z.custom<Awaited<ReturnType<typeof cardRepo.update>>>())
|
.output(cardUpdateResponseSchema)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
const userId = ctx.user?.id;
|
const userId = ctx.user?.id;
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import * as activityRepo from "@kan/db/repository/cardActivity.repo";
|
|||||||
import * as listRepo from "@kan/db/repository/list.repo";
|
import * as listRepo from "@kan/db/repository/list.repo";
|
||||||
|
|
||||||
import { createTRPCRouter, protectedProcedure } from "../trpc";
|
import { createTRPCRouter, protectedProcedure } from "../trpc";
|
||||||
|
import { listCreateResponseSchema, listUpdateResponseSchema } from "../schemas";
|
||||||
import { assertCanDelete, assertCanEdit, assertPermission } from "../utils/permissions";
|
import { assertCanDelete, assertCanEdit, assertPermission } from "../utils/permissions";
|
||||||
|
|
||||||
export const listRouter = createTRPCRouter({
|
export const listRouter = createTRPCRouter({
|
||||||
@@ -27,7 +28,7 @@ export const listRouter = createTRPCRouter({
|
|||||||
boardPublicId: z.string().min(12),
|
boardPublicId: z.string().min(12),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.output(z.custom<Awaited<ReturnType<typeof listRepo.create>>>())
|
.output(listCreateResponseSchema)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
const userId = ctx.user?.id;
|
const userId = ctx.user?.id;
|
||||||
|
|
||||||
@@ -163,12 +164,7 @@ export const listRouter = createTRPCRouter({
|
|||||||
index: z.number().optional(),
|
index: z.number().optional(),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.output(
|
.output(listUpdateResponseSchema)
|
||||||
z.custom<
|
|
||||||
| Awaited<ReturnType<typeof listRepo.update>>
|
|
||||||
| Awaited<ReturnType<typeof listRepo.reorder>>
|
|
||||||
>(),
|
|
||||||
)
|
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
const userId = ctx.user?.id;
|
const userId = ctx.user?.id;
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
import { updateSubscriptionSeats } from "@kan/stripe";
|
import { updateSubscriptionSeats } from "@kan/stripe";
|
||||||
|
|
||||||
import { createTRPCRouter, protectedProcedure, publicProcedure } from "../trpc";
|
import { createTRPCRouter, protectedProcedure, publicProcedure } from "../trpc";
|
||||||
|
import { memberInviteResponseSchema } from "../schemas";
|
||||||
import {
|
import {
|
||||||
assertCanManageMember,
|
assertCanManageMember,
|
||||||
assertCanManageRole,
|
assertCanManageRole,
|
||||||
@@ -40,7 +41,7 @@ export const memberRouter = createTRPCRouter({
|
|||||||
workspacePublicId: z.string().min(12),
|
workspacePublicId: z.string().min(12),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.output(z.custom<Awaited<ReturnType<typeof memberRepo.create>>>())
|
.output(memberInviteResponseSchema)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
const userId = ctx.user?.id;
|
const userId = ctx.user?.id;
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,14 @@ import * as workspaceSlugRepo from "@kan/db/repository/workspaceSlug.repo";
|
|||||||
import { generateAvatarUrl, generateUID } from "@kan/shared/utils";
|
import { generateAvatarUrl, generateUID } from "@kan/shared/utils";
|
||||||
|
|
||||||
import { createTRPCRouter, protectedProcedure, publicProcedure } from "../trpc";
|
import { createTRPCRouter, protectedProcedure, publicProcedure } from "../trpc";
|
||||||
|
import {
|
||||||
|
workspaceListItemSchema,
|
||||||
|
workspaceDetailSchema,
|
||||||
|
workspaceWithBoardsSchema,
|
||||||
|
workspaceCreateResponseSchema,
|
||||||
|
workspaceUpdateResponseSchema,
|
||||||
|
workspaceDeleteResponseSchema,
|
||||||
|
} from "../schemas";
|
||||||
import { assertPermission } from "../utils/permissions";
|
import { assertPermission } from "../utils/permissions";
|
||||||
|
|
||||||
export const workspaceRouter = createTRPCRouter({
|
export const workspaceRouter = createTRPCRouter({
|
||||||
@@ -22,9 +30,7 @@ export const workspaceRouter = createTRPCRouter({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.input(z.void())
|
.input(z.void())
|
||||||
.output(
|
.output(z.array(workspaceListItemSchema))
|
||||||
z.custom<Awaited<ReturnType<typeof workspaceRepo.getAllByUserId>>>(),
|
|
||||||
)
|
|
||||||
.query(async ({ ctx }) => {
|
.query(async ({ ctx }) => {
|
||||||
const userId = ctx.user?.id;
|
const userId = ctx.user?.id;
|
||||||
|
|
||||||
@@ -50,11 +56,7 @@ export const workspaceRouter = createTRPCRouter({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.input(z.object({ workspacePublicId: z.string().min(12) }))
|
.input(z.object({ workspacePublicId: z.string().min(12) }))
|
||||||
.output(
|
.output(workspaceDetailSchema)
|
||||||
z.custom<
|
|
||||||
Awaited<ReturnType<typeof workspaceRepo.getByPublicIdWithMembers>>
|
|
||||||
>(),
|
|
||||||
)
|
|
||||||
.query(async ({ ctx, input }) => {
|
.query(async ({ ctx, input }) => {
|
||||||
const userId = ctx.user?.id;
|
const userId = ctx.user?.id;
|
||||||
|
|
||||||
@@ -134,7 +136,7 @@ export const workspaceRouter = createTRPCRouter({
|
|||||||
return {
|
return {
|
||||||
...result,
|
...result,
|
||||||
members: sanitizedMembers,
|
members: sanitizedMembers,
|
||||||
} as Awaited<ReturnType<typeof workspaceRepo.getByPublicIdWithMembers>>;
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -162,9 +164,7 @@ export const workspaceRouter = createTRPCRouter({
|
|||||||
.regex(/^(?![-]+$)[a-zA-Z0-9-]+$/),
|
.regex(/^(?![-]+$)[a-zA-Z0-9-]+$/),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.output(
|
.output(workspaceWithBoardsSchema)
|
||||||
z.custom<Awaited<ReturnType<typeof workspaceRepo.getBySlugWithBoards>>>(),
|
|
||||||
)
|
|
||||||
.query(async ({ ctx, input }) => {
|
.query(async ({ ctx, input }) => {
|
||||||
const userId = ctx.user?.id;
|
const userId = ctx.user?.id;
|
||||||
|
|
||||||
@@ -211,7 +211,7 @@ export const workspaceRouter = createTRPCRouter({
|
|||||||
.optional(),
|
.optional(),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.output(z.custom<Awaited<ReturnType<typeof workspaceRepo.create>>>())
|
.output(workspaceCreateResponseSchema)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
const userId = ctx.user?.id;
|
const userId = ctx.user?.id;
|
||||||
const userEmail = ctx.user?.email;
|
const userEmail = ctx.user?.email;
|
||||||
@@ -270,7 +270,13 @@ export const workspaceRouter = createTRPCRouter({
|
|||||||
code: "INTERNAL_SERVER_ERROR",
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
});
|
});
|
||||||
|
|
||||||
return result;
|
return {
|
||||||
|
publicId: result.publicId,
|
||||||
|
name: result.name!,
|
||||||
|
slug: result.slug!,
|
||||||
|
description: result.description ?? null,
|
||||||
|
plan: result.plan!,
|
||||||
|
};
|
||||||
}),
|
}),
|
||||||
update: protectedProcedure
|
update: protectedProcedure
|
||||||
.meta({
|
.meta({
|
||||||
@@ -300,7 +306,7 @@ export const workspaceRouter = createTRPCRouter({
|
|||||||
.optional(),
|
.optional(),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.output(z.custom<Awaited<ReturnType<typeof workspaceRepo.update>>>())
|
.output(workspaceUpdateResponseSchema)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
const userId = ctx.user?.id;
|
const userId = ctx.user?.id;
|
||||||
|
|
||||||
@@ -383,7 +389,7 @@ export const workspaceRouter = createTRPCRouter({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.input(z.object({ workspacePublicId: z.string().min(12) }))
|
.input(z.object({ workspacePublicId: z.string().min(12) }))
|
||||||
.output(z.custom<Awaited<ReturnType<typeof workspaceRepo.hardDelete>>>())
|
.output(workspaceDeleteResponseSchema)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
const userId = ctx.user?.id;
|
const userId = ctx.user?.id;
|
||||||
|
|
||||||
@@ -405,12 +411,12 @@ export const workspaceRouter = createTRPCRouter({
|
|||||||
});
|
});
|
||||||
await assertPermission(ctx.db, userId, workspace.id, "workspace:delete");
|
await assertPermission(ctx.db, userId, workspace.id, "workspace:delete");
|
||||||
|
|
||||||
const result = await workspaceRepo.hardDelete(
|
await workspaceRepo.hardDelete(
|
||||||
ctx.db,
|
ctx.db,
|
||||||
input.workspacePublicId,
|
input.workspacePublicId,
|
||||||
);
|
);
|
||||||
|
|
||||||
return result;
|
return { success: true };
|
||||||
}),
|
}),
|
||||||
checkSlugAvailability: publicProcedure
|
checkSlugAvailability: publicProcedure
|
||||||
.meta({
|
.meta({
|
||||||
|
|||||||
12
packages/api/src/schemas/attachment.ts
Normal file
12
packages/api/src/schemas/attachment.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
// ─── attachment.confirm ──────────────────────────────────────
|
||||||
|
export const attachmentConfirmResponseSchema = z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
filename: z.string(),
|
||||||
|
originalFilename: z.string(),
|
||||||
|
contentType: z.string(),
|
||||||
|
size: z.number(),
|
||||||
|
s3Key: z.string(),
|
||||||
|
createdAt: z.date(),
|
||||||
|
});
|
||||||
130
packages/api/src/schemas/board.ts
Normal file
130
packages/api/src/schemas/board.ts
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import {
|
||||||
|
checklistResponseSchema,
|
||||||
|
labelSchema,
|
||||||
|
workspaceMemberSchema,
|
||||||
|
} from "./common";
|
||||||
|
|
||||||
|
// ─── board.all ───────────────────────────────────────────────
|
||||||
|
export const boardListItemSchema = z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
favorite: z.boolean(),
|
||||||
|
lists: z.array(
|
||||||
|
z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
index: z.number(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
labels: z.array(labelSchema),
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── Card sub-object inside board detail (byId) ─────────────
|
||||||
|
const boardCardMemberSchema = z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
email: z.string(),
|
||||||
|
user: z
|
||||||
|
.object({
|
||||||
|
name: z.string().nullable(),
|
||||||
|
email: z.string(),
|
||||||
|
image: z.string().nullable(),
|
||||||
|
})
|
||||||
|
.nullable(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const boardDetailCardSchema = z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
title: z.string(),
|
||||||
|
description: z.string().nullable(),
|
||||||
|
index: z.number(),
|
||||||
|
dueDate: z.date().nullable(),
|
||||||
|
labels: z.array(labelSchema),
|
||||||
|
members: z.array(boardCardMemberSchema),
|
||||||
|
attachments: z.array(z.object({ publicId: z.string() })),
|
||||||
|
checklists: z.array(checklistResponseSchema),
|
||||||
|
comments: z.array(z.object({ publicId: z.string() })),
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── board.byId ──────────────────────────────────────────────
|
||||||
|
export const boardDetailSchema = z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
slug: z.string(),
|
||||||
|
visibility: z.string(),
|
||||||
|
isArchived: z.boolean(),
|
||||||
|
favorite: z.boolean(),
|
||||||
|
workspace: z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
members: z.array(workspaceMemberSchema),
|
||||||
|
}),
|
||||||
|
labels: z.array(labelSchema),
|
||||||
|
lists: z.array(
|
||||||
|
z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
index: z.number(),
|
||||||
|
cards: z.array(boardDetailCardSchema),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
allLists: z.array(
|
||||||
|
z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── Card sub-object inside board detail (bySlug — no members) ─
|
||||||
|
const boardSlugCardSchema = z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
title: z.string(),
|
||||||
|
description: z.string().nullable(),
|
||||||
|
index: z.number(),
|
||||||
|
dueDate: z.date().nullable(),
|
||||||
|
labels: z.array(labelSchema),
|
||||||
|
attachments: z.array(z.object({ publicId: z.string() })),
|
||||||
|
checklists: z.array(checklistResponseSchema),
|
||||||
|
comments: z.array(z.object({ publicId: z.string() })),
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── board.bySlug ────────────────────────────────────────────
|
||||||
|
export const boardBySlugSchema = z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
slug: z.string(),
|
||||||
|
visibility: z.string(),
|
||||||
|
workspace: z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
slug: z.string(),
|
||||||
|
}),
|
||||||
|
labels: z.array(labelSchema),
|
||||||
|
lists: z.array(
|
||||||
|
z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
index: z.number(),
|
||||||
|
cards: z.array(boardSlugCardSchema),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
allLists: z.array(
|
||||||
|
z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── board.create / board.createFromSnapshot ─────────────────
|
||||||
|
export const boardCreateResponseSchema = z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── board.update ────────────────────────────────────────────
|
||||||
|
export const boardUpdateResponseSchema = z.union([
|
||||||
|
z.object({ success: z.boolean() }),
|
||||||
|
z.object({ publicId: z.string(), name: z.string() }),
|
||||||
|
]);
|
||||||
213
packages/api/src/schemas/card.ts
Normal file
213
packages/api/src/schemas/card.ts
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import {
|
||||||
|
checklistResponseSchema,
|
||||||
|
labelSchema,
|
||||||
|
workspaceMemberSchema,
|
||||||
|
} from "./common";
|
||||||
|
|
||||||
|
// ─── card.create ─────────────────────────────────────────────
|
||||||
|
export const cardCreateResponseSchema = z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── card.update ─────────────────────────────────────────────
|
||||||
|
export const cardUpdateResponseSchema = z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
title: z.string(),
|
||||||
|
description: z.string().nullable(),
|
||||||
|
dueDate: z.date().nullable(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── Comment responses ───────────────────────────────────────
|
||||||
|
export const commentResponseSchema = z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
comment: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const commentDeleteResponseSchema = z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── card.byId ───────────────────────────────────────────────
|
||||||
|
|
||||||
|
const cardMemberSchema = z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
email: z.string(),
|
||||||
|
user: z
|
||||||
|
.object({
|
||||||
|
name: z.string().nullable(),
|
||||||
|
})
|
||||||
|
.nullable(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const cardDetailSchema = z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
title: z.string(),
|
||||||
|
description: z.string().nullable(),
|
||||||
|
dueDate: z.date().nullable(),
|
||||||
|
createdBy: z.string().nullable(),
|
||||||
|
labels: z.array(labelSchema),
|
||||||
|
attachments: z.array(
|
||||||
|
z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
contentType: z.string(),
|
||||||
|
s3Key: z.string(),
|
||||||
|
originalFilename: z.string().nullable(),
|
||||||
|
size: z.number().nullable(),
|
||||||
|
url: z.string().nullable(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
checklists: z.array(checklistResponseSchema),
|
||||||
|
list: z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
board: z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
labels: z.array(labelSchema),
|
||||||
|
lists: z.array(
|
||||||
|
z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
workspace: z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
members: z.array(workspaceMemberSchema),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
members: z.array(cardMemberSchema),
|
||||||
|
activities: z.array(
|
||||||
|
z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
type: z.string(),
|
||||||
|
createdAt: z.date(),
|
||||||
|
fromIndex: z.number().nullable(),
|
||||||
|
toIndex: z.number().nullable(),
|
||||||
|
fromTitle: z.string().nullable(),
|
||||||
|
toTitle: z.string().nullable(),
|
||||||
|
fromDescription: z.string().nullable(),
|
||||||
|
toDescription: z.string().nullable(),
|
||||||
|
fromDueDate: z.date().nullable(),
|
||||||
|
toDueDate: z.date().nullable(),
|
||||||
|
fromList: z
|
||||||
|
.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
index: z.number(),
|
||||||
|
})
|
||||||
|
.nullable(),
|
||||||
|
toList: z
|
||||||
|
.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
index: z.number(),
|
||||||
|
})
|
||||||
|
.nullable(),
|
||||||
|
label: z
|
||||||
|
.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
})
|
||||||
|
.nullable(),
|
||||||
|
member: z
|
||||||
|
.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
user: z
|
||||||
|
.object({
|
||||||
|
name: z.string().nullable(),
|
||||||
|
email: z.string(),
|
||||||
|
})
|
||||||
|
.nullable(),
|
||||||
|
})
|
||||||
|
.nullable(),
|
||||||
|
user: z
|
||||||
|
.object({
|
||||||
|
name: z.string().nullable(),
|
||||||
|
email: z.string(),
|
||||||
|
})
|
||||||
|
.nullable(),
|
||||||
|
comment: z
|
||||||
|
.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
comment: z.string(),
|
||||||
|
createdBy: z.string().nullable(),
|
||||||
|
updatedAt: z.date().nullable(),
|
||||||
|
deletedAt: z.date().nullable(),
|
||||||
|
})
|
||||||
|
.nullable(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── card.getActivities ──────────────────────────────────────
|
||||||
|
export const activityItemSchema = z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
type: z.string(),
|
||||||
|
createdAt: z.date(),
|
||||||
|
fromIndex: z.number().nullable(),
|
||||||
|
toIndex: z.number().nullable(),
|
||||||
|
fromTitle: z.string().nullable(),
|
||||||
|
toTitle: z.string().nullable(),
|
||||||
|
fromDescription: z.string().nullable(),
|
||||||
|
toDescription: z.string().nullable(),
|
||||||
|
fromDueDate: z.date().nullable(),
|
||||||
|
toDueDate: z.date().nullable(),
|
||||||
|
fromList: z
|
||||||
|
.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
index: z.number(),
|
||||||
|
})
|
||||||
|
.nullable(),
|
||||||
|
toList: z
|
||||||
|
.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
index: z.number(),
|
||||||
|
})
|
||||||
|
.nullable(),
|
||||||
|
label: z
|
||||||
|
.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
})
|
||||||
|
.nullable(),
|
||||||
|
member: z
|
||||||
|
.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
user: z
|
||||||
|
.object({
|
||||||
|
name: z.string().nullable(),
|
||||||
|
email: z.string(),
|
||||||
|
image: z.string().nullable(),
|
||||||
|
})
|
||||||
|
.nullable(),
|
||||||
|
})
|
||||||
|
.nullable(),
|
||||||
|
user: z
|
||||||
|
.object({
|
||||||
|
name: z.string().nullable(),
|
||||||
|
email: z.string(),
|
||||||
|
image: z.string().nullable(),
|
||||||
|
})
|
||||||
|
.nullable(),
|
||||||
|
comment: z
|
||||||
|
.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
comment: z.string(),
|
||||||
|
createdBy: z.string().nullable(),
|
||||||
|
updatedAt: z.date().nullable(),
|
||||||
|
deletedAt: z.date().nullable(),
|
||||||
|
})
|
||||||
|
.nullable(),
|
||||||
|
attachment: z
|
||||||
|
.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
filename: z.string(),
|
||||||
|
originalFilename: z.string().nullable(),
|
||||||
|
})
|
||||||
|
.nullable(),
|
||||||
|
});
|
||||||
39
packages/api/src/schemas/common.ts
Normal file
39
packages/api/src/schemas/common.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
// Shared label schema used across board and card responses
|
||||||
|
export const labelSchema = z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
colourCode: z.string().nullable(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Shared checklist item schema
|
||||||
|
export const checklistItemResponseSchema = z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
title: z.string(),
|
||||||
|
completed: z.boolean(),
|
||||||
|
index: z.number(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Shared checklist schema with items
|
||||||
|
export const checklistResponseSchema = z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
index: z.number(),
|
||||||
|
items: z.array(checklistItemResponseSchema),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Shared user sub-object (used inside member/workspace responses)
|
||||||
|
export const userSchema = z.object({
|
||||||
|
name: z.string().nullable(),
|
||||||
|
email: z.string(),
|
||||||
|
image: z.string().nullable(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Workspace member sub-object
|
||||||
|
export const workspaceMemberSchema = z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
email: z.string(),
|
||||||
|
status: z.string(),
|
||||||
|
user: userSchema.nullable(),
|
||||||
|
});
|
||||||
39
packages/api/src/schemas/index.ts
Normal file
39
packages/api/src/schemas/index.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
export {
|
||||||
|
boardListItemSchema,
|
||||||
|
boardDetailSchema,
|
||||||
|
boardBySlugSchema,
|
||||||
|
boardCreateResponseSchema,
|
||||||
|
boardUpdateResponseSchema,
|
||||||
|
} from "./board";
|
||||||
|
|
||||||
|
export {
|
||||||
|
cardCreateResponseSchema,
|
||||||
|
cardUpdateResponseSchema,
|
||||||
|
cardDetailSchema,
|
||||||
|
commentResponseSchema,
|
||||||
|
commentDeleteResponseSchema,
|
||||||
|
activityItemSchema,
|
||||||
|
} from "./card";
|
||||||
|
|
||||||
|
export {
|
||||||
|
labelSchema,
|
||||||
|
checklistItemResponseSchema,
|
||||||
|
checklistResponseSchema,
|
||||||
|
userSchema,
|
||||||
|
workspaceMemberSchema,
|
||||||
|
} from "./common";
|
||||||
|
|
||||||
|
export {
|
||||||
|
workspaceListItemSchema,
|
||||||
|
workspaceDetailSchema,
|
||||||
|
workspaceWithBoardsSchema,
|
||||||
|
workspaceCreateResponseSchema,
|
||||||
|
workspaceUpdateResponseSchema,
|
||||||
|
workspaceDeleteResponseSchema,
|
||||||
|
} from "./workspace";
|
||||||
|
|
||||||
|
export { listCreateResponseSchema, listUpdateResponseSchema } from "./list";
|
||||||
|
|
||||||
|
export { memberInviteResponseSchema } from "./member";
|
||||||
|
|
||||||
|
export { attachmentConfirmResponseSchema } from "./attachment";
|
||||||
13
packages/api/src/schemas/list.ts
Normal file
13
packages/api/src/schemas/list.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
// ─── list.create ─────────────────────────────────────────────
|
||||||
|
export const listCreateResponseSchema = z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── list.update / list.reorder ──────────────────────────────
|
||||||
|
export const listUpdateResponseSchema = z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
});
|
||||||
6
packages/api/src/schemas/member.ts
Normal file
6
packages/api/src/schemas/member.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
// ─── member.invite ───────────────────────────────────────────
|
||||||
|
export const memberInviteResponseSchema = z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
});
|
||||||
92
packages/api/src/schemas/workspace.ts
Normal file
92
packages/api/src/schemas/workspace.ts
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { userSchema } from "./common";
|
||||||
|
|
||||||
|
// ─── workspace.all ───────────────────────────────────────────
|
||||||
|
export const workspaceListItemSchema = z.object({
|
||||||
|
role: z.string(),
|
||||||
|
workspace: z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
description: z.string().nullable(),
|
||||||
|
slug: z.string(),
|
||||||
|
plan: z.enum(["free", "pro", "enterprise"]),
|
||||||
|
weekStartDay: z.number().nullable(),
|
||||||
|
deletedAt: z.date().nullable(),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── workspace.byId ──────────────────────────────────────────
|
||||||
|
const workspaceMemberDetailSchema = z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
email: z.string().optional(),
|
||||||
|
role: z.string(),
|
||||||
|
status: z.string(),
|
||||||
|
createdAt: z.date(),
|
||||||
|
user: z
|
||||||
|
.object({
|
||||||
|
name: z.string().nullable(),
|
||||||
|
email: z.string().optional(),
|
||||||
|
image: z.string().nullable(),
|
||||||
|
})
|
||||||
|
.nullable(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const workspaceSubscriptionSchema = z.object({
|
||||||
|
plan: z.string(),
|
||||||
|
status: z.string(),
|
||||||
|
seats: z.number().nullable(),
|
||||||
|
unlimitedSeats: z.boolean().nullable(),
|
||||||
|
periodStart: z.date().nullable(),
|
||||||
|
periodEnd: z.date().nullable(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const workspaceDetailSchema = z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
slug: z.string(),
|
||||||
|
showEmailsToMembers: z.boolean().nullable(),
|
||||||
|
weekStartDay: z.number().nullable(),
|
||||||
|
members: z.array(workspaceMemberDetailSchema),
|
||||||
|
subscriptions: z.array(workspaceSubscriptionSchema),
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── workspace.bySlug ────────────────────────────────────────
|
||||||
|
export const workspaceWithBoardsSchema = z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
description: z.string().nullable(),
|
||||||
|
slug: z.string(),
|
||||||
|
boards: z.array(
|
||||||
|
z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
slug: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── workspace.create ────────────────────────────────────────
|
||||||
|
export const workspaceCreateResponseSchema = z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
slug: z.string(),
|
||||||
|
description: z.string().nullable(),
|
||||||
|
plan: z.enum(["free", "pro", "enterprise"]),
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── workspace.update ────────────────────────────────────────
|
||||||
|
export const workspaceUpdateResponseSchema = z.object({
|
||||||
|
publicId: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
slug: z.string(),
|
||||||
|
description: z.string().nullable(),
|
||||||
|
plan: z.enum(["free", "pro", "enterprise"]),
|
||||||
|
showEmailsToMembers: z.boolean().nullable(),
|
||||||
|
weekStartDay: z.number().nullable(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── workspace.delete ────────────────────────────────────────
|
||||||
|
export const workspaceDeleteResponseSchema = z.object({
|
||||||
|
success: z.boolean(),
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user