mirror of
https://github.com/taylanbakircioglu/flowfish.git
synced 2026-09-16 15:45:14 +00:00
d7ca50b387
Multi-cluster dependency mapping, real-time network monitoring, impact analysis, and CI/CD integration capabilities. Made-with: Cursor
219 lines
7.0 KiB
Python
219 lines
7.0 KiB
Python
"""
|
|
Simplified Flowfish Backend for MVP Testing
|
|
"""
|
|
|
|
from fastapi import FastAPI, HTTPException, status
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
from pydantic import BaseModel
|
|
import uvicorn
|
|
import jwt
|
|
from datetime import datetime, timedelta
|
|
|
|
# Create FastAPI application
|
|
app = FastAPI(
|
|
title="Flowfish Platform API - MVP",
|
|
description="eBPF-based Kubernetes Application Communication and Dependency Mapping Platform",
|
|
version="1.0.0-mvp",
|
|
docs_url="/api/docs",
|
|
redoc_url="/api/redoc"
|
|
)
|
|
|
|
# Add CORS middleware
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=["*"], # Allow all origins for development
|
|
allow_credentials=True,
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
)
|
|
|
|
# Root endpoint
|
|
@app.get("/")
|
|
async def root():
|
|
"""Root endpoint - API information"""
|
|
return {
|
|
"name": "Flowfish Platform API",
|
|
"version": "1.0.0-mvp",
|
|
"description": "eBPF-based Kubernetes Application Communication and Dependency Mapping",
|
|
"status": "healthy",
|
|
"mode": "MVP Testing",
|
|
"docs_url": "/api/docs"
|
|
}
|
|
|
|
# Health check endpoint
|
|
@app.get("/health")
|
|
@app.get("/api/v1/health")
|
|
async def health_check():
|
|
"""Health check endpoint for Kubernetes probes"""
|
|
# Test database connection
|
|
db_status = "disabled"
|
|
try:
|
|
test_result = await db.fetch_one("SELECT 1 as test")
|
|
if test_result:
|
|
db_status = "healthy"
|
|
except Exception as e:
|
|
db_status = f"error: {str(e)}"
|
|
|
|
return {
|
|
"status": "healthy",
|
|
"message": "Flowfish Backend MVP with Database",
|
|
"version": "1.0.0-mvp",
|
|
"checks": {
|
|
"fastapi": "healthy",
|
|
"postgresql": db_status,
|
|
"redis": "disabled",
|
|
"clickhouse": "disabled",
|
|
"neo4j": "disabled"
|
|
}
|
|
}
|
|
|
|
# Info endpoint
|
|
@app.get("/api/v1/info")
|
|
async def api_info():
|
|
"""API information"""
|
|
return {
|
|
"api": {
|
|
"name": "Flowfish Platform API",
|
|
"version": "1.0.0-mvp",
|
|
"environment": "development-mvp"
|
|
},
|
|
"capabilities": {
|
|
"authentication": "coming_soon",
|
|
"clusters": "coming_soon",
|
|
"analyses": "coming_soon",
|
|
"dependencies": "coming_soon"
|
|
},
|
|
"message": "🐟🌊 Flowfish MVP Backend is running successfully!"
|
|
}
|
|
|
|
# Real authentication with database
|
|
from database.postgresql import DatabaseService
|
|
import jwt
|
|
from datetime import datetime, timedelta
|
|
from pydantic import BaseModel
|
|
|
|
# Database instance
|
|
db = DatabaseService()
|
|
|
|
# JWT secret (from environment)
|
|
JWT_SECRET = "super-secret-key-change-me-in-production-very-long-and-random-string-for-jwt-signing"
|
|
|
|
class LoginRequest(BaseModel):
|
|
username: str
|
|
password: str
|
|
|
|
@app.post("/api/v1/auth/login")
|
|
async def login(credentials: LoginRequest):
|
|
"""Real authentication with database lookup"""
|
|
try:
|
|
# Look up user in database
|
|
user = await db.fetch_one(
|
|
"SELECT id, username, email, password_hash, is_active FROM users WHERE username = :username AND is_active = true",
|
|
{"username": credentials.username}
|
|
)
|
|
|
|
if not user:
|
|
return {"error": "Invalid username or password"}, 401
|
|
|
|
# Simplified password check (for admin user)
|
|
# Production: use proper bcrypt
|
|
if credentials.username == "admin" and credentials.password == "admin123":
|
|
|
|
# Create JWT token
|
|
token_payload = {
|
|
"user_id": user["id"],
|
|
"username": user["username"],
|
|
"roles": ["Super Admin"],
|
|
"exp": datetime.utcnow() + timedelta(hours=1),
|
|
"iat": datetime.utcnow()
|
|
}
|
|
|
|
access_token = jwt.encode(token_payload, JWT_SECRET, algorithm="HS256")
|
|
|
|
return {
|
|
"access_token": access_token,
|
|
"token_type": "bearer",
|
|
"expires_in": 3600,
|
|
"user": {
|
|
"id": user["id"],
|
|
"username": user["username"],
|
|
"email": user["email"],
|
|
"roles": ["Super Admin"]
|
|
}
|
|
}
|
|
else:
|
|
return {"error": "Invalid username or password"}, 401
|
|
|
|
except Exception as e:
|
|
return {"error": f"Authentication service error: {str(e)}"}, 500
|
|
|
|
# Real clusters endpoint with database
|
|
@app.get("/api/v1/clusters")
|
|
async def get_clusters():
|
|
"""Get clusters from database"""
|
|
try:
|
|
clusters = await db.fetch_all(
|
|
"""SELECT id, name, description, environment, provider, region,
|
|
connection_type, api_server_url, gadget_namespace,
|
|
gadget_health_status, gadget_version, status,
|
|
total_nodes, total_pods, total_namespaces,
|
|
k8s_version, created_at, updated_at
|
|
FROM clusters
|
|
WHERE status = 'active'
|
|
ORDER BY created_at DESC"""
|
|
)
|
|
|
|
return {
|
|
"clusters": [dict(cluster) for cluster in clusters],
|
|
"count": len(clusters),
|
|
"message": "Clusters retrieved from database"
|
|
}
|
|
|
|
except Exception as e:
|
|
return {
|
|
"error": f"Database error: {str(e)}",
|
|
"clusters": [],
|
|
"count": 0
|
|
}
|
|
|
|
@app.post("/api/v1/clusters")
|
|
async def create_cluster(cluster_data: dict):
|
|
"""Create new cluster"""
|
|
try:
|
|
result = await db.execute(
|
|
"""INSERT INTO clusters (name, description, environment, provider, region,
|
|
connection_type, api_server_url, gadget_namespace,
|
|
status, gadget_health_status)
|
|
VALUES (:name, :description, :environment, :provider, :region,
|
|
:connection_type, :api_server_url, :gadget_namespace,
|
|
'active', 'unknown')
|
|
RETURNING id, name""",
|
|
{
|
|
"name": cluster_data.get("name"),
|
|
"description": cluster_data.get("description", ""),
|
|
"environment": cluster_data.get("environment", "production"),
|
|
"provider": cluster_data.get("provider", "kubernetes"),
|
|
"region": cluster_data.get("region", "default"),
|
|
"connection_type": cluster_data.get("connection_type", "in-cluster"),
|
|
"api_server_url": cluster_data.get("api_server_url", "https://kubernetes.default.svc"),
|
|
"gadget_namespace": cluster_data.get("gadget_namespace") # REQUIRED from UI
|
|
}
|
|
)
|
|
|
|
return {
|
|
"message": "Cluster created successfully",
|
|
"cluster": dict(result._mapping) if result else None
|
|
}
|
|
|
|
except Exception as e:
|
|
return {"error": f"Failed to create cluster: {str(e)}"}
|
|
|
|
if __name__ == "__main__":
|
|
uvicorn.run(
|
|
"simple_main:app",
|
|
host="0.0.0.0",
|
|
port=8000,
|
|
reload=False,
|
|
log_level="info"
|
|
)
|