first commit
This commit is contained in:
14
backend/app/api/__init__.py
Normal file
14
backend/app/api/__init__.py
Normal file
@@ -0,0 +1,14 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
from app.api.routes import auth, backups, dashboard, health, logs, routers, settings, swos_beta
|
||||
|
||||
api_router = APIRouter()
|
||||
api_router.include_router(auth.router, prefix="/auth", tags=["auth"])
|
||||
api_router.include_router(dashboard.router, prefix="/dashboard", tags=["dashboard"])
|
||||
api_router.include_router(routers.router, prefix="/routers", tags=["routers"])
|
||||
api_router.include_router(backups.router, prefix="/backups", tags=["backups"])
|
||||
api_router.include_router(settings.router, prefix="/settings", tags=["settings"])
|
||||
api_router.include_router(logs.router, prefix="/logs", tags=["logs"])
|
||||
api_router.include_router(health.router, tags=["health"])
|
||||
|
||||
api_router.include_router(swos_beta.router, prefix='/swos-beta', tags=['swos-beta'])
|
||||
40
backend/app/api/deps.py
Normal file
40
backend/app/api/deps.py
Normal file
@@ -0,0 +1,40 @@
|
||||
from typing import Generator
|
||||
|
||||
from fastapi import Depends, HTTPException, status
|
||||
from fastapi.security import OAuth2PasswordBearer
|
||||
from jose import JWTError, jwt
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.config import settings
|
||||
from app.db.session import SessionLocal
|
||||
from app.models.user import User
|
||||
|
||||
|
||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/auth/login")
|
||||
|
||||
|
||||
def get_db() -> Generator[Session, None, None]:
|
||||
db = SessionLocal()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
def get_current_user(token: str = Depends(oauth2_scheme), db: Session = Depends(get_db)) -> User:
|
||||
credentials_exception = HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Could not validate credentials",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
try:
|
||||
payload = jwt.decode(token, settings.secret_key, algorithms=[settings.jwt_algorithm])
|
||||
username: str | None = payload.get("sub")
|
||||
if username is None:
|
||||
raise credentials_exception
|
||||
except JWTError as exc:
|
||||
raise credentials_exception from exc
|
||||
user = db.query(User).filter(User.username == username).first()
|
||||
if not user:
|
||||
raise credentials_exception
|
||||
return user
|
||||
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"}
|
||||
125
backend/app/api/routes/backups.py
Normal file
125
backend/app/api/routes/backups.py
Normal file
@@ -0,0 +1,125 @@
|
||||
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),
|
||||
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,
|
||||
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}
|
||||
71
backend/app/api/routes/routers.py
Normal file
71
backend/app/api/routes/routers.py
Normal file
@@ -0,0 +1,71 @@
|
||||
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()
|
||||
|
||||
|
||||
@router.get("", response_model=list[RouterResponse])
|
||||
def list_routers(current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
return db.query(Router).filter(Router.owner_id == current_user.id).order_by(Router.created_at.desc()).all()
|
||||
|
||||
|
||||
@router.post("", response_model=RouterResponse)
|
||||
def create_router(payload: RouterCreate, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
router = Router(**payload.model_dump(), owner_id=current_user.id)
|
||||
db.add(router)
|
||||
db.commit()
|
||||
db.refresh(router)
|
||||
return router
|
||||
|
||||
|
||||
@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="Router not found")
|
||||
return router
|
||||
|
||||
|
||||
@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="Router not found")
|
||||
for key, value in payload.model_dump(exclude_unset=True).items():
|
||||
setattr(router, key, value)
|
||||
db.add(router)
|
||||
db.commit()
|
||||
db.refresh(router)
|
||||
return router
|
||||
|
||||
|
||||
@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="Router 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": "Router 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="Router not found")
|
||||
settings = settings_service.get_or_create(db)
|
||||
return router_service.test_connection(db, router, settings.global_ssh_key)
|
||||
75
backend/app/api/routes/settings.py
Normal file
75
backend/app/api/routes/settings.py
Normal file
@@ -0,0 +1,75 @@
|
||||
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())
|
||||
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