diff --git a/apps/api/src/controllers/data.ts b/apps/api/src/controllers/data.ts index 88fea0404..b75c3abe2 100644 --- a/apps/api/src/controllers/data.ts +++ b/apps/api/src/controllers/data.ts @@ -1,11 +1,14 @@ import { FastifyInstance, FastifyReply, FastifyRequest } from "fastify"; +import { requirePermission } from "../lib/roles"; import { prisma } from "../prisma"; export function dataRoutes(fastify: FastifyInstance) { // Get total count of all tickets fastify.get( "/api/v1/data/tickets/all", - + { + preHandler: requirePermission(["issue::read"]), + }, async (request: FastifyRequest, reply: FastifyReply) => { const result = await prisma.ticket.count({ where: { hidden: false }, @@ -18,7 +21,9 @@ export function dataRoutes(fastify: FastifyInstance) { // Get total count of all completed tickets fastify.get( "/api/v1/data/tickets/completed", - + { + preHandler: requirePermission(["issue::read"]), + }, async (request: FastifyRequest, reply: FastifyReply) => { const result = await prisma.ticket.count({ where: { isComplete: true, hidden: false }, @@ -31,7 +36,9 @@ export function dataRoutes(fastify: FastifyInstance) { // Get total count of all open tickets fastify.get( "/api/v1/data/tickets/open", - + { + preHandler: requirePermission(["issue::read"]), + }, async (request: FastifyRequest, reply: FastifyReply) => { const result = await prisma.ticket.count({ where: { isComplete: false, hidden: false }, @@ -44,7 +51,9 @@ export function dataRoutes(fastify: FastifyInstance) { // Get total of all unsassigned tickets fastify.get( "/api/v1/data/tickets/unassigned", - + { + preHandler: requirePermission(["issue::read"]), + }, async (request: FastifyRequest, reply: FastifyReply) => { const result = await prisma.ticket.count({ where: { userId: null, hidden: false, isComplete: false }, diff --git a/apps/api/src/controllers/notebook.ts b/apps/api/src/controllers/notebook.ts index 71aa66e50..378dfe575 100644 --- a/apps/api/src/controllers/notebook.ts +++ b/apps/api/src/controllers/notebook.ts @@ -1,5 +1,6 @@ import { FastifyInstance, FastifyReply, FastifyRequest } from "fastify"; import { track } from "../lib/hog"; +import { requirePermission } from "../lib/roles"; import { checkSession } from "../lib/session"; import { prisma } from "../prisma"; @@ -19,7 +20,9 @@ export function notebookRoutes(fastify: FastifyInstance) { // Create a new entry fastify.post( "/api/v1/notebook/note/create", - + { + preHandler: requirePermission(["document::create"]), + }, async (request: FastifyRequest, reply: FastifyReply) => { const { content, title }: any = request.body; const user = await checkSession(request); @@ -43,7 +46,9 @@ export function notebookRoutes(fastify: FastifyInstance) { // Get all entries fastify.get( "/api/v1/notebooks/all", - + { + preHandler: requirePermission(["document::read"]), + }, async (request: FastifyRequest, reply: FastifyReply) => { const user = await checkSession(request); @@ -58,7 +63,9 @@ export function notebookRoutes(fastify: FastifyInstance) { // Get a single entry fastify.get( "/api/v1/notebooks/note/:id", - + { + preHandler: requirePermission(["document::read"]), + }, async (request: FastifyRequest, reply: FastifyReply) => { const user = await checkSession(request); @@ -75,14 +82,17 @@ export function notebookRoutes(fastify: FastifyInstance) { // Delete an entry fastify.delete( "/api/v1/notebooks/note/:id", + { + preHandler: requirePermission(["document::delete"]), + }, async (request: FastifyRequest, reply: FastifyReply) => { const user = await checkSession(request); const { id }: any = request.params; await prisma.notes.delete({ - where: { + where: { id: id, - userId: user!.id + userId: user!.id, }, }); @@ -95,15 +105,18 @@ export function notebookRoutes(fastify: FastifyInstance) { // Update an entry fastify.put( "/api/v1/notebooks/note/:id/update", + { + preHandler: requirePermission(["document::update"]), + }, async (request: FastifyRequest, reply: FastifyReply) => { const user = await checkSession(request); const { id }: any = request.params; const { content, title }: any = request.body; await prisma.notes.update({ - where: { + where: { id: id, - userId: user!.id + userId: user!.id, }, data: { title: title, diff --git a/apps/api/src/controllers/roles.ts b/apps/api/src/controllers/roles.ts index 02549144d..a277293f2 100644 --- a/apps/api/src/controllers/roles.ts +++ b/apps/api/src/controllers/roles.ts @@ -98,7 +98,7 @@ export function roleRoutes(fastify: FastifyInstance) { }, async (request: FastifyRequest, reply: FastifyReply) => { const { id }: any = request.params; - const { name, description, permissions, isDefault }: any = request.body; + const { name, description, permissions, isDefault, users }: any = request.body; try { const updatedRole = await prisma.role.update({ @@ -109,6 +109,9 @@ export function roleRoutes(fastify: FastifyInstance) { permissions, isDefault, updatedAt: new Date(), + users: { + set: Array.isArray(users) ? users.map(userId => ({ id: userId })) : [{ id: users }], // Ensure users is an array of objects with unique IDs when updating + }, }, }); @@ -156,7 +159,7 @@ export function roleRoutes(fastify: FastifyInstance) { fastify.post( "/api/v1/role/assign", { - // preHandler: requirePermission(['role::assign']), + preHandler: requirePermission(['role::update']), }, async (request: FastifyRequest, reply: FastifyReply) => { const { userId, roleId }: any = request.body; diff --git a/apps/api/src/controllers/ticket.ts b/apps/api/src/controllers/ticket.ts index 31fba5626..018b8fe34 100644 --- a/apps/api/src/controllers/ticket.ts +++ b/apps/api/src/controllers/ticket.ts @@ -12,6 +12,7 @@ import { sendTicketStatus } from "../lib/nodemailer/ticket/status"; import { assignedNotification } from "../lib/notifications/issue/assigned"; import { commentNotification } from "../lib/notifications/issue/comment"; import { sendWebhookNotification } from "../lib/notifications/webhook"; +import { requirePermission } from "../lib/roles"; import { checkSession } from "../lib/session"; import { prisma } from "../prisma"; @@ -24,10 +25,12 @@ const validateEmail = (email: string) => { }; export function ticketRoutes(fastify: FastifyInstance) { - // Create a new ticket + // Create a new ticket - public endpoint, no preHandler needed fastify.post( "/api/v1/ticket/create", - + { + preHandler: requirePermission(["issue::create"]), + }, async (request: FastifyRequest, reply: FastifyReply) => { const { name, @@ -130,137 +133,130 @@ export function ticketRoutes(fastify: FastifyInstance) { } ); - // Get a ticket by id + // Get a ticket by id - requires auth fastify.get( "/api/v1/ticket/:id", - + { + preHandler: requirePermission(["issue::read"]), + }, async (request: FastifyRequest, reply: FastifyReply) => { - const bearer = request.headers.authorization!.split(" ")[1]; - const token = checkToken(bearer); - const { id }: any = request.params; - if (token) { - const ticket = await prisma.ticket.findUnique({ - where: { - id: id, + const ticket = await prisma.ticket.findUnique({ + where: { + id: id, + }, + include: { + client: { + select: { id: true, name: true, number: true, notes: true }, }, - include: { - client: { - select: { id: true, name: true, number: true, notes: true }, - }, - assignedTo: { - select: { id: true, name: true }, - }, + assignedTo: { + select: { id: true, name: true }, }, - }); + }, + }); - const timeTracking = await prisma.timeTracking.findMany({ - where: { - ticketId: id, - }, - include: { - user: { - select: { - name: true, - }, + const timeTracking = await prisma.timeTracking.findMany({ + where: { + ticketId: id, + }, + include: { + user: { + select: { + name: true, }, }, - }); + }, + }); - const comments = await prisma.comment.findMany({ - where: { - ticketId: ticket!.id, - }, - include: { - user: { - select: { - name: true, - }, + const comments = await prisma.comment.findMany({ + where: { + ticketId: ticket!.id, + }, + include: { + user: { + select: { + name: true, }, }, - }); + }, + }); - const files = await prisma.ticketFile.findMany({ - where: { - ticketId: id, - }, - }); + const files = await prisma.ticketFile.findMany({ + where: { + ticketId: id, + }, + }); - var t = { - ...ticket, - comments: [...comments], - TimeTracking: [...timeTracking], - files: [...files], - }; + var t = { + ...ticket, + comments: [...comments], + TimeTracking: [...timeTracking], + files: [...files], + }; - reply.send({ - ticket: t, - sucess: true, - }); - } + reply.send({ + ticket: t, + sucess: true, + }); } ); - // Get all tickets + // Get all tickets - requires auth fastify.get( "/api/v1/tickets/open", + { + preHandler: requirePermission(["issue::read"]), + }, async (request: FastifyRequest, reply: FastifyReply) => { - const bearer = request.headers.authorization!.split(" ")[1]; - const token = checkToken(bearer); - - if (token) { - const tickets = await prisma.ticket.findMany({ - where: { isComplete: false, hidden: false }, - orderBy: [ - { - createdAt: "desc", - }, - ], - include: { - client: { - select: { id: true, name: true, number: true }, - }, - assignedTo: { - select: { id: true, name: true }, - }, - team: { - select: { id: true, name: true }, - }, + const tickets = await prisma.ticket.findMany({ + where: { isComplete: false, hidden: false }, + orderBy: [ + { + createdAt: "desc", }, - }); + ], + include: { + client: { + select: { id: true, name: true, number: true }, + }, + assignedTo: { + select: { id: true, name: true }, + }, + team: { + select: { id: true, name: true }, + }, + }, + }); - reply.send({ - tickets: tickets, - sucess: true, - }); - } + reply.send({ + tickets: tickets, + sucess: true, + }); } ); - // Basic Search for a ticket + // Basic Search - requires auth fastify.post( "/api/v1/tickets/search", + { + preHandler: requirePermission(["issue::read"]), + }, async (request: FastifyRequest, reply: FastifyReply) => { - const bearer = request.headers.authorization!.split(" ")[1]; - const token = checkToken(bearer); - const { query }: any = request.body; - if (token) { - const tickets = await prisma.ticket.findMany({ - where: { - title: { - contains: query, - }, + const tickets = await prisma.ticket.findMany({ + where: { + title: { + contains: query, }, - }); + }, + }); - reply.send({ - tickets: tickets, - success: true, - }); - } + reply.send({ + tickets: tickets, + success: true, + }); } ); @@ -271,32 +267,30 @@ export function ticketRoutes(fastify: FastifyInstance) { const bearer = request.headers.authorization!.split(" ")[1]; const token = checkToken(bearer); - if (token) { - const tickets = await prisma.ticket.findMany({ - where: { hidden: false }, - orderBy: [ - { - createdAt: "desc", - }, - ], - include: { - client: { - select: { id: true, name: true, number: true }, - }, - assignedTo: { - select: { id: true, name: true }, - }, - team: { - select: { id: true, name: true }, - }, + const tickets = await prisma.ticket.findMany({ + where: { hidden: false }, + orderBy: [ + { + createdAt: "desc", }, - }); + ], + include: { + client: { + select: { id: true, name: true, number: true }, + }, + assignedTo: { + select: { id: true, name: true }, + }, + team: { + select: { id: true, name: true }, + }, + }, + }); - reply.send({ - tickets: tickets, - sucess: true, - }); - } + reply.send({ + tickets: tickets, + sucess: true, + }); } ); @@ -305,32 +299,27 @@ export function ticketRoutes(fastify: FastifyInstance) { "/api/v1/tickets/user/open", async (request: FastifyRequest, reply: FastifyReply) => { - const bearer = request.headers.authorization!.split(" ")[1]; - const token = checkToken(bearer); + const user = await checkSession(request); - if (token) { - const user = await checkSession(bearer); - - const tickets = await prisma.ticket.findMany({ - where: { isComplete: false, userId: user!.id, hidden: false }, - include: { - client: { - select: { id: true, name: true, number: true }, - }, - assignedTo: { - select: { id: true, name: true }, - }, - team: { - select: { id: true, name: true }, - }, + const tickets = await prisma.ticket.findMany({ + where: { isComplete: false, userId: user!.id, hidden: false }, + include: { + client: { + select: { id: true, name: true, number: true }, }, - }); + assignedTo: { + select: { id: true, name: true }, + }, + team: { + select: { id: true, name: true }, + }, + }, + }); - reply.send({ - tickets: tickets, - sucess: true, - }); - } + reply.send({ + tickets: tickets, + sucess: true, + }); } ); @@ -339,30 +328,25 @@ export function ticketRoutes(fastify: FastifyInstance) { "/api/v1/tickets/completed", async (request: FastifyRequest, reply: FastifyReply) => { - const bearer = request.headers.authorization!.split(" ")[1]; - const token = checkToken(bearer); - - if (token) { - const tickets = await prisma.ticket.findMany({ - where: { isComplete: true, hidden: false }, - include: { - client: { - select: { id: true, name: true, number: true }, - }, - assignedTo: { - select: { id: true, name: true }, - }, - team: { - select: { id: true, name: true }, - }, + const tickets = await prisma.ticket.findMany({ + where: { isComplete: true, hidden: false }, + include: { + client: { + select: { id: true, name: true, number: true }, }, - }); + assignedTo: { + select: { id: true, name: true }, + }, + team: { + select: { id: true, name: true }, + }, + }, + }); - reply.send({ - tickets: tickets, - sucess: true, - }); - } + reply.send({ + tickets: tickets, + sucess: true, + }); } ); @@ -371,94 +355,83 @@ export function ticketRoutes(fastify: FastifyInstance) { "/api/v1/tickets/unassigned", async (request: FastifyRequest, reply: FastifyReply) => { - const bearer = request.headers.authorization!.split(" ")[1]; - const token = checkToken(bearer); - - if (token) { - const tickets = await prisma.ticket.findMany({ - where: { - isComplete: false, - assignedTo: null, - hidden: false, - }, - }); + const tickets = await prisma.ticket.findMany({ + where: { + isComplete: false, + assignedTo: null, + hidden: false, + }, + }); - reply.send({ - success: true, - tickets: tickets, - }); - } + reply.send({ + success: true, + tickets: tickets, + }); } ); // Update a ticket fastify.put( "/api/v1/ticket/update", - + { + preHandler: requirePermission(["issue::update"]), + }, async (request: FastifyRequest, reply: FastifyReply) => { - const bearer = request.headers.authorization!.split(" ")[1]; - const token = checkToken(bearer); - const { id, note, detail, title, priority, status }: any = request.body; - if (token) { - await prisma.ticket.update({ - where: { id: id }, - data: { - detail, - note, - title, - priority, - status, - }, - }); + await prisma.ticket.update({ + where: { id: id }, + data: { + detail, + note, + title, + priority, + status, + }, + }); - reply.send({ - success: true, - }); - } + reply.send({ + success: true, + }); } ); // Transfer a ticket to another user fastify.post( "/api/v1/ticket/transfer", - + { + preHandler: requirePermission(["issue::transfer"]), + }, async (request: FastifyRequest, reply: FastifyReply) => { - const bearer = request.headers.authorization!.split(" ")[1]; - const token = checkToken(bearer); - const { user, id }: any = request.body; - if (token) { - if (user) { - const assigned = await prisma.user.update({ - where: { id: user }, - data: { - tickets: { - connect: { - id: id, - }, + if (user) { + const assigned = await prisma.user.update({ + where: { id: user }, + data: { + tickets: { + connect: { + id: id, }, }, - }); - - const { email } = assigned; + }, + }); - await sendAssignedEmail(email); - } else { - await prisma.ticket.update({ - where: { id: id }, - data: { - userId: null, - }, - }); - } + const { email } = assigned; - reply.send({ - success: true, + await sendAssignedEmail(email); + } else { + await prisma.ticket.update({ + where: { id: id }, + data: { + userId: null, + }, }); } + + reply.send({ + success: true, + }); } ); @@ -510,189 +483,174 @@ export function ticketRoutes(fastify: FastifyInstance) { // Comment on a ticket fastify.post( "/api/v1/ticket/comment", - + { + preHandler: requirePermission(["issue::comment"]), + }, async (request: FastifyRequest, reply: FastifyReply) => { - const bearer = request.headers.authorization!.split(" ")[1]; - const token = checkToken(bearer); - const { text, id, public: public_comment }: any = request.body; - if (token) { - const user = await checkSession(bearer); + const user = await checkSession(request); - await prisma.comment.create({ - data: { - text: text, - public: public_comment, - ticketId: id, - userId: user!.id, - }, - }); + await prisma.comment.create({ + data: { + text: text, + public: public_comment, + ticketId: id, + userId: user!.id, + }, + }); - const ticket = await prisma.ticket.findUnique({ - where: { - id: id, - }, - }); + const ticket = await prisma.ticket.findUnique({ + where: { + id: id, + }, + }); - //@ts-expect-error - const { email, title } = ticket; - if (public_comment && email) { - sendComment(text, title, ticket!.id, email!); - } + //@ts-expect-error + const { email, title } = ticket; + if (public_comment && email) { + sendComment(text, title, ticket!.id, email!); + } - await commentNotification(user!.id, ticket, user!.name); + await commentNotification(user!.id, ticket, user!.name); - const hog = track(); + const hog = track(); - hog.capture({ - event: "ticket_comment", - distinctId: ticket!.id, - }); + hog.capture({ + event: "ticket_comment", + distinctId: ticket!.id, + }); - reply.send({ - success: true, - }); - } + reply.send({ + success: true, + }); } ); // Update status of a ticket fastify.put( "/api/v1/ticket/status/update", - + { + preHandler: requirePermission(["issue::update"]), + }, async (request: FastifyRequest, reply: FastifyReply) => { - const bearer = request.headers.authorization!.split(" ")[1]; - const token = checkToken(bearer); + const { status, id }: any = request.body; - if (token) { - const { status, id }: any = request.body; + const ticket: any = await prisma.ticket.update({ + where: { id: id }, + data: { + isComplete: status, + }, + }); - const ticket: any = await prisma.ticket.update({ - where: { id: id }, - data: { - isComplete: status, - }, - }); + const webhook = await prisma.webhooks.findMany({ + where: { + type: "ticket_status_changed", + }, + }); - const webhook = await prisma.webhooks.findMany({ - where: { - type: "ticket_status_changed", - }, - }); + for (let i = 0; i < webhook.length; i++) { + const url = webhook[i].url; - for (let i = 0; i < webhook.length; i++) { - const url = webhook[i].url; - - if (webhook[i].active === true) { - const s = status ? "Completed" : "Outstanding"; - if (url.includes("discord.com")) { - const message = { - content: `Ticket ${ticket.id} created by ${ticket.email}, has had it's status changed to ${s}`, - avatar_url: - "https://avatars.githubusercontent.com/u/76014454?s=200&v=4", - username: "Peppermint.sh", - }; - axios - .post(url, message) - .then((response) => { - console.log("Message sent successfully!"); - console.log("Discord API response:", response.data); - }) - .catch((error) => { - console.error("Error sending message:", error); - }); - } else { - await axios.post(`${webhook[i].url}`, { - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - data: `Ticket ${ticket.id} created by ${ticket.email}, has had it's status changed to ${s}`, - }), + if (webhook[i].active === true) { + const s = status ? "Completed" : "Outstanding"; + if (url.includes("discord.com")) { + const message = { + content: `Ticket ${ticket.id} created by ${ticket.email}, has had it's status changed to ${s}`, + avatar_url: + "https://avatars.githubusercontent.com/u/76014454?s=200&v=4", + username: "Peppermint.sh", + }; + axios + .post(url, message) + .then((response) => { + console.log("Message sent successfully!"); + console.log("Discord API response:", response.data); + }) + .catch((error) => { + console.error("Error sending message:", error); }); - } + } else { + await axios.post(`${webhook[i].url}`, { + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + data: `Ticket ${ticket.id} created by ${ticket.email}, has had it's status changed to ${s}`, + }), + }); } } + } - sendTicketStatus(ticket); + sendTicketStatus(ticket); - reply.send({ - success: true, - }); - } + reply.send({ + success: true, + }); } ); // Hide a ticket fastify.put( "/api/v1/ticket/status/hide", - + { + preHandler: requirePermission(["issue::update"]), + }, async (request: FastifyRequest, reply: FastifyReply) => { - const bearer = request.headers.authorization!.split(" ")[1]; - const token = checkToken(bearer); - - if (token) { - const { hidden, id }: any = request.body; + const { hidden, id }: any = request.body; - await prisma.ticket.update({ - where: { id: id }, - data: { - hidden: hidden, - }, - }); + await prisma.ticket.update({ + where: { id: id }, + data: { + hidden: hidden, + }, + }); - reply.send({ - success: true, - }); - } + reply.send({ + success: true, + }); } ); // Lock a ticket fastify.put( "/api/v1/ticket/status/lock", - + { + preHandler: requirePermission(["issue::update"]), + }, async (request: FastifyRequest, reply: FastifyReply) => { - const bearer = request.headers.authorization!.split(" ")[1]; - const token = checkToken(bearer); - - if (token) { - const { locked, id }: any = request.body; + const { locked, id }: any = request.body; - await prisma.ticket.update({ - where: { id: id }, - data: { - locked: locked, - }, - }); + await prisma.ticket.update({ + where: { id: id }, + data: { + locked: locked, + }, + }); - reply.send({ - success: true, - }); - } + reply.send({ + success: true, + }); } ); // Delete a ticket fastify.post( "/api/v1/ticket/delete", - + { + preHandler: requirePermission(["issue::delete"]), + }, async (request: FastifyRequest, reply: FastifyReply) => { - const bearer = request.headers.authorization!.split(" ")[1]; - const token = checkToken(bearer); - - if (token) { - const { id }: any = request.body; + const { id }: any = request.body; - await prisma.ticket.delete({ - where: { id: id }, - }); + await prisma.ticket.delete({ + where: { id: id }, + }); - reply.send({ - success: true, - }); - } + reply.send({ + success: true, + }); } ); @@ -706,80 +664,71 @@ export function ticketRoutes(fastify: FastifyInstance) { // GET all ticket templates fastify.get( "/api/v1/ticket/templates", - + { + preHandler: requirePermission(["email_template::manage"]), + }, async (request: FastifyRequest, reply: FastifyReply) => { - const bearer = request.headers.authorization!.split(" ")[1]; - const token = checkToken(bearer); - - if (token) { - const templates = await prisma.emailTemplate.findMany({ - select: { - createdAt: true, - updatedAt: true, - type: true, - id: true, - }, - }); + const templates = await prisma.emailTemplate.findMany({ + select: { + createdAt: true, + updatedAt: true, + type: true, + id: true, + }, + }); - reply.send({ - success: true, - templates: templates, - }); - } + reply.send({ + success: true, + templates: templates, + }); } ); // GET ticket template by ID fastify.get( "/api/v1/ticket/template/:id", - + { + preHandler: requirePermission(["email_template::manage"]), + }, async (request: FastifyRequest, reply: FastifyReply) => { - const bearer = request.headers.authorization!.split(" ")[1]; - const token = checkToken(bearer); - const { id }: any = request.params; - if (token) { - const template = await prisma.emailTemplate.findMany({ - where: { - id: id, - }, - }); + const template = await prisma.emailTemplate.findMany({ + where: { + id: id, + }, + }); - reply.send({ - success: true, - template: template, - }); - } + reply.send({ + success: true, + template: template, + }); } ); // PUT ticket template by ID fastify.put( "/api/v1/ticket/template/:id", - + { + preHandler: requirePermission(["email_template::manage"]), + }, async (request: FastifyRequest, reply: FastifyReply) => { - const bearer = request.headers.authorization!.split(" ")[1]; - const token = checkToken(bearer); - const { id }: any = request.params; const { html }: any = request.body; - if (token) { - await prisma.emailTemplate.update({ - where: { - id: id, - }, - data: { - html: html, - }, - }); + await prisma.emailTemplate.update({ + where: { + id: id, + }, + data: { + html: html, + }, + }); - reply.send({ - success: true, - }); - } + reply.send({ + success: true, + }); } ); @@ -788,32 +737,27 @@ export function ticketRoutes(fastify: FastifyInstance) { "/api/v1/tickets/user/open/external", async (request: FastifyRequest, reply: FastifyReply) => { - const bearer = request.headers.authorization!.split(" ")[1]; - const token = checkToken(bearer); - - if (token) { - const user = await checkSession(bearer); + const user = await checkSession(request); - const tickets = await prisma.ticket.findMany({ - where: { isComplete: false, email: user!.email, hidden: false }, - include: { - client: { - select: { id: true, name: true, number: true }, - }, - assignedTo: { - select: { id: true, name: true }, - }, - team: { - select: { id: true, name: true }, - }, + const tickets = await prisma.ticket.findMany({ + where: { isComplete: false, email: user!.email, hidden: false }, + include: { + client: { + select: { id: true, name: true, number: true }, }, - }); + assignedTo: { + select: { id: true, name: true }, + }, + team: { + select: { id: true, name: true }, + }, + }, + }); - reply.send({ - tickets: tickets, - sucess: true, - }); - } + reply.send({ + tickets: tickets, + sucess: true, + }); } ); @@ -822,66 +766,58 @@ export function ticketRoutes(fastify: FastifyInstance) { "/api/v1/tickets/user/closed/external", async (request: FastifyRequest, reply: FastifyReply) => { - const bearer = request.headers.authorization!.split(" ")[1]; - const token = checkToken(bearer); - - if (token) { - const user = await checkSession(bearer); + const user = await checkSession(request); - const tickets = await prisma.ticket.findMany({ - where: { isComplete: true, email: user!.email, hidden: false }, - include: { - client: { - select: { id: true, name: true, number: true }, - }, - assignedTo: { - select: { id: true, name: true }, - }, - team: { - select: { id: true, name: true }, - }, + const tickets = await prisma.ticket.findMany({ + where: { isComplete: true, email: user!.email, hidden: false }, + include: { + client: { + select: { id: true, name: true, number: true }, }, - }); + assignedTo: { + select: { id: true, name: true }, + }, + team: { + select: { id: true, name: true }, + }, + }, + }); - reply.send({ - tickets: tickets, - sucess: true, - }); - } + reply.send({ + tickets: tickets, + sucess: true, + }); } ); // Get all tickets for an external user fastify.get( "/api/v1/tickets/user/external", - + { + preHandler: requirePermission(["issue::read"]), + }, async (request: FastifyRequest, reply: FastifyReply) => { - const bearer = request.headers.authorization!.split(" ")[1]; - const token = checkToken(bearer); + const user = await checkSession(request); - if (token) { - const user = await checkSession(bearer); - - const tickets = await prisma.ticket.findMany({ - where: { email: user!.email, hidden: false }, - include: { - client: { - select: { id: true, name: true, number: true }, - }, - assignedTo: { - select: { id: true, name: true }, - }, - team: { - select: { id: true, name: true }, - }, + const tickets = await prisma.ticket.findMany({ + where: { email: user!.email, hidden: false }, + include: { + client: { + select: { id: true, name: true, number: true }, }, - }); + assignedTo: { + select: { id: true, name: true }, + }, + team: { + select: { id: true, name: true }, + }, + }, + }); - reply.send({ - tickets: tickets, - sucess: true, - }); - } + reply.send({ + tickets: tickets, + sucess: true, + }); } ); } diff --git a/apps/api/src/lib/roles.ts b/apps/api/src/lib/roles.ts index cfeba7521..44662276b 100644 --- a/apps/api/src/lib/roles.ts +++ b/apps/api/src/lib/roles.ts @@ -25,6 +25,11 @@ export function hasPermission( requiredPermissions: Permission | Permission[], requireAll: boolean = true ): boolean { + // Admins have all permissions + // if (user?.isAdmin) { + // return true; + // } + // Convert single permission to array for consistent handling const permissions = Array.isArray(requiredPermissions) ? requiredPermissions @@ -69,33 +74,35 @@ export function requirePermission( const user = await checkSession(req); const config = await prisma.config.findFirst(); - if (!config?.roles_active) { - next(); - } + if (config?.roles_active) { + const userWithRoles = user + ? await prisma.user.findUnique({ + where: { id: user.id }, + include: { + roles: true, + }, + }) + : null; + + if (!userWithRoles) { + return res.status(401).send({ + message: "Unauthorized", + success: false, + }); + } + + if (!hasPermission(userWithRoles, requiredPermissions, requireAll)) { + return res.status(401).send({ + message: "You do not have the required permission to access this resource.", + success: false, + status: 403, + }); + } - const userWithRoles = user - ? await prisma.user.findUnique({ - where: { id: user.id }, - include: { - roles: true, - }, - }) - : null; - - // Admins have all permissions - if (user?.isAdmin) { + next(); + } else { next(); } - - if (!userWithRoles) { - throw new Error("User not authenticated"); - } - - if (!hasPermission(userWithRoles, requiredPermissions, requireAll)) { - throw new InsufficientPermissionsError(); - } - - next(); } catch (error) { next(error); } diff --git a/apps/client/@/shadcn/components/forbidden.tsx b/apps/client/@/shadcn/components/forbidden.tsx new file mode 100644 index 000000000..a9e8b24f2 --- /dev/null +++ b/apps/client/@/shadcn/components/forbidden.tsx @@ -0,0 +1,20 @@ +import { toast } from "../hooks/use-toast"; + +const NoPermissions = () => { + toast({ + title: "Unauthorized", + description: "Please check your permissions.", + }); + return ( +
+ You do not have permission to view this page. +
+- Webhooks allow external services to be notified when certain - events happen. When the specified events happen, we'll send - a POST request to each of the URLs you provide. -
-+ Webhooks allow external services to be notified when certain + events happen. When the specified events happen, we'll send + a POST request to each of the URLs you provide. +
+Loading...
} {status === "error" &&Error loading documents.
} - {data && data.notebooks.length === 0 ? ( + {data && data.notebooks && data.notebooks.length === 0 ? (No documents found.