import secrets from datetime import datetime, timedelta, timezone from urllib.parse import urlencode import httpx import web.lib.api_logger as api_logger 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("""
Завершаем авторизацию…