Files
evo-sync/web/main.py
mguschin fc65e591b3 test: add test suite with 65 tests, 73% coverage
- Unit tests: password hashing, notification providers, webhook field parsing
- Integration tests: auth routes (register/login/confirm-email/logout),
  invite flow, Evotor webhooks (/user/create, /user/verify, /user/token),
  admin panel (access control, activate/suspend/delete/reset-password)
- conftest: SQLite in-memory engine, transactional sessions, factory-boy
  factories (UserFactory with UserRoleEnum variants)
- Fix bcrypt: replace passlib (broken on Python 3.14 + bcrypt 5.x) with
  direct bcrypt calls; drop passlib from requirements.txt
- Fix datetime.utcnow() deprecation across routes and tests
- Fix Jinja2 TemplateResponse signature (request as first positional arg)
- Add coverage config to pyproject.toml

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 12:27:42 +03:00

78 lines
3.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import logging
from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from starlette.middleware.sessions import SessionMiddleware
try:
from pythonjsonlogger import jsonlogger
handler = logging.StreamHandler()
handler.setFormatter(jsonlogger.JsonFormatter("%(asctime)s %(levelname)s %(name)s %(message)s"))
logging.root.addHandler(handler)
except ImportError:
logging.basicConfig(level=logging.INFO)
logging.root.setLevel(logging.INFO)
from web.config import settings # noqa: E402 — after logging setup
from web.templates_env import templates # noqa: E402
app = FastAPI(title="ЭвоСинк")
app.add_middleware(
SessionMiddleware,
secret_key=settings.SECRET_KEY,
max_age=86400 * 30,
https_only=False,
)
app.mount("/static", StaticFiles(directory="web/static"), name="static")
# ── Routers ───────────────────────────────────────────────────────────────────
from web.routes.auth import router as auth_router # noqa: E402
from web.routes.reset import router as reset_router # noqa: E402
from web.routes.invite import router as invite_router # noqa: E402
from web.routes.profile import router as profile_router # noqa: E402
from web.routes.evotor_webhooks import router as evotor_webhooks_router # noqa: E402
from web.routes.admin import router as admin_router # noqa: E402
app.include_router(auth_router)
app.include_router(reset_router)
app.include_router(invite_router)
app.include_router(profile_router)
app.include_router(evotor_webhooks_router)
app.include_router(admin_router)
# ── Health ────────────────────────────────────────────────────────────────────
@app.get("/health")
async def health():
return {"status": "ok"}
# ── Root redirect ─────────────────────────────────────────────────────────────
@app.get("/")
async def root(request: Request):
from fastapi.responses import RedirectResponse
user_id = request.session.get("user_id")
if user_id:
return RedirectResponse("/profile", 303)
return RedirectResponse("/login", 303)
# ── 403 handler ───────────────────────────────────────────────────────────────
from fastapi import HTTPException # noqa: E402
from fastapi.exception_handlers import http_exception_handler # noqa: E402
@app.exception_handler(403)
async def forbidden_handler(request: Request, exc: HTTPException) -> HTMLResponse:
return templates.TemplateResponse(request, "message.html", {
"user": None,
"title": "Нет доступа",
"message": "У вас недостаточно прав для просмотра этой страницы.",
"link": "/profile",
"link_text": "В личный кабинет",
"jivosite_widget_id": settings.JIVOSITE_WIDGET_ID,
}, status_code=403)