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
21 changes: 21 additions & 0 deletions docs/process/sentinel-log.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,24 @@ Related: [ADF-PROTOCOL](./ADF-PROTOCOL.md) | [Next.js 15 Build Fix](./../../supe
- **Commits**: 8 commits atômicos realizados e enviados.
- **Scripts**: Validados e funcionais via `pg` driver e `PrismaClient`.

---

## [2026-05-06] Incident: Vercel 504 Gateway Timeout (Middleware)

**Status**: RESOLVED
**Impact**: High (Site unreachable in production)
**Symptoms**: `MIDDLEWARE_INVOCATION_TIMEOUT` error on Vercel.

### 🔍 Analysis (RCA)
Latency accumulation. The middleware performed up to 3 sequential Supabase queries (`auth.getUser` -> `funcionario.id` -> `funcionario.role`). The Brasil/USA cross-region latency exceeded the 5-10s Edge Runtime limit.

### 💡 Key Learning (Filtro B - Regra de Projeto)
**Mandato de Round-trip Mínimo**: Middleware em Next.js 15 deve realizar no máximo UMA query externa se o banco não estiver na mesma região do Edge.
**Ação**: Consolidar checagem de existência e permissão em um único `.select('role')`.

### 🛡️ Proof of State
- **PR #99**: Criado e enviado.
- **Deploy**: Realizado via Vercel CLI com sucesso.
- **Verification**: `npm run typecheck` e testes de auth passando.


12 changes: 4 additions & 8 deletions src/utils/supabase/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,15 @@ export const updateSession = async (request: NextRequest) => {
}

if (user && isProtectedRoute) {
// Single query to fetch role - if null, user is not a 'funcionario' (ALUNO)
const { data: funcionarioProfile } = await supabase
.from('funcionarios')
.select('id')
.select('role')
.eq('id', user.id)
.maybeSingle();

const isFuncionario = !!funcionarioProfile;
const userRole = funcionarioProfile?.role;

if (isFuncionario && isAlunoRoute) {
const dashboardUrl = request.nextUrl.clone();
Expand All @@ -68,13 +70,7 @@ export const updateSession = async (request: NextRequest) => {
const isFinancialRoute = FINANCIAL_ROUTES.some((r) => pathname.startsWith(r));

if (isFuncionario && isFinancialRoute) {
const { data: roleData, error } = await supabase
.from('funcionarios')
.select('role')
.eq('id', user.id)
.maybeSingle();

if (error || roleData?.role !== 'GERENTE') {
if (userRole !== 'GERENTE') {
const dashboardUrl = request.nextUrl.clone();
dashboardUrl.pathname = '/dashboard';
return NextResponse.redirect(dashboardUrl);
Expand Down
Loading