crm
This commit is contained in:
@@ -108,6 +108,8 @@ router.put('/settings', requireAuth, (req, res) => {
|
||||
if (typeof body.logonScript === 'string') patch.logonScript = body.logonScript;
|
||||
if (typeof body.homeDrive === 'string') patch.homeDrive = body.homeDrive;
|
||||
if (typeof body.homeBasePath === 'string') patch.homeBasePath = body.homeBasePath;
|
||||
if (typeof body.crmUrl === 'string') patch.crmUrl = body.crmUrl;
|
||||
if (typeof body.quickLinksUrl === 'string') patch.quickLinksUrl = body.quickLinksUrl;
|
||||
if (Object.keys(patch).length === 0) {
|
||||
return res.status(400).json({ success: false, error: 'Нет данных для сохранения' });
|
||||
}
|
||||
@@ -273,7 +275,11 @@ router.post('/requests/:id/approve', requireAuth, async (req, res) => {
|
||||
groupCNs
|
||||
});
|
||||
try {
|
||||
await mailer.sendApproved(record.email, { login: record.login, password: record.password, fio: record.fio, position: record.position });
|
||||
await mailer.sendApproved(record.email, {
|
||||
login: record.login, password: record.password, fio: record.fio, position: record.position,
|
||||
crmUrl: settings.crmUrl, quickLinksUrl: settings.quickLinksUrl,
|
||||
netbios: config.domain.split('.')[0].toLowerCase()
|
||||
});
|
||||
requests.markProcessed(record.id, { phase: 'approved', emailSent: true });
|
||||
} catch (mailErr) {
|
||||
requests.markProcessed(record.id, { phase: 'approved', emailSent: false });
|
||||
@@ -322,7 +328,12 @@ router.post('/requests/:id/resend', requireAuth, async (req, res) => {
|
||||
await mailer.sendPasswordChangeDenied(record.email, { fio: record.fio, reason: record.reason });
|
||||
}
|
||||
} else if (record.phase === 'approved') {
|
||||
await mailer.sendApproved(record.email, { login: record.login, password: record.password, fio: record.fio, position: record.position });
|
||||
const s = adminSettings.getSettings();
|
||||
await mailer.sendApproved(record.email, {
|
||||
login: record.login, password: record.password, fio: record.fio, position: record.position,
|
||||
crmUrl: s.crmUrl, quickLinksUrl: s.quickLinksUrl,
|
||||
netbios: config.domain.split('.')[0].toLowerCase()
|
||||
});
|
||||
} else {
|
||||
await mailer.sendDenied(record.email, { fio: record.fio, reason: record.reason });
|
||||
}
|
||||
|
||||
@@ -19,12 +19,15 @@ function getSettings() {
|
||||
apiKey: typeof parsed.apiKey === 'string' ? parsed.apiKey : '',
|
||||
logonScript: typeof parsed.logonScript === 'string' ? parsed.logonScript : 'PST.bat',
|
||||
homeDrive: typeof parsed.homeDrive === 'string' ? parsed.homeDrive : 'Z:',
|
||||
homeBasePath: typeof parsed.homeBasePath === 'string' ? parsed.homeBasePath : '\\\\dc\\Teachers'
|
||||
homeBasePath: typeof parsed.homeBasePath === 'string' ? parsed.homeBasePath : '\\\\dc\\Teachers',
|
||||
crmUrl: typeof parsed.crmUrl === 'string' ? parsed.crmUrl : '',
|
||||
quickLinksUrl: typeof parsed.quickLinksUrl === 'string' ? parsed.quickLinksUrl : ''
|
||||
};
|
||||
} catch (err) {
|
||||
return {
|
||||
ouDn: '', defaultGroups: [], phoneRequired: false, authEnabled: true, apiKey: '',
|
||||
logonScript: 'PST.bat', homeDrive: 'Z:', homeBasePath: '\\\\dc\\Teachers'
|
||||
logonScript: 'PST.bat', homeDrive: 'Z:', homeBasePath: '\\\\dc\\Teachers',
|
||||
crmUrl: '', quickLinksUrl: ''
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -42,6 +45,8 @@ function updateSettings(patch) {
|
||||
if (patch && typeof patch.logonScript === 'string') next.logonScript = patch.logonScript;
|
||||
if (patch && typeof patch.homeDrive === 'string') next.homeDrive = patch.homeDrive;
|
||||
if (patch && typeof patch.homeBasePath === 'string') next.homeBasePath = patch.homeBasePath;
|
||||
if (patch && typeof patch.crmUrl === 'string') next.crmUrl = patch.crmUrl;
|
||||
if (patch && typeof patch.quickLinksUrl === 'string') next.quickLinksUrl = patch.quickLinksUrl;
|
||||
fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
|
||||
fs.writeFileSync(settingsPath, JSON.stringify(next, null, 2), 'utf8');
|
||||
return next;
|
||||
|
||||
@@ -38,12 +38,15 @@ function sendVerificationCode(email, code) {
|
||||
});
|
||||
}
|
||||
|
||||
function sendApproved(email, { login, password, fio, position }) {
|
||||
function sendApproved(email, { login, password, fio, position, crmUrl, quickLinksUrl, netbios }) {
|
||||
const positionLine = position ? `\nДолжность: ${position}` : '';
|
||||
const netbiosName = (netbios || config.domain.split('.')[0]).toLowerCase();
|
||||
const crmLine = crmUrl ? `\n\nАдрес СРМ (задачник): ${crmUrl}` : '';
|
||||
const linksLine = quickLinksUrl ? `\nСервис быстрых ссылок: ${quickLinksUrl}` : '';
|
||||
return sendMail({
|
||||
to: email,
|
||||
subject: 'Регистрация одобрена',
|
||||
text: `Здравствуйте, ${fio}!\n\nВаша заявка на регистрацию одобрена.\n\nВаши данные для входа:\nЛогин: ${login}\nПароль: ${password}${positionLine}`
|
||||
text: `Здравствуйте, ${fio}!\n\nВаша заявка на регистрацию одобрена.\n\nВаши данные для входа:\nЛогин: ${login}\nПароль: ${password}${positionLine}\n\nИнструкция по входу в систему:\n• Чтобы зайти под своей учётной записью, на экране входа выберите «Другой пользователь».\n• Логин и пароль едины и для входа на ПК, и для задачника.\n• Если простой логин и пароль не принимаются, введите учётную запись полностью с указанием домена: ${netbiosName}\\${login}\n (пример: ${netbiosName}\\${login})${crmLine}${linksLine}`
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -95,6 +95,20 @@
|
||||
</div>
|
||||
<input type="text" id="cfgHomeBasePath" class="api-key-input" placeholder="\\dc\Teachers">
|
||||
</div>
|
||||
<div class="settings-row">
|
||||
<div class="settings-label">
|
||||
Адрес СРМ (задачник)
|
||||
<div class="hint">Отображается в письме об одобрении регистрации</div>
|
||||
</div>
|
||||
<input type="text" id="cfgCrmUrl" class="api-key-input" placeholder="https://...">
|
||||
</div>
|
||||
<div class="settings-row">
|
||||
<div class="settings-label">
|
||||
Сервис быстрых ссылок
|
||||
<div class="hint">Отображается в письме об одобрении регистрации</div>
|
||||
</div>
|
||||
<input type="text" id="cfgQuickLinksUrl" class="api-key-input" placeholder="https://...">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="toolbar">
|
||||
@@ -281,7 +295,7 @@
|
||||
<script>
|
||||
const TOKEN_KEY = 'ldap_admin_token';
|
||||
const USER_KEY = 'ldap_admin_user';
|
||||
let adminSettings = { ouDn: '', defaultGroups: [], phoneRequired: false, authEnabled: true, apiKey: '', logonScript: 'PST.bat', homeDrive: 'Z:', homeBasePath: '\\\\dc\\Teachers' };
|
||||
let adminSettings = { ouDn: '', defaultGroups: [], phoneRequired: false, authEnabled: true, apiKey: '', logonScript: 'PST.bat', homeDrive: 'Z:', homeBasePath: '\\\\dc\\Teachers', crmUrl: '', quickLinksUrl: '' };
|
||||
let currentMemberGroups = [];
|
||||
let requestsCache = { pending: [], history: [] };
|
||||
let reqTab = 'pending';
|
||||
@@ -387,15 +401,17 @@
|
||||
async function loadSettings() {
|
||||
try {
|
||||
const data = await api('/api/admin/settings');
|
||||
adminSettings = { ouDn: '', defaultGroups: [], phoneRequired: false, authEnabled: true, apiKey: '', logonScript: 'PST.bat', homeDrive: 'Z:', homeBasePath: '\\\\dc\\Teachers', ...(data.settings || {}) };
|
||||
adminSettings = { ouDn: '', defaultGroups: [], phoneRequired: false, authEnabled: true, apiKey: '', logonScript: 'PST.bat', homeDrive: 'Z:', homeBasePath: '\\\\dc\\Teachers', crmUrl: '', quickLinksUrl: '', ...(data.settings || {}) };
|
||||
$('reqPhoneRequired').checked = !!adminSettings.phoneRequired;
|
||||
$('reqAuthEnabled').checked = adminSettings.authEnabled !== false;
|
||||
$('apiKeyField').value = adminSettings.apiKey || '';
|
||||
$('cfgLogonScript').value = adminSettings.logonScript || '';
|
||||
$('cfgHomeDrive').value = adminSettings.homeDrive || '';
|
||||
$('cfgHomeBasePath').value = adminSettings.homeBasePath || '';
|
||||
$('cfgCrmUrl').value = adminSettings.crmUrl || '';
|
||||
$('cfgQuickLinksUrl').value = adminSettings.quickLinksUrl || '';
|
||||
} catch (err) {
|
||||
adminSettings = { ouDn: '', defaultGroups: [], phoneRequired: false, authEnabled: true, apiKey: '', logonScript: 'PST.bat', homeDrive: 'Z:', homeBasePath: '\\\\dc\\Teachers' };
|
||||
adminSettings = { ouDn: '', defaultGroups: [], phoneRequired: false, authEnabled: true, apiKey: '', logonScript: 'PST.bat', homeDrive: 'Z:', homeBasePath: '\\\\dc\\Teachers', crmUrl: '', quickLinksUrl: '' };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -552,11 +568,13 @@
|
||||
const date = new Date(r.createdAt || Date.now()).toLocaleString('ru-RU');
|
||||
const resend = (r.phase === 'approved' || r.phase === 'denied') && !r.emailSent
|
||||
? `<button class="btn btn-secondary" onclick="resendEmail('${r.id}')">Письмо не отправлено — повторить</button>` : '';
|
||||
const resendApproved = reqTab === 'history' && r.type !== 'password-change' && r.phase === 'approved'
|
||||
? `<button class="btn btn-secondary" onclick="resendApprovedEmail('${r.id}')">Отправить письмо повторно</button>` : '';
|
||||
return `<div class="req-card">
|
||||
<div class="req-head">${badge}${typeBadge}<span class="req-fio">${escapeHtml(r.fio || '')}</span><span class="req-date">${date}</span></div>
|
||||
<div class="req-meta">Логин: ${escapeHtml(r.login || '—')} · Почта: ${escapeHtml(r.email || '—')}${r.phone ? ' · Телефон: ' + escapeHtml(r.phone) : ''}${r.position ? ' · Должность: ' + escapeHtml(r.position) : ''}</div>
|
||||
${groups}${reason}
|
||||
<div class="actions" style="margin-top:8px">${reqTab === 'pending' ? `<button class="btn btn-secondary" onclick="openRequestModal('${r.id}')">Рассмотреть</button>` : ''}${resend}</div>
|
||||
<div class="actions" style="margin-top:8px">${reqTab === 'pending' ? `<button class="btn btn-secondary" onclick="openRequestModal('${r.id}')">Рассмотреть</button>` : ''}${resend}${resendApproved}</div>
|
||||
</div>`;
|
||||
}).join('');
|
||||
}
|
||||
@@ -666,6 +684,17 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function resendApprovedEmail(id) {
|
||||
if (!confirm('Отправить письмо с данными об одобрении ещё раз?')) return;
|
||||
try {
|
||||
await api(`/api/admin/requests/${id}/resend`, { method: 'POST' });
|
||||
showMsg($('adminMsg'), 'Письмо отправлено повторно', 'success');
|
||||
await loadRequests();
|
||||
} catch (err) {
|
||||
showMsg($('adminMsg'), err.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// ================= Security journal / whitelist =================
|
||||
|
||||
let journalType = 'admin';
|
||||
@@ -869,6 +898,8 @@
|
||||
bindSettingField('cfgLogonScript', 'logonScript');
|
||||
bindSettingField('cfgHomeDrive', 'homeDrive');
|
||||
bindSettingField('cfgHomeBasePath', 'homeBasePath');
|
||||
bindSettingField('cfgCrmUrl', 'crmUrl');
|
||||
bindSettingField('cfgQuickLinksUrl', 'quickLinksUrl');
|
||||
|
||||
$('apiKeyCopyBtn').addEventListener('click', async () => {
|
||||
const val = $('apiKeyField').value;
|
||||
|
||||
Reference in New Issue
Block a user