From 92a12aa65034e3c5d06656d99c48818bf91a9d98 Mon Sep 17 00:00:00 2001 From: huandrey Date: Wed, 9 Oct 2024 01:27:22 -0300 Subject: [PATCH 1/3] =?UTF-8?q?Refina=20c=C3=B3digo=20para=20entrega=20fin?= =?UTF-8?q?al;=20Diminui=20um=20pouco=20o=20escopo=20do=20projeto=20para?= =?UTF-8?q?=20fechar=20as=20pontas=20soltas=20do=20software;=20Alguns=20aj?= =?UTF-8?q?ustes=20e=20melhorias=20foram=20realizados=20durante=20o=20dese?= =?UTF-8?q?nvolvimento.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migration.sql | 140 +++++++++ .../migration.sql | 39 +++ .../migration.sql | 2 + .../migration.sql | 2 + prisma/schema.prisma | 151 +++------- src/controllers/auth-controller.ts | 33 --- src/controllers/sistema-controller.ts | 92 ------ src/database/database-interface.ts | 24 -- src/database/prisma-database.ts | 106 ------- src/dtos/discente-dto.ts | 6 - src/dtos/student-dto.ts | 4 - .../administrador-controller.ts} | 0 .../administrador/administrador-dto.ts | 0 .../administrador/administrador-repository.ts | 0 .../administrador/administrador-route.ts | 0 .../administrador/administrador-service.ts | 0 .../alunos/aluno-controller.ts} | 48 ++-- src/features/alunos/aluno-dto.ts | 5 + src/features/alunos/aluno-repository.ts | 51 ++++ src/features/alunos/aluno-route.ts | 24 ++ src/features/alunos/aluno-service.ts | 92 ++++++ .../autenticacao/autenticacao-controller.ts | 33 +++ .../autenticacao/autenticacao-repository.ts} | 8 +- .../autenticacao/autenticacao-route.ts | 16 ++ .../autenticacao/autenticacao-service.ts} | 22 +- .../disciplinas}/disciplina-controller.ts | 6 +- .../disciplinas/disciplina-repository.ts | 18 ++ .../disciplinas}/disciplina-route.ts | 12 +- .../disciplinas/disciplina-service.ts | 44 +++ src/features/sessoes/sessao-controller.ts | 81 ++++++ .../sessoes/sessao-dto.ts} | 4 +- src/features/sessoes/sessao-route.ts | 34 +++ .../sessoes/sessao-service.ts} | 8 +- .../usuarios/usuario-controller.ts} | 51 ++-- .../usuarios/usuario-dto.ts} | 4 +- src/features/usuarios/usuario-repository.ts | 45 +++ src/features/usuarios/usuario-route.ts | 21 ++ src/features/usuarios/usuario-service.ts | 79 +++++ src/importers/implementation/ufcg-importer.ts | 222 -------------- src/importers/importer.ts | 6 - src/repositories/discente-repository.ts | 51 ---- src/repositories/disciplina-repository.ts | 18 -- src/repositories/user-repository.ts | 63 ---- src/routes/auth-route.ts | 16 -- src/routes/discente-route.ts | 20 -- src/routes/sistema-route.ts | 34 --- src/routes/user-route.ts | 21 -- src/server.ts | 22 +- src/services/discente-service.ts | 92 ------ src/services/disciplina-service.ts | 41 --- src/services/user-service.ts | 80 ------ src/shared/database/database-interface.ts | 24 ++ src/shared/database/prisma-database.ts | 108 +++++++ .../exceptions/ReportarErroAoSistema.ts | 0 .../importers/implementation/ufcg-importer.ts | 270 ++++++++++++++++++ src/shared/importers/importer.ts | 9 + .../middlewares/autorizacao-middleware.ts | 58 ++-- 57 files changed, 1298 insertions(+), 1162 deletions(-) create mode 100644 prisma/migrations/20241009025017_refinamento_de_propriedades_das_entidades_do_sistema/migration.sql create mode 100644 prisma/migrations/20241009025141_altera_nomenclatura_user_para_usuario/migration.sql create mode 100644 prisma/migrations/20241009025515_adiciona_propriedade_default_para_a_propriedade_curso_do_aluno/migration.sql create mode 100644 prisma/migrations/20241009042431_muda_tipo_da_propriedade_codigo_de_disciplina/migration.sql delete mode 100644 src/controllers/auth-controller.ts delete mode 100644 src/controllers/sistema-controller.ts delete mode 100644 src/database/database-interface.ts delete mode 100644 src/database/prisma-database.ts delete mode 100644 src/dtos/discente-dto.ts delete mode 100644 src/dtos/student-dto.ts rename src/{repositories/docente-repository.ts => features/administrador/administrador-controller.ts} (100%) create mode 100644 src/features/administrador/administrador-dto.ts create mode 100644 src/features/administrador/administrador-repository.ts create mode 100644 src/features/administrador/administrador-route.ts create mode 100644 src/features/administrador/administrador-service.ts rename src/{controllers/discente-controller.ts => features/alunos/aluno-controller.ts} (55%) create mode 100644 src/features/alunos/aluno-dto.ts create mode 100644 src/features/alunos/aluno-repository.ts create mode 100644 src/features/alunos/aluno-route.ts create mode 100644 src/features/alunos/aluno-service.ts create mode 100644 src/features/autenticacao/autenticacao-controller.ts rename src/{repositories/auth-repository.ts => features/autenticacao/autenticacao-repository.ts} (68%) create mode 100644 src/features/autenticacao/autenticacao-route.ts rename src/{services/auth-service.ts => features/autenticacao/autenticacao-service.ts} (61%) rename src/{controllers => features/disciplinas}/disciplina-controller.ts (92%) create mode 100644 src/features/disciplinas/disciplina-repository.ts rename src/{routes => features/disciplinas}/disciplina-route.ts (68%) create mode 100644 src/features/disciplinas/disciplina-service.ts create mode 100644 src/features/sessoes/sessao-controller.ts rename src/{dtos/session-dto.ts => features/sessoes/sessao-dto.ts} (53%) create mode 100644 src/features/sessoes/sessao-route.ts rename src/{services/sistema-service.ts => features/sessoes/sessao-service.ts} (61%) rename src/{controllers/user-controller.ts => features/usuarios/usuario-controller.ts} (65%) rename src/{dtos/user-dto.ts => features/usuarios/usuario-dto.ts} (57%) create mode 100644 src/features/usuarios/usuario-repository.ts create mode 100644 src/features/usuarios/usuario-route.ts create mode 100644 src/features/usuarios/usuario-service.ts delete mode 100644 src/importers/implementation/ufcg-importer.ts delete mode 100644 src/importers/importer.ts delete mode 100644 src/repositories/discente-repository.ts delete mode 100644 src/repositories/disciplina-repository.ts delete mode 100644 src/repositories/user-repository.ts delete mode 100644 src/routes/auth-route.ts delete mode 100644 src/routes/discente-route.ts delete mode 100644 src/routes/sistema-route.ts delete mode 100644 src/routes/user-route.ts delete mode 100644 src/services/discente-service.ts delete mode 100644 src/services/disciplina-service.ts delete mode 100644 src/services/user-service.ts create mode 100644 src/shared/database/database-interface.ts create mode 100644 src/shared/database/prisma-database.ts rename src/{ => shared}/exceptions/ReportarErroAoSistema.ts (100%) create mode 100644 src/shared/importers/implementation/ufcg-importer.ts create mode 100644 src/shared/importers/importer.ts rename src/{ => shared}/middlewares/autorizacao-middleware.ts (55%) diff --git a/prisma/migrations/20241009025017_refinamento_de_propriedades_das_entidades_do_sistema/migration.sql b/prisma/migrations/20241009025017_refinamento_de_propriedades_das_entidades_do_sistema/migration.sql new file mode 100644 index 0000000..466dfe3 --- /dev/null +++ b/prisma/migrations/20241009025017_refinamento_de_propriedades_das_entidades_do_sistema/migration.sql @@ -0,0 +1,140 @@ +/* + Warnings: + + - The values [DISCENTE] on the enum `Role` will be removed. If these variants are still used in the database, this will fail. + - You are about to drop the column `cursoId` on the `Disciplina` table. All the data in the column will be lost. + - You are about to drop the column `discenteId` on the `Disciplina` table. All the data in the column will be lost. + - You are about to drop the column `docenteId` on the `Disciplina` table. All the data in the column will be lost. + - You are about to drop the column `status` on the `Disciplina` table. All the data in the column will be lost. + - You are about to drop the `Curso` table. If the table is not empty, all the data it contains will be lost. + - You are about to drop the `Discente` table. If the table is not empty, all the data it contains will be lost. + - You are about to drop the `DisciplinaEmCurso` table. If the table is not empty, all the data it contains will be lost. + - You are about to drop the `Docente` table. If the table is not empty, all the data it contains will be lost. + - You are about to drop the `HistoricoAlunoDisciplina` table. If the table is not empty, all the data it contains will be lost. + - You are about to drop the `LivroEmprestadoBiblioteca` table. If the table is not empty, all the data it contains will be lost. + - Added the required column `codigo` to the `Disciplina` table without a default value. This is not possible if the table is not empty. + +*/ +-- CreateEnum +CREATE TYPE "SituacaoDisciplina" AS ENUM ('APROVADO', 'EM_PROGRESSO', 'TRANCADA', 'REPROVADO', 'DISPENSA', 'REPROVADO_POR_FALTA'); + +-- AlterEnum +BEGIN; +CREATE TYPE "Role_new" AS ENUM ('ADMIN', 'ALUNO', 'DOCENTE'); +ALTER TABLE "User" ALTER COLUMN "role" DROP DEFAULT; +ALTER TABLE "User" ALTER COLUMN "role" TYPE "Role_new" USING ("role"::text::"Role_new"); +ALTER TYPE "Role" RENAME TO "Role_old"; +ALTER TYPE "Role_new" RENAME TO "Role"; +DROP TYPE "Role_old"; +ALTER TABLE "User" ALTER COLUMN "role" SET DEFAULT 'ALUNO'; +COMMIT; + +-- DropForeignKey +ALTER TABLE "Curso" DROP CONSTRAINT "Curso_universidadeId_fkey"; + +-- DropForeignKey +ALTER TABLE "Discente" DROP CONSTRAINT "Discente_cursoId_fkey"; + +-- DropForeignKey +ALTER TABLE "Discente" DROP CONSTRAINT "Discente_livroId_fkey"; + +-- DropForeignKey +ALTER TABLE "Discente" DROP CONSTRAINT "Discente_userId_fkey"; + +-- DropForeignKey +ALTER TABLE "Disciplina" DROP CONSTRAINT "Disciplina_cursoId_fkey"; + +-- DropForeignKey +ALTER TABLE "Disciplina" DROP CONSTRAINT "Disciplina_discenteId_fkey"; + +-- DropForeignKey +ALTER TABLE "Disciplina" DROP CONSTRAINT "Disciplina_docenteId_fkey"; + +-- DropForeignKey +ALTER TABLE "DisciplinaEmCurso" DROP CONSTRAINT "DisciplinaEmCurso_discenteId_fkey"; + +-- DropForeignKey +ALTER TABLE "DisciplinaEmCurso" DROP CONSTRAINT "DisciplinaEmCurso_disciplinaId_fkey"; + +-- DropForeignKey +ALTER TABLE "DisciplinaEmCurso" DROP CONSTRAINT "DisciplinaEmCurso_docenteId_fkey"; + +-- DropForeignKey +ALTER TABLE "Docente" DROP CONSTRAINT "Docente_cursoId_fkey"; + +-- DropForeignKey +ALTER TABLE "Docente" DROP CONSTRAINT "Docente_userId_fkey"; + +-- DropForeignKey +ALTER TABLE "HistoricoAlunoDisciplina" DROP CONSTRAINT "HistoricoAlunoDisciplina_discenteId_fkey"; + +-- DropForeignKey +ALTER TABLE "HistoricoAlunoDisciplina" DROP CONSTRAINT "HistoricoAlunoDisciplina_disciplinaId_fkey"; + +-- DropForeignKey +ALTER TABLE "HistoricoAlunoDisciplina" DROP CONSTRAINT "HistoricoAlunoDisciplina_docenteId_fkey"; + +-- AlterTable +ALTER TABLE "Disciplina" DROP COLUMN "cursoId", +DROP COLUMN "discenteId", +DROP COLUMN "docenteId", +DROP COLUMN "status", +ADD COLUMN "alunoId" INTEGER, +ADD COLUMN "cargaHoraria" INTEGER NOT NULL DEFAULT 0, +ADD COLUMN "docente" TEXT NOT NULL DEFAULT 'Desconhecido', +ADD COLUMN "situacao" "SituacaoDisciplina" DEFAULT 'EM_PROGRESSO', +DROP COLUMN "codigo", +ADD COLUMN "codigo" INTEGER NOT NULL; + +-- AlterTable +ALTER TABLE "User" ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, +ALTER COLUMN "role" SET DEFAULT 'ALUNO'; + +-- DropTable +DROP TABLE "Curso"; + +-- DropTable +DROP TABLE "Discente"; + +-- DropTable +DROP TABLE "DisciplinaEmCurso"; + +-- DropTable +DROP TABLE "Docente"; + +-- DropTable +DROP TABLE "HistoricoAlunoDisciplina"; + +-- DropTable +DROP TABLE "LivroEmprestadoBiblioteca"; + +-- DropEnum +DROP TYPE "DisciplinaEmCursoStatus"; + +-- DropEnum +DROP TYPE "DisciplinaStatus"; + +-- CreateTable +CREATE TABLE "Aluno" ( + "id" SERIAL NOT NULL, + "userId" INTEGER NOT NULL, + "nome" TEXT NOT NULL, + "matricula" VARCHAR(9) NOT NULL, + "curso" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "Aluno_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "Aluno_userId_key" ON "Aluno"("userId"); + +-- CreateIndex +CREATE UNIQUE INDEX "Aluno_matricula_key" ON "Aluno"("matricula"); + +-- AddForeignKey +ALTER TABLE "Aluno" ADD CONSTRAINT "Aluno_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Disciplina" ADD CONSTRAINT "Disciplina_alunoId_fkey" FOREIGN KEY ("alunoId") REFERENCES "Aluno"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/prisma/migrations/20241009025141_altera_nomenclatura_user_para_usuario/migration.sql b/prisma/migrations/20241009025141_altera_nomenclatura_user_para_usuario/migration.sql new file mode 100644 index 0000000..b28b338 --- /dev/null +++ b/prisma/migrations/20241009025141_altera_nomenclatura_user_para_usuario/migration.sql @@ -0,0 +1,39 @@ +/* + Warnings: + + - You are about to drop the column `userId` on the `Aluno` table. All the data in the column will be lost. + - You are about to drop the `User` table. If the table is not empty, all the data it contains will be lost. + - A unique constraint covering the columns `[usuarioId]` on the table `Aluno` will be added. If there are existing duplicate values, this will fail. + - Added the required column `usuarioId` to the `Aluno` table without a default value. This is not possible if the table is not empty. + +*/ +-- DropForeignKey +ALTER TABLE "Aluno" DROP CONSTRAINT "Aluno_userId_fkey"; + +-- DropIndex +DROP INDEX "Aluno_userId_key"; + +-- AlterTable +ALTER TABLE "Aluno" DROP COLUMN "userId", +ADD COLUMN "usuarioId" INTEGER NOT NULL; + +-- DropTable +DROP TABLE "User"; + +-- CreateTable +CREATE TABLE "Usuario" ( + "id" SERIAL NOT NULL, + "nome" TEXT NOT NULL, + "token" TEXT, + "role" "Role" NOT NULL DEFAULT 'ALUNO', + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "Usuario_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "Aluno_usuarioId_key" ON "Aluno"("usuarioId"); + +-- AddForeignKey +ALTER TABLE "Aluno" ADD CONSTRAINT "Aluno_usuarioId_fkey" FOREIGN KEY ("usuarioId") REFERENCES "Usuario"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/migrations/20241009025515_adiciona_propriedade_default_para_a_propriedade_curso_do_aluno/migration.sql b/prisma/migrations/20241009025515_adiciona_propriedade_default_para_a_propriedade_curso_do_aluno/migration.sql new file mode 100644 index 0000000..01e39a3 --- /dev/null +++ b/prisma/migrations/20241009025515_adiciona_propriedade_default_para_a_propriedade_curso_do_aluno/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Aluno" ALTER COLUMN "curso" SET DEFAULT 'Universidade Federal de Campina Grande'; diff --git a/prisma/migrations/20241009042431_muda_tipo_da_propriedade_codigo_de_disciplina/migration.sql b/prisma/migrations/20241009042431_muda_tipo_da_propriedade_codigo_de_disciplina/migration.sql new file mode 100644 index 0000000..5ec7e93 --- /dev/null +++ b/prisma/migrations/20241009042431_muda_tipo_da_propriedade_codigo_de_disciplina/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Disciplina" ALTER COLUMN "codigo" SET DATA TYPE TEXT; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 9092824..61c329d 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -8,140 +8,65 @@ datasource db { } enum Role { - ADMIN // Administrador do sistema - DISCENTE // Aluno de alguma instituição que usa o sistema - DOCENTE // Professor de alguma instituição que usa o sistema + ADMIN + ALUNO + DOCENTE } -enum DisciplinaStatus { - APENAS_MEDIA_APROVADO // A disciplina foi registrada como paga com êxito e aprovado no sistema - APENAS_MEDIA_REPROVADO // A disciplina foi registrada como nota insuficiente e reprovado no sistema - REPROVADO_POR_FALTA // A disciplina foi registrada como aluno reprovado por falta - TRANCADA // A disciplina foi registrada como trancada pelo aluno +enum SituacaoDisciplina { + APROVADO EM_PROGRESSO -} - -enum DisciplinaEmCursoStatus { - EM_PROGRESSO // A disciplina está em curso - TRANCADA // O aluno trancou a disciplina + TRANCADA + REPROVADO + DISPENSA + REPROVADO_POR_FALTA } model Universidade { id Int @id @default(autoincrement()) - sigla String // A sigla da universidade. Ex: 'UFCG' + sigla String // 'UFCG', 'UFPB', 'UFRJ' nome String cidade String estado String createdAt DateTime @default(now()) - cursos Curso[] } -model User { +model Usuario { id Int @id @default(autoincrement()) nome String - token String? - role Role @default(DISCENTE) + token String? + role Role @default(ALUNO) + aluno Aluno? createdAt DateTime @default(now()) - discente Discente? - docente Docente? + updatedAt DateTime @default(now()) } -model Discente { - id Int @id @default(autoincrement()) - nome String - matricula String @unique @db.VarChar(9) - cursoId Int - userId Int @unique - livroId Int? @unique - createdAt DateTime @default(now()) - user User @relation(fields: [userId], references: [id]) - curso Curso @relation(fields: [cursoId], references: [id]) - livros LivroEmprestadoBiblioteca? @relation(fields: [livroId], references: [id]) - disciplinasEmCurso DisciplinaEmCurso[] - historicos HistoricoAlunoDisciplina[] - Disciplina Disciplina[] -} - -model LivroEmprestadoBiblioteca { - id Int @id @default(autoincrement()) - nomeDoLivro String - dataQueFoiPego String - dataQuePrecisaSerEntregue String - dataQueAlunoQuerSerLembrado String - anotacoesSobreOLivro String? - createdAt DateTime @default(now()) - updatedAt DateTime @default(now()) - discente Discente? -} - - -model Docente { - id Int @id @default(autoincrement()) - nome String - cursoId Int - userId Int @unique - createdAt DateTime @default(now()) - user User @relation(fields: [userId], references: [id]) - curso Curso @relation(fields: [cursoId], references: [id]) - disciplinas Disciplina[] - disciplinasEmCurso DisciplinaEmCurso[] - historicos HistoricoAlunoDisciplina[] -} - -model Curso { - id Int @id @default(autoincrement()) - universidadeId Int - nome String - createdAt DateTime @default(now()) - universidade Universidade @relation(fields: [universidadeId], references: [id]) - discentes Discente[] - docentes Docente[] - disciplinas Disciplina[] +model Aluno { + id Int @id @default(autoincrement()) + usuarioId Int @unique + nome String + matricula String @unique @db.VarChar(9) + curso String @default("Universidade Federal de Campina Grande") + usuario Usuario @relation(fields: [usuarioId], references: [id]) + historicoAcademico Disciplina[] + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) } model Disciplina { - id Int @id @default(autoincrement()) - codigo String? - nome String - creditos Int // Créditos são definidos por horas. Ex: "Cáculo I possui 4 horas semanais, portanto 4 créditos." - semestre String // Semestre em que a disciplina foi cursada + id Int @id @default(autoincrement()) + codigo String + nome String + creditos Int + cargaHoraria Int @default(0) + semestre String + situacao SituacaoDisciplina? @default(EM_PROGRESSO) quantidadeProvas Int quantidadeFaltas Int - mediaFinal Float? - docenteId Int? - discenteId Int? - status DisciplinaStatus - cursoId Int - createdAt DateTime @default(now()) - docente Docente? @relation(fields: [docenteId], references: [id]) - discente Discente? @relation(fields: [discenteId], references: [id]) - curso Curso @relation(fields: [cursoId], references: [id]) - historicos HistoricoAlunoDisciplina[] - disciplinasEmCurso DisciplinaEmCurso[] - horario String? -} - -model HistoricoAlunoDisciplina { - id Int @id @default(autoincrement()) - discenteId Int - docenteId Int? - disciplinaId Int - createdAt DateTime @default(now()) - updatedAt DateTime @default(now()) - discente Discente @relation(fields: [discenteId], references: [id]) - docente Docente? @relation(fields: [docenteId], references: [id]) - disciplina Disciplina @relation(fields: [disciplinaId], references: [id]) -} - -model DisciplinaEmCurso { - id Int @id @default(autoincrement()) - discenteId Int - docenteId Int? - disciplinaId Int - notaDoDiscente Float? - status DisciplinaEmCursoStatus - discente Discente @relation(fields: [discenteId], references: [id]) - docente Docente? @relation(fields: [docenteId], references: [id]) - disciplina Disciplina @relation(fields: [disciplinaId], references: [id]) + mediaFinal Float? + docente String @default("Desconhecido") + alunoId Int? + aluno Aluno? @relation(fields: [alunoId], references: [id]) + horario String? + createdAt DateTime @default(now()) } - diff --git a/src/controllers/auth-controller.ts b/src/controllers/auth-controller.ts deleted file mode 100644 index 0033688..0000000 --- a/src/controllers/auth-controller.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Request, Response } from "express" -import { AuthService } from "../services/auth-service" -import { ReportarErrorAoSistema } from "../exceptions/ReportarErroAoSistema" - -export class AuthController { - private authService: AuthService - - constructor(authService: AuthService) { - this.authService = authService - } - - async gerarToken(req: Request, res: Response) { - const { id, role } = req.body - - try { - const token = await this.authService.lidaComGeracaoDoToken({ id, role }) - - return res.status(201).json({ - message: "Token gerado com sucesso.", - data: token - }) - } catch (error) { - if (error instanceof ReportarErrorAoSistema) { - return res.status(400).json({ - error: error.message, - details: process.env.NODE_ENV === 'development' ? error.stack : "" - }) - } else { - res.status(500).json({ error: 'Internal Server Error' }) - } - } - } -} diff --git a/src/controllers/sistema-controller.ts b/src/controllers/sistema-controller.ts deleted file mode 100644 index f0db8ef..0000000 --- a/src/controllers/sistema-controller.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { Request, Response } from "express"; -import { PayloadUserAlreadyExists } from "../middlewares/autorizacao-middleware"; -import { UserService } from "../services/user-service"; -import { DiscenteService } from "../services/discente-service"; -import { DisciplinaService } from "../services/disciplina-service"; -import { SessionDTO } from "../dtos/session-dto"; -import { SistemaService } from "../services/sistema-service"; - -export class SistemaController { - private userService: UserService - private discenteService: DiscenteService - private disciplinaService: DisciplinaService - private sistemaService: SistemaService - - constructor(userService: UserService, discenteService: DiscenteService, disciplinaService: DisciplinaService, sistemaService: SistemaService) { - this.userService = userService, - this.discenteService = discenteService - this.disciplinaService = disciplinaService - this.sistemaService = sistemaService - } - - public async criaConexaoComSistema(req: Request, res: Response): Promise { - interface CustomRequest extends Request { - user: PayloadUserAlreadyExists; - } - - const sessionDTO: SessionDTO = req.body; - - // // customReq agora compartilha o mesmo acesso de memória que req - // const customReq: CustomRequest = req as CustomRequest; - - // const { userAlreadyExists } = customReq.user; - - // if (userAlreadyExists) { - // this.retornaDadosDoDiscente() - // } - - this.configuraSistemaDoUsuario(sessionDTO) - - - res.status(200).json({ message: 'Conexão com o sistema criada com sucesso!' }) - } - - public atualizaSistema(): void { - // Implementar a atualização do sistema - } - - private retornaDadosDoDiscente(): void { - // Implementar a consulta dos dados do discente - } - - // Implementar a configuração do sistema do usuário - private async configuraSistemaDoUsuario(sessionDTO: SessionDTO) { - // Importa dados do usuário - // Cria usuário - const usuario = await this.userService.lidaComCriacaoDoUsuario({ nome: '', role: 'DISCENTE' }) - - if (!usuario) { - throw new Error('Erro ao cadastrar o usuário') - } - - const discente = await this.discenteService.lidaComCriacaoDoDiscente({ - nome: '', - matricula: sessionDTO.matricula, - cursoId: 1, // Adicionar o ID do curso - userId: usuario!.id, // Adicionar o ID do usuário - }); - - if (!discente) { - throw new Error('Erro ao cadastrar o discente') - } - - // Identifica a instituição que o aluno possui vínculo - const importer = this.sistemaService.lidaComIdentificacaoDaInstituicaoDeEnsino(sessionDTO.vinculo) - - // Importa disciplinas do usuário - this.disciplinaService.lidaComImportacaoDasDisciplinasDoDiscente({ ...sessionDTO, discenteId: discente.id }, importer) - - // Configura as permissões do usuário - - // Cria as rotas do sistema - - // Cria as rotas do sistema do usuário - - // Inicia as rotas do sistema - - // Inicia as rotas do sistema do usuário - - // Inicia a API - } - -} diff --git a/src/database/database-interface.ts b/src/database/database-interface.ts deleted file mode 100644 index 2783944..0000000 --- a/src/database/database-interface.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Discente, Disciplina, User } from "@prisma/client" -import { UserDTO } from "../dtos/user-dto" -import { DiscenteDTO } from "../dtos/student-dto" - -export interface IDatabase { - criaUsuario(data: UserDTO): Promise - buscaUsuarioPorId(id: number): Promise - buscaUsuarioPorEmail(email: string): Promise - atualizaUsuario(id: number, data: Partial): Promise - deletaUsuario(id: number): Promise - buscaPorUsuarios(): Promise - - criaDiscente(data: DiscenteDTO): Promise - atualizaDiscente(id: number, data: Partial): Promise - deletaDiscente(id: number): Promise - buscaDiscentePorId(id: number): Promise - buscaDiscentePorMatricula(matricula: string): Promise - buscaDiscentePorEmail(email: string): Promise - - autenticaUsuario(email: string, senha: string): Promise - adicionaTokenDeAutenticacao(id: number, token: string): Promise - - salvaDisciplinas(data: Disciplina[]): Promise -} diff --git a/src/database/prisma-database.ts b/src/database/prisma-database.ts deleted file mode 100644 index 7bb9f16..0000000 --- a/src/database/prisma-database.ts +++ /dev/null @@ -1,106 +0,0 @@ -// src/database/PrismaDatabase.tsimport { PrismaClient } from'@prisma/client' -import { Discente, Disciplina, PrismaClient, User } from '@prisma/client' -import { UserDTO } from '../dtos/user-dto' -import { IDatabase } from'./database-interface' -import { DiscenteDTO } from '../dtos/discente-dto' -import { PrismaClientInitializationError } from '@prisma/client/runtime/library' - -const prisma = new PrismaClient() - -export class PrismaDatabase implements IDatabase { - async salvaDisciplinas(data: Disciplina[]): Promise { - try { - const disciplinas = await prisma.disciplina.createMany({ data }) - - return disciplinas.count - } catch (error) { - if (error instanceof PrismaClientInitializationError) { - console.error('Erro ao inicializar o Prisma Client:', error.message) - } else { - console.error('Erro desconhecido ao salvar disciplinas:', error) - } - throw error - } - } - - async buscaPorUsuarios(): Promise { - return await prisma.user.findMany() - } - - async adicionaTokenDeAutenticacao(id: number, token: string): Promise { - await prisma.user.update({ - where: { - id, - }, - data: { - token - } - }) - } - buscaUsuarioPorEmail(email: string): Promise { - throw new Error('Method not implemented.') - } - atualizaDiscente(id: number, data: Partial): Promise { - throw new Error('Method not implemented.') - } - deletaDiscente(id: number): Promise { - throw new Error('Method not implemented.') - } - buscaDiscentePorId(id: number): Promise { - throw new Error('Method not implemented.') - } - buscaDiscentePorMatricula(matricula: string): Promise { - throw new Error('Method not implemented.') - } - buscaDiscentePorEmail(email: string): Promise { - throw new Error('Method not implemented.') - } - criaDiscente(data: DiscenteDTO): Promise { - console.log(data) - const discente = prisma.discente.create({ - data: { - nome: data.nome, - matricula: data.matricula, - user: { - connect: { - id: data.userId, - } - }, - curso: { - connect: { - id: data.cursoId, - } - } - }, - }) - - return discente - } - - async criaUsuario(data: UserDTO): Promise { - return prisma.user.create({ data: { - nome: data.nome, - role: data.role, - }}) - } - - async buscaUsuarioPorId(id: number): Promise { - return prisma.user.findUnique({ where: { id } }) - } - - async atualizaUsuario(id: number, data: Partial): Promise { - return prisma.user.update({ where: { id }, data }) - } - - async deletaUsuario(id: number): Promise { - await prisma.user.delete({ where: { id } }) - } - - async autenticaUsuario(email: string, password: string): Promise { - return null - } - - async validaToken(token: string): Promise { - return true - } -} diff --git a/src/dtos/discente-dto.ts b/src/dtos/discente-dto.ts deleted file mode 100644 index a7b4dc1..0000000 --- a/src/dtos/discente-dto.ts +++ /dev/null @@ -1,6 +0,0 @@ -export type DiscenteDTO = { - nome: string - matricula: string - userId: number - cursoId: number -} diff --git a/src/dtos/student-dto.ts b/src/dtos/student-dto.ts deleted file mode 100644 index 8c8dea6..0000000 --- a/src/dtos/student-dto.ts +++ /dev/null @@ -1,4 +0,0 @@ -export type DiscenteDTO = { - nome: string - matricula: string -} diff --git a/src/repositories/docente-repository.ts b/src/features/administrador/administrador-controller.ts similarity index 100% rename from src/repositories/docente-repository.ts rename to src/features/administrador/administrador-controller.ts diff --git a/src/features/administrador/administrador-dto.ts b/src/features/administrador/administrador-dto.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/features/administrador/administrador-repository.ts b/src/features/administrador/administrador-repository.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/features/administrador/administrador-route.ts b/src/features/administrador/administrador-route.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/features/administrador/administrador-service.ts b/src/features/administrador/administrador-service.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/controllers/discente-controller.ts b/src/features/alunos/aluno-controller.ts similarity index 55% rename from src/controllers/discente-controller.ts rename to src/features/alunos/aluno-controller.ts index 811e15a..ed9529a 100644 --- a/src/controllers/discente-controller.ts +++ b/src/features/alunos/aluno-controller.ts @@ -1,20 +1,24 @@ +import { AlunoDTO } from './aluno-dto' import { Request, Response } from 'express' -import { DiscenteDTO } from '../dtos/discente-dto' -import { IDiscenteService } from '../services/discente-service' -import { ReportarErrorAoSistema } from '../exceptions/ReportarErroAoSistema' +import { IAlunoService } from './aluno-service' +import { IUsuarioService } from '../usuarios/usuario-service' +import { ReportarErrorAoSistema } from '../../shared/exceptions/ReportarErroAoSistema' -export class DiscenteController { - private discenteService: IDiscenteService +export class AlunoController { + private usuarioService: IUsuarioService + private alunoService: IAlunoService - constructor(discenteService: IDiscenteService) { - this.discenteService = discenteService + constructor(usuarioService: IUsuarioService, alunoService: IAlunoService) { + this.alunoService = alunoService + this.usuarioService = usuarioService } - public async criaDiscente(req: Request, res: Response): Promise { - const discente: DiscenteDTO = req.body + public async criaAluno(req: Request, res: Response): Promise { + const aluno: AlunoDTO = req.body try { - await this.discenteService.lidaComCriacaoDoDiscente(discente) + const usuarioId = await this.usuarioService.lidaComCriacaoDoUsuario(aluno) + await this.alunoService.lidaComCriacaoDoAluno({ ...aluno, usuarioId }) res.status(201).json({ message: "Aluno criado com sucesso!" }) } catch (error) { if (error instanceof ReportarErrorAoSistema) { @@ -26,11 +30,11 @@ export class DiscenteController { } } - public async buscaDiscentePorId(req: Request, res: Response): Promise { - const discenteId = parseInt(req.params.id, 10) + public async buscaAlunoPorId(req: Request, res: Response): Promise { + const alunoId = parseInt(req.params.id, 10) try { - const student = await this.discenteService.lidaComBuscaDoDiscentePorId(discenteId) + const student = await this.alunoService.lidaComBuscaDoAlunoPorId(alunoId) if (student) { res.status(200).json(student) } else { @@ -46,11 +50,11 @@ export class DiscenteController { } } - public async buscaDiscentePorMatricula(req: Request, res: Response): Promise { + public async buscaAlunoPorMatricula(req: Request, res: Response): Promise { const matricula = req.body.matricula try { - const student = await this.discenteService.lidaComBuscaDoDiscentePorMatricula(matricula) + const student = await this.alunoService.lidaComBuscaDoAlunoPorMatricula(matricula) if (student) { res.status(200).json(student) } else { @@ -66,12 +70,12 @@ export class DiscenteController { } } - public async atualizaDiscente(req: Request, res: Response): Promise { - const discenteId = parseInt(req.params.id, 10) - const discente: DiscenteDTO = req.body + public async atualizaAluno(req: Request, res: Response): Promise { + const alunoId = parseInt(req.params.id, 10) + const aluno: AlunoDTO = req.body try { - const updatedStudent = await this.discenteService.lidaComAtualizacaoDoDiscente(discenteId, discente) + const updatedStudent = await this.alunoService.lidaComAtualizacaoDoAluno(alunoId, aluno) if (updatedStudent) { res.status(200).json(updatedStudent) } else { @@ -87,11 +91,11 @@ export class DiscenteController { } } - public async deletaDiscente(req: Request, res: Response): Promise { - const discenteId = parseInt(req.params.id, 10) + public async deletaAluno(req: Request, res: Response): Promise { + const alunoId = parseInt(req.params.id, 10) try { - await this.discenteService.lidaComRemocaoDoDiscente(discenteId) + await this.alunoService.lidaComRemocaoDoAluno(alunoId) res.status(204).send() } catch (error) { if (error instanceof ReportarErrorAoSistema) { diff --git a/src/features/alunos/aluno-dto.ts b/src/features/alunos/aluno-dto.ts new file mode 100644 index 0000000..1533457 --- /dev/null +++ b/src/features/alunos/aluno-dto.ts @@ -0,0 +1,5 @@ +export type AlunoDTO = { + nome: string + matricula: string + usuarioId: number +} diff --git a/src/features/alunos/aluno-repository.ts b/src/features/alunos/aluno-repository.ts new file mode 100644 index 0000000..9e7e6b7 --- /dev/null +++ b/src/features/alunos/aluno-repository.ts @@ -0,0 +1,51 @@ +import { Aluno } from "@prisma/client" +import { AlunoDTO } from "./aluno-dto" +import { IDatabase } from "../../shared/database/database-interface" + +export interface IAlunoRepository { + salvaAluno(data: AlunoDTO): Promise + atualizaAluno(id: number, data: Partial): Promise + removeAluno(id: number): Promise + buscaAlunoPorId(id: number): Promise + buscaAlunoPorEmail(email: string): Promise + buscaAlunoPorMatricula(matricula: string): Promise +} + +export type AtualizaAlunoDTO = { + email?: string + name?: string + password?: string + role?: string +} + +export class AlunoRepository implements IAlunoRepository { + private orm: IDatabase + + constructor(orm: IDatabase) { + this.orm = orm + } + + async salvaAluno(data: AlunoDTO): Promise { + return this.orm.salvaAluno(data) + } + + async atualizaAluno(id: number, data: AlunoDTO): Promise { + return this.orm.atualizaAluno(id, data) + } + + async removeAluno(id: number): Promise { + await this.orm.removeAluno(id) + } + + async buscaAlunoPorId(id: number): Promise { + return this.orm.buscaAlunoPorId(id) + } + + async buscaAlunoPorMatricula(matricula: string): Promise { + return this.orm.buscaAlunoPorMatricula(matricula) + } + + async buscaAlunoPorEmail(email: string): Promise { + return this.orm.buscaAlunoPorEmail(email) + } +} diff --git a/src/features/alunos/aluno-route.ts b/src/features/alunos/aluno-route.ts new file mode 100644 index 0000000..5b5addd --- /dev/null +++ b/src/features/alunos/aluno-route.ts @@ -0,0 +1,24 @@ +import { AlunoRepository } from './aluno-repository' +import { Router } from 'express' +import { AlunoController } from './aluno-controller' +import { AlunoService } from './aluno-service' +import { PrismaDatabase } from '../../shared/database/prisma-database' +import { AutorizacaoMiddleware } from '../../shared/middlewares/autorizacao-middleware' +import { UsuarioRepository } from '../usuarios/usuario-repository' +import { UsuarioService } from '../usuarios/usuario-service' + +const router = Router() +const database = new PrismaDatabase(); + +const alunoRepository = new AlunoRepository(database) +const usuarioRepository = new UsuarioRepository(database) +const alunoService = new AlunoService(alunoRepository) +const usuarioService = new UsuarioService(usuarioRepository) +const alunoController = new AlunoController(usuarioService, alunoService) +const middlewareAutorizacao = new AutorizacaoMiddleware() + +router.post('/create', middlewareAutorizacao.autorizarApenas('ADMIN'), alunoController.criaAluno.bind(alunoController)) +router.post('/update', middlewareAutorizacao.autorizarApenas('ADMIN'), alunoController.atualizaAluno.bind(alunoController)) +router.delete('/delete', middlewareAutorizacao.autorizarApenas('ADMIN'), alunoController.deletaAluno.bind(alunoController)); + +export { router as alunoRoutes } diff --git a/src/features/alunos/aluno-service.ts b/src/features/alunos/aluno-service.ts new file mode 100644 index 0000000..6b1778c --- /dev/null +++ b/src/features/alunos/aluno-service.ts @@ -0,0 +1,92 @@ +import { IAlunoRepository } from './aluno-repository' +import { AlunoDTO } from './aluno-dto' +import { Aluno } from '@prisma/client' +import { ReportarErrorAoSistema } from '../../shared/exceptions/ReportarErroAoSistema' + +export interface IAlunoService { + lidaComCriacaoDoAluno(data: AlunoDTO): Promise + lidaComAtualizacaoDoAluno(id: number, data: Partial): Promise + lidaComRemocaoDoAluno(id: number): Promise + lidaComBuscaDoAlunoPorId(id: number): Promise + lidaComBuscaDoAlunoPorMatricula(matricula: string): Promise +} + +export class AlunoService implements IAlunoService { + private alunoRepository: IAlunoRepository + + constructor(alunoRepository: IAlunoRepository) { + this.alunoRepository = alunoRepository + } + + async lidaComCriacaoDoAluno(data: AlunoDTO) { + if (!data.nome || !data.matricula || !data.usuarioId) { + throw new ReportarErrorAoSistema('Dados insuficientes para criar aluno. Veja a documentação: Nome e matrícula são obrigatórios. Além disso, um usuário precisa ser criado para que seu id seja vinculado a entidade aluno.') + } + + try { + data.usuarioId = Number(data.usuarioId) + + const aluno = await this.alunoRepository.salvaAluno(data) + return aluno + } catch (error) { + console.error(error) + throw new Error('Error creating aluno') + } + } + + async lidaComBuscaDoAlunoPorId(id: number) { + if (!id) { + throw new ReportarErrorAoSistema('ID do usuário não informado.') + } + try { + const aluno = await this.alunoRepository.buscaAlunoPorId(id) + if (!aluno) { + throw new Error('User not found') + } + return aluno + } catch (error) { + throw new Error('Error fetching aluno') + } + } + + async lidaComBuscaDoAlunoPorMatricula(matricula: string): Promise { + if (!matricula) { + throw new ReportarErrorAoSistema('Matricula do usuário não informada.') + } + try { + const aluno = await this.alunoRepository.buscaAlunoPorMatricula(matricula) + + return aluno + } catch (error) { + throw new Error('Error fetching aluno') + } + } + + async lidaComAtualizacaoDoAluno(id: number, data: Partial) { + if (!id) { + throw new ReportarErrorAoSistema('ID do usuário não informado.') + } + + if (!data.nome || !data.matricula || !data.usuarioId) { + throw new ReportarErrorAoSistema('Dados insuficientes para criar aluno. Veja a documentação: Nome e matrícula são obrigatórios. Além disso, um usuário precisa ser criado para que seu id seja vinculado a entidade aluno.') + } + + try { + const alunoAtualizado = await this.alunoRepository.atualizaAluno(id, data) + return alunoAtualizado + } catch (error) { + throw new Error('Error updating aluno') + } + } + + async lidaComRemocaoDoAluno(id: number) { + if (!id) { + throw new ReportarErrorAoSistema('ID do usuário não informado.') + } + try { + await this.alunoRepository.removeAluno(id) + } catch (error) { + throw new Error('Error deleting aluno') + } + } +} diff --git a/src/features/autenticacao/autenticacao-controller.ts b/src/features/autenticacao/autenticacao-controller.ts new file mode 100644 index 0000000..b2ddb50 --- /dev/null +++ b/src/features/autenticacao/autenticacao-controller.ts @@ -0,0 +1,33 @@ +import { Request, Response } from "express" +import { AutenticacaoService } from "./autenticacao-service" +import { ReportarErrorAoSistema } from "../../shared/exceptions/ReportarErroAoSistema" + +export class AutenticacaoController { + private autenticacaoService: AutenticacaoService + + constructor(autenticacaoService: AutenticacaoService) { + this.autenticacaoService = autenticacaoService + } + + async gerarToken(req: Request, res: Response) { + const { id, role } = req.body + + try { + const token = await this.autenticacaoService.lidaComGeracaoDoToken({ id, role }) + + return res.status(201).json({ + message: "Token gerado com sucesso.", + data: token + }) + } catch (error) { + if (error instanceof ReportarErrorAoSistema) { + return res.status(400).json({ + error: error.message, + details: process.env.NODE_ENV === 'development' ? error.stack : "" + }) + } else { + res.status(500).json({ error: 'Internal Server Error' }) + } + } + } +} diff --git a/src/repositories/auth-repository.ts b/src/features/autenticacao/autenticacao-repository.ts similarity index 68% rename from src/repositories/auth-repository.ts rename to src/features/autenticacao/autenticacao-repository.ts index 9bd5aeb..b5e588e 100644 --- a/src/repositories/auth-repository.ts +++ b/src/features/autenticacao/autenticacao-repository.ts @@ -1,10 +1,10 @@ -import { IDatabase } from "../database/database-interface" +import { IDatabase } from "../../shared/database/database-interface" -export interface IAuthRepository { +export interface IAutenticacaoRepository { adicionaTokenDeAutenticacao(id: number, token: string): Promise } -export class AuthRepository implements IAuthRepository { +export class AutenticacaoRepository implements IAutenticacaoRepository { private orm: IDatabase constructor(orm: IDatabase) { @@ -19,6 +19,6 @@ export class AuthRepository implements IAuthRepository { // } async adicionaTokenDeAutenticacao(userId: number, token: string): Promise { - await this.orm.adicionaTokenDeAutenticacao(userId, token) + await this.orm.adicionaTokenDeAutenticacaoAoUsuario(userId, token) } } diff --git a/src/features/autenticacao/autenticacao-route.ts b/src/features/autenticacao/autenticacao-route.ts new file mode 100644 index 0000000..b200b35 --- /dev/null +++ b/src/features/autenticacao/autenticacao-route.ts @@ -0,0 +1,16 @@ +import { Router } from 'express' +import { PrismaDatabase } from '../../shared/database/prisma-database' +import { AutenticacaoController } from './autenticacao-controller' +import { AutenticacaoService } from './autenticacao-service' +import { AutenticacaoRepository } from './autenticacao-repository' + +const router = Router() +const database = new PrismaDatabase(); + +const autenticacaoRepository = new AutenticacaoRepository(database) +const autenticacaoService = new AutenticacaoService(autenticacaoRepository) +const autenticacaoController = new AutenticacaoController(autenticacaoService) + +router.post('/token/generate', autenticacaoController.gerarToken.bind(autenticacaoController)) + +export { router as autenticacaoRoutes } diff --git a/src/services/auth-service.ts b/src/features/autenticacao/autenticacao-service.ts similarity index 61% rename from src/services/auth-service.ts rename to src/features/autenticacao/autenticacao-service.ts index c4f5186..b2d6cc4 100644 --- a/src/services/auth-service.ts +++ b/src/features/autenticacao/autenticacao-service.ts @@ -1,21 +1,21 @@ import { Role } from "@prisma/client"; -import { ReportarErrorAoSistema } from "../exceptions/ReportarErroAoSistema"; -import { IAuthRepository } from "../repositories/auth-repository" +import { ReportarErrorAoSistema } from "../../shared/exceptions/ReportarErroAoSistema"; +import { IAutenticacaoRepository } from "./autenticacao-repository" import jwt from 'jsonwebtoken' -export interface IAuthService { +export interface IAutenticacaoService { lidaComGeracaoDoToken(usuario: { id: number, role: string }): Promise } -export class AuthService implements IAuthService { - private authRepository: IAuthRepository +export class AutenticacaoService implements IAutenticacaoService { + private autenticacaoRepository: IAutenticacaoRepository private segredo: string; - constructor(authRepository: IAuthRepository) { - this.authRepository = authRepository + constructor(autenticacaoRepository: IAutenticacaoRepository) { + this.autenticacaoRepository = autenticacaoRepository this.segredo = process.env.JWT_SECRET || 'segredo_padrao' } - + public async lidaComGeracaoDoToken(usuario: { id: number, role: string }): Promise { const payload = { id: usuario.id, @@ -27,7 +27,7 @@ export class AuthService implements IAuthService { }); try { - await this.authRepository.adicionaTokenDeAutenticacao(usuario.id, token) + await this.autenticacaoRepository.adicionaTokenDeAutenticacao(usuario.id, token) return token; } catch (err) { throw new ReportarErrorAoSistema("Erro ao tentar salvar token no banco de dados") @@ -37,7 +37,7 @@ export class AuthService implements IAuthService { public async lidaComGeracaoDoTokenDoDiscente(usuario: { id: number }): Promise { const payload = { id: usuario.id, - role: Role.DISCENTE, + role: Role.ALUNO, }; const token = jwt.sign(payload, this.segredo, { @@ -45,7 +45,7 @@ export class AuthService implements IAuthService { }); try { - await this.authRepository.adicionaTokenDeAutenticacao(usuario.id, token) + await this.autenticacaoRepository.adicionaTokenDeAutenticacao(usuario.id, token) return token; } catch (err) { throw new ReportarErrorAoSistema("Erro ao tentar salvar token no banco de dados", err) diff --git a/src/controllers/disciplina-controller.ts b/src/features/disciplinas/disciplina-controller.ts similarity index 92% rename from src/controllers/disciplina-controller.ts rename to src/features/disciplinas/disciplina-controller.ts index cc00f4e..9eeb0ed 100644 --- a/src/controllers/disciplina-controller.ts +++ b/src/features/disciplinas/disciplina-controller.ts @@ -1,7 +1,7 @@ // constructor(private userService: IUserService) {} import { Request, Response } from "express"; -import { IDisciplinaService } from "../services/disciplina-service"; +import { IDisciplinaService } from "./disciplina-service"; // async importUserData(req: Request, res: Response) { // const { login, senha, vinculo } = req.body @@ -16,7 +16,7 @@ import { IDisciplinaService } from "../services/disciplina-service"; export class DisciplinaController { private disciplinaService: IDisciplinaService - + constructor(disciplinaService: IDisciplinaService) { this.disciplinaService = disciplinaService } @@ -33,7 +33,7 @@ export class DisciplinaController { } - public async importaDisciplinas(req: Request, res: Response): Promise {} + public async importaDisciplinas(req: Request, res: Response): Promise { } } // toString() { diff --git a/src/features/disciplinas/disciplina-repository.ts b/src/features/disciplinas/disciplina-repository.ts new file mode 100644 index 0000000..06d516d --- /dev/null +++ b/src/features/disciplinas/disciplina-repository.ts @@ -0,0 +1,18 @@ +import { IDatabase } from "../../shared/database/database-interface"; +import { DisciplinaHistoricoProps } from "../../shared/importers/implementation/ufcg-importer"; + +export interface IDisciplinaRepository { + saveDisciplinas(disciplinas: DisciplinaHistoricoProps[]): Promise +} + +export class DisciplinaRepository implements IDisciplinaRepository { + private orm: IDatabase + + constructor(orm: IDatabase) { + this.orm = orm + } + + saveDisciplinas(disciplinas: DisciplinaHistoricoProps[]): Promise { + return this.orm.salvaVariasDisciplinas(disciplinas) + } +} diff --git a/src/routes/disciplina-route.ts b/src/features/disciplinas/disciplina-route.ts similarity index 68% rename from src/routes/disciplina-route.ts rename to src/features/disciplinas/disciplina-route.ts index 4b7f8e5..a96ed0d 100644 --- a/src/routes/disciplina-route.ts +++ b/src/features/disciplinas/disciplina-route.ts @@ -1,17 +1,17 @@ -import { DisciplinaRepository } from './../repositories/disciplina-repository' import { Router } from 'express' -import { DisciplinaService } from '../services/disciplina-service' -import { PrismaDatabase } from '../database/prisma-database' -import { DisciplinaController } from '../controllers/disciplina-controller' -import { AutorizacaoMiddleware } from '../middlewares/autorizacao-middleware' +import { DisciplinaService } from './disciplina-service' +import { DisciplinaController } from './disciplina-controller' +import { PrismaDatabase } from '../../shared/database/prisma-database' +import { AutorizacaoMiddleware } from '../../shared/middlewares/autorizacao-middleware' +import { DisciplinaRepository } from './../../features/disciplinas/disciplina-repository' const router = Router() const database = new PrismaDatabase(); +const middlewareAutorizacao = new AutorizacaoMiddleware() const disciplinaRepository = new DisciplinaRepository(database) const disciplinaService = new DisciplinaService(disciplinaRepository) const disciplinaController = new DisciplinaController(disciplinaService) -const middlewareAutorizacao = new AutorizacaoMiddleware() router.post('/create', middlewareAutorizacao.autorizarApenas('ADMIN'), disciplinaController.criaDisciplina.bind(disciplinaController)) router.post('/update', middlewareAutorizacao.autorizarApenas('ADMIN'), disciplinaController.atualizaDisciplina.bind(disciplinaController)) diff --git a/src/features/disciplinas/disciplina-service.ts b/src/features/disciplinas/disciplina-service.ts new file mode 100644 index 0000000..e3bbf32 --- /dev/null +++ b/src/features/disciplinas/disciplina-service.ts @@ -0,0 +1,44 @@ +import { Disciplina } from "@prisma/client" +import { IDisciplinaRepository } from "../../features/disciplinas/disciplina-repository" +import { UFCGImporter } from "../../shared/importers/implementation/ufcg-importer" +import { Importer } from "../../shared/importers/importer" +import { SessaoDTO } from "../../features/sessoes/sessao-dto" + +export interface IDisciplinaService { + lidaComImportacaoDasDisciplinasDoDiscente(sessaoDTO: SessaoDTO, importer: Importer): Promise +} + +export class DisciplinaService implements IDisciplinaService { + private disciplinaRepository: IDisciplinaRepository + + constructor(disciplinaRepository: IDisciplinaRepository) { + this.disciplinaRepository = disciplinaRepository + } + + async lidaComImportacaoDasDisciplinasDoDiscente(sessaoDTO: SessaoDTO, importer: Importer): Promise { + const { matricula, senha, alunoId } = sessaoDTO + + if (!sessaoDTO.matricula || !sessaoDTO.senha) { + throw new Error("Login, senha são obrigatórios") + } + + + const disciplinasImportadas = await importer.obterDisciplinasDoHistoricoAcademico(alunoId!) + const disciplinasImportadasDetalhadas = await importer.obterDetalhesDaDisciplina() + + console.log(`importUserData: ${JSON.stringify(disciplinasImportadas, null, 2)}`) + + const countDisciplinasCriadas = await this.disciplinaRepository.saveDisciplinas(disciplinasImportadas) + + return countDisciplinasCriadas + } + + switchImporter(vinculo: string) { + switch (vinculo) { + case "UFCG": + return new UFCGImporter() + default: + throw new Error("Importer error") + } + } +} diff --git a/src/features/sessoes/sessao-controller.ts b/src/features/sessoes/sessao-controller.ts new file mode 100644 index 0000000..bc47de0 --- /dev/null +++ b/src/features/sessoes/sessao-controller.ts @@ -0,0 +1,81 @@ +import { Request, Response } from "express"; +import { PayloadUsuarioAlreadyExists } from "../../shared/middlewares/autorizacao-middleware"; +import { UsuarioService } from "../../features/usuarios/usuario-service"; +import { AlunoService } from "../../features/alunos/aluno-service"; +import { DisciplinaService } from "../../features/disciplinas/disciplina-service"; +import { SessaoDTO } from "../../features/sessoes/sessao-dto"; +import { SessaoService } from "./sessao-service"; +import { Role } from "@prisma/client"; +import { Importer } from "../../shared/importers/importer"; + +export class SessaoController { + private usuarioService: UsuarioService + private alunoService: AlunoService + private disciplinaService: DisciplinaService + private sessaoService: SessaoService + + constructor(usuarioService: UsuarioService, alunoService: AlunoService, disciplinaService: DisciplinaService, sessaoService: SessaoService) { + this.usuarioService = usuarioService, + this.alunoService = alunoService + this.disciplinaService = disciplinaService + this.sessaoService = sessaoService + } + + public async criaConexaoComSessao(req: Request, res: Response): Promise { + interface CustomRequest extends Request { + usuario: PayloadUsuarioAlreadyExists; + } + + const sessaoDTO: SessaoDTO = req.body; + + // customReq agora compartilha o mesmo acesso de memória que req + // const customReq: CustomRequest = req as CustomRequest; + + // const { usuarioAlreadyExists } = customReq.usuario; + + // if (usuarioAlreadyExists) { + // this.retornaDadosDoAluno() + // } + + this.configuraSessaoDoUsuario(sessaoDTO) + + + res.status(200).json({ message: 'Conexão com o sessao criada com sucesso!' }) + } + + private async configuraSessaoDoUsuario(sessaoDTO: SessaoDTO) { + const { matricula, senha, vinculo, alunoId } = sessaoDTO + + const importer: Importer = this.sessaoService.lidaComIdentificacaoDaInstituicaoDeEnsino(vinculo) + + try { + await importer.autenticaAluno(matricula, senha) + + const dadosAluno = { + nome: 'Huandrey', + } + + const usuarioId: number = await this.usuarioService.lidaComCriacaoDoUsuario({ nome: dadosAluno.nome }) + + if (!usuarioId) { + throw new Error('Erro ao cadastrar o usuário') + } + + const aluno = await this.alunoService.lidaComCriacaoDoAluno({ + nome: dadosAluno.nome, + matricula: matricula, + usuarioId, + }); + + if (!aluno) { + throw new Error('Erro ao cadastrar o aluno') + } + + this.disciplinaService.lidaComImportacaoDasDisciplinasDoDiscente({ ...sessaoDTO, alunoId: aluno.id }, importer) + } catch (err) { + console.error(err) + } + + } + +} diff --git a/src/dtos/session-dto.ts b/src/features/sessoes/sessao-dto.ts similarity index 53% rename from src/dtos/session-dto.ts rename to src/features/sessoes/sessao-dto.ts index 4666cc5..fa095da 100644 --- a/src/dtos/session-dto.ts +++ b/src/features/sessoes/sessao-dto.ts @@ -1,6 +1,6 @@ -export type SessionDTO = { +export type SessaoDTO = { matricula: string senha: string vinculo: string - discenteId?: number + alunoId?: number } diff --git a/src/features/sessoes/sessao-route.ts b/src/features/sessoes/sessao-route.ts new file mode 100644 index 0000000..581ddde --- /dev/null +++ b/src/features/sessoes/sessao-route.ts @@ -0,0 +1,34 @@ +import { Router } from 'express' +import { PrismaDatabase } from '../../shared/database/prisma-database' +import { SessaoController } from './sessao-controller'; +import { AlunoService } from '../../features/alunos/aluno-service'; +import { AlunoRepository } from '../../features/alunos/aluno-repository'; +import { DisciplinaRepository } from '../../features/disciplinas/disciplina-repository'; +import { UsuarioRepository } from '../../features/usuarios/usuario-repository'; +import { DisciplinaService } from '../../features/disciplinas/disciplina-service'; +import { UsuarioService } from '../../features/usuarios/usuario-service'; +import { AutorizacaoMiddleware } from '../../shared/middlewares/autorizacao-middleware'; +import { SessaoService } from './sessao-service'; + +const router = Router() +const database = new PrismaDatabase(); + +const alunoRepository = new AlunoRepository(database) +const disciplinaRepository = new DisciplinaRepository(database) +const usuarioRepository = new UsuarioRepository(database) + +const usuarioService = new UsuarioService(usuarioRepository) +const alunoService = new AlunoService(alunoRepository) +const disciplinaService = new DisciplinaService(disciplinaRepository) +const sessaoService = new SessaoService() + +const sessaoController = new SessaoController( + usuarioService, + alunoService, + disciplinaService, + sessaoService, +) + +router.post('/session', sessaoController.criaConexaoComSessao.bind(sessaoController)) + +export { router as sessaoRoutes } diff --git a/src/services/sistema-service.ts b/src/features/sessoes/sessao-service.ts similarity index 61% rename from src/services/sistema-service.ts rename to src/features/sessoes/sessao-service.ts index 9b1c085..ed8aba4 100644 --- a/src/services/sistema-service.ts +++ b/src/features/sessoes/sessao-service.ts @@ -1,10 +1,10 @@ -import { UFCGImporter } from "../importers/implementation/ufcg-importer" -import { Importer } from "../importers/importer" +import { UFCGImporter } from "../../shared/importers/implementation/ufcg-importer" +import { Importer } from "../../shared/importers/importer" -export interface ISistemaService { +export interface ISessaoService { lidaComIdentificacaoDaInstituicaoDeEnsino(vinculo: string): Importer } -export class SistemaService implements ISistemaService { +export class SessaoService implements ISessaoService { lidaComIdentificacaoDaInstituicaoDeEnsino(vinculo: string): Importer { return this.switchImporter(vinculo) } diff --git a/src/controllers/user-controller.ts b/src/features/usuarios/usuario-controller.ts similarity index 65% rename from src/controllers/user-controller.ts rename to src/features/usuarios/usuario-controller.ts index 6b3840f..c67a3b6 100644 --- a/src/controllers/user-controller.ts +++ b/src/features/usuarios/usuario-controller.ts @@ -1,28 +1,28 @@ +import { UsuarioDTO } from './usuario-dto' import { Request, Response } from 'express' -import { UserDTO } from "../dtos/user-dto" -import { IUserService } from '../services/user-service' -import { ReportarErrorAoSistema } from '../exceptions/ReportarErroAoSistema' +import { IUsuarioService } from './usuario-service' +import { ReportarErrorAoSistema } from '../../shared/exceptions/ReportarErroAoSistema' export interface CriaUsuarioRequest extends Request { - body: UserDTO + body: UsuarioDTO } export interface AtualizaUsuarioRequest extends Request { - body: UserDTO + body: UsuarioDTO } -export class UserController { - private userService: IUserService +export class UsuarioController { + private usuarioService: IUsuarioService - constructor(userService: IUserService) { - this.userService = userService + constructor(usuarioService: IUsuarioService) { + this.usuarioService = usuarioService } public async criaUsuario(req: CriaUsuarioRequest, res: Response): Promise { try { const { nome, role } = req.body - const user = await this.userService.lidaComCriacaoDoUsuario({ nome, role }) - res.status(201).json(user) + const usuario = await this.usuarioService.lidaComCriacaoDoUsuario({ nome, role }) + res.status(201).json(usuario) } catch (error) { if (error instanceof ReportarErrorAoSistema) { res.status(400).json({ error: error.message }) @@ -35,16 +35,15 @@ export class UserController { public async atualizaUsuario(req: AtualizaUsuarioRequest, res: Response): Promise { try { - const userId = parseInt(req.params.id, 10) - console.log(userId) - const updates = req.body + const usuarioId: number = parseInt(req.params.id, 10) + const usuarioDTO: UsuarioDTO = req.body - const updatedUser = await this.userService.lidaComAtualizacaoDoUsuario(userId, updates) - - if (updatedUser) { - res.status(200).json(updatedUser) + const updatedUsuario = await this.usuarioService.lidaComAtualizacaoDoUsuario(usuarioId, usuarioDTO) + + if (updatedUsuario) { + res.status(200).json(updatedUsuario) } else { - res.status(404).json({ error: 'User not found' }) + res.status(404).json({ error: 'Usuario not found' }) } } catch (error) { if (error instanceof ReportarErrorAoSistema) { @@ -58,8 +57,8 @@ export class UserController { public async deletaUsuario(req: Request, res: Response): Promise { try { - const userId = parseInt(req.params.id, 10) - await this.userService.lidaComRemocaoDoUsuario(userId) + const usuarioId = parseInt(req.params.id, 10) + await this.usuarioService.lidaComRemocaoDoUsuario(usuarioId) res.status(204).send() // No Content } catch (error) { if (error instanceof ReportarErrorAoSistema) { @@ -73,9 +72,9 @@ export class UserController { public async buscaUsuario(req: Request, res: Response): Promise { try { - const userId = parseInt(req.params.id, 10) - const usuarioEncontrado = await this.userService.lidaComBuscaDoUsuarioPorId(userId) - + const usuarioId = parseInt(req.params.id, 10) + const usuarioEncontrado = await this.usuarioService.lidaComBuscaDoUsuarioPorId(usuarioId) + if (!usuarioEncontrado) { res.status(200).json({ message: 'Usuário não encontrado', @@ -96,8 +95,8 @@ export class UserController { public async buscaUsuarios(req: Request, res: Response): Promise { try { - const usuariosEncontrados = await this.userService.lidaComBuscaDosUsuarios() - + const usuariosEncontrados = await this.usuarioService.lidaComBuscaDeTodosUsuariosDoSistema() + res.status(200).json({ message: "Usuários encontrados", data: usuariosEncontrados diff --git a/src/dtos/user-dto.ts b/src/features/usuarios/usuario-dto.ts similarity index 57% rename from src/dtos/user-dto.ts rename to src/features/usuarios/usuario-dto.ts index 61d8e75..2734a92 100644 --- a/src/dtos/user-dto.ts +++ b/src/features/usuarios/usuario-dto.ts @@ -1,6 +1,6 @@ import { Role } from "@prisma/client" -export type UserDTO = { +export type UsuarioDTO = { nome: string - role: Role + role?: Role } diff --git a/src/features/usuarios/usuario-repository.ts b/src/features/usuarios/usuario-repository.ts new file mode 100644 index 0000000..7b1de1d --- /dev/null +++ b/src/features/usuarios/usuario-repository.ts @@ -0,0 +1,45 @@ +import { Usuario } from "@prisma/client" +import { IDatabase } from "../../shared/database/database-interface" +import { UsuarioDTO } from "./usuario-dto" + +export interface IUsuarioRepository { + criaUsuario(usuarioDTO: UsuarioDTO): Promise + atualizaUsuario(id: number, data: Partial): Promise + deletaUsuario(id: number): Promise + buscaUsuarioPorId(id: number): Promise + buscaUsuarioPorEmail(email: string): Promise + buscaPorTodosUsuarios(): Promise +} + +export class UsuarioRepository implements IUsuarioRepository { + private orm: IDatabase + + constructor(orm: IDatabase) { + this.orm = orm + } + + async criaUsuario(data: UsuarioDTO): Promise { + const usuario = await this.orm.salvaUsuario(data) + return usuario.id + } + + async atualizaUsuario(id: number, data: UsuarioDTO): Promise { + return this.orm.atualizaUsuario(id, data) + } + + buscaUsuarioPorId(id: number): Promise { + return this.orm.buscaUsuarioPorId(id) + } + + buscaUsuarioPorEmail(email: string): Promise { + return this.orm.buscaUsuarioPorEmail(email) + } + + async buscaPorTodosUsuarios(): Promise { + return this.orm.buscaPorTodosUsuarios() + } + + async deletaUsuario(id: number): Promise { + await this.orm.removeUsuario(id) + } +} diff --git a/src/features/usuarios/usuario-route.ts b/src/features/usuarios/usuario-route.ts new file mode 100644 index 0000000..b2e86c9 --- /dev/null +++ b/src/features/usuarios/usuario-route.ts @@ -0,0 +1,21 @@ +import { Router } from 'express' +import { UsuarioService } from './usuario-service' +import { UsuarioRepository } from './usuario-repository' +import { UsuarioController } from './usuario-controller' +import { PrismaDatabase } from '../../shared/database/prisma-database' +import { AutorizacaoMiddleware } from '../../shared/middlewares/autorizacao-middleware' + +const router = Router() +const database = new PrismaDatabase(); + +const usuarioRepository = new UsuarioRepository(database) +const middlewareAutorizacao = new AutorizacaoMiddleware() +const usuarioService = new UsuarioService(usuarioRepository) +const usuarioController = new UsuarioController(usuarioService) + +router.get('', usuarioController.buscaUsuarios.bind(usuarioController)) +router.post('/create', usuarioController.criaUsuario.bind(usuarioController)) +router.put('/update/:id', middlewareAutorizacao.autorizarApenas('ADMIN'), usuarioController.atualizaUsuario.bind(usuarioController)) +router.delete('/delete/:id', middlewareAutorizacao.autorizarApenas('ADMIN'), usuarioController.deletaUsuario.bind(usuarioController)) + +export { router as usuarioRoutes } diff --git a/src/features/usuarios/usuario-service.ts b/src/features/usuarios/usuario-service.ts new file mode 100644 index 0000000..307b7ed --- /dev/null +++ b/src/features/usuarios/usuario-service.ts @@ -0,0 +1,79 @@ +import { IUsuarioRepository } from './usuario-repository' +import { UsuarioDTO } from './usuario-dto' +import { Role, Usuario } from '@prisma/client' +import { ReportarErrorAoSistema } from '../../shared/exceptions/ReportarErroAoSistema' + +export interface IUsuarioService { + lidaComCriacaoDoUsuario(usuarioDTO: UsuarioDTO): Promise + lidaComBuscaDoUsuarioPorId(id: number): Promise + lidaComAtualizacaoDoUsuario(id: number, data: Partial): Promise + lidaComRemocaoDoUsuario(id: number): Promise + lidaComBuscaDeTodosUsuariosDoSistema(): Promise +} +export class UsuarioService implements IUsuarioService { + private usuarioRepository: IUsuarioRepository + + constructor(usuarioRepository: IUsuarioRepository) { + this.usuarioRepository = usuarioRepository + } + + async lidaComCriacaoDoUsuario(usuarioDTO: UsuarioDTO): Promise { + const { nome, role = Role.ALUNO } = usuarioDTO + + try { + if (!nome || typeof nome !== 'string') { + throw new ReportarErrorAoSistema('Nome é obrigatório e precisa ser uma string.') + } + + const usuarioId = await this.usuarioRepository.criaUsuario({ nome, role }) + return usuarioId + } catch (error) { + throw new ReportarErrorAoSistema('Erro ao tentar salvar usuário ao banco de dados.') + } + } + + async lidaComBuscaDoUsuarioPorId(id: number) { + try { + const usuario = await this.usuarioRepository.buscaUsuarioPorId(id) + if (!usuario) { + throw new Error('Usuario not found') + } + return usuario + } catch (error) { + throw new Error('Erro ao buscar usuário') + } + } + async lidaComAtualizacaoDoUsuario(id: number, data: Partial) { + if (!data.nome || !data.role) { + throw new ReportarErrorAoSistema('Nome e role são obrigatórios') + } + try { + const updatedUsuario = await this.usuarioRepository.atualizaUsuario(id, data) + return updatedUsuario + } catch (error) { + throw new Error('Erro ao atualizar o usuário') + } + } + + async lidaComRemocaoDoUsuario(id: number) { + try { + await this.usuarioRepository.deletaUsuario(id) + } catch (error) { + throw new Error('Error deleting usuario') + } + } + + async lidaComBuscaDeTodosUsuariosDoSistema() { + try { + const usuarios = await this.usuarioRepository.buscaPorTodosUsuarios() + + if (!usuarios) { + return [] + } + + return usuarios + } catch (error) { + throw new Error('Error fetching usuario') + } + } +} diff --git a/src/importers/implementation/ufcg-importer.ts b/src/importers/implementation/ufcg-importer.ts deleted file mode 100644 index c9044ec..0000000 --- a/src/importers/implementation/ufcg-importer.ts +++ /dev/null @@ -1,222 +0,0 @@ -import axios from 'axios' -import * as cheerio from 'cheerio' // Importação correta -import he from 'he' -import { Importer } from '../importer' -import { Disciplina, DisciplinaStatus } from '@prisma/client' -import iconv from 'iconv-lite'; - -export class UFCGImporter implements Importer { - private URL_BASE = "https://pre.ufcg.edu.br:8443/ControleAcademicoOnline/Controlador" - private URL_LOGIN = this.URL_BASE - private LOGIN = "login" - private SENHA = "senha" - private COMMAND = "command" - private ALUNO_LOGIN = "AlunoLogin" - - private REP_FALTA = "Reprovado por Falta" - private TRANCADO = "Trancado" - private DISPENSA = "Dispensa" - private APROVADO = "Aprovado" - private REPROVADO = "Reprovado" - private ERRO_AUTENTICACAO = "ERRO NA AUTENTICAÇÃO" - public static readonly ERRO = "Matrícula inválida ou senha incorreta." - - reportProgress(message: string): void { - console.log(message) // Implement your progress reporting logic here - } - - private cleanText = (text: string): string => { - return he.decode(text) // Decodifica entidades HTML - .replace(/\s+/g, ' ') - .trim() // Remove espaços em branco no início e no final - } - - public async importaDisciplinas(discenteId: number, matricula: string, senha: string): Promise { - const disciplinas: any = [] - const codigos: string[] = [] - this.reportProgress("Tentando fazer login...") - - try { - // Se autentica e armazena o cookie - const response = await axios.post(this.URL_LOGIN, new URLSearchParams({ - [this.LOGIN]: matricula, - [this.SENHA]: senha, - [this.COMMAND]: this.ALUNO_LOGIN - }), { - timeout: 10000, - withCredentials: true, - validateStatus: () => true, - httpsAgent: new (require('https').Agent)({ - rejectUnauthorized: false - }), - responseType: 'arraybuffer', // Receber os dados como array buffer - responseEncoding: 'binary', - }) - - const cookies: string[] = response.headers['set-cookie']! - let html = iconv.decode(Buffer.from(response.data), 'ISO-8859-1'); - - // Substituir por - html = html.replace(/]*charset=["']?[^"'>]+["']?[^>]*>/i, ''); - - const $ = cheerio.load(html) // Carregando o HTML - - if ($('head').text().includes(this.ERRO_AUTENTICACAO) || $('body').text().includes(UFCGImporter.ERRO)) { - throw new Error("Authentication Failed") - } - - console.log(response.data) - - // Abre a página do Histórico e pega o texto das disciplinas - this.reportProgress("Obtendo histórico...") - const historicoResponse = await axios.get(this.URL_LOGIN, { - params: { [this.COMMAND]: "AlunoHistorico" }, - headers: { Cookie: cookies.join(' ') }, - timeout: 10000, - httpsAgent: new (require('https').Agent)({ - rejectUnauthorized: false - }), - responseType: 'arraybuffer', // Receber os dados como array buffer - responseEncoding: 'binary', - }) - - let htmlHistoricoResponse = iconv.decode(Buffer.from(historicoResponse.data), 'ISO-8859-1'); - htmlHistoricoResponse = htmlHistoricoResponse.replace(/]*charset=["']?[^"'>]+["']?[^>]*>/i, ''); - console.log(htmlHistoricoResponse) - const historico$ = cheerio.load(htmlHistoricoResponse) - - const trsDisciplinas: any = historico$("div[id=disciplinas] > table > tbody > tr").toArray() - - for (const el of trsDisciplinas) { - const tds = historico$(el).find("td") - const disciplina = { - codigo: tds.eq(0).text() || '', - nome: this.cleanText(tds.eq(1).text()) || '', - creditos: parseInt(tds.eq(3).text() || '0', 10), - quantidadeProvas: this.calcularQtdeNotas(parseInt(tds.eq(3).text() || '0', 10)), - quantidadeFaltas: this.calcularQtdeFaltas(parseInt(tds.eq(3).text() || '0', 10)), - semestre: tds.eq(7).text() || '', - mediaFinal: isNaN(parseFloat(tds.eq(5).text().replace(",", "."))) ? null : parseFloat(tds.eq(5).text().replace(",", ".")), - status: '', - cursoId: 1, - discenteId, - } - - switch (tds.eq(6).text()) { - case this.APROVADO: - case this.DISPENSA: - disciplina.status = DisciplinaStatus.APENAS_MEDIA_APROVADO - if (disciplina.mediaFinal === null) { - disciplina.mediaFinal = 7.0 - } - break - case this.REPROVADO: - disciplina.status = DisciplinaStatus.APENAS_MEDIA_REPROVADO - break - case this.REP_FALTA: - disciplina.status = DisciplinaStatus.REPROVADO_POR_FALTA - disciplina.mediaFinal = 0 - break - case this.TRANCADO: - disciplina.status = DisciplinaStatus.TRANCADA - default: - disciplina.status = DisciplinaStatus.EM_PROGRESSO - break - } - - disciplinas.push(disciplina) - codigos.push(tds.eq(0).text() || '') - } - - if (disciplinas.length === 0) { - throw new Error("Você não possui histórico acadêmico") - } - - this.reportProgress("Obtendo detalhes das disciplinas...") - const horarioResponse = await axios.get(this.URL_BASE, { - params: { [this.COMMAND]: "AlunoTurmasListar" }, - headers: { Cookie: cookies.join(' ') }, - httpsAgent: new (require('https').Agent)({ - rejectUnauthorized: false - }), - responseType: 'arraybuffer', // Receber os dados como array buffer - responseEncoding: 'binary', - }) - - let htmlHorarioResponse = iconv.decode(Buffer.from(horarioResponse.data), 'ISO-8859-1'); - htmlHorarioResponse = htmlHorarioResponse.replace(/]*charset=["']?[^"'>]+["']?[^>]*>/i, ''); - - const horario$ = cheerio.load(htmlHorarioResponse) - const table = horario$("table") - - if (table) { - const trs = table.find("tr") - trs.each((index, element) => { - if (index === 0) return // Skip header row - - const tds = horario$(element).children() - const disciplina = this.findDisciplina(disciplinas, codigos, tds.eq(1).text() || '') - - if (disciplina) { - disciplina.horario = `Turma: ${tds.eq(3).text()}\nHorário: ${tds.eq(4).text()}` - } - }) - } - - } catch (error: any) { - if (axios.isAxiosError(error)) { - if (error.code === 'ECONNABORTED') { - throw new Error("Não foi possível buscar os dados. Tente novamente.") - } else if (error.message.includes('ENOTFOUND')) { - throw new Error("Não foi possível acessar o site da Universidade. Você está conectado a internet?") - } else if (error.message.includes('UNABLE_TO_VERIFY_LEAF_SIGNATURE')) { - throw new Error("Não foi possível acessar o site. Tente novamente") - } - } - - throw new Error(error.message) - } - - return disciplinas - } - - private findDisciplina(disciplinas: Disciplina[], codigos: string[], codigo: string): Disciplina | undefined { - const disciplinasFind = disciplinas.filter((disciplina, index) => codigos[index] === codigo) - - return disciplinasFind.reduce((latest, disciplina) => { - return (!latest || disciplina.semestre > latest.semestre) ? disciplina : latest - }, undefined as Disciplina | undefined) - } - - private calcularQtdeNotas(creditos: number): number { - switch (creditos) { - case 1: - case 2: - case 3: - return 2 - case 4: - return 3 - case 5: - case 6: - return 4 - default: - return 1 - } - } - - private calcularQtdeFaltas(creditos: number): number { - switch (creditos) { - case 1: - case 2: - case 3: - return 4 - case 4: - return 5 - case 5: - case 6: - return 6 - default: - return 3 - } - } -} diff --git a/src/importers/importer.ts b/src/importers/importer.ts deleted file mode 100644 index ae7532a..0000000 --- a/src/importers/importer.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { Disciplina } from "@prisma/client" - -export interface Importer { - importaDisciplinas(discenteId: number, matricula: string, senha: string): Promise - reportProgress(msg: string): void -} diff --git a/src/repositories/discente-repository.ts b/src/repositories/discente-repository.ts deleted file mode 100644 index 667d077..0000000 --- a/src/repositories/discente-repository.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { Discente } from "@prisma/client" -import { DiscenteDTO } from "../dtos/discente-dto" -import { IDatabase } from "../database/database-interface" - -export interface IDiscenteRepository { - criaDiscente(data: DiscenteDTO): Promise - atualizaDiscente(id: number, data: Partial): Promise - deletaDiscente(id: number): Promise - buscaDiscentePorId(id: number): Promise - buscaDiscentePorEmail(email: string): Promise - buscaDiscentePorMatricula(matricula: string): Promise -} - -export type AtualizaDiscenteDTO = { - email?: string - name?: string - password?: string - role?: string -} - -export class DiscenteRepository implements IDiscenteRepository { - private orm: IDatabase - - constructor(orm: IDatabase) { - this.orm = orm - } - - async criaDiscente(data: Discente): Promise { - return this.orm.criaDiscente(data) - } - - async atualizaDiscente(id: number, data: DiscenteDTO): Promise { - return this.orm.atualizaDiscente(id, data) - } - - async deletaDiscente(id: number): Promise { - await this.orm.deletaDiscente(id) - } - - async buscaDiscentePorId(id: number): Promise { - return this.orm.buscaDiscentePorId(id) - } - - async buscaDiscentePorMatricula(matricula: string): Promise { - return this.orm.buscaDiscentePorMatricula(matricula) - } - - async buscaDiscentePorEmail(email: string): Promise { - return this.orm.buscaDiscentePorEmail(email) - } -} diff --git a/src/repositories/disciplina-repository.ts b/src/repositories/disciplina-repository.ts deleted file mode 100644 index 2f83d50..0000000 --- a/src/repositories/disciplina-repository.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Disciplina } from "@prisma/client"; -import { IDatabase } from "../database/database-interface"; - -export interface IDisciplinaRepository { - saveDisciplinas(disciplinas: Disciplina[]): Promise -} - -export class DisciplinaRepository implements IDisciplinaRepository { - private orm: IDatabase - - constructor(orm: IDatabase) { - this.orm = orm - } - - saveDisciplinas(disciplinas: Disciplina[]): Promise { - return this.orm.salvaDisciplinas(disciplinas) - } -} diff --git a/src/repositories/user-repository.ts b/src/repositories/user-repository.ts deleted file mode 100644 index 7fe3b5f..0000000 --- a/src/repositories/user-repository.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { User } from "@prisma/client" -import { IDatabase } from "../database/database-interface" -import { UserDTO } from "../dtos/user-dto" - -export interface IUserRepository { - criaUsuario(data: UserDTO): Promise - atualizaUsuario(id: number, data: Partial): Promise - deletaUsuario(id: number): Promise - buscaUsuarioPorId(id: number): Promise - buscaUsuarioPorEmail(email: string): Promise - buscaUsuarios(): Promise -} - -export type UpdateUserDTO = { - email?: string - name?: string - password?: string - role?: string -} - -export class UserRepository implements IUserRepository { - private orm: IDatabase - - constructor(orm: IDatabase) { - this.orm = orm - } - buscaUsuarioPorId(id: number): Promise { - throw new Error("Method not implemented.") - } - buscaUsuarioPorEmail(email: string): Promise { - throw new Error("Method not implemented.") - } - async criaUsuario(data: UserDTO): Promise { - const user = await this.orm.criaUsuario(data) - return { - id: user.id, - token: '', - nome: user.nome, - role: user.role, - createdAt: user.createdAt - } - } - - async atualizaUsuario(id: number, data: UserDTO): Promise { - return this.orm.atualizaUsuario(id, data) - } - - async deletaUsuario(id: number): Promise { - await this.orm.deletaUsuario(id) - } - - async findUserById(id: number): Promise { - return this.orm.buscaUsuarioPorId(id) - } - - async findUserByEmail(email: string): Promise { - return this.orm.buscaUsuarioPorEmail(email) - } - - async buscaUsuarios(): Promise { - return this.orm.buscaPorUsuarios() - } -} diff --git a/src/routes/auth-route.ts b/src/routes/auth-route.ts deleted file mode 100644 index b305c18..0000000 --- a/src/routes/auth-route.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Router } from 'express' -import { PrismaDatabase } from '../database/prisma-database' -import { AuthController } from '../controllers/auth-controller' -import { AuthService } from '../services/auth-service' -import { AuthRepository } from '../repositories/auth-repository' - -const router = Router() -const database = new PrismaDatabase(); - -const authRepository = new AuthRepository(database) -const authService = new AuthService(authRepository) -const authController = new AuthController(authService) - -router.post('/token/generate', authController.gerarToken.bind(authController)) - -export { router as authRoutes } diff --git a/src/routes/discente-route.ts b/src/routes/discente-route.ts deleted file mode 100644 index b5d96f4..0000000 --- a/src/routes/discente-route.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { DiscenteRepository } from './../repositories/discente-repository' -import { Router } from 'express' -import { DiscenteController } from '../controllers/discente-controller' -import { DiscenteService } from '../services/discente-service' -import { PrismaDatabase } from '../database/prisma-database' -import { AutorizacaoMiddleware } from '../middlewares/autorizacao-middleware' - -const router = Router() -const database = new PrismaDatabase(); - -const discenteRepository = new DiscenteRepository(database) -const discenteService = new DiscenteService(discenteRepository) -const discenteController = new DiscenteController(discenteService) -const middlewareAutorizacao = new AutorizacaoMiddleware() - -router.post('/create', middlewareAutorizacao.autorizarApenas('ADMIN'), discenteController.criaDiscente.bind(discenteController)) -router.post('/update', middlewareAutorizacao.autorizarApenas('ADMIN'), discenteController.atualizaDiscente.bind(discenteController)) -router.delete('/delete', middlewareAutorizacao.autorizarApenas('ADMIN'), discenteController.deletaDiscente.bind(discenteController)); - -export { router as discenteRoutes } diff --git a/src/routes/sistema-route.ts b/src/routes/sistema-route.ts deleted file mode 100644 index 6de0b29..0000000 --- a/src/routes/sistema-route.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Router } from 'express' -import { PrismaDatabase } from '../database/prisma-database' -import { SistemaController } from '../controllers/sistema-controller'; -import { DiscenteService } from '../services/discente-service'; -import { DiscenteRepository } from '../repositories/discente-repository'; -import { DisciplinaRepository } from '../repositories/disciplina-repository'; -import { UserRepository } from '../repositories/user-repository'; -import { DisciplinaService } from '../services/disciplina-service'; -import { UserService } from '../services/user-service'; -import { AutorizacaoMiddleware } from '../middlewares/autorizacao-middleware'; -import { SistemaService } from '../services/sistema-service'; - -const router = Router() -const database = new PrismaDatabase(); - -const discenteRepository = new DiscenteRepository(database) -const disciplinaRepository = new DisciplinaRepository(database) -const userRepository = new UserRepository(database) - -const userService = new UserService(userRepository) -const discenteService = new DiscenteService(discenteRepository) -const disciplinaService = new DisciplinaService(disciplinaRepository) -const sistemaService = new SistemaService() - -const sistemaController = new SistemaController( - userService, - discenteService, - disciplinaService, - sistemaService, -) - -router.post('/session', sistemaController.criaConexaoComSistema.bind(sistemaController)) - -export { router as sistemaRoutes } diff --git a/src/routes/user-route.ts b/src/routes/user-route.ts deleted file mode 100644 index 37eda73..0000000 --- a/src/routes/user-route.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { UserRepository } from './../repositories/user-repository' -import { Router } from 'express' -import { UserController } from '../controllers/user-controller' -import { UserService } from '../services/user-service' -import { PrismaDatabase } from '../database/prisma-database' -import { AutorizacaoMiddleware } from '../middlewares/autorizacao-middleware' - -const router = Router() -const database = new PrismaDatabase(); - -const userRepository = new UserRepository(database) -const userService = new UserService(userRepository) -const userController = new UserController(userService) -const middlewareAutorizacao = new AutorizacaoMiddleware() - -router.get('', userController.buscaUsuarios.bind(userController)) -router.post('/create', userController.criaUsuario.bind(userController)) -router.put('/update/:id', middlewareAutorizacao.autorizarApenas('ADMIN'), userController.atualizaUsuario.bind(userController)) -router.delete('/delete/:id', middlewareAutorizacao.autorizarApenas('ADMIN'), userController.deletaUsuario.bind(userController)); - -export { router as userRoutes } diff --git a/src/server.ts b/src/server.ts index fc1965c..3cf2062 100644 --- a/src/server.ts +++ b/src/server.ts @@ -2,22 +2,22 @@ import express, { Request, Response } from 'express' import path from 'path' import cors from 'cors' import { PrismaClient } from '@prisma/client' -import { userRoutes } from './routes/user-route' -import { disciplinaRoutes } from './routes/disciplina-route' -import { discenteRoutes } from './routes/discente-route' -import { authRoutes } from './routes/auth-route' -import { sistemaRoutes } from './routes/sistema-route' +import { autenticacaoRoutes } from './features/autenticacao/autenticacao-route' +import { usuarioRoutes } from './features/usuarios/usuario-route' +import { alunoRoutes } from './features/alunos/aluno-route' +import { disciplinaRoutes } from './features/disciplinas/disciplina-route' +import { sessaoRoutes } from './features/sessoes/sessao-route' const prisma = new PrismaClient() const app = express() -const port = 3000 +const port = 7777 app.use(cors({ origin: true })) app.use(express.json()) app.use(express.static(path.join(__dirname, 'public'))) -async function main() {} +async function main() { } main() .then(async () => { @@ -29,11 +29,11 @@ main() process.exit(1) }) -app.use('/api/auth', authRoutes) -app.use('/api/users', userRoutes) -app.use('/api/discentes', discenteRoutes) +app.use('/api/auth', autenticacaoRoutes) +app.use('/api/users', usuarioRoutes) +app.use('/api/discentes', alunoRoutes) app.use('/api/disciplinas', disciplinaRoutes) -app.use('/api', sistemaRoutes) +app.use('/api', sessaoRoutes) app.get('/', (req: Request, res: Response) => { res.send('Bem-vindo a API do Controle Acadêmico!') diff --git a/src/services/discente-service.ts b/src/services/discente-service.ts deleted file mode 100644 index 6740658..0000000 --- a/src/services/discente-service.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { IDiscenteRepository } from '../repositories/discente-repository' -import { DiscenteDTO } from '../dtos/discente-dto' -import { Discente } from '@prisma/client' -import { ReportarErrorAoSistema } from '../exceptions/ReportarErroAoSistema' - -export interface IDiscenteService { - lidaComCriacaoDoDiscente(data: DiscenteDTO): Promise - lidaComAtualizacaoDoDiscente(id: number, data: Partial): Promise - lidaComRemocaoDoDiscente(id: number): Promise - lidaComBuscaDoDiscentePorId(id: number): Promise - lidaComBuscaDoDiscentePorMatricula(matricula: string): Promise -} -export class DiscenteService implements IDiscenteService { - private discenteRepository: IDiscenteRepository - - constructor(discenteRepository: IDiscenteRepository) { - this.discenteRepository = discenteRepository - } - - async lidaComCriacaoDoDiscente(data: DiscenteDTO) { - if (!data.nome || !data.matricula || !data.cursoId || !data.userId) { - throw new ReportarErrorAoSistema('Dados insuficientes para criar o usuário.') - } - - try { - data.cursoId = Number(data.cursoId) - data.userId = Number(data.userId) - - const discente = await this.discenteRepository.criaDiscente(data) - return discente - } catch (error) { - console.log(error) - throw new Error('Error creating discente') - } - } - - async lidaComBuscaDoDiscentePorId(id: number) { - if (!id) { - throw new ReportarErrorAoSistema('ID do usuário não informado.') - } - try { - const discente = await this.discenteRepository.buscaDiscentePorId(id) - if (!discente) { - throw new Error('User not found') - } - return discente - } catch (error) { - throw new Error('Error fetching discente') - } - } - - async lidaComBuscaDoDiscentePorMatricula(matricula: string): Promise { - if (!matricula) { - throw new ReportarErrorAoSistema('Matricula do usuário não informada.') - } - try { - const discente = await this.discenteRepository.buscaDiscentePorMatricula(matricula) - - return discente - } catch (error) { - throw new Error('Error fetching discente') - } - } - - async lidaComAtualizacaoDoDiscente(id: number, data: Partial) { - if (!id) { - throw new ReportarErrorAoSistema('ID do usuário não informado.') - } - - if (!data.nome || !data.matricula || !data.cursoId || !data.userId) { - throw new ReportarErrorAoSistema('Dados insuficientes para atualizar o usuário.') - } - - try { - const discenteAtualizado = await this.discenteRepository.atualizaDiscente(id, data) - return discenteAtualizado - } catch (error) { - throw new Error('Error updating discente') - } - } - - async lidaComRemocaoDoDiscente(id: number) { - if (!id) { - throw new ReportarErrorAoSistema('ID do usuário não informado.') - } - try { - await this.discenteRepository.deletaDiscente(id) - } catch (error) { - throw new Error('Error deleting discente') - } - } -} diff --git a/src/services/disciplina-service.ts b/src/services/disciplina-service.ts deleted file mode 100644 index c339a8f..0000000 --- a/src/services/disciplina-service.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { Disciplina } from "@prisma/client" -import { IDisciplinaRepository } from "../repositories/disciplina-repository" -import { UFCGImporter } from "../importers/implementation/ufcg-importer" -import { Importer } from "../importers/importer" -import { SessionDTO } from "../dtos/session-dto" - -export interface IDisciplinaService { - lidaComImportacaoDasDisciplinasDoDiscente(sessionDTO: SessionDTO, importer: Importer): Promise -} - -export class DisciplinaService implements IDisciplinaService { - private disciplinaRepository: IDisciplinaRepository - - constructor(disciplinaRepository: IDisciplinaRepository) { - this.disciplinaRepository = disciplinaRepository - } - - async lidaComImportacaoDasDisciplinasDoDiscente(sessionDTO: SessionDTO, importer: Importer): Promise { - if (!sessionDTO.matricula || !sessionDTO.senha) { - throw new Error("Login, senha são obrigatórios") - } - - const { matricula, senha, discenteId } = sessionDTO - - const disciplinasImportadas = await importer.importaDisciplinas(discenteId!, matricula, senha) - console.log(`importUserData: ${JSON.stringify(disciplinasImportadas, null, 2)}`) - - const countDisciplinasCriadas = await this.disciplinaRepository.saveDisciplinas(disciplinasImportadas) - - return countDisciplinasCriadas - } - - switchImporter(vinculo: string) { - switch (vinculo) { - case "UFCG": - return new UFCGImporter() - default: - throw new Error("Importer error") - } - } -} diff --git a/src/services/user-service.ts b/src/services/user-service.ts deleted file mode 100644 index 79a5474..0000000 --- a/src/services/user-service.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { IUserRepository } from'../repositories/user-repository' -import { UserDTO } from '../dtos/user-dto' -import { User } from '@prisma/client' -import { ReportarErrorAoSistema } from '../exceptions/ReportarErroAoSistema' - -export interface IUserService { - lidaComCriacaoDoUsuario(data: UserDTO): Promise - lidaComBuscaDoUsuarioPorId(id: number): Promise - lidaComAtualizacaoDoUsuario(id: number, data: Partial): Promise - lidaComRemocaoDoUsuario(id: number): Promise - lidaComBuscaDosUsuarios(): Promise -} -export class UserService implements IUserService { - private userRepository: IUserRepository - - constructor(userRepository: IUserRepository) { - this.userRepository = userRepository - } - - async lidaComCriacaoDoUsuario({ nome, role }: UserDTO): Promise { - try { - if (!nome || typeof nome !== 'string') { - throw new ReportarErrorAoSistema('Nome é obrigatório e precisa ser uma string.') - } - if (!role || !['ADMIN', 'DISCENTE', 'DOCENTE'].includes(role)) { - throw new ReportarErrorAoSistema('Uma role é obrigatória. Veja a documentação') - } - - const user = await this.userRepository.criaUsuario({ nome, role }) - return user - } catch (error) { - throw new ReportarErrorAoSistema('Erro ao tentar salvar usuário ao banco de dados.') - } - } - - async lidaComBuscaDoUsuarioPorId(id: number) { - try { - const user = await this.userRepository.buscaUsuarioPorId(id) - if (!user) { - throw new Error('User not found') - } - return user - } catch (error) { - throw new Error('Erro ao buscar usuário') - } - } - async lidaComAtualizacaoDoUsuario(id: number, data: Partial) { - if (!data.nome || !data.role) { - throw new ReportarErrorAoSistema('Nome e role são obrigatórios') - } - try { - const updatedUser = await this.userRepository.atualizaUsuario(id, data) - return updatedUser - } catch (error) { - throw new Error('Erro ao atualizar o usuário') - } - } - - async lidaComRemocaoDoUsuario(id: number) { - try { - await this.userRepository.deletaUsuario(id) - } catch (error) { - throw new Error('Error deleting user') - } - } - - async lidaComBuscaDosUsuarios() { - try { - const users = await this.userRepository.buscaUsuarios() - - if (!users) { - return [] - } - - return users - } catch (error) { - throw new Error('Error fetching user') - } - } -} diff --git a/src/shared/database/database-interface.ts b/src/shared/database/database-interface.ts new file mode 100644 index 0000000..b010dff --- /dev/null +++ b/src/shared/database/database-interface.ts @@ -0,0 +1,24 @@ +import { Aluno, Disciplina, Usuario } from "@prisma/client" +import { AlunoDTO } from "../../features/alunos/aluno-dto" +import { UsuarioDTO } from "../../features/usuarios/usuario-dto" +import { DisciplinaHistoricoProps } from "../importers/implementation/ufcg-importer" + +export interface IDatabase { + salvaUsuario(usuarioDTO: UsuarioDTO): Promise + atualizaUsuario(id: number, data: Partial): Promise + adicionaTokenDeAutenticacaoAoUsuario(id: number, token: string): Promise + buscaUsuarioPorId(id: number): Promise + buscaUsuarioPorEmail(email: string): Promise + buscaPorTodosUsuarios(): Promise + removeUsuario(id: number): Promise + + salvaAluno(data: AlunoDTO): Promise + atualizaAluno(id: number, data: Partial): Promise + buscaAlunoPorId(id: number): Promise + buscaAlunoPorEmail(email: string): Promise + buscaAlunoPorMatricula(matricula: string): Promise + removeAluno(id: number): Promise + + + salvaVariasDisciplinas(data: DisciplinaHistoricoProps[]): Promise +} diff --git a/src/shared/database/prisma-database.ts b/src/shared/database/prisma-database.ts new file mode 100644 index 0000000..07c8883 --- /dev/null +++ b/src/shared/database/prisma-database.ts @@ -0,0 +1,108 @@ +// src/database/PrismaDatabase.tsimport { PrismaClient } from'@prisma/client' +import { Aluno, Disciplina, PrismaClient, Usuario } from '@prisma/client' +import { UsuarioDTO } from '../../features/usuarios/usuario-dto' +import { IDatabase } from './database-interface' +import { AlunoDTO } from '../../features/alunos/aluno-dto' +import { PrismaClientInitializationError } from '@prisma/client/runtime/library' +import { DisciplinaHistoricoProps } from '../importers/implementation/ufcg-importer' + +const prisma = new PrismaClient() + +export class PrismaDatabase implements IDatabase { + + // Métodos de consulta e escrita do banco de dados para Usuário + async salvaUsuario(data: UsuarioDTO): Promise { + return prisma.usuario.create({ + data: { + nome: data.nome, + role: data.role, + } + }) + } + + async atualizaUsuario(id: number, data: Partial): Promise { + return prisma.usuario.update({ where: { id }, data }) + } + + async adicionaTokenDeAutenticacaoAoUsuario(id: number, token: string): Promise { + await prisma.usuario.update({ + where: { + id, + }, + data: { + token + } + }) + } + + async buscaUsuarioPorId(id: number): Promise { + return prisma.usuario.findUnique({ where: { id } }) + } + + buscaUsuarioPorEmail(email: string): Promise { + throw new Error('Method not implemented.') + } + + async buscaPorTodosUsuarios(): Promise { + return await prisma.usuario.findMany() + } + + async removeUsuario(id: number): Promise { + await prisma.usuario.delete({ where: { id } }) + } + + // Métodos de consulta e escrita do banco de dados para Aluno + salvaAluno(data: AlunoDTO): Promise { + const aluno = prisma.aluno.create({ + data: { + nome: data.nome, + matricula: data.matricula, + usuario: { + connect: { + id: data.usuarioId, + } + }, + }, + }) + + return aluno + } + + atualizaAluno(id: number, data: Partial): Promise { + throw new Error('Method not implemented.') + } + + buscaAlunoPorId(id: number): Promise { + throw new Error('Method not implemented.') + } + + buscaAlunoPorEmail(email: string): Promise { + throw new Error('Method not implemented.') + } + + buscaAlunoPorMatricula(matricula: string): Promise { + throw new Error('Method not implemented.') + } + + removeAluno(id: number): Promise { + throw new Error('Method not implemented.') + } + + // Métodos de consulta e escrita do banco de dados para Disciplina + async salvaVariasDisciplinas(data: DisciplinaHistoricoProps[]): Promise { + try { + const disciplinas = await prisma.disciplina.createMany({ + data: [...data] + }) + + return disciplinas.count + } catch (error) { + if (error instanceof PrismaClientInitializationError) { + console.error('Erro ao inicializar o Prisma Client:', error.message) + } else { + console.error('Erro desconhecido ao salvar disciplinas:', error) + } + throw error + } + } +} diff --git a/src/exceptions/ReportarErroAoSistema.ts b/src/shared/exceptions/ReportarErroAoSistema.ts similarity index 100% rename from src/exceptions/ReportarErroAoSistema.ts rename to src/shared/exceptions/ReportarErroAoSistema.ts diff --git a/src/shared/importers/implementation/ufcg-importer.ts b/src/shared/importers/implementation/ufcg-importer.ts new file mode 100644 index 0000000..261e582 --- /dev/null +++ b/src/shared/importers/implementation/ufcg-importer.ts @@ -0,0 +1,270 @@ +import axios, { AxiosRequestConfig, AxiosResponse } from 'axios' +import * as cheerio from 'cheerio' +import he from 'he' +import { Importer } from '../importer' +import { Disciplina, SituacaoDisciplina } from '@prisma/client' +import iconv from 'iconv-lite'; + +const urlSearchParams: AxiosRequestConfig = { + timeout: 10000, + withCredentials: true, + validateStatus: () => true, + httpsAgent: new (require('https').Agent)({ + rejectUnauthorized: false + }), + responseType: 'arraybuffer', + responseEncoding: 'binary', +} + +export interface DisciplinaHistoricoProps { + alunoId: number; + codigo: string; + nome: string; + creditos: number; + cargaHoraria: number; + quantidadeProvas: number; + quantidadeFaltas: number; + semestre: string; + mediaFinal: number | null; + situacao: SituacaoDisciplina; + docente: string; + horario?: string; +} + +export class UFCGImporter implements Importer { + private disciplinas: DisciplinaHistoricoProps[] = [] + private codigos: string[] = [] + private cookies: string[] = [] + private URL_BASE = "https://pre.ufcg.edu.br:8443/ControleAcademicoOnline/Controlador" + private URL_LOGIN = this.URL_BASE + private LOGIN = "login" + private SENHA = "senha" + private COMMAND = "command" + private ALUNO_LOGIN = "AlunoLogin" + + private REPROVADO_POR_FALTA = "Reprovado por Falta" + private TRANCADO = "Trancado" + private DISPENSA = "Dispensa" + private APROVADO = "Aprovado" + private REPROVADO = "Reprovado" + private ERRO_AUTENTICACAO = "ERRO NA AUTENTICAÇÃO" + public static readonly ERRO = "Matrícula inválida ou senha incorreta." + + reportProgress(message: string): void { + console.log(message) // Implement your progress reporting logic here + } + + private cleanText = (text: string): string => { + return he.decode(text) // Decodifica entidades HTML + .replace(/\s+/g, ' ') + .trim() // Remove espaços em branco no início e no final + } + + public async autenticaAluno(matricula: string, senha: string): Promise { + this.reportProgress("Tentando fazer login...") + + try { + const response = await axios.post(this.URL_LOGIN, new URLSearchParams({ + [this.LOGIN]: matricula, + [this.SENHA]: senha, + [this.COMMAND]: this.ALUNO_LOGIN + }), urlSearchParams) + + this.cookies = response.headers['set-cookie']! + let html = iconv.decode(Buffer.from(response.data), 'ISO-8859-1') + html = html.replace(/]*charset=["']?[^"'>]+["']?[^>]*>/i, '') + + const loadedDocument = cheerio.load(html) + + if (loadedDocument('head').text().includes(this.ERRO_AUTENTICACAO) || loadedDocument('body').text().includes(UFCGImporter.ERRO)) { + throw new Error("Authentication Failed") + } + + console.log(loadedDocument) + return loadedDocument + } catch (err) { + console.error(err) + } + } + + public async obterDisciplinasDoHistoricoAcademico(alunoId: number): Promise { + this.reportProgress("Obtendo histórico...") + try { + const historicoResponse = await axios.get(this.URL_LOGIN, { + params: { [this.COMMAND]: "AlunoHistorico" }, + headers: { Cookie: this.cookies.join(' ') }, + timeout: 10000, + httpsAgent: new (require('https').Agent)({ + rejectUnauthorized: false + }), + responseType: 'arraybuffer', // Receber os dados como array buffer + responseEncoding: 'binary', + }) + + const loadedHistoricoDocument = this.carregaHtml(historicoResponse) + const trsDisciplinas = loadedHistoricoDocument("div[id=disciplinas] > table > tbody > tr").toArray() + + for (const el of trsDisciplinas) { + const tds = loadedHistoricoDocument(el).find("td") + const disciplina: DisciplinaHistoricoProps = this.montaDisciplina(tds, alunoId) + this.disciplinas.push(disciplina) + this.codigos.push(tds.eq(0).text() || '') + } + + if (this.disciplinas.length === 0) { + throw new Error("Você não possui histórico acadêmico") + } + + return this.disciplinas + } catch (error: any) { + if (axios.isAxiosError(error)) { + if (error.code === 'ECONNABORTED') { + throw new Error("Não foi possível buscar os dados. Tente novamente.") + } else if (error.message.includes('ENOTFOUND')) { + throw new Error("Não foi possível acessar o site da Universidade. Você está conectado a internet?") + } else if (error.message.includes('UNABLE_TO_VERIFY_LEAF_SIGNATURE')) { + throw new Error("Não foi possível acessar o site. Tente novamente") + } + } + throw new Error(error.message) + } + } + + public async obterDetalhesDaDisciplina(): Promise { + this.reportProgress("Obtendo detalhes das disciplinas...") + + try { + const horarioResponse = await axios.get(this.URL_BASE, { + params: { [this.COMMAND]: "AlunoTurmasListar" }, + headers: { Cookie: this.cookies.join(' ') }, + httpsAgent: new (require('https').Agent)({ + rejectUnauthorized: false + }), + responseType: 'arraybuffer', // Receber os dados como array buffer + responseEncoding: 'binary', + }) + + let htmlHorarioResponse = iconv.decode(Buffer.from(horarioResponse.data), 'ISO-8859-1'); + htmlHorarioResponse = htmlHorarioResponse.replace(/]*charset=["']?[^"'>]+["']?[^>]*>/i, ''); + + const loadedHorarioDocument = this.carregaHtml(horarioResponse) + const table = loadedHorarioDocument("table") + + if (table) { + const trs = table.find("tr") + trs.each((index, element) => { + if (index === 0) return // Skip header row + + const tds = loadedHorarioDocument(element).children() + const disciplina = this.deParaDisciplinaPorCodigo(tds.eq(1).text() || '') + + if (disciplina) { + disciplina.horario = `Turma: ${tds.eq(3).text()}\nHorário: ${tds.eq(4).text()}` + } + }) + } + + return this.disciplinas + } catch (err) { + console.error(err) + } + } + + private carregaHtml(response: AxiosResponse): cheerio.CheerioAPI { + let htmlFormatado = iconv.decode(Buffer.from(response.data), 'ISO-8859-1'); + htmlFormatado = htmlFormatado.replace(/]*charset=["']?[^"'>]+["']?[^>]*>/i, ''); + const loadedDocument = cheerio.load(htmlFormatado) // Carregando o HTML + return loadedDocument + } + + private deParaDisciplinaPorCodigo(codigo: string): DisciplinaHistoricoProps | undefined { + const disciplinasFind = this.disciplinas.filter((disciplina, index) => this.codigos[index] === codigo) + + return disciplinasFind.reduce((latest, disciplina) => { + return (!latest || disciplina.semestre > latest.semestre) ? disciplina : latest + }, undefined as DisciplinaHistoricoProps | undefined) + } + + private montaDisciplina(tds: any, alunoId: number): DisciplinaHistoricoProps { + const disciplina: DisciplinaHistoricoProps = { + alunoId, + codigo: tds.eq(0).text() || '', + nome: this.cleanText(tds.eq(1).text()) || '', + creditos: parseInt(tds.eq(3).text() || '0', 10), + cargaHoraria: parseInt(tds.eq(4).text() || '0', 10), + quantidadeProvas: this.calcularQuantidadeNotasPorCreditosDaDisciplina(tds.eq(3).text()), + quantidadeFaltas: this.calcularQuantidadeFaltasPorCreditosDaDisciplina(tds.eq(3).text()), + semestre: tds.eq(7).text() || '', + mediaFinal: isNaN(parseFloat(tds.eq(5).text().replace(",", "."))) ? null : parseFloat(tds.eq(5).text().replace(",", ".")), + situacao: this.definirSituacaoDisciplina(tds.eq(6).text(), parseFloat(tds.eq(5).text().replace(",", "."))), + docente: 'Ausente', + } + + return disciplina + } + + private definirSituacaoDisciplina(situacao: string, mediaFinal: number | null): SituacaoDisciplina { + let status: SituacaoDisciplina; + + switch (situacao) { + case this.APROVADO: + case this.DISPENSA: + status = SituacaoDisciplina.DISPENSA; + if (mediaFinal === null) { + mediaFinal = 7.0; + } + break; + case this.REPROVADO: + status = SituacaoDisciplina.REPROVADO; + break; + case this.REPROVADO_POR_FALTA: + status = SituacaoDisciplina.REPROVADO_POR_FALTA; + mediaFinal = 0; + break; + case this.TRANCADO: + status = SituacaoDisciplina.TRANCADA; + break; + default: + status = SituacaoDisciplina.EM_PROGRESSO; + break; + } + + return status; + } + + private calcularQuantidadeNotasPorCreditosDaDisciplina(creditos: string): number { + const creditosDisciplina = parseInt(creditos || '0', 10) + + switch (creditosDisciplina) { + case 1: + case 2: + case 3: + return 2 + case 4: + return 3 + case 5: + case 6: + return 4 + default: + return 1 + } + } + + private calcularQuantidadeFaltasPorCreditosDaDisciplina(creditos: string): number { + const creditosDisciplina = parseInt(creditos || '0', 10) + + switch (creditosDisciplina) { + case 1: + case 2: + case 3: + return 4 + case 4: + return 5 + case 5: + case 6: + return 6 + default: + return 3 + } + } +} diff --git a/src/shared/importers/importer.ts b/src/shared/importers/importer.ts new file mode 100644 index 0000000..72bfdf0 --- /dev/null +++ b/src/shared/importers/importer.ts @@ -0,0 +1,9 @@ +import { Disciplina } from "@prisma/client" +import * as cheerio from 'cheerio' +import { DisciplinaHistoricoProps } from "./implementation/ufcg-importer" + +export interface Importer { + autenticaAluno(matricula: string, senha: string): Promise + obterDisciplinasDoHistoricoAcademico(alunoId: number): Promise + obterDetalhesDaDisciplina(): Promise +} diff --git a/src/middlewares/autorizacao-middleware.ts b/src/shared/middlewares/autorizacao-middleware.ts similarity index 55% rename from src/middlewares/autorizacao-middleware.ts rename to src/shared/middlewares/autorizacao-middleware.ts index f5bb98c..1b13491 100644 --- a/src/middlewares/autorizacao-middleware.ts +++ b/src/shared/middlewares/autorizacao-middleware.ts @@ -1,7 +1,7 @@ -import { NextFunction, Request, Response } from "express"; -import { DiscenteService, IDiscenteService } from "../services/discente-service"; import jwt from 'jsonwebtoken' -import { ReportarErrorAoSistema } from "../exceptions/ReportarErroAoSistema"; +import { NextFunction, Request, Response } from "express"; +import { ReportarErrorAoSistema } from "../exceptions/ReportarErroAoSistema" +import { AlunoService, IAlunoService } from "../../features/alunos/aluno-service" interface PayloadToken { id: number; @@ -10,23 +10,23 @@ interface PayloadToken { exp: number; } -export interface PayloadUserAlreadyExists extends Partial { - userAlreadyExists: boolean; +export interface PayloadUsuarioAlreadyExists extends Partial { + usuarioAlreadyExists: boolean; } export class AutorizacaoMiddleware { private segredo: string; - private discenteService?: DiscenteService + private alunoService?: AlunoService - constructor(discenteService?: DiscenteService) { + constructor(alunoService?: AlunoService) { this.segredo = process.env.JWT_SECRET || 'segredo_padrao' - this.discenteService = discenteService + this.alunoService = alunoService - console.log(discenteService) + console.log(alunoService) } public autorizarApenas(roleNecessaria: string) { - return(req: Request, res: Response, next: NextFunction) => { + return (req: Request, res: Response, next: NextFunction) => { const cabecalhoAutenticacao = req.headers.authorization; if (!cabecalhoAutenticacao) { @@ -43,10 +43,10 @@ export class AutorizacaoMiddleware { } interface CustomRequest extends Request { - user: PayloadToken; + usuario: PayloadToken; } const customReq: CustomRequest = req as CustomRequest; - customReq.user = decodificado; + customReq.usuario = decodificado; next() } catch (erro) { return res.status(401).json({ mensagem: 'Token inválido ou expirado.' }); @@ -54,35 +54,35 @@ export class AutorizacaoMiddleware { }; } - /** - * Esse método checa se um usuário já existe no sistema, ou seja, se já foi adicionado a base - * Se a classe `discenteService` não for inicializa por algum motivo e esse método for chamada você receberá um erro. - * @param {Request} req - The Express request object. - * @param {Response} res - The Express response object. - * @param {NextFunction} next - The Express next middleware function. - * @throws {Error} If the `discenteService` is not properly initialized, a warning message will be returned. - */ + /** + * Esse método checa se um usuário já existe no sistema, ou seja, se já foi adicionado a base + * Se a classe `alunoService` não for inicializa por algum motivo e esse método for chamada você receberá um erro. + * @param {Request} req - The Express request object. + * @param {Response} res - The Express response object. + * @param {NextFunction} next - The Express next middleware function. + * @throws {Error} If the `alunoService` is not properly initialized, a warning message will be returned. + */ public async verificaSeAlunoJaExisteNoSistema(req: Request, res: Response, next: NextFunction) { const { matricula } = req.body; - - if (!this.discenteService) { - throw new ReportarErrorAoSistema("DiscenteService is undefined. Por favor, verifique se a propriedade foi inicializada corretamente.") + + if (!this.alunoService) { + throw new ReportarErrorAoSistema("AlunoService is undefined. Por favor, verifique se a propriedade foi inicializada corretamente.") } try { - const aluno = await this.discenteService.lidaComBuscaDoDiscentePorMatricula(matricula); + const aluno = await this.alunoService.lidaComBuscaDoAlunoPorMatricula(matricula); interface CustomRequest extends Request { - user: PayloadUserAlreadyExists; + usuario: PayloadUsuarioAlreadyExists; } const customReq: CustomRequest = req as CustomRequest; - + if (aluno) { - customReq.user = { - userAlreadyExists: true, + customReq.usuario = { + usuarioAlreadyExists: true, } } - + next(); } catch (error) { if (error instanceof ReportarErrorAoSistema) { From 8c203d0c1535939bddc16f7e484ee8faf058f5dd Mon Sep 17 00:00:00 2001 From: huandrey Date: Wed, 9 Oct 2024 05:09:18 -0300 Subject: [PATCH 2/3] Ajustes --- .../migration.sql | 3 + prisma/schema.prisma | 3 +- src/features/alunos/aluno-controller.ts | 6 ++ src/features/alunos/aluno-repository.ts | 6 +- src/features/alunos/aluno-service.ts | 17 ++++- .../disciplinas/disciplina-service.ts | 5 +- src/features/sessoes/sessao-controller.ts | 75 +++++++++---------- src/features/usuarios/usuario-repository.ts | 5 -- src/shared/database/database-interface.ts | 5 +- src/shared/database/prisma-database.ts | 67 ++++++++--------- .../importers/implementation/ufcg-importer.ts | 49 ++++++------ src/shared/importers/importer.ts | 2 +- 12 files changed, 125 insertions(+), 118 deletions(-) create mode 100644 prisma/migrations/20241009054755_ajusta_propriedades_do_aluno/migration.sql diff --git a/prisma/migrations/20241009054755_ajusta_propriedades_do_aluno/migration.sql b/prisma/migrations/20241009054755_ajusta_propriedades_do_aluno/migration.sql new file mode 100644 index 0000000..85dcfac --- /dev/null +++ b/prisma/migrations/20241009054755_ajusta_propriedades_do_aluno/migration.sql @@ -0,0 +1,3 @@ +-- AlterTable +ALTER TABLE "Aluno" ADD COLUMN "universidade" TEXT NOT NULL DEFAULT 'Universidade Federal de Campina Grande', +ALTER COLUMN "curso" SET DEFAULT 'Ciência da Computação'; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 61c329d..806c25b 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -46,7 +46,8 @@ model Aluno { usuarioId Int @unique nome String matricula String @unique @db.VarChar(9) - curso String @default("Universidade Federal de Campina Grande") + curso String @default("Ciência da Computação") + universidade String @default("Universidade Federal de Campina Grande") usuario Usuario @relation(fields: [usuarioId], references: [id]) historicoAcademico Disciplina[] createdAt DateTime @default(now()) diff --git a/src/features/alunos/aluno-controller.ts b/src/features/alunos/aluno-controller.ts index ed9529a..f29d057 100644 --- a/src/features/alunos/aluno-controller.ts +++ b/src/features/alunos/aluno-controller.ts @@ -17,6 +17,12 @@ export class AlunoController { const aluno: AlunoDTO = req.body try { + const alunoJaExiste = await this.alunoService.lidaComBuscaDoAlunoPorMatricula(aluno.matricula) + if (alunoJaExiste) { + res.status(400).json({ error: 'Aluno já cadastrado' }) + return + } + const usuarioId = await this.usuarioService.lidaComCriacaoDoUsuario(aluno) await this.alunoService.lidaComCriacaoDoAluno({ ...aluno, usuarioId }) res.status(201).json({ message: "Aluno criado com sucesso!" }) diff --git a/src/features/alunos/aluno-repository.ts b/src/features/alunos/aluno-repository.ts index 9e7e6b7..b28c30c 100644 --- a/src/features/alunos/aluno-repository.ts +++ b/src/features/alunos/aluno-repository.ts @@ -7,8 +7,8 @@ export interface IAlunoRepository { atualizaAluno(id: number, data: Partial): Promise removeAluno(id: number): Promise buscaAlunoPorId(id: number): Promise - buscaAlunoPorEmail(email: string): Promise buscaAlunoPorMatricula(matricula: string): Promise + buscaInformacoesDoAlunoPorId(id: number): Promise | null> } export type AtualizaAlunoDTO = { @@ -45,7 +45,7 @@ export class AlunoRepository implements IAlunoRepository { return this.orm.buscaAlunoPorMatricula(matricula) } - async buscaAlunoPorEmail(email: string): Promise { - return this.orm.buscaAlunoPorEmail(email) + async buscaInformacoesDoAlunoPorId(id: number): Promise | null> { + return this.orm.buscaInformacoesDoAlunoPorId(id) } } diff --git a/src/features/alunos/aluno-service.ts b/src/features/alunos/aluno-service.ts index 6b1778c..0d6480e 100644 --- a/src/features/alunos/aluno-service.ts +++ b/src/features/alunos/aluno-service.ts @@ -40,9 +40,19 @@ export class AlunoService implements IAlunoService { } try { const aluno = await this.alunoRepository.buscaAlunoPorId(id) - if (!aluno) { - throw new Error('User not found') - } + return aluno + } catch (error) { + throw new Error('Error fetching aluno') + } + } + + async lidaComBuscaDeInformacoesDoAlunoPorId(id: number) { + if (!id) { + throw new ReportarErrorAoSistema('ID do usuário não informado.') + } + + try { + const aluno = await this.alunoRepository.buscaInformacoesDoAlunoPorId(id) return aluno } catch (error) { throw new Error('Error fetching aluno') @@ -55,7 +65,6 @@ export class AlunoService implements IAlunoService { } try { const aluno = await this.alunoRepository.buscaAlunoPorMatricula(matricula) - return aluno } catch (error) { throw new Error('Error fetching aluno') diff --git a/src/features/disciplinas/disciplina-service.ts b/src/features/disciplinas/disciplina-service.ts index e3bbf32..380d597 100644 --- a/src/features/disciplinas/disciplina-service.ts +++ b/src/features/disciplinas/disciplina-service.ts @@ -24,10 +24,7 @@ export class DisciplinaService implements IDisciplinaService { const disciplinasImportadas = await importer.obterDisciplinasDoHistoricoAcademico(alunoId!) - const disciplinasImportadasDetalhadas = await importer.obterDetalhesDaDisciplina() - - console.log(`importUserData: ${JSON.stringify(disciplinasImportadas, null, 2)}`) - + await importer.obterDetalhesDaDisciplina() const countDisciplinasCriadas = await this.disciplinaRepository.saveDisciplinas(disciplinasImportadas) return countDisciplinasCriadas diff --git a/src/features/sessoes/sessao-controller.ts b/src/features/sessoes/sessao-controller.ts index bc47de0..96ba1f6 100644 --- a/src/features/sessoes/sessao-controller.ts +++ b/src/features/sessoes/sessao-controller.ts @@ -5,8 +5,8 @@ import { AlunoService } from "../../features/alunos/aluno-service"; import { DisciplinaService } from "../../features/disciplinas/disciplina-service"; import { SessaoDTO } from "../../features/sessoes/sessao-dto"; import { SessaoService } from "./sessao-service"; -import { Role } from "@prisma/client"; import { Importer } from "../../shared/importers/importer"; +import { ReportarErrorAoSistema } from "../../shared/exceptions/ReportarErroAoSistema"; export class SessaoController { private usuarioService: UsuarioService @@ -16,66 +16,59 @@ export class SessaoController { constructor(usuarioService: UsuarioService, alunoService: AlunoService, disciplinaService: DisciplinaService, sessaoService: SessaoService) { this.usuarioService = usuarioService, - this.alunoService = alunoService + this.alunoService = alunoService this.disciplinaService = disciplinaService this.sessaoService = sessaoService } public async criaConexaoComSessao(req: Request, res: Response): Promise { - interface CustomRequest extends Request { - usuario: PayloadUsuarioAlreadyExists; - } - const sessaoDTO: SessaoDTO = req.body; - // customReq agora compartilha o mesmo acesso de memória que req - // const customReq: CustomRequest = req as CustomRequest; - - // const { usuarioAlreadyExists } = customReq.usuario; - - // if (usuarioAlreadyExists) { - // this.retornaDadosDoAluno() - // } - - this.configuraSessaoDoUsuario(sessaoDTO) - - - res.status(200).json({ message: 'Conexão com o sessao criada com sucesso!' }) + try { + const aluno = await this.configuraSessaoDoUsuario(sessaoDTO) + res.status(200).json({ message: 'Conexão com o sistema criada com sucesso!', data: aluno }) + } catch (error) { + if (error instanceof ReportarErrorAoSistema) { + res.status(400).json({ error: error.message }) + } else { + console.error(error) + res.status(500).json({ error: 'Internal Server Error' }) + } + } } private async configuraSessaoDoUsuario(sessaoDTO: SessaoDTO) { const { matricula, senha, vinculo, alunoId } = sessaoDTO const importer: Importer = this.sessaoService.lidaComIdentificacaoDaInstituicaoDeEnsino(vinculo) + await importer.autenticaAluno(matricula, senha) // Autentica usuário e salva cookie retornado da response - try { - await importer.autenticaAluno(matricula, senha) + const dadosAluno = { + nome: 'Huandrey', + } - const dadosAluno = { - nome: 'Huandrey', - } + const alunoJaExiste = await this.alunoService.lidaComBuscaDoAlunoPorMatricula(matricula) + if (alunoJaExiste) { + return await this.alunoService.lidaComBuscaDeInformacoesDoAlunoPorId(alunoJaExiste.id) + } - const usuarioId: number = await this.usuarioService.lidaComCriacaoDoUsuario({ nome: dadosAluno.nome }) + const usuarioId: number = await this.usuarioService.lidaComCriacaoDoUsuario({ nome: dadosAluno.nome }) - if (!usuarioId) { - throw new Error('Erro ao cadastrar o usuário') - } - - const aluno = await this.alunoService.lidaComCriacaoDoAluno({ - nome: dadosAluno.nome, - matricula: matricula, - usuarioId, - }); + if (!usuarioId) { + throw new ReportarErrorAoSistema('Erro ao cadastrar o usuário') + } - if (!aluno) { - throw new Error('Erro ao cadastrar o aluno') - } + const aluno = await this.alunoService.lidaComCriacaoDoAluno({ + nome: dadosAluno.nome, + matricula: matricula, + usuarioId, + }); - this.disciplinaService.lidaComImportacaoDasDisciplinasDoDiscente({ ...sessaoDTO, alunoId: aluno.id }, importer) - } catch (err) { - console.error(err) + if (!aluno) { + throw new ReportarErrorAoSistema('Erro ao cadastrar o aluno') } + await this.disciplinaService.lidaComImportacaoDasDisciplinasDoDiscente({ ...sessaoDTO, alunoId: aluno.id }, importer) + return await this.alunoService.lidaComBuscaDeInformacoesDoAlunoPorId(aluno.id) } - } diff --git a/src/features/usuarios/usuario-repository.ts b/src/features/usuarios/usuario-repository.ts index 7b1de1d..67dacfd 100644 --- a/src/features/usuarios/usuario-repository.ts +++ b/src/features/usuarios/usuario-repository.ts @@ -7,7 +7,6 @@ export interface IUsuarioRepository { atualizaUsuario(id: number, data: Partial): Promise deletaUsuario(id: number): Promise buscaUsuarioPorId(id: number): Promise - buscaUsuarioPorEmail(email: string): Promise buscaPorTodosUsuarios(): Promise } @@ -31,10 +30,6 @@ export class UsuarioRepository implements IUsuarioRepository { return this.orm.buscaUsuarioPorId(id) } - buscaUsuarioPorEmail(email: string): Promise { - return this.orm.buscaUsuarioPorEmail(email) - } - async buscaPorTodosUsuarios(): Promise { return this.orm.buscaPorTodosUsuarios() } diff --git a/src/shared/database/database-interface.ts b/src/shared/database/database-interface.ts index b010dff..531cc76 100644 --- a/src/shared/database/database-interface.ts +++ b/src/shared/database/database-interface.ts @@ -8,15 +8,14 @@ export interface IDatabase { atualizaUsuario(id: number, data: Partial): Promise adicionaTokenDeAutenticacaoAoUsuario(id: number, token: string): Promise buscaUsuarioPorId(id: number): Promise - buscaUsuarioPorEmail(email: string): Promise buscaPorTodosUsuarios(): Promise removeUsuario(id: number): Promise salvaAluno(data: AlunoDTO): Promise atualizaAluno(id: number, data: Partial): Promise buscaAlunoPorId(id: number): Promise - buscaAlunoPorEmail(email: string): Promise - buscaAlunoPorMatricula(matricula: string): Promise + buscaAlunoPorMatricula(matricula: string): Promise + buscaInformacoesDoAlunoPorId(id: number): Promise | null> removeAluno(id: number): Promise diff --git a/src/shared/database/prisma-database.ts b/src/shared/database/prisma-database.ts index 07c8883..71575f4 100644 --- a/src/shared/database/prisma-database.ts +++ b/src/shared/database/prisma-database.ts @@ -7,40 +7,22 @@ import { PrismaClientInitializationError } from '@prisma/client/runtime/library' import { DisciplinaHistoricoProps } from '../importers/implementation/ufcg-importer' const prisma = new PrismaClient() - export class PrismaDatabase implements IDatabase { - // Métodos de consulta e escrita do banco de dados para Usuário - async salvaUsuario(data: UsuarioDTO): Promise { - return prisma.usuario.create({ - data: { - nome: data.nome, - role: data.role, - } - }) + async salvaUsuario(usuarioDTO: UsuarioDTO): Promise { + return await prisma.usuario.create({ data: { ...usuarioDTO } }) } async atualizaUsuario(id: number, data: Partial): Promise { - return prisma.usuario.update({ where: { id }, data }) + return await prisma.usuario.update({ where: { id }, data }) } async adicionaTokenDeAutenticacaoAoUsuario(id: number, token: string): Promise { - await prisma.usuario.update({ - where: { - id, - }, - data: { - token - } - }) + await prisma.usuario.update({ where: { id }, data: { token } }) } async buscaUsuarioPorId(id: number): Promise { - return prisma.usuario.findUnique({ where: { id } }) - } - - buscaUsuarioPorEmail(email: string): Promise { - throw new Error('Method not implemented.') + return await prisma.usuario.findUnique({ where: { id } }) } async buscaPorTodosUsuarios(): Promise { @@ -52,8 +34,8 @@ export class PrismaDatabase implements IDatabase { } // Métodos de consulta e escrita do banco de dados para Aluno - salvaAluno(data: AlunoDTO): Promise { - const aluno = prisma.aluno.create({ + async salvaAluno(data: AlunoDTO): Promise { + const aluno = await prisma.aluno.create({ data: { nome: data.nome, matricula: data.matricula, @@ -68,24 +50,39 @@ export class PrismaDatabase implements IDatabase { return aluno } - atualizaAluno(id: number, data: Partial): Promise { - throw new Error('Method not implemented.') + async atualizaAluno(id: number, data: Partial): Promise { + return await prisma.aluno.update({ where: { id }, data }) } - buscaAlunoPorId(id: number): Promise { - throw new Error('Method not implemented.') + async buscaAlunoPorId(id: number): Promise { + return await prisma.aluno.findUnique({ where: { id } }) } - buscaAlunoPorEmail(email: string): Promise { - throw new Error('Method not implemented.') + async buscaInformacoesDoAlunoPorId(id: number): Promise | null> { + return await prisma.aluno.findUnique({ + where: { + id, + }, + select: { + id: true, + nome: true, + matricula: true, + universidade: true, + curso: true, + historicoAcademico: true, + createdAt: true, + updatedAt: true, + } + }) } - buscaAlunoPorMatricula(matricula: string): Promise { - throw new Error('Method not implemented.') + + async buscaAlunoPorMatricula(matricula: string): Promise { + return await prisma.aluno.findUnique({ where: { matricula } }) } - removeAluno(id: number): Promise { - throw new Error('Method not implemented.') + async removeAluno(id: number) { + await prisma.aluno.delete({ where: { id } }) } // Métodos de consulta e escrita do banco de dados para Disciplina diff --git a/src/shared/importers/implementation/ufcg-importer.ts b/src/shared/importers/implementation/ufcg-importer.ts index 261e582..1c431ec 100644 --- a/src/shared/importers/implementation/ufcg-importer.ts +++ b/src/shared/importers/implementation/ufcg-importer.ts @@ -50,17 +50,7 @@ export class UFCGImporter implements Importer { private ERRO_AUTENTICACAO = "ERRO NA AUTENTICAÇÃO" public static readonly ERRO = "Matrícula inválida ou senha incorreta." - reportProgress(message: string): void { - console.log(message) // Implement your progress reporting logic here - } - - private cleanText = (text: string): string => { - return he.decode(text) // Decodifica entidades HTML - .replace(/\s+/g, ' ') - .trim() // Remove espaços em branco no início e no final - } - - public async autenticaAluno(matricula: string, senha: string): Promise { + public async autenticaAluno(matricula: string, senha: string): Promise { this.reportProgress("Tentando fazer login...") try { @@ -68,20 +58,22 @@ export class UFCGImporter implements Importer { [this.LOGIN]: matricula, [this.SENHA]: senha, [this.COMMAND]: this.ALUNO_LOGIN - }), urlSearchParams) + }), { + timeout: 10000, + withCredentials: true, + validateStatus: () => true, + httpsAgent: new (require('https').Agent)({ + rejectUnauthorized: false + }), + responseEncoding: 'binary', + }) this.cookies = response.headers['set-cookie']! - let html = iconv.decode(Buffer.from(response.data), 'ISO-8859-1') - html = html.replace(/]*charset=["']?[^"'>]+["']?[^>]*>/i, '') - - const loadedDocument = cheerio.load(html) + const loadedDocument = this.carregaHtml(response) if (loadedDocument('head').text().includes(this.ERRO_AUTENTICACAO) || loadedDocument('body').text().includes(UFCGImporter.ERRO)) { throw new Error("Authentication Failed") } - - console.log(loadedDocument) - return loadedDocument } catch (err) { console.error(err) } @@ -101,9 +93,12 @@ export class UFCGImporter implements Importer { responseEncoding: 'binary', }) + + const loadedHistoricoDocument = this.carregaHtml(historicoResponse) - const trsDisciplinas = loadedHistoricoDocument("div[id=disciplinas] > table > tbody > tr").toArray() + + const trsDisciplinas = loadedHistoricoDocument("div[id=disciplinas] > table > tbody > tr").toArray() for (const el of trsDisciplinas) { const tds = loadedHistoricoDocument(el).find("td") const disciplina: DisciplinaHistoricoProps = this.montaDisciplina(tds, alunoId) @@ -197,7 +192,7 @@ export class UFCGImporter implements Importer { semestre: tds.eq(7).text() || '', mediaFinal: isNaN(parseFloat(tds.eq(5).text().replace(",", "."))) ? null : parseFloat(tds.eq(5).text().replace(",", ".")), situacao: this.definirSituacaoDisciplina(tds.eq(6).text(), parseFloat(tds.eq(5).text().replace(",", "."))), - docente: 'Ausente', + docente: this.cleanText(tds.eq(1).find('span.small').text() || 'Ausente') } return disciplina @@ -208,6 +203,8 @@ export class UFCGImporter implements Importer { switch (situacao) { case this.APROVADO: + status = SituacaoDisciplina.APROVADO; + break; case this.DISPENSA: status = SituacaoDisciplina.DISPENSA; if (mediaFinal === null) { @@ -267,4 +264,14 @@ export class UFCGImporter implements Importer { return 3 } } + + reportProgress(message: string): void { + console.log(message) // Implement your progress reporting logic here + } + + private cleanText = (text: string): string => { + return he.decode(text) // Decodifica entidades HTML + .replace(/\s+/g, ' ') + .trim() // Remove espaços em branco no início e no final + } } diff --git a/src/shared/importers/importer.ts b/src/shared/importers/importer.ts index 72bfdf0..eef4476 100644 --- a/src/shared/importers/importer.ts +++ b/src/shared/importers/importer.ts @@ -3,7 +3,7 @@ import * as cheerio from 'cheerio' import { DisciplinaHistoricoProps } from "./implementation/ufcg-importer" export interface Importer { - autenticaAluno(matricula: string, senha: string): Promise + autenticaAluno(matricula: string, senha: string): Promise obterDisciplinasDoHistoricoAcademico(alunoId: number): Promise obterDetalhesDaDisciplina(): Promise } From 7636971d861e38c638526954647c4a4128b9e1bc Mon Sep 17 00:00:00 2001 From: huandrey Date: Wed, 9 Oct 2024 09:28:58 -0300 Subject: [PATCH 3/3] =?UTF-8?q?Melhorias=20antes=20da=20apresenta=C3=A7?= =?UTF-8?q?=C3=A3o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../administrador/administrador-controller.ts | 9 ++++++++ .../administrador/administrador-repository.ts | 3 +++ .../administrador/administrador-service.ts | 3 +++ src/features/alunos/aluno-route.ts | 1 + .../autenticacao/autenticacao-route.ts | 2 +- .../autenticacao/autenticacao-service.ts | 2 +- src/features/sessoes/sessao-controller.ts | 10 ++++----- src/features/sessoes/sessao-route.ts | 2 +- src/features/usuarios/usuario-controller.ts | 22 +++++++++---------- src/features/usuarios/usuario-repository.ts | 4 ++-- src/features/usuarios/usuario-route.ts | 3 ++- src/features/usuarios/usuario-service.ts | 11 ++++------ 12 files changed, 43 insertions(+), 29 deletions(-) diff --git a/src/features/administrador/administrador-controller.ts b/src/features/administrador/administrador-controller.ts index e69de29..b5e8a0f 100644 --- a/src/features/administrador/administrador-controller.ts +++ b/src/features/administrador/administrador-controller.ts @@ -0,0 +1,9 @@ +import { AdministradorService } from "./administrador-service" + +export class AdministradorController { + private administadorService: AdministradorService + + constructor(administradorService: AdministradorService) { + this.administadorService = administradorService + } +} diff --git a/src/features/administrador/administrador-repository.ts b/src/features/administrador/administrador-repository.ts index e69de29..b49ebbc 100644 --- a/src/features/administrador/administrador-repository.ts +++ b/src/features/administrador/administrador-repository.ts @@ -0,0 +1,3 @@ +export class AdministradorRepository { + constructor() { } +} diff --git a/src/features/administrador/administrador-service.ts b/src/features/administrador/administrador-service.ts index e69de29..6c2fe84 100644 --- a/src/features/administrador/administrador-service.ts +++ b/src/features/administrador/administrador-service.ts @@ -0,0 +1,3 @@ +export class AdministradorService { + constructor() { } +} diff --git a/src/features/alunos/aluno-route.ts b/src/features/alunos/aluno-route.ts index 5b5addd..b6eae41 100644 --- a/src/features/alunos/aluno-route.ts +++ b/src/features/alunos/aluno-route.ts @@ -17,6 +17,7 @@ const usuarioService = new UsuarioService(usuarioRepository) const alunoController = new AlunoController(usuarioService, alunoService) const middlewareAutorizacao = new AutorizacaoMiddleware() +router.get('/:id', middlewareAutorizacao.autorizarApenas('ADMIN'), alunoController.buscaAlunoPorId.bind(alunoController)) router.post('/create', middlewareAutorizacao.autorizarApenas('ADMIN'), alunoController.criaAluno.bind(alunoController)) router.post('/update', middlewareAutorizacao.autorizarApenas('ADMIN'), alunoController.atualizaAluno.bind(alunoController)) router.delete('/delete', middlewareAutorizacao.autorizarApenas('ADMIN'), alunoController.deletaAluno.bind(alunoController)); diff --git a/src/features/autenticacao/autenticacao-route.ts b/src/features/autenticacao/autenticacao-route.ts index b200b35..1d27099 100644 --- a/src/features/autenticacao/autenticacao-route.ts +++ b/src/features/autenticacao/autenticacao-route.ts @@ -11,6 +11,6 @@ const autenticacaoRepository = new AutenticacaoRepository(database) const autenticacaoService = new AutenticacaoService(autenticacaoRepository) const autenticacaoController = new AutenticacaoController(autenticacaoService) -router.post('/token/generate', autenticacaoController.gerarToken.bind(autenticacaoController)) +router.post('/gera-token', autenticacaoController.gerarToken.bind(autenticacaoController)) export { router as autenticacaoRoutes } diff --git a/src/features/autenticacao/autenticacao-service.ts b/src/features/autenticacao/autenticacao-service.ts index b2d6cc4..47cf9d0 100644 --- a/src/features/autenticacao/autenticacao-service.ts +++ b/src/features/autenticacao/autenticacao-service.ts @@ -27,7 +27,7 @@ export class AutenticacaoService implements IAutenticacaoService { }); try { - await this.autenticacaoRepository.adicionaTokenDeAutenticacao(usuario.id, token) + // await this.autenticacaoRepository.adicionaTokenDeAutenticacao(usuario.id, token) return token; } catch (err) { throw new ReportarErrorAoSistema("Erro ao tentar salvar token no banco de dados") diff --git a/src/features/sessoes/sessao-controller.ts b/src/features/sessoes/sessao-controller.ts index 96ba1f6..262d8ed 100644 --- a/src/features/sessoes/sessao-controller.ts +++ b/src/features/sessoes/sessao-controller.ts @@ -16,13 +16,13 @@ export class SessaoController { constructor(usuarioService: UsuarioService, alunoService: AlunoService, disciplinaService: DisciplinaService, sessaoService: SessaoService) { this.usuarioService = usuarioService, - this.alunoService = alunoService + this.alunoService = alunoService this.disciplinaService = disciplinaService this.sessaoService = sessaoService } public async criaConexaoComSessao(req: Request, res: Response): Promise { - const sessaoDTO: SessaoDTO = req.body; + const sessaoDTO: Omit = req.body; try { const aluno = await this.configuraSessaoDoUsuario(sessaoDTO) @@ -37,11 +37,11 @@ export class SessaoController { } } - private async configuraSessaoDoUsuario(sessaoDTO: SessaoDTO) { - const { matricula, senha, vinculo, alunoId } = sessaoDTO + private async configuraSessaoDoUsuario(sessaoDTO: Omit) { + const { matricula, senha, vinculo } = sessaoDTO const importer: Importer = this.sessaoService.lidaComIdentificacaoDaInstituicaoDeEnsino(vinculo) - await importer.autenticaAluno(matricula, senha) // Autentica usuário e salva cookie retornado da response + await importer.autenticaAluno(matricula, senha) // Autentica usuário e salva cookie retornado da response const dadosAluno = { nome: 'Huandrey', diff --git a/src/features/sessoes/sessao-route.ts b/src/features/sessoes/sessao-route.ts index 581ddde..cc5f20f 100644 --- a/src/features/sessoes/sessao-route.ts +++ b/src/features/sessoes/sessao-route.ts @@ -29,6 +29,6 @@ const sessaoController = new SessaoController( sessaoService, ) -router.post('/session', sessaoController.criaConexaoComSessao.bind(sessaoController)) +router.post('/iniciar-sessao', sessaoController.criaConexaoComSessao.bind(sessaoController)) export { router as sessaoRoutes } diff --git a/src/features/usuarios/usuario-controller.ts b/src/features/usuarios/usuario-controller.ts index c67a3b6..0415606 100644 --- a/src/features/usuarios/usuario-controller.ts +++ b/src/features/usuarios/usuario-controller.ts @@ -21,8 +21,8 @@ export class UsuarioController { public async criaUsuario(req: CriaUsuarioRequest, res: Response): Promise { try { const { nome, role } = req.body - const usuario = await this.usuarioService.lidaComCriacaoDoUsuario({ nome, role }) - res.status(201).json(usuario) + const usuarioId = await this.usuarioService.lidaComCriacaoDoUsuario({ nome, role }) + res.status(201).json({ message: `Usuário com id ${usuarioId} criado com sucesso` }) } catch (error) { if (error instanceof ReportarErrorAoSistema) { res.status(400).json({ error: error.message }) @@ -43,14 +43,14 @@ export class UsuarioController { if (updatedUsuario) { res.status(200).json(updatedUsuario) } else { - res.status(404).json({ error: 'Usuario not found' }) + res.status(404).json({ message: 'Usuario not found' }) } } catch (error) { if (error instanceof ReportarErrorAoSistema) { - res.status(400).json({ error: error.message }) + res.status(400).json({ message: error.message }) } else { console.error(error) - res.status(500).json({ error: 'Internal Server Error' }) + res.status(500).json({ message: 'Internal Server Error' }) } } } @@ -62,10 +62,10 @@ export class UsuarioController { res.status(204).send() // No Content } catch (error) { if (error instanceof ReportarErrorAoSistema) { - res.status(400).json({ error: error.message }) + res.status(400).json({ message: error.message }) } else { console.error(error) - res.status(500).json({ error: 'Internal Server Error' }) + res.status(500).json({ message: 'Internal Server Error' }) } } } @@ -85,10 +85,10 @@ export class UsuarioController { res.status(200).json(usuarioEncontrado) } catch (error) { if (error instanceof ReportarErrorAoSistema) { - res.status(400).json({ error: error.message }) + res.status(400).json({ message: error.message }) } else { console.error(error) - res.status(500).json({ error: 'Internal Server Error' }) + res.status(500).json({ message: 'Internal Server Error' }) } } } @@ -103,10 +103,10 @@ export class UsuarioController { }) } catch (error) { if (error instanceof ReportarErrorAoSistema) { - res.status(400).json({ error: error.message }) + res.status(400).json({ message: error.message }) } else { console.error(error) - res.status(500).json({ error: 'Internal Server Error' }) + res.status(500).json({ message: 'Internal Server Error' }) } } } diff --git a/src/features/usuarios/usuario-repository.ts b/src/features/usuarios/usuario-repository.ts index 67dacfd..33f1b35 100644 --- a/src/features/usuarios/usuario-repository.ts +++ b/src/features/usuarios/usuario-repository.ts @@ -5,7 +5,7 @@ import { UsuarioDTO } from "./usuario-dto" export interface IUsuarioRepository { criaUsuario(usuarioDTO: UsuarioDTO): Promise atualizaUsuario(id: number, data: Partial): Promise - deletaUsuario(id: number): Promise + removeUsuario(id: number): Promise buscaUsuarioPorId(id: number): Promise buscaPorTodosUsuarios(): Promise } @@ -34,7 +34,7 @@ export class UsuarioRepository implements IUsuarioRepository { return this.orm.buscaPorTodosUsuarios() } - async deletaUsuario(id: number): Promise { + async removeUsuario(id: number): Promise { await this.orm.removeUsuario(id) } } diff --git a/src/features/usuarios/usuario-route.ts b/src/features/usuarios/usuario-route.ts index b2e86c9..5942bdc 100644 --- a/src/features/usuarios/usuario-route.ts +++ b/src/features/usuarios/usuario-route.ts @@ -13,7 +13,8 @@ const middlewareAutorizacao = new AutorizacaoMiddleware() const usuarioService = new UsuarioService(usuarioRepository) const usuarioController = new UsuarioController(usuarioService) -router.get('', usuarioController.buscaUsuarios.bind(usuarioController)) +router.get('', middlewareAutorizacao.autorizarApenas('ADMIN'), usuarioController.buscaUsuarios.bind(usuarioController)) +router.get('/:id', middlewareAutorizacao.autorizarApenas('ADMIN'), usuarioController.buscaUsuario.bind(usuarioController)) router.post('/create', usuarioController.criaUsuario.bind(usuarioController)) router.put('/update/:id', middlewareAutorizacao.autorizarApenas('ADMIN'), usuarioController.atualizaUsuario.bind(usuarioController)) router.delete('/delete/:id', middlewareAutorizacao.autorizarApenas('ADMIN'), usuarioController.deletaUsuario.bind(usuarioController)) diff --git a/src/features/usuarios/usuario-service.ts b/src/features/usuarios/usuario-service.ts index 307b7ed..26dd71c 100644 --- a/src/features/usuarios/usuario-service.ts +++ b/src/features/usuarios/usuario-service.ts @@ -44,9 +44,10 @@ export class UsuarioService implements IUsuarioService { } } async lidaComAtualizacaoDoUsuario(id: number, data: Partial) { - if (!data.nome || !data.role) { - throw new ReportarErrorAoSistema('Nome e role são obrigatórios') + if (!data.nome) { + throw new ReportarErrorAoSistema('Não foi informado nenhum parâmetro. Veja a documentação.') } + try { const updatedUsuario = await this.usuarioRepository.atualizaUsuario(id, data) return updatedUsuario @@ -56,11 +57,7 @@ export class UsuarioService implements IUsuarioService { } async lidaComRemocaoDoUsuario(id: number) { - try { - await this.usuarioRepository.deletaUsuario(id) - } catch (error) { - throw new Error('Error deleting usuario') - } + await this.usuarioRepository.removeUsuario(id) } async lidaComBuscaDeTodosUsuariosDoSistema() {