import { query } from '../config/database.js'; export const syncUser = async (data: { externalId: string, email: string, name: string, organizationId?: string, clerkRole?: string }) => { const { externalId, email, name, organizationId, clerkRole } = data; let result = await query( `INSERT INTO gpi.users (clerk_id, email, name, role, updated_at) VALUES ($1, $2, $3, 'guest', NOW()) ON CONFLICT (clerk_id) DO UPDATE SET email = EXCLUDED.email, name = EXCLUDED.name, updated_at = NOW() RETURNING *`, [externalId, email, name] ); let user = result.rows[0]; if (organizationId) { let appRole = 'guest'; if (clerkRole === 'org:admin') appRole = 'admin'; else if (clerkRole === 'org:member') appRole = 'user'; const orgResult = await query('SELECT id FROM gpi.organizations WHERE clerk_id = $1 OR logto_id = $1', [organizationId]); if (orgResult.rows.length > 0) { const orgUuid = orgResult.rows[0].id; await query( `INSERT INTO gpi.user_organizations (user_id, organization_id, role, updated_at) VALUES ($1, $2, $3, NOW()) ON CONFLICT (user_id, organization_id) DO NOTHING`, [user.id, orgUuid, appRole] ); const memberResult = await query( 'SELECT role, is_banned FROM gpi.user_organizations WHERE user_id = $1 AND organization_id = $2', [user.id, orgUuid] ); return { ...user, organizationRole: memberResult.rows[0].role, organizationBanned: memberResult.rows[0].is_banned }; } } return user; }; export const getCurrentUser = async (externalId: string, organizationId?: string) => { const userResult = await query('SELECT * FROM gpi.users WHERE clerk_id = $1 OR logto_id = $1', [externalId]); if (userResult.rows.length === 0) return null; const user = userResult.rows[0]; if (organizationId) { const orgResult = await query('SELECT id FROM gpi.organizations WHERE clerk_id = $1 OR logto_id = $1', [organizationId]); if (orgResult.rows.length > 0) { const orgUuid = orgResult.rows[0].id; const memberResult = await query( 'SELECT role, is_banned FROM gpi.user_organizations WHERE user_id = $1 AND organization_id = $2', [user.id, orgUuid] ); if (memberResult.rows.length > 0) { return { ...user, role: memberResult.rows[0].role, isBanned: memberResult.rows[0].is_banned, organizationId }; } } } return user; }; export const getAllUsersInOrg = async (organizationId: string) => { const orgResult = await query('SELECT id FROM gpi.organizations WHERE clerk_id = $1 OR logto_id = $1', [organizationId]); if (orgResult.rows.length === 0) return []; const orgUuid = orgResult.rows[0].id; const results = await query( `SELECT u.*, uo.role as organization_role, uo.is_banned as organization_banned, uo.id as member_id FROM gpi.users u JOIN gpi.user_organizations uo ON u.id = uo.user_id WHERE uo.organization_id = $1 ORDER BY uo.created_at DESC`, [orgUuid] ); return results.rows; }; export const updateUserRole = async (userIdOrMemberId: string, organizationId: string, role: string) => { const orgResult = await query('SELECT id FROM gpi.organizations WHERE clerk_id = $1 OR logto_id = $1', [organizationId]); if (orgResult.rows.length === 0) return null; const orgUuid = orgResult.rows[0].id; // Try update by member_id or user_id const result = await query( 'UPDATE gpi.user_organizations SET role = $1, updated_at = NOW() WHERE (id = $2 OR user_id = $2) AND organization_id = $3 RETURNING *', [role, userIdOrMemberId, orgUuid] ); return result.rows[0]; }; export const toggleBanUser = async (userIdOrMemberId: string, organizationId: string, isBanned: boolean) => { const orgResult = await query('SELECT id FROM gpi.organizations WHERE clerk_id = $1 OR logto_id = $1', [organizationId]); if (orgResult.rows.length === 0) return null; const orgUuid = orgResult.rows[0].id; const result = await query( 'UPDATE gpi.user_organizations SET is_banned = $1, updated_at = NOW() WHERE (id = $2 OR user_id = $2) AND organization_id = $3 RETURNING *', [isBanned, userIdOrMemberId, orgUuid] ); return result.rows[0]; }; export const heartbeat = async (userId: string) => { await query('UPDATE gpi.users SET last_seen_at = NOW() WHERE id = $1', [userId]); }; export const getActiveUsers = async (organizationId: string, excludeUserId?: string) => { const orgResult = await query('SELECT id FROM gpi.organizations WHERE clerk_id = $1 OR logto_id = $1', [organizationId]); if (orgResult.rows.length === 0) return []; const orgUuid = orgResult.rows[0].id; const twoMinutesAgo = new Date(Date.now() - 2 * 60 * 1000); const result = await query( `SELECT u.id, u.name, u.email, u.last_seen_at, u.clerk_id as "externalId" FROM gpi.users u JOIN gpi.user_organizations uo ON u.id = uo.user_id WHERE uo.organization_id = $1 AND u.last_seen_at >= $2 AND u.id != $3`, [orgUuid, twoMinutesAgo, excludeUserId || '00000000-0000-0000-0000-000000000000'] ); return result.rows; }; export const deleteUserFromOrg = async (userIdOrMemberId: string, organizationId: string) => { const orgResult = await query('SELECT id FROM gpi.organizations WHERE clerk_id = $1 OR logto_id = $1', [organizationId]); if (orgResult.rows.length === 0) return false; const orgUuid = orgResult.rows[0].id; const result = await query( 'DELETE FROM gpi.user_organizations WHERE (id = $1 OR user_id = $1) AND organization_id = $2', [userIdOrMemberId, orgUuid] ); return result.rowCount > 0; };