import cron from 'node-cron'; import axios from 'axios'; import dayjs from 'dayjs'; import prisma from '../prisma/PrismaClient'; import { URLSearchParams } from 'url'; interface KeycloakUser { id: string; firstName?: string; lastName?: string; createdTimestamp?: number; attributes?: { phone?: string[]; }; } interface KeycloakRole { id: string; name: string; description?: string; composite?: boolean; clientRole?: boolean; containerId?: string; } const getAdminToken = async (): Promise => { const response = await axios.post( `${process.env.KEYCLOAK_URL}/realms/master/protocol/openid-connect/token`, new URLSearchParams({ client_id: 'admin-cli', username: process.env.KEYCLOAK_ADMIN_USERNAME ?? '', password: process.env.KEYCLOAK_ADMIN_PASSWORD ?? '', grant_type: 'password', }), { headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, } ); return response.data.access_token; }; const getAllUsers = async (token: string): Promise => { const res = await axios.get( `${process.env.KEYCLOAK_URL}/admin/realms/${process.env.KEYCLOAK_REALM}/users`, { headers: { Authorization: `Bearer ${token}`, }, } ); return res.data; }; const getUserRoles = async (token: string, userId: string): Promise => { const res = await axios.get( `${process.env.KEYCLOAK_URL}/admin/realms/${process.env.KEYCLOAK_REALM}/users/${userId}/role-mappings/realm`, { headers: { Authorization: `Bearer ${token}`, }, } ); return res.data; }; const syncUserToDB = async (user: KeycloakUser, token: string): Promise => { const fullname = `${user.firstName || ''} ${user.lastName || ''}`.trim(); const phone = user.attributes?.phone?.[0] || null; const roles = await getUserRoles(token, user.id); const role = roles[0]?.name || null; const existing = await prisma.userKeycloak.findUnique({ where: { id: user.id }, }); if (!existing) { await prisma.userKeycloak.create({ data: { id: user.id, fullname, phone, role }, }); console.log('✔️ Synced user:', fullname); } }; // Cron job setiap 1 menit cron.schedule('* * * * *', async () => { console.log('[SYNC] Checking for new users...'); try { const token = await getAdminToken(); const users = await getAllUsers(token); const now = dayjs(); const oneMinuteAgo = now.subtract(1, 'minute'); const newUsers = users.filter( (u) => u.createdTimestamp && dayjs(Number(u.createdTimestamp)).isAfter(oneMinuteAgo) ); for (const user of newUsers) { await syncUserToDB(user, token); } if (newUsers.length === 0) { console.log('ℹ️ No new users.'); } } catch (err: any) { console.error('❌ Sync error:', err.message); } });