import { db, pool } from "../db/index"; import { users, applications, userApplications, profiles, crmPartners, tenants, tenantUsers, type User, type InsertUser, type Application, type InsertApplication } from "@shared/schema"; import { eq, and } from "drizzle-orm"; import session from "express-session"; import connectPg from "connect-pg-simple"; const PostgresSessionStore = connectPg(session); export interface IStorage { sessionStore: session.Store; getUser(id: string): Promise; getUserByUsername(username: string, tenantId?: number): Promise; createUser(user: InsertUser): Promise; getEnrichedUser(user: User): Promise; getApplications(): Promise; getApplication(id: string): Promise; createApplication(app: InsertApplication): Promise; updateApplication(id: string, app: Partial): Promise; deleteApplication(id: string): Promise; getUserApplications(userId: string): Promise; assignApplicationToUser(userId: string, applicationId: string): Promise; removeApplicationFromUser(userId: string, applicationId: string): Promise; getTenant(id: number): Promise<{ id: number; name: string; slug: string; tenantType?: string; plan?: string; status?: string } | undefined>; getTenants(): Promise<{ id: number; name: string; slug: string }[]>; } export class DatabaseStorage implements IStorage { sessionStore: session.Store; constructor() { console.log('[Storage] Inicializando session store...'); this.sessionStore = new PostgresSessionStore({ pool, createTableIfMissing: true, tableName: 'session', pruneSessionInterval: 60 * 60, // 1 hora }); // Log de erros do session store this.sessionStore.on('error', (err: any) => { console.error('[SessionStore] Erro:', err.message); }); console.log('[Storage] Session store inicializado'); } async getUser(id: string): Promise { console.log(`[Storage] Buscando usuário por ID: ${id}`); const [user] = await db.select().from(users).where(eq(users.id, id)); console.log(`[Storage] Usuário ${user ? 'encontrado' : 'não encontrado'}: ${id}`); return user; } // CORREÇÃO: getUserByUsername com validação de tenant async getUserByUsername(username: string, tenantId?: number): Promise { console.log(`[Storage] Buscando usuário por username: ${username}`); const [user] = await db.select().from(users).where(eq(users.username, username)); console.log(`[Storage] Usuário ${user ? 'encontrado' : 'não encontrado'}: ${username}`); // Se tenantId foi passado, validar que usuário pertence a esse tenant if (tenantId && user) { console.log(`[Storage] Validando tenant ${tenantId} para usuário: ${username}`); const [tenantUser] = await db.select() .from(tenantUsers) .where(and( eq(tenantUsers.userId, user.id), eq(tenantUsers.tenantId, tenantId) )); if (!tenantUser) { console.log(`[Storage] Usuário ${username} não pertence ao tenant ${tenantId}`); return undefined; } } return user; } async createUser(insertUser: InsertUser): Promise { const [user] = await db.insert(users).values(insertUser).returning(); return user; } async getApplications(): Promise { return await db.select().from(applications); } async getApplication(id: string): Promise { const [app] = await db.select().from(applications).where(eq(applications.id, id)); return app; } async createApplication(app: InsertApplication): Promise { const [newApp] = await db.insert(applications).values(app).returning(); return newApp; } async updateApplication(id: string, app: Partial): Promise { const [updated] = await db.update(applications) .set(app) .where(eq(applications.id, id)) .returning(); return updated; } async deleteApplication(id: string): Promise { const result = await db.delete(applications).where(eq(applications.id, id)); return result.rowCount ? result.rowCount > 0 : false; } async getUserApplications(userId: string): Promise { const result = await db .select({ application: applications }) .from(userApplications) .innerJoin(applications, eq(userApplications.applicationId, applications.id)) .where(eq(userApplications.userId, userId)); return result.map(r => r.application); } async assignApplicationToUser(userId: string, applicationId: string): Promise { await db.insert(userApplications).values({ userId, applicationId }).onConflictDoNothing(); } async removeApplicationFromUser(userId: string, applicationId: string): Promise { await db.delete(userApplications).where( and( eq(userApplications.userId, userId), eq(userApplications.applicationId, applicationId) ) ); } async getEnrichedUser(user: User): Promise { console.log(`[Storage] Iniciando enriquecimento do usuário: ${user.id}`); const enriched: any = { ...user }; if (user.profileId) { console.log(`[Storage] Buscando profile: ${user.profileId}`); const [profile] = await db.select().from(profiles).where(eq(profiles.id, user.profileId)); if (profile) { enriched.profile = profile; enriched.allowedModules = profile.allowedModules || []; console.log(`[Storage] Profile encontrado: ${profile.name}`); } } if (user.partnerId) { console.log(`[Storage] Buscando partner: ${user.partnerId}`); const [partner] = await db.select().from(crmPartners).where(eq(crmPartners.id, user.partnerId)); if (partner) { enriched.partner = partner; console.log(`[Storage] Partner encontrado: ${partner.name}`); } } // Buscar tenant do usuário (apenas campos seguros para o cliente) console.log(`[Storage] Buscando tenantUser para: ${user.id}`); const [tenantUser] = await db.select().from(tenantUsers).where(eq(tenantUsers.userId, user.id)); if (tenantUser) { console.log(`[Storage] TenantUser encontrado, buscando tenant: ${tenantUser.tenantId}`); const [tenant] = await db.select().from(tenants).where(eq(tenants.id, tenantUser.tenantId)); if (tenant) { // Expor apenas campos seguros do tenant enriched.tenant = { id: tenant.id, name: tenant.name, tenantType: tenant.tenantType, plan: tenant.plan, status: tenant.status }; enriched.tenantId = tenant.id; enriched.tenantType = tenant.tenantType; enriched.tenantRole = tenantUser.role; enriched.isOwner = tenantUser.isOwner === "true"; console.log(`[Storage] Tenant enriquecido: ${tenant.name}`); } } else { console.log(`[Storage] Usuário sem tenant associado: ${user.id}`); } console.log(`[Storage] Enriquecimento completo: ${user.id}`); return enriched; } // CORREÇÃO: getTenant para buscar tenant específico async getTenant(id: number): Promise<{ id: number; name: string; slug: string; tenantType?: string; plan?: string; status?: string } | undefined> { const [tenant] = await db.select({ id: tenants.id, name: tenants.name, slug: tenants.slug, tenantType: tenants.tenantType, plan: tenants.plan, status: tenants.status }).from(tenants).where(eq(tenants.id, id)); return tenant; } async getTenants(): Promise<{ id: number; name: string; slug: string }[]> { const result = await db.select({ id: tenants.id, name: tenants.name, slug: tenants.slug }).from(tenants); return result.map(t => ({ id: t.id, name: t.name || '', slug: t.slug || '' })); } } export const storage = new DatabaseStorage();