Update configuration and access control for Telegram miniapp

- Added ALLOWED_USERNAMES and ADMIN_USERNAMES to .env.example for user access control.
- Implemented validation of Telegram Web App initData in a new telegram_auth.py module.
- Enhanced API to check user access before fetching duties.
- Updated README with instructions for configuring miniapp access.
- Modified .dockerignore and .gitignore to include data directory and database files.
This commit is contained in:
2026-02-17 13:10:45 +03:00
parent d60a4fdf3f
commit 57c24a79af
10 changed files with 166 additions and 3 deletions

View File

@@ -10,6 +10,9 @@
const dutyListEl = document.getElementById("dutyList");
const loadingEl = document.getElementById("loading");
const errorEl = document.getElementById("error");
const accessDeniedEl = document.getElementById("accessDenied");
const headerEl = document.querySelector(".header");
const weekdaysEl = document.querySelector(".weekdays");
function isoDate(d) {
return d.toISOString().slice(0, 10);
@@ -29,10 +32,38 @@
return new Date(d.getFullYear(), d.getMonth(), diff);
}
function getInitData() {
return (window.Telegram && window.Telegram.WebApp && window.Telegram.WebApp.initData) || "";
}
function showAccessDenied() {
if (headerEl) headerEl.hidden = true;
if (weekdaysEl) weekdaysEl.hidden = true;
calendarEl.hidden = true;
dutyListEl.hidden = true;
loadingEl.classList.add("hidden");
errorEl.hidden = true;
accessDeniedEl.hidden = false;
}
function hideAccessDenied() {
accessDeniedEl.hidden = true;
if (headerEl) headerEl.hidden = false;
if (weekdaysEl) weekdaysEl.hidden = false;
calendarEl.hidden = false;
dutyListEl.hidden = false;
}
async function fetchDuties(from, to) {
const base = window.location.origin;
const url = base + "/api/duties?from=" + encodeURIComponent(from) + "&to=" + encodeURIComponent(to);
const res = await fetch(url);
const initData = getInitData();
const headers = {};
if (initData) headers["X-Telegram-Init-Data"] = initData;
const res = await fetch(url, { headers: headers });
if (res.status === 403) {
throw new Error("ACCESS_DENIED");
}
if (!res.ok) throw new Error("Ошибка загрузки");
return res.json();
}
@@ -116,6 +147,11 @@
}
async function loadMonth() {
if (!getInitData()) {
showAccessDenied();
return;
}
hideAccessDenied();
loadingEl.classList.remove("hidden");
errorEl.hidden = true;
const from = isoDate(firstDayOfMonth(current));
@@ -126,6 +162,10 @@
renderCalendar(current.getFullYear(), current.getMonth(), byDate);
renderDutyList(duties);
} catch (e) {
if (e.message === "ACCESS_DENIED") {
showAccessDenied();
return;
}
showError(e.message || "Не удалось загрузить данные.");
return;
}