123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122 |
- 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<string> => {
- 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<KeycloakUser[]> => {
- 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<KeycloakRole[]> => {
- 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<void> => {
- 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);
- }
- });
|