139 lines
4.4 KiB
Python
139 lines
4.4 KiB
Python
from database import engine, SessionLocal, Base
|
||
from fastapi import FastAPI, HTTPException, Depends, Security
|
||
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
||
from sqlalchemy.exc import IntegrityError
|
||
from sqlalchemy.orm import Session
|
||
import json
|
||
import models
|
||
import os
|
||
import schemas
|
||
|
||
from sqlalchemy import select, update
|
||
from sqlalchemy.orm import aliased
|
||
|
||
app = FastAPI(
|
||
title="Сервис Счетчиков",
|
||
description="Сервис для инкрементирования, получения и сброса счетчиков с поддержкой namespaces, приложений и версий.",
|
||
version="1.0.0"
|
||
)
|
||
|
||
# Создаем таблицы в базе данных
|
||
Base.metadata.create_all(bind=engine)
|
||
|
||
# Зависимость для получения сессии базы данных
|
||
def get_db():
|
||
db = SessionLocal()
|
||
try:
|
||
yield db
|
||
finally:
|
||
db.close()
|
||
|
||
# Базовая авторизация через Bearer токен
|
||
security = HTTPBearer()
|
||
|
||
|
||
TOKEN: str = os.environ.get("BUILD_INC_TOKEN", None)
|
||
if TOKEN is None:
|
||
THIS_SCRIPT_PATH = os.path.dirname(os.path.abspath(__file__))
|
||
with open(os.path.join(THIS_SCRIPT_PATH, "token.json"), 'r') as f:
|
||
t = json.load(f)
|
||
TOKEN = t["auth.token"]
|
||
|
||
|
||
def get_current_user(credentials: HTTPAuthorizationCredentials = Security(security)):
|
||
if credentials.credentials != TOKEN:
|
||
raise HTTPException(status_code=401, detail="Invalid or missing token")
|
||
return credentials.credentials
|
||
|
||
|
||
@app.post("/{namespace}/{application}/{version}", response_model=schemas.CounterResponse, summary="Инкрементировать счетчик и получить новое значение")
|
||
def increment_counter(
|
||
namespace: str,
|
||
application: str,
|
||
version: str,
|
||
db: Session = Depends(get_db),
|
||
user: str = Depends(get_current_user)
|
||
):
|
||
try:
|
||
# Попытка найти существующий счетчик
|
||
counter = db.query(models.Counter).filter_by(
|
||
namespace=namespace,
|
||
application=application,
|
||
version=version
|
||
).with_for_update().first()
|
||
|
||
if counter:
|
||
counter.value += 1
|
||
else:
|
||
# Если счетчик не существует, создать новый с value=1
|
||
counter = models.Counter(
|
||
namespace=namespace,
|
||
application=application,
|
||
version=version,
|
||
value=1
|
||
)
|
||
db.add(counter)
|
||
|
||
db.commit()
|
||
db.refresh(counter)
|
||
return counter
|
||
|
||
except IntegrityError:
|
||
db.rollback()
|
||
raise HTTPException(status_code=500, detail="Ошибка при доступе к базе данных.")
|
||
|
||
@app.get("/{namespace}/{application}/{version}", response_model=schemas.CounterResponse, summary="Получить текущее значение счетчика")
|
||
def get_counter(
|
||
namespace: str,
|
||
application: str,
|
||
version: str,
|
||
db: Session = Depends(get_db),
|
||
user: str = Depends(get_current_user)
|
||
):
|
||
counter = db.query(models.Counter).filter_by(
|
||
namespace=namespace,
|
||
application=application,
|
||
version=version
|
||
).first()
|
||
|
||
if not counter:
|
||
# Если счетчик не найден, вернуть значение 0
|
||
return schemas.CounterResponse(
|
||
namespace=namespace,
|
||
application=application,
|
||
version=version,
|
||
value=0
|
||
)
|
||
|
||
return counter
|
||
|
||
@app.delete("/{namespace}/{application}/{version}", response_model=schemas.CounterResponse, summary="Сбросить счетчик до 0")
|
||
def reset_counter(
|
||
namespace: str,
|
||
application: str,
|
||
version: str,
|
||
db: Session = Depends(get_db),
|
||
user: str = Depends(get_current_user)
|
||
):
|
||
counter = db.query(models.Counter).filter_by(
|
||
namespace=namespace,
|
||
application=application,
|
||
version=version
|
||
).with_for_update().first()
|
||
|
||
if counter:
|
||
counter.value = 0
|
||
else:
|
||
# Если счетчик не существует, создать его с value=0
|
||
counter = models.Counter(
|
||
namespace=namespace,
|
||
application=application,
|
||
version=version,
|
||
value=0
|
||
)
|
||
db.add(counter)
|
||
|
||
db.commit()
|
||
db.refresh(counter)
|
||
return counter
|