Skip to content

feat: integração SPA de painel financeiro e autenticação global#255

Open
glauccoeng-prog wants to merge 1 commit intomate-academy:masterfrom
glauccoeng-prog:develop
Open

feat: integração SPA de painel financeiro e autenticação global#255
glauccoeng-prog wants to merge 1 commit intomate-academy:masterfrom
glauccoeng-prog:develop

Conversation

@glauccoeng-prog
Copy link
Copy Markdown

No description provided.

Copilot AI review requested due to automatic review settings March 26, 2026 20:20
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Esta PR introduz uma SPA (React/Vite) para painel financeiro (despesas/categorias) e adiciona uma camada completa de autenticação no backend (JWT em cookies, fluxo de ativação por email, reset de senha e OAuth via Passport), integrando rotas protegidas e rotas de visitante.

Changes:

  • Backend: implementação de autenticação (JWT stateless + cookies), perfil do usuário, reset/ativação por email e integração OAuth (Google/GitHub/Apple).
  • Backend: criação de endpoints de contabilidade (/accounting/expenses e /accounting/categories) com serviços/models Sequelize.
  • Frontend: nova SPA com rotas protegidas/guest, contexto global de auth, APIs para auth/expenses/categories e um conjunto de componentes UI/tema.

Reviewed changes

Copilot reviewed 114 out of 118 changed files in this pull request and generated 19 comments.

