Files
wrapped/app/services/settings_service.py
T
Sergey Antropoff 54749e5e12
devops-tools/wrapped/wrapped-build/pipeline/head This commit looks good
devops-tools/wrapped/wrapped-deploy/pipeline/head This commit looks good
Выпущен 0.1.4: N открытий, «доступно с», шаблоны, /verify и A2HS.
2026-07-29 20:27:51 +03:00

75 lines
2.4 KiB
Python

from __future__ import annotations
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.models import (
DEFAULT_MIME_ALLOWLIST,
AppSettings,
CaptchaProvider,
PasswordMode,
)
PASSWORD_MODE_HELP = {
"client_only": (
"Password also wraps ciphertext in the browser (PBKDF2 + AES-GCM). "
"Server still stores an Argon2id hash and rejects wrong passwords before "
"the one-time unwrap, so a mistyped password does not burn the package."
),
"server_gate": (
"Server stores an Argon2id hash and releases ciphertext only after a correct password. "
"Ciphertext is still encrypted client-side with the same password. "
"Stops offline bruteforce if someone steals the share link without the password."
),
}
async def get_or_create_settings(db: AsyncSession) -> AppSettings:
result = await db.execute(select(AppSettings).where(AppSettings.id == 1))
row = result.scalar_one_or_none()
if row:
return row
row = AppSettings(
id=1,
mime_allowlist=list(DEFAULT_MIME_ALLOWLIST),
captcha_provider=CaptchaProvider.off,
password_mode=PasswordMode.client_only,
)
db.add(row)
await db.commit()
await db.refresh(row)
return row
def mime_allowed(allowlist: list[str], content_type: str) -> bool:
ct = (content_type or "").split(";")[0].strip().lower()
if not ct:
return False
for pattern in allowlist:
p = pattern.strip().lower()
if not p:
continue
if p.endswith("/*"):
if ct.startswith(p[:-1]):
return True
elif ct == p:
return True
return False
def public_settings_payload(row: AppSettings) -> dict:
return {
"max_upload_bytes": row.max_upload_bytes,
"default_ttl_seconds": row.default_ttl_seconds,
"max_ttl_seconds": row.max_ttl_seconds,
"mime_allowlist": list(row.mime_allowlist or []),
"captcha_provider": row.captcha_provider.value,
"turnstile_site_key": row.turnstile_site_key or "",
"hcaptcha_site_key": row.hcaptcha_site_key or "",
"password_mode": row.password_mode.value,
"password_mode_description": PASSWORD_MODE_HELP,
"password_max_attempts": max(1, int(row.password_max_attempts or 3)),
"max_opens_limit": max(1, min(10, int(getattr(row, "max_opens_limit", None) or 3))),
}