Files
minicrm/api2-groups.js
2026-02-07 22:28:36 +05:00

990 lines
41 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// api2-groups.js
// API для управления внешними идентификаторами пользователей
const express = require('express');
const router = express.Router();
module.exports = function(app, db) {
// Middleware для проверки аутентификации
const requireAuth = (req, res, next) => {
if (!req.session || !req.session.user) {
return res.status(401).json({ error: 'Требуется аутентификация' });
}
next();
};
// Middleware для проверки прав администратора
const requireAdmin = (req, res, next) => {
if (!req.session || !req.session.user || req.session.user.role !== 'admin') {
return res.status(403).json({ error: 'Недостаточно прав' });
}
next();
};
// 1. Создание таблиц при инициализации
function createIdTables() {
return new Promise((resolve, reject) => {
// Таблица с группами для идентификаторов
db.run(`
CREATE TABLE IF NOT EXISTS idgroups (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL UNIQUE,
description TEXT,
service_type TEXT NOT NULL, -- 'sberbank', 'yandex', 'ldap', 'other'
is_active BOOLEAN DEFAULT true,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
)
`, (err) => {
if (err) {
console.error('❌ Ошибка создания таблицы idgroups:', err.message);
reject(err);
return;
}
// Таблица с идентификаторами пользователей
db.run(`
CREATE TABLE IF NOT EXISTS idusers (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
service_type TEXT NOT NULL, -- 'sberbank', 'yandex', 'ldap', 'other'
external_id TEXT, -- СДЕЛАЛИ НЕОБЯЗАТЕЛЬНЫМ
login TEXT, -- Логин LDAP (если есть)
ldap_group TEXT, -- Группа LDAP (если есть)
group_id INTEGER, -- Ссылка на группу в idgroups
metadata TEXT, -- Дополнительные данные в JSON
is_active BOOLEAN DEFAULT true,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE,
FOREIGN KEY (group_id) REFERENCES idgroups (id) ON DELETE SET NULL,
UNIQUE(user_id, service_type, external_id) -- Теперь external_id может быть NULL
)
`, (err) => {
if (err) {
console.error('❌ Ошибка создания таблицы idusers:', err.message);
reject(err);
return;
}
// Создаем индексы
db.run('CREATE INDEX IF NOT EXISTS idx_idusers_user_id ON idusers(user_id)', (err) => {
if (err) console.error('❌ Ошибка создания индекса idx_idusers_user_id:', err.message);
});
db.run('CREATE INDEX IF NOT EXISTS idx_idusers_service_type ON idusers(service_type)', (err) => {
if (err) console.error('❌ Ошибка создания индекса idx_idusers_service_type:', err.message);
});
db.run('CREATE INDEX IF NOT EXISTS idx_idusers_external_id ON idusers(external_id)', (err) => {
if (err) console.error('❌ Ошибка создания индекса idx_idusers_external_id:', err.message);
});
db.run('CREATE INDEX IF NOT EXISTS idx_idgroups_service_type ON idgroups(service_type)', (err) => {
if (err) console.error('❌ Ошибка создания индекса idx_idgroups_service_type:', err.message);
});
console.log('✅ Таблицы для внешних идентификаторов созданы');
resolve();
});
});
});
}
// 2. Добавление стандартных групп
function addDefaultGroups() {
const defaultGroups = [
{
name: 'Сбербанк',
description: 'Идентификаторы в системе Сбербанка',
service_type: 'sberbank',
is_active: true
},
{
name: 'Яндекс',
description: 'Идентификаторы в системе Яндекса',
service_type: 'yandex',
is_active: true
},
{
name: 'LDAP - Преподаватели',
description: 'Пользователи из LDAP с ролью преподавателя',
service_type: 'ldap',
is_active: true
},
{
name: 'LDAP - Администрация',
description: 'Пользователи из LDAP в группе Администрация',
service_type: 'ldap',
is_active: true
},
{
name: 'LDAP - Руководство',
description: 'Руководители',
service_type: 'ldap',
is_active: true
},
{
name: 'Прочие системы',
description: 'Идентификаторы из других систем',
service_type: 'other',
is_active: true
}
];
defaultGroups.forEach(group => {
db.get("SELECT id FROM idgroups WHERE name = ?", [group.name], (err, existing) => {
if (err) {
console.error(`❌ Ошибка проверки группы ${group.name}:`, err.message);
return;
}
if (!existing) {
db.run(
`INSERT INTO idgroups (name, description, service_type, is_active)
VALUES (?, ?, ?, ?)`,
[group.name, group.description, group.service_type, group.is_active ? 1 : 0],
(insertErr) => {
if (insertErr) {
console.error(`❌ Ошибка создания группы ${group.name}:`, insertErr.message);
} else {
console.log(`✅ Группа "${group.name}" создана по умолчанию`);
}
}
);
}
});
});
}
// 3. API эндпоинты
// GET /api2/groups - Получить все группы (доступно всем аутентифицированным)
router.get('/api2/groups', requireAuth, (req, res) => {
const { service_type, is_active } = req.query;
let query = 'SELECT * FROM idgroups WHERE 1=1';
const params = [];
if (service_type) {
query += ' AND service_type = ?';
params.push(service_type);
}
if (is_active !== undefined) {
query += ' AND is_active = ?';
params.push(is_active === 'true' ? 1 : 0);
}
query += ' ORDER BY service_type, name';
db.all(query, params, (err, groups) => {
if (err) {
console.error('❌ Ошибка получения групп:', err);
return res.status(500).json({ error: 'Ошибка получения групп' });
}
res.json(groups || []);
});
});
// GET /api2/groups/:id - Получить группу по ID (доступно всем аутентифицированным)
router.get('/api2/groups/:id', requireAuth, (req, res) => {
const { id } = req.params;
db.get('SELECT * FROM idgroups WHERE id = ?', [id], (err, group) => {
if (err) {
console.error('❌ Ошибка получения группы:', err);
return res.status(500).json({ error: 'Ошибка получения группы' });
}
if (!group) {
return res.status(404).json({ error: 'Группа не найдена' });
}
res.json(group);
});
});
// POST /api2/groups - Создать новую группу (только админ)
router.post('/api2/groups', requireAuth, requireAdmin, (req, res) => {
const { name, description, service_type, is_active } = req.body;
if (!name || !service_type) {
return res.status(400).json({
error: 'Обязательные поля: name, service_type'
});
}
const validServiceTypes = ['sberbank', 'yandex', 'ldap', 'other'];
if (!validServiceTypes.includes(service_type)) {
return res.status(400).json({
error: `Недопустимый тип сервиса. Допустимые значения: ${validServiceTypes.join(', ')}`
});
}
db.run(
`INSERT INTO idgroups (name, description, service_type, is_active)
VALUES (?, ?, ?, ?)`,
[
name,
description || '',
service_type,
is_active !== undefined ? (is_active ? 1 : 0) : 1
],
function(err) {
if (err) {
if (err.code === 'SQLITE_CONSTRAINT') {
return res.status(409).json({ error: 'Группа с таким именем уже существует' });
}
console.error('❌ Ошибка создания группы:', err);
return res.status(500).json({ error: 'Ошибка создания группы' });
}
res.status(201).json({
success: true,
id: this.lastID,
message: 'Группа успешно создана'
});
}
);
});
// PUT /api2/groups/:id - Обновить группу (только админ)
router.put('/api2/groups/:id', requireAuth, requireAdmin, (req, res) => {
const { id } = req.params;
const { name, description, service_type, is_active } = req.body;
// Проверяем существование группы
db.get('SELECT id FROM idgroups WHERE id = ?', [id], (err, existing) => {
if (err) {
console.error('❌ Ошибка проверки группы:', err);
return res.status(500).json({ error: 'Ошибка проверки группы' });
}
if (!existing) {
return res.status(404).json({ error: 'Группа не найдена' });
}
// Собираем поля для обновления
const updates = [];
const params = [];
if (name !== undefined) {
updates.push('name = ?');
params.push(name);
}
if (description !== undefined) {
updates.push('description = ?');
params.push(description);
}
if (service_type !== undefined) {
const validServiceTypes = ['sberbank', 'yandex', 'ldap', 'other'];
if (!validServiceTypes.includes(service_type)) {
return res.status(400).json({
error: `Недопустимый тип сервиса. Допустимые значения: ${validServiceTypes.join(', ')}`
});
}
updates.push('service_type = ?');
params.push(service_type);
}
if (is_active !== undefined) {
updates.push('is_active = ?');
params.push(is_active ? 1 : 0);
}
if (updates.length === 0) {
return res.status(400).json({ error: 'Нет данных для обновления' });
}
updates.push('updated_at = CURRENT_TIMESTAMP');
params.push(id);
const query = `UPDATE idgroups SET ${updates.join(', ')} WHERE id = ?`;
db.run(query, params, function(err) {
if (err) {
if (err.code === 'SQLITE_CONSTRAINT') {
return res.status(409).json({ error: 'Группа с таким именем уже существует' });
}
console.error('❌ Ошибка обновления группы:', err);
return res.status(500).json({ error: 'Ошибка обновления группы' });
}
res.json({
success: true,
changes: this.changes,
message: 'Группа успешно обновлена'
});
});
});
});
// DELETE /api2/groups/:id - Удалить группу (только админ)
router.delete('/api2/groups/:id', requireAuth, requireAdmin, (req, res) => {
const { id } = req.params;
// Проверяем, используется ли группа
db.get('SELECT COUNT(*) as count FROM idusers WHERE group_id = ?', [id], (err, result) => {
if (err) {
console.error('❌ Ошибка проверки использования группы:', err);
return res.status(500).json({ error: 'Ошибка проверки группы' });
}
if (result.count > 0) {
return res.status(400).json({
error: 'Невозможно удалить группу, так как она используется в идентификаторах пользователей',
used_count: result.count
});
}
db.run('DELETE FROM idgroups WHERE id = ?', [id], function(err) {
if (err) {
console.error('❌ Ошибка удаления группы:', err);
return res.status(500).json({ error: 'Ошибка удаления группы' });
}
res.json({
success: true,
changes: this.changes,
message: 'Группа успешно удалена'
});
});
});
});
// GET /api2/idusers - Получить все идентификаторы пользователей (доступно всем аутентифицированным)
router.get('/api2/idusers', requireAuth, (req, res) => {
const { user_id, service_type, external_id, group_id, is_active } = req.query;
let query = `
SELECT iu.*,
u.name as user_name,
u.login as user_login,
u.email as user_email,
g.name as group_name
FROM idusers iu
LEFT JOIN users u ON iu.user_id = u.id
LEFT JOIN idgroups g ON iu.group_id = g.id
WHERE 1=1
`;
const params = [];
if (user_id) {
query += ' AND iu.user_id = ?';
params.push(user_id);
}
if (service_type) {
query += ' AND iu.service_type = ?';
params.push(service_type);
}
if (external_id) {
query += ' AND iu.external_id LIKE ?';
params.push(`%${external_id}%`);
}
if (group_id) {
query += ' AND iu.group_id = ?';
params.push(group_id);
}
if (is_active !== undefined) {
query += ' AND iu.is_active = ?';
params.push(is_active === 'true' ? 1 : 0);
}
query += ' ORDER BY iu.service_type, iu.external_id';
db.all(query, params, (err, idusers) => {
if (err) {
console.error('❌ Ошибка получения идентификаторов пользователей:', err);
return res.status(500).json({ error: 'Ошибка получения данных' });
}
// Парсим JSON metadata если есть
const result = (idusers || []).map(item => {
if (item.metadata) {
try {
item.metadata = JSON.parse(item.metadata);
} catch (e) {
item.metadata = {};
}
} else {
item.metadata = {};
}
return item;
});
res.json(result);
});
});
// GET /api2/idusers/:id - Получить идентификатор по ID (доступно всем аутентифицированным)
router.get('/api2/idusers/:id', requireAuth, (req, res) => {
const { id } = req.params;
db.get(`
SELECT iu.*,
u.name as user_name,
u.login as user_login,
u.email as user_email,
g.name as group_name
FROM idusers iu
LEFT JOIN users u ON iu.user_id = u.id
LEFT JOIN idgroups g ON iu.group_id = g.id
WHERE iu.id = ?
`, [id], (err, iduser) => {
if (err) {
console.error('❌ Ошибка получения идентификатора:', err);
return res.status(500).json({ error: 'Ошибка получения данных' });
}
if (!iduser) {
return res.status(404).json({ error: 'Идентификатор не найден' });
}
// Парсим JSON metadata если есть
if (iduser.metadata) {
try {
iduser.metadata = JSON.parse(iduser.metadata);
} catch (e) {
iduser.metadata = {};
}
} else {
iduser.metadata = {};
}
res.json(iduser);
});
});
// GET /api2/idusers/user/:userId - Получить идентификаторы конкретного пользователя (доступно всем аутентифицированным)
router.get('/api2/idusers/user/:userId', requireAuth, (req, res) => {
const { userId } = req.params;
const { service_type } = req.query;
let query = `
SELECT iu.*, g.name as group_name
FROM idusers iu
LEFT JOIN idgroups g ON iu.group_id = g.id
WHERE iu.user_id = ?
`;
const params = [userId];
if (service_type) {
query += ' AND iu.service_type = ?';
params.push(service_type);
}
query += ' ORDER BY iu.service_type, iu.external_id';
db.all(query, params, (err, idusers) => {
if (err) {
console.error('❌ Ошибка получения идентификаторов пользователя:', err);
return res.status(500).json({ error: 'Ошибка получения данных' });
}
// Парсим JSON metadata если есть
const result = (idusers || []).map(item => {
if (item.metadata) {
try {
item.metadata = JSON.parse(item.metadata);
} catch (e) {
item.metadata = {};
}
} else {
item.metadata = {};
}
return item;
});
res.json(result);
});
});
// POST /api2/idusers - Создать новый идентификатор пользователя (только админ)
router.post('/api2/idusers', requireAuth, requireAdmin, (req, res) => {
const {
user_id,
service_type,
external_id,
login,
ldap_group,
group_id,
metadata,
is_active
} = req.body;
// Валидация обязательных полей - СДЕЛАЕМ external_id НЕОБЯЗАТЕЛЬНЫМ
if (!user_id || !service_type) {
return res.status(400).json({
error: 'Обязательные поля: user_id, service_type'
});
}
const validServiceTypes = ['sberbank', 'yandex', 'ldap', 'other'];
if (!validServiceTypes.includes(service_type)) {
return res.status(400).json({
error: `Недопустимый тип сервиса. Допустимые значения: ${validServiceTypes.join(', ')}`
});
}
// Проверяем существование пользователя
db.get('SELECT id FROM users WHERE id = ?', [user_id], (err, user) => {
if (err) {
console.error('❌ Ошибка проверки пользователя:', err);
return res.status(500).json({ error: 'Ошибка проверки пользователя' });
}
if (!user) {
return res.status(404).json({ error: 'Пользователь не найден' });
}
// Если указана группа, проверяем ее существование
if (group_id) {
db.get('SELECT id, service_type FROM idgroups WHERE id = ?', [group_id], (err, group) => {
if (err) {
console.error('❌ Ошибка проверки группы:', err);
return res.status(500).json({ error: 'Ошибка проверки группы' });
}
if (!group) {
return res.status(404).json({ error: 'Указанная группа не найдена' });
}
// УБИРАЕМ проверку соответствия типа сервиса
// Теперь группы независимы от сервиса
createIdUser();
});
} else {
// Создаем идентификатор без группы
createIdUser();
}
function createIdUser() {
const metadataJson = metadata ? JSON.stringify(metadata) : null;
// Генерируем уникальный external_id если он не предоставлен
const finalExternalId = external_id || `temp_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
db.run(
`INSERT INTO idusers
(user_id, service_type, external_id, login, ldap_group, group_id, metadata, is_active)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
[
user_id,
service_type,
finalExternalId,
login || null,
ldap_group || null,
group_id || null,
metadataJson,
is_active !== undefined ? (is_active ? 1 : 0) : 1
],
function(err) {
if (err) {
if (err.code === 'SQLITE_CONSTRAINT') {
return res.status(409).json({
error: 'Идентификатор с такими параметрами уже существует для этого пользователя'
});
}
console.error('❌ Ошибка создания идентификатора:', err);
return res.status(500).json({ error: 'Ошибка создания идентификатора' });
}
res.status(201).json({
success: true,
id: this.lastID,
message: 'Идентификатор успешно создан'
});
}
);
}
});
});
// PUT /api2/idusers/:id - Обновить идентификатор пользователя (только админ)
router.put('/api2/idusers/:id', requireAuth, requireAdmin, (req, res) => {
const { id } = req.params;
const {
user_id,
service_type,
external_id,
login,
ldap_group,
group_id,
metadata,
is_active
} = req.body;
// Проверяем существование идентификатора
db.get('SELECT * FROM idusers WHERE id = ?', [id], (err, existing) => {
if (err) {
console.error('❌ Ошибка проверки идентификатора:', err);
return res.status(500).json({ error: 'Ошибка проверки идентификатора' });
}
if (!existing) {
return res.status(404).json({ error: 'Идентификатор не найден' });
}
// Если указана группа и она меняется, проверяем существование новой группы
if (group_id !== undefined && group_id !== existing.group_id) {
if (group_id) {
db.get('SELECT id FROM idgroups WHERE id = ?', [group_id], (err, group) => {
if (err) {
console.error('❌ Ошибка проверки группы:', err);
return res.status(500).json({ error: 'Ошибка проверки группы' });
}
if (!group) {
return res.status(404).json({ error: 'Указанная группа не найдена' });
}
// УБИРАЕМ проверку соответствия типа сервиса
// Теперь группы независимы от сервиса
updateIdUser();
});
} else {
updateIdUser();
}
} else {
updateIdUser();
}
function updateIdUser() {
// Собираем поля для обновления
const updates = [];
const params = [];
if (user_id !== undefined) {
updates.push('user_id = ?');
params.push(user_id);
}
if (service_type !== undefined) {
const validServiceTypes = ['sberbank', 'yandex', 'ldap', 'other'];
if (!validServiceTypes.includes(service_type)) {
return res.status(400).json({
error: `Недопустимый тип сервиса. Допустимые значения: ${validServiceTypes.join(', ')}`
});
}
updates.push('service_type = ?');
params.push(service_type);
}
if (external_id !== undefined) {
updates.push('external_id = ?');
// Генерируем временный external_id если он пустой
const finalExternalId = external_id || `temp_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
params.push(finalExternalId);
}
if (login !== undefined) {
updates.push('login = ?');
params.push(login || null);
}
if (ldap_group !== undefined) {
updates.push('ldap_group = ?');
params.push(ldap_group || null);
}
if (group_id !== undefined) {
updates.push('group_id = ?');
params.push(group_id || null);
}
if (metadata !== undefined) {
updates.push('metadata = ?');
params.push(metadata ? JSON.stringify(metadata) : null);
}
if (is_active !== undefined) {
updates.push('is_active = ?');
params.push(is_active ? 1 : 0);
}
if (updates.length === 0) {
return res.status(400).json({ error: 'Нет данных для обновления' });
}
updates.push('updated_at = CURRENT_TIMESTAMP');
params.push(id);
const query = `UPDATE idusers SET ${updates.join(', ')} WHERE id = ?`;
db.run(query, params, function(err) {
if (err) {
if (err.code === 'SQLITE_CONSTRAINT') {
return res.status(409).json({
error: 'Идентификатор с такими параметрами уже существует'
});
}
console.error('❌ Ошибка обновления идентификатора:', err);
return res.status(500).json({ error: 'Ошибка обновления идентификатора' });
}
res.json({
success: true,
changes: this.changes,
message: 'Идентификатор успешно обновлен'
});
});
}
});
});
// DELETE /api2/idusers/:id - Удалить идентификатор пользователя (только админ)
router.delete('/api2/idusers/:id', requireAuth, requireAdmin, (req, res) => {
const { id } = req.params;
db.run('DELETE FROM idusers WHERE id = ?', [id], function(err) {
if (err) {
console.error('❌ Ошибка удаления идентификатора:', err);
return res.status(500).json({ error: 'Ошибка удаления идентификатора' });
}
res.json({
success: true,
changes: this.changes,
message: 'Идентификатор успешно удален'
});
});
});
// GET /api2/idusers/search - Поиск идентификаторов (доступно всем аутентифицированным)
router.get('/api2/idusers/search', requireAuth, (req, res) => {
const { q, service_type } = req.query;
if (!q || q.length < 2) {
return res.status(400).json({
error: 'Строка поиска должна содержать минимум 2 символа'
});
}
let query = `
SELECT iu.*,
u.name as user_name,
u.login as user_login,
u.email as user_email,
g.name as group_name
FROM idusers iu
LEFT JOIN users u ON iu.user_id = u.id
LEFT JOIN idgroups g ON iu.group_id = g.id
WHERE (iu.external_id LIKE ? OR iu.login LIKE ? OR u.name LIKE ? OR u.login LIKE ?)
`;
const params = [`%${q}%`, `%${q}%`, `%${q}%`, `%${q}%`];
if (service_type) {
query += ' AND iu.service_type = ?';
params.push(service_type);
}
query += ' ORDER BY iu.service_type, iu.external_id LIMIT 50';
db.all(query, params, (err, idusers) => {
if (err) {
console.error('❌ Ошибка поиска идентификаторов:', err);
return res.status(500).json({ error: 'Ошибка поиска' });
}
// Парсим JSON metadata если есть
const result = (idusers || []).map(item => {
if (item.metadata) {
try {
item.metadata = JSON.parse(item.metadata);
} catch (e) {
item.metadata = {};
}
} else {
item.metadata = {};
}
return item;
});
res.json(result);
});
});
// GET /api2/idusers/stats - Статистика по идентификаторам (доступно всем аутентифицированным)
router.get('/api2/idusers/stats', requireAuth, (req, res) => {
// Получаем статистику по типам сервисов
const query = `
SELECT
service_type,
COUNT(*) as total_count,
COUNT(CASE WHEN is_active = 1 THEN 1 END) as active_count,
COUNT(DISTINCT user_id) as unique_users
FROM idusers
GROUP BY service_type
ORDER BY total_count DESC
`;
db.all(query, [], (err, stats) => {
if (err) {
console.error('❌ Ошибка получения статистики:', err);
return res.status(500).json({ error: 'Ошибка получения статистики' });
}
// Получаем общую статистику
db.get(`
SELECT
COUNT(*) as total_identifiers,
COUNT(DISTINCT user_id) as total_users,
COUNT(CASE WHEN is_active = 1 THEN 1 END) as total_active
FROM idusers
`, [], (err, totalStats) => {
if (err) {
console.error('❌ Ошибка получения общей статистики:', err);
return res.status(500).json({ error: 'Ошибка получения статистики' });
}
res.json({
by_service_type: stats || [],
totals: totalStats || {},
timestamp: new Date().toISOString()
});
});
});
});
// 4. Инициализация при подключении модуля
const init = async () => {
try {
await createIdTables();
addDefaultGroups();
console.log('✅ API для внешних идентификаторов инициализирован');
} catch (error) {
console.error('❌ Ошибка инициализации API для внешних идентификаторов:', error.message);
}
};
// Запускаем инициализацию
init();
// Подключаем роутер к приложению
app.use(router);
// Экспортируем функции для использования в других модулях
return {
// Функция для получения внешнего идентификатора пользователя
getUserIdByExternalId: function(service_type, external_id, callback) {
db.get(
'SELECT user_id FROM idusers WHERE service_type = ? AND external_id = ? AND is_active = 1',
[service_type, external_id],
(err, result) => {
if (err) {
console.error('❌ Ошибка получения идентификатора пользователя:', err);
return callback(err, null);
}
callback(null, result ? result.user_id : null);
}
);
},
// Функция для получения внешних идентификаторов пользователя
getUserExternalIds: function(user_id, service_type, callback) {
let query = 'SELECT * FROM idusers WHERE user_id = ? AND is_active = 1';
const params = [user_id];
if (service_type) {
query += ' AND service_type = ?';
params.push(service_type);
}
db.all(query, params, (err, results) => {
if (err) {
console.error('❌ Ошибка получения внешних идентификаторов:', err);
return callback(err, null);
}
// Парсим JSON metadata если есть
const formattedResults = (results || []).map(item => {
if (item.metadata) {
try {
item.metadata = JSON.parse(item.metadata);
} catch (e) {
item.metadata = {};
}
} else {
item.metadata = {};
}
return item;
});
callback(null, formattedResults);
});
},
// Функция для добавления или обновления идентификатора
upsertUserId: function(user_id, service_type, external_id, data, callback) {
const { login, ldap_group, group_id, metadata, is_active } = data || {};
db.get(
'SELECT id FROM idusers WHERE user_id = ? AND service_type = ? AND external_id = ?',
[user_id, service_type, external_id],
(err, existing) => {
if (err) {
return callback(err);
}
const metadataJson = metadata ? JSON.stringify(metadata) : null;
if (existing) {
// Обновляем существующий
db.run(
`UPDATE idusers SET
login = COALESCE(?, login),
ldap_group = COALESCE(?, ldap_group),
group_id = COALESCE(?, group_id),
metadata = COALESCE(?, metadata),
is_active = COALESCE(?, is_active),
updated_at = CURRENT_TIMESTAMP
WHERE id = ?`,
[
login || null,
ldap_group || null,
group_id || null,
metadataJson,
is_active !== undefined ? (is_active ? 1 : 0) : 1,
existing.id
],
function(err) {
callback(err, { updated: true, id: existing.id });
}
);
} else {
// Создаем новый
db.run(
`INSERT INTO idusers
(user_id, service_type, external_id, login, ldap_group, group_id, metadata, is_active)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
[
user_id,
service_type,
external_id,
login || null,
ldap_group || null,
group_id || null,
metadataJson,
is_active !== undefined ? (is_active ? 1 : 0) : 1
],
function(err) {
callback(err, { created: true, id: this.lastID });
}
);
}
}
);
}
};
};