import secrets from datetime import datetime, timedelta, timezone from urllib.parse import urlencode import httpx from fastapi import APIRouter, Depends, Request from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse from sqlalchemy.orm import Session from web.auth.session import get_current_user from web.config import settings from web.database import get_db from web.models.connections import EvotorConnection, VkConnection from web.templates_env import templates VK_SCOPE = 335876 # photos(4) + wall(8192) + groups(262144) + offline(65536) router = APIRouter() def _render(request: Request, template: str, ctx: dict) -> HTMLResponse: ctx["request"] = request ctx.setdefault("jivosite_widget_id", settings.JIVOSITE_WIDGET_ID) return templates.TemplateResponse(ctx.pop("request"), template, ctx) def _now() -> datetime: return datetime.now(timezone.utc).replace(tzinfo=None) @router.get("/connections") async def connections_get(request: Request, db: Session = Depends(get_db)): try: user = get_current_user(request, db) except Exception: return RedirectResponse("/login", 303) evotor = db.query(EvotorConnection).filter_by(user_id=user.id).first() vk = db.query(VkConnection).filter_by(user_id=user.id).first() return _render(request, "connections.html", {"user": user, "evotor": evotor, "vk": vk}) @router.post("/connections/evotor") async def connections_evotor_post(request: Request, db: Session = Depends(get_db)): try: user = get_current_user(request, db) except Exception: return RedirectResponse("/login", 303) form = await request.form() access_token = str(form.get("access_token", "")).strip() evotor_user_id = str(form.get("evotor_user_id", "")).strip() or None if not access_token: evotor = db.query(EvotorConnection).filter_by(user_id=user.id).first() return _render(request, "connections.html", { "user": user, "evotor": evotor, "errors": ["API-токен обязателен"], }) now = _now() conn = db.query(EvotorConnection).filter_by(user_id=user.id).first() if conn: conn.access_token = access_token if evotor_user_id: conn.evotor_user_id = evotor_user_id conn.updated_at = now else: conn = EvotorConnection( user_id=user.id, evotor_user_id=evotor_user_id, access_token=access_token, api_token=secrets.token_urlsafe(32), connected_at=now, updated_at=now, ) db.add(conn) if evotor_user_id and not user.evotor_user_id: user.evotor_user_id = evotor_user_id db.commit() return RedirectResponse("/connections?success=1", 303) @router.post("/connections/evotor/disconnect") async def connections_evotor_disconnect(request: Request, db: Session = Depends(get_db)): try: user = get_current_user(request, db) except Exception: return RedirectResponse("/login", 303) conn = db.query(EvotorConnection).filter_by(user_id=user.id).first() if conn: db.delete(conn) db.commit() return RedirectResponse("/connections", 303) @router.post("/connections/vk") async def connections_vk_post(request: Request, db: Session = Depends(get_db)): try: user = get_current_user(request, db) except Exception: return RedirectResponse("/login", 303) form = await request.form() access_token = str(form.get("access_token", "")).strip() vk_group_id = str(form.get("vk_group_id", "")).strip() or None if not access_token: evotor = db.query(EvotorConnection).filter_by(user_id=user.id).first() vk = db.query(VkConnection).filter_by(user_id=user.id).first() return _render(request, "connections.html", { "user": user, "evotor": evotor, "vk": vk, "errors": ["Токен VK обязателен"], }) now = _now() conn = db.query(VkConnection).filter_by(user_id=user.id).first() if conn: conn.access_token = access_token if vk_group_id: conn.vk_user_id = vk_group_id conn.updated_at = now else: conn = VkConnection( user_id=user.id, access_token=access_token, vk_user_id=vk_group_id, connected_at=now, updated_at=now, ) db.add(conn) db.commit() return RedirectResponse("/connections?success=1", 303) @router.get("/vk-auth") async def vk_auth(request: Request): try: get_current_user(request, next(get_db())) except Exception: return RedirectResponse("/login", 303) if not settings.VK_CLIENT_ID: return RedirectResponse("/connections?error=vk_not_configured", 303) state = secrets.token_urlsafe(16) request.session["vk_oauth_state"] = state redirect_uri = f"{settings.BASE_URL}/vk-callback" params = urlencode({ "client_id": settings.VK_CLIENT_ID, "redirect_uri": redirect_uri, "scope": VK_SCOPE, "response_type": "token", "display": "page", "state": state, "revoke": "1", }) return RedirectResponse(f"https://oauth.vk.com/authorize?{params}", 302) @router.get("/vk-callback") async def vk_callback_page(request: Request): """Serves the callback page that reads the token from the URL fragment and POSTs it.""" return HTMLResponse("""
Завершаем авторизацию…