Files
IT-Nexus/backend/src/services/entraSync.service.js

100 lines
4.1 KiB
JavaScript

const { getClientToken } = require('./graph.service');
const Database = require('better-sqlite3');
const path = require('path');
const fs = require('fs');
const DB_PATH = process.env.DATABASE_PATH || './database.sqlite';
async function syncEntraProfiles() {
const db = new Database(DB_PATH);
const startedAt = new Date().toISOString();
// Log starten
const logId = db.prepare(`
INSERT INTO cron_logs (job_name, started_at, status) VALUES (?, ?, 'running')
`).run('entra_profile_sync', startedAt).lastInsertRowid;
try {
const token = await getClientToken();
const users = db.prepare(`SELECT id, azure_id, username FROM users WHERE azure_id IS NOT NULL AND azure_id != ''`).all();
let synced = 0, errors = 0, photos = 0;
for (const user of users) {
try {
// Profildaten holen
const profileRes = await fetch(
`https://graph.microsoft.com/v1.0/users/${user.azure_id}?$select=jobTitle,department,mobilePhone,businessPhones,officeLocation,employeeHireDate,displayName,mail,givenName,surname`,
{ headers: { Authorization: `Bearer ${token}` } }
);
if (profileRes.ok) {
const p = await profileRes.json();
const phone = p.mobilePhone || (p.businessPhones?.[0]) || null;
db.prepare(`
UPDATE users SET
position = COALESCE(?, position),
department = COALESCE(?, department),
phone = COALESCE(?, phone),
location = COALESCE(?, location),
joined_date = COALESCE(?, joined_date),
first_name = COALESCE(?, first_name),
last_name = COALESCE(?, last_name)
WHERE id = ?
`).run(
p.jobTitle || null,
p.department || null,
phone,
p.officeLocation || null,
p.employeeHireDate ? p.employeeHireDate.split('T')[0] : null,
p.givenName || null,
p.surname || null,
user.id
);
synced++;
}
// Foto holen
const photoRes = await fetch(
`https://graph.microsoft.com/v1.0/users/${user.azure_id}/photo/$value`,
{ headers: { Authorization: `Bearer ${token}` } }
);
if (photoRes.ok) {
const avatarDir = path.join(process.env.UPLOAD_PATH || path.join(__dirname, '../../uploads'), 'avatars');
if (!fs.existsSync(avatarDir)) fs.mkdirSync(avatarDir, { recursive: true });
const buffer = Buffer.from(await photoRes.arrayBuffer());
const filename = `${user.azure_id}.jpg`;
fs.writeFileSync(path.join(avatarDir, filename), buffer);
db.prepare(`UPDATE users SET avatar_url = ? WHERE id = ?`)
.run(`/uploads/avatars/${filename}`, user.id);
photos++;
}
} catch (err) {
console.error(`[Entra Sync] Fehler bei User ${user.username}:`, err.message);
errors++;
}
}
const message = `${synced} Profile synchronisiert, ${photos} Fotos gespeichert, ${errors} Fehler`;
db.prepare(`UPDATE cron_logs SET status='success', finished_at=?, message=?, details=? WHERE id=?`)
.run(new Date().toISOString(), message, JSON.stringify({ synced, photos, errors, total: users.length }), logId);
console.log(`[Entra Sync] ${message}`);
return { synced, photos, errors };
} catch (err) {
db.prepare(`UPDATE cron_logs SET status='error', finished_at=?, message=? WHERE id=?`)
.run(new Date().toISOString(), err.message, logId);
throw err;
} finally {
db.close();
}
}
module.exports = { syncEntraProfiles };