194 lines
5.9 KiB
JavaScript
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();
|