Files
evo-sync/web/main.py
mguschin 5ead89e0cf feat: Evotor user lifecycle, RBAC, admin panel
- Receive Evotor webhooks: POST /user/create, /user/verify, /user/token
- Create users in pending status; match to existing users by email/phone
- Send invite link via Celery notification task; user sets password at /invite
- Abstract EmailProvider/SMSProvider with ConsoleEmailProvider default
- Role-based access control: role enum on users + roles/permissions tables
- Admin panel: /admin/users (list, filter, search, paginate), user detail card
  with activate/suspend/reset-password/send-invite/edit/delete actions
- Admin roles management: /admin/roles with per-role permission assignment
- Extend user profile card: role, status, Evotor ID, email confirmation badge
- Auth routes: register, login, logout, confirm-email, forgot/reset password
- Alembic migrations 0002 (full schema + new fields) and 0003 (RBAC + seeds)
- Port Pico CSS + Bootstrap Icons UI from Node.js commit (854c912)

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

79 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("message.html", {
"request": request,
"user": None,
"title": "Нет доступа",
"message": "У вас недостаточно прав для просмотра этой страницы.",
"link": "/profile",
"link_text": "В личный кабинет",
"jivosite_widget_id": settings.JIVOSITE_WIDGET_ID,
}, status_code=403)