Eliminando middleware legado do Clerk e corrigindo rotas/controllers para Logto

This commit is contained in:
2026-03-31 10:52:42 +00:00
parent b6900e8b3c
commit eb560596bd
26 changed files with 29 additions and 208 deletions

View File

@@ -14,7 +14,7 @@ import geometryTypeRoutes from '../src/server/routes/geometryTypeRoutes.js';
import stockRoutes from '../src/server/routes/stockRoutes.js'; import stockRoutes from '../src/server/routes/stockRoutes.js';
import notificationRoutes from '../src/server/routes/notificationRoutes.js'; import notificationRoutes from '../src/server/routes/notificationRoutes.js';
import instrumentRoutes from '../src/server/routes/instrumentRoutes.js'; import instrumentRoutes from '../src/server/routes/instrumentRoutes.js';
import { extractUser } from '../src/server/middleware/roleMiddleware.js'; import { extractUser } from '../src/server/middleware/authMiddleware.js';
import path from 'path'; import path from 'path';
const app = express(); const app = express();
@@ -22,7 +22,7 @@ const app = express();
app.use(cors({ app.use(cors({
origin: '*', // Be more specific in production origin: '*', // Be more specific in production
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'], methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization', 'x-clerk-user-id', 'x-organization-id', 'x-organization-name'] allowedHeaders: ['Content-Type', 'Authorization', 'x-organization-id', 'x-organization-name']
})); }));
app.use(express.json()); app.use(express.json());

View File

