feat: convert board router to supabase
This commit is contained in:
@@ -1,7 +1,4 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { and, eq, asc, isNull, inArray } from "drizzle-orm";
|
|
||||||
|
|
||||||
import { boards, cards, lists, workspaces } from "~/server/db/schema";
|
|
||||||
import { generateUID } from "~/utils/generateUID";
|
import { generateUID } from "~/utils/generateUID";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -13,78 +10,81 @@ export const boardRouter = createTRPCRouter({
|
|||||||
all: publicProcedure
|
all: publicProcedure
|
||||||
.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 userId = ctx.session?.user.id;
|
const workspace = await ctx.supabase
|
||||||
|
.from('workspace')
|
||||||
|
.select(`id`)
|
||||||
|
.eq('publicId', input.workspacePublicId)
|
||||||
|
.limit(1)
|
||||||
|
.single();
|
||||||
|
|
||||||
// @todo: validate user has access to workspace
|
if (!workspace.data) return;
|
||||||
|
|
||||||
// if (!userId) return
|
const { data } = await ctx.supabase
|
||||||
|
.from('board')
|
||||||
// const workspace = await ctx.db.query.workspaces.findFirst({
|
.select(`
|
||||||
// where: eq(workspaces.publicId, input.workspacePublicId),
|
publicId,
|
||||||
// })
|
name
|
||||||
|
`)
|
||||||
// if (!workspace) return;
|
.is('deletedAt', null)
|
||||||
|
.eq('workspaceId', workspace.data.id);
|
||||||
const { data, error } = await ctx.supabase.from('board').select(`
|
|
||||||
publicId,
|
|
||||||
name
|
|
||||||
`).is('deletedAt', null);
|
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}),
|
}),
|
||||||
byId: publicProcedure
|
byId: publicProcedure
|
||||||
.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, error } = await ctx.supabase.from('board').select(`
|
const { data } = await ctx.supabase
|
||||||
publicId,
|
.from('board')
|
||||||
name,
|
.select(`
|
||||||
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;
|
||||||
}),
|
}),
|
||||||
@@ -96,22 +96,35 @@ export const boardRouter = createTRPCRouter({
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
const userId = ctx.session?.user.id;
|
const userId = ctx.user?.id;
|
||||||
|
|
||||||
if (!userId) return;
|
if (!userId) return;
|
||||||
|
|
||||||
const workspace = await ctx.db.query.workspaces.findFirst({
|
const workspace = await ctx.supabase
|
||||||
where: eq(workspaces.publicId, input.workspacePublicId),
|
.from('workspace')
|
||||||
})
|
.select(`id`)
|
||||||
|
.eq('publicId', input.workspacePublicId)
|
||||||
|
.limit(1)
|
||||||
|
.single();
|
||||||
|
|
||||||
if (!workspace) return;
|
console.log({ workspace })
|
||||||
|
|
||||||
return ctx.db.insert(boards).values({
|
if (!workspace.data) return;
|
||||||
publicId: generateUID(),
|
|
||||||
name: input.name,
|
const { data, error } = await ctx.supabase
|
||||||
createdBy: userId,
|
.from('board')
|
||||||
workspaceId: workspace.id
|
.insert({
|
||||||
});
|
publicId: generateUID(),
|
||||||
|
name: input.name,
|
||||||
|
createdBy: userId,
|
||||||
|
workspaceId: workspace.data.id
|
||||||
|
})
|
||||||
|
.select(`
|
||||||
|
publicId,
|
||||||
|
name
|
||||||
|
`);
|
||||||
|
|
||||||
|
return data;
|
||||||
}),
|
}),
|
||||||
update: publicProcedure
|
update: publicProcedure
|
||||||
.input(
|
.input(
|
||||||
@@ -119,41 +132,59 @@ export const boardRouter = createTRPCRouter({
|
|||||||
boardId: z.string().min(12),
|
boardId: z.string().min(12),
|
||||||
name: z.string().min(1),
|
name: z.string().min(1),
|
||||||
}))
|
}))
|
||||||
.mutation(({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
const userId = ctx.session?.user.id;
|
const userId = ctx.user?.id;
|
||||||
|
|
||||||
if (!userId) return;
|
if (!userId) return;
|
||||||
|
|
||||||
return ctx.db.update(boards).set({ name: input.name }).where(eq(boards.publicId, input.boardId));
|
const { data } = await ctx.supabase
|
||||||
|
.from('board')
|
||||||
|
.update({ name: input.name })
|
||||||
|
.eq('publicId', input.boardId);
|
||||||
|
|
||||||
|
return data;
|
||||||
}),
|
}),
|
||||||
delete: publicProcedure
|
delete: publicProcedure
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
boardPublicId: z.string().min(12),
|
boardPublicId: z.string().min(12),
|
||||||
}))
|
}))
|
||||||
.mutation(({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
const userId = ctx.session?.user.id;
|
const userId = ctx.user?.id;
|
||||||
|
|
||||||
if (!userId) return;
|
if (!userId) return;
|
||||||
|
|
||||||
return ctx.db.transaction(async (tx) => {
|
|
||||||
const board = await tx.query.boards.findFirst({
|
|
||||||
where: eq(boards.publicId, input.boardPublicId),
|
|
||||||
with: {
|
|
||||||
lists: true,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!board) return;
|
|
||||||
|
|
||||||
const listIds = board.lists.map((list) => list.id)
|
const board = await ctx.supabase
|
||||||
|
.from('board')
|
||||||
await tx.update(boards).set({ deletedAt: new Date(), deletedBy: userId }).where(eq(boards.id, board.id));
|
.select(`
|
||||||
|
id,
|
||||||
|
lists:list (id)
|
||||||
|
`)
|
||||||
|
.eq('publicId', input.boardPublicId)
|
||||||
|
.limit(1)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
if (!board.data) return;
|
||||||
|
|
||||||
if (listIds.length) {
|
const listIds = board.data.lists.map((list) => list.id);
|
||||||
await tx.update(lists).set({ deletedAt: new Date(), deletedBy: userId }).where(eq(lists.boardId, board.id));
|
|
||||||
await tx.update(cards).set({ deletedAt: new Date(), deletedBy: userId }).where(inArray(cards.listId, listIds));
|
const deletedAt = new Date().toString();
|
||||||
}
|
|
||||||
})
|
await ctx.supabase
|
||||||
}),
|
.from('board')
|
||||||
|
.update({ deletedAt, deletedBy: userId })
|
||||||
|
.eq('id', board.data.id);
|
||||||
|
|
||||||
|
if (listIds.length) {
|
||||||
|
await ctx.supabase
|
||||||
|
.from('list')
|
||||||
|
.update({ deletedAt, deletedBy: userId })
|
||||||
|
.eq('boardId', board.data.id);
|
||||||
|
|
||||||
|
await ctx.supabase
|
||||||
|
.from('card')
|
||||||
|
.update({ deletedAt, deletedBy: userId })
|
||||||
|
.in('listId', listIds);
|
||||||
|
}
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,11 +1,5 @@
|
|||||||
import { sql } from '@vercel/postgres'
|
import { sql } from '@vercel/postgres'
|
||||||
import { drizzle } from 'drizzle-orm/vercel-postgres'
|
import { drizzle } from 'drizzle-orm/vercel-postgres'
|
||||||
import { createClient } from '@supabase/supabase-js'
|
|
||||||
import { type Database } from "~/types/database.types";
|
|
||||||
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
|
|
||||||
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
|
|
||||||
|
|
||||||
export const supabase = createClient<Database>(supabaseUrl!, supabaseKey!);
|
|
||||||
|
|
||||||
import * as schema from "./schema";
|
import * as schema from "./schema";
|
||||||
|
|
||||||
|
|||||||
1
src/server/db/migrations/0001_stale_mattie_franklin.sql
Normal file
1
src/server/db/migrations/0001_stale_mattie_franklin.sql
Normal file
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE "board" ALTER COLUMN "deletedAt" DROP DEFAULT;
|
||||||
1179
src/server/db/migrations/meta/0001_snapshot.json
Normal file
1179
src/server/db/migrations/meta/0001_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -8,6 +8,13 @@
|
|||||||
"when": 1711571659259,
|
"when": 1711571659259,
|
||||||
"tag": "0000_legal_quicksilver",
|
"tag": "0000_legal_quicksilver",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 1,
|
||||||
|
"version": "5",
|
||||||
|
"when": 1713021051974,
|
||||||
|
"tag": "0001_stale_mattie_franklin",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -27,7 +27,7 @@ export const boards = pgTable(
|
|||||||
createdBy: uuid("createdBy").notNull().references(() => users.id),
|
createdBy: uuid("createdBy").notNull().references(() => users.id),
|
||||||
createdAt: timestamp("createdAt").defaultNow().notNull(),
|
createdAt: timestamp("createdAt").defaultNow().notNull(),
|
||||||
updatedAt: timestamp("updatedAt"),
|
updatedAt: timestamp("updatedAt"),
|
||||||
deletedAt: timestamp("deletedAt").defaultNow(),
|
deletedAt: timestamp("deletedAt"),
|
||||||
deletedBy: uuid("deletedBy").references(() => users.id),
|
deletedBy: uuid("deletedBy").references(() => users.id),
|
||||||
importId: bigint("importId", { mode: "number" }).references(() => imports.id),
|
importId: bigint("importId", { mode: "number" }).references(() => imports.id),
|
||||||
workspaceId: bigint("workspaceId", { mode: "number" }).notNull().references(() => workspaces.id),
|
workspaceId: bigint("workspaceId", { mode: "number" }).notNull().references(() => workspaces.id),
|
||||||
|
|||||||
25
src/utils/supabase/api.ts
Normal file
25
src/utils/supabase/api.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { createServerClient, type CookieOptions, serialize } from '@supabase/ssr'
|
||||||
|
import { type NextApiRequest, type NextApiResponse } from 'next'
|
||||||
|
import { type Database } from "~/types/database.types";
|
||||||
|
|
||||||
|
export default function createClient(req: NextApiRequest, res: NextApiResponse) {
|
||||||
|
const supabase = createServerClient<Database>(
|
||||||
|
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
||||||
|
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
||||||
|
{
|
||||||
|
cookies: {
|
||||||
|
get(name: string) {
|
||||||
|
return req.cookies[name]
|
||||||
|
},
|
||||||
|
set(name: string, value: string, options: CookieOptions) {
|
||||||
|
res.appendHeader('Set-Cookie', serialize(name, value, options))
|
||||||
|
},
|
||||||
|
remove(name: string, options: CookieOptions) {
|
||||||
|
res.appendHeader('Set-Cookie', serialize(name, '', options))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return supabase
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user