Files
GPI/src/server/services/userService.ts
T

146 lines
5.6 KiB
TypeScript

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;
};