new awesome build
This commit is contained in:
0
APP_PROFILER/routers/__init__.py
Normal file
0
APP_PROFILER/routers/__init__.py
Normal file
137
APP_PROFILER/routers/profiles.py
Normal file
137
APP_PROFILER/routers/profiles.py
Normal file
@@ -0,0 +1,137 @@
|
||||
import os
|
||||
from fastapi import APIRouter, Depends, HTTPException, BackgroundTasks
|
||||
from fastapi.responses import FileResponse
|
||||
from sqlalchemy.orm import Session
|
||||
from database import get_db
|
||||
from utils.auth import verify_token
|
||||
from models import UserProfile
|
||||
from schemas import UserProfile as UserProfileSchema, UserProfileCreate
|
||||
from services import pki, generator
|
||||
from datetime import datetime
|
||||
|
||||
router = APIRouter(dependencies=[Depends(verify_token)])
|
||||
|
||||
@router.get("/profiles", response_model=list[UserProfileSchema])
|
||||
def list_profiles(db: Session = Depends(get_db)):
|
||||
# 1. Fetch profiles from DB
|
||||
profiles = db.query(UserProfile).all()
|
||||
|
||||
# 2. Get PKI Data (Index mapping: CN -> Expiration Date)
|
||||
pki_data = pki.get_pki_index_data(db)
|
||||
|
||||
now = datetime.utcnow()
|
||||
|
||||
updated_profiles = []
|
||||
|
||||
for profile in profiles:
|
||||
# Sync expiration if available in PKI data
|
||||
if profile.username in pki_data:
|
||||
exp_date = pki_data[profile.username]
|
||||
# Update DB if different
|
||||
if profile.expiration_date != exp_date:
|
||||
profile.expiration_date = exp_date
|
||||
db.add(profile)
|
||||
|
||||
# Calculate derived fields
|
||||
|
||||
# 1. is_expired
|
||||
is_expired = False
|
||||
if profile.expiration_date:
|
||||
if now > profile.expiration_date:
|
||||
is_expired = True
|
||||
|
||||
# 2. is_revoked
|
||||
# (Assuming status='revoked' in DB is the source of truth)
|
||||
is_revoked = profile.status == 'revoked'
|
||||
|
||||
# 3. days_remaining (computed field)
|
||||
days_remaining = None
|
||||
if profile.expiration_date:
|
||||
delta = profile.expiration_date - now
|
||||
days_remaining = delta.days
|
||||
|
||||
# Update DB fields for persistence if they differ
|
||||
if profile.is_expired != is_expired:
|
||||
profile.is_expired = is_expired
|
||||
db.add(profile)
|
||||
|
||||
if profile.is_revoked != is_revoked:
|
||||
profile.is_revoked = is_revoked
|
||||
db.add(profile)
|
||||
|
||||
# Inject computed fields for response schema
|
||||
# Since 'days_remaining' is not a DB column, we attach it to the object instance
|
||||
setattr(profile, 'days_remaining', days_remaining)
|
||||
|
||||
updated_profiles.append(profile)
|
||||
|
||||
db.commit() # Save any updates
|
||||
|
||||
return updated_profiles
|
||||
|
||||
@router.post("/profiles", response_model=UserProfileSchema)
|
||||
def create_profile(
|
||||
profile_in: UserProfileCreate,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
# Check existing
|
||||
existing = db.query(UserProfile).filter(UserProfile.username == profile_in.username).first()
|
||||
if existing:
|
||||
raise HTTPException(status_code=400, detail="User already exists")
|
||||
|
||||
# Build PKI
|
||||
try:
|
||||
pki.build_client(profile_in.username, db)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"PKI Build failed: {str(e)}")
|
||||
|
||||
# Generate Config
|
||||
client_conf_dir = "client-config"
|
||||
os.makedirs(client_conf_dir, exist_ok=True)
|
||||
file_path = os.path.join(client_conf_dir, f"{profile_in.username}.ovpn")
|
||||
|
||||
try:
|
||||
generator.generate_client_config(db, profile_in.username, file_path)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"Config Generation failed: {str(e)}")
|
||||
|
||||
# Create DB Entry
|
||||
new_profile = UserProfile(
|
||||
username=profile_in.username,
|
||||
status="active",
|
||||
created_at=datetime.utcnow(),
|
||||
file_path=file_path
|
||||
# expired_at would be extracted from cert in a real robust implementation
|
||||
)
|
||||
db.add(new_profile)
|
||||
db.commit()
|
||||
db.refresh(new_profile)
|
||||
return new_profile
|
||||
|
||||
@router.delete("/profiles/{profile_id}")
|
||||
def revoke_profile(profile_id: int, db: Session = Depends(get_db)):
|
||||
profile = db.query(UserProfile).filter(UserProfile.id == profile_id).first()
|
||||
if not profile:
|
||||
raise HTTPException(status_code=404, detail="Profile not found")
|
||||
|
||||
try:
|
||||
pki.revoke_client(profile.username, db)
|
||||
except Exception as e:
|
||||
# Log but maybe continue to update DB status?
|
||||
raise HTTPException(status_code=500, detail=f"Revocation failed: {str(e)}")
|
||||
|
||||
profile.status = "revoked"
|
||||
profile.revoked_at = datetime.utcnow()
|
||||
db.commit()
|
||||
return {"message": f"Profile {profile.username} revoked"}
|
||||
|
||||
@router.get("/profiles/{profile_id}/download")
|
||||
def download_profile(profile_id: int, db: Session = Depends(get_db)):
|
||||
profile = db.query(UserProfile).filter(UserProfile.id == profile_id).first()
|
||||
if not profile:
|
||||
raise HTTPException(status_code=404, detail="Profile not found")
|
||||
|
||||
if not profile.file_path or not os.path.exists(profile.file_path):
|
||||
raise HTTPException(status_code=404, detail="Config file not found")
|
||||
|
||||
return FileResponse(profile.file_path, filename=os.path.basename(profile.file_path))
|
||||
25
APP_PROFILER/routers/server.py
Normal file
25
APP_PROFILER/routers/server.py
Normal file
@@ -0,0 +1,25 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
from database import get_db
|
||||
from utils.auth import verify_token
|
||||
from services import generator
|
||||
|
||||
router = APIRouter(dependencies=[Depends(verify_token)])
|
||||
|
||||
@router.post("/server/configure")
|
||||
def configure_server(db: Session = Depends(get_db)):
|
||||
try:
|
||||
# Generate to a temporary location or standard location
|
||||
# As per plan, we behave like srvconf
|
||||
output_path = "/etc/openvpn/server.conf"
|
||||
# Since running locally for dev, maybe output to staging
|
||||
import os
|
||||
if not os.path.exists("/etc/openvpn"):
|
||||
# For local dev safety, don't try to write to /etc/openvpn if not root or not existing
|
||||
output_path = "staging/server.conf"
|
||||
os.makedirs("staging", exist_ok=True)
|
||||
|
||||
content = generator.generate_server_config(db, output_path=output_path)
|
||||
return {"message": "Server configuration generated", "path": output_path}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
44
APP_PROFILER/routers/server_process.py
Normal file
44
APP_PROFILER/routers/server_process.py
Normal file
@@ -0,0 +1,44 @@
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from pydantic import BaseModel
|
||||
from services import process
|
||||
from utils.auth import verify_token
|
||||
from typing import Optional
|
||||
from fastapi import Depends
|
||||
|
||||
router = APIRouter(dependencies=[Depends(verify_token)])
|
||||
|
||||
class ProcessActionResponse(BaseModel):
|
||||
status: str
|
||||
message: str
|
||||
stdout: Optional[str] = None
|
||||
stderr: Optional[str] = None
|
||||
|
||||
class ProcessStats(BaseModel):
|
||||
status: str
|
||||
pid: Optional[int] = None
|
||||
cpu_percent: float
|
||||
memory_mb: float
|
||||
uptime: Optional[str] = None
|
||||
|
||||
@router.post("/server/process/{action}", response_model=ProcessActionResponse)
|
||||
def manage_process(action: str):
|
||||
"""
|
||||
Control the OpenVPN server process.
|
||||
Action: start, stop, restart
|
||||
"""
|
||||
if action not in ["start", "stop", "restart"]:
|
||||
raise HTTPException(status_code=400, detail="Invalid action. Use start, stop, or restart")
|
||||
|
||||
result = process.control_service(action)
|
||||
|
||||
if result["status"] == "error":
|
||||
raise HTTPException(status_code=500, detail=result["message"])
|
||||
|
||||
return result
|
||||
|
||||
@router.get("/server/process/stats", response_model=ProcessStats)
|
||||
def get_process_stats():
|
||||
"""
|
||||
Get current telemetry for the OpenVPN process.
|
||||
"""
|
||||
return process.get_process_stats()
|
||||
50
APP_PROFILER/routers/system.py
Normal file
50
APP_PROFILER/routers/system.py
Normal file
@@ -0,0 +1,50 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from sqlalchemy.orm import Session
|
||||
from database import get_db
|
||||
from utils.auth import verify_token
|
||||
from schemas import (
|
||||
ConfigResponse, SystemSettings, PKISetting,
|
||||
SystemSettingsUpdate, PKISettingUpdate
|
||||
)
|
||||
from services import config, pki
|
||||
|
||||
router = APIRouter(dependencies=[Depends(verify_token)])
|
||||
|
||||
@router.get("/config", response_model=ConfigResponse)
|
||||
def get_config(
|
||||
section: str = Query(None, enum=["server", "pki"]),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
response = ConfigResponse()
|
||||
if section is None or section == "server":
|
||||
response.server = config.get_system_settings(db)
|
||||
if section is None or section == "pki":
|
||||
response.pki = config.get_pki_settings(db)
|
||||
return response
|
||||
|
||||
@router.put("/config/server", response_model=SystemSettings)
|
||||
def update_server_config(
|
||||
settings: SystemSettingsUpdate,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
return config.update_system_settings(db, settings)
|
||||
|
||||
@router.put("/config/pki", response_model=PKISetting)
|
||||
def update_pki_config(
|
||||
settings: PKISettingUpdate,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
return config.update_pki_settings(db, settings)
|
||||
|
||||
@router.post("/system/init")
|
||||
def init_system_pki(db: Session = Depends(get_db)):
|
||||
try:
|
||||
msg = pki.init_pki(db)
|
||||
return {"message": msg}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@router.delete("/system/pki")
|
||||
def clear_system_pki(db: Session = Depends(get_db)):
|
||||
msg = pki.clear_pki(db)
|
||||
return {"message": msg}
|
||||
Reference in New Issue
Block a user