stable secure version
This commit is contained in:
parent
5a0c7e0ae8
commit
98c8e8d545
8 changed files with 180 additions and 114 deletions
|
|
@ -1,8 +1,10 @@
|
|||
import uvicorn
|
||||
from fastapi import FastAPI, File, UploadFile, HTTPException, Depends, Body, Query
|
||||
from fastapi import FastAPI, File, UploadFile, HTTPException, Depends, Body, Query, status
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.responses import JSONResponse, Response
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.security import OAuth2PasswordRequestForm
|
||||
|
||||
import uvicorn
|
||||
from typing import List, Dict, Any, Optional
|
||||
import uuid
|
||||
import os
|
||||
|
|
@ -10,6 +12,10 @@ import json
|
|||
import asyncio
|
||||
from datetime import datetime
|
||||
import logging
|
||||
from database import get_db, Database
|
||||
from agent_service import AgentService, get_agent_service
|
||||
from datetime import timedelta
|
||||
from typing import Dict
|
||||
|
||||
from models import (
|
||||
Workspace,
|
||||
|
|
@ -21,8 +27,16 @@ from models import (
|
|||
LogEntry,
|
||||
Result
|
||||
)
|
||||
from database import get_db, Database
|
||||
from agent_service import AgentService, get_agent_service
|
||||
|
||||
from auth import (
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES,
|
||||
authenticate_user,
|
||||
create_access_token,
|
||||
fake_users_db,
|
||||
get_current_user
|
||||
)
|
||||
|
||||
|
||||
|
||||
# Konfiguration des Loggers
|
||||
logging.basicConfig(
|
||||
|
|
@ -32,18 +46,17 @@ logging.basicConfig(
|
|||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
app = FastAPI(title="Data Platform API", description="Backend-API für die Multi-Agent Datenplattform")
|
||||
app = FastAPI(title="PowerOn | Data Platform API", description="Backend-API für die Multi-Agent Platform von ValueOn AG")
|
||||
|
||||
# CORS-Konfiguration für Frontend-Anfragen
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"], # In Produktion einschränken
|
||||
allow_origins=["http://localhost:8080"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
|
||||
# Pfad zum Webparts-Verzeichnis
|
||||
WEBPARTS_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "webparts")
|
||||
|
||||
|
|
@ -52,7 +65,9 @@ UPLOAD_DIR = os.path.join(os.getcwd(), "uploads")
|
|||
os.makedirs(UPLOAD_DIR, exist_ok=True)
|
||||
|
||||
# Komponenten des Frontends
|
||||
app.mount("/webparts", StaticFiles(directory="webparts"), name="webparts")
|
||||
app.mount("/static", StaticFiles(directory="static"), name="static")
|
||||
|
||||
# API - ENDPUNKTE
|
||||
|
||||
@app.get("/", tags=["General"])
|
||||
async def root():
|
||||
|
|
@ -64,9 +79,34 @@ async def root():
|
|||
async def get_test():
|
||||
return "OK 1.0"
|
||||
|
||||
# Token-Endpunkt
|
||||
@app.post("/token", response_model=Dict[str, str])
|
||||
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
|
||||
user = authenticate_user(fake_users_db, form_data.username, form_data.password)
|
||||
if not user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Ungültige Zugangsdaten",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||
access_token = create_access_token(
|
||||
data={"sub": user["username"]}, expires_delta=access_token_expires
|
||||
)
|
||||
return {"access_token": access_token, "token_type": "bearer"}
|
||||
|
||||
# Geschützter Endpunkt (Beispiel)
|
||||
@app.get("/api/users/me")
|
||||
async def read_users_me(current_user = Depends(get_current_user)):
|
||||
return current_user
|
||||
|
||||
|
||||
# Workspace-Endpunkte
|
||||
@app.get("/api/workspaces", tags=["Workspaces"], response_model=List[Workspace])
|
||||
async def get_workspaces(db: Database = Depends(get_db)):
|
||||
async def get_workspaces(
|
||||
current_user = Depends(get_current_user), # <-- Authentifizierung hinzugefügt
|
||||
db: Database = Depends(get_db)
|
||||
):
|
||||
"""Alle verfügbaren Workspaces abrufen"""
|
||||
return db.get_all_workspaces()
|
||||
|
||||
|
|
@ -187,6 +227,42 @@ async def upload_file(
|
|||
raise HTTPException(status_code=500, detail=f"Fehler beim Hochladen der Datei: {str(e)}")
|
||||
|
||||
|
||||
@app.delete("/api/files/{file_id}", tags=["Files"])
|
||||
async def delete_file(
|
||||
file_id: str,
|
||||
current_user = Depends(get_current_user),
|
||||
db: Database = Depends(get_db)
|
||||
):
|
||||
"""Löscht eine Datei"""
|
||||
try:
|
||||
# Hole die Datei aus der Datenbank
|
||||
file = db.get_file(file_id)
|
||||
if not file:
|
||||
raise HTTPException(status_code=404, detail=f"Datei mit ID {file_id} nicht gefunden")
|
||||
|
||||
# Prüfe, ob die physische Datei existiert
|
||||
if "path" in file and os.path.exists(file["path"]):
|
||||
try:
|
||||
# Versuche, die physische Datei zu löschen
|
||||
os.remove(file["path"])
|
||||
except Exception as e:
|
||||
logger.warning(f"Konnte physische Datei nicht löschen: {e}")
|
||||
|
||||
# Lösche die Datei aus der Datenbank
|
||||
success = db.delete_file(file_id)
|
||||
|
||||
if not success:
|
||||
raise HTTPException(status_code=500, detail="Fehler beim Löschen der Datei aus der Datenbank")
|
||||
|
||||
return {"success": True, "message": f"Datei '{file.get('name', 'unbekannt')}' wurde gelöscht"}
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Löschen der Datei: {e}")
|
||||
raise HTTPException(status_code=500, detail=f"Fehler beim Löschen der Datei: {str(e)}")
|
||||
|
||||
|
||||
# Prompt-Endpunkte
|
||||
@app.get("/api/prompts", tags=["Prompts"], response_model=List[Prompt])
|
||||
async def get_prompts(workspace_id: Optional[str] = Query(None), db: Database = Depends(get_db)):
|
||||
|
|
|
|||
70
gwserver/auth.py
Normal file
70
gwserver/auth.py
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
from datetime import datetime, timedelta
|
||||
from typing import Optional
|
||||
from jose import JWTError, jwt
|
||||
from passlib.context import CryptContext
|
||||
from fastapi import Depends, HTTPException, status
|
||||
from fastapi.security import OAuth2PasswordBearer
|
||||
|
||||
# Konfigurationsvariablen
|
||||
SECRET_KEY = "dein-geheimer-schlüssel" # In Produktion aus Umgebungsvariablen laden!
|
||||
ALGORITHM = "HS256"
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES = 600
|
||||
|
||||
# Password-Hashing
|
||||
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
||||
|
||||
# OAuth2 Setup
|
||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
|
||||
|
||||
# Beispiel-Benutzer (in Produktion aus Datenbank laden)
|
||||
fake_users_db = {
|
||||
"admin": {
|
||||
"username": "admin",
|
||||
"hashed_password": pwd_context.hash("admin123"),
|
||||
}
|
||||
}
|
||||
|
||||
def verify_password(plain_password, hashed_password):
|
||||
return pwd_context.verify(plain_password, hashed_password)
|
||||
|
||||
def get_user(db, username: str):
|
||||
if username in db:
|
||||
user_dict = db[username]
|
||||
return user_dict
|
||||
return None
|
||||
|
||||
def authenticate_user(fake_db, username: str, password: str):
|
||||
user = get_user(fake_db, username)
|
||||
if not user:
|
||||
return False
|
||||
if not verify_password(password, user["hashed_password"]):
|
||||
return False
|
||||
return user
|
||||
|
||||
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
|
||||
to_encode = data.copy()
|
||||
if expires_delta:
|
||||
expire = datetime.utcnow() + expires_delta
|
||||
else:
|
||||
expire = datetime.utcnow() + timedelta(minutes=15)
|
||||
to_encode.update({"exp": expire})
|
||||
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
|
||||
return encoded_jwt
|
||||
|
||||
async def get_current_user(token: str = Depends(oauth2_scheme)):
|
||||
credentials_exception = HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Ungültige Authentifizierungsdaten",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
try:
|
||||
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
|
||||
username: str = payload.get("sub")
|
||||
if username is None:
|
||||
raise credentials_exception
|
||||
except JWTError:
|
||||
raise credentials_exception
|
||||
user = get_user(fake_users_db, username)
|
||||
if user is None:
|
||||
raise credentials_exception
|
||||
return user
|
||||
|
|
@ -1,65 +1,11 @@
|
|||
[
|
||||
{
|
||||
"id": "file_001",
|
||||
"name": "Quartalsbericht Q1 2025.pdf",
|
||||
"id": "0399d060-5beb-46e6-9c3c-830ae7a471cb",
|
||||
"name": "Index.pdf",
|
||||
"type": "document",
|
||||
"path": "D:\\Athi\\Local\\Web\\git-apps\\gateway\\gwserver\\uploads\\0399d060-5beb-46e6-9c3c-830ae7a471cb.pdf",
|
||||
"content_type": "application/pdf",
|
||||
"size": 2500000,
|
||||
"upload_date": "2025-03-14T00:25:25.076526",
|
||||
"path": "uploads/dummy_file1.pdf"
|
||||
},
|
||||
{
|
||||
"id": "file_002",
|
||||
"name": "Marktanalyse-Diagramm.png",
|
||||
"type": "image",
|
||||
"content_type": "image/png",
|
||||
"size": 1150000,
|
||||
"upload_date": "2025-03-14T00:25:25.076526",
|
||||
"path": "uploads/dummy_file2.png"
|
||||
},
|
||||
{
|
||||
"id": "file_003",
|
||||
"name": "Finanzdaten_2024-2025.xlsx",
|
||||
"type": "document",
|
||||
"content_type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
"size": 3880000,
|
||||
"upload_date": "2025-03-14T00:25:25.076526",
|
||||
"path": "uploads/dummy_file3.xlsx"
|
||||
},
|
||||
{
|
||||
"id": "77bb2097-7044-4267-aae6-f2ca1c119ab8",
|
||||
"name": "11 PowerOn HLD - Journey.pdf",
|
||||
"type": "document",
|
||||
"path": "D:\\Athi\\Local\\Web\\git-apps\\gateway\\backend\\uploads\\77bb2097-7044-4267-aae6-f2ca1c119ab8.pdf",
|
||||
"content_type": "application/pdf",
|
||||
"size": 56687,
|
||||
"upload_date": "2025-03-14T00:25:55.641089"
|
||||
},
|
||||
{
|
||||
"id": "f066b3d6-7d59-4851-a8eb-de4334f4a64e",
|
||||
"name": "11 PowerOn HLD - Journey.pdf",
|
||||
"type": "document",
|
||||
"path": "D:\\Athi\\Local\\Web\\git-apps\\gateway\\backend\\uploads\\f066b3d6-7d59-4851-a8eb-de4334f4a64e.pdf",
|
||||
"content_type": "application/pdf",
|
||||
"size": 56687,
|
||||
"upload_date": "2025-03-14T09:46:37.306858"
|
||||
},
|
||||
{
|
||||
"id": "bb36ca5e-cae0-441d-b59c-feb80ecf6d51",
|
||||
"name": "11 PowerOn HLD - Journey.pdf",
|
||||
"type": "document",
|
||||
"path": "D:\\Athi\\Local\\Web\\git-apps\\gateway\\backend\\uploads\\bb36ca5e-cae0-441d-b59c-feb80ecf6d51.pdf",
|
||||
"content_type": "application/pdf",
|
||||
"size": 56687,
|
||||
"upload_date": "2025-03-14T10:53:08.043663"
|
||||
},
|
||||
{
|
||||
"id": "a40ec285-afab-45fe-9668-ba99d4da3a70",
|
||||
"name": "ReportingSheet2025.xlsx",
|
||||
"type": "document",
|
||||
"path": "D:\\Athi\\Local\\Web\\git-apps\\gateway\\backend\\uploads\\a40ec285-afab-45fe-9668-ba99d4da3a70.xlsx",
|
||||
"content_type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
"size": 14727,
|
||||
"upload_date": "2025-03-14T13:55:15.194910"
|
||||
"size": 114510,
|
||||
"upload_date": "2025-03-16T02:10:23.229879"
|
||||
}
|
||||
]
|
||||
|
|
@ -250,6 +250,19 @@ class Database:
|
|||
self._save_data(self.files_file, files)
|
||||
return file_data
|
||||
|
||||
def delete_file(self, file_id: str) -> bool:
|
||||
"""Löscht eine Datei aus der Datenbank"""
|
||||
try:
|
||||
index = next((i for i, file in enumerate(self.data["files"]) if file["id"] == file_id), -1)
|
||||
if index == -1:
|
||||
return False
|
||||
self.data["files"].pop(index)
|
||||
self._save_data()
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"Datenbankfehler beim Löschen der Datei: {e}")
|
||||
return False
|
||||
|
||||
# Prompt-Methoden
|
||||
def get_all_prompts(self) -> List[Dict[str, Any]]:
|
||||
"""Gibt alle Prompts zurück"""
|
||||
|
|
|
|||
1
gwserver/static/test.txt
Normal file
1
gwserver/static/test.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
OK 1.0
|
||||
|
|
@ -16,3 +16,6 @@ openpyxl==3.1.2
|
|||
PyPDF2==3.0.1
|
||||
beautifulsoup4==4.12.2
|
||||
requests==2.31.0
|
||||
python-jose[cryptography]
|
||||
passlib==1.7.4
|
||||
bcrypt==3.2.0
|
||||
43
start.bat
43
start.bat
|
|
@ -1,43 +0,0 @@
|
|||
@echo off
|
||||
echo Data Platform - Multi-Agent Service
|
||||
echo Startskript fuer gwserver
|
||||
echo ----------------------------------------
|
||||
|
||||
:: Verzeichnisstruktur erstellen, falls sie nicht existiert
|
||||
if not exist gwserver\data mkdir gwserver\data
|
||||
if not exist gwserver\uploads mkdir gwserver\uploads
|
||||
if not exist gwserver\results mkdir gwserver\results
|
||||
if not exist gwserver\webparts mkdir gwserver\webparts
|
||||
|
||||
:: Prüfen, ob Python installiert ist
|
||||
python --version >nul 2>&1
|
||||
if %errorlevel% neq 0 (
|
||||
echo Python ist nicht installiert. Bitte installieren Sie Python 3.8 oder hoeher.
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
:: Virtuelle Umgebung erstellen, falls sie nicht existiert
|
||||
if not exist gwserver\venv (
|
||||
echo Erstelle virtuelle Python-Umgebung...
|
||||
cd gwserver
|
||||
python -m venv venv
|
||||
cd ..
|
||||
)
|
||||
|
||||
:: Virtuelle Umgebung aktivieren
|
||||
echo Aktiviere virtuelle Umgebung...
|
||||
call gwserver\venv\Scripts\activate
|
||||
|
||||
:: Abhängigkeiten installieren
|
||||
echo Installiere Abhaengigkeiten...
|
||||
pip install -r requirements.txt
|
||||
|
||||
:: Starte gwserver in neuem Fenster
|
||||
echo Starte gwserver-Server...
|
||||
start cmd /k "cd gwserver && call venv\Scripts\activate && uvicorn app:app --reload --host 0.0.0.0 --port 8000"
|
||||
|
||||
echo ----------------------------------------
|
||||
echo Server wurden gestartet!
|
||||
echo Gateway laeuft auf: http://localhost:8000
|
||||
echo API-Dokumentation: http://localhost:8000/docs
|
||||
pause
|
||||
2
start.sh
2
start.sh
|
|
@ -13,7 +13,7 @@ echo "----------------------------------------"
|
|||
mkdir -p gwserver/data
|
||||
mkdir -p gwserver/uploads
|
||||
mkdir -p gwserver/results
|
||||
mkdir -p gwserver/webparts
|
||||
mkdir -p gwserver/static
|
||||
|
||||
# Prüfen, ob Python installiert ist
|
||||
if command -v python3 &>/dev/null; then
|
||||
|
|
|
|||
Loading…
Reference in a new issue