2024-06-11 14:51:34 +00:00
|
|
|
|
import asyncio
|
2023-12-16 15:24:30 +00:00
|
|
|
|
import os
|
|
|
|
|
import re
|
2024-12-11 22:04:11 +00:00
|
|
|
|
from asyncio.log import logger
|
2023-12-16 15:24:30 +00:00
|
|
|
|
|
2024-05-20 13:46:05 +00:00
|
|
|
|
from sqlalchemy import select
|
2023-12-16 15:24:30 +00:00
|
|
|
|
from starlette.endpoints import HTTPEndpoint
|
2024-04-08 07:38:58 +00:00
|
|
|
|
from starlette.exceptions import HTTPException
|
2023-12-16 15:24:30 +00:00
|
|
|
|
from starlette.requests import Request
|
|
|
|
|
from starlette.responses import JSONResponse
|
|
|
|
|
|
2024-08-09 06:37:06 +00:00
|
|
|
|
from cache.cache import cache_author
|
2023-12-16 15:24:30 +00:00
|
|
|
|
from orm.author import Author
|
2024-05-18 10:57:30 +00:00
|
|
|
|
from resolvers.stat import get_with_stat
|
2024-05-20 13:46:05 +00:00
|
|
|
|
from services.db import local_session
|
2024-12-11 19:07:36 +00:00
|
|
|
|
from services.schema import request_graphql_data
|
2024-12-11 19:21:05 +00:00
|
|
|
|
from settings import ADMIN_SECRET, WEBHOOK_SECRET
|
2024-12-11 19:07:36 +00:00
|
|
|
|
|
|
|
|
|
|
2024-12-11 21:29:04 +00:00
|
|
|
|
async def check_webhook_existence():
|
2024-12-16 16:13:16 +00:00
|
|
|
|
"""
|
|
|
|
|
Проверяет существование вебхука для user.login события
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
tuple: (bool, str, str) - существует ли вебхук, его id и endpoint если существует
|
|
|
|
|
"""
|
2024-12-11 20:01:03 +00:00
|
|
|
|
logger.info("check_webhook_existence called")
|
2024-12-16 17:14:11 +00:00
|
|
|
|
if not ADMIN_SECRET:
|
|
|
|
|
logger.error("ADMIN_SECRET is not set")
|
|
|
|
|
return False, None, None
|
|
|
|
|
|
2024-12-11 20:01:03 +00:00
|
|
|
|
headers = {
|
|
|
|
|
"Content-Type": "application/json",
|
2024-12-16 17:07:56 +00:00
|
|
|
|
"X-Authorizer-Admin-Secret": ADMIN_SECRET
|
2024-12-11 20:01:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
operation = "GetWebhooks"
|
|
|
|
|
query_name = "_webhooks"
|
2024-12-11 20:02:14 +00:00
|
|
|
|
variables = {"params": {}}
|
2024-12-11 20:01:03 +00:00
|
|
|
|
# https://docs.authorizer.dev/core/graphql-api#_webhooks
|
|
|
|
|
gql = {
|
2024-12-11 20:06:55 +00:00
|
|
|
|
"query": f"query {operation}($params: PaginatedInput!)"
|
2024-12-11 20:01:03 +00:00
|
|
|
|
+ "{"
|
2024-12-16 16:50:25 +00:00
|
|
|
|
+ f"{query_name}(params: $params) {{ webhooks {{ id event_name endpoint }} }} "
|
2024-12-11 20:01:03 +00:00
|
|
|
|
+ "}",
|
|
|
|
|
"variables": variables,
|
|
|
|
|
"operationName": operation,
|
|
|
|
|
}
|
|
|
|
|
result = await request_graphql_data(gql, headers=headers)
|
2024-12-11 20:04:45 +00:00
|
|
|
|
if result:
|
2024-12-16 16:13:16 +00:00
|
|
|
|
webhooks = result.get("data", {}).get(query_name, {}).get("webhooks", [])
|
2024-12-16 17:14:11 +00:00
|
|
|
|
logger.info(webhooks)
|
2024-12-16 16:13:16 +00:00
|
|
|
|
for webhook in webhooks:
|
|
|
|
|
if webhook["event_name"].startswith("user.login"):
|
|
|
|
|
return True, webhook["id"], webhook["endpoint"]
|
|
|
|
|
return False, None, None
|
2024-12-11 20:01:03 +00:00
|
|
|
|
|
|
|
|
|
|
2024-12-11 19:07:36 +00:00
|
|
|
|
async def create_webhook_endpoint():
|
2024-12-16 16:13:16 +00:00
|
|
|
|
"""
|
2024-12-16 16:44:24 +00:00
|
|
|
|
Создает вебхук для user.login события.
|
|
|
|
|
Если существует старый вебхук - удаляет его и создает новый.
|
2024-12-16 16:13:16 +00:00
|
|
|
|
"""
|
2024-12-11 19:10:48 +00:00
|
|
|
|
logger.info("create_webhook_endpoint called")
|
2024-12-11 19:07:36 +00:00
|
|
|
|
|
|
|
|
|
headers = {
|
|
|
|
|
"Content-Type": "application/json",
|
2024-12-16 17:07:56 +00:00
|
|
|
|
"X-Authorizer-Admin-Secret": ADMIN_SECRET
|
2024-12-11 19:07:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-12-16 16:13:16 +00:00
|
|
|
|
exists, webhook_id, current_endpoint = await check_webhook_existence()
|
2024-12-16 16:44:24 +00:00
|
|
|
|
|
|
|
|
|
# Определяем endpoint в зависимости от окружения
|
|
|
|
|
host = os.environ.get('HOST', 'core.dscrs.site')
|
|
|
|
|
endpoint = f"https://{host}/new-author"
|
2024-12-16 16:13:16 +00:00
|
|
|
|
|
|
|
|
|
if exists:
|
|
|
|
|
# Если вебхук существует, но с другим endpoint или с модифицированным именем
|
|
|
|
|
if current_endpoint != endpoint or webhook_id:
|
2024-12-16 17:10:39 +00:00
|
|
|
|
# https://docs.authorizer.dev/core/graphql-api#_delete_webhook
|
2024-12-16 16:13:16 +00:00
|
|
|
|
operation = "DeleteWebhook"
|
|
|
|
|
query_name = "_delete_webhook"
|
2024-12-16 17:10:39 +00:00
|
|
|
|
variables = {"params": {"id": webhook_id}} # Изменено с id на webhook_id
|
2024-12-16 16:13:16 +00:00
|
|
|
|
gql = {
|
2024-12-16 16:44:24 +00:00
|
|
|
|
"query": f"mutation {operation}($params: WebhookRequest!)"
|
2024-12-16 16:13:16 +00:00
|
|
|
|
+ "{"
|
|
|
|
|
+ f"{query_name}(params: $params) {{ message }} "
|
|
|
|
|
+ "}",
|
|
|
|
|
"variables": variables,
|
|
|
|
|
"operationName": operation,
|
|
|
|
|
}
|
2024-12-16 16:44:24 +00:00
|
|
|
|
try:
|
|
|
|
|
await request_graphql_data(gql, headers=headers)
|
|
|
|
|
exists = False
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"Failed to delete webhook: {e}")
|
|
|
|
|
# Продолжаем выполнение даже при ошибке удаления
|
|
|
|
|
exists = False
|
2024-12-16 16:13:16 +00:00
|
|
|
|
else:
|
|
|
|
|
logger.info(f"Webhook already exists and configured correctly: {webhook_id}")
|
|
|
|
|
return
|
2024-12-11 20:01:03 +00:00
|
|
|
|
|
2024-12-16 16:13:16 +00:00
|
|
|
|
if not exists:
|
2024-12-16 16:50:25 +00:00
|
|
|
|
# https://docs.authorizer.dev/core/graphql-api#_add_webhook
|
|
|
|
|
operation = "AddWebhook"
|
|
|
|
|
query_name = "_add_webhook"
|
2024-12-16 16:13:16 +00:00
|
|
|
|
variables = {
|
|
|
|
|
"params": {
|
2024-12-16 16:44:24 +00:00
|
|
|
|
"event_name": "user.login",
|
2024-12-16 16:13:16 +00:00
|
|
|
|
"endpoint": endpoint,
|
|
|
|
|
"enabled": True,
|
|
|
|
|
"headers": {"Authorization": WEBHOOK_SECRET},
|
|
|
|
|
}
|
2024-12-11 19:07:36 +00:00
|
|
|
|
}
|
2024-12-16 16:13:16 +00:00
|
|
|
|
gql = {
|
|
|
|
|
"query": f"mutation {operation}($params: AddWebhookRequest!)"
|
|
|
|
|
+ "{"
|
|
|
|
|
+ f"{query_name}(params: $params) {{ message }} "
|
|
|
|
|
+ "}",
|
|
|
|
|
"variables": variables,
|
|
|
|
|
"operationName": operation,
|
|
|
|
|
}
|
2024-12-16 16:44:24 +00:00
|
|
|
|
try:
|
|
|
|
|
result = await request_graphql_data(gql, headers=headers)
|
|
|
|
|
logger.info(result)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"Failed to create webhook: {e}")
|
2023-12-16 15:24:30 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class WebhookEndpoint(HTTPEndpoint):
|
|
|
|
|
async def post(self, request: Request) -> JSONResponse:
|
|
|
|
|
try:
|
|
|
|
|
data = await request.json()
|
2024-03-03 13:59:15 +00:00
|
|
|
|
if not data:
|
2024-04-17 15:32:23 +00:00
|
|
|
|
raise HTTPException(status_code=400, detail="Request body is empty")
|
|
|
|
|
auth = request.headers.get("Authorization")
|
|
|
|
|
if not auth or auth != os.environ.get("WEBHOOK_SECRET"):
|
2024-05-30 04:12:00 +00:00
|
|
|
|
raise HTTPException(status_code=401, detail="Invalid Authorization header")
|
2024-03-04 18:08:01 +00:00
|
|
|
|
# logger.debug(data)
|
2024-04-17 15:32:23 +00:00
|
|
|
|
user = data.get("user")
|
2024-03-03 13:59:15 +00:00
|
|
|
|
if not isinstance(user, dict):
|
2024-05-30 04:12:00 +00:00
|
|
|
|
raise HTTPException(status_code=400, detail="User data is not a dictionary")
|
2024-04-17 16:20:35 +00:00
|
|
|
|
#
|
2024-03-06 10:43:30 +00:00
|
|
|
|
name: str = (
|
|
|
|
|
f"{user.get('given_name', user.get('slug'))} {user.get('middle_name', '')}"
|
2024-04-17 15:31:11 +00:00
|
|
|
|
+ f"{user.get('family_name', '')}".strip()
|
2024-04-17 15:32:23 +00:00
|
|
|
|
) or "Аноним"
|
2024-04-17 16:54:38 +00:00
|
|
|
|
user_id: str = user.get("id", "")
|
2024-04-17 15:32:23 +00:00
|
|
|
|
email: str = user.get("email", "")
|
|
|
|
|
pic: str = user.get("picture", "")
|
2024-04-17 16:54:38 +00:00
|
|
|
|
if user_id:
|
|
|
|
|
with local_session() as session:
|
2024-05-30 04:12:00 +00:00
|
|
|
|
author = session.query(Author).filter(Author.user == user_id).first()
|
2024-04-17 16:54:38 +00:00
|
|
|
|
if not author:
|
|
|
|
|
# If the author does not exist, create a new one
|
|
|
|
|
slug: str = email.split("@")[0].replace(".", "-").lower()
|
|
|
|
|
slug: str = re.sub("[^0-9a-z]+", "-", slug)
|
|
|
|
|
while True:
|
2024-05-30 04:12:00 +00:00
|
|
|
|
author = session.query(Author).filter(Author.slug == slug).first()
|
2024-04-17 16:54:38 +00:00
|
|
|
|
if not author:
|
|
|
|
|
break
|
|
|
|
|
slug = f"{slug}-{len(session.query(Author).filter(Author.email == email).all()) + 1}"
|
|
|
|
|
author = Author(user=user_id, slug=slug, name=name, pic=pic)
|
|
|
|
|
session.add(author)
|
|
|
|
|
session.commit()
|
2024-05-18 11:15:05 +00:00
|
|
|
|
author_query = select(Author).filter(Author.user == user_id)
|
2024-06-11 14:51:34 +00:00
|
|
|
|
result = get_with_stat(author_query)
|
2024-06-11 19:46:35 +00:00
|
|
|
|
if result:
|
2024-06-11 14:51:34 +00:00
|
|
|
|
author_with_stat = result[0]
|
|
|
|
|
author_dict = author_with_stat.dict()
|
|
|
|
|
# await cache_author(author_with_stat)
|
|
|
|
|
asyncio.create_task(cache_author(author_dict))
|
2023-12-16 15:24:30 +00:00
|
|
|
|
|
2024-04-17 15:32:23 +00:00
|
|
|
|
return JSONResponse({"status": "success"})
|
2024-03-03 13:59:15 +00:00
|
|
|
|
except HTTPException as e:
|
2024-05-30 04:12:00 +00:00
|
|
|
|
return JSONResponse({"status": "error", "message": str(e.detail)}, status_code=e.status_code)
|
2023-12-16 15:24:30 +00:00
|
|
|
|
except Exception as e:
|
|
|
|
|
import traceback
|
|
|
|
|
|
|
|
|
|
traceback.print_exc()
|
2024-04-17 15:32:23 +00:00
|
|
|
|
return JSONResponse({"status": "error", "message": str(e)}, status_code=500)
|