This commit is contained in:
Vladislav 2026-08-02 04:31:32 -04:00
parent d6512dac5a
commit c1efb9d36f
4 changed files with 243 additions and 38 deletions

View File

@ -8,12 +8,14 @@
{% block content %} {% block content %}
{{ header.section(user) }} {{ header.section(user) }}
<form on class="c_sbrxk"> <form class="c_sbrxk">
<img class="login-illustration" src="/static/login-art.png" alt="" aria-hidden="true">
<div class="c_dtjoh"> <div class="c_dtjoh">
<div class="c_aihm6 floating-group"> <div class="c_aihm6 floating-group">
<input type="email" id="email" name="email" class="c_xhd5k" required> <input type="text" id="email" name="email" class="c_xhd5k" autocomplete="username" required>
<label for="email" class="c_z3ybu floating-label">Email</label> <label for="email" class="c_z3ybu floating-label">Логин</label>
</div> </div>
<span class="login-hint">Можно указать телефон или email от аккаунта</span>
<div class="c_aihm6 floating-group file-group poster-group password-group" style="margin-bottom: 0;"> <div class="c_aihm6 floating-group file-group poster-group password-group" style="margin-bottom: 0;">
<input type="password" id="password" name="password" class="c_xhd5k" required> <input type="password" id="password" name="password" class="c_xhd5k" required>
<label for="password" class="c_z3ybu floating-label">Пароль</label> <label for="password" class="c_z3ybu floating-label">Пароль</label>
@ -34,7 +36,8 @@
</div> </div>
</div> </div>
<a href="/restore{% if next %}?next={{next}}{% endif %}" onclick="sendToNextPage(event)" style="margin-bottom: 1rem;">Забыли пароль ?</a> <span class="login-hint">Минимум 8 символов</span>
<a href="/restore{% if next %}?next={{next}}{% endif %}" onclick="sendToNextPage(event)" class="login-forgot" style="margin-bottom: 1rem;">Забыли пароль ?</a>
<button class="c_aq9et" id="createBtn" type="button"> <button class="c_aq9et" id="createBtn" type="button">
<span class="c_som9s">Войти</span> <span class="c_som9s">Войти</span>
@ -42,6 +45,18 @@
<div style="display: flex; gap:15px"> <div style="display: flex; gap:15px">
<span>Нет аккаунта?</span><a href="/registration{% if next %}?next={{next}}{% endif %}">Регистрация</a> <span>Нет аккаунта?</span><a href="/registration{% if next %}?next={{next}}{% endif %}">Регистрация</a>
</div> </div>
<div class="login-social">
<div class="login-social__or"><span>или войдите через</span></div>
<div class="login-social__row">
<a class="login-social__btn login-social__btn--vk" href="/api/v1/social/vk/register{% if next %}?next={{next}}{% endif %}">
<svg class="login-social__logo" width="22" height="22" viewBox="0 0 24 24" fill="#0077FF" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<path d="M13.162 18.994c-6.13 0-9.625-4.203-9.77-11.196h3.07c.101 5.132 2.366 7.307 4.16 7.755V7.798h2.89v4.427c1.77-.19 3.63-2.208 4.256-4.427h2.89c-.48 2.734-2.498 4.752-3.933 5.581 1.435.673 3.733 2.432 4.61 5.614h-3.182c-.682-2.125-2.384-3.767-4.64-3.99v3.99h-.34z"/>
</svg>
<span>VK</span>
</a>
</div>
</div>
</div> </div>
</form> </form>
@ -120,13 +135,18 @@ function sendToNextPage(e) {
window.location.href = next; window.location.href = next;
} else { } else {
// non-2xx — mark all input groups as error
const allFields = document.querySelectorAll('.floating-group'); const allFields = document.querySelectorAll('.floating-group');
allFields.forEach(g => g.classList.add('error')); let msg;
if (res.status === 403) {
// Optional: you can show server message if present // Аккаунт заблокирован — поля ввода ни при чём, показываем причину
// const errorText = await res.text(); msg = 'Ваш аккаунт заблокирован администратором.';
// console.error('Server error:', errorText); try { const d = await res.json(); if (d && typeof d.detail === 'string') msg = d.detail; } catch (e) {}
allFields.forEach(g => g.classList.remove('error'));
} else {
msg = 'Неверный логин или пароль';
allFields.forEach(g => g.classList.add('error'));
}
if (typeof showToast === 'function') showToast({ message: msg, status: 'error', duration: 9000 });
} }
} catch (err) { } catch (err) {
// Network or other unexpected error — mark fields as error // Network or other unexpected error — mark fields as error
@ -179,6 +199,17 @@ function sendToNextPage(e) {
pwd.focus(); pwd.focus();
}); });
// Вход через Макс — бэкенд ещё не готов: уведомление, без перехода
var maxLoginBtn = document.querySelector('.login-social__btn--max');
if (maxLoginBtn) {
maxLoginBtn.addEventListener('click', function (e) {
e.preventDefault();
if (typeof showToast === 'function') {
showToast({ message: 'Вход через Макс — в процессе реализации', status: 'info', duration: 2800 });
}
});
}
</script> </script>
{{ footer.section() }} {{ footer.section() }}
<script> <script>
@ -441,6 +472,43 @@ input[type="number"]:focus::-webkit-inner-spin-button {display: none;}
} }
.c_sbrxk{z-index: 1000;} .c_sbrxk{z-index: 1000;}
#preview_poster{width: 335px;} #preview_poster{width: 335px;}
/* «Забыли пароль?» — второстепенная ссылка */
.login-forgot { font-size: 13px; color: #888; align-self: flex-start; }
.login-forgot:hover { color: #5F1798; text-decoration: underline; }
/* Подписи-подсказки под полями */
.login-hint { font-size: 12px; color: #888; align-self: flex-start; line-height: 1.3; margin-top: -6px; }
/* убираем большой нижний отступ у группы, за которой идёт подсказка */
.floating-group:has(+ .login-hint) { margin-bottom: 0; }
/* Микроанимация кнопки «Войти» */
.c_aq9et { cursor: pointer; box-shadow: 0 4px 14px -4px rgba(95,23,152,.5); transition: transform .18s, box-shadow .18s, background .18s; }
.c_aq9et:hover { background: #6d1ba9; transform: translateY(-2px); box-shadow: 0 10px 24px -6px rgba(95,23,152,.6); }
.c_aq9et:active { transform: translateY(0); box-shadow: 0 4px 12px -4px rgba(95,23,152,.5); }
@media (prefers-reduced-motion: reduce) { .c_aq9et:hover, .c_aq9et:active { transform: none; } }
/* Картинка слева от формы (только десктоп) */
.login-illustration { display: none; }
@media (min-width: 901px) {
.login-illustration { display: block; width: 340px; max-width: 38vw; height: auto; align-self: center; }
.c_sbrxk { gap: 80px; }
}
/* Вход через VK / Макс */
.login-social { display: flex; flex-direction: column; gap: 10px; align-self: stretch; margin-top: 4px; }
.login-social__or { display: flex; align-items: center; gap: 10px; color: #999; font-size: 13px; }
.login-social__or::before, .login-social__or::after { content: ''; flex: 1; height: 1px; background: #e5e5e5; }
.login-social__row { display: flex; gap: 10px; }
.login-social__btn { flex: 1 1 0; min-width: 0; display: flex; align-items: center; justify-content: center; gap: 8px; min-height: 48px; border-radius: 8px; background: #fff; border: 2px solid #ddd; color: #111; text-decoration: none; font-size: 15px; font-weight: 600; transition: border-color .18s, box-shadow .18s, transform .18s; }
.login-social__btn:hover { transform: translateY(-1px); box-shadow: 0 6px 16px -6px rgba(0,0,0,.28); }
.login-social__btn:active { transform: translateY(0); }
.login-social__btn--vk:hover { border-color: #0077FF; }
.login-social__btn--max:hover { border-color: #7a3ff2; }
.login-social__logo { flex: 0 0 auto; display: block; width: 22px; height: 22px; }
@media (prefers-reduced-motion: reduce) {
.login-social__btn:hover, .login-social__btn:active { transform: none; }
}
</style> </style>
{{ footer.style() }} {{ footer.style() }}
{% endblock %} {% endblock %}

View File

@ -1,5 +1,6 @@
import os import os
import json import json
import re # регистронезависимый поиск email при VK-регистрации
import hashlib import hashlib
import base64 import base64
from datetime import datetime, timedelta, UTC from datetime import datetime, timedelta, UTC
@ -9,7 +10,8 @@ from bson import ObjectId
from config import * from config import *
from db import mongo_client, redis_client, gridfs_map from db import mongo_client, redis_client, gridfs_map
import asyncio import asyncio
from utils import fetch_bytes import secrets
from utils import fetch_bytes, get_password_hash
import io import io
import uuid import uuid
from utils import encrypt, decrypt from utils import encrypt, decrypt
@ -17,7 +19,7 @@ from utils import encrypt, decrypt
# ---------------------- Константы OAuth ---------------------- # ---------------------- Константы OAuth ----------------------
OAUTH_URL = 'https://id.vk.com/oauth2/auth' OAUTH_URL = 'https://id.vk.com/oauth2/auth'
AUTHORIZE_URL = 'https://id.vk.ru/authorize' AUTHORIZE_URL = 'https://id.vk.ru/authorize'
SCOPES = 'offline,wall,photos,groups' SCOPES = 'offline,wall,photos,groups,email' # email — для регистрации через VK
REDIRECT_URL = f'{FULL_DOMAIN}/api/v1/social/vk/token' REDIRECT_URL = f'{FULL_DOMAIN}/api/v1/social/vk/token'
@ -29,14 +31,21 @@ class TokenManager:
def __init__(self): def __init__(self):
self.tokens_coll = mongo_client["vk"]["tokens"] self.tokens_coll = mongo_client["vk"]["tokens"]
async def get_auth_url(self, user_id: str) -> str: async def get_auth_url(self, user_id: str = None, purpose: str = "link", next: str = None) -> str:
"""purpose: "link" (привязка соцсети) или "register" (вход/регистрация через VK);
next — куда вернуть пользователя после успешного входа."""
state = os.urandom(32).hex() state = os.urandom(32).hex()
code_verifier = os.urandom(50).hex() code_verifier = os.urandom(50).hex()
code_challenge = base64.urlsafe_b64encode( code_challenge = base64.urlsafe_b64encode(
hashlib.sha256(code_verifier.encode('utf-8')).digest() hashlib.sha256(code_verifier.encode('utf-8')).digest()
).decode('utf-8').rstrip('=') ).decode('utf-8').rstrip('=')
oauth_data = {"user_id": user_id, "code_verifier": code_verifier} oauth_data = {
"user_id": user_id,
"code_verifier": code_verifier,
"purpose": purpose,
"next": next,
}
await redis_client.setex(f"oauth_state:{state}", 60 * 15, json.dumps(oauth_data)) await redis_client.setex(f"oauth_state:{state}", 60 * 15, json.dumps(oauth_data))
url = ( url = (
@ -73,7 +82,10 @@ class TokenManager:
return None return None
oauth_data = json.loads(oauth_data_raw) oauth_data = json.loads(oauth_data_raw)
user_id = oauth_data["user_id"] user_id = oauth_data.get("user_id")
purpose = oauth_data.get("purpose", "link")
next_url = oauth_data.get("next")
created = False
code_verifier = oauth_data["code_verifier"] code_verifier = oauth_data["code_verifier"]
# Выполняем запрос к VK для обмена кода на токены # Выполняем запрос к VK для обмена кода на токены
@ -103,6 +115,13 @@ class TokenManager:
}) })
vk_profile = reqp.json()['response'][0] vk_profile = reqp.json()['response'][0]
# Режим регистрации: находим или создаём пользователя ДО сохранения соцсетей,
# чтобы дальше socials/tokens сохранились уже с реальным user_id.
if purpose == "register" and not user_id:
user_id, created = await self._resolve_user_for_register(
vk_profile, token_data.get("email")
)
reqg = await client.post(f"{VK_API_URI}groups.get", data={ reqg = await client.post(f"{VK_API_URI}groups.get", data={
'access_token':access_token, 'access_token':access_token,
'extended':1, 'extended':1,
@ -111,8 +130,11 @@ class TokenManager:
}) })
vk_groups = reqg.json()['response']['items'] vk_groups = reqg.json()['response']['items']
urls = [vk_profile['photo_max_orig']] + [i['photo_200'] for i in vk_groups] urls = [vk_profile['photo_max_orig']] + [i['photo_200'] for i in vk_groups]
results = await asyncio.gather(*(fetch_bytes(client, u) for u in urls)) image_bytes = await asyncio.gather(*(fetch_bytes(client, u) for u in urls))
results = [await self.save_vk(i) for i in results] # В режиме регистрации ставим аватар VK в профиль, если фото не задано вручную
if purpose == "register" and user_id and image_bytes:
await self._maybe_set_vk_avatar(user_id, created, image_bytes[0])
results = [await self.save_vk(i) for i in image_bytes]
socials = [{ socials = [{
'name': f"{vk_profile['last_name']} {vk_profile['first_name']}", 'name': f"{vk_profile['last_name']} {vk_profile['first_name']}",
'screen_name': vk_profile['screen_name'], 'screen_name': vk_profile['screen_name'],
@ -142,7 +164,12 @@ class TokenManager:
# Удаляем использованный state, чтобы его нельзя было переиспользовать. # Удаляем использованный state, чтобы его нельзя было переиспользовать.
await redis_client.delete(key) await redis_client.delete(key)
return token_data return {
"user_id": user_id,
"registered": created,
"purpose": purpose,
"next": next_url,
}
# Обработка ошибок HTTP (напр., неверный код, истёк скоп) # Обработка ошибок HTTP (напр., неверный код, истёк скоп)
except httpx.HTTPStatusError as e: except httpx.HTTPStatusError as e:
@ -157,6 +184,73 @@ class TokenManager:
return None return None
async def _resolve_user_for_register(self, vk_profile: dict, email: str | None):
"""Находит пользователя по vk_id или email, иначе создаёт нового.
Возвращает кортеж (user_id: str, created: bool).
При совпадении по email — привязывает vk_id к найденному аккаунту.
"""
users = mongo_client["auth"]["users"]
vk_id = vk_profile.get("id")
# 1) Уже привязанный VK-аккаунт
existing = await users.find_one({"vk_id": vk_id}, {"_id": 1})
if existing:
return str(existing["_id"]), False
# 2) Существующий аккаунт с таким email — логиним и привязываем VK
if email:
existing = await users.find_one(
{"email": {"$regex": f"^{re.escape(email)}$", "$options": "i"}},
{"_id": 1},
)
if existing:
await users.update_one({"_id": existing["_id"]}, {"$set": {"vk_id": vk_id}})
return str(existing["_id"]), False
# 3) Создаём нового пользователя (роль по умолчанию — художник)
first = vk_profile.get("first_name", "") or ""
last = vk_profile.get("last_name", "") or ""
doc = {
"username": f"{first} {last}".strip(),
"female_name_lastname": f"{last} {first}".strip(),
"email": email,
"phone": None,
"img": "/static/painter.svg",
"roles": ["user"],
"extra_scopes": [],
"vk_id": vk_id,
"client_id": "vk",
"living_place": None,
"offer_accepted": False,
# Неиспользуемый случайный пароль: вход по email/паролю невозможен,
# но login-эндпоинт не падает на None.startswith(...).
"hashed_password": get_password_hash(secrets.token_urlsafe(32)),
}
res = await users.insert_one(doc)
return str(res.inserted_id), True
async def _maybe_set_vk_avatar(self, user_id: str, created: bool, avatar_bytes: bytes):
"""Ставит аватар из VK в профиль, если пользователь не задал своё фото.
«Не задан вручную» = img пустой или дефолтная заглушка (/static/...).
Для только что созданного пользователя ставим всегда.
"""
if not avatar_bytes:
return
users = mongo_client["auth"]["users"]
if not created:
u = await users.find_one({"_id": ObjectId(user_id)}, {"img": 1})
img = (u or {}).get("img")
if img and not str(img).startswith("/static/"):
return # у пользователя уже своё фото — не трогаем
try:
grid = gridfs_map["users"]
file_id = await grid.upload_from_stream(f"vk_{user_id}.jpg", io.BytesIO(avatar_bytes))
await users.update_one({"_id": ObjectId(user_id)}, {"$set": {"img": str(file_id)}})
except Exception as e:
logging.error(f"[TokenManager] VK avatar save failed: {e}")
# ====================== ШИФРОВАНИЕ ====================== # ====================== ШИФРОВАНИЕ ======================
async def _encrypt_token_fields(self, token_data: dict) -> dict: async def _encrypt_token_fields(self, token_data: dict) -> dict:
"""Шифрует access_token и refresh_token.""" """Шифрует access_token и refresh_token."""

View File

@ -46,8 +46,13 @@
"\n", "\n",
"try:\n", "try:\n",
" load_env('env')\n", " load_env('env')\n",
"except:\n", "except Exception:\n",
" load_env('env_example')" " # env_example тоже может отсутствовать (в образ эти файлы не копируются) —\n",
" # тогда переменные берутся из окружения контейнера/k8s-секрета\n",
" try:\n",
" load_env('env_example')\n",
" except Exception:\n",
" pass"
], ],
"display_name": ".venv" "display_name": ".venv"
}, },
@ -78,7 +83,7 @@
"from celery import Celery\n", "from celery import Celery\n",
"from bson import ObjectId\n", "from bson import ObjectId\n",
"from fastapi import status\n", "from fastapi import status\n",
"from fastapi import APIRouter,Depends\n", "from fastapi import APIRouter, Depends, Request, Response\n",
"from fastapi.responses import RedirectResponse\n", "from fastapi.responses import RedirectResponse\n",
"from images import insert_logo\n" "from images import insert_logo\n"
], ],
@ -130,10 +135,29 @@
" return RedirectResponse(url=url, status_code=status.HTTP_303_SEE_OTHER)\n", " return RedirectResponse(url=url, status_code=status.HTTP_303_SEE_OTHER)\n",
"\n", "\n",
"\n", "\n",
"@router.get(\"/register\")\n",
"async def vk_register_start(next: str = '/profile'):\n",
" \"\"\"Старт регистрации/входа через VK (без предварительной авторизации).\"\"\"\n",
" url = await token_manager.get_auth_url(purpose='register', next=next)\n",
" return RedirectResponse(url=url, status_code=status.HTTP_303_SEE_OTHER)\n",
"\n",
"\n",
"@router.get(\"/token\")\n", "@router.get(\"/token\")\n",
"async def oauth_callback(code: str, state: str, device_id: str = None):\n", "async def oauth_callback(request: Request, response: Response, code: str, state: str, device_id: str = None):\n",
" token_data = await token_manager.get_token(code=code, state=state, device_id=device_id)\n", " result = await token_manager.get_token(code=code, state=state, device_id=device_id)\n",
" return RedirectResponse(url='/profile/social', status_code=status.HTTP_303_SEE_OTHER)\n" " if not result or not result.get('user_id'):\n",
" return RedirectResponse(url='/login', status_code=status.HTTP_303_SEE_OTHER)\n",
"\n",
" # Регистрация/вход через VK — выдаём нашу сессию и редиректим на next\n",
" if result.get('purpose') == 'register':\n",
" await issue_session(result['user_id'], request, response, client_id='vk')\n",
" next_url = result.get('next') or '/profile'\n",
" if not is_safe_redirect(next_url):\n",
" next_url = '/profile'\n",
" return RedirectResponse(url=next_url, status_code=status.HTTP_303_SEE_OTHER, headers=response.headers)\n",
"\n",
" # Привязка соцсети к уже залогиненному пользователю\n",
" return RedirectResponse(url='/profile/social', status_code=status.HTTP_303_SEE_OTHER)"
], ],
"display_name": ".venv" "display_name": ".venv"
}, },
@ -175,7 +199,6 @@
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
"# async def send_vk(item: dict, text: str, file_ids: list, doc: dict, sign_avatar: bool = True): TODO\n",
"@router.post(\"/send\")\n", "@router.post(\"/send\")\n",
"async def send_vk(data:dict, request: Request):\n", "async def send_vk(data:dict, request: Request):\n",
" auth = request.headers.get(\"Authorization\")\n", " auth = request.headers.get(\"Authorization\")\n",
@ -250,7 +273,6 @@
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
"# async def edit_vk(item: dict, text: str, file_ids: list, doc: dict, sign_avatar: bool = True): TODO\n",
"@router.post(\"/edit\")\n", "@router.post(\"/edit\")\n",
"async def edit_vk(data:dict, request: Request):\n", "async def edit_vk(data:dict, request: Request):\n",
" auth = request.headers.get(\"Authorization\")\n", " auth = request.headers.get(\"Authorization\")\n",
@ -265,6 +287,7 @@
" doc = data['doc']\n", " doc = data['doc']\n",
" post_id = data['post_id']\n", " post_id = data['post_id']\n",
" sign_avatar = data['sign_avatar']\n", " sign_avatar = data['sign_avatar']\n",
"# async def edit_vk(item: dict, text: str, file_ids: list, doc: dict, sign_avatar: bool = True):\n",
" db_name = item.get('db')\n", " db_name = item.get('db')\n",
" if db_name == 'competitions':\n", " if db_name == 'competitions':\n",
" social = await social_collection.find_one({'service': True, 'type':'vk'})\n", " social = await social_collection.find_one({'service': True, 'type':'vk'})\n",
@ -327,7 +350,6 @@
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
"# TODO сделать вызов функции из @celery_app_vk.task( а не по url в router\n",
"@router.post(\"/delete\")\n", "@router.post(\"/delete\")\n",
"async def wall_delete(data: dict, request: Request):\n", "async def wall_delete(data: dict, request: Request):\n",
" # ---- auth ----\n", " # ---- auth ----\n",
@ -1614,7 +1636,7 @@
], ],
"metadata": { "metadata": {
"kernelspec": { "kernelspec": {
"display_name": ".venv (3.14.6.final.0)", "display_name": ".venv",
"language": "python", "language": "python",
"name": "python3" "name": "python3"
}, },

41
vk.py
View File

@ -31,8 +31,13 @@ def load_env(file_path):
try: try:
load_env('env') load_env('env')
except: except Exception:
load_env('env_example') # env_example тоже может отсутствовать (в образ эти файлы не копируются) —
# тогда переменные берутся из окружения контейнера/k8s-секрета
try:
load_env('env_example')
except Exception:
pass
## Class ## Class
@ -48,7 +53,7 @@ from io import BytesIO
from celery import Celery from celery import Celery
from bson import ObjectId from bson import ObjectId
from fastapi import status from fastapi import status
from fastapi import APIRouter,Depends from fastapi import APIRouter, Depends, Request, Response
from fastapi.responses import RedirectResponse from fastapi.responses import RedirectResponse
from images import insert_logo from images import insert_logo
@ -70,12 +75,30 @@ async def auth_redirect(
return RedirectResponse(url=url, status_code=status.HTTP_303_SEE_OTHER) return RedirectResponse(url=url, status_code=status.HTTP_303_SEE_OTHER)
@router.get("/token") @router.get("/register")
async def oauth_callback(code: str, state: str, device_id: str = None): async def vk_register_start(next: str = '/profile'):
token_data = await token_manager.get_token(code=code, state=state, device_id=device_id) """Старт регистрации/входа через VK (без предварительной авторизации)."""
return RedirectResponse(url='/profile/social', status_code=status.HTTP_303_SEE_OTHER) url = await token_manager.get_auth_url(purpose='register', next=next)
return RedirectResponse(url=url, status_code=status.HTTP_303_SEE_OTHER)
@router.get("/token")
async def oauth_callback(request: Request, response: Response, code: str, state: str, device_id: str = None):
result = await token_manager.get_token(code=code, state=state, device_id=device_id)
if not result or not result.get('user_id'):
return RedirectResponse(url='/login', status_code=status.HTTP_303_SEE_OTHER)
# Регистрация/вход через VK — выдаём нашу сессию и редиректим на next
# if result.get('purpose') == 'register':
# await issue_session(result['user_id'], request, response, client_id='vk')
# next_url = result.get('next') or '/profile'
# if not is_safe_redirect(next_url):
# next_url = '/profile'
# return RedirectResponse(url=next_url, status_code=status.HTTP_303_SEE_OTHER, headers=response.headers)
# Привязка соцсети к уже залогиненному пользователю
return RedirectResponse(url='/profile/social', status_code=status.HTTP_303_SEE_OTHER)
async def get_images(db_name: str, file_ids: list, sign_avatar: bool = True): async def get_images(db_name: str, file_ids: list, sign_avatar: bool = True):
images = {} images = {}
photo_bytes_list= [] photo_bytes_list= []
@ -99,7 +122,6 @@ async def get_images(db_name: str, file_ids: list, sign_avatar: bool = True):
return images return images
# async def send_vk(item: dict, text: str, file_ids: list, doc: dict, sign_avatar: bool = True): TODO
@router.post("/send") @router.post("/send")
async def send_vk(data:dict, request: Request): async def send_vk(data:dict, request: Request):
auth = request.headers.get("Authorization") auth = request.headers.get("Authorization")
@ -166,7 +188,6 @@ async def send_vk(data:dict, request: Request):
# async def edit_vk(item: dict, text: str, file_ids: list, doc: dict, sign_avatar: bool = True): TODO
@router.post("/edit") @router.post("/edit")
async def edit_vk(data:dict, request: Request): async def edit_vk(data:dict, request: Request):
auth = request.headers.get("Authorization") auth = request.headers.get("Authorization")
@ -181,6 +202,7 @@ async def edit_vk(data:dict, request: Request):
doc = data['doc'] doc = data['doc']
post_id = data['post_id'] post_id = data['post_id']
sign_avatar = data['sign_avatar'] sign_avatar = data['sign_avatar']
# async def edit_vk(item: dict, text: str, file_ids: list, doc: dict, sign_avatar: bool = True):
db_name = item.get('db') db_name = item.get('db')
if db_name == 'competitions': if db_name == 'competitions':
social = await social_collection.find_one({'service': True, 'type':'vk'}) social = await social_collection.find_one({'service': True, 'type':'vk'})
@ -235,7 +257,6 @@ async def edit_vk(data:dict, request: Request):
# TODO сделать вызов функции из @celery_app_vk.task( а не по url в router
@router.post("/delete") @router.post("/delete")
async def wall_delete(data: dict, request: Request): async def wall_delete(data: dict, request: Request):
# ---- auth ---- # ---- auth ----