Show a summary per file
File Description
src/utils/validatePassword.js Validação de regras de senha
src/utils/ApiError.js Erro HTTP estruturado
src/setup.js Setup Sequelize/Postgres
src/services/userService.js CRUD/queries User
src/services/tokenService.js Geração/validação JWT
src/services/jwtService.js Wrapper jsonwebtoken
src/services/expenses.service.js Serviço de despesas + filtros
src/services/emailService.js Envio de emails via Resend
src/services/categories.service.js Serviço de categorias
src/services/authService.js Fluxos de auth (register/login/refresh/reset)
src/routes/userRoutes.js Rotas de perfil protegidas
src/routes/oauthRoutes.js Rotas OAuth (Passport)
src/routes/expensesRoutes.js Rotas REST de despesas
src/routes/categoriesRoutes.js Rotas REST de categorias
src/routes/authRoutes.js Rotas de autenticação
src/models/User.js Model Sequelize User
src/models/Expense.js Model Sequelize Expense
src/models/Category.js Model Sequelize Category
src/middlewares/guestMiddleware.js Proteção guest-only
src/middlewares/errorMiddleware.js Handler global de erros
src/middlewares/authMiddleware.js Proteção por access token cookie
src/index.js Bootstrap + sync DB + start server
src/createServer.js Factory do Express (CORS/session/passport/rotas)
src/controllers/userController.js Controller de perfil
src/controllers/expenses.controller.js Controller de despesas
src/controllers/categories.controller.js Controller de categorias
src/controllers/authController.js Controller de autenticação
src/config/passport.js Estratégias Passport (Google/GitHub/Apple)
package.json Deps backend + bump scripts
client/vite.config.js Proxy /api/* para backend
client/src/styles/theme.css Variáveis/tema (dark/light)
client/src/styles/tailwind.css Imports Tailwind
client/src/styles/index.css Entry CSS
client/src/styles/fonts.css Fonte Inter (Google Fonts)
client/src/main.jsx Bootstrap React root
client/src/context/useAuth.js Hook de auth context
client/src/context/authContextValue.js Context value isolado
client/src/context/AuthContext.jsx AuthProvider (refresh/login/logout/etc)
client/src/app/components/ui/utils.ts cn helper (clsx + twMerge)
client/src/app/components/ui/use-mobile.ts Hook breakpoint mobile
client/src/app/components/ui/tooltip.tsx Tooltip (Radix)
client/src/app/components/ui/toggle.tsx Toggle (Radix)
client/src/app/components/ui/toggle-group.tsx ToggleGroup (Radix)
client/src/app/components/ui/textarea.tsx Textarea UI
client/src/app/components/ui/tabs.tsx Tabs UI
client/src/app/components/ui/table.tsx Table UI
client/src/app/components/ui/switch.tsx Switch UI
client/src/app/components/ui/sonner.tsx Toaster wrapper (Sonner)
client/src/app/components/ui/slider.tsx Slider UI
client/src/app/components/ui/skeleton.tsx Skeleton UI
client/src/app/components/ui/sheet.tsx Sheet/Drawer UI (Dialog)
client/src/app/components/ui/separator.tsx Separator UI
client/src/app/components/ui/select.tsx Select UI
client/src/app/components/ui/scroll-area.tsx ScrollArea UI
client/src/app/components/ui/resizable.tsx Resizable panels UI
client/src/app/components/ui/radio-group.tsx RadioGroup UI
client/src/app/components/ui/progress.tsx Progress UI
client/src/app/components/ui/popover.tsx Popover UI
client/src/app/components/ui/pagination.tsx Pagination UI
client/src/app/components/ui/navigation-menu.tsx NavigationMenu UI
client/src/app/components/ui/label.tsx Label UI
client/src/app/components/ui/input.tsx Input UI
client/src/app/components/ui/input-otp.tsx OTP input UI
client/src/app/components/ui/hover-card.tsx HoverCard UI
client/src/app/components/ui/form.tsx RHF form primitives
client/src/app/components/ui/drawer.tsx Drawer (Vaul) UI
client/src/app/components/ui/dialog.tsx Dialog UI
client/src/app/components/ui/context-menu.tsx ContextMenu UI
client/src/app/components/ui/command.tsx Command palette UI
client/src/app/components/ui/collapsible.tsx Collapsible UI
client/src/app/components/ui/checkbox.tsx Checkbox UI
client/src/app/components/ui/carousel.tsx Carousel UI (Embla)
client/src/app/components/ui/card.tsx Card UI
client/src/app/components/ui/calendar.tsx Calendar UI
client/src/app/components/ui/button.tsx Button UI
client/src/app/components/ui/breadcrumb.tsx Breadcrumb UI
client/src/app/components/ui/badge.tsx Badge UI
client/src/app/components/ui/avatar.tsx Avatar UI
client/src/app/components/ui/aspect-ratio.tsx AspectRatio UI
client/src/app/components/ui/alert.tsx Alert UI
client/src/app/components/ui/alert-dialog.tsx AlertDialog UI
client/src/app/components/ui/accordion.tsx Accordion UI
client/src/app/components/theme-toggle.tsx Toggle light/dark
client/src/app/components/theme-provider.tsx Provider de tema (custom)
client/src/app/components/reset-password-page.tsx Tela solicitar reset
client/src/app/components/reset-confirm-page.tsx Tela confirmar reset
client/src/app/components/register-page.tsx Tela de registro
client/src/app/components/protected-route.tsx Guard rota protegida
client/src/app/components/profile-page.tsx Página de perfil
client/src/app/components/not-found-page.tsx 404 page
client/src/app/components/guest-route.tsx Guard rota guest
client/src/app/components/categories-page.tsx Página categorias
client/src/app/components/auth-layout.tsx Layout telas auth
client/src/app/components/app-layout.tsx Layout app (sidebar/topbar)
client/src/app/components/activation-page.tsx Tela ativação conta
client/src/api/expenseApi.ts Client API despesas
client/src/api/categoryApi.ts Client API categorias
client/src/api/authApi.js Client API auth/perfil
client/src/App.jsx Rotas SPA + providers
client/public/favicon.svg Favicon
client/package.json Deps/scripts do client
client/jsconfig.json Config TS/paths (JS)
client/index.html HTML Vite
client/eslint.config.js ESLint config client
client/.gitignore Gitignore client
.gitignore Ignora .env etc
.github/workflows/test.yml-template Template CI
.eslintrc.js Ajustes ESLint root

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +25 to +37
const create = async (req, res) => {
const { name, type } = req.body;

// Validação básica: nome não pode ser vazio
if (!name) {
return res.status(400).json({ message: 'Nome é obrigatório' });
}

const newCategory = await categoriesService.create({
name,
type,
userId: req.user.id,
});
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

O controller envia type para categoriesService.create, mas o modelo Category não define a coluna type. Dependendo da config do Sequelize, isso pode ser ignorado ou causar erro, e o cliente espera type na resposta. Sugestão: ou adicionar type ao model/migration, ou remover type do payload/contrato da API.

Copilot uses AI. Check for mistakes.
Comment on lines +79 to +84
// Verifica a senha atual
const isOldPasswordValid = await bcrypt.compare(oldPassword, user.password);

if (!isOldPasswordValid) {
throw ApiError.badRequest('Senha atual incorreta');
}
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bcrypt.compare(oldPassword, user.password) vai lançar erro se user.password for null (contas OAuth). Sugestão: antes do compare, bloquear a ação para contas sem senha (retornar 400/409) ou implementar fluxo de “definir senha” para OAuth.

Copilot uses AI. Check for mistakes.
Comment on lines +66 to +75
app.use(
session({
secret: process.env.SESSION_SECRET || 'session_secret',
resave: false,
saveUninitialized: false,
cookie: {
secure: false, // true em produção com HTTPS
maxAge: 24 * 60 * 60 * 1000, // 24 horas
},
}),
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A configuração de sessão usa fallback session_secret e cookie.secure: false. Em produção isso é inseguro (secret previsível e cookie de sessão pode trafegar em HTTP). Sugestão: exigir SESSION_SECRET e setar cookie.secure/sameSite/proxy corretamente quando NODE_ENV==='production' (e considerar trust proxy).

Copilot uses AI. Check for mistakes.
Comment on lines +3 to +8
import { useTheme } from "next-themes";
import { Toaster as Sonner, ToasterProps } from "sonner";

const Toaster = ({ ...props }: ToasterProps) => {
const { theme = "system" } = useTheme();

Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Este componente usa useTheme de next-themes, mas a aplicação está usando um ThemeProvider próprio (client/src/app/components/theme-provider.tsx). Isso deve causar erro de runtime ou tema não funcionar. Sugestão: trocar para o hook/context do provider local, ou envolver a app com o ThemeProvider do next-themes consistentemente.

Copilot uses AI. Check for mistakes.
Comment on lines +16 to +19
// Sincroniza os modelos com o banco de dados
// alter: true atualiza as tabelas existentes sem perder dados
await sequelize.sync({ alter: true });

Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sequelize.sync({ alter: true }) em runtime é perigoso (pode gerar alterações de schema inesperadas e travar/romper produção). Sugestão: usar migrations e/ou condicionar alter apenas a ambiente de desenvolvimento (ex.: NODE_ENV !== 'production').

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +4
import { cn } from "./utils";

function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
return (
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Este arquivo usa React.ComponentProps mas não importa React (nem type import). Em TS isso normalmente quebra com Cannot find name 'React'. Sugestão: adicionar import type * as React from 'react' (ou importar ComponentProps diretamente).

Copilot uses AI. Check for mistakes.
Comment on lines +13 to +19
style={
{
"--normal-bg": "var(--popover)",
"--normal-text": "var(--popover-foreground)",
"--normal-border": "var(--border)",
} as React.CSSProperties
}
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

O style faz cast para React.CSSProperties, mas não há import do namespace React no arquivo. Isso tende a causar erro de TypeScript. Sugestão: importar type { CSSProperties } de react e usar as CSSProperties (ou adicionar import type * as React from 'react').

Copilot uses AI. Check for mistakes.
Comment on lines +35 to +40
// Validação: campos obrigatórios
if (!spentAt || !title || !amount) {
return res
.status(400)
.json({ message: 'Todos os campos são obrigatórios' });
}
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A validação if (!spentAt || !title || !amount) trata amount = 0 como inválido. Se 0 for um valor permitido (ex.: ajuste/estorno), isso vai bloquear requests. Sugestão: validar amount == null (null/undefined) e/ou Number.isFinite(amount) em vez de checagem truthy.

Copilot uses AI. Check for mistakes.
Comment on lines +132 to +137
// Verifica a senha
const isPasswordValid = await bcrypt.compare(password, user.password);

if (!isPasswordValid) {
throw ApiError.badRequest('Senha incorreta');
}
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bcrypt.compare(password, user.password) também falha para usuários OAuth (user.password null). Além disso, mudança de email via senha não funciona para esse tipo de conta. Sugestão: validar user.password antes e retornar erro claro/instrução (ex.: “conta social, defina uma senha primeiro”).

Copilot uses AI. Check for mistakes.
id: number;
userId: number;
name: string;
type: string;
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

O client tipa/serializa CategoryResponse com campo obrigatório type, mas o backend (model/controller) não garante esse campo na resposta. Isso pode causar inconsistências/erros de runtime. Sugestão: alinhar o contrato (remover type do client ou adicionar suporte completo no backend).

Suggested change
type: string;
type?: string;

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants