Initial SQL-only 1C adapter baseline

This commit is contained in:
2026-07-22 03:03:47 +03:00
commit e2503b77e7
545 changed files with 184711 additions and 0 deletions
+106
View File
@@ -0,0 +1,106 @@
const $ = (id) => document.getElementById(id);
const state = { bases: [], requests: [] };
function headers(json = false) {
const value = {};
if (json) value["Content-Type"] = "application/json";
return value;
}
async function request(path, options = {}) {
const response = await fetch(path, { ...options, headers: { ...headers(Boolean(options.body)), ...(options.headers || {}) } });
let data = {};
try { data = await response.json(); } catch (_) { /* empty response */ }
if (!response.ok) throw new Error(data.message || data.error || `HTTP ${response.status}`);
return data;
}
function notice(message = "", error = false) {
$("notice").hidden = !message;
$("notice").textContent = message;
$("notice").className = `notice${error ? " error" : ""}`;
}
function render() {
$("bases").replaceChildren(...state.bases.map((base) => {
const row = document.createElement("tr");
const cells = [base.base_id, base.server, base.database, base.user];
cells.forEach((value) => { const td = document.createElement("td"); td.textContent = value; row.append(td); });
const password = document.createElement("td");
password.innerHTML = `<span class="badge ${base.has_password ? "" : "missing"}">${base.password_env ? "ENV · " + escapeHtml(base.password_env) : base.has_password ? "Сохранён" : "Не задан"}</span>`;
row.append(password);
const actions = document.createElement("td"); actions.className = "actions";
const edit = document.createElement("button"); edit.className = "text-button"; edit.textContent = "Изменить"; edit.onclick = () => openEditor(base);
const remove = document.createElement("button"); remove.className = "text-button danger"; remove.textContent = "Удалить"; remove.onclick = () => deleteBase(base);
actions.append(edit, remove); row.append(actions); return row;
}));
$("empty").hidden = state.bases.length > 0;
$("count").textContent = `${state.bases.length} ${state.bases.length === 1 ? "подключение" : "подключений"}`;
}
function renderRequests() {
$("requests").replaceChildren(...state.requests.map((item) => {
const row = document.createElement("tr");
const values = [item.request_id, item.base_id, item.status, (item.objects || []).join(", "), item.created_at ? new Date(item.created_at * 1000).toLocaleString("ru-RU") : "—"];
values.forEach((value, index) => { const td = document.createElement("td"); td.textContent = value || "—"; if (index === 2) td.className = `request-status ${item.status || ""}`; if (index === 3) td.className = "request-objects"; row.append(td); });
return row;
}));
$("requestsEmpty").hidden = state.requests.length > 0;
$("requestCount").textContent = `${state.requests.length} заявок`;
}
function escapeHtml(value) {
const div = document.createElement("div"); div.textContent = value; return div.innerHTML;
}
async function load() {
try {
notice();
const [data, repository] = await Promise.all([request("/admin/api/bases"), request("/admin/api/repository/requests")]);
state.bases = data.bases || [];
state.requests = repository.requests || [];
$("statusDot").classList.add("online"); $("connectionText").textContent = "Адаптер подключён";
render();
renderRequests();
} catch (error) {
$("statusDot").classList.remove("online"); $("connectionText").textContent = "Требуется подключение";
notice(error.message === "bearer_token_required" ? "Введите Bearer-токен адаптера." : error.message, true);
}
}
function openEditor(base = null) {
$("editorTitle").textContent = base ? "Редактировать базу" : "Новая база";
$("originalId").value = base?.base_id || "";
$("baseId").value = base?.base_id || ""; $("server").value = base?.server || "";
$("database").value = base?.database || ""; $("user").value = base?.user || "";
$("password").value = ""; $("passwordEnv").value = base?.password_env || "";
$("repositoryEnabled").checked = Boolean(base?.repository);
$("repositoryBackend").value = base?.repository?.backend || "direct";
$("repositoryLayer").value = base?.repository?.layer || "base";
$("repositoryLockMode").value = base?.repository?.lock_mode || "automatic";
$("repositoryBridgeId").value = base?.repository?.bridge_id || "";
$("repositoryUser").value = base?.repository?.repository_user || "";
$("passwordHint").textContent = base?.has_password ? "Пароль уже задан. Оставьте пустым, чтобы сохранить текущий." : "Задайте пароль или переменную окружения.";
$("editor").showModal(); setTimeout(() => $("baseId").focus(), 30);
}
async function save(event) {
event.preventDefault();
if (!$("baseForm").reportValidity()) return;
const original = $("originalId").value;
const payload = { base_id: $("baseId").value.trim(), server: $("server").value.trim(), database: $("database").value.trim(), user: $("user").value.trim(), password: $("password").value, password_env: $("passwordEnv").value.trim(), repository: $("repositoryEnabled").checked ? { backend: $("repositoryBackend").value, layer: $("repositoryLayer").value, lock_mode: $("repositoryLockMode").value, bridge_id: $("repositoryBridgeId").value.trim(), repository_user: $("repositoryUser").value.trim() } : { enabled: false } };
$("saveButton").disabled = true;
try {
await request(original ? `/admin/api/bases/${encodeURIComponent(original)}` : "/admin/api/bases", { method: original ? "PUT" : "POST", body: JSON.stringify(payload) });
$("editor").close(); await load(); notice(original ? "Подключение обновлено." : "Подключение добавлено.");
} catch (error) { notice(error.message, true); } finally { $("saveButton").disabled = false; }
}
async function deleteBase(base) {
if (!confirm(`Удалить подключение «${base.base_id}»?`)) return;
try { await request(`/admin/api/bases/${encodeURIComponent(base.base_id)}`, { method: "DELETE" }); await load(); notice("Подключение удалено."); }
catch (error) { notice(error.message, true); }
}
$("refreshButton").onclick = load; $("addButton").onclick = () => openEditor(); $("baseForm").onsubmit = save;
load();