first commit
This commit is contained in:
106
backend/app/api/routes/auth.py
Normal file
106
backend/app/api/routes/auth.py
Normal file
@@ -0,0 +1,106 @@
|
||||
from datetime import timedelta
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.deps import get_current_user, get_db
|
||||
from app.core.config import settings
|
||||
from app.core.security import create_access_token, get_password_hash, verify_password
|
||||
from app.models.user import User
|
||||
from app.schemas.auth import (
|
||||
ChangePasswordRequest,
|
||||
RegisterRequest,
|
||||
TokenResponse,
|
||||
UpdateUserPreferencesRequest,
|
||||
UserResponse,
|
||||
)
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.post("/register", response_model=UserResponse)
|
||||
def register(payload: RegisterRequest, db: Session = Depends(get_db)):
|
||||
if not settings.allow_registration:
|
||||
raise HTTPException(status_code=403, detail="Registration is disabled")
|
||||
existing = db.query(User).filter(User.username == payload.username).first()
|
||||
if existing:
|
||||
raise HTTPException(status_code=409, detail="Username already exists")
|
||||
user = User(username=payload.username, password_hash=get_password_hash(payload.password))
|
||||
db.add(user)
|
||||
db.commit()
|
||||
db.refresh(user)
|
||||
return user
|
||||
|
||||
|
||||
@router.post("/login", response_model=TokenResponse)
|
||||
async def login(request: Request, db: Session = Depends(get_db)):
|
||||
username = None
|
||||
password = None
|
||||
content_type = (request.headers.get("content-type") or "").lower()
|
||||
|
||||
if "application/json" in content_type:
|
||||
try:
|
||||
payload = await request.json()
|
||||
except Exception:
|
||||
payload = {}
|
||||
username = payload.get("username")
|
||||
password = payload.get("password")
|
||||
else:
|
||||
form_data = await request.form()
|
||||
username = form_data.get("username")
|
||||
password = form_data.get("password")
|
||||
|
||||
if not username or not password:
|
||||
raise HTTPException(status_code=422, detail="Username and password are required")
|
||||
|
||||
user = db.query(User).filter(User.username == username).first()
|
||||
if not user or not verify_password(password, user.password_hash):
|
||||
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid credentials")
|
||||
token = create_access_token(
|
||||
subject=user.username,
|
||||
expires_delta=timedelta(minutes=settings.access_token_expire_minutes),
|
||||
)
|
||||
return TokenResponse(access_token=token, user=UserResponse.model_validate(user))
|
||||
|
||||
|
||||
@router.get("/me", response_model=UserResponse)
|
||||
def me(current_user: User = Depends(get_current_user)):
|
||||
return current_user
|
||||
|
||||
|
||||
|
||||
|
||||
@router.put("/preferences", response_model=UserResponse)
|
||||
def update_preferences(
|
||||
payload: UpdateUserPreferencesRequest,
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
preferred_language = (payload.preferred_language or 'pl').strip().lower()
|
||||
preferred_font = (payload.preferred_font or 'default').strip().lower()
|
||||
|
||||
if preferred_language not in {'pl', 'en', 'es', 'no'}:
|
||||
raise HTTPException(status_code=422, detail='Unsupported language')
|
||||
if preferred_font not in {'default', 'adwaita_mono', 'hack'}:
|
||||
raise HTTPException(status_code=422, detail='Unsupported font')
|
||||
|
||||
current_user.preferred_language = preferred_language
|
||||
current_user.preferred_font = preferred_font
|
||||
db.add(current_user)
|
||||
db.commit()
|
||||
db.refresh(current_user)
|
||||
return current_user
|
||||
|
||||
|
||||
@router.post("/change-password")
|
||||
def change_password(
|
||||
payload: ChangePasswordRequest,
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
if not verify_password(payload.current_password, current_user.password_hash):
|
||||
raise HTTPException(status_code=400, detail="Current password is invalid")
|
||||
current_user.password_hash = get_password_hash(payload.new_password)
|
||||
db.add(current_user)
|
||||
db.commit()
|
||||
return {"message": "Password changed successfully"}
|
||||
128
backend/app/api/routes/backups.py
Normal file
128
backend/app/api/routes/backups.py
Normal file
@@ -0,0 +1,128 @@
|
||||
from datetime import date
|
||||
import io
|
||||
import zipfile
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from fastapi.responses import FileResponse, HTMLResponse, StreamingResponse
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.deps import get_current_user, get_db
|
||||
from app.models.user import User
|
||||
from app.schemas.backup import BackupDiffResponse, BackupResponse, BulkActionRequest
|
||||
from app.services.backup_service import backup_service
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("", response_model=list[BackupResponse])
|
||||
def list_backups(
|
||||
search: str | None = Query(default=None),
|
||||
backup_type: str | None = Query(default=None, pattern="^(export|binary)$"),
|
||||
router_id: int | None = Query(default=None),
|
||||
created_on: date | None = Query(default=None),
|
||||
sort_by: str = Query(default="created_at"),
|
||||
order: str = Query(default="desc", pattern="^(asc|desc)$"),
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
return backup_service.list_backups(
|
||||
db,
|
||||
current_user,
|
||||
search=search,
|
||||
backup_type=backup_type,
|
||||
router_id=router_id,
|
||||
created_on=created_on,
|
||||
sort_by=sort_by,
|
||||
order=order,
|
||||
)
|
||||
|
||||
|
||||
@router.get("/router/{router_id}", response_model=list[BackupResponse])
|
||||
def list_router_backups(router_id: int, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
return backup_service.list_router_backups(db, current_user, router_id)
|
||||
|
||||
|
||||
@router.post("/routers/export-all")
|
||||
def export_all(current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
return backup_service.export_all(db, current_user)
|
||||
|
||||
|
||||
@router.post("/routers/binary-all")
|
||||
def binary_all(current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
return backup_service.binary_all(db, current_user)
|
||||
|
||||
|
||||
@router.post("/router/{router_id}/export", response_model=BackupResponse)
|
||||
def export_router(router_id: int, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
return backup_service.export_router(db, current_user, router_id)
|
||||
|
||||
|
||||
@router.post("/router/{router_id}/binary", response_model=BackupResponse)
|
||||
def binary_backup(router_id: int, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
return backup_service.binary_backup(db, current_user, router_id)
|
||||
|
||||
|
||||
@router.post("/router/{router_id}/upload/{backup_id}")
|
||||
def upload_to_router(router_id: int, backup_id: int, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
backup_service.upload_backup_to_router(db, current_user, router_id, backup_id)
|
||||
return {"message": "Backup uploaded to router"}
|
||||
|
||||
|
||||
@router.delete("/{backup_id}")
|
||||
def delete_backup(backup_id: int, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
backup_service.delete_backup(db, current_user, backup_id)
|
||||
return {"message": "Backup deleted"}
|
||||
|
||||
|
||||
@router.get("/{backup_id}/download")
|
||||
def download_backup(backup_id: int, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
backup = backup_service.get_backup_for_user(db, current_user, backup_id)
|
||||
return FileResponse(path=backup.file_path, filename=backup.file_name)
|
||||
|
||||
|
||||
@router.get("/{backup_id}/view")
|
||||
def view_export(backup_id: int, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
backup = backup_service.get_backup_for_user(db, current_user, backup_id)
|
||||
if backup.backup_type != "export":
|
||||
raise HTTPException(status_code=400, detail="Only export backups can be viewed")
|
||||
with open(backup.file_path, "r", encoding="utf-8", errors="ignore") as handle:
|
||||
return {"content": handle.read(), "backup": BackupResponse.model_validate(backup_service._serialize_backup(backup))}
|
||||
|
||||
|
||||
@router.post("/{backup_id}/email")
|
||||
def email_backup(backup_id: int, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
backup_service.email_backup(db, current_user, backup_id)
|
||||
return {"message": "Backup sent by email"}
|
||||
|
||||
|
||||
@router.get("/{left_id}/diff/{right_id}", response_model=BackupDiffResponse)
|
||||
def diff_backups(left_id: int, right_id: int, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
return backup_service.diff_backups(db, current_user, left_id, right_id)
|
||||
|
||||
|
||||
@router.get("/{left_id}/diff/{right_id}/html", response_class=HTMLResponse)
|
||||
def diff_backups_html(left_id: int, right_id: int, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
result = backup_service.diff_backups(db, current_user, left_id, right_id)
|
||||
return HTMLResponse(result["diff_html"])
|
||||
|
||||
|
||||
@router.post("/bulk")
|
||||
def bulk_action(payload: BulkActionRequest, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
backups = [backup_service.get_backup_for_user(db, current_user, backup_id) for backup_id in payload.backup_ids]
|
||||
if payload.action == "delete":
|
||||
for backup in backups:
|
||||
backup_service.delete_backup(db, current_user, backup.id, commit=False)
|
||||
db.commit()
|
||||
return {"message": f"Deleted {len(backups)} backups"}
|
||||
if payload.action == "download":
|
||||
stream = io.BytesIO()
|
||||
with zipfile.ZipFile(stream, "w") as archive:
|
||||
for backup in backups:
|
||||
archive.write(backup.file_path, backup.file_name)
|
||||
stream.seek(0)
|
||||
return StreamingResponse(
|
||||
stream,
|
||||
media_type="application/zip",
|
||||
headers={"Content-Disposition": 'attachment; filename="backups.zip"'},
|
||||
)
|
||||
raise HTTPException(status_code=400, detail="Unsupported bulk action")
|
||||
42
backend/app/api/routes/dashboard.py
Normal file
42
backend/app/api/routes/dashboard.py
Normal file
@@ -0,0 +1,42 @@
|
||||
from fastapi import APIRouter, Depends
|
||||
from sqlalchemy import func
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.deps import get_current_user, get_db
|
||||
from app.models.backup import Backup
|
||||
from app.models.log import OperationLog
|
||||
from app.models.router import Router
|
||||
from app.models.user import User
|
||||
from app.schemas.dashboard import DashboardResponse
|
||||
from app.services.file_service import get_storage_stats
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("", response_model=DashboardResponse)
|
||||
def get_dashboard(current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
routers_count = db.query(func.count(Router.id)).filter(Router.owner_id == current_user.id).scalar() or 0
|
||||
export_count = (
|
||||
db.query(func.count(Backup.id))
|
||||
.join(Router)
|
||||
.filter(Router.owner_id == current_user.id, Backup.backup_type == "export")
|
||||
.scalar()
|
||||
or 0
|
||||
)
|
||||
binary_count = (
|
||||
db.query(func.count(Backup.id))
|
||||
.join(Router)
|
||||
.filter(Router.owner_id == current_user.id, Backup.backup_type == "binary")
|
||||
.scalar()
|
||||
or 0
|
||||
)
|
||||
recent_logs = db.query(OperationLog).order_by(OperationLog.timestamp.desc()).limit(10).all()
|
||||
storage = get_storage_stats()
|
||||
return DashboardResponse(
|
||||
routers_count=routers_count,
|
||||
export_count=export_count,
|
||||
binary_count=binary_count,
|
||||
total_backups=export_count + binary_count,
|
||||
storage=storage,
|
||||
recent_logs=recent_logs,
|
||||
)
|
||||
19
backend/app/api/routes/health.py
Normal file
19
backend/app/api/routes/health.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from sqlalchemy import text
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.deps import get_db
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/health")
|
||||
def health(db: Session = Depends(get_db)):
|
||||
status = "ok"
|
||||
try:
|
||||
db.execute(text("SELECT 1"))
|
||||
except Exception:
|
||||
status = "error"
|
||||
return {"status": status, "timestamp": datetime.now(timezone.utc).isoformat()}
|
||||
32
backend/app/api/routes/logs.py
Normal file
32
backend/app/api/routes/logs.py
Normal file
@@ -0,0 +1,32 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.deps import get_current_user, get_db
|
||||
from app.models.log import OperationLog
|
||||
from app.models.user import User
|
||||
from app.services.log_service import log_service
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("")
|
||||
def get_logs(
|
||||
limit: int = Query(100, ge=1, le=500),
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
_ = current_user
|
||||
return db.query(OperationLog).order_by(OperationLog.timestamp.desc()).limit(limit).all()
|
||||
|
||||
|
||||
@router.delete("/older-than/{days}")
|
||||
def delete_logs(
|
||||
days: int,
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
_ = current_user
|
||||
if days < 1:
|
||||
raise HTTPException(status_code=400, detail="Days must be >= 1")
|
||||
deleted = log_service.delete_older_than(db, days)
|
||||
return {"message": f"Deleted {deleted} logs", "deleted": deleted}
|
||||
120
backend/app/api/routes/routers.py
Normal file
120
backend/app/api/routes/routers.py
Normal file
@@ -0,0 +1,120 @@
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.deps import get_current_user, get_db
|
||||
from app.models.router import Router
|
||||
from app.models.user import User
|
||||
from app.schemas.router import RouterCreate, RouterResponse, RouterTestConnection, RouterUpdate
|
||||
from app.services.router_service import router_service
|
||||
from app.services.settings_service import settings_service
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
def serialize_router(router: Router, global_settings) -> RouterResponse:
|
||||
has_router_key = bool((router.ssh_key or '').strip())
|
||||
has_global_key = bool((global_settings.global_ssh_key or '').strip())
|
||||
router_user = (router.ssh_user or '').strip() or None
|
||||
router_password = (router.ssh_password or '').strip() or None
|
||||
default_swos_user = (global_settings.default_switchos_username or '').strip() or None
|
||||
default_swos_password = (global_settings.default_switchos_password or '').strip() or None
|
||||
effective_username = router_user
|
||||
uses_global_switchos_credentials = False
|
||||
has_effective_password = bool(router_password)
|
||||
|
||||
if router.device_type == 'switchos':
|
||||
effective_username = router_user or default_swos_user
|
||||
uses_global_switchos_credentials = bool(
|
||||
(not router_user and default_swos_user) or (not router_password and default_swos_password)
|
||||
)
|
||||
has_effective_password = bool(router_password or default_swos_password)
|
||||
|
||||
payload = RouterResponse.model_validate(router, from_attributes=True).model_dump()
|
||||
payload['effective_username'] = effective_username
|
||||
payload['uses_global_ssh_key'] = router.device_type == 'routeros' and has_global_key and not has_router_key
|
||||
payload['has_effective_ssh_key'] = router.device_type == 'routeros' and (has_router_key or has_global_key)
|
||||
payload['uses_global_switchos_credentials'] = uses_global_switchos_credentials
|
||||
payload['has_effective_password'] = has_effective_password
|
||||
payload['supports_export'] = router.device_type == 'routeros'
|
||||
payload['supports_restore_upload'] = router.device_type == 'routeros'
|
||||
return RouterResponse.model_validate(payload)
|
||||
|
||||
|
||||
@router.get('', response_model=list[RouterResponse])
|
||||
def list_routers(current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
global_settings = settings_service.get_or_create(db)
|
||||
routers = db.query(Router).filter(Router.owner_id == current_user.id).order_by(Router.created_at.desc()).all()
|
||||
return [serialize_router(router, global_settings) for router in routers]
|
||||
|
||||
|
||||
@router.post('', response_model=RouterResponse)
|
||||
def create_router(payload: RouterCreate, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
router_data = payload.model_dump()
|
||||
if router_data.get('device_type') == 'switchos' and router_data.get('ssh_user') is None:
|
||||
router_data['ssh_user'] = ''
|
||||
router = Router(**router_data, owner_id=current_user.id)
|
||||
db.add(router)
|
||||
db.commit()
|
||||
db.refresh(router)
|
||||
global_settings = settings_service.get_or_create(db)
|
||||
return serialize_router(router, global_settings)
|
||||
|
||||
|
||||
@router.get('/{router_id}', response_model=RouterResponse)
|
||||
def get_router(router_id: int, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
router = db.query(Router).filter(Router.id == router_id, Router.owner_id == current_user.id).first()
|
||||
if not router:
|
||||
raise HTTPException(status_code=404, detail='Device not found')
|
||||
global_settings = settings_service.get_or_create(db)
|
||||
return serialize_router(router, global_settings)
|
||||
|
||||
|
||||
@router.put('/{router_id}', response_model=RouterResponse)
|
||||
def update_router(router_id: int, payload: RouterUpdate, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
router = db.query(Router).filter(Router.id == router_id, Router.owner_id == current_user.id).first()
|
||||
if not router:
|
||||
raise HTTPException(status_code=404, detail='Device not found')
|
||||
changes = payload.model_dump(exclude_unset=True)
|
||||
target_device_type = changes.get('device_type', router.device_type)
|
||||
if target_device_type == 'switchos':
|
||||
changes['ssh_key'] = None
|
||||
if 'port' not in changes:
|
||||
changes['port'] = 80
|
||||
if changes.get('ssh_user') is None:
|
||||
changes['ssh_user'] = ''
|
||||
elif target_device_type == 'routeros' and 'port' not in changes and router.device_type != 'routeros':
|
||||
changes['port'] = 22
|
||||
if not changes.get('ssh_user'):
|
||||
changes['ssh_user'] = router.ssh_user or 'admin'
|
||||
for key, value in changes.items():
|
||||
setattr(router, key, value)
|
||||
db.add(router)
|
||||
db.commit()
|
||||
db.refresh(router)
|
||||
global_settings = settings_service.get_or_create(db)
|
||||
return serialize_router(router, global_settings)
|
||||
|
||||
|
||||
@router.delete('/{router_id}')
|
||||
def delete_router(router_id: int, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
router = db.query(Router).filter(Router.id == router_id, Router.owner_id == current_user.id).first()
|
||||
if not router:
|
||||
raise HTTPException(status_code=404, detail='Device not found')
|
||||
for backup in list(router.backups):
|
||||
path = Path(backup.file_path)
|
||||
if path.exists():
|
||||
path.unlink()
|
||||
db.delete(router)
|
||||
db.commit()
|
||||
return {'message': 'Device deleted'}
|
||||
|
||||
|
||||
@router.get('/{router_id}/test-connection', response_model=RouterTestConnection)
|
||||
def test_connection(router_id: int, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
router = db.query(Router).filter(Router.id == router_id, Router.owner_id == current_user.id).first()
|
||||
if not router:
|
||||
raise HTTPException(status_code=404, detail='Device not found')
|
||||
global_settings = settings_service.get_or_create(db)
|
||||
return router_service.test_connection(db, router, global_settings)
|
||||
78
backend/app/api/routes/settings.py
Normal file
78
backend/app/api/routes/settings.py
Normal file
@@ -0,0 +1,78 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.deps import get_current_user, get_db
|
||||
from app.core.security import verify_password
|
||||
from app.models.settings import GlobalSettings
|
||||
from app.models.user import User
|
||||
from app.schemas.settings import (
|
||||
RevealSshKeyRequest,
|
||||
RevealSshKeyResponse,
|
||||
SchedulerStatusResponse,
|
||||
SettingsResponse,
|
||||
SettingsUpdate,
|
||||
)
|
||||
from app.services.notification_service import notification_service
|
||||
from app.services.scheduler import scheduler_service
|
||||
from app.services.settings_service import settings_service
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
def serialize_settings(settings: GlobalSettings) -> SettingsResponse:
|
||||
payload = SettingsResponse.model_validate(settings, from_attributes=True).model_dump()
|
||||
payload['global_ssh_key'] = None
|
||||
payload['has_global_ssh_key'] = bool((settings.global_ssh_key or '').strip())
|
||||
payload['has_default_switchos_credentials'] = bool(
|
||||
(settings.default_switchos_username or '').strip() or (settings.default_switchos_password or '').strip()
|
||||
)
|
||||
return SettingsResponse.model_validate(payload)
|
||||
|
||||
|
||||
@router.get('', response_model=SettingsResponse)
|
||||
def get_settings(current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
_ = current_user
|
||||
settings = settings_service.get_or_create(db)
|
||||
return serialize_settings(settings)
|
||||
|
||||
|
||||
@router.get('/scheduler-status', response_model=SchedulerStatusResponse)
|
||||
def get_scheduler_status(current_user: User = Depends(get_current_user)):
|
||||
_ = current_user
|
||||
return scheduler_service.scheduler_status()
|
||||
|
||||
|
||||
@router.put('', response_model=SettingsResponse)
|
||||
def update_settings(payload: SettingsUpdate, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
_ = current_user
|
||||
settings = settings_service.update(db, payload)
|
||||
scheduler_service.reschedule()
|
||||
return serialize_settings(settings)
|
||||
|
||||
|
||||
@router.post('/reveal-ssh-key', response_model=RevealSshKeyResponse)
|
||||
def reveal_ssh_key(
|
||||
payload: RevealSshKeyRequest,
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
if not verify_password(payload.password, current_user.password_hash):
|
||||
raise HTTPException(status_code=400, detail='Current password is invalid')
|
||||
settings = settings_service.get_or_create(db)
|
||||
return {'global_ssh_key': settings.global_ssh_key}
|
||||
|
||||
|
||||
@router.post('/test-email')
|
||||
def test_email(current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
_ = current_user
|
||||
settings = settings_service.get_or_create(db)
|
||||
notification_service.send_test_email(settings)
|
||||
return {'message': 'Test email sent'}
|
||||
|
||||
|
||||
@router.post('/test-pushover')
|
||||
def test_pushover(current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
_ = current_user
|
||||
settings = settings_service.get_or_create(db)
|
||||
notification_service.send_test_pushover(settings)
|
||||
return {'message': 'Test pushover sent'}
|
||||
33
backend/app/api/routes/swos_beta.py
Normal file
33
backend/app/api/routes/swos_beta.py
Normal file
@@ -0,0 +1,33 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from fastapi.responses import StreamingResponse
|
||||
|
||||
from app.api.deps import get_current_user
|
||||
from app.models.user import User
|
||||
from app.schemas.swos_beta import SwosBetaCredentials, SwosBetaProbeResponse
|
||||
from app.services.swos_beta_service import swos_beta_service
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.post('/probe', response_model=SwosBetaProbeResponse)
|
||||
def probe_swos(payload: SwosBetaCredentials, current_user: User = Depends(get_current_user)):
|
||||
del current_user
|
||||
try:
|
||||
return swos_beta_service.probe(payload)
|
||||
except ValueError as exc:
|
||||
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
||||
|
||||
|
||||
@router.post('/download')
|
||||
def download_swos_backup(payload: SwosBetaCredentials, current_user: User = Depends(get_current_user)):
|
||||
del current_user
|
||||
try:
|
||||
backup = swos_beta_service.download_backup(payload)
|
||||
except ValueError as exc:
|
||||
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
||||
|
||||
return StreamingResponse(
|
||||
iter([backup.content]),
|
||||
media_type=backup.content_type,
|
||||
headers={'Content-Disposition': f'attachment; filename="{backup.filename}"'},
|
||||
)
|
||||
Reference in New Issue
Block a user