efa1c26e5d
- Store request origin domain in PendingAccessRequest.portal_url - Use per-request portal URL in approval/rejection emails - Embed logo as base64 so it displays without external image loading - Fix 'Предоставлен доступ к продуктам' text color to match body color - Switch Telegram polling to 30-second interval with single-worker flock fix
2110 lines
260 KiB
Python
2110 lines
260 KiB
Python
import datetime as dt
|
||
import logging
|
||
import re
|
||
import secrets
|
||
import threading
|
||
import uuid
|
||
import time
|
||
import contextvars
|
||
from typing import Optional
|
||
|
||
from fastapi import Depends, FastAPI, File, Form, HTTPException, Query, Request, UploadFile, status
|
||
from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse
|
||
from fastapi.staticfiles import StaticFiles
|
||
from fastapi.templating import Jinja2Templates
|
||
from markupsafe import Markup, escape
|
||
from sqlalchemy import select
|
||
from sqlalchemy import delete, select, text, update
|
||
from sqlalchemy.orm import Session
|
||
from starlette.responses import HTMLResponse as _HR
|
||
|
||
import urllib.request as _urllib_request
|
||
import urllib.parse as _urllib_parse
|
||
import json as _json
|
||
from config import (
|
||
COOKIE_NAME, CSRF_COOKIE, GO_POOL_LOCK_TIMEOUT_SECONDS,
|
||
GO_USER_LOCK_TIMEOUT_SECONDS, LOG_LEVEL, LOG_SLOW_REQUEST_MS,
|
||
MAX_ACTIVE_SERVICES_PER_USER, PUBLIC_HOST, SESSION_IDLE_SECONDS,
|
||
WEB_POOL_BUFFER, WEB_POOL_SIZE,
|
||
TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID, TELEGRAM_API_URL,
|
||
SMTP_HOST, SMTP_PORT, SMTP_USERNAME, SMTP_PASSWORD,
|
||
SMTP_FROM_EMAIL, SMTP_FROM_NAME, PORTAL_URL,
|
||
)
|
||
from database import get_db
|
||
from models import (
|
||
AuditLog, Category, RdpSlot, Service, ServiceCategory, ServiceType,
|
||
PendingAccessRequest, SessionModel, SessionStatus, User, UserServiceAccess,
|
||
)
|
||
from utils import (
|
||
audit, ensure_icons_dir, format_service_comment, log_event, normalize_web_target,
|
||
now_utc, parse_rdp_target, remove_icon_file, request_id_ctx, set_service_categories,
|
||
session_closed_reason, store_service_icon,
|
||
)
|
||
from auth import (
|
||
get_current_user, has_access, issue_auth_cookie, issue_csrf_cookie,
|
||
require_admin, require_user, user_is_valid, validate_csrf, verify_password, hash_password,
|
||
)
|
||
from runtime import (
|
||
acquire_universal_slot, acquire_web_pool_slot, allocator_lock,
|
||
container_running, create_runtime_container, desired_pool_size,
|
||
dispatch_universal_target, dispatch_web_pool_target,
|
||
docker_client, ensure_universal_pool, ensure_warm_pool, ensure_web_pool,
|
||
find_active_session_for_service, find_active_session_for_user_service,
|
||
get_active_sessions_count, get_pool_detailed_status, get_pool_status_for_service,
|
||
get_universal_pool_status, get_web_pool_status, LockTimeoutError, open_warm_web_url,
|
||
_rdp_slot_container_name, route_ready, sanitize_client_resolution,
|
||
service_uses_universal_pool, session_redirect_url,
|
||
connect_rdp_slot, start_rdp_slot_container, stop_rdp_slot_container,
|
||
stop_runtime_container, terminate_active_slot_sessions,
|
||
terminate_session_record, wait_for_session_route,
|
||
)
|
||
from maintenance import on_startup
|
||
|
||
logging.basicConfig(
|
||
level=LOG_LEVEL,
|
||
format="%(asctime)s %(levelname)s %(name)s %(message)s",
|
||
)
|
||
logger = logging.getLogger("portal")
|
||
|
||
templates = Jinja2Templates(directory="templates")
|
||
|
||
def _get_real_ip(request) -> str:
|
||
"""Real client IP from X-Forwarded-For (Traefik trusts NPM via trustedIPs)."""
|
||
forwarded_for = request.headers.get("x-forwarded-for", "")
|
||
if forwarded_for:
|
||
return forwarded_for.split(",")[0].strip()
|
||
return request.client.host if request.client else "unknown"
|
||
|
||
|
||
def _get_geo(ip: str) -> str:
|
||
"""Lookup city/country for IP via ip-api.com. Returns formatted string or empty."""
|
||
try:
|
||
if ip in ("unknown", "127.0.0.1", "::1") or ip.startswith("10.") or ip.startswith("192.168."):
|
||
return ""
|
||
url = f"http://ip-api.com/json/{ip}?lang=ru&fields=status,country,regionName,city,query"
|
||
req = _urllib_request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
|
||
with _urllib_request.urlopen(req, timeout=5) as resp:
|
||
data = _json.loads(resp.read())
|
||
if data.get("status") == "success":
|
||
parts = [data.get("country", ""), data.get("regionName", ""), data.get("city", "")]
|
||
return ", ".join(p for p in parts if p)
|
||
except Exception:
|
||
pass
|
||
return ""
|
||
|
||
|
||
import secrets as _secrets
|
||
import string as _string
|
||
import smtplib as _smtplib
|
||
import ssl as _ssl
|
||
import json as _json2
|
||
from email.mime.multipart import MIMEMultipart as _MIMEMultipart
|
||
from email.mime.text import MIMEText as _MIMEText
|
||
|
||
def _generate_password(length: int = 10) -> str:
|
||
alphabet = _string.ascii_letters + _string.digits
|
||
while True:
|
||
pwd = ''.join(_secrets.choice(alphabet) for _ in range(length))
|
||
if (any(c.isupper() for c in pwd) and any(c.islower() for c in pwd)
|
||
and any(c.isdigit() for c in pwd)):
|
||
return pwd
|
||
|
||
def _send_email(to: str, subject: str, html_body: str) -> None:
|
||
msg = _MIMEMultipart("alternative")
|
||
msg["Subject"] = subject
|
||
msg["From"] = f"{SMTP_FROM_NAME} <{SMTP_FROM_EMAIL}>"
|
||
msg["To"] = to
|
||
msg.attach(_MIMEText(html_body, "html", "utf-8"))
|
||
ctx = _ssl.create_default_context()
|
||
with _smtplib.SMTP_SSL(SMTP_HOST, SMTP_PORT, context=ctx) as srv:
|
||
srv.login(SMTP_USERNAME, SMTP_PASSWORD)
|
||
srv.sendmail(SMTP_FROM_EMAIL, to, msg.as_string())
|
||
|
||
def _tg_api(method: str, payload: dict) -> dict:
|
||
import urllib.request as _ur
|
||
import json as _j
|
||
url = f"{TELEGRAM_API_URL}{TELEGRAM_BOT_TOKEN}/{method}"
|
||
data = _j.dumps(payload).encode()
|
||
req = _ur.Request(url, data=data, headers={"Content-Type": "application/json"})
|
||
with _ur.urlopen(req, timeout=10) as r:
|
||
return _j.loads(r.read())
|
||
|
||
def _make_approval_keyboard(req_id: str) -> dict:
|
||
return {
|
||
"inline_keyboard": [
|
||
[
|
||
{"text": "7 дней", "callback_data": f"a7_{req_id}"},
|
||
{"text": "14 дней", "callback_data": f"a14_{req_id}"},
|
||
{"text": "30 дней", "callback_data": f"a30_{req_id}"},
|
||
{"text": "90 дней", "callback_data": f"a90_{req_id}"},
|
||
],
|
||
[{"text": "Отказать", "callback_data": f"r_{req_id}"}],
|
||
]
|
||
}
|
||
|
||
|
||
_tg_poll_offset: int = 0
|
||
_tg_poll_lock_file = None
|
||
|
||
async def _telegram_poll_loop():
|
||
"""Poll Telegram for callback_query updates every 5 minutes."""
|
||
import asyncio as _asyncio
|
||
import json as _jp
|
||
global _tg_poll_offset
|
||
await _asyncio.sleep(10) # wait for app to fully start
|
||
while True:
|
||
try:
|
||
result = _tg_api("getUpdates", {
|
||
"offset": _tg_poll_offset,
|
||
"timeout": 0,
|
||
"allowed_updates": ["callback_query"],
|
||
})
|
||
updates = result.get("result", [])
|
||
for upd in updates:
|
||
_tg_poll_offset = upd["update_id"] + 1
|
||
cq = upd.get("callback_query")
|
||
if cq:
|
||
try:
|
||
await _process_callback_query(cq)
|
||
except Exception as ex:
|
||
log_event("tg_callback_error", error=str(ex))
|
||
except Exception as ex:
|
||
log_event("tg_poll_error", error=str(ex))
|
||
await _asyncio.sleep(30) # 30 seconds
|
||
|
||
async def _process_callback_query(cq: dict):
|
||
import json as _jc
|
||
import datetime as _dtc
|
||
import re as _rec
|
||
from database import SessionLocal as _SL
|
||
|
||
cq_id = cq["id"]
|
||
cb_data = cq.get("data", "")
|
||
chat_id = cq["message"]["chat"]["id"]
|
||
msg_id = cq["message"]["message_id"]
|
||
|
||
try:
|
||
_tg_api("answerCallbackQuery", {"callback_query_id": cq_id})
|
||
except Exception:
|
||
pass
|
||
|
||
approve_match = _rec.match(r'^a(\d+)_(.+)$', cb_data)
|
||
reject_match = _rec.match(r'^r_(.+)$', cb_data)
|
||
|
||
if not approve_match and not reject_match:
|
||
return
|
||
|
||
req_id = approve_match.group(2) if approve_match else reject_match.group(1)
|
||
|
||
db = _SL()
|
||
try:
|
||
pending = db.get(PendingAccessRequest, req_id)
|
||
if not pending:
|
||
_tg_api("editMessageText", {
|
||
"chat_id": chat_id, "message_id": msg_id,
|
||
"text": "Запрос не найден (возможно уже обработан).",
|
||
})
|
||
return
|
||
|
||
if pending.status != "pending":
|
||
_tg_api("editMessageText", {
|
||
"chat_id": chat_id, "message_id": msg_id,
|
||
"text": f"Запрос уже обработан: {pending.status}.",
|
||
})
|
||
return
|
||
|
||
products = _jc.loads(pending.products_json or "[]")
|
||
portal_url = pending.portal_url or PORTAL_URL
|
||
|
||
if approve_match:
|
||
days = int(approve_match.group(1))
|
||
password = _generate_password()
|
||
username = pending.email
|
||
|
||
if db.scalar(select(User).where(User.username == username)):
|
||
import secrets as _sec
|
||
username = pending.email.split("@")[0] + "_" + _sec.token_hex(3)
|
||
|
||
expires = _dtc.datetime.now(_dtc.timezone.utc) + _dtc.timedelta(days=days)
|
||
parts = pending.name.strip().split(None, 1)
|
||
new_user = User(
|
||
username=username,
|
||
password_hash=hash_password(password),
|
||
expires_at=expires,
|
||
active=True,
|
||
is_admin=False,
|
||
first_name=parts[0] if parts else "",
|
||
last_name=parts[1] if len(parts) > 1 else "",
|
||
)
|
||
db.add(new_user)
|
||
db.flush()
|
||
|
||
if products:
|
||
from sqlalchemy import func as _func2
|
||
matched = db.scalars(
|
||
select(Service).where(
|
||
_func2.lower(Service.name).in_([p.lower() for p in products]),
|
||
Service.active == True,
|
||
)
|
||
).all()
|
||
for svc in matched:
|
||
db.add(UserServiceAccess(user_id=new_user.id, service_id=svc.id))
|
||
|
||
db.commit()
|
||
|
||
products_html = ""
|
||
if products:
|
||
items = "".join(f"<li>{p}</li>" for p in products)
|
||
products_html = (
|
||
'<p style="margin:16px 0 6px;color:#c8d8ea"><b>Предоставлен доступ к продуктам:</b></p>'
|
||
f'<ul style="margin:0;padding-left:20px;color:#c8d8ea">{items}</ul>'
|
||
)
|
||
|
||
day_word = "день" if days == 1 else ("дня" if days < 5 else "дней")
|
||
html_email = f"""<!DOCTYPE html>
|
||
<html lang="ru"><head><meta charset="utf-8"/></head>
|
||
<body style="margin:0;padding:0;background:#0a1929;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif">
|
||
<table width="100%" cellpadding="0" cellspacing="0"><tr><td align="center" style="padding:40px 20px">
|
||
<table width="560" cellpadding="0" cellspacing="0" style="background:linear-gradient(150deg,#0b1a2e,#0d2040);border-radius:16px;overflow:hidden;border:1px solid rgba(255,255,255,0.08)">
|
||
<tr><td style="padding:32px 36px 0">
|
||
<img src="data:image/png;base64,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" alt="MONT" height="40" style="margin-bottom:24px"/><br>
|
||
<h1 style="margin:0 0 8px;font-size:22px;color:#e8f1fb">Доступ к Инфраструктурному полигону MONT</h1>
|
||
<p style="margin:0 0 24px;color:#7a9abd;font-size:14px">Ваш запрос одобрен</p>
|
||
<hr style="border:none;border-top:1px solid rgba(255,255,255,0.08);margin:0 0 24px"/>
|
||
</td></tr>
|
||
<tr><td style="padding:0 36px">
|
||
<p style="color:#c8d8ea;font-size:15px;line-height:1.6;margin:0 0 20px">Здравствуйте, <b>{pending.name}</b>!<br>
|
||
Вам предоставлен доступ к полигону на <b>{days} {day_word}</b>.</p>
|
||
<table width="100%" cellpadding="12" cellspacing="0" style="background:rgba(255,255,255,0.04);border-radius:10px;border:1px solid rgba(255,255,255,0.07)">
|
||
<tr><td style="color:#7a9abd;font-size:13px;width:40%">Адрес портала</td>
|
||
<td><a href="{portal_url}" style="color:#5b9bd5;font-size:14px">{portal_url}</a></td></tr>
|
||
<tr><td style="color:#7a9abd;font-size:13px;border-top:1px solid rgba(255,255,255,0.06)">Логин</td>
|
||
<td style="border-top:1px solid rgba(255,255,255,0.06);color:#e8f1fb;font-size:14px;font-family:monospace">{username}</td></tr>
|
||
<tr><td style="color:#7a9abd;font-size:13px;border-top:1px solid rgba(255,255,255,0.06)">Пароль</td>
|
||
<td style="border-top:1px solid rgba(255,255,255,0.06);color:#e8f1fb;font-size:14px;font-family:monospace">{password}</td></tr>
|
||
<tr><td style="color:#7a9abd;font-size:13px;border-top:1px solid rgba(255,255,255,0.06)">Доступ до</td>
|
||
<td style="border-top:1px solid rgba(255,255,255,0.06);color:#e8f1fb;font-size:14px">{expires.strftime("%d.%m.%Y")}</td></tr>
|
||
</table>
|
||
{products_html}
|
||
<div style="margin:28px 0">
|
||
<a href="{portal_url}" style="display:inline-block;padding:12px 28px;background:linear-gradient(135deg,#1a5db5,#2d8cf0);color:#fff;text-decoration:none;border-radius:8px;font-size:15px;font-weight:600">Войти в полигон</a>
|
||
</div>
|
||
</td></tr>
|
||
<tr><td style="padding:20px 36px 28px;color:#4a6a8a;font-size:12px;border-top:1px solid rgba(255,255,255,0.06);line-height:1.6">
|
||
Если у вас возникли вопросы, свяжитесь с вашим менеджером MONT или напишите на <a href="mailto:mont@mont.ru" style="color:#5b9bd5">mont@mont.ru</a>
|
||
</td></tr>
|
||
</table></td></tr></table>
|
||
</body></html>"""
|
||
|
||
pending.status = "approved"
|
||
db.commit()
|
||
|
||
try:
|
||
_send_email(pending.email, "Доступ к Инфраструктурному полигону MONT", html_email)
|
||
email_status = "Email отправлен"
|
||
except Exception as ex:
|
||
log_event("email_send_error", error=str(ex))
|
||
email_status = f"Ошибка email: {ex}"
|
||
|
||
_tg_api("editMessageText", {
|
||
"chat_id": chat_id, "message_id": msg_id,
|
||
"text": (
|
||
f"✅ Одобрено на {days} {day_word}\n"
|
||
f"👤 Логин: `{username}`\n"
|
||
f"🔑 Пароль: `{password}`\n"
|
||
f"📧 {email_status}"
|
||
),
|
||
"parse_mode": "Markdown",
|
||
})
|
||
|
||
elif reject_match:
|
||
manager_contact = pending.manager if pending.manager else "менеджера MONT"
|
||
html_email = f"""<!DOCTYPE html>
|
||
<html lang="ru"><head><meta charset="utf-8"/></head>
|
||
<body style="margin:0;padding:0;background:#0a1929;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif">
|
||
<table width="100%" cellpadding="0" cellspacing="0"><tr><td align="center" style="padding:40px 20px">
|
||
<table width="560" cellpadding="0" cellspacing="0" style="background:linear-gradient(150deg,#0b1a2e,#0d2040);border-radius:16px;overflow:hidden;border:1px solid rgba(255,255,255,0.08)">
|
||
<tr><td style="padding:32px 36px 0">
|
||
<img src="data:image/png;base64,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" alt="MONT" height="40" style="margin-bottom:24px"/><br>
|
||
<h1 style="margin:0 0 8px;font-size:22px;color:#e8f1fb">Запрос на доступ к полигону MONT</h1>
|
||
<hr style="border:none;border-top:1px solid rgba(255,255,255,0.08);margin:16px 0 24px"/>
|
||
</td></tr>
|
||
<tr><td style="padding:0 36px">
|
||
<p style="color:#c8d8ea;font-size:15px;line-height:1.7;margin:0 0 20px">Здравствуйте, <b>{pending.name}</b>!<br><br>
|
||
К сожалению, на данный момент мы не можем предоставить доступ к полигону.</p>
|
||
<p style="color:#c8d8ea;font-size:15px;line-height:1.7;margin:0 0 24px">
|
||
Для уточнения деталей свяжитесь с <b>{manager_contact}</b>.<br>
|
||
Если не знаете кто ваш менеджер — напишите на <a href="mailto:mont@mont.ru" style="color:#5b9bd5">mont@mont.ru</a>.</p>
|
||
</td></tr>
|
||
<tr><td style="padding:20px 36px 28px;color:#4a6a8a;font-size:12px;border-top:1px solid rgba(255,255,255,0.06)">
|
||
С уважением, команда MONT
|
||
</td></tr>
|
||
</table></td></tr></table>
|
||
</body></html>"""
|
||
|
||
pending.status = "rejected"
|
||
db.commit()
|
||
|
||
try:
|
||
_send_email(pending.email, "Запрос на доступ к полигону MONT", html_email)
|
||
email_status = "Email отправлен"
|
||
except Exception as ex:
|
||
log_event("email_send_error", error=str(ex))
|
||
email_status = f"Ошибка email: {ex}"
|
||
|
||
_tg_api("editMessageText", {
|
||
"chat_id": chat_id, "message_id": msg_id,
|
||
"text": f"❌ Отклонено. {email_status}",
|
||
})
|
||
finally:
|
||
db.close()
|
||
|
||
app = FastAPI(title="MONT - инфрастуктурный полигон")
|
||
app.mount("/static", StaticFiles(directory="static"), name="static")
|
||
|
||
|
||
@app.middleware("http")
|
||
async def request_logging_middleware(request: Request, call_next):
|
||
req_id = request.headers.get("X-Request-ID", str(uuid.uuid4())[:8])
|
||
token = request_id_ctx.set(req_id)
|
||
started = time.time()
|
||
client_ip = request.client.host if request.client else "-"
|
||
user_agent = request.headers.get("user-agent", "-")
|
||
try:
|
||
response = await call_next(request)
|
||
except Exception:
|
||
log_event(
|
||
"request_failed",
|
||
level=logging.ERROR,
|
||
method=request.method,
|
||
path=request.url.path,
|
||
client_ip=client_ip,
|
||
user_agent=user_agent,
|
||
)
|
||
request_id_ctx.reset(token)
|
||
raise
|
||
duration_ms = int((time.time() - started) * 1000)
|
||
level = logging.INFO
|
||
if response.status_code >= 500:
|
||
level = logging.ERROR
|
||
elif response.status_code >= 400:
|
||
level = logging.WARNING
|
||
log_event(
|
||
"request",
|
||
level=level,
|
||
method=request.method,
|
||
path=request.url.path,
|
||
query=str(request.url.query or ""),
|
||
status=response.status_code,
|
||
duration_ms=duration_ms,
|
||
client_ip=client_ip,
|
||
user_agent=user_agent,
|
||
)
|
||
if duration_ms >= LOG_SLOW_REQUEST_MS:
|
||
log_event(
|
||
"slow_request",
|
||
level=logging.WARNING,
|
||
method=request.method,
|
||
path=request.url.path,
|
||
duration_ms=duration_ms,
|
||
threshold_ms=LOG_SLOW_REQUEST_MS,
|
||
)
|
||
response.headers["X-Request-ID"] = req_id
|
||
request_id_ctx.reset(token)
|
||
return response
|
||
|
||
|
||
_MOBILE_UA_RE = re.compile(
|
||
r"(Mobile|Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|webOS)",
|
||
re.IGNORECASE,
|
||
)
|
||
_MOBILE_PAGE = (
|
||
"<!doctype html>"
|
||
'<html lang="ru">'
|
||
"<head>"
|
||
'<meta charset="utf-8"/>'
|
||
'<meta name="viewport" content="width=device-width,initial-scale=1"/>'
|
||
"<title>MONT - инфрастуктурный полигон</title>"
|
||
"<style>"
|
||
"*{box-sizing:border-box;margin:0;padding:0}"
|
||
"body{min-height:100dvh;display:flex;flex-direction:column;align-items:center;"
|
||
"justify-content:center;background:linear-gradient(160deg,#0a2a4a 0%,#1565a0 60%,#1e88c8 100%);"
|
||
"font-family:sans-serif;color:#fff;padding:2rem 1.5rem;text-align:center}"
|
||
".logo{width:120px;margin-bottom:2rem}"
|
||
"h1{font-size:1.3rem;font-weight:700;margin-bottom:1rem;line-height:1.35}"
|
||
"p{font-size:0.95rem;color:rgba(255,255,255,.75);line-height:1.5;max-width:280px}"
|
||
".icon{font-size:3.5rem;margin-bottom:1.2rem}"
|
||
"</style>"
|
||
"</head>"
|
||
"<body>"
|
||
'<img class="logo" src="/static/logo.png" alt="MONT"/>'
|
||
'<div class="icon">🖥</div>'
|
||
"<h1>Ресурс доступен<br>только с ПК</h1>"
|
||
"<p>Пожалуйста, откройте эту страницу на компьютере или ноутбуке.</p>"
|
||
"</body>"
|
||
"</html>"
|
||
)
|
||
|
||
|
||
@app.middleware("http")
|
||
async def mobile_block_middleware(request: Request, call_next):
|
||
path = request.url.path
|
||
if path.startswith("/static/"):
|
||
return await call_next(request)
|
||
ua = request.headers.get("user-agent", "")
|
||
if _MOBILE_UA_RE.search(ua):
|
||
return _HR(content=_MOBILE_PAGE, status_code=200)
|
||
return await call_next(request)
|
||
|
||
|
||
@app.on_event("startup")
|
||
async def startup_event():
|
||
global _tg_poll_lock_file
|
||
on_startup()
|
||
import asyncio as _aio, fcntl as _fcntl
|
||
_lf = open("/tmp/portal-tg-poll.lock", "w")
|
||
try:
|
||
_fcntl.flock(_lf.fileno(), _fcntl.LOCK_EX | _fcntl.LOCK_NB)
|
||
_tg_poll_lock_file = _lf
|
||
_aio.create_task(_telegram_poll_loop())
|
||
except BlockingIOError:
|
||
_lf.close()
|
||
|
||
|
||
@app.get("/", response_class=HTMLResponse)
|
||
def index(request: Request, user: Optional[User] = Depends(get_current_user), db: Session = Depends(get_db)):
|
||
session_closed = (request.query_params.get("session_closed") or "").strip().lower()
|
||
launch_error = (request.query_params.get("launch_error") or "").strip().lower()
|
||
session_notice = ""
|
||
if session_closed == "idle":
|
||
session_notice = "Сессия была закрыта из-за простоя. Откройте сервис заново."
|
||
elif session_closed == "limit":
|
||
session_notice = (
|
||
f"Сессия была закрыта из-за лимита в {MAX_ACTIVE_SERVICES_PER_USER} сервиса(ов). "
|
||
"Освободите один сервис и попробуйте снова."
|
||
)
|
||
elif launch_error == "max_services":
|
||
session_notice = (
|
||
f"Есть ограничение на {MAX_ACTIVE_SERVICES_PER_USER} сервиса(ов). "
|
||
"Освободите один сервис и попробуйте снова."
|
||
)
|
||
if not user:
|
||
csrf = request.cookies.get(CSRF_COOKIE) or secrets.token_urlsafe(24)
|
||
response = templates.TemplateResponse(
|
||
"login.html",
|
||
{
|
||
"request": request,
|
||
"csrf_token": csrf,
|
||
"login_error": "",
|
||
"session_notice": session_notice,
|
||
},
|
||
)
|
||
response.set_cookie(CSRF_COOKIE, csrf, httponly=False, secure=True, samesite="lax", path="/")
|
||
return response
|
||
|
||
services = db.scalars(
|
||
select(Service)
|
||
.join(UserServiceAccess, UserServiceAccess.service_id == Service.id)
|
||
.where(
|
||
UserServiceAccess.user_id == user.id,
|
||
Service.active == True,
|
||
Service.type.in_([ServiceType.WEB, ServiceType.RDP]),
|
||
)
|
||
.order_by(Service.name)
|
||
).all()
|
||
|
||
service_categories = {svc.id: [] for svc in services}
|
||
categories = []
|
||
if services:
|
||
service_ids = [svc.id for svc in services]
|
||
rows = db.execute(
|
||
select(ServiceCategory.service_id, Category.id, Category.name, Category.slug)
|
||
.join(Category, Category.id == ServiceCategory.category_id)
|
||
.where(ServiceCategory.service_id.in_(service_ids))
|
||
.order_by(Category.name)
|
||
).all()
|
||
category_map = {}
|
||
for service_id, category_id, category_name, category_slug in rows:
|
||
service_categories.setdefault(service_id, []).append(
|
||
{
|
||
"id": category_id,
|
||
"name": category_name,
|
||
"slug": category_slug,
|
||
}
|
||
)
|
||
if category_id not in category_map:
|
||
category_map[category_id] = {"id": category_id, "name": category_name, "slug": category_slug}
|
||
categories = sorted(category_map.values(), key=lambda x: x["name"].lower())
|
||
|
||
selected_category_slug = (request.query_params.get("category") or "").strip().lower()
|
||
if selected_category_slug:
|
||
services = [
|
||
svc for svc in services
|
||
if any(cat["slug"] == selected_category_slug for cat in service_categories.get(svc.id, []))
|
||
]
|
||
service_comment_html = {svc.id: format_service_comment(svc.comment) for svc in services}
|
||
|
||
return templates.TemplateResponse(
|
||
"dashboard.html",
|
||
{
|
||
"request": request,
|
||
"user": user,
|
||
"services": services,
|
||
"categories": categories,
|
||
"selected_category_slug": selected_category_slug,
|
||
"service_categories": service_categories,
|
||
"service_comment_html": service_comment_html,
|
||
"csrf_token": request.cookies.get(CSRF_COOKIE, ""),
|
||
"session_notice": session_notice,
|
||
},
|
||
)
|
||
|
||
|
||
@app.get("/admin", response_class=HTMLResponse)
|
||
def admin_page(request: Request, admin: User = Depends(require_admin), db: Session = Depends(get_db)):
|
||
users = db.scalars(select(User).order_by(User.id)).all()
|
||
categories = db.scalars(select(Category).order_by(Category.name)).all()
|
||
services = db.scalars(select(Service).where(Service.type.in_([ServiceType.WEB, ServiceType.RDP])).order_by(Service.id)).all()
|
||
web_services = [s for s in services if s.type == ServiceType.WEB]
|
||
rdp_services = [s for s in services if s.type == ServiceType.RDP]
|
||
service_category_map = {s.id: [] for s in services}
|
||
if services:
|
||
service_rows = db.execute(
|
||
select(ServiceCategory.service_id, ServiceCategory.category_id).where(
|
||
ServiceCategory.service_id.in_([s.id for s in services])
|
||
)
|
||
).all()
|
||
for service_id, category_id in service_rows:
|
||
service_category_map.setdefault(service_id, []).append(category_id)
|
||
acl_rows = db.scalars(select(UserServiceAccess)).all()
|
||
acl = {}
|
||
for row in acl_rows:
|
||
acl.setdefault(row.user_id, []).append(row.service_id)
|
||
for user_id in acl:
|
||
acl[user_id] = sorted(acl[user_id])
|
||
pool_status = {s.id: get_pool_status_for_service(s) for s in services}
|
||
service_health = {}
|
||
for sid, st in pool_status.items():
|
||
service_health[sid] = {
|
||
"health": st["health"],
|
||
"running": st["running"],
|
||
"desired": st["desired"],
|
||
"active_sessions": get_active_sessions_count(db, sid),
|
||
}
|
||
web_pool = get_web_pool_status()
|
||
web_totals = {
|
||
"services": len(web_services),
|
||
"running": web_pool["running"],
|
||
"desired": web_pool["desired"],
|
||
"active_sessions": sum(service_health[s.id]["active_sessions"] for s in web_services),
|
||
}
|
||
recent_sessions = db.execute(
|
||
text(
|
||
"""
|
||
SELECT s.id, u.username, sv.name AS service_name, sv.slug AS service_slug,
|
||
s.status, s.created_at, s.last_access_at
|
||
FROM sessions s
|
||
JOIN users u ON u.id = s.user_id
|
||
JOIN services sv ON sv.id = s.service_id
|
||
WHERE sv.type IN ('WEB','RDP')
|
||
ORDER BY s.created_at DESC
|
||
LIMIT 200
|
||
"""
|
||
)
|
||
).mappings().all()
|
||
open_stats = db.execute(
|
||
text(
|
||
"""
|
||
SELECT u.username, sv.name AS service_name, sv.slug AS service_slug, COUNT(*) AS opens
|
||
FROM sessions s
|
||
JOIN users u ON u.id = s.user_id
|
||
JOIN services sv ON sv.id = s.service_id
|
||
WHERE sv.type IN ('WEB','RDP')
|
||
GROUP BY u.username, sv.name, sv.slug
|
||
ORDER BY opens DESC, u.username ASC
|
||
LIMIT 200
|
||
"""
|
||
)
|
||
).mappings().all()
|
||
cutoff = now_utc() - dt.timedelta(seconds=SESSION_IDLE_SECONDS)
|
||
online_sessions = db.execute(
|
||
text(
|
||
"""
|
||
SELECT s.id, u.username, sv.name AS service_name, sv.slug AS service_slug,
|
||
sv.type AS service_type, s.container_id, s.created_at, s.last_access_at
|
||
FROM sessions s
|
||
JOIN users u ON u.id = s.user_id
|
||
JOIN services sv ON sv.id = s.service_id
|
||
WHERE s.status = 'ACTIVE'
|
||
AND s.last_access_at >= :cutoff
|
||
AND sv.type IN ('WEB','RDP')
|
||
ORDER BY s.last_access_at DESC, s.created_at DESC
|
||
LIMIT 500
|
||
"""
|
||
),
|
||
{"cutoff": cutoff},
|
||
).mappings().all()
|
||
rdp_slots: dict[int, list] = {}
|
||
for svc in rdp_services:
|
||
slots = db.scalars(select(RdpSlot).where(RdpSlot.service_id == svc.id).order_by(RdpSlot.id)).all()
|
||
slot_list = []
|
||
for slot in slots:
|
||
active_sess = db.scalar(
|
||
select(SessionModel).where(
|
||
SessionModel.container_id == f"RDPSLOT:{slot.id}",
|
||
SessionModel.status == SessionStatus.ACTIVE,
|
||
)
|
||
)
|
||
running = False
|
||
if slot.container_name:
|
||
try:
|
||
c = docker_client().containers.get(slot.container_name)
|
||
running = c.status == "running"
|
||
except Exception:
|
||
pass
|
||
occupied_username = None
|
||
if active_sess:
|
||
u = db.get(User, active_sess.user_id)
|
||
occupied_username = u.username if u else f"id={active_sess.user_id}"
|
||
slot_list.append({
|
||
"id": slot.id,
|
||
"rdp_username": slot.rdp_username,
|
||
"container_name": slot.container_name or "",
|
||
"running": running,
|
||
"occupied_username": occupied_username,
|
||
})
|
||
rdp_slots[svc.id] = slot_list
|
||
return templates.TemplateResponse(
|
||
"admin.html",
|
||
{
|
||
"request": request,
|
||
"admin": admin,
|
||
"users": users,
|
||
"web_services": web_services,
|
||
"rdp_services": rdp_services,
|
||
"services": services,
|
||
"categories": categories,
|
||
"service_category_map": service_category_map,
|
||
"acl": acl,
|
||
"pool_status": pool_status,
|
||
"service_health": service_health,
|
||
"web_totals": web_totals,
|
||
"web_pool_size": WEB_POOL_SIZE,
|
||
"web_pool_buffer": WEB_POOL_BUFFER,
|
||
"recent_sessions": recent_sessions,
|
||
"open_stats": open_stats,
|
||
"online_sessions": online_sessions,
|
||
"csrf_token": request.cookies.get(CSRF_COOKIE, ""),
|
||
"max_active_services_per_user": MAX_ACTIVE_SERVICES_PER_USER,
|
||
"rdp_slots": rdp_slots,
|
||
},
|
||
)
|
||
|
||
|
||
|
||
|
||
|
||
@app.get("/yandex_b847b9b35f967fcc.html", include_in_schema=False)
|
||
def yandex_verify():
|
||
from fastapi.responses import HTMLResponse
|
||
return HTMLResponse('''<html>
|
||
<head>
|
||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||
</head>
|
||
<body>Verification: b847b9b35f967fcc</body>
|
||
</html>''')
|
||
|
||
|
||
@app.get("/privacy", include_in_schema=False)
|
||
def privacy_page():
|
||
from fastapi.responses import RedirectResponse
|
||
return RedirectResponse(url="https://www.mont.ru/ru-ru/privacy", status_code=301)
|
||
|
||
|
||
|
||
@app.post("/api/telegram-webhook", include_in_schema=False)
|
||
async def telegram_webhook(request: Request, db: Session = Depends(get_db)):
|
||
import json as _jw
|
||
import datetime as _dt2
|
||
try:
|
||
data = await request.json()
|
||
except Exception:
|
||
return {"ok": True}
|
||
|
||
cq = data.get("callback_query")
|
||
if not cq:
|
||
return {"ok": True}
|
||
|
||
cq_id = cq["id"]
|
||
cb_data = cq.get("data", "")
|
||
chat_id = cq["message"]["chat"]["id"]
|
||
msg_id = cq["message"]["message_id"]
|
||
|
||
try:
|
||
_tg_api("answerCallbackQuery", {"callback_query_id": cq_id})
|
||
except Exception:
|
||
pass
|
||
|
||
# parse callback_data: a7_ID, a14_ID, a30_ID, a90_ID, r_ID
|
||
import re as _rew
|
||
approve_match = _rew.match(r'^a(\d+)_(.+)$', cb_data)
|
||
reject_match = _rew.match(r'^r_(.+)$', cb_data)
|
||
|
||
if not approve_match and not reject_match:
|
||
return {"ok": True}
|
||
|
||
req_id = approve_match.group(2) if approve_match else reject_match.group(1)
|
||
pending = db.get(PendingAccessRequest, req_id)
|
||
if not pending:
|
||
try:
|
||
_tg_api("editMessageText", {
|
||
"chat_id": chat_id, "message_id": msg_id,
|
||
"text": "Запрос не найден (возможно уже обработан).",
|
||
})
|
||
except Exception:
|
||
pass
|
||
return {"ok": True}
|
||
|
||
if pending.status != "pending":
|
||
try:
|
||
_tg_api("editMessageText", {
|
||
"chat_id": chat_id, "message_id": msg_id,
|
||
"text": f"Запрос уже обработан: {pending.status}.",
|
||
})
|
||
except Exception:
|
||
pass
|
||
return {"ok": True}
|
||
|
||
products = _jw.loads(pending.products_json or "[]")
|
||
portal_url = pending.portal_url or PORTAL_URL
|
||
|
||
if approve_match:
|
||
days = int(approve_match.group(1))
|
||
password = _generate_password()
|
||
username = pending.email
|
||
|
||
# ensure username unique
|
||
if db.scalar(select(User).where(User.username == username)):
|
||
username = pending.email.split("@")[0] + "_" + _secrets.token_hex(3)
|
||
|
||
expires = _dt2.datetime.now(_dt2.timezone.utc) + _dt2.timedelta(days=days)
|
||
parts = pending.name.strip().split(None, 1)
|
||
new_user = User(
|
||
username=username,
|
||
password_hash=hash_password(password),
|
||
expires_at=expires,
|
||
active=True,
|
||
is_admin=False,
|
||
first_name=parts[0] if parts else "",
|
||
last_name=parts[1] if len(parts) > 1 else "",
|
||
)
|
||
db.add(new_user)
|
||
db.flush()
|
||
|
||
# assign requested services
|
||
if products:
|
||
from sqlalchemy import func as _func
|
||
matched = db.scalars(
|
||
select(Service).where(
|
||
func.lower(Service.name).in_([p.lower() for p in products]),
|
||
Service.active == True,
|
||
)
|
||
).all()
|
||
for svc in matched:
|
||
db.add(UserServiceAccess(user_id=new_user.id, service_id=svc.id))
|
||
|
||
db.commit()
|
||
|
||
# send approval email
|
||
products_html = ""
|
||
if products:
|
||
items = "".join(f"<li>{p}</li>" for p in products)
|
||
products_html = f"<p style='margin:16px 0 6px;color:#c8d8ea'><b>Предоставлен доступ к продуктам:</b></p><ul style='margin:0;padding-left:20px;color:#c8d8ea'>{items}</ul>"
|
||
|
||
html_email = f"""<!DOCTYPE html>
|
||
<html lang="ru"><head><meta charset="utf-8"/></head>
|
||
<body style="margin:0;padding:0;background:#0a1929;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif">
|
||
<table width="100%" cellpadding="0" cellspacing="0"><tr><td align="center" style="padding:40px 20px">
|
||
<table width="560" cellpadding="0" cellspacing="0" style="background:linear-gradient(150deg,#0b1a2e,#0d2040);border-radius:16px;overflow:hidden;border:1px solid rgba(255,255,255,0.08)">
|
||
<tr><td style="padding:32px 36px 0">
|
||
<img src="data:image/png;base64,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" alt="MONT" height="40" style="margin-bottom:24px"/><br>
|
||
<h1 style="margin:0 0 8px;font-size:22px;color:#e8f1fb">Доступ к Инфраструктурному полигону MONT</h1>
|
||
<p style="margin:0 0 24px;color:#7a9abd;font-size:14px">Ваш запрос одобрен</p>
|
||
<hr style="border:none;border-top:1px solid rgba(255,255,255,0.08);margin:0 0 24px"/>
|
||
</td></tr>
|
||
<tr><td style="padding:0 36px">
|
||
<p style="color:#c8d8ea;font-size:15px;line-height:1.6;margin:0 0 20px">Здравствуйте, <b>{pending.name}</b>!<br>
|
||
Вам предоставлен доступ к полигону на <b>{days} {'день' if days==1 else 'дня' if days<5 else 'дней'}</b>.</p>
|
||
<table width="100%" cellpadding="12" cellspacing="0" style="background:rgba(255,255,255,0.04);border-radius:10px;border:1px solid rgba(255,255,255,0.07)">
|
||
<tr><td style="color:#7a9abd;font-size:13px;width:40%">Адрес портала</td>
|
||
<td><a href="{portal_url}" style="color:#5b9bd5;font-size:14px">{portal_url}</a></td></tr>
|
||
<tr><td style="color:#7a9abd;font-size:13px;border-top:1px solid rgba(255,255,255,0.06)">Логин</td>
|
||
<td style="border-top:1px solid rgba(255,255,255,0.06);color:#e8f1fb;font-size:14px;font-family:monospace">{username}</td></tr>
|
||
<tr><td style="color:#7a9abd;font-size:13px;border-top:1px solid rgba(255,255,255,0.06)">Пароль</td>
|
||
<td style="border-top:1px solid rgba(255,255,255,0.06);color:#e8f1fb;font-size:14px;font-family:monospace">{password}</td></tr>
|
||
<tr><td style="color:#7a9abd;font-size:13px;border-top:1px solid rgba(255,255,255,0.06)">Доступ до</td>
|
||
<td style="border-top:1px solid rgba(255,255,255,0.06);color:#e8f1fb;font-size:14px">{expires.strftime('%d.%m.%Y')}</td></tr>
|
||
</table>
|
||
{products_html}
|
||
<div style="margin:28px 0">
|
||
<a href="{portal_url}" style="display:inline-block;padding:12px 28px;background:linear-gradient(135deg,#1a5db5,#2d8cf0);color:#fff;text-decoration:none;border-radius:8px;font-size:15px;font-weight:600">Войти в полигон</a>
|
||
</div>
|
||
</td></tr>
|
||
<tr><td style="padding:20px 36px 28px;color:#4a6a8a;font-size:12px;border-top:1px solid rgba(255,255,255,0.06);line-height:1.6">
|
||
Если у вас возникли вопросы, свяжитесь с вашим менеджером MONT или напишите на <a href="mailto:mont@mont.ru" style="color:#5b9bd5">mont@mont.ru</a>
|
||
</td></tr>
|
||
</table></td></tr></table>
|
||
</body></html>"""
|
||
|
||
pending.status = "approved"
|
||
db.commit()
|
||
|
||
try:
|
||
_send_email(pending.email, "Доступ к Инфраструктурному полигону MONT", html_email)
|
||
email_status = "Email отправлен"
|
||
except Exception as ex:
|
||
log_event("email_send_error", error=str(ex))
|
||
email_status = f"Ошибка отправки email: {ex}"
|
||
|
||
try:
|
||
_tg_api("editMessageText", {
|
||
"chat_id": chat_id, "message_id": msg_id,
|
||
"text": (
|
||
f"Одобрено на {days} дней\n"
|
||
f"Логин: `{username}`\n"
|
||
f"Пароль: `{password}`\n"
|
||
f"{email_status}"
|
||
),
|
||
"parse_mode": "Markdown",
|
||
})
|
||
except Exception:
|
||
pass
|
||
|
||
elif reject_match:
|
||
manager_contact = pending.manager if pending.manager else "менеджера MONT"
|
||
html_email = f"""<!DOCTYPE html>
|
||
<html lang="ru"><head><meta charset="utf-8"/></head>
|
||
<body style="margin:0;padding:0;background:#0a1929;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif">
|
||
<table width="100%" cellpadding="0" cellspacing="0"><tr><td align="center" style="padding:40px 20px">
|
||
<table width="560" cellpadding="0" cellspacing="0" style="background:linear-gradient(150deg,#0b1a2e,#0d2040);border-radius:16px;overflow:hidden;border:1px solid rgba(255,255,255,0.08)">
|
||
<tr><td style="padding:32px 36px 0">
|
||
<img src="data:image/png;base64,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" alt="MONT" height="40" style="margin-bottom:24px"/><br>
|
||
<h1 style="margin:0 0 8px;font-size:22px;color:#e8f1fb">Запрос на доступ к полигону MONT</h1>
|
||
<hr style="border:none;border-top:1px solid rgba(255,255,255,0.08);margin:16px 0 24px"/>
|
||
</td></tr>
|
||
<tr><td style="padding:0 36px">
|
||
<p style="color:#c8d8ea;font-size:15px;line-height:1.7;margin:0 0 20px">Здравствуйте, <b>{pending.name}</b>!<br><br>
|
||
К сожалению, на данный момент мы не можем предоставить доступ к полигону.</p>
|
||
<p style="color:#c8d8ea;font-size:15px;line-height:1.7;margin:0 0 24px">
|
||
Для уточнения деталей, пожалуйста, свяжитесь с <b>{manager_contact}</b>.<br>
|
||
Если вы не знаете, кто ваш менеджер, напишите нам на <a href="mailto:mont@mont.ru" style="color:#5b9bd5">mont@mont.ru</a> — мы поможем.</p>
|
||
</td></tr>
|
||
<tr><td style="padding:20px 36px 28px;color:#4a6a8a;font-size:12px;border-top:1px solid rgba(255,255,255,0.06)">
|
||
С уважением, команда MONT
|
||
</td></tr>
|
||
</table></td></tr></table>
|
||
</body></html>"""
|
||
|
||
pending.status = "rejected"
|
||
db.commit()
|
||
|
||
try:
|
||
_send_email(pending.email, "Запрос на доступ к полигону MONT", html_email)
|
||
email_status = "Email отправлен"
|
||
except Exception as ex:
|
||
log_event("email_send_error", error=str(ex))
|
||
email_status = f"Ошибка отправки email: {ex}"
|
||
|
||
try:
|
||
_tg_api("editMessageText", {
|
||
"chat_id": chat_id, "message_id": msg_id,
|
||
"text": f"Отклонено. {email_status}",
|
||
})
|
||
except Exception:
|
||
pass
|
||
|
||
return {"ok": True}
|
||
|
||
@app.get("/robots.txt", include_in_schema=False)
|
||
def robots_txt():
|
||
from fastapi.responses import FileResponse
|
||
return FileResponse("static/robots.txt", media_type="text/plain")
|
||
|
||
|
||
@app.get("/sitemap.xml", include_in_schema=False)
|
||
def sitemap_xml():
|
||
from fastapi.responses import FileResponse
|
||
return FileResponse("static/sitemap.xml", media_type="application/xml")
|
||
|
||
@app.get("/api/public/services-by-category")
|
||
def public_services_by_category(db: Session = Depends(get_db)):
|
||
services = db.execute(
|
||
select(Service).where(Service.active == True).order_by(Service.name)
|
||
).scalars().all()
|
||
categories = db.execute(select(Category).order_by(Category.name)).scalars().all()
|
||
cat_map = {c.id: c.name for c in categories}
|
||
|
||
svc_cats: dict[int, list[str]] = {}
|
||
links = db.execute(select(ServiceCategory)).scalars().all()
|
||
for lnk in links:
|
||
svc_cats.setdefault(lnk.service_id, []).append(cat_map.get(lnk.category_id, ""))
|
||
|
||
result: dict[str, list[dict]] = {"Без категории": []}
|
||
for svc in services:
|
||
cats = svc_cats.get(svc.id, [])
|
||
entry = {"id": svc.id, "name": svc.name}
|
||
if cats:
|
||
for cat in cats:
|
||
result.setdefault(cat, []).append(entry)
|
||
else:
|
||
result["Без категории"].append(entry)
|
||
if not result["Без категории"]:
|
||
del result["Без категории"]
|
||
return result
|
||
|
||
|
||
@app.post("/api/request-access")
|
||
async def request_access(request: Request, db: Session = Depends(get_db)):
|
||
try:
|
||
data = await request.json()
|
||
except Exception:
|
||
raise HTTPException(status_code=400, detail="Invalid JSON")
|
||
|
||
name = str(data.get("name", "")).strip()
|
||
company = str(data.get("company", "")).strip()
|
||
email = str(data.get("email", "")).strip()
|
||
phone = str(data.get("phone", "")).strip()
|
||
manager = str(data.get("manager", "")).strip()
|
||
products = data.get("products", [])
|
||
|
||
import re as _re
|
||
if not name or not company or not email or not phone:
|
||
raise HTTPException(status_code=422, detail="Заполните все обязательные поля")
|
||
if not _re.match(r'^[^\s@]+@[^\s@]+\.[^\s@]+$', email):
|
||
raise HTTPException(status_code=422, detail="Некорректный email")
|
||
if not _re.match(r'^[\+\d][\d\s\-\(\)]{6,18}$', phone):
|
||
raise HTTPException(status_code=422, detail="Некорректный номер телефона")
|
||
|
||
products_text = ""
|
||
if products:
|
||
items = "\n".join(f" • {p}" for p in products)
|
||
products_text = f"\n\n🖥 *Интересующие продукты:*\n{items}"
|
||
|
||
divider = "━━━━━━━━━━━━━━━━━━━━━━"
|
||
manager_text = f"\n🤝 *Менеджер MONT:* {manager}" if manager else ""
|
||
text = (
|
||
f"🔔 *Новый запрос доступа к полигону MONT*\n"
|
||
f"{divider}\n\n"
|
||
f"👤 *Имя:* {name}\n"
|
||
f"🏢 *Компания:* {company}\n"
|
||
f"📧 *Email:* {email}\n"
|
||
f"📱 *Телефон:* {phone}"
|
||
f"{manager_text}"
|
||
f"{products_text}"
|
||
)
|
||
|
||
log_event("ip_headers", xff=request.headers.get("x-forwarded-for","–"), xri=request.headers.get("x-real-ip","–"), client=str(request.client.host if request.client else "–"))
|
||
ip = _get_real_ip(request)
|
||
geo = _get_geo(ip)
|
||
geo_text = ""
|
||
if geo:
|
||
geo_text += "\n📍 *Местоположение:* " + geo
|
||
geo_text += "\n🖥 *IP:* " + ip
|
||
text += geo_text
|
||
|
||
if not TELEGRAM_BOT_TOKEN or not TELEGRAM_CHAT_ID:
|
||
log_event("telegram_not_configured")
|
||
return {"ok": True}
|
||
|
||
# save pending request
|
||
import json as _j2
|
||
req_id = _secrets.token_urlsafe(8)[:12]
|
||
_origin = request.headers.get('origin', '')
|
||
if 'stand.mont.ru' in _origin:
|
||
_req_portal_url = 'https://stand.mont.ru'
|
||
else:
|
||
_req_portal_url = PORTAL_URL
|
||
pending = PendingAccessRequest(
|
||
id=req_id, name=name, company=company, email=email,
|
||
phone=phone, manager=manager,
|
||
products_json=_j2.dumps(products, ensure_ascii=False),
|
||
portal_url=_req_portal_url,
|
||
)
|
||
db.add(pending)
|
||
db.commit()
|
||
|
||
try:
|
||
_tg_api("sendMessage", {
|
||
"chat_id": TELEGRAM_CHAT_ID,
|
||
"text": text,
|
||
"parse_mode": "Markdown",
|
||
"reply_markup": _make_approval_keyboard(req_id),
|
||
})
|
||
except Exception as e:
|
||
log_event("telegram_send_error", error=str(e))
|
||
|
||
return {"ok": True}
|
||
|
||
|
||
|
||
|
||
@app.post("/api/contact")
|
||
async def contact_ruslan(request: Request):
|
||
import re as _re
|
||
try:
|
||
data = await request.json()
|
||
except Exception:
|
||
raise HTTPException(status_code=400, detail="Invalid JSON")
|
||
|
||
name = str(data.get("name", "")).strip()
|
||
email = str(data.get("email", "")).strip()
|
||
phone = str(data.get("phone", "")).strip()
|
||
text = str(data.get("text", "")).strip()
|
||
|
||
if not name or not email or not phone or not text:
|
||
raise HTTPException(status_code=422, detail="Заполните все обязательные поля")
|
||
if not _re.match(r"^[^\s@]+@[^\s@]+\.[^\s@]+$", email):
|
||
raise HTTPException(status_code=422, detail="Некорректный email")
|
||
if not _re.match(r"^[\+\d][\d\s\-\(\)]{6,18}$", phone):
|
||
raise HTTPException(status_code=422, detail="Некорректный номер телефона")
|
||
|
||
divider = "━━━━━━━━━━━━━━━━━━━━━━"
|
||
msg = (
|
||
f"🔔 *Сообщение через форму полигона*\n"
|
||
f"{divider}\n\n"
|
||
f"👤 *Имя:* {name}\n"
|
||
f"📧 *Email:* {email}\n"
|
||
f"📱 *Телефон:* {phone}\n\n"
|
||
f"💬 *Сообщение:*\n{text}"
|
||
)
|
||
|
||
ip = _get_real_ip(request)
|
||
geo = _get_geo(ip)
|
||
geo_text = ""
|
||
if geo:
|
||
geo_text += f"\n📍 *Местоположение:* {geo}"
|
||
geo_text += f"\n🖥 *IP:* {ip}"
|
||
msg += geo_text
|
||
|
||
if not TELEGRAM_BOT_TOKEN or not TELEGRAM_CHAT_ID:
|
||
log_event("telegram_not_configured")
|
||
return {"ok": True}
|
||
|
||
try:
|
||
payload = _json.dumps({
|
||
"chat_id": TELEGRAM_CHAT_ID,
|
||
"text": msg,
|
||
"parse_mode": "Markdown",
|
||
}).encode()
|
||
url = f"{TELEGRAM_API_URL}{TELEGRAM_BOT_TOKEN}/sendMessage"
|
||
req = _urllib_request.Request(url, data=payload, headers={"Content-Type": "application/json"})
|
||
with _urllib_request.urlopen(req, timeout=10) as resp:
|
||
resp.read()
|
||
except Exception as e:
|
||
log_event("telegram_send_error", error=str(e))
|
||
raise HTTPException(status_code=502, detail="Ошибка отправки")
|
||
|
||
return {"ok": True}
|
||
|
||
@app.post("/login")
|
||
def login(
|
||
request: Request,
|
||
username: str = Form(...),
|
||
password: str = Form(...),
|
||
csrf_token: str = Form(...),
|
||
db: Session = Depends(get_db),
|
||
):
|
||
cookie_csrf = request.cookies.get(CSRF_COOKIE)
|
||
if not cookie_csrf or csrf_token != cookie_csrf:
|
||
raise HTTPException(status_code=403, detail="CSRF failed")
|
||
|
||
user = db.scalar(select(User).where(User.username == username))
|
||
if not user or not verify_password(password, user.password_hash):
|
||
csrf = request.cookies.get(CSRF_COOKIE) or secrets.token_urlsafe(24)
|
||
response = templates.TemplateResponse(
|
||
"login.html",
|
||
{
|
||
"request": request,
|
||
"csrf_token": csrf,
|
||
"login_error": "Неверный логин или пароль",
|
||
"session_notice": "",
|
||
},
|
||
status_code=401,
|
||
)
|
||
response.set_cookie(CSRF_COOKIE, csrf, httponly=False, secure=True, samesite="lax", path="/")
|
||
return response
|
||
if not user_is_valid(user):
|
||
csrf = request.cookies.get(CSRF_COOKIE) or secrets.token_urlsafe(24)
|
||
response = templates.TemplateResponse(
|
||
"login.html",
|
||
{
|
||
"request": request,
|
||
"csrf_token": csrf,
|
||
"login_error": "Доступ к сервису приостоновлен, обратитесь к вашему менеджеру",
|
||
},
|
||
status_code=403,
|
||
)
|
||
response.set_cookie(CSRF_COOKIE, csrf, httponly=False, secure=True, samesite="lax", path="/")
|
||
return response
|
||
|
||
response = RedirectResponse(url="/", status_code=303)
|
||
issue_auth_cookie(response, user)
|
||
issue_csrf_cookie(response)
|
||
audit(db, "LOGIN", f"login success: {username}", user_id=user.id)
|
||
return response
|
||
|
||
|
||
@app.post("/logout")
|
||
def logout(request: Request):
|
||
response = RedirectResponse(url="/", status_code=303)
|
||
response.delete_cookie(COOKIE_NAME, path="/")
|
||
response.delete_cookie(CSRF_COOKIE, path="/")
|
||
return response
|
||
|
||
|
||
@app.get("/go/{slug}")
|
||
def go_service(
|
||
slug: str,
|
||
sw: Optional[int] = Query(default=None, ge=320, le=7680),
|
||
sh: Optional[int] = Query(default=None, ge=240, le=4320),
|
||
user: User = Depends(require_user),
|
||
db: Session = Depends(get_db),
|
||
):
|
||
total_started = time.perf_counter()
|
||
phase_ms = {}
|
||
|
||
def _mark(name: str, started: float) -> None:
|
||
phase_ms[name] = int((time.perf_counter() - started) * 1000)
|
||
|
||
def _emit(result: str, **extra) -> None:
|
||
payload = {
|
||
"user_id": user.id,
|
||
"service_slug": slug,
|
||
"result": result,
|
||
"total_ms": int((time.perf_counter() - total_started) * 1000),
|
||
}
|
||
payload.update(phase_ms)
|
||
payload.update(extra)
|
||
log_event("go_service_timing", **payload)
|
||
|
||
log_event("session_open_requested", user_id=user.id, service_slug=slug, sw=sw, sh=sh)
|
||
service = db.scalar(select(Service).where(Service.slug == slug, Service.active == True))
|
||
if not service:
|
||
raise HTTPException(status_code=404, detail="Service not found")
|
||
if service.type == ServiceType.VNC:
|
||
raise HTTPException(status_code=410, detail="VNC services are deprecated")
|
||
if not has_access(db, user.id, service.id):
|
||
raise HTTPException(status_code=403, detail="ACL denied")
|
||
|
||
client_width, client_height = sanitize_client_resolution(sw, sh)
|
||
log_event(
|
||
"session_open_resolution",
|
||
user_id=user.id,
|
||
service_slug=slug,
|
||
sw=sw,
|
||
sh=sh,
|
||
client_width=client_width,
|
||
client_height=client_height,
|
||
)
|
||
|
||
user_lock_started = time.perf_counter()
|
||
try:
|
||
with allocator_lock(db, 92000 + int(user.id), timeout_seconds=GO_USER_LOCK_TIMEOUT_SECONDS):
|
||
_mark("wait_user_lock_ms", user_lock_started)
|
||
|
||
t_existing = time.perf_counter()
|
||
existing_user_session = find_active_session_for_user_service(db, user.id, service.id)
|
||
_mark("check_existing_ms", t_existing)
|
||
if existing_user_session:
|
||
_emit("reuse_session", session_id=existing_user_session.id)
|
||
if existing_user_session.container_id and existing_user_session.container_id.startswith("RDPSLOT:"):
|
||
try:
|
||
_rdp_slot_id = int(existing_user_session.container_id.split(":", 1)[1])
|
||
threading.Thread(target=connect_rdp_slot, args=(_rdp_slot_id,), daemon=True).start()
|
||
except Exception:
|
||
pass
|
||
return RedirectResponse(url=session_redirect_url(existing_user_session), status_code=303)
|
||
|
||
t_limit = time.perf_counter()
|
||
cutoff = now_utc() - dt.timedelta(seconds=SESSION_IDLE_SECONDS)
|
||
active_rows = db.scalars(
|
||
select(SessionModel).where(
|
||
SessionModel.user_id == user.id,
|
||
SessionModel.status == SessionStatus.ACTIVE,
|
||
SessionModel.last_access_at >= cutoff,
|
||
)
|
||
).all()
|
||
active_rows = sorted(active_rows, key=lambda row: row.created_at)
|
||
active_service_ids = {row.service_id for row in active_rows}
|
||
_mark("check_limit_ms", t_limit)
|
||
if service.id not in active_service_ids and len(active_service_ids) >= MAX_ACTIVE_SERVICES_PER_USER:
|
||
oldest = next((row for row in active_rows if row.service_id != service.id), None)
|
||
if oldest:
|
||
t_rotate = time.perf_counter()
|
||
terminate_session_record(db, oldest, SessionStatus.ROTATED, stop_container=True)
|
||
db.commit()
|
||
_mark("rotate_oldest_ms", t_rotate)
|
||
log_event(
|
||
"session_rotated",
|
||
user_id=user.id,
|
||
closed_session_id=oldest.id,
|
||
closed_service_id=oldest.service_id,
|
||
new_service_id=service.id,
|
||
)
|
||
else:
|
||
_emit("max_services_redirect")
|
||
return RedirectResponse(url="/?launch_error=max_services", status_code=303)
|
||
|
||
if service.type == ServiceType.RDP:
|
||
t_rdp_slots = time.perf_counter()
|
||
slots = db.scalars(select(RdpSlot).where(RdpSlot.service_id == service.id)).all()
|
||
_mark("check_rdp_slots_ms", t_rdp_slots)
|
||
if slots:
|
||
session_id = str(uuid.uuid4())
|
||
try:
|
||
with allocator_lock(db, 91003, timeout_seconds=GO_POOL_LOCK_TIMEOUT_SECONDS):
|
||
busy_slot_ids: set[int] = set()
|
||
for row in db.scalars(
|
||
select(SessionModel).where(
|
||
SessionModel.status == SessionStatus.ACTIVE,
|
||
SessionModel.service_id == service.id,
|
||
SessionModel.container_id.like("RDPSLOT:%"),
|
||
)
|
||
).all():
|
||
try:
|
||
busy_slot_ids.add(int(row.container_id.split(":", 1)[1]))
|
||
except Exception:
|
||
pass
|
||
free_slot = next((s for s in slots if s.id not in busy_slot_ids), None)
|
||
if not free_slot:
|
||
_emit("rdp_all_slots_busy")
|
||
raise HTTPException(
|
||
status_code=503,
|
||
detail="Все слоты этого RDP сервиса заняты. Попробуйте позже.",
|
||
)
|
||
session_obj = SessionModel(
|
||
id=session_id,
|
||
user_id=user.id,
|
||
service_id=service.id,
|
||
container_id=f"RDPSLOT:{free_slot.id}",
|
||
status=SessionStatus.ACTIVE,
|
||
created_at=now_utc(),
|
||
last_access_at=now_utc(),
|
||
)
|
||
db.add(session_obj)
|
||
db.commit()
|
||
except LockTimeoutError:
|
||
_emit("rdp_slot_lock_timeout")
|
||
raise HTTPException(status_code=503, detail="Пул RDP занят. Повторите через несколько секунд.")
|
||
log_event("session_created", user_id=user.id, service_slug=service.slug, session_id=session_id, mode="rdp_slot", slot_id=free_slot.id)
|
||
audit(db, "SESSION_CREATE_RDP_SLOT", f"service={service.slug} session={session_id} slot={free_slot.id}", user_id=user.id)
|
||
_emit("session_created_rdp_slot", session_id=session_id, slot_id=free_slot.id)
|
||
threading.Thread(target=connect_rdp_slot, args=(free_slot.id,), daemon=True).start()
|
||
return RedirectResponse(url=f"/s/{session_id}/", status_code=303)
|
||
else:
|
||
# Legacy: no slots configured — exclusive single-session behaviour
|
||
active_owner = find_active_session_for_service(db, service.id)
|
||
if active_owner:
|
||
if active_owner.user_id != user.id:
|
||
_emit("rdp_busy_legacy")
|
||
raise HTTPException(status_code=503, detail="RDP сервис занят. Попробуйте позже.")
|
||
_emit("reuse_rdp_session", session_id=active_owner.id)
|
||
return RedirectResponse(url=session_redirect_url(active_owner), status_code=303)
|
||
|
||
session_id = str(uuid.uuid4())
|
||
if service.type == ServiceType.WEB and WEB_POOL_SIZE > 0:
|
||
try:
|
||
t_pool_lock = time.perf_counter()
|
||
with allocator_lock(db, 91001, timeout_seconds=GO_POOL_LOCK_TIMEOUT_SECONDS):
|
||
_mark("wait_web_pool_lock_ms", t_pool_lock)
|
||
t_ensure = time.perf_counter()
|
||
ensure_web_pool()
|
||
_mark("ensure_web_pool_ms", t_ensure)
|
||
|
||
t_acquire = time.perf_counter()
|
||
slot = acquire_web_pool_slot(db)
|
||
_mark("acquire_web_slot_ms", t_acquire)
|
||
slot_cid = f"WEBPOOLIDX:{slot}"
|
||
|
||
t_dispatch = time.perf_counter()
|
||
terminate_active_slot_sessions(db, slot_cid)
|
||
dispatch_web_pool_target(slot, service, width=client_width, height=client_height)
|
||
_mark("dispatch_web_target_ms", t_dispatch)
|
||
|
||
t_commit = time.perf_counter()
|
||
session_obj = SessionModel(
|
||
id=session_id,
|
||
user_id=user.id,
|
||
service_id=service.id,
|
||
container_id=slot_cid,
|
||
status=SessionStatus.ACTIVE,
|
||
created_at=now_utc(),
|
||
last_access_at=now_utc(),
|
||
)
|
||
db.add(session_obj)
|
||
db.commit()
|
||
_mark("db_commit_ms", t_commit)
|
||
except LockTimeoutError:
|
||
_emit("web_pool_lock_timeout")
|
||
raise HTTPException(status_code=503, detail="Пул WEB занят. Повторите через несколько секунд.")
|
||
except Exception as exc:
|
||
logger.exception("web_pool_dispatch_failed slug=%s user_id=%s", slug, user.id)
|
||
log_event("session_create_failed", level=logging.ERROR, user_id=user.id, service_slug=slug, mode="web_pool", error=str(exc))
|
||
audit(db, "SESSION_CREATE_FAILED", f"slug={slug} err={str(exc)}", user_id=user.id)
|
||
_emit("web_pool_create_failed", error=str(exc))
|
||
raise HTTPException(status_code=502, detail="WEB runtime failed to switch target")
|
||
log_event("session_created", user_id=user.id, service_slug=service.slug, session_id=session_id, mode="web_pool", slot=slot)
|
||
audit(db, "SESSION_CREATE_WEB_POOL", f"service={service.slug} session={session_id} slot={slot}", user_id=user.id)
|
||
_emit("session_created_web_pool", session_id=session_id, slot=slot)
|
||
return RedirectResponse(url=f"/s/{session_id}/", status_code=303)
|
||
|
||
if service_uses_universal_pool(service):
|
||
try:
|
||
t_pool_lock = time.perf_counter()
|
||
with allocator_lock(db, 91002, timeout_seconds=GO_POOL_LOCK_TIMEOUT_SECONDS):
|
||
_mark("wait_universal_pool_lock_ms", t_pool_lock)
|
||
t_ensure = time.perf_counter()
|
||
ensure_universal_pool()
|
||
_mark("ensure_universal_pool_ms", t_ensure)
|
||
|
||
t_acquire = time.perf_counter()
|
||
slot = acquire_universal_slot(db)
|
||
_mark("acquire_universal_slot_ms", t_acquire)
|
||
slot_cid = f"POOLIDX:{slot}"
|
||
|
||
t_dispatch = time.perf_counter()
|
||
terminate_active_slot_sessions(db, slot_cid)
|
||
dispatch_universal_target(slot, service, width=client_width, height=client_height)
|
||
_mark("dispatch_universal_target_ms", t_dispatch)
|
||
|
||
t_commit = time.perf_counter()
|
||
session_obj = SessionModel(
|
||
id=session_id,
|
||
user_id=user.id,
|
||
service_id=service.id,
|
||
container_id=slot_cid,
|
||
status=SessionStatus.ACTIVE,
|
||
created_at=now_utc(),
|
||
last_access_at=now_utc(),
|
||
)
|
||
db.add(session_obj)
|
||
db.commit()
|
||
_mark("db_commit_ms", t_commit)
|
||
except LockTimeoutError:
|
||
_emit("universal_pool_lock_timeout")
|
||
raise HTTPException(status_code=503, detail="Пул RDP занят. Повторите через несколько секунд.")
|
||
except Exception as exc:
|
||
logger.exception("universal_pool_dispatch_failed slug=%s user_id=%s", slug, user.id)
|
||
log_event("session_create_failed", level=logging.ERROR, user_id=user.id, service_slug=slug, mode="universal_pool", error=str(exc))
|
||
audit(db, "SESSION_CREATE_FAILED", f"slug={slug} err={str(exc)}", user_id=user.id)
|
||
_emit("universal_pool_create_failed", error=str(exc))
|
||
raise HTTPException(status_code=502, detail="Universal runtime failed to switch target")
|
||
log_event("session_created", user_id=user.id, service_slug=service.slug, session_id=session_id, mode="universal_pool", slot=slot)
|
||
audit(db, "SESSION_CREATE_POOL", f"service={service.slug} session={session_id} slot={slot}", user_id=user.id)
|
||
_emit("session_created_universal_pool", session_id=session_id, slot=slot)
|
||
return RedirectResponse(url=f"/s/{session_id}/", status_code=303)
|
||
|
||
if service.type == ServiceType.WEB and desired_pool_size(service) > 0:
|
||
t_warm = time.perf_counter()
|
||
ensure_warm_pool(service)
|
||
open_warm_web_url(service, service.target)
|
||
_mark("warm_pool_prepare_ms", t_warm)
|
||
|
||
t_commit = time.perf_counter()
|
||
session_obj = SessionModel(
|
||
id=session_id,
|
||
user_id=user.id,
|
||
service_id=service.id,
|
||
container_id=f"POOL:{service.slug}",
|
||
status=SessionStatus.ACTIVE,
|
||
created_at=now_utc(),
|
||
last_access_at=now_utc(),
|
||
)
|
||
db.add(session_obj)
|
||
db.commit()
|
||
_mark("db_commit_ms", t_commit)
|
||
log_event("session_created", user_id=user.id, service_slug=service.slug, session_id=session_id, mode="warm_pool")
|
||
audit(db, "SESSION_CREATE_POOL", f"service={service.slug} session={session_id}", user_id=user.id)
|
||
_emit("session_created_warm_pool", session_id=session_id)
|
||
return RedirectResponse(url=f"/s/{session_id}/", status_code=303)
|
||
|
||
try:
|
||
t_create = time.perf_counter()
|
||
container_id = create_runtime_container(service, session_id)
|
||
_mark("create_runtime_container_ms", t_create)
|
||
except Exception as exc:
|
||
logger.exception("session_container_create_failed slug=%s user_id=%s", slug, user.id)
|
||
log_event("session_create_failed", level=logging.ERROR, user_id=user.id, service_slug=slug, mode="single_runtime", error=str(exc))
|
||
audit(db, "SESSION_CREATE_FAILED", f"slug={slug} err={str(exc)}", user_id=user.id)
|
||
_emit("single_runtime_create_failed", error=str(exc))
|
||
raise HTTPException(status_code=502, detail="Session runtime failed to start")
|
||
|
||
t_commit = time.perf_counter()
|
||
session_obj = SessionModel(
|
||
id=session_id,
|
||
user_id=user.id,
|
||
service_id=service.id,
|
||
container_id=container_id,
|
||
status=SessionStatus.ACTIVE,
|
||
created_at=now_utc(),
|
||
last_access_at=now_utc(),
|
||
)
|
||
db.add(session_obj)
|
||
db.commit()
|
||
_mark("db_commit_ms", t_commit)
|
||
log_event("session_created", user_id=user.id, service_slug=service.slug, session_id=session_id, mode="single_runtime", container_id=container_id)
|
||
|
||
audit(db, "SESSION_CREATE", f"service={service.slug} session={session_id}", user_id=user.id)
|
||
t_wait = time.perf_counter()
|
||
ready = wait_for_session_route(session_id)
|
||
_mark("wait_session_route_ms", t_wait)
|
||
log_event("session_route_ready", session_id=session_id, ready=ready)
|
||
_emit("session_created_single_runtime", session_id=session_id, ready=ready)
|
||
return RedirectResponse(url=f"/s/{session_id}/", status_code=303)
|
||
except LockTimeoutError:
|
||
_emit("user_lock_timeout")
|
||
raise HTTPException(status_code=429, detail="Слишком много параллельных запусков. Повторите через несколько секунд.")
|
||
|
||
|
||
@app.get("/svc/{slug}/", response_class=HTMLResponse)
|
||
def service_wait_page(slug: str, request: Request, user: User = Depends(require_user), db: Session = Depends(get_db)):
|
||
service = db.scalar(select(Service).where(Service.slug == slug, Service.active == True))
|
||
if not service:
|
||
raise HTTPException(status_code=404, detail="Service not found")
|
||
if not has_access(db, user.id, service.id):
|
||
raise HTTPException(status_code=403, detail="ACL denied")
|
||
return HTMLResponse(
|
||
content="""
|
||
<!doctype html>
|
||
<html>
|
||
<head>
|
||
<meta charset='utf-8'>
|
||
<title>Service Starting</title>
|
||
<style>
|
||
body { font-family: sans-serif; background: #f4f6f8; display: grid; place-items: center; height: 100vh; margin: 0; color:#1b3145; }
|
||
.card { background: #fff; padding: 1rem 1.2rem; border-radius: 10px; box-shadow: 0 8px 20px rgba(0,0,0,.08); min-width: 340px; }
|
||
.title { font-weight: 700; margin-bottom: 0.5rem; }
|
||
.state { margin-bottom: 0.6rem; }
|
||
ul { margin: 0; padding-left: 1.1rem; }
|
||
li { margin: 0.2rem 0; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="card">
|
||
<div class="title">Сервис запускается</div>
|
||
<div class="state" id="state">Проверка...</div>
|
||
<ul id="steps"></ul>
|
||
</div>
|
||
<script>
|
||
const slug = window.location.pathname.replace(/^\\/svc\\//, '').replace(/\\/$/, '');
|
||
async function tick() {
|
||
const r = await fetch(`/api/services/${slug}/status`, {credentials:'include'});
|
||
if (!r.ok) return;
|
||
const data = await r.json();
|
||
document.getElementById('state').textContent = data.message || 'Запуск...';
|
||
const ul = document.getElementById('steps');
|
||
ul.innerHTML = '';
|
||
(data.steps || []).forEach((x) => {
|
||
const li = document.createElement('li');
|
||
li.textContent = x;
|
||
ul.appendChild(li);
|
||
});
|
||
if (data.ready) window.location.replace(`/svc/${slug}/`);
|
||
}
|
||
setInterval(tick, 1000);
|
||
tick();
|
||
</script>
|
||
</body>
|
||
</html>
|
||
""".strip(),
|
||
status_code=200,
|
||
)
|
||
|
||
|
||
@app.get("/s/{session_id}/", response_class=HTMLResponse)
|
||
def session_wait_page(session_id: str, request: Request, user: User = Depends(require_user), db: Session = Depends(get_db)):
|
||
sess = db.get(SessionModel, session_id)
|
||
if not sess or sess.user_id != user.id:
|
||
raise HTTPException(status_code=404, detail="Session not found")
|
||
if sess.status != SessionStatus.ACTIVE:
|
||
raise HTTPException(status_code=410, detail="Session is not active")
|
||
service = db.get(Service, sess.service_id)
|
||
service_title = service.name if service else "Сервис"
|
||
is_rdp = service and service.type == ServiceType.RDP
|
||
label = "Ожидайте..." if is_rdp else "Сессия запускается..."
|
||
redirect_target = session_redirect_url(sess)
|
||
return HTMLResponse(
|
||
content=f"""
|
||
<!doctype html>
|
||
<html>
|
||
<head>
|
||
<meta charset='utf-8'>
|
||
<title>{service_title}</title>
|
||
<style>
|
||
*{{box-sizing:border-box}}
|
||
body{{font-family:sans-serif;background:#0f1720;display:grid;place-items:center;height:100vh;margin:0;color:#dce8f5}}
|
||
.card{{background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.12);padding:1.6rem 2rem;border-radius:14px;
|
||
box-shadow:0 12px 32px rgba(0,0,0,.4);min-width:320px;max-width:440px;text-align:center}}
|
||
.spinner{{width:48px;height:48px;border:4px solid rgba(220,232,245,.15);border-top-color:#2a8cd6;
|
||
border-radius:50%;animation:spin .9s linear infinite;margin:0 auto 1.2rem}}
|
||
@keyframes spin{{to{{transform:rotate(360deg)}}}}
|
||
.title{{font-size:1.15rem;font-weight:700;margin-bottom:.5rem;color:#fff}}
|
||
.state{{font-size:.9rem;color:#a0b8cc;margin-bottom:.8rem;min-height:1.2em}}
|
||
ul{{margin:0;padding:0;list-style:none;font-size:.82rem;color:#7a99b0;text-align:left}}
|
||
li::before{{content:"· ";color:#2a8cd6}}
|
||
li+li{{margin-top:.2rem}}
|
||
.sid{{display:block;margin-top:1.2rem;font-size:.7rem;color:rgba(160,184,204,.4);word-break:break-all}}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="card">
|
||
<div class="spinner"></div>
|
||
<div class="title">{label}</div>
|
||
<div class="state" id="state">Проверка...</div>
|
||
<ul id="steps"></ul>
|
||
<span class="sid">{session_id}</span>
|
||
</div>
|
||
<script>
|
||
const sessionId = "{session_id}";
|
||
async function tick() {{
|
||
const r = await fetch(`/api/sessions/${{sessionId}}/status`, {{credentials:'include'}});
|
||
if (!r.ok) return;
|
||
const data = await r.json();
|
||
document.getElementById('state').textContent = data.message || 'Запуск...';
|
||
const ul = document.getElementById('steps');
|
||
ul.innerHTML = '';
|
||
(data.steps || []).forEach((x) => {{
|
||
const li = document.createElement('li');
|
||
li.textContent = x;
|
||
ul.appendChild(li);
|
||
}});
|
||
if (data.ready) window.location.replace(data.redirect_url || "{redirect_target}");
|
||
}}
|
||
setInterval(tick, 1000);
|
||
tick();
|
||
</script>
|
||
</body>
|
||
</html>
|
||
""".strip(),
|
||
status_code=200,
|
||
)
|
||
|
||
|
||
@app.get("/s/{session_id}/view", response_class=HTMLResponse)
|
||
def session_view_page(session_id: str, request: Request, user: User = Depends(require_user), db: Session = Depends(get_db)):
|
||
sess = db.get(SessionModel, session_id)
|
||
if not sess or sess.user_id != user.id:
|
||
raise HTTPException(status_code=404, detail="Session not found")
|
||
if sess.status != SessionStatus.ACTIVE:
|
||
raise HTTPException(status_code=410, detail="Session is not active")
|
||
service = db.get(Service, sess.service_id)
|
||
if not service:
|
||
raise HTTPException(status_code=404, detail="Service not found")
|
||
iframe_src = None
|
||
if sess.container_id and sess.container_id.startswith("POOL:"):
|
||
iframe_src = f"/svc/{service.slug}/?sid={session_id}"
|
||
elif sess.container_id and sess.container_id.startswith("WEBPOOLIDX:"):
|
||
try:
|
||
slot = int(sess.container_id.split(":", 1)[1])
|
||
iframe_src = f"/w/{slot}/?sid={session_id}"
|
||
except Exception:
|
||
iframe_src = None
|
||
elif sess.container_id and sess.container_id.startswith("POOLIDX:"):
|
||
try:
|
||
slot = int(sess.container_id.split(":", 1)[1])
|
||
iframe_src = f"/u/{slot}/?sid={session_id}"
|
||
except Exception:
|
||
iframe_src = None
|
||
elif sess.container_id and sess.container_id.startswith("RDPSLOT:"):
|
||
try:
|
||
slot = int(sess.container_id.split(":", 1)[1])
|
||
iframe_src = f"/rdp/{slot}/?sid={session_id}"
|
||
except Exception:
|
||
iframe_src = None
|
||
if iframe_src:
|
||
creds_html = ""
|
||
if service.type != ServiceType.RDP and (service.svc_login or service.svc_password):
|
||
rows = ""
|
||
if service.svc_login:
|
||
login_esc = service.svc_login.replace('"', '"').replace('<', '<')
|
||
rows += f'''<div class="cr-row"><span class="cr-label">Логин</span><span class="cr-val">{login_esc}</span></div>'''
|
||
if service.svc_password:
|
||
pass_esc = service.svc_password.replace('"', '"').replace('<', '<')
|
||
rows += f'''<div class="cr-row"><span class="cr-label">Пароль</span><span class="cr-val cr-masked">{pass_esc}</span></div>'''
|
||
if service.svc_cred_hint:
|
||
hint_esc = service.svc_cred_hint.replace('<', '<')
|
||
rows += f'''<p class="cr-hint">{hint_esc}</p>'''
|
||
creds_html = f'''
|
||
<div class="creds-panel" id="creds-panel">
|
||
<button class="creds-close" id="creds-close" title="Закрыть">✕</button>
|
||
{rows}
|
||
</div>
|
||
<script>
|
||
document.getElementById("creds-close").onclick = function() {{
|
||
document.getElementById("creds-panel").style.display = "none";
|
||
}};
|
||
|
||
</script>'''
|
||
|
||
return HTMLResponse(
|
||
content=f"""
|
||
<!doctype html>
|
||
<html>
|
||
<head>
|
||
<meta charset='utf-8'>
|
||
<title>{service.name}</title>
|
||
<style>
|
||
html,body,iframe {{ margin:0; width:100%; height:100%; border:0; background:#0f1720; }}
|
||
.creds-panel{{
|
||
position:fixed;right:16px;top:16px;z-index:999;
|
||
background:linear-gradient(180deg,rgba(15,24,36,.88),rgba(9,14,22,.94));
|
||
border:1px solid rgba(255,255,255,.22);backdrop-filter:blur(6px);
|
||
box-shadow:0 10px 28px rgba(0,0,0,.4);padding:10px 12px 11px;border-radius:14px;
|
||
min-width:220px;max-width:320px;
|
||
}}
|
||
.creds-close{{
|
||
position:absolute;top:6px;right:8px;background:none;border:none;
|
||
color:rgba(255,255,255,.55);font-size:14px;cursor:pointer;line-height:1;padding:2px 4px;
|
||
}}
|
||
.creds-close:hover{{color:#fff}}
|
||
.cr-row{{display:flex;align-items:center;gap:6px;margin-bottom:5px;}}
|
||
.cr-label{{font:600 11px/1 sans-serif;text-transform:uppercase;letter-spacing:.04em;
|
||
color:rgba(180,210,240,.7);min-width:46px;flex-shrink:0;}}
|
||
.cr-val{{font:600 13px/1 monospace;color:#dce8f5;flex:1;overflow:hidden;
|
||
text-overflow:ellipsis;white-space:nowrap;}}
|
||
.cr-masked{{letter-spacing:.1em;font-size:14px;}}
|
||
|
||
.cr-hint{{margin:4px 0 0;font:400 11px/1.35 sans-serif;color:rgba(180,210,240,.65);}}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<iframe src="{iframe_src}" allow="clipboard-read; clipboard-write"></iframe>{creds_html}
|
||
</body>
|
||
</html>
|
||
""".strip()
|
||
)
|
||
return RedirectResponse(url=f"/s/{session_id}/", status_code=303)
|
||
|
||
|
||
@app.post("/api/sessions/{session_id}/touch")
|
||
def touch_session(session_id: str, user: User = Depends(require_user), db: Session = Depends(get_db)):
|
||
sess = db.get(SessionModel, session_id)
|
||
if not sess or sess.user_id != user.id:
|
||
raise HTTPException(status_code=404, detail="Session not found")
|
||
if sess.status != SessionStatus.ACTIVE:
|
||
reason = session_closed_reason(sess, db)
|
||
log_event(
|
||
"session_touch_rejected",
|
||
level=logging.WARNING,
|
||
session_id=session_id,
|
||
user_id=user.id,
|
||
status=sess.status.value,
|
||
reason=reason,
|
||
)
|
||
return JSONResponse(
|
||
status_code=410,
|
||
content={
|
||
"ok": False,
|
||
"reason": reason,
|
||
"status": sess.status.value,
|
||
},
|
||
)
|
||
sess.last_access_at = now_utc()
|
||
db.commit()
|
||
return {"ok": True}
|
||
|
||
|
||
@app.post("/api/sessions/{session_id}/close")
|
||
def close_session(session_id: str, user: User = Depends(require_user), db: Session = Depends(get_db)):
|
||
sess = db.get(SessionModel, session_id)
|
||
if not sess or sess.user_id != user.id:
|
||
raise HTTPException(status_code=404, detail="Session not found")
|
||
if sess.status != SessionStatus.ACTIVE:
|
||
log_event(
|
||
"session_close_already_closed",
|
||
session_id=session_id,
|
||
user_id=user.id,
|
||
status=sess.status.value,
|
||
reason=session_closed_reason(sess, db),
|
||
)
|
||
return {"ok": True, "status": sess.status.value}
|
||
terminate_session_record(db, sess, SessionStatus.TERMINATED, stop_container=True)
|
||
db.commit()
|
||
log_event("session_closed_by_user", session_id=session_id, user_id=user.id)
|
||
return {"ok": True, "status": "TERMINATED"}
|
||
|
||
|
||
@app.get("/api/services/{slug}/status")
|
||
def service_status(slug: str, user: User = Depends(require_user), db: Session = Depends(get_db)):
|
||
service = db.scalar(select(Service).where(Service.slug == slug, Service.active == True))
|
||
if not service:
|
||
raise HTTPException(status_code=404, detail="Service not found")
|
||
if service.type == ServiceType.VNC:
|
||
raise HTTPException(status_code=410, detail="VNC services are deprecated")
|
||
if not has_access(db, user.id, service.id):
|
||
raise HTTPException(status_code=403, detail="ACL denied")
|
||
pool = get_pool_status_for_service(service)
|
||
route_ok = route_ready(f"/svc/{slug}/")
|
||
ready = route_ok and (pool["running"] > 0 if desired_pool_size(service) > 0 else True)
|
||
steps = [
|
||
f"ACL: OK ({user.username})",
|
||
f"Пул: {pool['running']} / {pool['desired']}",
|
||
f"Маршрут /svc/{slug}/: {'OK' if route_ok else 'ожидание'}",
|
||
]
|
||
return {
|
||
"ready": ready,
|
||
"message": "Готово, открываем..." if ready else "Поднимаем контейнер и маршрут...",
|
||
"steps": steps,
|
||
}
|
||
|
||
|
||
@app.get("/api/sessions/{session_id}/status")
|
||
def session_status(session_id: str, user: User = Depends(require_user), db: Session = Depends(get_db)):
|
||
sess = db.get(SessionModel, session_id)
|
||
if not sess or sess.user_id != user.id:
|
||
raise HTTPException(status_code=404, detail="Session not found")
|
||
if sess.status != SessionStatus.ACTIVE:
|
||
raise HTTPException(status_code=410, detail="Session is not active")
|
||
service = db.get(Service, sess.service_id)
|
||
pooled_web = bool(sess.container_id and sess.container_id.startswith("POOL:") and service and service.type == ServiceType.WEB)
|
||
web_pool_idx = None
|
||
universal_pool_idx = None
|
||
if sess.container_id and sess.container_id.startswith("WEBPOOLIDX:"):
|
||
try:
|
||
web_pool_idx = int(sess.container_id.split(":", 1)[1])
|
||
except Exception:
|
||
web_pool_idx = None
|
||
if sess.container_id and sess.container_id.startswith("POOLIDX:"):
|
||
try:
|
||
universal_pool_idx = int(sess.container_id.split(":", 1)[1])
|
||
except Exception:
|
||
universal_pool_idx = None
|
||
pooled_rdp = bool(sess.container_id and sess.container_id.startswith("POOL:") and service and service.type == ServiceType.RDP)
|
||
rdp_slot_idx = None
|
||
if sess.container_id and sess.container_id.startswith("RDPSLOT:"):
|
||
try:
|
||
rdp_slot_idx = int(sess.container_id.split(":", 1)[1])
|
||
except Exception:
|
||
rdp_slot_idx = None
|
||
if pooled_web and service:
|
||
route_path = f"/svc/{service.slug}/"
|
||
elif pooled_rdp and service:
|
||
route_path = f"/svc/{service.slug}/"
|
||
elif rdp_slot_idx is not None:
|
||
route_path = f"/rdp/{rdp_slot_idx}/"
|
||
else:
|
||
route_path = f"/s/{session_id}/"
|
||
if web_pool_idx is not None:
|
||
route_path = f"/w/{web_pool_idx}/"
|
||
if universal_pool_idx is not None:
|
||
route_path = f"/u/{universal_pool_idx}/"
|
||
route_ok = route_ready(route_path)
|
||
running = container_running(sess.container_id)
|
||
ready = running and route_ok
|
||
steps = [
|
||
f"Контейнер: {'running' if running else 'starting'}",
|
||
f"Маршрут {route_path}: {'OK' if route_ok else 'ожидание'}",
|
||
]
|
||
payload = {
|
||
"ready": ready,
|
||
"message": "Готово, открываем..." if ready else "Запуск сессии...",
|
||
"steps": steps,
|
||
}
|
||
if pooled_web or pooled_rdp:
|
||
payload["redirect_url"] = f"/s/{session_id}/view"
|
||
if web_pool_idx is not None:
|
||
payload["redirect_url"] = f"/s/{session_id}/view"
|
||
if universal_pool_idx is not None:
|
||
payload["redirect_url"] = f"/s/{session_id}/view"
|
||
if rdp_slot_idx is not None:
|
||
payload["redirect_url"] = f"/s/{session_id}/view"
|
||
return payload
|
||
|
||
|
||
@app.post("/api/admin/services")
|
||
def create_service(payload: dict, request: Request, _: User = Depends(require_admin), db: Session = Depends(get_db)):
|
||
validate_csrf(request)
|
||
service_type = ServiceType(payload["type"])
|
||
if service_type == ServiceType.VNC:
|
||
raise HTTPException(status_code=400, detail="VNC services are no longer supported")
|
||
target = payload["target"]
|
||
if service_type == ServiceType.WEB:
|
||
target = normalize_web_target(target)
|
||
elif service_type == ServiceType.RDP:
|
||
parse_rdp_target(target)
|
||
service = Service(
|
||
name=payload["name"],
|
||
slug=payload["slug"],
|
||
type=service_type,
|
||
target=target,
|
||
comment=payload.get("comment", ""),
|
||
svc_login=payload.get("svc_login", ""),
|
||
svc_password=payload.get("svc_password", ""),
|
||
svc_cred_hint=payload.get("svc_cred_hint", ""),
|
||
active=payload.get("active", True),
|
||
warm_pool_size=max(0, int(payload.get("warm_pool_size", 0))),
|
||
)
|
||
db.add(service)
|
||
db.flush()
|
||
set_service_categories(db, service.id, payload.get("category_ids", []))
|
||
db.commit()
|
||
if service.type == ServiceType.WEB and WEB_POOL_SIZE <= 0:
|
||
ensure_warm_pool(service)
|
||
elif service_uses_universal_pool(service):
|
||
ensure_universal_pool()
|
||
return {"id": service.id}
|
||
|
||
|
||
@app.get("/api/admin/services/{service_id}/containers/status")
|
||
def service_containers_status(service_id: int, _: User = Depends(require_admin), db: Session = Depends(get_db)):
|
||
service = db.get(Service, service_id)
|
||
if not service:
|
||
raise HTTPException(status_code=404, detail="Service not found")
|
||
out = get_pool_detailed_status(service)
|
||
out["active_sessions"] = get_active_sessions_count(db, service.id)
|
||
return out
|
||
|
||
|
||
@app.post("/api/admin/services/{service_id}/icon")
|
||
async def upload_service_icon(
|
||
service_id: int,
|
||
request: Request,
|
||
file: UploadFile = File(...),
|
||
_: User = Depends(require_admin),
|
||
db: Session = Depends(get_db),
|
||
):
|
||
validate_csrf(request)
|
||
service = db.get(Service, service_id)
|
||
if not service:
|
||
raise HTTPException(status_code=404, detail="Service not found")
|
||
new_path = await store_service_icon(service, file)
|
||
old_path = service.icon_path
|
||
service.icon_path = new_path
|
||
db.commit()
|
||
if old_path and old_path != new_path:
|
||
remove_icon_file(old_path)
|
||
return {"ok": True, "icon_path": new_path}
|
||
|
||
|
||
@app.delete("/api/admin/services/{service_id}/icon")
|
||
def delete_service_icon(service_id: int, request: Request, _: User = Depends(require_admin), db: Session = Depends(get_db)):
|
||
validate_csrf(request)
|
||
service = db.get(Service, service_id)
|
||
if not service:
|
||
raise HTTPException(status_code=404, detail="Service not found")
|
||
old_path = service.icon_path
|
||
service.icon_path = ""
|
||
db.commit()
|
||
remove_icon_file(old_path)
|
||
return {"ok": True}
|
||
|
||
|
||
@app.put("/api/admin/services/{service_id}")
|
||
def edit_service(service_id: int, payload: dict, request: Request, _: User = Depends(require_admin), db: Session = Depends(get_db)):
|
||
validate_csrf(request)
|
||
service = db.get(Service, service_id)
|
||
if not service:
|
||
raise HTTPException(status_code=404, detail="Service not found")
|
||
for key in ["name", "slug", "target", "active", "comment", "svc_login", "svc_password", "svc_cred_hint"]:
|
||
if key in payload:
|
||
setattr(service, key, payload[key])
|
||
if "type" in payload:
|
||
service.type = ServiceType(payload["type"])
|
||
if service.type == ServiceType.VNC:
|
||
raise HTTPException(status_code=400, detail="VNC services are no longer supported")
|
||
if service.type == ServiceType.WEB:
|
||
service.target = normalize_web_target(service.target)
|
||
elif service.type == ServiceType.RDP:
|
||
parse_rdp_target(service.target)
|
||
if "warm_pool_size" in payload:
|
||
service.warm_pool_size = max(0, int(payload["warm_pool_size"]))
|
||
if "category_ids" in payload:
|
||
set_service_categories(db, service.id, payload.get("category_ids", []))
|
||
db.commit()
|
||
if service.type == ServiceType.WEB:
|
||
if WEB_POOL_SIZE <= 0:
|
||
ensure_warm_pool(service)
|
||
open_warm_web_url(service, service.target)
|
||
elif service_uses_universal_pool(service):
|
||
ensure_universal_pool()
|
||
return {"ok": True}
|
||
|
||
|
||
@app.delete("/api/admin/services/{service_id}")
|
||
def delete_service(service_id: int, request: Request, _: User = Depends(require_admin), db: Session = Depends(get_db)):
|
||
validate_csrf(request)
|
||
service = db.get(Service, service_id)
|
||
if not service:
|
||
raise HTTPException(status_code=404, detail="Service not found")
|
||
if service.type == ServiceType.WEB and WEB_POOL_SIZE <= 0:
|
||
ensure_warm_pool(service, 0)
|
||
remove_icon_file(service.icon_path)
|
||
db.delete(service)
|
||
db.commit()
|
||
return {"ok": True}
|
||
|
||
|
||
@app.post("/api/admin/services/{service_id}/prewarm")
|
||
def prewarm_now(service_id: int, request: Request, _: User = Depends(require_admin), db: Session = Depends(get_db)):
|
||
validate_csrf(request)
|
||
service = db.get(Service, service_id)
|
||
if not service:
|
||
raise HTTPException(status_code=404, detail="Service not found")
|
||
if service.type == ServiceType.WEB:
|
||
ensure_web_pool()
|
||
return {"ok": True, "pool": get_web_pool_status()}
|
||
if service_uses_universal_pool(service):
|
||
ensure_universal_pool()
|
||
return {"ok": True, "pool": get_universal_pool_status()}
|
||
if service.type == ServiceType.RDP:
|
||
return {"ok": True, "pool": get_pool_status_for_service(service), "message": "RDP запускается on-demand"}
|
||
ensure_warm_pool(service)
|
||
return {"ok": True, "pool": get_pool_status_for_service(service)}
|
||
|
||
|
||
@app.post("/api/admin/services/{service_id}/rdp-slots")
|
||
def create_rdp_slot(service_id: int, payload: dict, request: Request, _: User = Depends(require_admin), db: Session = Depends(get_db)):
|
||
validate_csrf(request)
|
||
service = db.get(Service, service_id)
|
||
if not service or service.type != ServiceType.RDP:
|
||
raise HTTPException(status_code=404, detail="RDP service not found")
|
||
rdp_username = (payload.get("rdp_username") or "").strip()
|
||
rdp_password = (payload.get("rdp_password") or "").strip()
|
||
if not rdp_username:
|
||
raise HTTPException(status_code=400, detail="rdp_username is required")
|
||
slot = RdpSlot(service_id=service_id, rdp_username=rdp_username, rdp_password=rdp_password)
|
||
db.add(slot)
|
||
db.flush()
|
||
try:
|
||
container_name = start_rdp_slot_container(slot, service)
|
||
slot.container_name = container_name
|
||
except Exception as exc:
|
||
logger.exception("rdp_slot_container_start_failed service_id=%s", service_id)
|
||
raise HTTPException(status_code=502, detail=f"Контейнер не запустился: {exc}")
|
||
db.commit()
|
||
audit(db, "RDP_SLOT_CREATE", f"service={service.slug} slot={slot.id} user={rdp_username}", user_id=None)
|
||
return {"ok": True, "slot_id": slot.id, "container_name": slot.container_name}
|
||
|
||
|
||
@app.delete("/api/admin/rdp-slots/{slot_id}")
|
||
def delete_rdp_slot(slot_id: int, request: Request, _: User = Depends(require_admin), db: Session = Depends(get_db)):
|
||
validate_csrf(request)
|
||
slot = db.get(RdpSlot, slot_id)
|
||
if not slot:
|
||
raise HTTPException(status_code=404, detail="Slot not found")
|
||
container_name = slot.container_name
|
||
db.delete(slot)
|
||
db.commit()
|
||
if container_name:
|
||
threading.Thread(target=stop_rdp_slot_container, args=(container_name,), daemon=True).start()
|
||
return {"ok": True}
|
||
|
||
|
||
@app.post("/api/admin/categories")
|
||
def create_category(payload: dict, request: Request, _: User = Depends(require_admin), db: Session = Depends(get_db)):
|
||
validate_csrf(request)
|
||
name = (payload.get("name") or "").strip()
|
||
slug = (payload.get("slug") or "").strip().lower().replace(" ", "-")
|
||
if not name:
|
||
raise HTTPException(status_code=400, detail="Category name is required")
|
||
if not slug:
|
||
raise HTTPException(status_code=400, detail="Category slug is required")
|
||
exists = db.scalar(select(Category).where((Category.name == name) | (Category.slug == slug)))
|
||
if exists:
|
||
raise HTTPException(status_code=409, detail="Category already exists")
|
||
category = Category(name=name, slug=slug)
|
||
db.add(category)
|
||
db.commit()
|
||
return {"id": category.id}
|
||
|
||
|
||
@app.delete("/api/admin/categories/{category_id}")
|
||
def delete_category(category_id: int, request: Request, _: User = Depends(require_admin), db: Session = Depends(get_db)):
|
||
validate_csrf(request)
|
||
category = db.get(Category, category_id)
|
||
if not category:
|
||
raise HTTPException(status_code=404, detail="Category not found")
|
||
db.delete(category)
|
||
db.commit()
|
||
return {"ok": True}
|
||
|
||
|
||
@app.put("/api/admin/web-pool-size")
|
||
def update_web_pool_size(payload: dict, request: Request, _: User = Depends(require_admin)):
|
||
validate_csrf(request)
|
||
global WEB_POOL_SIZE
|
||
value = max(0, int(payload.get("size", WEB_POOL_SIZE)))
|
||
WEB_POOL_SIZE = value
|
||
ensure_web_pool()
|
||
return {"ok": True, "size": WEB_POOL_SIZE, "pool": get_web_pool_status()}
|
||
|
||
|
||
@app.post("/api/admin/users")
|
||
def create_user(payload: dict, request: Request, _: User = Depends(require_admin), db: Session = Depends(get_db)):
|
||
validate_csrf(request)
|
||
expires_at = dt.datetime.fromisoformat(payload["expires_at"])
|
||
user = User(
|
||
username=payload["username"],
|
||
password_hash=hash_password(payload["password"]),
|
||
expires_at=expires_at,
|
||
active=payload.get("active", True),
|
||
is_admin=payload.get("is_admin", False),
|
||
first_name=payload.get("first_name", ""),
|
||
last_name=payload.get("last_name", ""),
|
||
)
|
||
db.add(user)
|
||
db.commit()
|
||
return {"id": user.id}
|
||
|
||
|
||
@app.put("/api/admin/users/{user_id}")
|
||
def edit_user(user_id: int, payload: dict, request: Request, _: User = Depends(require_admin), db: Session = Depends(get_db)):
|
||
validate_csrf(request)
|
||
user = db.get(User, user_id)
|
||
if not user:
|
||
raise HTTPException(status_code=404, detail="User not found")
|
||
for key in ["username", "active", "is_admin", "first_name", "last_name"]:
|
||
if key in payload:
|
||
setattr(user, key, payload[key])
|
||
if "password" in payload and payload["password"]:
|
||
user.password_hash = hash_password(payload["password"])
|
||
if "expires_at" in payload:
|
||
user.expires_at = dt.datetime.fromisoformat(payload["expires_at"])
|
||
db.commit()
|
||
return {"ok": True}
|
||
|
||
|
||
@app.delete("/api/admin/users/{user_id}")
|
||
def delete_user(user_id: int, request: Request, admin: User = Depends(require_admin), db: Session = Depends(get_db)):
|
||
validate_csrf(request)
|
||
user = db.get(User, user_id)
|
||
if not user:
|
||
raise HTTPException(status_code=404, detail="User not found")
|
||
if user.id == admin.id:
|
||
raise HTTPException(status_code=400, detail="Cannot delete current admin")
|
||
db.delete(user)
|
||
db.commit()
|
||
return {"ok": True}
|
||
|
||
|
||
@app.put("/api/admin/users/{user_id}/acl")
|
||
def set_acl(user_id: int, payload: dict, request: Request, _: User = Depends(require_admin), db: Session = Depends(get_db)):
|
||
validate_csrf(request)
|
||
user = db.get(User, user_id)
|
||
if not user:
|
||
raise HTTPException(status_code=404, detail="User not found")
|
||
service_ids = set(payload.get("service_ids", []))
|
||
|
||
existing = db.scalars(select(UserServiceAccess).where(UserServiceAccess.user_id == user_id)).all()
|
||
existing_map = {x.service_id: x for x in existing}
|
||
|
||
for sid in service_ids:
|
||
if sid not in existing_map:
|
||
db.add(UserServiceAccess(user_id=user_id, service_id=sid))
|
||
for sid, row in existing_map.items():
|
||
if sid not in service_ids:
|
||
db.delete(row)
|
||
|
||
db.commit()
|
||
return {"ok": True}
|