Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .vercelignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Testes sob api/ seriam interpretados como funções serverless pela Vercel.
api/**/*.test.ts
api/test/
server/**/*.test.ts
server/test/

# Artefatos e infraestrutura usados apenas no pipeline local/CI.
e2e/
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,8 @@ curl -X POST http://localhost:3000/api/clientes \
## 📂 Estrutura

```txt
api/ # Vercel Serverless Functions
api/ # roteador serverless único da Vercel
server/ # handlers, domínio e infraestrutura da API
├── _lib/ # auth, prisma e helpers compartilhados
├── auth/ # register, login, me
├── clientes/ # endpoints de clientes
Expand Down
69 changes: 69 additions & 0 deletions api/[...route].ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import type { VercelRequest, VercelResponse } from '@vercel/node';
import type { IncomingMessage, ServerResponse } from 'node:http';
import loginHandler from '../server/auth/login.js';
import registerHandler from '../server/auth/register.js';
import meHandler from '../server/auth/me.js';
import logoutHandler from '../server/auth/logout.js';
import clientesHandler from '../server/clientes/index.js';
import veiculosHandler from '../server/veiculos/index.js';
import fotoHandler from '../server/veiculos/[id]/foto.js';
import ordensHandler from '../server/ordens/index.js';
import statusHandler from '../server/ordens/[id]/status.js';
import healthHandler from '../server/health.js';
import currentOrganizationHandler from '../server/organizations/current.js';
import switchOrganizationHandler from '../server/organizations/switch.js';
import membersHandler from '../server/organizations/members/index.js';
import memberHandler from '../server/organizations/members/[id].js';
import invitationsHandler from '../server/invitations/index.js';
import invitationHandler from '../server/invitations/[id].js';
import acceptInvitationHandler from '../server/invitations/accept.js';

type Handler = (req: VercelRequest, res: VercelResponse) => unknown | Promise<unknown>;

const staticRoutes: Record<string, Handler> = {
'/api/auth/login': loginHandler,
'/api/auth/register': registerHandler,
'/api/auth/me': meHandler,
'/api/auth/logout': logoutHandler,
'/api/clientes': clientesHandler,
'/api/veiculos': veiculosHandler,
'/api/ordens': ordensHandler,
'/api/health': healthHandler,
'/api/organizations/current': currentOrganizationHandler,
'/api/organizations/switch': switchOrganizationHandler,
'/api/organizations/members': membersHandler,
'/api/invitations': invitationsHandler,
'/api/invitations/accept': acceptInvitationHandler,
};

const dynamicRoutes: Array<{
pattern: RegExp;
handler: Handler;
}> = [
{ pattern: /^\/api\/veiculos\/([^/]+)\/foto$/, handler: fotoHandler },
{ pattern: /^\/api\/ordens\/([^/]+)\/status$/, handler: statusHandler },
{ pattern: /^\/api\/organizations\/members\/([^/]+)$/, handler: memberHandler },
{ pattern: /^\/api\/invitations\/([^/]+)$/, handler: invitationHandler },
];

export default async function router(req: VercelRequest, res: VercelResponse) {
const pathname = new URL(req.url ?? '/', 'http://localhost').pathname.replace(/\/$/, '');
const staticHandler = staticRoutes[pathname];
if (staticHandler) return staticHandler(req, res);

for (const route of dynamicRoutes) {
const match = pathname.match(route.pattern);
if (!match) continue;
req.query = { ...req.query, id: decodeURIComponent(match[1]) };
return route.handler(req, res);
}

return res.status(404).json({
message: 'Rota não encontrada',
error: { code: 'NOT_FOUND', message: 'Rota não encontrada' },
});
}

// Mantém compatibilidade de tipos com o adaptador Node usado localmente.
export type NodeRequest = IncomingMessage;
export type NodeResponse = ServerResponse;
32 changes: 16 additions & 16 deletions e2e/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,22 @@ import { stat } from 'node:fs/promises';
import { createServer, type IncomingMessage, type ServerResponse } from 'node:http';
import { extname, resolve } from 'node:path';
import type { VercelRequest, VercelResponse } from '@vercel/node';
import loginHandler from '../api/auth/login.js';
import registerHandler from '../api/auth/register.js';
import meHandler from '../api/auth/me.js';
import logoutHandler from '../api/auth/logout.js';
import clientesHandler from '../api/clientes/index.js';
import veiculosHandler from '../api/veiculos/index.js';
import ordensHandler from '../api/ordens/index.js';
import statusHandler from '../api/ordens/[id]/status.js';
import healthHandler from '../api/health.js';
import currentOrganizationHandler from '../api/organizations/current.js';
import switchOrganizationHandler from '../api/organizations/switch.js';
import membersHandler from '../api/organizations/members/index.js';
import memberHandler from '../api/organizations/members/[id].js';
import invitationsHandler from '../api/invitations/index.js';
import invitationHandler from '../api/invitations/[id].js';
import acceptInvitationHandler from '../api/invitations/accept.js';
import loginHandler from '../server/auth/login.js';
import registerHandler from '../server/auth/register.js';
import meHandler from '../server/auth/me.js';
import logoutHandler from '../server/auth/logout.js';
import clientesHandler from '../server/clientes/index.js';
import veiculosHandler from '../server/veiculos/index.js';
import ordensHandler from '../server/ordens/index.js';
import statusHandler from '../server/ordens/[id]/status.js';
import healthHandler from '../server/health.js';
import currentOrganizationHandler from '../server/organizations/current.js';
import switchOrganizationHandler from '../server/organizations/switch.js';
import membersHandler from '../server/organizations/members/index.js';
import memberHandler from '../server/organizations/members/[id].js';
import invitationsHandler from '../server/invitations/index.js';
import invitationHandler from '../server/invitations/[id].js';
import acceptInvitationHandler from '../server/invitations/accept.js';

type Handler = (req: VercelRequest, res: VercelResponse) => unknown | Promise<unknown>;

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
1 change: 1 addition & 0 deletions tsconfig.api.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
},
"include": [
"api/**/*.ts",
"server/**/*.ts",
"e2e/**/*.ts",
"playwright.config.ts",
"prisma/**/*.ts",
Expand Down
6 changes: 3 additions & 3 deletions vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default defineConfig({
exclude: [
'**/node_modules/**',
'**/dist/**',
'api/**/*.integration.test.ts',
'server/**/*.integration.test.ts',
'e2e/**/*.spec.ts',
],
coverage: {
Expand All @@ -30,8 +30,8 @@ export default defineConfig({
// A cobertura publicada mede apenas unidades com suíte dedicada.
// Fluxos CRUD completos entram na futura suíte E2E/integração.
include: [
'api/_lib/auth.ts',
'api/_lib/observability.ts',
'server/_lib/auth.ts',
'server/_lib/observability.ts',
'src/lib/utils.ts',
'src/lib/useNumberTicker.ts',
'src/lib/useTheme.ts',
Expand Down
6 changes: 3 additions & 3 deletions vitest.integration.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ export default defineConfig({
test: {
environment: 'node',
include: [
'api/*.integration.test.ts',
'api/**/*.integration.test.ts',
'server/*.integration.test.ts',
'server/**/*.integration.test.ts',
],
globalSetup: ['./api/test/global-setup.ts'],
globalSetup: ['./server/test/global-setup.ts'],
fileParallelism: false,
sequence: {
concurrent: false,
Expand Down
Loading