From 64045c5660a0bfab55e5f73543649ebe6f43f7cd Mon Sep 17 00:00:00 2001 From: Jack Andrews <45759583+potts99@users.noreply.github.com> Date: Mon, 27 Nov 2023 00:12:54 +0000 Subject: [PATCH 1/5] Next (#184) * basic onboard * remove ant design * checks * internal users + auth * admin check --- apps/api/src/controllers/auth.ts | 79 ++++-- apps/api/src/controllers/clients.ts | 103 ++++--- apps/api/src/controllers/data.ts | 50 +++- apps/api/src/controllers/notebook.ts | 70 +++-- apps/api/src/controllers/queue.ts | 81 +++--- apps/api/src/controllers/ticket.ts | 210 +++++++------- apps/api/src/controllers/todos.ts | 76 +++--- apps/api/src/controllers/webhooks.ts | 60 ++-- apps/api/src/lib/checks.ts | 20 +- apps/api/src/lib/jwt.ts | 4 +- apps/api/src/main.ts | 3 +- .../20231126212553_onboarding/migration.sql | 2 + apps/api/src/prisma/schema.prisma | 42 +-- apps/client/components/ListTodo/index.js | 20 +- apps/client/components/ResetPassword/index.js | 54 ++-- apps/client/components/UserProfile/index.js | 226 ++++++++------- apps/client/layouts/adminLayout.tsx | 11 +- apps/client/layouts/newLayout.tsx | 2 +- apps/client/package.json | 1 - apps/client/pages/_app.tsx | 9 + apps/client/pages/admin/clients/index.tsx | 9 +- apps/client/pages/admin/clients/new.tsx | 4 + apps/client/pages/admin/email-queues/index.js | 14 +- apps/client/pages/admin/email-queues/new.js | 6 +- apps/client/pages/admin/settings.js | 258 ------------------ apps/client/pages/admin/teams.js | 18 +- apps/client/pages/admin/users/internal/new.js | 10 +- apps/client/pages/admin/webhooks.js | 18 +- apps/client/pages/auth/login.tsx | 6 +- apps/client/pages/index.tsx | 30 -- apps/client/pages/onboarding.tsx | 113 ++++++++ apps/client/pages/settings/password.tsx | 63 +++-- apps/client/pages/settings/profile.tsx | 11 +- apps/client/public/discord.svg | 8 + apps/client/public/swagger.json | 21 -- apps/client/public/version.json | 4 - apps/client/store/session.js | 7 +- 37 files changed, 886 insertions(+), 837 deletions(-) create mode 100644 apps/api/src/prisma/migrations/20231126212553_onboarding/migration.sql delete mode 100644 apps/client/pages/admin/settings.js create mode 100644 apps/client/pages/onboarding.tsx create mode 100644 apps/client/public/discord.svg delete mode 100644 apps/client/public/swagger.json delete mode 100644 apps/client/public/version.json diff --git a/apps/api/src/controllers/auth.ts b/apps/api/src/controllers/auth.ts index 118169152..20e62c5af 100644 --- a/apps/api/src/controllers/auth.ts +++ b/apps/api/src/controllers/auth.ts @@ -98,15 +98,15 @@ export function authRoutes(fastify: FastifyInstance) { throw new Error("Password is not valid"); } - var b64string = "TOMATOSOUP"; - var buf = new Buffer(b64string, "base64"); // Ta-da + var b64string = process.env.SECRET; + var buf = new Buffer(b64string!, "base64"); // Ta-da let token = jwt.sign( { data: { id: user!.id }, }, buf, - { expiresIn: "1d" } + { expiresIn: "7d" } ); await prisma.session.create({ @@ -127,6 +127,7 @@ export function authRoutes(fastify: FastifyInstance) { ticket_status_changed: user!.notify_ticket_status_changed, ticket_comments: user!.notify_ticket_comments, ticket_assigned: user!.notify_ticket_assigned, + firstLogin: user!.firstLogin, }; reply.send({ @@ -140,13 +141,18 @@ export function authRoutes(fastify: FastifyInstance) { fastify.delete( "/api/v1/auth/user/:id", async (request: FastifyRequest, reply: FastifyReply) => { - const { id } = request.params as { id: string }; + const bearer = request.headers.authorization!.split(" ")[1]; + const token = checkToken(bearer); - await prisma.user.delete({ - where: { id }, - }); + if (token) { + const { id } = request.params as { id: string }; + + await prisma.user.delete({ + where: { id }, + }); - reply.send({ success: true }); + reply.send({ success: true }); + } } ); @@ -154,11 +160,6 @@ export function authRoutes(fastify: FastifyInstance) { fastify.get( "/api/v1/auth/profile", async (request: FastifyRequest, reply: FastifyReply) => { - // check token - // see if token exists on session table - // if not, return 401 - // if yes, return user data - const bearer = request.headers.authorization!.split(" ")[1]; const token = checkToken(bearer); @@ -210,8 +211,6 @@ export function authRoutes(fastify: FastifyInstance) { }; const bearer = request.headers.authorization!.split(" ")[1]; - - //checks if token is valid and returns valid token const token = checkToken(bearer); if (token) { @@ -245,7 +244,41 @@ export function authRoutes(fastify: FastifyInstance) { fastify.put( "/api/v1/auth/profile", async (request: FastifyRequest, reply: FastifyReply) => { - // + const bearer = request.headers.authorization!.split(" ")[1]; + + //checks if token is valid and returns valid token + const token = checkToken(bearer); + + if (token) { + let session = await prisma.session.findUnique({ + where: { + sessionToken: bearer, + }, + }); + + const { name, email, language } = request.body as { + name: string; + email: string; + language: string; + }; + + let user = await prisma.user.update({ + where: { id: session?.userId }, + data: { + name: name, + email: email, + language: language, + }, + }); + + reply.send({ + user, + }); + } else { + reply.send({ + sucess: false, + }); + } } ); @@ -253,13 +286,17 @@ export function authRoutes(fastify: FastifyInstance) { fastify.get( "/api/v1/auth/user/:id/logout", async (request: FastifyRequest, reply: FastifyReply) => { - const { id } = request.params as { id: string }; + const bearer = request.headers.authorization!.split(" ")[1]; + const token = checkToken(bearer); + if (token) { + const { id } = request.params as { id: string }; - await prisma.session.deleteMany({ - where: { userId: id }, - }); + await prisma.session.deleteMany({ + where: { userId: id }, + }); - reply.send({ success: true }); + reply.send({ success: true }); + } } ); } diff --git a/apps/api/src/controllers/clients.ts b/apps/api/src/controllers/clients.ts index fa1868491..c3a9b63a5 100644 --- a/apps/api/src/controllers/clients.ts +++ b/apps/api/src/controllers/clients.ts @@ -1,4 +1,5 @@ import { FastifyInstance, FastifyReply, FastifyRequest } from "fastify"; +import { checkToken } from "../lib/jwt"; import { prisma } from "../prisma"; export function clientRoutes(fastify: FastifyInstance) { @@ -7,20 +8,25 @@ export function clientRoutes(fastify: FastifyInstance) { "/api/v1/client/create", async (request: FastifyRequest, reply: FastifyReply) => { - const { name, email, number, contactName }: any = request.body; - - await prisma.client.create({ - data: { - name, - contactName, - email, - number: String(number), - }, - }); - - reply.send({ - success: true, - }); + const bearer = request.headers.authorization!.split(" ")[1]; + const token = checkToken(bearer); + + if (token) { + const { name, email, number, contactName }: any = request.body; + + await prisma.client.create({ + data: { + name, + contactName, + email, + number: String(number), + }, + }); + + reply.send({ + success: true, + }); + } } ); @@ -29,21 +35,26 @@ export function clientRoutes(fastify: FastifyInstance) { "/api/v1/client/update", async (request: FastifyRequest, reply: FastifyReply) => { - const { name, email, number, contactName, id }: any = request.body; - - await prisma.client.update({ - where: { id: id }, - data: { - name, - contactName, - email, - number: String(number), - }, - }); - - reply.send({ - success: true, - }); + const bearer = request.headers.authorization!.split(" ")[1]; + const token = checkToken(bearer); + + if (token) { + const { name, email, number, contactName, id }: any = request.body; + + await prisma.client.update({ + where: { id: id }, + data: { + name, + contactName, + email, + number: String(number), + }, + }); + + reply.send({ + success: true, + }); + } } ); @@ -52,12 +63,17 @@ export function clientRoutes(fastify: FastifyInstance) { "/api/v1/clients/all", async (request: FastifyRequest, reply: FastifyReply) => { - const clients = await prisma.client.findMany({}); + const bearer = request.headers.authorization!.split(" ")[1]; + const token = checkToken(bearer); - reply.send({ - success: true, - clients: clients, - }); + if (token) { + const clients = await prisma.client.findMany({}); + + reply.send({ + success: true, + clients: clients, + }); + } } ); @@ -66,15 +82,20 @@ export function clientRoutes(fastify: FastifyInstance) { "/api/v1/clients/:id/delete-client", async (request: FastifyRequest, reply: FastifyReply) => { - const { id }: any = request.params; + const bearer = request.headers.authorization!.split(" ")[1]; + const token = checkToken(bearer); + + if (token) { + const { id }: any = request.params; - await prisma.client.delete({ - where: { id: id }, - }); + await prisma.client.delete({ + where: { id: id }, + }); - reply.send({ - success: true, - }); + reply.send({ + success: true, + }); + } } ); } diff --git a/apps/api/src/controllers/data.ts b/apps/api/src/controllers/data.ts index a8d3ba361..e3c6e69b2 100644 --- a/apps/api/src/controllers/data.ts +++ b/apps/api/src/controllers/data.ts @@ -1,4 +1,5 @@ import { FastifyInstance, FastifyReply, FastifyRequest } from "fastify"; +import { checkToken } from "../lib/jwt"; import { prisma } from "../prisma"; export function dataRoutes(fastify: FastifyInstance) { @@ -7,8 +8,14 @@ export function dataRoutes(fastify: FastifyInstance) { "/api/v1/data/tickets/all", async (request: FastifyRequest, reply: FastifyReply) => { - // check jwt is valid - // check user is admin + const bearer = request.headers.authorization!.split(" ")[1]; + const token = checkToken(bearer); + + if (token) { + const result = await prisma.ticket.count(); + + reply.send({ count: result }); + } } ); @@ -17,11 +24,16 @@ export function dataRoutes(fastify: FastifyInstance) { "/api/v1/data/tickets/completed", async (request: FastifyRequest, reply: FastifyReply) => { - const result = await prisma.ticket.count({ - where: { isComplete: true }, - }); + const bearer = request.headers.authorization!.split(" ")[1]; + const token = checkToken(bearer); + + if (token) { + const result = await prisma.ticket.count({ + where: { isComplete: true }, + }); - reply.send({ count: result }); + reply.send({ count: result }); + } } ); @@ -30,11 +42,16 @@ export function dataRoutes(fastify: FastifyInstance) { "/api/v1/data/tickets/open", async (request: FastifyRequest, reply: FastifyReply) => { - const result = await prisma.ticket.count({ - where: { isComplete: false }, - }); + const bearer = request.headers.authorization!.split(" ")[1]; + const token = checkToken(bearer); - reply.send({ count: result }); + if (token) { + const result = await prisma.ticket.count({ + where: { isComplete: false }, + }); + + reply.send({ count: result }); + } } ); @@ -43,11 +60,16 @@ export function dataRoutes(fastify: FastifyInstance) { "/api/v1/data/tickets/unassigned", async (request: FastifyRequest, reply: FastifyReply) => { - const result = await prisma.ticket.count({ - where: { userId: null }, - }); + const bearer = request.headers.authorization!.split(" ")[1]; + const token = checkToken(bearer); + + if (token) { + const result = await prisma.ticket.count({ + where: { userId: null }, + }); - reply.send({ count: result }); + reply.send({ count: result }); + } } ); } diff --git a/apps/api/src/controllers/notebook.ts b/apps/api/src/controllers/notebook.ts index 13f601ab9..0452452a4 100644 --- a/apps/api/src/controllers/notebook.ts +++ b/apps/api/src/controllers/notebook.ts @@ -17,19 +17,21 @@ export function notebookRoutes(fastify: FastifyInstance) { if (!title) { return reply.status(422).send({ error: "Please add a title" }); } else { - const user = await checkSession(bearer); + if (token) { + const user = await checkSession(bearer); - const data = await prisma.notes.create({ - data: { - title, - note: content, - userId: user!.id, - }, - }); + const data = await prisma.notes.create({ + data: { + title, + note: content, + userId: user!.id, + }, + }); - const { id } = data; + const { id } = data; - reply.status(200).send({ success: true, id }); + reply.status(200).send({ success: true, id }); + } } } ); @@ -41,13 +43,16 @@ export function notebookRoutes(fastify: FastifyInstance) { async (request: FastifyRequest, reply: FastifyReply) => { const bearer = request.headers.authorization!.split(" ")[1]; const token = checkToken(bearer); - const user = await checkSession(bearer); - const notebooks = await prisma.notes.findMany({ - where: { userId: user!.id }, - }); + if (token) { + const user = await checkSession(bearer); + + const notebooks = await prisma.notes.findMany({ + where: { userId: user!.id }, + }); - reply.status(200).send({ success: true, notebooks: notebooks }); + reply.status(200).send({ success: true, notebooks: notebooks }); + } } ); @@ -58,19 +63,40 @@ export function notebookRoutes(fastify: FastifyInstance) { async (request: FastifyRequest, reply: FastifyReply) => { const bearer = request.headers.authorization!.split(" ")[1]; const token = checkToken(bearer); - const user = await checkSession(bearer); - const { id }: any = request.params; + if (token) { + const user = await checkSession(bearer); + + const { id }: any = request.params; - const note = await prisma.notes.findUnique({ - where: { userId: user!.id, id: id }, - }); + const note = await prisma.notes.findUnique({ + where: { userId: user!.id, id: id }, + }); - reply.status(200).send({ success: true, note }); + reply.status(200).send({ success: true, note }); + } } ); // Delete an entry + fastify.delete( + "/api/v1/notebooks/note/:id/delete", + + async (request: FastifyRequest, reply: FastifyReply) => { + const { id }: any = request.params; + + const bearer = request.headers.authorization!.split(" ")[1]; + const token = checkToken(bearer); + + if (token) { + await prisma.notes.delete({ + where: { id: id }, + }); + + reply.status(200).send({ success: true }); + } + } + ); // Update an entry fastify.put( @@ -84,7 +110,7 @@ export function notebookRoutes(fastify: FastifyInstance) { const token = checkToken(bearer); if (token) { - const user = await checkSession(bearer); + await checkSession(bearer); await prisma.notes.update({ where: { id: id }, diff --git a/apps/api/src/controllers/queue.ts b/apps/api/src/controllers/queue.ts index 12171df17..ae7dcdaa7 100644 --- a/apps/api/src/controllers/queue.ts +++ b/apps/api/src/controllers/queue.ts @@ -1,4 +1,5 @@ import { FastifyInstance, FastifyReply, FastifyRequest } from "fastify"; +import { checkToken } from "../lib/jwt"; import { prisma } from "../prisma"; export function emailQueueRoutes(fastify: FastifyInstance) { @@ -7,21 +8,26 @@ export function emailQueueRoutes(fastify: FastifyInstance) { "/api/v1/email-queue/create", async (request: FastifyRequest, reply: FastifyReply) => { - const { name, username, password, hostname, tls }: any = request.body; - - await prisma.emailQueue.create({ - data: { - name, - username, - password, - hostname, - tls, - }, - }); - - reply.send({ - success: true, - }); + const bearer = request.headers.authorization!.split(" ")[1]; + const token = checkToken(bearer); + + if (token) { + const { name, username, password, hostname, tls }: any = request.body; + + await prisma.emailQueue.create({ + data: { + name, + username, + password, + hostname, + tls, + }, + }); + + reply.send({ + success: true, + }); + } } ); @@ -31,14 +37,17 @@ export function emailQueueRoutes(fastify: FastifyInstance) { "/api/v1/email-queues/all", async (request: FastifyRequest, reply: FastifyReply) => { - // check jwt is valid - // check user is admin - const queues = await prisma.emailQueue.findMany({}); - - reply.send({ - success: true, - queues: queues, - }); + const bearer = request.headers.authorization!.split(" ")[1]; + const token = checkToken(bearer); + + if (token) { + const queues = await prisma.emailQueue.findMany({}); + + reply.send({ + success: true, + queues: queues, + }); + } } ); @@ -47,16 +56,22 @@ export function emailQueueRoutes(fastify: FastifyInstance) { "/api/v1/email-queue/delete", async (request: FastifyRequest, reply: FastifyReply) => { - const { id }: any = request.body; - const queues = await prisma.emailQueue.delete({ - where: { - id: id, - }, - }); - - reply.send({ - success: true, - }); + const bearer = request.headers.authorization!.split(" ")[1]; + const token = checkToken(bearer); + + if (token) { + const { id }: any = request.params; + + await prisma.emailQueue.delete({ + where: { + id: id, + }, + }); + + reply.send({ + success: true, + }); + } } ); } diff --git a/apps/api/src/controllers/ticket.ts b/apps/api/src/controllers/ticket.ts index f1aeff869..febb19d03 100644 --- a/apps/api/src/controllers/ticket.ts +++ b/apps/api/src/controllers/ticket.ts @@ -22,8 +22,6 @@ export function ticketRoutes(fastify: FastifyInstance) { type, }: any = request.body; - console.log(request.body); - const ticket: any = await prisma.ticket.create({ data: { name, @@ -146,7 +144,6 @@ export function ticketRoutes(fastify: FastifyInstance) { // Get all tickets fastify.get( "/api/v1/tickets/open", - async (request: FastifyRequest, reply: FastifyReply) => { const bearer = request.headers.authorization!.split(" ")[1]; const token = checkToken(bearer); @@ -300,68 +297,73 @@ export function ticketRoutes(fastify: FastifyInstance) { "/api/v1/ticket/transfer", async (request: FastifyRequest, reply: FastifyReply) => { + const bearer = request.headers.authorization!.split(" ")[1]; + const token = checkToken(bearer); + const { user, id }: any = request.body; - await prisma.user.update({ - where: { id: user }, - data: { - tickets: { - connect: { - id: id, + if (token) { + await prisma.user.update({ + where: { id: user }, + data: { + tickets: { + connect: { + id: id, + }, }, }, - }, - }); + }); - reply.send({ - success: true, - }); + reply.send({ + success: true, + }); + } } ); // Link a ticket to another ticket - fastify.post( - "/api/v1/ticket/link", - - async (request: FastifyRequest, reply: FastifyReply) => { - const { ticket, id }: any = request.body; - - const prev: any = await prisma.ticket.findUnique({ - where: { - id: id, - }, - }); - - const ids = []; - - if (prev.length !== undefined && prev.linked.length > 0) { - ids.push(...prev.linked); - } - - ids.push({ - id: ticket.id, - title: ticket.title, - }); - - const data = await prisma.ticket.update({ - where: { - id: id, - }, - data: { - linked: { - ...ids, - }, - }, - }); - } - ); + // fastify.post( + // "/api/v1/ticket/link", + + // async (request: FastifyRequest, reply: FastifyReply) => { + // const { ticket, id }: any = request.body; + + // const prev: any = await prisma.ticket.findUnique({ + // where: { + // id: id, + // }, + // }); + + // const ids = []; + + // if (prev.length !== undefined && prev.linked.length > 0) { + // ids.push(...prev.linked); + // } + + // ids.push({ + // id: ticket.id, + // title: ticket.title, + // }); + + // const data = await prisma.ticket.update({ + // where: { + // id: id, + // }, + // data: { + // linked: { + // ...ids, + // }, + // }, + // }); + // } + // ); // Unlink a ticket from another ticket - fastify.post( - "/api/v1/ticket/unlink", + // fastify.post( + // "/api/v1/ticket/unlink", - async (request: FastifyRequest, reply: FastifyReply) => {} - ); + // async (request: FastifyRequest, reply: FastifyReply) => {} + // ); // Comment on a ticket fastify.post( @@ -397,44 +399,49 @@ export function ticketRoutes(fastify: FastifyInstance) { "/api/v1/ticket/status/update", async (request: FastifyRequest, reply: FastifyReply) => { - const { status, id }: any = request.body; - - const ticket: any = await prisma.ticket - .update({ - where: { id: id }, - data: { - isComplete: status, - }, - }) - .then(async (ticket) => { - // await sendTicketStatus(ticket); - }); + const bearer = request.headers.authorization!.split(" ")[1]; + const token = checkToken(bearer); - const webhook = await prisma.webhooks.findMany({ - where: { - type: "ticket_status_changed", - }, - }); + if (token) { + const { status, id }: any = request.body; - for (let i = 0; i < webhook.length; i++) { - if (webhook[i].active === true) { - const s = status ? "Completed" : "Outstanding"; - await axios.post(`${webhook[i].url}`, { - method: "POST", - headers: { - "Content-Type": "application/json", + const ticket: any = await prisma.ticket + .update({ + where: { id: id }, + data: { + isComplete: status, }, - body: JSON.stringify({ - data: `Ticket ${ticket.id} created by ${ticket.email}, has had it's status changed to ${s}`, - }), - redirect: "follow", + }) + .then(async (ticket) => { + // await sendTicketStatus(ticket); }); + + const webhook = await prisma.webhooks.findMany({ + where: { + type: "ticket_status_changed", + }, + }); + + for (let i = 0; i < webhook.length; i++) { + if (webhook[i].active === true) { + const s = status ? "Completed" : "Outstanding"; + await axios.post(`${webhook[i].url}`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + data: `Ticket ${ticket.id} created by ${ticket.email}, has had it's status changed to ${s}`, + }), + redirect: "follow", + }); + } } - } - reply.send({ - success: true, - }); + reply.send({ + success: true, + }); + } } ); @@ -443,22 +450,27 @@ export function ticketRoutes(fastify: FastifyInstance) { "/api/v1/ticket/status/hide", async (request: FastifyRequest, reply: FastifyReply) => { - const { hidden, id }: any = request.body; + const bearer = request.headers.authorization!.split(" ")[1]; + const token = checkToken(bearer); - await prisma.ticket - .update({ - where: { id: id }, - data: { - hidden: hidden, - }, - }) - .then(async (ticket) => { - // await sendTicketStatus(ticket); - }); + if (token) { + const { hidden, id }: any = request.body; - reply.send({ - success: true, - }); + await prisma.ticket + .update({ + where: { id: id }, + data: { + hidden: hidden, + }, + }) + .then(async (ticket) => { + // await sendTicketStatus(ticket); + }); + + reply.send({ + success: true, + }); + } } ); diff --git a/apps/api/src/controllers/todos.ts b/apps/api/src/controllers/todos.ts index 9083679d5..10f859a9c 100644 --- a/apps/api/src/controllers/todos.ts +++ b/apps/api/src/controllers/todos.ts @@ -30,20 +30,22 @@ export function todoRoutes(fastify: FastifyInstance) { console.log("No text found!"); reply.status(400).send({ success: false, message: "No text found!" }); } else { - const user = await checkSession(bearer); - - if (user) { - await prisma.todos.create({ - data: { - text: todo, - userId: user!.id, - }, - }); - reply.send({ success: true, message: "Todo created!" }); - } else { - reply - .status(400) - .send({ success: false, message: "User not found!" }); + if (token) { + const user = await checkSession(bearer); + + if (user) { + await prisma.todos.create({ + data: { + text: todo, + userId: user!.id, + }, + }); + reply.send({ success: true, message: "Todo created!" }); + } else { + reply + .status(400) + .send({ success: false, message: "User not found!" }); + } } } } @@ -55,11 +57,16 @@ export function todoRoutes(fastify: FastifyInstance) { "/api/v1/todos/all", async (request: FastifyRequest, reply: FastifyReply) => { - const todos = await prisma.todos.findMany({}); + const bearer = request.headers.authorization!.split(" ")[1]; + const token = checkToken(bearer); + + if (token) { + const todos = await prisma.todos.findMany({}); - reply.send({ - todos: todos, - }); + reply.send({ + todos: todos, + }); + } } ); @@ -68,24 +75,29 @@ export function todoRoutes(fastify: FastifyInstance) { "/api/v1/todo/:id/delete", async (request: FastifyRequest, reply: FastifyReply) => { - const { id } = request.params as { id: string }; + const bearer = request.headers.authorization!.split(" ")[1]; + const token = checkToken(bearer); - const todo = await doesTodoExist(id); + if (token) { + const { id } = request.params as { id: string }; - if (!todo) { - return reply.status(404).send({ - success: false, - error: "Todo not found.", - }); - } + const todo = await doesTodoExist(id); + + if (!todo) { + return reply.status(404).send({ + success: false, + error: "Todo not found.", + }); + } - await prisma.todos.delete({ - where: { - id: id, - }, - }); + await prisma.todos.delete({ + where: { + id: id, + }, + }); - reply.status(201).send({ success: true, message: "Todo deleted" }); + reply.status(201).send({ success: true, message: "Todo deleted" }); + } } ); } diff --git a/apps/api/src/controllers/webhooks.ts b/apps/api/src/controllers/webhooks.ts index 5cecfecb6..0da80efc0 100644 --- a/apps/api/src/controllers/webhooks.ts +++ b/apps/api/src/controllers/webhooks.ts @@ -1,4 +1,5 @@ import { FastifyInstance, FastifyReply, FastifyRequest } from "fastify"; +import { checkToken } from "../lib/jwt"; import { prisma } from "../prisma"; export function webhookRoutes(fastify: FastifyInstance) { @@ -7,18 +8,23 @@ export function webhookRoutes(fastify: FastifyInstance) { "/api/v1/webhook/create", async (request: FastifyRequest, reply: FastifyReply) => { - const { name, url, type, active, secret }: any = request.body; - await prisma.webhooks.create({ - data: { - name, - url, - type, - active, - secret, - createdBy: "375f7799-5485-40ff-ba8f-0a28e0855ecf", - }, - }); - reply.status(200).send({ message: "Hook created!", success: true }); + const bearer = request.headers.authorization!.split(" ")[1]; + const token = checkToken(bearer); + + if (token) { + const { name, url, type, active, secret }: any = request.body; + await prisma.webhooks.create({ + data: { + name, + url, + type, + active, + secret, + createdBy: "375f7799-5485-40ff-ba8f-0a28e0855ecf", + }, + }); + reply.status(200).send({ message: "Hook created!", success: true }); + } } ); @@ -27,9 +33,14 @@ export function webhookRoutes(fastify: FastifyInstance) { "/api/v1/webhooks/all", async (request: FastifyRequest, reply: FastifyReply) => { - const webhooks = await prisma.webhooks.findMany({}); + const bearer = request.headers.authorization!.split(" ")[1]; + const token = checkToken(bearer); + + if (token) { + const webhooks = await prisma.webhooks.findMany({}); - reply.status(200).send({ webhooks: webhooks, success: true }); + reply.status(200).send({ webhooks: webhooks, success: true }); + } } ); @@ -39,14 +50,19 @@ export function webhookRoutes(fastify: FastifyInstance) { "/api/v1/admin/webhook/:id/delete", async (request: FastifyRequest, reply: FastifyReply) => { - const { id }: any = request.params; - await prisma.webhooks.delete({ - where: { - id: id, - }, - }); - - reply.status(200).send({ success: true }); + const bearer = request.headers.authorization!.split(" ")[1]; + const token = checkToken(bearer); + + if (token) { + const { id }: any = request.params; + await prisma.webhooks.delete({ + where: { + id: id, + }, + }); + + reply.status(200).send({ success: true }); + } } ); } diff --git a/apps/api/src/lib/checks.ts b/apps/api/src/lib/checks.ts index 36e2314c9..985e707da 100644 --- a/apps/api/src/lib/checks.ts +++ b/apps/api/src/lib/checks.ts @@ -1 +1,19 @@ -// is Admin +import { FastifyReply, FastifyRequest } from "fastify"; +import { checkToken } from "./jwt"; + +// Check valid token +export const authenticateUser = ( + request: FastifyRequest, + reply: FastifyReply, + done: any +) => { + const bearer = request.headers.authorization!.split(" ")[1]; + const token = checkToken(bearer); + + if (!token) { + return reply.code(401).send({ error: "Unauthorized" }); + } + + // User is authenticated, continue to the route handler + done(); +}; diff --git a/apps/api/src/lib/jwt.ts b/apps/api/src/lib/jwt.ts index 1c2ac987e..2fa1779b2 100644 --- a/apps/api/src/lib/jwt.ts +++ b/apps/api/src/lib/jwt.ts @@ -3,8 +3,8 @@ import jwt from "jsonwebtoken"; export function checkToken(token: string) { const bearer = token; - var b64string = "TOMATOSOUP"; - var buf = new Buffer(b64string, "base64"); // Ta-da + var b64string = process.env.SECRET; + var buf = new Buffer(b64string!, "base64"); // Ta-da const verified = jwt.verify(bearer, buf); diff --git a/apps/api/src/main.ts b/apps/api/src/main.ts index bc4861bc9..4405a4e45 100644 --- a/apps/api/src/main.ts +++ b/apps/api/src/main.ts @@ -70,8 +70,7 @@ server.register(import("@fastify/rate-limit"), { registerRoutes(server); server.get("/", async function (request, response) { - const session = await prisma.session.findMany(); - response.send({ session }); + response.send({ healthy: true }); }); const start = async () => { diff --git a/apps/api/src/prisma/migrations/20231126212553_onboarding/migration.sql b/apps/api/src/prisma/migrations/20231126212553_onboarding/migration.sql new file mode 100644 index 000000000..53c41c469 --- /dev/null +++ b/apps/api/src/prisma/migrations/20231126212553_onboarding/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "User" ADD COLUMN "firstLogin" BOOLEAN NOT NULL DEFAULT true; diff --git a/apps/api/src/prisma/schema.prisma b/apps/api/src/prisma/schema.prisma index fa54db79e..9e1226fc5 100644 --- a/apps/api/src/prisma/schema.prisma +++ b/apps/api/src/prisma/schema.prisma @@ -46,30 +46,32 @@ model Session { } model User { - id String @id @default(uuid()) - createdAt DateTime @default(now()) - updatedAt DateTime @default(now()) + id String @id @default(uuid()) + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) name String password String? - email String @unique + email String @unique image String? emailVerified Boolean? - isAdmin Boolean @default(false) - language String? @default("en") - notify_ticket_created Boolean @default(true) - notify_ticket_status_changed Boolean @default(true) - notify_ticket_comments Boolean @default(true) - notify_ticket_assigned Boolean @default(true) - todos Todos[] - tickets Ticket[] - file UserFile[] - notes Notes[] - Team Team? @relation(fields: [teamId], references: [id]) - teamId String? - Comment Comment[] - Account Account[] - Session Session[] - TimeTracking TimeTracking[] + isAdmin Boolean @default(false) + language String? @default("en") + notify_ticket_created Boolean @default(true) + notify_ticket_status_changed Boolean @default(true) + notify_ticket_comments Boolean @default(true) + notify_ticket_assigned Boolean @default(true) + firstLogin Boolean @default(true) + + todos Todos[] + tickets Ticket[] + file UserFile[] + notes Notes[] + Team Team? @relation(fields: [teamId], references: [id]) + teamId String? + Comment Comment[] + Account Account[] + Session Session[] + TimeTracking TimeTracking[] } model VerificationToken { diff --git a/apps/client/components/ListTodo/index.js b/apps/client/components/ListTodo/index.js index 7f26f8b39..c203f9b4b 100644 --- a/apps/client/components/ListTodo/index.js +++ b/apps/client/components/ListTodo/index.js @@ -1,16 +1,18 @@ import { TrashIcon } from "@heroicons/react/20/solid"; -import { Pagination } from "antd"; import { getCookie } from "cookies-next"; import { useState } from "react"; import { useQuery } from "react-query"; async function getTodos(token) { - const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/v1/todos/all`, { - method: "GET", - headers: { - Authorization: `Bearer ${token}`, - }, - }); + const res = await fetch( + `${process.env.NEXT_PUBLIC_API_URL}/api/v1/todos/all`, + { + method: "GET", + headers: { + Authorization: `Bearer ${token}`, + }, + } + ); return res.json(); } @@ -125,13 +127,13 @@ export default function ListTodo() {

None Found

)} -
12 ? "mt-4" : "hidden"}> + {/*
12 ? "mt-4" : "hidden"}> -
+
*/} )} diff --git a/apps/client/components/ResetPassword/index.js b/apps/client/components/ResetPassword/index.js index f5a332f51..40d3f8010 100644 --- a/apps/client/components/ResetPassword/index.js +++ b/apps/client/components/ResetPassword/index.js @@ -1,6 +1,5 @@ import { Dialog, Transition } from "@headlessui/react"; import { XMarkIcon } from "@heroicons/react/24/outline"; -import { message } from "antd"; import React, { Fragment, useState } from "react"; export default function ResetPassword({ user }) { @@ -8,37 +7,46 @@ export default function ResetPassword({ user }) { const [password, setPassword] = useState(""); const [check, setCheck] = useState(""); - const success = () => { - message.success("Password updated"); - }; - - const fail = (f) => { - message.error(`${f}`); - }; - const postData = async () => { - const id = user.id; if (check === password && password.length > 0) { - await fetch(`/api/v1/admin/user/resetpassword`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - password, - id, - }), - }) + await fetch( + `${process.env.NEXT_PUBLIC_API_URL}/api/v1/auth/reset-password`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: "Bearer " + token, + }, + body: JSON.stringify({ + password, + }), + } + ) .then((res) => res.json()) .then((res) => { if (res.failed === false) { - success(); + notifications.show({ + title: "Success", + message: `Password updated :)`, + color: "green", + autoClose: 5000, + }); } else { - fail(res.message); + notifications.show({ + title: "Error", + message: `Error: ${res.message}`, + color: "red", + autoClose: 5000, + }); } }); } else { - fail("Passwords are not the same or empty"); + notifications.show({ + title: "Error", + message: `Error: passwords do not match`, + color: "red", + autoClose: 5000, + }); } }; diff --git a/apps/client/components/UserProfile/index.js b/apps/client/components/UserProfile/index.js index e0ee09ec8..f1f5746cd 100644 --- a/apps/client/components/UserProfile/index.js +++ b/apps/client/components/UserProfile/index.js @@ -1,138 +1,128 @@ -import React, { useState } from "react"; -import { message } from "antd"; import { useSession } from "next-auth/react"; import { useRouter } from "next/router"; - +import React, { useState } from "react"; export function UserProfile() { + const { data: session } = useSession(); - const { data: session } = useSession(); - - const router = useRouter(); + const router = useRouter(); - const [name, setName] = useState(); - const [email, setEmail] = useState(); - const [language, setLanguage] = useState(); + const [name, setName] = useState(); + const [email, setEmail] = useState(); + const [language, setLanguage] = useState(); - const success = () => { - message.success("Information updated!"); - }; + function changeLanguage(locale) { + setLanguage(locale); + router.push(router.pathname, router.asPath, { + locale, + }); + } - const fail = () => { - message.error("Information failed to update"); - }; + async function updateProfile() { + await fetch("/api/v1/users/profile/edit", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + id: session.user.id, + name: name ? name : session.user.name, + email: email ? email : session.user.email, + language: language ? language : session.user.language, + }), + }); + } - function changeLanguage(locale) { - setLanguage(locale) - router.push(router.pathname, router.asPath, { - locale, - }); - } - - async function updateProfile() { - await fetch("/api/v1/users/profile/edit", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - 'id': session.user.id, - 'name': name ? name : session.user.name, - 'email': email ? email : session.user.email, - 'language': language ? language : session.user.language, - }), - }); - } - - return ( - <> -
-
-

Profile

-

- This information will be displayed publicly so be careful what you - share. -

-
+ return ( + <> +
+
+

+ Profile +

+

+ This information will be displayed publicly so be careful what you + share. +

+
-
-
-
- -
- setName(e.target.value)} - /> -
+
+
+
+ +
+ setName(e.target.value)} + />
+
-
- -
- setEmail(e.target.value)} - /> -
+
+ +
+ setEmail(e.target.value)} + />
-
-
-
+
-
- -
- - ) +
+ +
+ + ); } diff --git a/apps/client/layouts/adminLayout.tsx b/apps/client/layouts/adminLayout.tsx index 38680cf05..01ab42a3f 100644 --- a/apps/client/layouts/adminLayout.tsx +++ b/apps/client/layouts/adminLayout.tsx @@ -1,6 +1,7 @@ import useTranslation from "next-translate/useTranslation"; import Link from "next/link"; import { useRouter } from "next/router"; +import { useUser } from "../store/session"; function classNames(...classes: any) { return classes.filter(Boolean).join(" "); @@ -10,7 +11,15 @@ export default function AdminLayout({ children }: any) { const { t, lang } = useTranslation("peppermint"); const router = useRouter(); - console.log("router.pathname", router.pathname); + const { user } = useUser(); + + if (user || user.role === "admin") { + return ( +
+

You are not an admin

+
+ ); + } const navigation = [ { diff --git a/apps/client/layouts/newLayout.tsx b/apps/client/layouts/newLayout.tsx index da008a2b1..75a0f1be0 100644 --- a/apps/client/layouts/newLayout.tsx +++ b/apps/client/layouts/newLayout.tsx @@ -544,7 +544,7 @@ export default function NewLayout({ children }: any) { {({ active }) => ( + + + + ); + } + return ( diff --git a/apps/client/pages/admin/clients/index.tsx b/apps/client/pages/admin/clients/index.tsx index b560f971f..a9b11776c 100644 --- a/apps/client/pages/admin/clients/index.tsx +++ b/apps/client/pages/admin/clients/index.tsx @@ -1,3 +1,4 @@ +import { getCookie } from "cookies-next"; import Link from "next/link"; import { useRouter } from "next/router"; import React from "react"; @@ -13,7 +14,13 @@ import { const fetchAllClients = async () => { const res = await fetch( - `${process.env.NEXT_PUBLIC_API_URL}/api/v1/clients/all` + `${process.env.NEXT_PUBLIC_API_URL}/api/v1/clients/all`, + { + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${getCookie("session")}`, + }, + } ); return res.json(); }; diff --git a/apps/client/pages/admin/clients/new.tsx b/apps/client/pages/admin/clients/new.tsx index c687eda39..04c2c3321 100644 --- a/apps/client/pages/admin/clients/new.tsx +++ b/apps/client/pages/admin/clients/new.tsx @@ -1,10 +1,13 @@ import { notifications } from "@mantine/notifications"; +import { getCookie } from "cookies-next"; import { useRouter } from "next/router"; import { useState } from "react"; export default function CreateClientPage() { const router = useRouter(); + const token = getCookie("session"); + const [number, setNumber] = useState(""); const [contactName, setContactName] = useState(""); const [name, setName] = useState(""); @@ -20,6 +23,7 @@ export default function CreateClientPage() { method: "POST", headers: { "Content-Type": "application/json", + Authorization: "Bearer " + token, }, body: JSON.stringify({ number, diff --git a/apps/client/pages/admin/email-queues/index.js b/apps/client/pages/admin/email-queues/index.js index b420f0e2b..b9f2be136 100644 --- a/apps/client/pages/admin/email-queues/index.js +++ b/apps/client/pages/admin/email-queues/index.js @@ -1,16 +1,17 @@ +import { getCookie } from "cookies-next"; import Link from "next/link"; import { useEffect, useState } from "react"; export default function EmailQueues() { const [queues, setQueues] = useState(); - // fetch queues / display them - // create a new queue - async function fetchQueues() { - const res = await fetch("/api/v1/admin/email-queue/check").then((res) => - res.json() - ); + const res = await fetch("/api/v1/admin/email-queue/check", { + headers: { + "Content-Type": "application/json", + Authorization: "Bearer " + getCookie("session"), + }, + }).then((res) => res.json()); setQueues(res.queues); } @@ -19,6 +20,7 @@ export default function EmailQueues() { method: "post", headers: { "Content-Type": "application/json", + Authorization: "Bearer " + getCookie("session"), }, body: JSON.stringify({ id, diff --git a/apps/client/pages/admin/email-queues/new.js b/apps/client/pages/admin/email-queues/new.js index 9076e0c04..fdc6086df 100644 --- a/apps/client/pages/admin/email-queues/new.js +++ b/apps/client/pages/admin/email-queues/new.js @@ -1,5 +1,6 @@ -import { useState, useEffect } from "react"; +import { getCookie } from "cookies-next"; import { useRouter } from "next/router"; +import { useState } from "react"; export default function EmailQueues() { const router = useRouter(); @@ -15,6 +16,7 @@ export default function EmailQueues() { method: "post", headers: { "Content-Type": "application/json", + Authorization: "Bearer " + getCookie("session"), }, body: JSON.stringify({ name, @@ -26,7 +28,7 @@ export default function EmailQueues() { }) .then((res) => res.json()) .then(() => { - // router.back(); + router.back("/admin/email-queues"); }); } diff --git a/apps/client/pages/admin/settings.js b/apps/client/pages/admin/settings.js deleted file mode 100644 index 7a6b039f0..000000000 --- a/apps/client/pages/admin/settings.js +++ /dev/null @@ -1,258 +0,0 @@ -import { useState, useEffect } from "react"; -import { Switch } from "@headlessui/react"; - -import Webhooks from "../../components/Webhooks"; -import NotificationsSettingsModal from "../../components/NotificationsSettingsModal"; - -function classNames(...classes) { - return classes.filter(Boolean).join(" "); -} - -export default function Settings() { - const [show, setShow] = useState("webhooks"); - const tabs = [ - { name: "webhooks", current: show === "webhooks" ? true : false }, - { name: "notifications", current: show === "notifications" ? true : false }, - ]; - - const [emails, setEmails] = useState(false); - const [discord, setDiscord] = useState(false); - const [telegram, setTelegram] = useState(false); - const [slack, setSlack] = useState(false); - - async function checkActive() { - const res = await fetch("/api/v1/admin/notifications/active"); - const data = await res.json(); - - data.emails ? setEmails(data.emails) : null - data.discord ? setDiscord(data.discord) : null - // setTelegram(data.telegram); - // setSlack(data.slack); - } - - useEffect(() => { - checkActive(); - }, []); - - return ( -
-
-
-
-
-

- Admin Settings -

-
-
-
-
- - -
-
-
- -
-
- -
-
- -
- -
-
- - - - Email notifications - - - Get notified when a handful events run, can be - individually turned off by a user. - - - null()} - className={classNames( - emails ? "bg-green-600" : "bg-gray-200", - "mr-4 relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" - )} - > - - {emails && } - - - - - - Discord notifications - - - Use discord webhooks to get sent an update everytime - a new ticket is created. - - - - - {discord && ( - - - - - )} - - - - - - Telegram notifications - - - Use telegram webhooks to get sent an update - everytime a new ticket is created. - - - - - {telegram && ( - - - - - )} - -
-
-
-
-
-
-
-
-
- ); -} diff --git a/apps/client/pages/admin/teams.js b/apps/client/pages/admin/teams.js index ac523e4ff..afaec0871 100644 --- a/apps/client/pages/admin/teams.js +++ b/apps/client/pages/admin/teams.js @@ -1,16 +1,15 @@ +import { getCookie } from "cookies-next"; import React from "react"; import { useQuery } from "react-query"; -import { Popconfirm } from "antd"; import { - useTable, useFilters, useGlobalFilter, usePagination, + useTable, } from "react-table"; import ClientNotesModal from "../../components/ClientNotesModal"; import CreateTeamModal from "../../components/CreateTeamModal"; import UpdateClientModal from "../../components/UpdateClientModal"; -import Link from "next/link"; function DefaultColumnFilter({ column: { filterValue, setFilter } }) { return ( @@ -186,13 +185,20 @@ function Table({ columns, data }) { ); } -const fetchAllTeams = async () => { - const res = await fetch("/api/v1/admin/team/all"); +const fetchAllTeams = async (token) => { + const res = await fetch("/api/v1/admin/team/all", { + headers: { + Authorization: `Bearer ${token}`, + }, + }); return res.json(); }; export default function Teams() { - const { data, status, refetch } = useQuery("fetchAllTeams", fetchAllTeams); + const token = getCookie("session"); + const { data, status, refetch } = useQuery("fetchAllTeams", () => + fetchAllTeams(token) + ); // async function deleteClient(id) { // try { diff --git a/apps/client/pages/admin/users/internal/new.js b/apps/client/pages/admin/users/internal/new.js index 28712d4a9..11b6379ba 100644 --- a/apps/client/pages/admin/users/internal/new.js +++ b/apps/client/pages/admin/users/internal/new.js @@ -1,10 +1,9 @@ import { notifications } from "@mantine/notifications"; +import { getCookie } from "cookies-next"; import { useRouter } from "next/router"; import React, { useState } from "react"; export default function CreateUser() { - const [open, setOpen] = useState(false); - const [password, setPassword] = useState(""); const [email, setEmail] = useState(""); const [name, setName] = useState(""); @@ -19,6 +18,7 @@ export default function CreateUser() { method: "POST", headers: { "Content-Type": "application/json", + Authorization: "Bearer " + getCookie("session"), }, body: JSON.stringify({ password, @@ -30,10 +30,10 @@ export default function CreateUser() { ) .then((res) => res.json()) .then((res) => { - if (res.sucess === true) { - router.push("/admin/internal/users"); + if (res.success === true) { + router.push("/admin/users"); notifications.show({ - title: "User created sucessfully", + title: "User created successfully", message: "The action was processed correctly! 💚", }); } else { diff --git a/apps/client/pages/admin/webhooks.js b/apps/client/pages/admin/webhooks.js index 71223a334..b75483a0d 100644 --- a/apps/client/pages/admin/webhooks.js +++ b/apps/client/pages/admin/webhooks.js @@ -1,10 +1,18 @@ import { Switch } from "@headlessui/react"; +import { getCookie } from "cookies-next"; import { useState } from "react"; import { useQuery } from "react-query"; -async function getHooks() { +async function getHooks(token) { const res = await fetch( - `${process.env.NEXT_PUBLIC_API_URL}/api/v1/webhooks/all` + `${process.env.NEXT_PUBLIC_API_URL}/api/v1/webhooks/all`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: "Bearer " + token, + }, + } ); return res.json(); } @@ -21,7 +29,11 @@ export default function Notifications() { const [secret, setSecret] = useState(); const [name, setName] = useState(""); - const { data, status, error, refetch } = useQuery("gethooks", getHooks); + const cookie = getCookie("session"); + + const { data, status, error, refetch } = useQuery("gethooks", () => + getHooks(cookie) + ); async function addHook() { await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/v1/webhook/create`, { diff --git a/apps/client/pages/auth/login.tsx b/apps/client/pages/auth/login.tsx index 623581a91..ea178f70a 100644 --- a/apps/client/pages/auth/login.tsx +++ b/apps/client/pages/auth/login.tsx @@ -25,7 +25,11 @@ export default function Login({}) { if (res.user) { setCookie("session", res.token); setUser(res.user); - router.push("/"); + if (res.user.firstLogin) { + router.push("/onboarding"); + } else { + router.push("/"); + } } }); } diff --git a/apps/client/pages/index.tsx b/apps/client/pages/index.tsx index 07e9b2388..47af799e0 100644 --- a/apps/client/pages/index.tsx +++ b/apps/client/pages/index.tsx @@ -1,5 +1,4 @@ import { CheckCircleIcon } from "@heroicons/react/20/solid"; -import { message } from "antd"; import Link from "next/link"; import { useEffect, useState } from "react"; @@ -107,35 +106,6 @@ export default function Home() { }, ]; - const propsUpload = { - name: "file", - action: `/api/v1/users/file/upload`, - data: () => { - let data = new FormData(); - data.append("file", file); - data.append("filename", file.name); - }, - onChange(info: any) { - if (info.file.status !== "uploading") { - console.log(info.file, info.fileList); - } - if (info.file.status === "done") { - message.success(`${info.file.name} file uploaded successfully`); - setUploaded(true); - } else if (info.file.status === "error") { - message.error(`${info.file.name} file upload failed.`); - } - }, - progress: { - strokeColor: { - "0%": "#108ee9", - "100%": "#87d068", - }, - strokeWidth: 3, - format: (percent: any) => `${parseFloat(percent.toFixed(2))}%`, - }, - }; - async function datafetch() { fetchTickets(); getOpenTickets(); diff --git a/apps/client/pages/onboarding.tsx b/apps/client/pages/onboarding.tsx new file mode 100644 index 000000000..9ea7b1d5e --- /dev/null +++ b/apps/client/pages/onboarding.tsx @@ -0,0 +1,113 @@ +import { useRouter } from "next/router"; + +import { getCookie } from "cookies-next"; +import Link from "next/link"; +import { useUser } from "../store/session"; + +// if admin -> set up a new user -> set up a new client -> set up a webhook -> set up an Email Queue +// if user -> new password -> check user info like name etc + +export default function Home() { + const router = useRouter(); + + const { user } = useUser(); + const token = getCookie("session"); + + return ( +
+
+
+
+
+

Peppermint

+

+ Welcome to Peppermint! A fully open sourced ticket management + system. +

+
+
+
+
+
+ +
+ Github + + Being an open source project, all of our source code can be + housed here. If you ever face a bug or are unsure about + something. + +
+ + Check it out + +
+
+ +
+ Docs + + Documentation for Peppermint can be found here. + +
+ + Check it out + +
+
+ +
+ Discord + + Join our discord server to get help from the community or + the developers. + +
+ + Check it out + +
+ {/*
+ +
+ Roadmap + + Being an open source project, all of our source code can be + housed here. If you ever face a bug or are unsure about + something. + +
+ + Check it out + +
*/} +
+
+
+ +
+
+
+
+ ); +} diff --git a/apps/client/pages/settings/password.tsx b/apps/client/pages/settings/password.tsx index 5877d84d3..27f7a09fb 100644 --- a/apps/client/pages/settings/password.tsx +++ b/apps/client/pages/settings/password.tsx @@ -1,26 +1,16 @@ -import { message } from "antd"; import { useState } from "react"; +import { notifications } from "@mantine/notifications"; import { getCookie } from "cookies-next"; export default function PasswordChange({ children }) { - const token = getCookie("token"); + const token = getCookie("session"); const [password, setPassword] = useState(""); const [check, setCheck] = useState(""); - const [show, setShow] = useState("profile"); - - const success = () => { - message.success("Password updated"); - }; - - const fail = (f: any) => { - message.error(`${f}`); - }; - const postData = async () => { - if (check === password) { + if (check === password && password.length > 0) { await fetch( `${process.env.NEXT_PUBLIC_API_URL}/api/v1/auth/reset-password`, { @@ -37,21 +27,36 @@ export default function PasswordChange({ children }) { .then((res) => res.json()) .then((res) => { if (res.failed === false) { - success(); + notifications.show({ + title: "Success", + message: `Password updated :)`, + color: "green", + autoClose: 5000, + }); } else { - fail(res.message); + notifications.show({ + title: "Error", + message: `Error: ${res.message}`, + color: "red", + autoClose: 5000, + }); } }); } else { - fail("Passwords are not the same"); + notifications.show({ + title: "Error", + message: `Error: passwords do not match`, + color: "red", + autoClose: 5000, + }); } }; return ( -
-
+ <> +
-
+
setCheck(e.target.value)} placeholder="Confirm users password" /> -
+
+ +
-
+ ); } diff --git a/apps/client/pages/settings/profile.tsx b/apps/client/pages/settings/profile.tsx index 0a674566e..c58222d10 100644 --- a/apps/client/pages/settings/profile.tsx +++ b/apps/client/pages/settings/profile.tsx @@ -1,4 +1,3 @@ -import { message } from "antd"; import { getCookie } from "cookies-next"; import { useRouter } from "next/router"; import { useState } from "react"; @@ -12,15 +11,7 @@ export default function UserProfile() { const [name, setName] = useState(user.name); const [email, setEmail] = useState(user.email); - const [language, setLanguage] = useState(user.locale); - - const success = () => { - message.success("Information updated!"); - }; - - const fail = () => { - message.error("Information failed to update"); - }; + const [language, setLanguage] = useState(user.language); function changeLanguage(locale) { setLanguage(locale); diff --git a/apps/client/public/discord.svg b/apps/client/public/discord.svg new file mode 100644 index 000000000..c03e8e127 --- /dev/null +++ b/apps/client/public/discord.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/apps/client/public/swagger.json b/apps/client/public/swagger.json deleted file mode 100644 index 05cab9c1e..000000000 --- a/apps/client/public/swagger.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "openapi": "3.0.0", - "info": { - "title": "Next Swagger API Example", - "version": "1.0" - }, - "paths": { - "/api/hello": { - "get": { - "description": "Returns all users", - "responses": { - "200": { - "description": "Array of all users" - } - } - } - } - }, - "components": {}, - "tags": [] -} \ No newline at end of file diff --git a/apps/client/public/version.json b/apps/client/public/version.json deleted file mode 100644 index 5dce157f9..000000000 --- a/apps/client/public/version.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "current_version": "0.2", - "github_latest_tag": "0.2" -} \ No newline at end of file diff --git a/apps/client/store/session.js b/apps/client/store/session.js index ec72fe1b1..11a464c42 100644 --- a/apps/client/store/session.js +++ b/apps/client/store/session.js @@ -48,10 +48,15 @@ export const SessionProvider = ({ children }) => { fetchUserProfile(); }, []); - return ( + return process.env.NEXT_PUBLIC_ENVIRONMENT === "production" && + process.env.NEXT_PUBLIC_TELEMETRY === "1" ? ( {children} + ) : ( + + {children} + ); }; From a77fafdc6997707a9522551286c89363d2e0262e Mon Sep 17 00:00:00 2001 From: Jack Andrews <45759583+potts99@users.noreply.github.com> Date: Mon, 27 Nov 2023 16:01:11 +0000 Subject: [PATCH 2/5] Update README.md (#186) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index abbe83792..8ac181e23 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ services: DB_PASSWORD: "1234" DB_HOST: "peppermint_postgres" SECRET: 'peppermint4life' - API_URL: "http://localhost:5003" + API_URL: "http://server-ip:5003" volumes: pgdata: From ba5030448093b00b787e6f40ddffde9169649591 Mon Sep 17 00:00:00 2001 From: Philipp Gruhn Date: Tue, 28 Nov 2023 23:22:32 +0100 Subject: [PATCH 3/5] Update German localization change unusual translations --- apps/client/locales/de/peppermint.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/client/locales/de/peppermint.json b/apps/client/locales/de/peppermint.json index efc061b72..908263930 100644 --- a/apps/client/locales/de/peppermint.json +++ b/apps/client/locales/de/peppermint.json @@ -1,8 +1,8 @@ { - "hello_good": "Gut", + "hello_good": "Wunderschönen", "hello_morning": "Morgen", "hello_afternoon": "Nachmittag", - "sl_dashboard": "Armaturenbrett", + "sl_dashboard": "Dashboard", "sl_tickets": "Tickets", "sl_history": "Verlauf", "sl_notebook": "Persönliches Notizbuch", @@ -59,7 +59,7 @@ "description": "Beschreibung", "comments": "Kommentare", "leave_comment": "Kommentar hinterlassen", - "close_issue": "Problem schließen", + "close_issue": "Ticket schließen", "comment": "Kommentar", "save": "Speichern", "labels": "Labels", @@ -68,7 +68,7 @@ "hide_ticket": "Ticket ausblenden", "show_ticket": "Global anzeigen", "open_issue": "Problem öffnen", - "closed_issue": "Geschlossenes Problem", + "closed_issue": "Geschlossenes Ticket", "recent_tickets": "Aktuelle Tickets", "notebook_title": "Notizbuchtitel", "admin_settings": "Admin-Einstellungen", From a8d7a18059fad1aac04be53b19cec9aaa5684808 Mon Sep 17 00:00:00 2001 From: Jack Andrews <45759583+potts99@users.noreply.github.com> Date: Tue, 28 Nov 2023 23:53:23 +0000 Subject: [PATCH 4/5] Update README.md (#195) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8ac181e23..af576d0f6 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ volumes: ``` -Once this is completed then you can go to your base_url which was added to the compose file and login. +Once this is completed then you can go to your server-ip:3000 which was added to the compose file and login. The default login credentials are From 6c320fa3a6c92d01d36d4f8b5e5f1af0e4efdd83 Mon Sep 17 00:00:00 2001 From: Jesper Wallberg <10283459+JesperWallberg@users.noreply.github.com> Date: Wed, 29 Nov 2023 09:05:22 +0100 Subject: [PATCH 5/5] Update peppermint.json (#197) --- apps/client/locales/se/peppermint.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/client/locales/se/peppermint.json b/apps/client/locales/se/peppermint.json index 4ed9f34bd..1306e0635 100644 --- a/apps/client/locales/se/peppermint.json +++ b/apps/client/locales/se/peppermint.json @@ -1,11 +1,11 @@ { - "hello_good": "Bra", - "hello_morning": "Morgon", - "hello_afternoon": "Eftermiddag", + "hello_good": "Hejsan", + "hello_morning": "God morgon", + "hello_afternoon": "God eftermiddag", "sl_dashboard": "Instrumentpanel", "sl_tickets": "Biljetter", "sl_history": "Historik", - "sl_notebook": "Personlig anteckningsbok", + "sl_notebook": "Anteckningsbok", "sl_users": "AnvĂ€ndare", "sl_clients": "Kunder", "sl_settings": "InstĂ€llningar", @@ -19,9 +19,9 @@ "ticket_name_here": "Namn", "ticket_email_here": "E-post", "ticket_details": "Rubrik", - "ticket_select_client": "VĂ€lj en kund", - "ticket_select_eng": "VĂ€lj en ingenjör", - "ticket_extra_details": "Ange extra detaljer hĂ€r.... markdown stöds", + "ticket_select_client": "VĂ€lj kund", + "ticket_select_eng": "VĂ€lj tekniker", + "ticket_extra_details": "Ange extra detaljer hĂ€r... markdown stöds", "cancel": "Avbryt", "create": "Skapa", "low": "LĂ„g",