diff --git a/src/components/file/DashboardFile/FileModal.tsx b/src/components/file/DashboardFile/FileModal.tsx index 3990c7e2..b39469c1 100755 --- a/src/components/file/DashboardFile/FileModal.tsx +++ b/src/components/file/DashboardFile/FileModal.tsx @@ -103,7 +103,7 @@ export default function FileModal({ const [editFileOpen, setEditFileOpen] = useState(false); const { data: folders } = useSWR>( - '/api/user/folders?noincl=true', + '/api/user/folders?noincl=true' + (user ? `&user=${user}` : ''), ); const folderCombobox = useCombobox(); @@ -117,7 +117,9 @@ export default function FileModal({ } }; - const { data: tags } = useSWR>('/api/user/tags'); + const { data: tags } = useSWR>( + user ? `/api/users/${user}/tags` : '/api/user/tags', + ); const tagsCombobox = useCombobox(); const [value, setValue] = useState(file?.tags?.map((x) => x.id) ?? []); @@ -229,7 +231,7 @@ export default function FileModal({ )} - {!reduce && !user && ( + {!reduce && ( diff --git a/src/components/file/DashboardFile/index.tsx b/src/components/file/DashboardFile/index.tsx index 2b8f0387..5cb7f09e 100755 --- a/src/components/file/DashboardFile/index.tsx +++ b/src/components/file/DashboardFile/index.tsx @@ -6,12 +6,12 @@ import FileModal from './FileModal'; import styles from './index.module.css'; -export default function DashboardFile({ file, reduce }: { file: File; reduce?: boolean }) { +export default function DashboardFile({ file, reduce, id }: { file: File; reduce?: boolean; id?: string }) { const [open, setOpen] = useState(false); return ( <> - <FileModal open={open} setOpen={setOpen} file={file} reduce={reduce} /> + <FileModal open={open} setOpen={setOpen} file={file} reduce={reduce} user={id} /> <Card shadow='md' radius='md' p={0} onClick={() => setOpen(true)} className={styles.file}> <DashboardFileType key={file.id} file={file} /> </Card> diff --git a/src/components/pages/files/views/Files.tsx b/src/components/pages/files/views/Files.tsx index 52262bfb..84e967a1 100755 --- a/src/components/pages/files/views/Files.tsx +++ b/src/components/pages/files/views/Files.tsx @@ -59,7 +59,7 @@ export default function Files({ id }: { id?: string }) { ) : (data?.page?.length ?? 0 > 0) ? ( data?.page.map((file) => ( <Suspense fallback={<Skeleton height={350} animate />} key={file.id}> - <DashboardFile file={file} /> + <DashboardFile file={file} id={id} /> </Suspense> )) ) : ( diff --git a/src/server/routes/api/user/files/[id]/index.ts b/src/server/routes/api/user/files/[id]/index.ts index ba0d24d1..4aba36c3 100755 --- a/src/server/routes/api/user/files/[id]/index.ts +++ b/src/server/routes/api/user/files/[id]/index.ts @@ -85,7 +85,7 @@ export default fastifyPlugin( if (req.body.tags !== undefined) { const tags = await prisma.tag.findMany({ where: { - userId: req.user.id, + userId: req.user.id !== file.User?.id ? file.User?.id : req.user.id, id: { in: req.body.tags, }, diff --git a/src/server/routes/api/user/folders/[id]/index.ts b/src/server/routes/api/user/folders/[id]/index.ts index 587831d3..c9ce5cc9 100755 --- a/src/server/routes/api/user/folders/[id]/index.ts +++ b/src/server/routes/api/user/folders/[id]/index.ts @@ -1,7 +1,9 @@ import { prisma } from '@/lib/db'; import { fileSelect } from '@/lib/db/models/file'; import { Folder, cleanFolder } from '@/lib/db/models/folder'; +import { User } from '@/lib/db/models/user'; import { log } from '@/lib/logger'; +import { canInteract } from '@/lib/role'; import { userMiddleware } from '@/server/middleware/user'; import fastifyPlugin from 'fastify-plugin'; @@ -20,6 +22,16 @@ type Body = { delete?: 'file' | 'folder'; }; +// TODO: need to refactor interaction checks to use this function in the future +function checkInteraction(current?: Partial<User> | null, owner?: Partial<User> | null) { + if (!current || !owner) return false; + if (current.id === owner.id) return true; + + const can = canInteract(current.role, owner.role); + + return can; +} + const logger = log('api').c('user').c('folders').c('[id]'); export const PATH = '/api/user/folders/:id'; @@ -46,10 +58,11 @@ export default fastifyPlugin( password: true, }, }, + User: true, }, }); if (!folder) return res.notFound('Folder not found'); - if (req.user.id !== folder.userId) return res.forbidden('You do not own this folder'); + if (!checkInteraction(req.user, folder.User)) return res.notFound('Folder not found'); if (req.method === 'PUT') { const { id } = req.body; @@ -59,9 +72,12 @@ export default fastifyPlugin( where: { id, }, + include: { + User: true, + }, }); if (!file) return res.notFound('File not found'); - if (file.userId !== req.user.id) return res.forbidden('You do not own this file'); + if (!checkInteraction(req.user, file.User)) return res.notFound('File not found'); const fileInFolder = await prisma.file.findFirst({ where: { @@ -91,6 +107,7 @@ export default fastifyPlugin( password: true, }, }, + User: true, }, }); @@ -145,6 +162,7 @@ export default fastifyPlugin( password: true, }, }, + User: true, }, }); @@ -161,9 +179,12 @@ export default fastifyPlugin( where: { id, }, + include: { + User: true, + }, }); if (!file) return res.notFound('File not found'); - if (file.userId !== req.user.id) return res.forbidden('You do not own this file'); + if (!checkInteraction(req.user, file.User)) return res.notFound('File not found'); const fileInFolder = await prisma.file.findFirst({ where: { diff --git a/src/server/routes/api/user/folders/index.ts b/src/server/routes/api/user/folders/index.ts index cb4ca726..f245c045 100755 --- a/src/server/routes/api/user/folders/index.ts +++ b/src/server/routes/api/user/folders/index.ts @@ -3,6 +3,7 @@ import { fileSelect } from '@/lib/db/models/file'; import { Folder, cleanFolder, cleanFolders } from '@/lib/db/models/folder'; import { log } from '@/lib/logger'; import { secondlyRatelimit } from '@/lib/ratelimits'; +import { canInteract } from '@/lib/role'; import { userMiddleware } from '@/server/middleware/user'; import fastifyPlugin from 'fastify-plugin'; @@ -17,6 +18,7 @@ type Body = { type Query = { noincl?: boolean; + user?: string; }; const logger = log('api').c('user').c('folders'); @@ -25,11 +27,24 @@ export const PATH = '/api/user/folders'; export default fastifyPlugin( (server, _, done) => { server.get<{ Querystring: Query }>(PATH, { preHandler: [userMiddleware] }, async (req, res) => { - const { noincl } = req.query; + const { noincl, user } = req.query; + + if (user) { + const user = await prisma.user.findUnique({ + where: { + id: req.user.id, + }, + }); + + if (!user) return res.notFound(); + if (req.user.id !== user.id) { + if (!canInteract(req.user.role, user.role)) return res.notFound(); + } + } const folders = await prisma.folder.findMany({ where: { - userId: req.user.id, + userId: user || req.user.id, }, orderBy: { createdAt: 'desc', diff --git a/src/server/routes/api/users/[id].ts b/src/server/routes/api/users/[id]/index.ts similarity index 100% rename from src/server/routes/api/users/[id].ts rename to src/server/routes/api/users/[id]/index.ts diff --git a/src/server/routes/api/users/[id]/tags.ts b/src/server/routes/api/users/[id]/tags.ts new file mode 100644 index 00000000..a233739d --- /dev/null +++ b/src/server/routes/api/users/[id]/tags.ts @@ -0,0 +1,48 @@ +import { prisma } from '@/lib/db'; +import { Tag, tagSelect } from '@/lib/db/models/tag'; +import { canInteract } from '@/lib/role'; +import { administratorMiddleware } from '@/server/middleware/administrator'; +import { userMiddleware } from '@/server/middleware/user'; +import fastifyPlugin from 'fastify-plugin'; + +export type ApiUsersIdTagsResponse = Tag[]; + +type Params = { + id: string; +}; + +// const logger = log('api').c('user').c('id').c('tags'); + +export const PATH = '/api/users/:id/tags'; +export default fastifyPlugin( + (server, _, done) => { + server.get<{ Params: Params }>( + PATH, + { preHandler: [userMiddleware, administratorMiddleware] }, + async (req, res) => { + const { id } = req.params; + + const user = await prisma.user.findUnique({ + where: { + id, + }, + }); + + if (!user) return res.notFound(); + if (!canInteract(req.user.role, user.role)) return res.notFound(); + + const tags = await prisma.tag.findMany({ + where: { + userId: user.id, + }, + select: tagSelect, + }); + + return res.send(tags); + }, + ); + + done(); + }, + { name: PATH }, +);