Protect recipe API with bearer token
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
GEMINI_API_KEY=replace-with-secret
|
||||
GEMINI_MODEL=gemini-3.6-flash
|
||||
GEMINI_GENERATION_CONFIG={"temperature":0,"maxOutputTokens":300,"thinkingConfig":{"thinkingLevel":"minimal"}}
|
||||
GEMINI_GENERATION_CONFIG={"temperature":0,"maxOutputTokens":300,"thinkingConfig":{"thinkingLevel":"minimal"}}
|
||||
RECIPE_API_TOKEN=replace-with-random-token
|
||||
@@ -0,0 +1,4 @@
|
||||
.env
|
||||
*.env
|
||||
__pycache__/
|
||||
*.pyc
|
||||
@@ -3,11 +3,14 @@
|
||||
Минимальный Flask-сервис на ВМ `5.172.178.213`.
|
||||
|
||||
Вход: `POST /recipe` в формате `multipart/form-data` с полями `image` и
|
||||
`prompt`. Изображение: JPEG/PNG/WEBP, не более 10 MB.
|
||||
`prompt`. Требуется заголовок `Authorization: Bearer <RECIPE_API_TOKEN>`.
|
||||
Изображение: JPEG/PNG/WEBP, не более 10 MB.
|
||||
|
||||
Ключ Gemini и JSON-строка `GEMINI_GENERATION_CONFIG` находятся только в
|
||||
серверном EnvironmentFile. Сервис передаёт изображение, prompt и настройки в
|
||||
Gemini и возвращает `text` и `usage`; разбор рецепта выполняется позднее.
|
||||
Токен `RECIPE_API_TOKEN` также хранится только в EnvironmentFile и не
|
||||
передаётся в Gemini.
|
||||
|
||||
Сервис изолирован от `elmer`: отдельные каталог, virtualenv, пользователь,
|
||||
порт, systemd-юнит и настройки nginx. Изображения на диск не сохраняются.
|
||||
@@ -25,6 +25,12 @@ def settings() -> tuple[str, dict]:
|
||||
return key, config
|
||||
|
||||
|
||||
def authorized() -> bool:
|
||||
expected = os.environ.get("RECIPE_API_TOKEN")
|
||||
authorization = request.headers.get("Authorization", "")
|
||||
return bool(expected and authorization == f"Bearer {expected}")
|
||||
|
||||
|
||||
@app.get("/health")
|
||||
def health():
|
||||
return jsonify(status="ok")
|
||||
@@ -32,6 +38,8 @@ def health():
|
||||
|
||||
@app.post("/recipe")
|
||||
def recipe():
|
||||
if not authorized():
|
||||
return jsonify(error="unauthorized"), 401
|
||||
image = request.files.get("image")
|
||||
prompt = request.form.get("prompt")
|
||||
if image is None or not prompt:
|
||||
|
||||
Reference in New Issue
Block a user