chore: lint all router files
This commit is contained in:
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@@ -1,3 +1,6 @@
|
|||||||
{
|
{
|
||||||
"spellright.addToSystemDictionary": true
|
"spellright.addToSystemDictionary": true,
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.fixAll": "explicit"
|
||||||
|
},
|
||||||
}
|
}
|
||||||
@@ -33,4 +33,4 @@ export type AppRouter = typeof appRouter;
|
|||||||
* ^? Post[]
|
* ^? Post[]
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
// export const createCaller = createCallerFactory(appRouter);
|
// export const createCaller = createCallerFactory(appRouter);
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import {
|
import { createTRPCRouter, publicProcedure } from "~/server/api/trpc";
|
||||||
createTRPCRouter,
|
|
||||||
publicProcedure,
|
|
||||||
} from "~/server/api/trpc";
|
|
||||||
|
|
||||||
export const authRouter = createTRPCRouter({
|
export const authRouter = createTRPCRouter({
|
||||||
login: publicProcedure
|
login: publicProcedure
|
||||||
@@ -12,10 +9,10 @@ export const authRouter = createTRPCRouter({
|
|||||||
const { data } = await ctx.db.auth.signInWithOtp({
|
const { data } = await ctx.db.auth.signInWithOtp({
|
||||||
email: input.email,
|
email: input.email,
|
||||||
options: {
|
options: {
|
||||||
emailRedirectTo: '/boards',
|
emailRedirectTo: "/boards",
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
})
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,98 +1,94 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { generateUID } from "~/utils/generateUID";
|
import { generateUID } from "~/utils/generateUID";
|
||||||
|
|
||||||
import {
|
import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";
|
||||||
createTRPCRouter,
|
|
||||||
publicProcedure,
|
|
||||||
} from "~/server/api/trpc";
|
|
||||||
|
|
||||||
export const boardRouter = createTRPCRouter({
|
export const boardRouter = createTRPCRouter({
|
||||||
all: publicProcedure
|
all: protectedProcedure
|
||||||
.input(z.object({ workspacePublicId: z.string().min(12) }))
|
.input(z.object({ workspacePublicId: z.string().min(12) }))
|
||||||
.query(async ({ ctx, input }) => {
|
.query(async ({ ctx, input }) => {
|
||||||
const workspace = await ctx.db
|
const workspace = await ctx.db
|
||||||
.from('workspace')
|
.from("workspace")
|
||||||
.select(`id`)
|
.select(`id`)
|
||||||
.eq('publicId', input.workspacePublicId)
|
.eq("publicId", input.workspacePublicId)
|
||||||
.limit(1)
|
.limit(1)
|
||||||
.single();
|
.single();
|
||||||
|
|
||||||
if (!workspace.data) return;
|
if (!workspace.data) return;
|
||||||
|
|
||||||
const { data } = await ctx.db
|
const { data } = await ctx.db
|
||||||
.from('board')
|
.from("board")
|
||||||
.select(`
|
.select(`publicId, name`)
|
||||||
publicId,
|
.is("deletedAt", null)
|
||||||
name
|
.eq("workspaceId", workspace.data.id);
|
||||||
`)
|
|
||||||
.is('deletedAt', null)
|
|
||||||
.eq('workspaceId', workspace.data.id);
|
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}),
|
}),
|
||||||
byId: publicProcedure
|
byId: protectedProcedure
|
||||||
.input(z.object({ id: z.string().min(12) }))
|
.input(z.object({ id: z.string().min(12) }))
|
||||||
.query(async ({ ctx, input }) => {
|
.query(async ({ ctx, input }) => {
|
||||||
const { data } = await ctx.db
|
const { data } = await ctx.db
|
||||||
.from('board')
|
.from("board")
|
||||||
.select(`
|
.select(
|
||||||
publicId,
|
`
|
||||||
name,
|
|
||||||
workspace (
|
|
||||||
publicId,
|
|
||||||
members:workspace_members (
|
|
||||||
publicId,
|
|
||||||
user (
|
|
||||||
name
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
labels:label (
|
|
||||||
publicId,
|
publicId,
|
||||||
name,
|
name,
|
||||||
colourCode
|
workspace (
|
||||||
),
|
|
||||||
lists:list (
|
|
||||||
publicId,
|
|
||||||
name,
|
|
||||||
boardId,
|
|
||||||
index,
|
|
||||||
cards:card (
|
|
||||||
publicId,
|
publicId,
|
||||||
title,
|
|
||||||
description,
|
|
||||||
listId,
|
|
||||||
index,
|
|
||||||
labels:label (
|
|
||||||
publicId,
|
|
||||||
name,
|
|
||||||
colourCode
|
|
||||||
),
|
|
||||||
members:workspace_members (
|
members:workspace_members (
|
||||||
publicId,
|
publicId,
|
||||||
user (
|
user (
|
||||||
name
|
name
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
),
|
||||||
|
labels:label (
|
||||||
|
publicId,
|
||||||
|
name,
|
||||||
|
colourCode
|
||||||
|
),
|
||||||
|
lists:list (
|
||||||
|
publicId,
|
||||||
|
name,
|
||||||
|
boardId,
|
||||||
|
index,
|
||||||
|
cards:card (
|
||||||
|
publicId,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
listId,
|
||||||
|
index,
|
||||||
|
labels:label (
|
||||||
|
publicId,
|
||||||
|
name,
|
||||||
|
colourCode
|
||||||
|
),
|
||||||
|
members:workspace_members (
|
||||||
|
publicId,
|
||||||
|
user (
|
||||||
|
name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
`,
|
||||||
`)
|
)
|
||||||
.eq('publicId', input.id)
|
.eq("publicId", input.id)
|
||||||
.is('deletedAt', null)
|
.is("deletedAt", null)
|
||||||
.is('lists.deletedAt', null)
|
.is("lists.deletedAt", null)
|
||||||
.is('lists.cards.deletedAt', null)
|
.is("lists.cards.deletedAt", null)
|
||||||
.order('index', { foreignTable: 'list', ascending: true })
|
.order("index", { foreignTable: "list", ascending: true })
|
||||||
.order('index', { foreignTable: 'list.card', ascending: true })
|
.order("index", { foreignTable: "list.card", ascending: true })
|
||||||
.limit(1)
|
.limit(1)
|
||||||
.single()
|
.single();
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}),
|
}),
|
||||||
create: publicProcedure
|
create: protectedProcedure
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
name: z.string().min(1),
|
name: z.string().min(1),
|
||||||
workspacePublicId: z.string().min(12)
|
workspacePublicId: z.string().min(12),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
@@ -101,91 +97,81 @@ export const boardRouter = createTRPCRouter({
|
|||||||
if (!userId) return;
|
if (!userId) return;
|
||||||
|
|
||||||
const workspace = await ctx.db
|
const workspace = await ctx.db
|
||||||
.from('workspace')
|
.from("workspace")
|
||||||
.select(`id`)
|
.select(`id`)
|
||||||
.eq('publicId', input.workspacePublicId)
|
.eq("publicId", input.workspacePublicId)
|
||||||
.limit(1)
|
.limit(1)
|
||||||
.single();
|
.single();
|
||||||
|
|
||||||
if (!workspace.data) return;
|
if (!workspace.data) return;
|
||||||
|
|
||||||
const { data } = await ctx.db
|
const { data } = await ctx.db
|
||||||
.from('board')
|
.from("board")
|
||||||
.insert({
|
.insert({
|
||||||
publicId: generateUID(),
|
publicId: generateUID(),
|
||||||
name: input.name,
|
name: input.name,
|
||||||
createdBy: userId,
|
createdBy: userId,
|
||||||
workspaceId: workspace.data.id
|
workspaceId: workspace.data.id,
|
||||||
})
|
})
|
||||||
.select(`
|
.select(`publicId, name`);
|
||||||
publicId,
|
|
||||||
name
|
|
||||||
`);
|
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}),
|
}),
|
||||||
update: publicProcedure
|
update: protectedProcedure
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
boardId: z.string().min(12),
|
boardId: z.string().min(12),
|
||||||
name: z.string().min(1),
|
name: z.string().min(1),
|
||||||
}))
|
|
||||||
.mutation(async ({ ctx, input }) => {
|
|
||||||
const userId = ctx.user?.id;
|
|
||||||
|
|
||||||
if (!userId) return;
|
|
||||||
|
|
||||||
const { data } = await ctx.db
|
|
||||||
.from('board')
|
|
||||||
.update({ name: input.name })
|
|
||||||
.eq('publicId', input.boardId);
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}),
|
}),
|
||||||
delete: publicProcedure
|
)
|
||||||
.input(
|
.mutation(async ({ ctx, input }) => {
|
||||||
z.object({
|
const { data } = await ctx.db
|
||||||
boardPublicId: z.string().min(12),
|
.from("board")
|
||||||
}))
|
.update({ name: input.name })
|
||||||
.mutation(async ({ ctx, input }) => {
|
.eq("publicId", input.boardId);
|
||||||
const userId = ctx.user?.id;
|
|
||||||
|
|
||||||
if (!userId) return;
|
|
||||||
|
|
||||||
const board = await ctx.db
|
return data;
|
||||||
.from('board')
|
}),
|
||||||
.select(`
|
delete: protectedProcedure
|
||||||
id,
|
.input(
|
||||||
lists:list (id)
|
z.object({
|
||||||
`)
|
boardPublicId: z.string().min(12),
|
||||||
.eq('publicId', input.boardPublicId)
|
}),
|
||||||
.limit(1)
|
)
|
||||||
.single();
|
.mutation(async ({ ctx, input }) => {
|
||||||
|
const userId = ctx.user?.id;
|
||||||
if (!board.data) return;
|
|
||||||
|
|
||||||
const listIds = board.data.lists.map((list) => list.id);
|
const board = await ctx.db
|
||||||
|
.from("board")
|
||||||
|
.select(`id, lists:list (id)`)
|
||||||
|
.eq("publicId", input.boardPublicId)
|
||||||
|
.limit(1)
|
||||||
|
.single();
|
||||||
|
|
||||||
const deletedAt = new Date().toISOString();
|
if (!board.data) return;
|
||||||
|
|
||||||
|
const listIds = board.data.lists.map((list) => list.id);
|
||||||
|
|
||||||
|
const deletedAt = new Date().toISOString();
|
||||||
|
|
||||||
|
await ctx.db
|
||||||
|
.from("board")
|
||||||
|
.update({ deletedAt, deletedBy: userId })
|
||||||
|
.eq("id", board.data.id)
|
||||||
|
.is("deletedAt", null);
|
||||||
|
|
||||||
|
if (listIds.length) {
|
||||||
|
await ctx.db
|
||||||
|
.from("list")
|
||||||
|
.update({ deletedAt, deletedBy: userId })
|
||||||
|
.eq("boardId", board.data.id)
|
||||||
|
.is("deletedAt", null);
|
||||||
|
|
||||||
await ctx.db
|
await ctx.db
|
||||||
.from('board')
|
.from("card")
|
||||||
.update({ deletedAt, deletedBy: userId })
|
.update({ deletedAt, deletedBy: userId })
|
||||||
.eq('id', board.data.id)
|
.in("listId", listIds)
|
||||||
.is('deletedAt', null);
|
.is("deletedAt", null);
|
||||||
|
}
|
||||||
if (listIds.length) {
|
}),
|
||||||
await ctx.db
|
|
||||||
.from('list')
|
|
||||||
.update({ deletedAt, deletedBy: userId })
|
|
||||||
.eq('boardId', board.data.id)
|
|
||||||
.is('deletedAt', null);
|
|
||||||
|
|
||||||
await ctx.db
|
|
||||||
.from('card')
|
|
||||||
.update({ deletedAt, deletedBy: userId })
|
|
||||||
.in('listId', listIds)
|
|
||||||
.is('deletedAt', null);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,19 +1,16 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { generateUID } from "~/utils/generateUID";
|
import { generateUID } from "~/utils/generateUID";
|
||||||
|
|
||||||
import {
|
import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";
|
||||||
createTRPCRouter,
|
|
||||||
publicProcedure,
|
|
||||||
} from "~/server/api/trpc";
|
|
||||||
|
|
||||||
export const cardRouter = createTRPCRouter({
|
export const cardRouter = createTRPCRouter({
|
||||||
create: publicProcedure
|
create: protectedProcedure
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
title: z.string().min(1),
|
title: z.string().min(1),
|
||||||
listPublicId: z.string().min(12),
|
listPublicId: z.string().min(12),
|
||||||
labelsPublicIds: z.array(z.string().min(12)),
|
labelsPublicIds: z.array(z.string().min(12)),
|
||||||
memberPublicIds: z.array(z.string().min(12))
|
memberPublicIds: z.array(z.string().min(12)),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
@@ -22,25 +19,25 @@ export const cardRouter = createTRPCRouter({
|
|||||||
if (!userId) return;
|
if (!userId) return;
|
||||||
|
|
||||||
const list = await ctx.db
|
const list = await ctx.db
|
||||||
.from('list')
|
.from("list")
|
||||||
.select(`id, cards:card (index)`)
|
.select(`id, cards:card (index)`)
|
||||||
.eq('publicId', input.listPublicId)
|
.eq("publicId", input.listPublicId)
|
||||||
.order('index', { foreignTable: 'card', ascending: false })
|
.order("index", { foreignTable: "card", ascending: false })
|
||||||
.limit(1)
|
.limit(1)
|
||||||
.single();
|
.single();
|
||||||
|
|
||||||
if (!list.data?.id) return;
|
if (!list.data?.id) return;
|
||||||
|
|
||||||
const latestCard = list.data.cards.length && list.data.cards[0]
|
const latestCard = list.data.cards.length && list.data.cards[0];
|
||||||
|
|
||||||
const newCard = await ctx.db
|
const newCard = await ctx.db
|
||||||
.from('card')
|
.from("card")
|
||||||
.insert({
|
.insert({
|
||||||
publicId: generateUID(),
|
publicId: generateUID(),
|
||||||
title: input.title,
|
title: input.title,
|
||||||
createdBy: userId,
|
createdBy: userId,
|
||||||
listId: list.data.id,
|
listId: list.data.id,
|
||||||
index: latestCard ? latestCard.index + 1 : 0
|
index: latestCard ? latestCard.index + 1 : 0,
|
||||||
})
|
})
|
||||||
.select(`id`)
|
.select(`id`)
|
||||||
.limit(1)
|
.limit(1)
|
||||||
@@ -49,153 +46,152 @@ export const cardRouter = createTRPCRouter({
|
|||||||
const newCardId = newCard.data?.id;
|
const newCardId = newCard.data?.id;
|
||||||
|
|
||||||
if (newCardId && input.labelsPublicIds.length) {
|
if (newCardId && input.labelsPublicIds.length) {
|
||||||
const labels = await ctx.db
|
const labels = await ctx.db
|
||||||
.from('label')
|
.from("label")
|
||||||
.select(`id`)
|
.select(`id`)
|
||||||
.eq('publicId', input.labelsPublicIds);
|
.eq("publicId", input.labelsPublicIds);
|
||||||
|
|
||||||
if (!labels.data?.length) return;
|
if (!labels.data?.length) return;
|
||||||
|
|
||||||
const labelsInsert = labels.data.map((label) => ({ cardId: newCardId, labelId: label.id }));
|
const labelsInsert = labels.data.map((label) => ({
|
||||||
|
cardId: newCardId,
|
||||||
|
labelId: label.id,
|
||||||
|
}));
|
||||||
|
|
||||||
await ctx.db
|
await ctx.db.from("_card_labels").insert(labelsInsert);
|
||||||
.from('_card_labels')
|
|
||||||
.insert(labelsInsert);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newCardId && input.memberPublicIds.length) {
|
if (newCardId && input.memberPublicIds.length) {
|
||||||
const members = await ctx.db
|
const members = await ctx.db
|
||||||
.from('workspace_members')
|
.from("workspace_members")
|
||||||
.select(`id`)
|
.select(`id`)
|
||||||
.eq('publicId', input.memberPublicIds);
|
.eq("publicId", input.memberPublicIds);
|
||||||
|
|
||||||
if (!members.data?.length) return;
|
if (!members.data?.length) return;
|
||||||
|
|
||||||
const membersInsert = members.data.map((member) => ({ cardId: newCardId, workspaceMemberId: member.id }));
|
const membersInsert = members.data.map((member) => ({
|
||||||
|
cardId: newCardId,
|
||||||
|
workspaceMemberId: member.id,
|
||||||
|
}));
|
||||||
|
|
||||||
await ctx.db
|
await ctx.db.from("_card_workspace_members").insert(membersInsert);
|
||||||
.from('_card_workspace_members')
|
|
||||||
.insert(membersInsert);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return newCard;
|
return newCard;
|
||||||
}),
|
}),
|
||||||
addOrRemoveLabel: publicProcedure
|
addOrRemoveLabel: protectedProcedure
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
cardPublicId: z.string().min(12),
|
cardPublicId: z.string().min(12),
|
||||||
labelPublicId: z.string().min(12),
|
labelPublicId: z.string().min(12),
|
||||||
}),
|
|
||||||
)
|
|
||||||
.mutation(async ({ ctx, input }) => {
|
|
||||||
const userId = ctx.user?.id;
|
|
||||||
|
|
||||||
if (!userId) return;
|
|
||||||
|
|
||||||
const card = await ctx.db
|
|
||||||
.from('card')
|
|
||||||
.select(`id`)
|
|
||||||
.eq('publicId', input.cardPublicId)
|
|
||||||
.limit(1)
|
|
||||||
.single();
|
|
||||||
|
|
||||||
const label = await ctx.db
|
|
||||||
.from('label')
|
|
||||||
.select(`id`)
|
|
||||||
.eq('publicId', input.labelPublicId)
|
|
||||||
.limit(1)
|
|
||||||
.single();
|
|
||||||
|
|
||||||
if (!card.data || !label.data) return;
|
|
||||||
|
|
||||||
const existingLabel = await ctx.db
|
|
||||||
.from('_card_labels')
|
|
||||||
.select()
|
|
||||||
.eq('cardId', card.data.id)
|
|
||||||
.eq('labelId', label.data.id)
|
|
||||||
.limit(1)
|
|
||||||
.single();
|
|
||||||
|
|
||||||
if (existingLabel.data) {
|
|
||||||
await ctx.db
|
|
||||||
.from('_card_labels')
|
|
||||||
.delete()
|
|
||||||
.eq('cardId', card.data.id)
|
|
||||||
.eq('labelId', label.data.id);
|
|
||||||
|
|
||||||
return { newLabel: false };
|
|
||||||
}
|
|
||||||
|
|
||||||
await ctx.db
|
|
||||||
.from('_card_labels')
|
|
||||||
.insert({
|
|
||||||
cardId: card.data.id,
|
|
||||||
labelId: label.data.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
return { newLabel: true };
|
|
||||||
}),
|
}),
|
||||||
addOrRemoveMember: publicProcedure
|
)
|
||||||
.input(
|
.mutation(async ({ ctx, input }) => {
|
||||||
z.object({
|
const userId = ctx.user?.id;
|
||||||
cardPublicId: z.string().min(12),
|
|
||||||
workspaceMemberPublicId: z.string().min(12),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.mutation(async ({ ctx, input }) => {
|
|
||||||
const userId = ctx.user?.id;
|
|
||||||
|
|
||||||
if (!userId) return;
|
if (!userId) return;
|
||||||
|
|
||||||
const card = await ctx.db
|
const card = await ctx.db
|
||||||
.from('card')
|
.from("card")
|
||||||
.select(`id`)
|
.select(`id`)
|
||||||
.eq('publicId', input.cardPublicId)
|
.eq("publicId", input.cardPublicId)
|
||||||
.limit(1)
|
.limit(1)
|
||||||
.single();
|
.single();
|
||||||
|
|
||||||
const member = await ctx.db
|
const label = await ctx.db
|
||||||
.from('workspace_members')
|
.from("label")
|
||||||
.select(`id`)
|
.select(`id`)
|
||||||
.eq('publicId', input.workspaceMemberPublicId)
|
.eq("publicId", input.labelPublicId)
|
||||||
.limit(1)
|
.limit(1)
|
||||||
.single();
|
.single();
|
||||||
|
|
||||||
if (!card.data || !member.data) return;
|
|
||||||
|
|
||||||
const existingMember = await ctx.db
|
if (!card.data || !label.data) return;
|
||||||
.from('_card_workspace_members')
|
|
||||||
.select()
|
|
||||||
.eq('cardId', card.data.id)
|
|
||||||
.eq('workspaceMemberId', member.data.id)
|
|
||||||
.limit(1)
|
|
||||||
.single();
|
|
||||||
|
|
||||||
if (existingMember.data) {
|
const existingLabel = await ctx.db
|
||||||
await ctx.db
|
.from("_card_labels")
|
||||||
.from('_card_workspace_members')
|
.select()
|
||||||
.delete()
|
.eq("cardId", card.data.id)
|
||||||
.eq('cardId', card.data.id)
|
.eq("labelId", label.data.id)
|
||||||
.eq('workspaceMemberId', member.data.id);
|
.limit(1)
|
||||||
|
.single();
|
||||||
return { newMember: false };
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (existingLabel.data) {
|
||||||
await ctx.db
|
await ctx.db
|
||||||
.from('_card_workspace_members')
|
.from("_card_labels")
|
||||||
.insert({
|
.delete()
|
||||||
cardId: card.data.id,
|
.eq("cardId", card.data.id)
|
||||||
workspaceMemberId: member.data.id,
|
.eq("labelId", label.data.id);
|
||||||
});
|
|
||||||
|
|
||||||
return { newMember: true };
|
return { newLabel: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
await ctx.db.from("_card_labels").insert({
|
||||||
|
cardId: card.data.id,
|
||||||
|
labelId: label.data.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
return { newLabel: true };
|
||||||
|
}),
|
||||||
|
addOrRemoveMember: protectedProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
cardPublicId: z.string().min(12),
|
||||||
|
workspaceMemberPublicId: z.string().min(12),
|
||||||
}),
|
}),
|
||||||
byId: publicProcedure
|
)
|
||||||
.input(z.object({ id: z.string().min(12) }))
|
.mutation(async ({ ctx, input }) => {
|
||||||
.query(async ({ ctx, input }) => {
|
const userId = ctx.user?.id;
|
||||||
const { data } = await ctx.db
|
|
||||||
.from('card')
|
if (!userId) return;
|
||||||
.select(`
|
|
||||||
|
const card = await ctx.db
|
||||||
|
.from("card")
|
||||||
|
.select(`id`)
|
||||||
|
.eq("publicId", input.cardPublicId)
|
||||||
|
.limit(1)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
const member = await ctx.db
|
||||||
|
.from("workspace_members")
|
||||||
|
.select(`id`)
|
||||||
|
.eq("publicId", input.workspaceMemberPublicId)
|
||||||
|
.limit(1)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
if (!card.data || !member.data) return;
|
||||||
|
|
||||||
|
const existingMember = await ctx.db
|
||||||
|
.from("_card_workspace_members")
|
||||||
|
.select()
|
||||||
|
.eq("cardId", card.data.id)
|
||||||
|
.eq("workspaceMemberId", member.data.id)
|
||||||
|
.limit(1)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
if (existingMember.data) {
|
||||||
|
await ctx.db
|
||||||
|
.from("_card_workspace_members")
|
||||||
|
.delete()
|
||||||
|
.eq("cardId", card.data.id)
|
||||||
|
.eq("workspaceMemberId", member.data.id);
|
||||||
|
|
||||||
|
return { newMember: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
await ctx.db.from("_card_workspace_members").insert({
|
||||||
|
cardId: card.data.id,
|
||||||
|
workspaceMemberId: member.data.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
return { newMember: true };
|
||||||
|
}),
|
||||||
|
byId: protectedProcedure
|
||||||
|
.input(z.object({ id: z.string().min(12) }))
|
||||||
|
.query(async ({ ctx, input }) => {
|
||||||
|
const { data } = await ctx.db
|
||||||
|
.from("card")
|
||||||
|
.select(
|
||||||
|
`
|
||||||
publicId,
|
publicId,
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
@@ -238,127 +234,131 @@ export const cardRouter = createTRPCRouter({
|
|||||||
name
|
name
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
`)
|
`,
|
||||||
.eq('publicId', input.id)
|
)
|
||||||
.is('deletedAt', null)
|
.eq("publicId", input.id)
|
||||||
.is('list.board.lists.deletedAt', null)
|
.is("deletedAt", null)
|
||||||
.limit(1)
|
.is("list.board.lists.deletedAt", null)
|
||||||
.single();
|
.limit(1)
|
||||||
|
.single();
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
|
}),
|
||||||
|
update: protectedProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
cardId: z.string().min(12),
|
||||||
|
title: z.string().min(1),
|
||||||
|
description: z.string(),
|
||||||
}),
|
}),
|
||||||
update: publicProcedure
|
)
|
||||||
.input(
|
.mutation(async ({ ctx, input }) => {
|
||||||
z.object({
|
const userId = ctx.user?.id;
|
||||||
cardId: z.string().min(12),
|
|
||||||
title: z.string().min(1),
|
|
||||||
description: z.string(),
|
|
||||||
}))
|
|
||||||
.mutation(async ({ ctx, input }) => {
|
|
||||||
const userId = ctx.user?.id;
|
|
||||||
|
|
||||||
if (!userId) return;
|
if (!userId) return;
|
||||||
|
|
||||||
const { data } = await ctx.db
|
const { data } = await ctx.db
|
||||||
.from('card')
|
.from("card")
|
||||||
.update({ title: input.title, description: input.description })
|
.update({ title: input.title, description: input.description })
|
||||||
.eq('publicId', input.cardId)
|
.eq("publicId", input.cardId)
|
||||||
.is('deletedAt', null);
|
.is("deletedAt", null);
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
|
}),
|
||||||
|
delete: protectedProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
cardPublicId: z.string().min(12),
|
||||||
}),
|
}),
|
||||||
delete: publicProcedure
|
)
|
||||||
.input(
|
.mutation(async ({ ctx, input }) => {
|
||||||
z.object({
|
const userId = ctx.user?.id;
|
||||||
cardPublicId: z.string().min(12),
|
|
||||||
}))
|
|
||||||
.mutation(async ({ ctx, input }) => {
|
|
||||||
const userId = ctx.user?.id;
|
|
||||||
|
|
||||||
if (!userId) return;
|
if (!userId) return;
|
||||||
|
|
||||||
const card = await ctx.db
|
const card = await ctx.db
|
||||||
.from('card')
|
.from("card")
|
||||||
.select(`id, index, listId`)
|
.select(`id, index, listId`)
|
||||||
.eq('publicId', input.cardPublicId)
|
.eq("publicId", input.cardPublicId)
|
||||||
.limit(1)
|
.limit(1)
|
||||||
.single();
|
.single();
|
||||||
|
|
||||||
if (!card.data) return;
|
if (!card.data) return;
|
||||||
|
|
||||||
const deletedAt = new Date().toISOString();
|
const deletedAt = new Date().toISOString();
|
||||||
|
|
||||||
await ctx.db
|
await ctx.db
|
||||||
.from('card')
|
.from("card")
|
||||||
.update({ deletedAt, deletedBy: userId})
|
.update({ deletedAt, deletedBy: userId })
|
||||||
.eq('publicId', input.cardPublicId);
|
.eq("publicId", input.cardPublicId);
|
||||||
|
|
||||||
await ctx.db
|
await ctx.db
|
||||||
.from('card')
|
.from("card")
|
||||||
.update({ deletedAt, deletedBy: userId})
|
.update({ deletedAt, deletedBy: userId })
|
||||||
.eq('publicId', input.cardPublicId);
|
.eq("publicId", input.cardPublicId);
|
||||||
|
|
||||||
await ctx.db
|
await ctx.db.rpc("shift_card_index", {
|
||||||
.rpc('shift_card_index', {
|
list_id: card.data.listId,
|
||||||
list_id: card.data.listId,
|
card_index: card.data.index,
|
||||||
card_index: card.data.index,
|
});
|
||||||
});
|
}),
|
||||||
|
reorder: protectedProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
cardId: z.string().min(12),
|
||||||
|
newListId: z.string().min(12),
|
||||||
|
newIndex: z.number().optional(),
|
||||||
}),
|
}),
|
||||||
reorder: publicProcedure
|
)
|
||||||
.input(
|
.mutation(async ({ ctx, input }) => {
|
||||||
z.object({
|
const userId = ctx.user?.id;
|
||||||
cardId: z.string().min(12),
|
|
||||||
newListId: z.string().min(12),
|
|
||||||
newIndex: z.number().optional(),
|
|
||||||
}))
|
|
||||||
.mutation(async ({ ctx, input }) => {
|
|
||||||
const userId = ctx.user?.id;
|
|
||||||
|
|
||||||
if (!userId) return;
|
if (!userId) return;
|
||||||
|
|
||||||
const card = await ctx.db
|
const card = await ctx.db
|
||||||
.from('card')
|
.from("card")
|
||||||
.select(`id, index, list (id)`)
|
.select(`id, index, list (id)`)
|
||||||
.eq('publicId', input.cardId)
|
.eq("publicId", input.cardId)
|
||||||
.is('deletedAt', null)
|
.is("deletedAt", null)
|
||||||
.limit(1)
|
.limit(1)
|
||||||
.single();
|
.single();
|
||||||
|
|
||||||
if (!card.data) return;
|
if (!card.data) return;
|
||||||
|
|
||||||
const currentList = card.data.list;
|
const currentList = card.data.list;
|
||||||
const currentIndex = card.data.index;
|
const currentIndex = card.data.index;
|
||||||
|
|
||||||
let newIndex = input.newIndex;
|
let newIndex = input.newIndex;
|
||||||
|
|
||||||
const newList = await ctx.db
|
const newList = await ctx.db
|
||||||
.from('list')
|
.from("list")
|
||||||
.select(`id, cards:card (index)`)
|
.select(`id, cards:card (index)`)
|
||||||
.eq('publicId', input.newListId)
|
.eq("publicId", input.newListId)
|
||||||
.is('deletedAt', null)
|
.is("deletedAt", null)
|
||||||
.order('index', { foreignTable: 'card', ascending: false })
|
.order("index", { foreignTable: "card", ascending: false })
|
||||||
.limit(1)
|
.limit(1)
|
||||||
.single();
|
.single();
|
||||||
|
|
||||||
if (!newList.data) return;
|
if (!newList.data) return;
|
||||||
|
|
||||||
if (newIndex === undefined) {
|
if (newIndex === undefined) {
|
||||||
const lastCardIndex = newList.data.cards.length ? newList.data.cards[0]?.index : undefined;
|
const lastCardIndex = newList.data.cards.length
|
||||||
|
? newList.data.cards[0]?.index
|
||||||
|
: undefined;
|
||||||
|
|
||||||
newIndex = lastCardIndex !== undefined ? lastCardIndex + 1 : 0;
|
newIndex = lastCardIndex !== undefined ? lastCardIndex + 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!currentList?.id || !newList?.data.id) return;
|
if (!currentList?.id || !newList?.data.id) return;
|
||||||
|
|
||||||
const { error } = await ctx.db
|
const { error } = await ctx.db.rpc("reorder_cards", {
|
||||||
.rpc('reorder_cards', {
|
current_list_id: currentList.id,
|
||||||
current_list_id: currentList.id,
|
new_list_id: newList.data.id,
|
||||||
new_list_id: newList.data.id,
|
current_index: currentIndex,
|
||||||
current_index: currentIndex,
|
new_index: newIndex,
|
||||||
new_index: newIndex,
|
card_id: card.data.id,
|
||||||
card_id: card.data.id,
|
});
|
||||||
});
|
|
||||||
|
|
||||||
return { success: !!error }
|
return { success: !!error };
|
||||||
})
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import {
|
import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";
|
||||||
createTRPCRouter,
|
|
||||||
publicProcedure,
|
|
||||||
} from "~/server/api/trpc";
|
|
||||||
|
|
||||||
import { generateUID } from "~/utils/generateUID";
|
import { generateUID } from "~/utils/generateUID";
|
||||||
|
|
||||||
const TRELLO_API_URL = 'https://api.trello.com/1';
|
const TRELLO_API_URL = "https://api.trello.com/1";
|
||||||
|
|
||||||
interface TrelloBoard {
|
interface TrelloBoard {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -34,29 +31,29 @@ interface MemberData {
|
|||||||
|
|
||||||
export const importRouter = createTRPCRouter({
|
export const importRouter = createTRPCRouter({
|
||||||
trello: createTRPCRouter({
|
trello: createTRPCRouter({
|
||||||
getBoards: publicProcedure
|
getBoards: protectedProcedure
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
apiKey: z.string().length(32),
|
apiKey: z.string().length(32),
|
||||||
token: z.string().length(76),
|
token: z.string().length(76),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.query(async ({ ctx, input }) => {
|
.query(async ({ input }) => {
|
||||||
const userId = ctx.user?.id;
|
const fetchMemberRes = await fetch(
|
||||||
|
`${TRELLO_API_URL}/tokens/${input.token}/member?key=${input.apiKey}`,
|
||||||
if (!userId) return;
|
);
|
||||||
|
|
||||||
const fetchMemberRes = await fetch(`${TRELLO_API_URL}/tokens/${input.token}/member?key=${input.apiKey}`)
|
const member = (await fetchMemberRes.json()) as MemberData;
|
||||||
|
|
||||||
const member = await fetchMemberRes.json() as MemberData;
|
|
||||||
|
|
||||||
const boardIds = member.idBoards;
|
const boardIds = member.idBoards;
|
||||||
|
|
||||||
const fetchBoard = async (boardId: string) => {
|
const fetchBoard = async (boardId: string) => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${TRELLO_API_URL}/boards/${boardId}?key=${input.apiKey}&token=${input.token}`);
|
const response = await fetch(
|
||||||
const data = await response.json() as TrelloBoard;
|
`${TRELLO_API_URL}/boards/${boardId}?key=${input.apiKey}&token=${input.token}`,
|
||||||
|
);
|
||||||
|
const data = (await response.json()) as TrelloBoard;
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw error;
|
throw error;
|
||||||
@@ -70,30 +67,33 @@ export const importRouter = createTRPCRouter({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const boardDataArray = await Promise.all(boards);
|
const boardDataArray = await Promise.all(boards);
|
||||||
|
|
||||||
return boardDataArray.map((board) => ({ id: board.id, name: board.name }));
|
return boardDataArray.map((board) => ({
|
||||||
|
id: board.id,
|
||||||
|
name: board.name,
|
||||||
|
}));
|
||||||
}),
|
}),
|
||||||
importBoards: publicProcedure
|
importBoards: protectedProcedure
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
boardIds: z.array(z.string()),
|
boardIds: z.array(z.string()),
|
||||||
apiKey: z.string().length(32),
|
apiKey: z.string().length(32),
|
||||||
token: z.string().length(76),
|
token: z.string().length(76),
|
||||||
workspacePublicId: z.string().min(12)
|
workspacePublicId: z.string().min(12),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
const userId = ctx.user?.id;
|
const userId = ctx.user?.id;
|
||||||
|
|
||||||
if (!userId) return;
|
if (!userId) return;
|
||||||
|
|
||||||
const newImport = await ctx.db
|
const newImport = await ctx.db
|
||||||
.from('import')
|
.from("import")
|
||||||
.insert({
|
.insert({
|
||||||
publicId: generateUID(),
|
publicId: generateUID(),
|
||||||
source: 'trello',
|
source: "trello",
|
||||||
createdBy: userId,
|
createdBy: userId,
|
||||||
status: 'started'
|
status: "started",
|
||||||
})
|
})
|
||||||
.select(`id`)
|
.select(`id`)
|
||||||
.limit(1)
|
.limit(1)
|
||||||
@@ -104,18 +104,20 @@ export const importRouter = createTRPCRouter({
|
|||||||
let boardsCreated = 0;
|
let boardsCreated = 0;
|
||||||
|
|
||||||
const workspace = await ctx.db
|
const workspace = await ctx.db
|
||||||
.from('workspace')
|
.from("workspace")
|
||||||
.select(`id`)
|
.select(`id`)
|
||||||
.eq('publicId', input.workspacePublicId)
|
.eq("publicId", input.workspacePublicId)
|
||||||
.is('deletedAt', null)
|
.is("deletedAt", null)
|
||||||
.limit(1)
|
.limit(1)
|
||||||
.single();
|
.single();
|
||||||
|
|
||||||
if (!workspace.data) return;
|
if (!workspace.data) return;
|
||||||
|
|
||||||
for (const boardId of input.boardIds) {
|
for (const boardId of input.boardIds) {
|
||||||
const response = await fetch(`${TRELLO_API_URL}/boards/${boardId}?key=${input.apiKey}&token=${input.token}&lists=open&cards=open`);
|
const response = await fetch(
|
||||||
const data = await response.json() as TrelloBoard;
|
`${TRELLO_API_URL}/boards/${boardId}?key=${input.apiKey}&token=${input.token}&lists=open&cards=open`,
|
||||||
|
);
|
||||||
|
const data = (await response.json()) as TrelloBoard;
|
||||||
|
|
||||||
const formattedData = {
|
const formattedData = {
|
||||||
name: data.name,
|
name: data.name,
|
||||||
@@ -125,25 +127,25 @@ export const importRouter = createTRPCRouter({
|
|||||||
.filter((card) => card.idList === list.id)
|
.filter((card) => card.idList === list.id)
|
||||||
.map((_card) => ({
|
.map((_card) => ({
|
||||||
name: _card.name,
|
name: _card.name,
|
||||||
description: _card.desc
|
description: _card.desc,
|
||||||
}))
|
})),
|
||||||
}))
|
})),
|
||||||
}
|
};
|
||||||
|
|
||||||
const newBoard = await ctx.db
|
const newBoard = await ctx.db
|
||||||
.from('board')
|
.from("board")
|
||||||
.insert({
|
.insert({
|
||||||
publicId: generateUID(),
|
publicId: generateUID(),
|
||||||
name: data.name,
|
name: data.name,
|
||||||
createdBy: userId,
|
createdBy: userId,
|
||||||
importId: newImportId,
|
importId: newImportId,
|
||||||
workspaceId: workspace.data.id
|
workspaceId: workspace.data.id,
|
||||||
})
|
})
|
||||||
.select(`id`)
|
.select(`id`)
|
||||||
.limit(1)
|
.limit(1)
|
||||||
.single();
|
.single();
|
||||||
|
|
||||||
const newBoardId = newBoard.data?.id
|
const newBoardId = newBoard.data?.id;
|
||||||
|
|
||||||
if (!newBoardId) return;
|
if (!newBoardId) return;
|
||||||
|
|
||||||
@@ -151,50 +153,48 @@ export const importRouter = createTRPCRouter({
|
|||||||
|
|
||||||
for (const list of formattedData.lists) {
|
for (const list of formattedData.lists) {
|
||||||
const newList = await ctx.db
|
const newList = await ctx.db
|
||||||
.from('list')
|
.from("list")
|
||||||
.insert({
|
.insert({
|
||||||
publicId: generateUID(),
|
publicId: generateUID(),
|
||||||
name: list.name,
|
name: list.name,
|
||||||
createdBy: userId,
|
createdBy: userId,
|
||||||
boardId: newBoardId,
|
boardId: newBoardId,
|
||||||
index: listIndex,
|
index: listIndex,
|
||||||
importId: newImportId
|
importId: newImportId,
|
||||||
})
|
})
|
||||||
.select(`id`)
|
.select(`id`)
|
||||||
.limit(1)
|
.limit(1)
|
||||||
.single();
|
.single();
|
||||||
|
|
||||||
const newListId = newList.data?.id
|
const newListId = newList.data?.id;
|
||||||
|
|
||||||
if (list.cards.length && newListId) {
|
if (list.cards.length && newListId) {
|
||||||
const cardsInsert = list.cards.map((card, index) => ({
|
const cardsInsert = list.cards.map((card, index) => ({
|
||||||
publicId: generateUID(),
|
publicId: generateUID(),
|
||||||
title: card.name,
|
title: card.name,
|
||||||
description: card.description,
|
description: card.description,
|
||||||
createdBy: userId,
|
createdBy: userId,
|
||||||
listId: newListId,
|
listId: newListId,
|
||||||
index,
|
index,
|
||||||
importId: newImportId
|
importId: newImportId,
|
||||||
}))
|
}));
|
||||||
|
|
||||||
await ctx.db
|
await ctx.db.from("card").insert(cardsInsert);
|
||||||
.from('card')
|
|
||||||
.insert(cardsInsert);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
listIndex ++
|
listIndex++;
|
||||||
}
|
}
|
||||||
boardsCreated ++
|
boardsCreated++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (boardsCreated > 0 && newImportId) {
|
if (boardsCreated > 0 && newImportId) {
|
||||||
await ctx.db
|
await ctx.db
|
||||||
.from('import')
|
.from("import")
|
||||||
.update({ status: 'success' })
|
.update({ status: "success" })
|
||||||
.eq('importId', newImportId);
|
.eq("importId", newImportId);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { boardsCreated }
|
return { boardsCreated };
|
||||||
}),
|
}),
|
||||||
})
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { generateUID } from "~/utils/generateUID";
|
import { generateUID } from "~/utils/generateUID";
|
||||||
|
|
||||||
import {
|
import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";
|
||||||
createTRPCRouter,
|
|
||||||
publicProcedure,
|
|
||||||
} from "~/server/api/trpc";
|
|
||||||
|
|
||||||
export const labelRouter = createTRPCRouter({
|
export const labelRouter = createTRPCRouter({
|
||||||
create: publicProcedure
|
create: protectedProcedure
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
name: z.string().min(1).max(36),
|
name: z.string().min(1).max(36),
|
||||||
@@ -21,17 +18,17 @@ export const labelRouter = createTRPCRouter({
|
|||||||
if (!userId) return;
|
if (!userId) return;
|
||||||
|
|
||||||
const card = await ctx.db
|
const card = await ctx.db
|
||||||
.from('card')
|
.from("card")
|
||||||
.select(`id, list (boardId)`)
|
.select(`id, list (boardId)`)
|
||||||
.eq('publicId', input.cardPublicId)
|
.eq("publicId", input.cardPublicId)
|
||||||
.is('deletedAt', null)
|
.is("deletedAt", null)
|
||||||
.limit(1)
|
.limit(1)
|
||||||
.single();
|
.single();
|
||||||
|
|
||||||
if (!card.data?.list) return;
|
if (!card.data?.list) return;
|
||||||
|
|
||||||
const newLabel = await ctx.db
|
const newLabel = await ctx.db
|
||||||
.from('label')
|
.from("label")
|
||||||
.insert({
|
.insert({
|
||||||
publicId: generateUID(),
|
publicId: generateUID(),
|
||||||
name: input.name,
|
name: input.name,
|
||||||
@@ -45,13 +42,11 @@ export const labelRouter = createTRPCRouter({
|
|||||||
|
|
||||||
if (!newLabel.data) return;
|
if (!newLabel.data) return;
|
||||||
|
|
||||||
await ctx.db
|
await ctx.db.from("_card_labels").insert({
|
||||||
.from('_card_labels')
|
cardId: card.data.id,
|
||||||
.insert({
|
labelId: newLabel.data.id,
|
||||||
cardId: card.data.id,
|
});
|
||||||
labelId: newLabel.data.id,
|
|
||||||
})
|
|
||||||
|
|
||||||
return newLabel.data;
|
return newLabel.data;
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,17 +1,14 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { generateUID } from "~/utils/generateUID";
|
import { generateUID } from "~/utils/generateUID";
|
||||||
|
|
||||||
import {
|
import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";
|
||||||
createTRPCRouter,
|
|
||||||
publicProcedure,
|
|
||||||
} from "~/server/api/trpc";
|
|
||||||
|
|
||||||
export const listRouter = createTRPCRouter({
|
export const listRouter = createTRPCRouter({
|
||||||
create: publicProcedure
|
create: protectedProcedure
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
name: z.string().min(1),
|
name: z.string().min(1),
|
||||||
boardPublicId: z.string().min(12)
|
boardPublicId: z.string().min(12),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
@@ -20,80 +17,72 @@ export const listRouter = createTRPCRouter({
|
|||||||
if (!userId) return;
|
if (!userId) return;
|
||||||
|
|
||||||
const board = await ctx.db
|
const board = await ctx.db
|
||||||
.from('board')
|
.from("board")
|
||||||
.select(`id, lists:list (index)`)
|
.select(`id, lists:list (index)`)
|
||||||
.eq('publicId', input.boardPublicId)
|
.eq("publicId", input.boardPublicId)
|
||||||
.order('index', { foreignTable: 'list', ascending: false })
|
.order("index", { foreignTable: "list", ascending: false })
|
||||||
.is('list.deletedAt', null)
|
.is("list.deletedAt", null)
|
||||||
.limit(1)
|
.limit(1)
|
||||||
.single();
|
.single();
|
||||||
|
|
||||||
if (!board.data) return;
|
if (!board.data) return;
|
||||||
|
|
||||||
const latestListIndex = board.data.lists[0]?.index
|
const latestListIndex = board.data.lists[0]?.index;
|
||||||
|
|
||||||
const { data } = await ctx.db
|
const { data } = await ctx.db.from("list").insert({
|
||||||
.from('list')
|
publicId: generateUID(),
|
||||||
.insert({
|
name: input.name,
|
||||||
publicId: generateUID(),
|
createdBy: userId,
|
||||||
name: input.name,
|
boardId: board.data.id,
|
||||||
createdBy: userId,
|
index: latestListIndex ? latestListIndex + 1 : 0,
|
||||||
boardId: board.data.id,
|
}).select(`
|
||||||
index: latestListIndex ? latestListIndex + 1 : 0
|
|
||||||
})
|
|
||||||
.select(`
|
|
||||||
publicId,
|
publicId,
|
||||||
name
|
name
|
||||||
`);
|
`);
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}),
|
}),
|
||||||
reorder: publicProcedure
|
reorder: protectedProcedure
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
boardId: z.string().min(12),
|
boardId: z.string().min(12),
|
||||||
listId: z.string().min(12),
|
listId: z.string().min(12),
|
||||||
currentIndex: z.number(),
|
currentIndex: z.number(),
|
||||||
newIndex: z.number(),
|
newIndex: z.number(),
|
||||||
}))
|
}),
|
||||||
|
)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
const userId = ctx.user?.id;
|
|
||||||
|
|
||||||
if (!userId) return;
|
|
||||||
|
|
||||||
const list = await ctx.db
|
const list = await ctx.db
|
||||||
.from('list')
|
.from("list")
|
||||||
.select(`id, boardId`)
|
.select(`id, boardId`)
|
||||||
.eq('publicId', input.listId)
|
.eq("publicId", input.listId)
|
||||||
.limit(1)
|
.limit(1)
|
||||||
.single();
|
.single();
|
||||||
|
|
||||||
if (!list?.data) return;
|
if (!list?.data) return;
|
||||||
|
|
||||||
const { data } = await ctx.db
|
const { data } = await ctx.db.rpc("reorder_lists", {
|
||||||
.rpc('reorder_list', {
|
board_id: list.data.boardId,
|
||||||
board_id: list.data.boardId,
|
list_id: list.data.id,
|
||||||
list_id: list.data.id,
|
current_index: input.currentIndex,
|
||||||
current_index: input.currentIndex,
|
new_index: input.newIndex,
|
||||||
new_index: input.newIndex,
|
});
|
||||||
})
|
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}),
|
}),
|
||||||
delete: publicProcedure
|
delete: protectedProcedure
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
listPublicId: z.string().min(12),
|
listPublicId: z.string().min(12),
|
||||||
}))
|
}),
|
||||||
|
)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
const userId = ctx.user?.id;
|
const userId = ctx.user?.id;
|
||||||
|
|
||||||
if (!userId) return;
|
|
||||||
|
|
||||||
const list = await ctx.db
|
const list = await ctx.db
|
||||||
.from('list')
|
.from("list")
|
||||||
.select(`id, boardId, index`)
|
.select(`id, boardId, index`)
|
||||||
.eq('publicId', input.listPublicId)
|
.eq("publicId", input.listPublicId)
|
||||||
.limit(1)
|
.limit(1)
|
||||||
.single();
|
.single();
|
||||||
|
|
||||||
@@ -102,43 +91,39 @@ export const listRouter = createTRPCRouter({
|
|||||||
const deletedAt = new Date().toISOString();
|
const deletedAt = new Date().toISOString();
|
||||||
|
|
||||||
await ctx.db
|
await ctx.db
|
||||||
.from('list')
|
.from("list")
|
||||||
.update({ deletedAt, deletedBy: userId })
|
.update({ deletedAt, deletedBy: userId })
|
||||||
.eq('id', list.data.id)
|
.eq("id", list.data.id)
|
||||||
.is('deletedAt', null);
|
.is("deletedAt", null);
|
||||||
|
|
||||||
await ctx.db
|
await ctx.db
|
||||||
.from('card')
|
.from("card")
|
||||||
.update({ deletedAt, deletedBy: userId })
|
.update({ deletedAt, deletedBy: userId })
|
||||||
.eq('listId', list.data.id)
|
.eq("listId", list.data.id)
|
||||||
.is('deletedAt', null);
|
.is("deletedAt", null);
|
||||||
|
|
||||||
const { data } = await ctx.db
|
const { data } = await ctx.db.rpc("shift_list_index", {
|
||||||
.rpc('shift_list_index', {
|
board_id: list.data.boardId,
|
||||||
board_id: list.data.boardId,
|
list_index: list.data.index,
|
||||||
list_index: list.data.index
|
});
|
||||||
})
|
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}),
|
}),
|
||||||
update: publicProcedure
|
update: protectedProcedure
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
listPublicId: z.string().min(12),
|
listPublicId: z.string().min(12),
|
||||||
name: z.string().min(1),
|
name: z.string().min(1),
|
||||||
}))
|
}),
|
||||||
|
)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
const userId = ctx.user?.id;
|
|
||||||
|
|
||||||
if (!userId) return;
|
|
||||||
|
|
||||||
const { data } = await ctx.db
|
const { data } = await ctx.db
|
||||||
.from('list')
|
.from("list")
|
||||||
.update({ name: input.name })
|
.update({ name: input.name })
|
||||||
.eq('publicId', input.listPublicId)
|
.eq("publicId", input.listPublicId)
|
||||||
.is('deletedAt', null)
|
.is("deletedAt", null)
|
||||||
.select(`publicId, name`);
|
.select(`publicId, name`);
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,61 +1,57 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { generateUID } from "~/utils/generateUID";
|
import { generateUID } from "~/utils/generateUID";
|
||||||
|
|
||||||
import {
|
import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";
|
||||||
createTRPCRouter,
|
|
||||||
publicProcedure,
|
|
||||||
} from "~/server/api/trpc";
|
|
||||||
|
|
||||||
export const workspaceRouter = createTRPCRouter({
|
export const workspaceRouter = createTRPCRouter({
|
||||||
all: publicProcedure
|
all: protectedProcedure.query(async ({ ctx }) => {
|
||||||
.query(async ({ ctx }) => {
|
const userId = ctx.user?.id;
|
||||||
const userId = ctx.user?.id;
|
|
||||||
|
|
||||||
if (!userId) return;
|
if (!userId) return;
|
||||||
|
|
||||||
const { data } = await ctx.db
|
const { data } = await ctx.db
|
||||||
.from('workspace_members')
|
.from("workspace_members")
|
||||||
.select(`
|
.select(
|
||||||
|
`
|
||||||
role,
|
role,
|
||||||
workspace (
|
workspace (
|
||||||
publicId,
|
publicId,
|
||||||
name
|
name
|
||||||
)
|
)
|
||||||
`)
|
`,
|
||||||
.eq('userId', userId)
|
)
|
||||||
.is('deletedAt', null);
|
.eq("userId", userId)
|
||||||
|
.is("deletedAt", null);
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}),
|
}),
|
||||||
byId: publicProcedure
|
byId: protectedProcedure
|
||||||
.input(z.object({ publicId: z.string().min(12) }))
|
.input(z.object({ publicId: z.string().min(12) }))
|
||||||
.query(async ({ ctx, input }) => {
|
.query(async ({ ctx, input }) => {
|
||||||
const userId = ctx.user?.id;
|
|
||||||
|
|
||||||
if (!userId) return;
|
|
||||||
|
|
||||||
const { data } = await ctx.db
|
const { data } = await ctx.db
|
||||||
.from('workspace')
|
.from("workspace")
|
||||||
.select(`
|
.select(
|
||||||
publicId,
|
`
|
||||||
members: workspace_members (
|
|
||||||
publicId,
|
publicId,
|
||||||
role,
|
members: workspace_members (
|
||||||
user (
|
publicId,
|
||||||
id,
|
role,
|
||||||
name,
|
user (
|
||||||
email
|
id,
|
||||||
|
name,
|
||||||
|
email
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
`,
|
||||||
`)
|
)
|
||||||
.eq('publicId', input.publicId)
|
.eq("publicId", input.publicId)
|
||||||
.is('deletedAt', null)
|
.is("deletedAt", null)
|
||||||
.limit(1)
|
.limit(1)
|
||||||
.single();
|
.single();
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}),
|
}),
|
||||||
create: publicProcedure
|
create: protectedProcedure
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
name: z.string().min(1),
|
name: z.string().min(1),
|
||||||
@@ -67,34 +63,28 @@ export const workspaceRouter = createTRPCRouter({
|
|||||||
if (!userId) return;
|
if (!userId) return;
|
||||||
|
|
||||||
const workspace = await ctx.db
|
const workspace = await ctx.db
|
||||||
.from('workspace')
|
.from("workspace")
|
||||||
.insert({
|
.insert({
|
||||||
publicId: generateUID(),
|
publicId: generateUID(),
|
||||||
name: input.name,
|
name: input.name,
|
||||||
slug: input.name.toLowerCase(),
|
slug: input.name.toLowerCase(),
|
||||||
createdBy: userId,
|
createdBy: userId,
|
||||||
})
|
})
|
||||||
.select(`
|
.select(`id, publicId, name`)
|
||||||
id,
|
|
||||||
publicId,
|
|
||||||
name
|
|
||||||
`)
|
|
||||||
.limit(1)
|
.limit(1)
|
||||||
.single()
|
.single();
|
||||||
|
|
||||||
const newWorkspaceId = workspace.data?.id
|
const newWorkspaceId = workspace.data?.id;
|
||||||
|
|
||||||
if (!newWorkspaceId) return;
|
if (!newWorkspaceId) return;
|
||||||
|
|
||||||
await ctx.db
|
await ctx.db.from("workspace_members").insert({
|
||||||
.from('workspace_members')
|
publicId: generateUID(),
|
||||||
.insert({
|
userId,
|
||||||
publicId: generateUID(),
|
workspaceId: newWorkspaceId,
|
||||||
userId,
|
createdBy: userId,
|
||||||
workspaceId: newWorkspaceId,
|
role: "admin",
|
||||||
createdBy: userId,
|
});
|
||||||
role: 'admin'
|
|
||||||
})
|
|
||||||
|
|
||||||
const newWorkspace = { ...workspace.data };
|
const newWorkspace = { ...workspace.data };
|
||||||
|
|
||||||
@@ -102,4 +92,4 @@ export const workspaceRouter = createTRPCRouter({
|
|||||||
|
|
||||||
return newWorkspace;
|
return newWorkspace;
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,14 +6,14 @@
|
|||||||
* TL;DR - This is where all the tRPC server stuff is created and plugged in. The pieces you will
|
* TL;DR - This is where all the tRPC server stuff is created and plugged in. The pieces you will
|
||||||
* need to use are documented accordingly near the end.
|
* need to use are documented accordingly near the end.
|
||||||
*/
|
*/
|
||||||
import { initTRPC } from "@trpc/server";
|
import { initTRPC, TRPCError } from "@trpc/server";
|
||||||
import { type CreateNextContextOptions } from "@trpc/server/adapters/next";
|
import { type CreateNextContextOptions } from "@trpc/server/adapters/next";
|
||||||
import superjson from "superjson";
|
import superjson from "superjson";
|
||||||
import { ZodError } from "zod";
|
import { ZodError } from "zod";
|
||||||
|
|
||||||
import createClient from "~/utils/supabase/api";
|
import createClient from "~/utils/supabase/api";
|
||||||
import { type Database } from "~/types/database.types";
|
import { type Database } from "~/types/database.types";
|
||||||
import { type SupabaseClient } from '@supabase/supabase-js';
|
import { type SupabaseClient } from "@supabase/supabase-js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 1. CONTEXT
|
* 1. CONTEXT
|
||||||
@@ -29,7 +29,7 @@ type User = {
|
|||||||
|
|
||||||
interface CreateContextOptions {
|
interface CreateContextOptions {
|
||||||
user: User | null;
|
user: User | null;
|
||||||
db: SupabaseClient<Database>
|
db: SupabaseClient<Database>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -59,9 +59,11 @@ export const createInnerTRPCContext = (opts: CreateContextOptions) => {
|
|||||||
export const createTRPCContext = async (_opts: CreateNextContextOptions) => {
|
export const createTRPCContext = async (_opts: CreateNextContextOptions) => {
|
||||||
const db = createClient(_opts.req, _opts.res);
|
const db = createClient(_opts.req, _opts.res);
|
||||||
|
|
||||||
const { data: { user } } = await db.auth.getUser()
|
const {
|
||||||
|
data: { user },
|
||||||
|
} = await db.auth.getUser();
|
||||||
|
|
||||||
return createInnerTRPCContext({ user, db });
|
return createInnerTRPCContext({ db, user });
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -115,3 +117,24 @@ export const createTRPCRouter = t.router;
|
|||||||
* are logged in.
|
* are logged in.
|
||||||
*/
|
*/
|
||||||
export const publicProcedure = t.procedure;
|
export const publicProcedure = t.procedure;
|
||||||
|
|
||||||
|
/** Reusable middleware that enforces users are logged in before running the procedure. */
|
||||||
|
const enforceUserIsAuthed = t.middleware(async ({ ctx, next }) => {
|
||||||
|
if (!ctx.user) {
|
||||||
|
throw new TRPCError({ code: "UNAUTHORIZED" });
|
||||||
|
}
|
||||||
|
|
||||||
|
return next({
|
||||||
|
ctx,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Protected (authenticated) procedure
|
||||||
|
*
|
||||||
|
* If you want a query or mutation to ONLY be accessible to logged in users, use this. It verifies
|
||||||
|
* the session is valid and guarantees `ctx.session.user` is not null.
|
||||||
|
*
|
||||||
|
* @see https://trpc.io/docs/procedures
|
||||||
|
*/
|
||||||
|
export const protectedProcedure = t.procedure.use(enforceUserIsAuthed);
|
||||||
|
|||||||
Reference in New Issue
Block a user