Files
call-center/ui/login/app.js
T

194 lines
5.9 KiB
JavaScript

const SESSION_STORAGE_KEY = 'cc_session';
const state = {
user: 'admin',
role: 'admin',
token: null,
authSource: 'local',
oidc: {
enabled: false,
loginPath: '/auth/oidc/start?return_mode=popup',
providerLabel: 'Keycloak',
},
};
const $ = (id) => document.getElementById(id);
function destinationForRole(role) {
if (role === 'admin') {
return '/admin';
}
if (role === 'supervisor') {
return '/supervisor';
}
if (role === 'analyst') {
return '/analyst';
}
return '/operator';
}
function syncSessionFromInputs() {
state.user = $('sessionUser').value.trim() || 'admin';
}
function writeLog(message, payload) {
const suffix = payload ? ` ${JSON.stringify(payload, null, 2)}` : '';
$('loginLog').textContent = `${message}${suffix}`;
}
function persistSession(data) {
const payload = {
access_token: data.access_token,
user: data.username || state.user,
role: data.role,
auth_source: data.auth_source || 'local',
provider: data.provider || null,
full_name: data.full_name || null,
};
window.localStorage.setItem(SESSION_STORAGE_KEY, JSON.stringify(payload));
}
function updateSessionInfo(extra = '') {
if (!state.token) {
$('sessionInfo').textContent = 'Токен: отсутствует';
return;
}
const source = state.authSource === 'oidc' ? 'корпоративный' : 'локальный';
$('sessionInfo').textContent = `Токен активен | роль: ${state.role} | вход: ${source}${extra ? ` | ${extra}` : ''}`;
}
function applyAuthenticatedSession(data) {
state.token = data.access_token;
state.role = data.role;
state.user = data.username || state.user;
state.authSource = data.auth_source || 'local';
$('sessionUser').value = state.user;
updateSessionInfo(data.provider ? `provider: ${data.provider}` : '');
persistSession(data);
}
async function api(service, path, options = {}) {
syncSessionFromInputs();
const headers = {
'Content-Type': 'application/json',
...(options.headers || {}),
};
if (state.token) {
headers.Authorization = `Bearer ${state.token}`;
}
const response = await fetch(`/proxy/${service}/${path}`, {
...options,
headers,
});
let data;
try {
data = await response.json();
} catch {
data = { error: 'Non-JSON response' };
}
if (!response.ok) {
throw new Error(`${response.status}: ${JSON.stringify(data)}`);
}
return data;
}
async function loadOidcConfig() {
try {
const response = await fetch('/proxy/auth/auth/oidc/config');
const data = await response.json();
state.oidc.enabled = Boolean(data.enabled);
state.oidc.loginPath = data.login_path || '/auth/oidc/start?return_mode=popup';
state.oidc.providerLabel = data.provider_label || 'Keycloak';
$('corporateLoginBtn').disabled = !state.oidc.enabled;
$('corporateLoginBtn').textContent = state.oidc.enabled
? `Корпоративный вход (${state.oidc.providerLabel})`
: 'Корпоративный вход недоступен';
} catch {
$('corporateLoginBtn').disabled = true;
$('corporateLoginBtn').textContent = 'Корпоративный вход недоступен';
}
}
function startCorporateLogin() {
if (!state.oidc.enabled) {
writeLog('Корпоративный вход недоступен.');
return;
}
const popup = window.open(`/proxy/auth${state.oidc.loginPath}`, 'oidc-login', 'width=620,height=760');
if (!popup) {
writeLog('Не удалось открыть окно корпоративного входа.');
return;
}
writeLog('Открыт корпоративный вход.', { provider: state.oidc.providerLabel });
}
function handleOidcMessage(event) {
if (!event?.data || typeof event.data !== 'object') {
return;
}
if (event.data.type === 'oidc-login' && event.data.access_token) {
applyAuthenticatedSession(event.data);
writeLog('Корпоративный вход выполнен.', { user: state.user, role: state.role });
window.location.href = destinationForRole(state.role);
return;
}
if (event.data.type === 'oidc-error') {
writeLog('Корпоративный вход не выполнен.', { error: event.data.message || 'unknown' });
}
}
async function login() {
syncSessionFromInputs();
try {
const data = await api('auth', 'auth/login', {
method: 'POST',
body: JSON.stringify({
username: state.user,
password: $('sessionPassword').value,
}),
});
applyAuthenticatedSession({
...data,
username: state.user,
auth_source: data.auth_source || 'local',
});
writeLog('Локальный вход выполнен.', { user: state.user, role: data.role });
window.location.href = destinationForRole(data.role);
} catch (err) {
$('sessionInfo').textContent = `Ошибка входа: ${err.message}`;
writeLog('Вход не выполнен.', { error: err.message });
}
}
function wire() {
$('loginBtn').addEventListener('click', login);
$('corporateLoginBtn').addEventListener('click', startCorporateLogin);
$('refreshBtn').addEventListener('click', loadOidcConfig);
window.addEventListener('message', handleOidcMessage);
}
async function init() {
wire();
updateSessionInfo();
await loadOidcConfig();
const raw = window.localStorage.getItem(SESSION_STORAGE_KEY);
if (raw) {
try {
const payload = JSON.parse(raw);
if (payload?.access_token) {
state.token = payload.access_token;
state.user = payload.user || state.user;
state.role = payload.role || state.role;
state.authSource = payload.auth_source || 'local';
$('sessionUser').value = state.user;
updateSessionInfo(payload.provider ? `provider: ${payload.provider}` : '');
writeLog('Найдена активная сессия.', { user: state.user, role: state.role });
}
} catch {
window.localStorage.removeItem(SESSION_STORAGE_KEY);
}
}
}
init();