- Add sync_engine.py: background asyncio loop syncing Evotor products to VK market - Wire sync_loop into lifespan alongside health_check_loop - Add SYNC_INTERVAL_SECONDS and VK_DEFAULT_PHOTO_PATH settings to config - Mount default product image in docker-compose - Add synced_at column to CachedProduct model + migration - Show synced_at status in catalog products template - Fix VK groups API response parsing (handle list vs dict) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
118 lines
3.5 KiB
Python
118 lines
3.5 KiB
Python
from datetime import datetime
|
|
|
|
import httpx
|
|
|
|
from fastapi import APIRouter, Request, Depends
|
|
from fastapi.responses import RedirectResponse
|
|
from web.templates_env import templates
|
|
from sqlalchemy.orm import Session
|
|
|
|
from web.auth import get_current_user
|
|
from web.config import settings
|
|
from web.database import get_db
|
|
from web.models import User, VkConnection
|
|
|
|
router = APIRouter(prefix="/vk")
|
|
|
|
VK_API_URL = "https://api.vk.com/method"
|
|
|
|
|
|
@router.get("")
|
|
def vk_page(
|
|
request: Request,
|
|
db: Session = Depends(get_db),
|
|
user: User | None = Depends(get_current_user),
|
|
):
|
|
if not user:
|
|
return RedirectResponse("/login", 303)
|
|
|
|
connection = db.query(VkConnection).filter(VkConnection.user_id == user.id).first()
|
|
error = request.query_params.get("error")
|
|
return templates.TemplateResponse("vk.html", {
|
|
"request": request,
|
|
"user": user,
|
|
"connection": connection,
|
|
"error": error,
|
|
})
|
|
|
|
|
|
@router.post("/token")
|
|
async def vk_token(
|
|
request: Request,
|
|
db: Session = Depends(get_db),
|
|
user: User | None = Depends(get_current_user),
|
|
):
|
|
"""Save a manually entered VK community access token."""
|
|
if not user:
|
|
return RedirectResponse("/login", 303)
|
|
|
|
form = await request.form()
|
|
token = (form.get("token") or "").strip()
|
|
if not token:
|
|
return RedirectResponse("/vk?error=empty_token", 303)
|
|
|
|
# Fetch community info to validate the token and get group name/id
|
|
group_id = None
|
|
group_name = None
|
|
try:
|
|
async with httpx.AsyncClient() as client:
|
|
resp = await client.get(
|
|
f"{VK_API_URL}/groups.getById",
|
|
params={"access_token": token, "v": settings.VK_API_VERSION},
|
|
timeout=15,
|
|
)
|
|
if resp.status_code == 200:
|
|
data = resp.json()
|
|
if "error" in data:
|
|
return RedirectResponse("/vk?error=invalid_token", 303)
|
|
response = data.get("response", [])
|
|
groups = response if isinstance(response, list) else response.get("groups", [])
|
|
if groups:
|
|
group_id = str(groups[0].get("id", ""))
|
|
group_name = groups[0].get("name")
|
|
elif resp.status_code == 401:
|
|
return RedirectResponse("/vk?error=invalid_token", 303)
|
|
except Exception:
|
|
pass
|
|
|
|
connection = db.query(VkConnection).filter(VkConnection.user_id == user.id).first()
|
|
now = datetime.utcnow()
|
|
if connection:
|
|
connection.access_token = token
|
|
connection.vk_user_id = group_id
|
|
connection.first_name = group_name
|
|
connection.last_name = None
|
|
connection.is_online = True
|
|
connection.last_checked_at = now
|
|
else:
|
|
connection = VkConnection(
|
|
user_id=user.id,
|
|
access_token=token,
|
|
vk_user_id=group_id,
|
|
first_name=group_name,
|
|
last_name=None,
|
|
is_online=True,
|
|
last_checked_at=now,
|
|
)
|
|
db.add(connection)
|
|
db.commit()
|
|
|
|
return RedirectResponse("/connections", 303)
|
|
|
|
|
|
@router.post("/disconnect")
|
|
async def vk_disconnect(
|
|
request: Request,
|
|
db: Session = Depends(get_db),
|
|
user: User | None = Depends(get_current_user),
|
|
):
|
|
if not user:
|
|
return RedirectResponse("/login", 303)
|
|
|
|
connection = db.query(VkConnection).filter(VkConnection.user_id == user.id).first()
|
|
if connection:
|
|
db.delete(connection)
|
|
db.commit()
|
|
|
|
return RedirectResponse("/connections", 303)
|