gitscope
← marzneshin / v0.1.4 / app/routes/node.py
https://dev.kronoz.win/r/marzneshin.git
app/routes/node.py · Python
Blame Raw
  1import logging
  2from datetime import datetime
  3from typing import List
  4
  5import sqlalchemy
  6from fastapi import APIRouter, Depends
  7from fastapi import HTTPException, WebSocket
  8
  9from app import marznode
 10from app.db import crud, get_tls_certificate
 11from app.dependencies import DBDep, SudoAdminDep, sudo_admin, EndDateDep, StartDateDep
 12from app.marznode import MarzNodeGRPCIO
 13from app.models.admin import Admin
 14from app.models.node import (NodeCreate, NodeModify, NodeResponse,
 15                             NodeSettings, NodeStatus, NodesUsageResponse)
 16
 17logger = logging.getLogger(__name__)
 18router = APIRouter(prefix="/nodes", tags=['Node'], dependencies=[Depends(sudo_admin)])
 19
 20
 21@router.get("/settings", response_model=NodeSettings)
 22def get_node_settings(db: DBDep,
 23                      admin: SudoAdminDep):
 24    tls = crud.get_tls_certificate(db)
 25
 26    return NodeSettings(
 27        certificate=tls.certificate
 28    )
 29
 30
 31@router.post("", response_model=NodeResponse)
 32async def add_node(new_node: NodeCreate,
 33                   db: DBDep,
 34                   admin: SudoAdminDep):
 35    try:
 36        db_node = crud.create_node(db, new_node)
 37    except sqlalchemy.exc.IntegrityError:
 38        db.rollback()
 39        raise HTTPException(status_code=409, detail=f"Node \"{new_node.name}\" already exists")
 40    certificate = get_tls_certificate(db)
 41
 42    await marznode.operations.add_node(db_node, certificate)
 43
 44    logger.info("New node `%s` added", db_node.name)
 45    return db_node
 46
 47
 48@router.get("/{node_id}", response_model=NodeResponse)
 49def get_node(node_id: int,
 50             db: DBDep,
 51             admin: SudoAdminDep):
 52    db_node = crud.get_node_by_id(db, node_id)
 53    if not db_node:
 54        raise HTTPException(status_code=404, detail="Node not found")
 55
 56    return db_node
 57
 58
 59@router.websocket("/{node_id}/logs", dependencies=None)
 60async def node_logs(node_id: int,
 61                    websocket: WebSocket,
 62                    db: DBDep):
 63    token = (
 64        websocket.query_params.get('token')
 65        or
 66        websocket.headers.get('Authorization', '').removeprefix("Bearer ")
 67    )
 68    admin = Admin.get_admin(token, db)
 69    if not admin:
 70        return await websocket.close(reason="Unauthorized", code=4401)
 71
 72    if not admin.is_sudo:
 73        return await websocket.close(reason="You're not allowed", code=4403)
 74
 75    if not marznode.nodes.get(node_id):
 76        return await websocket.close(reason="Node not found", code=4404)
 77
 78    if not await xray.nodes[node_id].is_healthy():
 79        return await websocket.close(reason="Node is not connected", code=4400)
 80
 81    interval = websocket.query_params.get('interval')
 82    if interval:
 83        try:
 84            interval = float(interval)
 85        except ValueError:
 86            return await websocket.close(reason="Invalid interval value", code=4400)
 87        if interval > 10:
 88            return await websocket.close(reason="Interval must be more than 0 and at most 10 seconds", code=4400)
 89
 90    await websocket.accept()
 91    async for l in xray.nodes[node_id].get_logs():
 92        await websocket.send_text(l)
 93
 94
 95@router.get("", response_model=List[NodeResponse])
 96def get_nodes(db: DBDep,
 97              admin: SudoAdminDep):
 98    return crud.get_nodes(db)
 99
100
101@router.put("/{node_id}", response_model=NodeResponse)
102async def modify_node(node_id: int,
103                      modified_node: NodeModify,
104                      db: DBDep,
105                      admin: SudoAdminDep):
106    db_node = crud.get_node_by_id(db, node_id)
107    if not db_node:
108        raise HTTPException(status_code=404, detail="Node not found")
109
110    db_node = crud.update_node(db, db_node, modified_node)
111
112    await marznode.operations.remove_node(db_node.id)
113    if db_node.status != NodeStatus.disabled:
114        certificate = get_tls_certificate(db)
115        await marznode.operations.add_node(db_node, certificate)
116
117    logger.info("Node `%s` modified", db_node.name)
118    return db_node
119
120
121@router.post("/{node_id}/resync")
122async def reconnect_node(node_id: int,
123                         db: DBDep,
124                         admin: SudoAdminDep):
125    db_node = crud.get_node_by_id(db, node_id)
126    if not db_node:
127        raise HTTPException(status_code=404, detail="Node not found")
128
129    return {}
130
131
132@router.delete("/{node_id}")
133async def remove_node(node_id: int,
134                      db: DBDep,
135                      admin: SudoAdminDep):
136    db_node = crud.get_node_by_id(db, node_id)
137    if not db_node:
138        raise HTTPException(status_code=404, detail="Node not found")
139
140    crud.remove_node(db, db_node)
141    await marznode.operations.remove_node(db_node.id)
142
143    logger.info(f"Node `%s` deleted", db_node.name)
144    return {}
145
146
147@router.get("/usage", response_model=NodesUsageResponse)
148def get_usage(db: DBDep,
149              admin: SudoAdminDep,
150              start_date: StartDateDep,
151              end_date: EndDateDep):
152    """
153    Get nodes usage
154    """
155    usages = crud.get_nodes_usage(db, start_date, end_date)
156
157    return {"usages": usages}