diff --git a/client/src/pages/NewAccount/NewAccount.tsx b/client/src/pages/NewAccount/NewAccount.tsx index d16b4a5..36a1a19 100644 --- a/client/src/pages/NewAccount/NewAccount.tsx +++ b/client/src/pages/NewAccount/NewAccount.tsx @@ -31,8 +31,8 @@ export const NewAccount = () => { }) .then(async (res) => { const result = await res.json(); - if (result.error) { - setError(result.error); + if (result.errors) { + setError(result.errors[0]); } else { login(data.email, data.password); } diff --git a/client/src/providers/AuthProvider.tsx b/client/src/providers/AuthProvider.tsx index 5a2a1b1..e214f84 100644 --- a/client/src/providers/AuthProvider.tsx +++ b/client/src/providers/AuthProvider.tsx @@ -47,7 +47,7 @@ export const AuthProvider = ({ children, ...rest }: AuthProviderProps) => { }), }).then(async (res) => { if (res.status === 200) { - setToken((await res.json()).token); + setToken((await res.json()).data.token); setIsLoginDialogOpen(false); } }); diff --git a/server/src/controller/index.ts b/server/src/controller/index.ts index 5aae651..98e3cc4 100644 --- a/server/src/controller/index.ts +++ b/server/src/controller/index.ts @@ -1,2 +1,3 @@ -export { UserRoutes } from './user.controller' -export { ProjectRoutes } from './project.controller' \ No newline at end of file +export { routes as UserRoutes } from "./user.controller"; +export { routes as ProjectRoutes } from "./project.controller"; +export { routes as TaskRoutes } from "./task.controller"; diff --git a/server/src/controller/project.controller.ts b/server/src/controller/project.controller.ts index 4413845..e5c80b8 100644 --- a/server/src/controller/project.controller.ts +++ b/server/src/controller/project.controller.ts @@ -4,16 +4,17 @@ import { NewProjectDto } from "../dto/project.new.dto"; import { UpdateProjectDto } from "../dto/project.update.dto"; import { ProjectService } from "../service/project.service"; import { - CreateResponse, DeleteResponse, IndexResponse, - UpdateResponse + CreateResponse, + DeleteResponse, + IndexResponse, + UpdateResponse, } from "./typings/responses"; -const router = Router(); -export const ProjectRoutes = router; +export const routes = Router(); export const apiNamespace = "/projects"; -router.get>( +routes.get>( apiNamespace, async (req, res) => { const projects = await ProjectService.listAllByUserId(req.userId); @@ -27,67 +28,71 @@ router.get>( } ); -router.post, NewProjectDto>( - apiNamespace, - (req, res) => { - const { name } = req.body; +routes.post< + typeof apiNamespace, + unknown, + CreateResponse, + NewProjectDto +>(apiNamespace, (req, res) => { + const { name } = req.body; - ProjectService.create(req.userId, { - name, + ProjectService.create(req.userId, { + name, + }) + .then((project) => { + res.json({ + data: { + id: project.id, + name: project.name, + }, + }); }) - .then((project) => { - res.json({ - data: { - id: project.id, - name: project.name, - }, - }); - }) - .catch((err) => { - res.status(422).json({ - errors: [err.message], - }); + .catch((err) => { + res.status(422).json({ + errors: [err.message], }); + }); +}); + +const putPath = `${apiNamespace}/:id`; +routes.put< + typeof putPath, + { id: string }, + UpdateResponse, + UpdateProjectDto +>(putPath, async (req, res) => { + const { id } = req.params; + const { name } = req.body; + const projectId = parseInt(id); + + const projectToBeUpdated = await ProjectService.findProjectFromUserById( + req.userId, + projectId + ); + + if (projectToBeUpdated) { + ProjectService.update(projectToBeUpdated, { + name, + }).then((success) => { + if (success) { + res.status(204).json(); + } else { + res.status(402).json(); + } + }); + } else { + res.status(404).json({ + errors: ["Project not found"], + }); } -); +}); -const putPath = `${apiNamespace}/:taskId`; -router.put, UpdateProjectDto>( - putPath, - async (req, res) => { - const { taskId } = req.params; - const { name } = req.body; - const projectId = parseInt(taskId); - - const projectToBeUpdated = await ProjectService.findProjectFromUserById( - req.userId, - projectId - ); - - if (projectToBeUpdated) { - ProjectService.update(projectToBeUpdated, { - name, - }).then((success) => { - if (success) { - res.status(204).json(); - } else { - res.status(402).json(); - } - }); - } else { - res.status(404).json({ - errors: ["Project not found"], - }); - } - } -); - -const deletePath = `${apiNamespace}/:taskId`; -router.delete( +const deletePath = `${apiNamespace}/:id`; +routes.delete( deletePath, async (req, res) => { - const { taskId } = req.params; - const projectId = parseInt(taskId); + const { id } = req.params; + const projectId = parseInt(id); const projecToBeDeleted = await ProjectService.findProjectFromUserById( req.userId, diff --git a/server/src/controller/task.controller.ts b/server/src/controller/task.controller.ts index 19957c9..34cb19b 100644 --- a/server/src/controller/task.controller.ts +++ b/server/src/controller/task.controller.ts @@ -1,77 +1,90 @@ import { Router } from "express"; -import { ProjectDto } from "../dto/poject.dto"; +import { NewTaskDto } from "../dto/task.new.dto"; import { ProjectService } from "../service/project.service"; import { TaskService } from "../service/task.service"; +import { + CreateResponse, + DeleteResponse, + IndexResponse, + ShowResponse, + UpdateResponse, +} from "./typings/responses"; -const router = Router(); -export const TaskRoutes = router; +export const routes = Router(); -export const apiNamespace = "/projects/:projectId"; +export const apiNamespace = "/projects/:projectId/tasks"; -router.get(`${apiNamespace}/tasks`, async (req, res) => { +routes.get>( + apiNamespace, + async (req, res) => { + const { projectId } = req.params; + const parsedProjctId = parseInt(projectId); + + const project = await ProjectService.findProjectFromUserById( + req.userId, + parsedProjctId + ); + + if (!project) { + res.status(404).send("Not found"); + } else { + const tasks = await TaskService.findByProjectId(parsedProjctId); + + res.json({ + data: tasks.map((task) => ({ + id: task.id, + description: task.description, + createdAt: task.createdAt, + finishedAt: task.finishedAt, + })), + }); + } + } +); + +routes.post< + typeof apiNamespace, + { projectId: string }, + CreateResponse, + NewTaskDto +>(apiNamespace, async (req, res) => { let { projectId } = req.params; - const parsedProjctId = parseInt(projectId); const project = await ProjectService.findProjectFromUserById( req.userId, - parsedProjctId + parseInt(projectId) ); if (!project) { - res.status(404).json({ - error: "Project not found", + res.status(404).send("Not found"); + } else { + const task = await TaskService.create(project, { + description: req.body.description, + }); + + res.json({ + data: { + id: task.id, + createdAt: task.createdAt, + description: task.description, + finishedAt: task.finishedAt, + }, }); - return; } - - const tasks: TaskDto[] = ( - await TaskService.findByProjectId(parsedProjctId) - ).map((task) => ({ - id: task.id, - description: task.description, - createdAt: task.createdAt, - finishedAt: task.finishedAt, - })); - - res.json({ - data: tasks, - }); }); -router.post(`${apiNamespace}/tasks`, async (req, res) => { - let { projectId } = req.params; - const parsedProjctId = parseInt(projectId); - - const project = await ProjectService.findProjectFromUserById( - req.userId, - parsedProjctId - ); - - if (!project) { - res.status(404).json(); - return; - } - - const task = await TaskService.create(project, { - description: req.body.description, - }); - - const taskDto: TaskDto = { - id: task.id, - createdAt: task.createdAt, - description: task.description, - finishedAt: task.finishedAt, - }; - - res.json({ - data: taskDto, - }); -}); - -router.put(`${apiNamespace}/tasks/:id`, async (req, res) => { - let { projectId, id } = req.params; +const puthPath = `${apiNamespace}/:taskId`; +routes.put< + typeof puthPath, + { + projectId: string; + taskId: string; + }, + UpdateResponse, + UpdateTaskDto +>(puthPath, async (req, res) => { + const { projectId, taskId } = req.params; const { description }: UpdateTaskDto = req.body; - const parsedProjctId = parseInt(projectId); const project = await ProjectService.findProjectFromUserById( @@ -80,31 +93,36 @@ router.put(`${apiNamespace}/tasks/:id`, async (req, res) => { ); if (!project) { - res.status(404).json(); - return; + res.status(404).send("Not found"); } const task = await TaskService.findByProjectIdAndTaskId( parsedProjctId, - parseInt(id) + parseInt(taskId) ); await TaskService.update(task, { description }); - const taskDto: TaskDto = { - id: task.id, - createdAt: task.createdAt, - description: task.description, - finishedAt: task.finishedAt, - }; - res.json({ - data: taskDto, + data: { + id: task.id, + createdAt: task.createdAt, + description: task.description, + finishedAt: task.finishedAt, + }, }); }); -router.delete(`${apiNamespace}/tasks/:id`, async (req, res) => { - let { projectId, id } = req.params; +const deletePath = `${apiNamespace}/:taskId`; +routes.delete< + typeof deletePath, + { + projectId: string; + taskId: string; + }, + DeleteResponse +>(deletePath, async (req, res) => { + let { projectId, taskId } = req.params; const parsedProjctId = parseInt(projectId); const project = await ProjectService.findProjectFromUserById( @@ -112,32 +130,32 @@ router.delete(`${apiNamespace}/tasks/:id`, async (req, res) => { parsedProjctId ); - if (!project) { - res.status(404).json(); - return; - } - const task = await TaskService.findByProjectIdAndTaskId( parsedProjctId, - parseInt(id) + parseInt(taskId) ); - if (!task) { - res.status(404).json(); - return; - } - - const success = await TaskService.destroy(task); - - if (success) { - res.status(204).json(); + if (!project || !task) { + res.status(404).send("Not found"); } else { - res.status(504).json(); + const success = await TaskService.destroy(task); + + res.status(success ? 204 : 505).json({ + success, + }); } }); -router.get(`${apiNamespace}/tasks/:id/finish`, async (req, res) => { - const { projectId, id } = req.params; +const finishPath = `${apiNamespace}/:taskId/finish`; +routes.get< + typeof finishPath, + { + projectId: string; + taskId: string; + }, + ShowResponse +>(finishPath, async (req, res) => { + const { projectId, taskId } = req.params; const parsedProjctId = parseInt(projectId); const project = await ProjectService.findProjectFromUserById( @@ -145,27 +163,23 @@ router.get(`${apiNamespace}/tasks/:id/finish`, async (req, res) => { parsedProjctId ); - const task = await TaskService.findByProjectIdAndTaskId( parsedProjctId, - parseInt(id) + parseInt(taskId) ); if (!project || !task) { res.status(404).json(); - return; + } else { + await TaskService.finish(task); + + res.json({ + data: { + id: task.id, + createdAt: task.createdAt, + description: task.description, + finishedAt: task.finishedAt, + }, + }); } - - const result = await TaskService.finish(task); - - const resultDto: TaskDto = { - id: result.id, - createdAt: result.createdAt, - description: result.description, - finishedAt: result.finishedAt, - }; - - res.json({ - data: resultDto, - }); }); diff --git a/server/src/controller/typings/responses.d.ts b/server/src/controller/typings/responses.d.ts index 9ce4be5..4a86f23 100644 --- a/server/src/controller/typings/responses.d.ts +++ b/server/src/controller/typings/responses.d.ts @@ -1,10 +1,14 @@ export type ErrorResponse = { errors: string[]; -}; +} | string; export type IndexResponse = { data: TData[]; -}; +} | string; + +export type ShowResponse = { + data: TData; +} export type CreateResponse = | { diff --git a/server/src/controller/user.controller.ts b/server/src/controller/user.controller.ts index 68b6515..208072e 100644 --- a/server/src/controller/user.controller.ts +++ b/server/src/controller/user.controller.ts @@ -1,72 +1,91 @@ import { Router } from "express"; +import { UserDto } from "../dto/user.dto"; import { AuthService } from "../service/auth.service"; import { UserService } from "../service/user.service"; +import { CreateResponse, DeleteResponse } from "./typings/responses"; -const router = Router(); -export const UserRoutes = router; +export const routes = Router(); export const apiNamespace = "/users"; -router.post(apiNamespace, (req, res) => { - const { email, password } = req.body; +routes.post>( + apiNamespace, + (req, res) => { + const { email, password } = req.body; + + UserService.create({ + email, + password, + }) + .then((user) => { + res.json({ + data: { + email: user.email, + }, + }); + }) + .catch((err) => { + res.status(422).json({ + errors: [err.message], + }); + }); + } +); + +const signInPath = `${apiNamespace}/sign_in`; +routes.post>( + signInPath, + async (req, res) => { + const { email, password } = req.body; + + const user = await UserService.findByEmail(email); + + if (!user) { + res.status(500).json({ + errors: ["Invalid credentails"], + }); + return; + } + + const isPasswordValid = await AuthService.isUserPasswordValid( + user, + password + ); + + if (isPasswordValid) { + const token = await AuthService.createSession(user); - UserService.create({ - email, - password, - }) - .then(() => { res.json({ + data: { + token, + }, + }); + } else { + res.status(500).json({ + data: { + token: null, + }, + }); + } + } +); + +const signOutPath = `${apiNamespace}/sign_out`; +routes.delete( + signOutPath, + async (req, res) => { + const token = req.headers["x-access-token"]; + + if (typeof token === "string") { + await AuthService.destoySession(token); + + res.status(204).json({ success: true, }); - }) - .catch((err) => { + } else { res.status(422).json({ - error: err.message, + success: false, }); - }); -}); - -router.post(`${apiNamespace}/sign_in`, async (req, res) => { - const { email, password } = req.body; - - const user = await UserService.findByEmail(email); - - if (!user) { - res.status(500).json({ - error: "Invalid credentails", - }); - return; + } } - - const isPasswordValid = await AuthService.isUserPasswordValid(user, password); - - if (isPasswordValid) { - const token = await AuthService.createSession(user); - - res.json({ - auth: true, - token, - }); - } else { - res.status(500).json({ - auth: false, - token: null, - }); - } -}); - -router.delete(`${apiNamespace}/sign_out`, async (req, res) => { - const token = req.headers["x-access-token"]; - - if (typeof token === "string") { - await AuthService.destoySession(token); - - res.status(204).json({ - success: true, - }); - } else { - res.status(422).json({ - success: false, - }); - } -}); +); diff --git a/server/src/dto/user.dto.ts b/server/src/dto/user.dto.ts index 91334f6..f5665ea 100644 --- a/server/src/dto/user.dto.ts +++ b/server/src/dto/user.dto.ts @@ -1,4 +1,3 @@ export type UserDto = { - id: number email: string } \ No newline at end of file diff --git a/server/src/dto/user.session.dto.ts b/server/src/dto/user.session.dto.ts index 7f702a9..73d2ad4 100644 --- a/server/src/dto/user.session.dto.ts +++ b/server/src/dto/user.session.dto.ts @@ -1,3 +1,3 @@ type SessionDto = { - userEmail: string; -} \ No newline at end of file + token: string | null; +}; diff --git a/server/src/index.ts b/server/src/index.ts index 48e6ec4..f3cbdb3 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -1,9 +1,8 @@ -import * as cors from 'cors'; -import * as dotenv from 'dotenv'; -import * as express from 'express'; +import * as cors from "cors"; +import * as dotenv from "dotenv"; +import * as express from "express"; import "reflect-metadata"; -import { ProjectRoutes, UserRoutes } from "./controller"; -import { TaskRoutes } from "./controller/task.controller"; +import { ProjectRoutes, UserRoutes, TaskRoutes } from "./controller"; import { AppDataSource } from "./infra/dataSource"; import { RedisConnection } from "./infra/redis"; import { sessionMiddleware } from "./middleware/session.middleware"; @@ -12,28 +11,28 @@ dotenv.config(); const app = express(); -app.use(cors()) +app.use(cors()); app.use(express.json()); -app.use(sessionMiddleware) +app.use(sessionMiddleware); -app.use(UserRoutes) -app.use(ProjectRoutes) -app.use(TaskRoutes) +app.use(UserRoutes); +app.use(ProjectRoutes); +app.use(TaskRoutes); const startApp = async () => { - console.log('[redis]: connecting') - await RedisConnection.connect() - console.log('[redis]: connected') + console.log("[redis]: connecting"); + await RedisConnection.connect(); + console.log("[redis]: connected"); - console.log('[database]: connecting') - await AppDataSource.initialize() - console.log('[database]: connected') + console.log("[database]: connecting"); + await AppDataSource.initialize(); + console.log("[database]: connected"); const port = process.env.PORT; app.listen(port, () => { console.log(`[server] is running at ${port} 🚀`); }); -} +}; -startApp() \ No newline at end of file +startApp();