313 lines
9.8 KiB
Python
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) |