mirror of
https://github.com/migatu/conjurer.git
synced 2026-07-14 21:38:38 +00:00
1ef75678a1
Intermediate commits (oldest → newest): - Partial + reformatting - upgrade to latest OpenAI API usage and logging improvements - refix - Vibe coding :P - Merge branch 'main' of https://github.com/migatu/conjurer - fx - fx - ffx - testing - ttt - fix - aaa - FX - aaa - sa
156 lines
5.4 KiB
Python
156 lines
5.4 KiB
Python
# -*- coding: utf-8 -*-
|
|
from __future__ import annotations
|
|
import os
|
|
from pathlib import Path
|
|
from typing import List, Dict, Any, Optional
|
|
|
|
from flask import Blueprint, request, jsonify, send_file, abort, current_app
|
|
from werkzeug.utils import secure_filename
|
|
|
|
# ===== Konfiguracja przez ENV =====
|
|
|
|
if API_KEY := os.getenv("API_KEY") is None:
|
|
with open("/home/pi/gpt_cont_api_key", "r") as f:
|
|
API_KEY = f.read().strip()
|
|
else:
|
|
API_KEY = os.getenv("API_KEY", "") # np. openssl rand -hex 32
|
|
|
|
UPLOAD_DIR = Path(os.getenv("UPLOAD_DIR", "/home/pi/tmp_git/")) # katalog na dysku
|
|
MAX_CONTENT_MB = int(os.getenv("MAX_CONTENT_MB", "200"))
|
|
|
|
# Google Drive (opcjonalnie)
|
|
GDRIVE_ENABLE = os.getenv("GDRIVE_ENABLE", "0") == "1"
|
|
GDRIVE_SA_JSON = os.getenv("GDRIVE_SA_JSON", "") # ścieżka do pliku .json konta serwisowego
|
|
GDRIVE_FOLDER_ID = os.getenv("GDRIVE_FOLDER_ID", "") # ID folderu na Drive
|
|
|
|
bp = Blueprint("uploader", __name__)
|
|
|
|
# Inicjalizacja katalogu
|
|
UPLOAD_DIR.mkdir(parents=True, exist_ok=True)
|
|
|
|
def _check_auth() -> bool:
|
|
"""Proste Bearer auth. Jeśli API_KEY puste -> bez auth (niezalecane)."""
|
|
if not API_KEY:
|
|
return True
|
|
auth = request.headers.get("Authorization", "")
|
|
if auth.startswith("Bearer "):
|
|
token = auth[7:].strip()
|
|
return token == API_KEY
|
|
return False
|
|
|
|
def _safe_join(base: Path, *parts: str) -> Path:
|
|
"""Zapobiega ../ — wymusza pozostanie w katalogu bazowym."""
|
|
p = (base.joinpath(*parts)).resolve()
|
|
if not str(p).startswith(str(base.resolve())):
|
|
abort(400, description="Invalid path")
|
|
return p
|
|
|
|
# ===== Google Drive helper =====
|
|
_drive_client_cached = None
|
|
|
|
def _get_drive():
|
|
global _drive_client_cached
|
|
if _drive_client_cached is not None:
|
|
return _drive_client_cached
|
|
|
|
if not (GDRIVE_ENABLE and GDRIVE_SA_JSON and os.path.exists(GDRIVE_SA_JSON)):
|
|
return None
|
|
|
|
from google.oauth2 import service_account
|
|
from googleapiclient.discovery import build
|
|
|
|
scopes = ["https://www.googleapis.com/auth/drive.file"]
|
|
creds = service_account.Credentials.from_service_account_file(GDRIVE_SA_JSON, scopes=scopes)
|
|
_drive_client_cached = build("drive", "v3", credentials=creds, cache_discovery=False)
|
|
return _drive_client_cached
|
|
|
|
def upload_to_drive(local_path: Path, filename: str) -> Optional[Dict[str, Any]]:
|
|
drv = _get_drive()
|
|
if drv is None:
|
|
return None
|
|
from googleapiclient.http import MediaFileUpload
|
|
file_metadata = {"name": filename}
|
|
if GDRIVE_FOLDER_ID:
|
|
file_metadata["parents"] = [GDRIVE_FOLDER_ID]
|
|
media = MediaFileUpload(str(local_path), resumable=False)
|
|
created = drv.files().create(body=file_metadata, media_body=media, fields="id,webViewLink,webContentLink").execute()
|
|
file_id = created.get("id")
|
|
# Przydatne linki:
|
|
return {
|
|
"file_id": file_id,
|
|
"webViewLink": created.get("webViewLink"),
|
|
"webContentLink": created.get("webContentLink"),
|
|
"direct_view": f"https://drive.google.com/file/d/{file_id}/view",
|
|
"direct_download": f"https://drive.google.com/uc?export=download&id={file_id}",
|
|
}
|
|
|
|
@bp.get("/health")
|
|
def health():
|
|
return jsonify(ok=True, info="Okidokie")
|
|
|
|
@bp.post("/upload")
|
|
def upload():
|
|
"""Przyjmuje:
|
|
- multipart/form-data z jednym plikiem ('file') lub wieloma ('files')
|
|
- opcjonalnie: form 'subdir' (podkatalog), 'drive' (1/0) żeby wymusić wysyłkę na Drive
|
|
"""
|
|
if not _check_auth():
|
|
return jsonify(error="Unauthorized"), 401
|
|
|
|
# Limit ciała żądania po stronie Flaska:
|
|
request.max_content_length = MAX_CONTENT_MB * 1024 * 1024
|
|
|
|
files = []
|
|
if "file" in request.files:
|
|
files = [request.files["file"]]
|
|
elif "files" in request.files:
|
|
files = request.files.getlist("files")
|
|
else:
|
|
return jsonify(error="No file provided (use 'file' or 'files')"), 400
|
|
|
|
subdir = (request.form.get("subdir") or "").strip()
|
|
target_dir = _safe_join(UPLOAD_DIR, subdir) if subdir else UPLOAD_DIR
|
|
target_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
want_drive = (request.form.get("drive") == "1") or (request.args.get("drive") == "1")
|
|
saved: List[Dict[str, Any]] = []
|
|
|
|
for f in files:
|
|
if not f or not f.filename:
|
|
continue
|
|
fname = secure_filename(f.filename)
|
|
dest = _safe_join(target_dir, fname)
|
|
f.save(dest)
|
|
|
|
item = {
|
|
"filename": fname,
|
|
"size": dest.stat().st_size,
|
|
"path": str(dest.relative_to(UPLOAD_DIR)),
|
|
"url_hint": f"/api/files/{dest.relative_to(UPLOAD_DIR)}",
|
|
}
|
|
|
|
if want_drive and GDRIVE_ENABLE:
|
|
try:
|
|
gd = upload_to_drive(dest, fname)
|
|
if gd:
|
|
item["gdrive"] = gd
|
|
except Exception as e:
|
|
# log i idziemy dalej
|
|
current_app.logger.exception("Drive upload failed: %s", e)
|
|
item["gdrive_error"] = str(e)
|
|
|
|
saved.append(item)
|
|
|
|
if not saved:
|
|
return jsonify(error="No valid files"), 400
|
|
return jsonify(ok=True, saved=saved)
|
|
|
|
@bp.get("/files/<path:relpath>")
|
|
def get_file(relpath: str):
|
|
if not _check_auth():
|
|
return jsonify(error="Unauthorized"), 401
|
|
target = _safe_join(UPLOAD_DIR, relpath)
|
|
if not target.exists() or not target.is_file():
|
|
return jsonify(error="Not found"), 404
|
|
return send_file(target, as_attachment=False)
|