freeleaps-ops/apps/gitea-webhook-ambassador-python/app/main_enhanced.py

313 lines
9.8 KiB
Python

from fastapi import FastAPI, Request, Depends, HTTPException, status
from fastapi.responses import HTMLResponse, RedirectResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from fastapi.middleware.cors import CORSMiddleware
from sqlalchemy.orm import Session
import os
import time
import psutil
from datetime import datetime, timedelta
# Import database models
from app.models.database import create_tables, get_db, APIKey, ProjectMapping, TriggerLog
from app.auth.middleware import auth_middleware, get_current_user
from app.config import settings
# Create FastAPI app
app = FastAPI(
title="Gitea Webhook Ambassador",
description="High-performance Gitea to Jenkins Webhook service",
version="2.0.0"
)
# Add CORS middleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Create database tables
create_tables()
# Mount static files
app.mount("/static", StaticFiles(directory="app/static"), name="static")
# Set up templates
templates = Jinja2Templates(directory="app/templates")
# Startup time
start_time = datetime.now()
@app.get("/", response_class=HTMLResponse)
async def root(request: Request):
"""Root path - redirect to login page"""
return RedirectResponse(url="/login")
@app.get("/login", response_class=HTMLResponse)
async def login_page(request: Request):
"""Login page"""
return templates.TemplateResponse("login.html", {"request": request})
@app.get("/dashboard", response_class=HTMLResponse)
async def dashboard_page(request: Request):
"""Dashboard page"""
return templates.TemplateResponse("dashboard.html", {"request": request})
@app.post("/api/auth/login")
async def login(request: dict):
"""Admin login"""
admin_key = os.getenv("ADMIN_SECRET_KEY", "admin-secret-key-change-in-production")
if request.get("secret_key") != admin_key:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid secret key"
)
# Generate JWT token
token = auth_middleware.create_access_token(
data={"sub": "admin", "role": "admin"}
)
return {"token": token}
@app.get("/api/stats")
async def get_stats(db: Session = Depends(get_db), current_user: dict = Depends(get_current_user)):
"""Get statistics"""
try:
# Get total number of projects
total_projects = db.query(ProjectMapping).count()
# Get total number of API keys
total_api_keys = db.query(APIKey).count()
# Get today's trigger count
today = datetime.now().date()
today_triggers = db.query(TriggerLog).filter(
TriggerLog.created_at >= today
).count()
# Get successful trigger count
successful_triggers = db.query(TriggerLog).filter(
TriggerLog.status == "success"
).count()
return {
"total_projects": total_projects,
"total_api_keys": total_api_keys,
"today_triggers": today_triggers,
"successful_triggers": successful_triggers
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Failed to get statistics: {str(e)}")
@app.get("/api/keys", response_model=dict)
async def list_api_keys(db: Session = Depends(get_db), current_user: dict = Depends(get_current_user)):
"""Get all API keys (frontend compatible)"""
try:
keys = db.query(APIKey).order_by(APIKey.created_at.desc()).all()
return {
"keys": [
{
"id": key.id,
"key": key.key,
"description": key.description,
"created_at": key.created_at.isoformat()
}
for key in keys
]
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Failed to get API keys: {str(e)}")
@app.post("/api/keys", response_model=dict)
async def create_api_key(
request: dict,
db: Session = Depends(get_db),
current_user: dict = Depends(get_current_user)
):
"""Create a new API key (frontend compatible)"""
try:
# Generate new API key
api_key_value = auth_middleware.generate_api_key()
# Save to database
db_key = APIKey(
key=api_key_value,
description=request.get("description", "")
)
db.add(db_key)
db.commit()
db.refresh(db_key)
return {
"id": db_key.id,
"key": db_key.key,
"description": db_key.description,
"created_at": db_key.created_at.isoformat()
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Failed to create API key: {str(e)}")
@app.delete("/api/keys/{key_id}")
async def delete_api_key(
key_id: int,
db: Session = Depends(get_db),
current_user: dict = Depends(get_current_user)
):
"""Delete API key (frontend compatible)"""
try:
key = db.query(APIKey).filter(APIKey.id == key_id).first()
if not key:
raise HTTPException(status_code=404, detail="API key does not exist")
db.delete(key)
db.commit()
return {"message": "API key deleted successfully"}
except HTTPException:
raise
except Exception as e:
raise HTTPException(status_code=500, detail=f"Failed to delete API key: {str(e)}")
@app.get("/api/projects/", response_model=dict)
async def list_projects(db: Session = Depends(get_db), current_user: dict = Depends(get_current_user)):
"""Get all projects (frontend compatible)"""
try:
projects = db.query(ProjectMapping).order_by(ProjectMapping.created_at.desc()).all()
return {
"projects": [
{
"id": project.id,
"name": project.repository_name.split('/')[-1],
"jenkinsJob": project.default_job,
"giteaRepo": project.repository_name,
"created_at": project.created_at.isoformat()
}
for project in projects
]
}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Failed to get project list: {str(e)}")
@app.post("/api/projects/", response_model=dict)
async def create_project(
request: dict,
db: Session = Depends(get_db),
current_user: dict = Depends(get_current_user)
):
"""Create a new project (frontend compatible)"""
try:
# Check if project already exists
existing_project = db.query(ProjectMapping).filter(
ProjectMapping.repository_name == request["giteaRepo"]
).first()
if existing_project:
raise HTTPException(status_code=400, detail="Project already exists")
# Create new project
project = ProjectMapping(
repository_name=request["giteaRepo"],
default_job=request["jenkinsJob"]
)
db.add(project)
db.commit()
db.refresh(project)
return {
"id": project.id,
"name": request["name"],
"jenkinsJob": project.default_job,
"giteaRepo": project.repository_name,
"created_at": project.created_at.isoformat()
}
except HTTPException:
raise
except Exception as e:
raise HTTPException(status_code=500, detail=f"Failed to create project: {str(e)}")
@app.delete("/api/projects/{project_id}")
async def delete_project(
project_id: int,
db: Session = Depends(get_db),
current_user: dict = Depends(get_current_user)
):
"""Delete project (frontend compatible)"""
try:
project = db.query(ProjectMapping).filter(ProjectMapping.id == project_id).first()
if not project:
raise HTTPException(status_code=404, detail="Project does not exist")
db.delete(project)
db.commit()
return {"message": "Project deleted successfully"}
except HTTPException:
raise
except Exception as e:
raise HTTPException(status_code=500, detail=f"Failed to delete project: {str(e)}")
@app.get("/health")
async def health_check():
"""Health check endpoint"""
try:
# Calculate uptime
uptime = datetime.now() - start_time
uptime_str = str(uptime).split('.')[0] # Remove microseconds
# Get memory usage
process = psutil.Process()
memory_info = process.memory_info()
memory_mb = memory_info.rss / 1024 / 1024
return {
"status": "healthy",
"version": "2.0.0",
"uptime": uptime_str,
"memory": f"{memory_mb:.1f} MB",
"timestamp": datetime.now().isoformat()
}
except Exception as e:
return {
"status": "unhealthy",
"error": str(e),
"timestamp": datetime.now().isoformat()
}
@app.get("/api/logs")
async def get_logs(
startTime: str = None,
endTime: str = None,
level: str = None,
query: str = None,
db: Session = Depends(get_db),
current_user: dict = Depends(get_current_user)
):
"""Get logs (simplified version)"""
try:
# Here should be the real log query logic
# Currently returns mock data
logs = [
{
"timestamp": datetime.now().isoformat(),
"level": "info",
"message": "System running normally"
}
]
return {"logs": logs}
except Exception as e:
raise HTTPException(status_code=500, detail=f"Failed to get logs: {str(e)}")
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)