@@ -1,6 +1,6 @@
import { Request, Response } from 'express'; import { Request, Response } from 'express';
import * as appRecordService from '../services/applicationRecordService.js'; import * as appRecordService from '../services/applicationRecordService.js';
import '../middleware/roleMiddleware.js'; // Ensure type augmentation import '../middleware/authMiddleware.js'; // Ensure type augmentation
export const createApplicationRecord = async (req: Request, res: Response) => { export const createApplicationRecord = async (req: Request, res: Response) => {
try { try {

View File

@@ -2,7 +2,7 @@ import { Request, Response } from 'express';
import * as dataSheetService from '../services/dataSheetService.js'; import * as dataSheetService from '../services/dataSheetService.js';
import fs from 'fs'; import fs from 'fs';
import * as pdfExtractionService from '../services/pdfExtractionService.js'; import * as pdfExtractionService from '../services/pdfExtractionService.js';
import { IAppUser } from '../middleware/roleMiddleware.js'; import { IAppUser } from '../middleware/authMiddleware.js';
import { notificationService } from '../services/notificationService.js'; import { notificationService } from '../services/notificationService.js';
interface AuthRequest extends Request { interface AuthRequest extends Request {

View File

@@ -1,6 +1,6 @@
import { Request, Response } from 'express'; import { Request, Response } from 'express';
import GeometryType from '../models/GeometryType.js'; import GeometryType from '../models/GeometryType.js';
import { IAppUser } from '../middleware/roleMiddleware.js'; import { IAppUser } from '../middleware/authMiddleware.js';
interface AuthRequest extends Request { interface AuthRequest extends Request {
appUser?: IAppUser; appUser?: IAppUser;

View File

@@ -1,7 +1,7 @@
import { Request, Response } from 'express'; import { Request, Response } from 'express';
import * as inspectionService from '../services/inspectionService.js'; import * as inspectionService from '../services/inspectionService.js';
import { notificationService } from '../services/notificationService.js'; import { notificationService } from '../services/notificationService.js';
import '../middleware/roleMiddleware.js'; // Ensure type augmentation import '../middleware/authMiddleware.js'; // Ensure type augmentation
export const createInspection = async (req: Request, res: Response) => { export const createInspection = async (req: Request, res: Response) => {
try { try {

View File

@@ -1,6 +1,6 @@
import { Request, Response } from 'express'; import { Request, Response } from 'express';
import Instrument from '../models/Instrument.js'; import Instrument from '../models/Instrument.js';
import { IAppUser } from '../middleware/roleMiddleware.js'; import { IAppUser } from '../middleware/authMiddleware.js';
interface AuthRequest extends Request { interface AuthRequest extends Request {
appUser?: IAppUser; appUser?: IAppUser;

View File

@@ -1,6 +1,6 @@
import { Request, Response } from 'express'; import { Request, Response } from 'express';
import * as partService from '../services/partService.js'; import * as partService from '../services/partService.js';
import { IAppUser } from '../middleware/roleMiddleware.js'; import { IAppUser } from '../middleware/authMiddleware.js';
interface AuthRequest extends Request { interface AuthRequest extends Request {
appUser?: IAppUser; appUser?: IAppUser;

View File

@@ -1,6 +1,6 @@
import { Request, Response } from 'express'; import { Request, Response } from 'express';
import * as projectService from '../services/projectService.js'; import * as projectService from '../services/projectService.js';
import { IAppUser } from '../middleware/roleMiddleware.js'; import { IAppUser } from '../middleware/authMiddleware.js';
import { notificationService } from '../services/notificationService.js'; import { notificationService } from '../services/notificationService.js';
interface AuthRequest extends Request { interface AuthRequest extends Request {

View File

@@ -2,7 +2,7 @@ import { Request, Response } from 'express';
import StockItem from '../models/StockItem.js'; import StockItem from '../models/StockItem.js';
import StockMovement from '../models/StockMovement.js'; import StockMovement from '../models/StockMovement.js';
import { IAppUser } from '../middleware/roleMiddleware.js'; import { IAppUser } from '../middleware/authMiddleware.js';
import { notificationService } from '../services/notificationService.js'; import { notificationService } from '../services/notificationService.js';
interface AuthRequest extends Request { interface AuthRequest extends Request {

View File

@@ -2,7 +2,7 @@ import { Request, Response, NextFunction } from 'express';
import { authenticateRequest } from './logtoAuth.js'; import { authenticateRequest } from './logtoAuth.js';
import { findOneGpi } from '../config/supabase.js'; import { findOneGpi } from '../config/supabase.js';
export interface AppUser { export interface IAppUser {
id: string; id: string;
logtoId: string; logtoId: string;
email: string; email: string;
@@ -14,7 +14,7 @@ export interface AppUser {
declare module 'express-serve-static-core' { declare module 'express-serve-static-core' {
interface Request { interface Request {
appUser?: any; appUser?: IAppUser;
} }
} }

View File

@@ -1,19 +1,14 @@
import { createRemoteJWKSet, jwtVerify } from 'jose'; import { createRemoteJWKSet, jwtVerify } from 'jose';
import { supabase, findOneGpi } from '../config/supabase.js'; import { supabase, findOneGpi } from '../config/supabase.js';
import { IAppUser } from './authMiddleware.js';
const LOGTO_URL = process.env.LOGTO_URL || 'https://logto-admin-bzlued1boxl3t8ewsyn99an9.187.77.227.172.sslip.io'; const LOGTO_URL = process.env.LOGTO_URL || 'https://logto-admin-bzlued1boxl3t8ewsyn99an9.187.77.227.172.sslip.io';
const APP_ID = process.env.LOGTO_APP_ID || 'gpi-app-001'; const APP_ID = process.env.LOGTO_APP_ID || 'gpi-app-001';
const jwks = createRemoteJWKSet(new URL(`${LOGTO_URL}/oidc/jwks`)); const jwks = createRemoteJWKSet(new URL(`${LOGTO_URL}/oidc/jwks`));
export interface AppUser { export type AppUser = IAppUser;
id: string;
logtoId: string;
email: string;
name: string;
role: string;
}
export async function authenticateRequest(req: any): Promise<AppUser | null> { export async function authenticateRequest(req: any): Promise<IAppUser | null> {
const authHeader = req.headers.authorization; const authHeader = req.headers.authorization;
if (!authHeader?.startsWith('Bearer ')) { if (!authHeader?.startsWith('Bearer ')) {

View File

@@ -1,174 +0,0 @@
import { Request, Response, NextFunction } from 'express';
import User, { IUser } from '../models/User.js';
import OrganizationMember, { OrgRole } from '../models/OrganizationMember.js';
import Organization from '../models/Organization.js';
// Extended user info with organization context
export interface IAppUser extends IUser {
organizationId?: string;
organizationRole?: OrgRole;
organizationBanned?: boolean;
}
// Module augmentation for Express Request
declare module 'express-serve-static-core' {
interface Request {
appUser?: IAppUser;
}
}
/**
* Middleware to extract and verify user from Clerk ID header
* Also loads organization-specific role if organization context is provided
*/
export const extractUser = async (req: Request, res: Response, next: NextFunction) => {
try {
const clerkId = req.headers['x-clerk-user-id'] as string;
const organizationId = req.headers['x-organization-id'] as string;
if (!clerkId) {
return next(); // No user, continue without
}
const user = await User.findOne({ clerkId });
if (user) {
if (user.isBanned) {
return res.status(403).json({ error: 'Conta bloqueada. Entre em contato com o administrador.' });
}
// Create extended user object
const appUser: IAppUser = user.toObject() as IAppUser;
appUser.organizationId = organizationId;
// If organization context, get org-specific role
if (organizationId) {
// Check if Organization is globally banned (subscription specific, etc.)
const orgStatus = await Organization.findOne({ clerkId: organizationId });
const orgName = req.headers['x-organization-name'] ? decodeURIComponent(req.headers['x-organization-name'] as string) : undefined;
if (orgStatus) {
// Update name if different and present
if (orgName && orgStatus.name !== orgName) {
try {
await Organization.updateOne(
{ clerkId: organizationId },
{ name: orgName }
);
} catch (err) {
console.warn('Failed to update organization name', err);
}
}
if (orgStatus.isBanned) {
return res.status(403).json({
error: 'Acesso bloqueado: Esta organização está suspensa. Entre em contato com o suporte.'
});
}
} else {
// Create new org with name if present
try {
await Organization.create({
clerkId: organizationId,
name: orgName
});
} catch (_e) {
console.warn('Organization auto-create race condition', _e);
}
}
const member = await OrganizationMember.findOne({ clerkUserId: clerkId, organizationId });
if (member) {
if (member.isBanned) {
return res.status(403).json({ error: 'Acesso bloqueado nesta organização.' });
}
appUser.organizationRole = member.role;
appUser.role = member.role; // Override global role with org role
} else {
// User exists but is not a member of this org yet
appUser.organizationRole = 'guest';
appUser.role = 'guest';
}
}
req.appUser = appUser;
// console.log(`✅ Request authenticated as: ${appUser.name} (${appUser.role})`);
} else {
console.warn(`⚠️ User with Clerk ID ${clerkId} not found in MongoDB. Sync required.`);
}
next();
} catch (error) {
console.error('Error extracting user:', error);
next();
}
};
/**
* Middleware to require specific roles for a route
* @param allowedRoles Array of roles that can access the route
*/
export const requireRole = (allowedRoles: OrgRole[]) => {
return (req: Request, res: Response, next: NextFunction) => {
if (!req.appUser) {
return res.status(401).json({ error: 'Autenticação necessária.' });
}
// DEV Bypass: Developer has full power
if (req.appUser.email === 'admtracksteel@gmail.com') {
return next();
}
const effectiveRole = req.appUser.organizationRole || req.appUser.role;
if (!allowedRoles.includes(effectiveRole as OrgRole)) {
return res.status(403).json({ error: 'Acesso negado. Permissões insuficientes.' });
}
next();
};
};
/**
* Middleware to require admin role
*/
export const requireAdmin = requireRole(['admin']);
/**
* Middleware to require at least user role (user or admin)
*/
export const requireUser = requireRole(['user', 'admin']);
/**
* Middleware to check if user can edit (user or admin, not guest)
*/
export const canEdit = (req: Request, res: Response, next: NextFunction) => {
if (!req.appUser) {
return res.status(401).json({ error: 'Autenticação necessária.' });
}
const effectiveRole = req.appUser.organizationRole || req.appUser.role;
if (effectiveRole === 'guest') {
return res.status(403).json({ error: 'Convidados não podem editar. Solicite acesso ao administrador.' });
}
next();
};
/**
* Middleware to require Developer (Super Admin) access
* Hardcoded to specific email for security
*/
export const requireDeveloper = (req: Request, res: Response, next: NextFunction) => {
if (!req.appUser) {
return res.status(401).json({ error: 'Autenticação necessária.' });
}
if (req.appUser.email !== 'admtracksteel@gmail.com') {
console.warn(`⛔ Attempted unauthorized developer access by: ${req.appUser.email}`);
return res.status(403).json({ error: 'Acesso restrito ao desenvolvedor.' });
}
next();
};

View File

@@ -1,6 +1,6 @@
import { Router } from 'express'; import { Router } from 'express';
import * as appRecordController from '../controllers/applicationRecordController.js'; import * as appRecordController from '../controllers/applicationRecordController.js';
import { extractUser, requireUser } from '../middleware/roleMiddleware.js'; import { extractUser, requireUser } from '../middleware/authMiddleware.js';
const router = Router(); const router = Router();

View File

@@ -1,6 +1,6 @@
import { Router, Request, Response } from 'express'; import { Router, Request, Response } from 'express';
import { backupService } from '../services/backupService.js'; import { backupService } from '../services/backupService.js';
import { requireRole } from '../middleware/roleMiddleware.js'; import { requireRole } from '../middleware/authMiddleware.js';
const router = Router(); const router = Router();

View File

@@ -3,7 +3,7 @@ import multer from 'multer';
import path from 'path'; import path from 'path';
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
import * as dataSheetController from '../controllers/dataSheetController.js'; import * as dataSheetController from '../controllers/dataSheetController.js';
import { extractUser, requireAdmin } from '../middleware/roleMiddleware.js'; import { extractUser, requireAdmin } from '../middleware/authMiddleware.js';
import os from 'os'; import os from 'os';
const router = Router(); const router = Router();

View File

@@ -1,6 +1,6 @@
import { Router } from 'express'; import { Router } from 'express';
import * as geometryTypeController from '../controllers/geometryTypeController.js'; import * as geometryTypeController from '../controllers/geometryTypeController.js';
import { extractUser, requireAdmin } from '../middleware/roleMiddleware.js'; import { extractUser, requireAdmin } from '../middleware/authMiddleware.js';
const router = Router(); const router = Router();

View File

@@ -1,6 +1,6 @@
import { Router } from 'express'; import { Router } from 'express';
import * as inspectionController from '../controllers/inspectionController.js'; import * as inspectionController from '../controllers/inspectionController.js';
import { extractUser, requireUser } from '../middleware/roleMiddleware.js'; import { extractUser, requireUser } from '../middleware/authMiddleware.js';
import multer from 'multer'; import multer from 'multer';
import path from 'path'; import path from 'path';
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';

View File

@@ -1,6 +1,6 @@
import { Router } from 'express'; import { Router } from 'express';
import * as instrumentController from '../controllers/instrumentController.js'; import * as instrumentController from '../controllers/instrumentController.js';
import { extractUser, requireAdmin } from '../middleware/roleMiddleware.js'; import { extractUser, requireAdmin } from '../middleware/authMiddleware.js';
const router = Router(); const router = Router();

View File

@@ -1,6 +1,6 @@
import express from 'express'; import express from 'express';
import { sendMessage, getUnreadMessages, markMessageAsRead, getMyPendingMessages, deleteMessage, archiveMessage, recipientDeleteMessage } from '../controllers/messageController.js'; import { sendMessage, getUnreadMessages, markMessageAsRead, getMyPendingMessages, deleteMessage, archiveMessage, recipientDeleteMessage } from '../controllers/messageController.js';
import { extractUser } from '../middleware/roleMiddleware.js'; import { extractUser } from '../middleware/authMiddleware.js';
const router = express.Router(); const router = express.Router();

View File

@@ -1,6 +1,6 @@
import { Router } from 'express'; import { Router } from 'express';
import * as paintingSchemeController from '../controllers/paintingSchemeController.js'; import * as paintingSchemeController from '../controllers/paintingSchemeController.js';
import { extractUser, requireAdmin } from '../middleware/roleMiddleware.js'; import { extractUser, requireAdmin } from '../middleware/authMiddleware.js';
const router = Router(); const router = Router();

View File

@@ -1,6 +1,6 @@
import { Router } from 'express'; import { Router } from 'express';
import * as partController from '../controllers/partController.js'; import * as partController from '../controllers/partController.js';
import { extractUser, requireAdmin } from '../middleware/roleMiddleware.js'; import { extractUser, requireAdmin } from '../middleware/authMiddleware.js';
const router = Router(); const router = Router();

View File

@@ -1,6 +1,6 @@
import { Router } from 'express'; import { Router } from 'express';
import * as projectController from '../controllers/projectController.js'; import * as projectController from '../controllers/projectController.js';
import { extractUser, requireAdmin } from '../middleware/roleMiddleware.js'; import { extractUser, requireAdmin } from '../middleware/authMiddleware.js';
const router = Router(); const router = Router();

View File

@@ -1,6 +1,6 @@
import { Router } from 'express'; import { Router } from 'express';
import * as stockController from '../controllers/stockController.js'; import * as stockController from '../controllers/stockController.js';
import { extractUser, requireAdmin, requireUser } from '../middleware/roleMiddleware.js'; import { extractUser, requireAdmin, requireUser } from '../middleware/authMiddleware.js';
const router = Router(); const router = Router();

View File

@@ -1,6 +1,6 @@
import express from 'express'; import express from 'express';
import { getSettings, updateSettings, uploadLogo, serveLogo } from '../controllers/systemSettingsController.js'; import { getSettings, updateSettings, uploadLogo, serveLogo } from '../controllers/systemSettingsController.js';
import { extractUser, requireDeveloper } from '../middleware/roleMiddleware.js'; import { extractUser, requireDeveloper } from '../middleware/authMiddleware.js';
import { uploadLogoDetails } from '../middleware/uploadMiddleware.js'; import { uploadLogoDetails } from '../middleware/uploadMiddleware.js';
const router = express.Router(); const router = express.Router();

View File

@@ -1,6 +1,6 @@
import express from 'express'; import express from 'express';
import { syncUser, getCurrentUser, getAllUsers, updateUserRole, toggleBanUser, heartbeat, getActiveUsers, deleteUser } from '../controllers/userController.js'; import { syncUser, getCurrentUser, getAllUsers, updateUserRole, toggleBanUser, heartbeat, getActiveUsers, deleteUser } from '../controllers/userController.js';
import { extractUser, requireAdmin } from '../middleware/roleMiddleware.js'; import { extractUser, requireAdmin } from '../middleware/authMiddleware.js';
const router = express.Router(); const router = express.Router();

View File

@@ -1,6 +1,6 @@
import { Router } from 'express'; import { Router } from 'express';
import * as yieldStudyController from '../controllers/yieldStudyController.js'; import * as yieldStudyController from '../controllers/yieldStudyController.js';
import { extractUser, requireAdmin } from '../middleware/roleMiddleware.js'; import { extractUser, requireAdmin } from '../middleware/authMiddleware.js';
const router = Router(); const router = Router();