1import sqlalchemy
2from fastapi import APIRouter, Query
3from fastapi import HTTPException
4from fastapi_pagination.ext.sqlalchemy import paginate
5from fastapi_pagination.links import Page
6
7from app import marznode
8from app.db import crud
9from app.db.models import Service, User
10from app.dependencies import DBDep, AdminDep, SudoAdminDep
11from app.models.proxy import Inbound
12from app.models.service import ServiceCreate, ServiceModify, ServiceResponse
13from app.models.user import UserResponse
14
15router = APIRouter(prefix="/services", tags=["Service"])
16
17
18@router.get("", response_model=Page[ServiceResponse])
19def get_services(db: DBDep, admin: AdminDep, name: str = Query(None)):
20 query = db.query(Service)
21 if name:
22 query = query.filter(Service.name.ilike(f"%{name}%"))
23 return paginate(query)
24
25
26@router.post("", response_model=ServiceResponse)
27def add_service(new_service: ServiceCreate, db: DBDep, admin: SudoAdminDep):
28 """
29 Add a new service
30
31 - **name** service name
32 - **inbounds** list of inbound ids
33 """
34 try:
35 return crud.create_service(db, new_service)
36 except sqlalchemy.exc.IntegrityError:
37 db.rollback()
38 raise HTTPException(
39 status_code=409, detail="Service by this name already exists"
40 )
41
42
43@router.get("/{id}", response_model=ServiceResponse)
44def get_service(id: int, db: DBDep, admin: AdminDep):
45 """
46 Get Service information with id
47 """
48 dbservice = crud.get_service(db, id)
49 if not dbservice:
50 raise HTTPException(status_code=404, detail="Service not found")
51
52 return dbservice
53
54
55@router.get("/{id}/users", response_model=Page[UserResponse])
56def get_service_users(id: int, db: DBDep, admin: SudoAdminDep):
57 """
58 Get service users
59 """
60 service = crud.get_service(db, id)
61
62 if not service:
63 raise HTTPException(status_code=404, detail="Service not found")
64
65 query = db.query(User).join(User.services).where(Service.id == service.id)
66
67 return paginate(query)
68
69
70@router.get("/{id}/inbounds", response_model=Page[Inbound])
71def get_service_inbounds(id: int, db: DBDep, admin: SudoAdminDep):
72 """
73 Get service inbounds
74 """
75 service = crud.get_service(db, id)
76
77 if not service:
78 raise HTTPException(status_code=404, detail="Service not found")
79
80 query = (
81 db.query(Inbound)
82 .join(Inbound.services)
83 .where(Service.id == service.id)
84 )
85
86 return paginate(query)
87
88
89@router.put("/{id}", response_model=ServiceResponse)
90async def modify_service(
91 id: int, modification: ServiceModify, db: DBDep, admin: SudoAdminDep
92):
93 """
94 Modify Service
95
96 - **name** can be up to 64 characters
97 - **inbounds** list of inbound ids. if not specified no change will be applied;
98 in case of an empty list all inbounds would be removed.
99 """
100 dbservice = crud.get_service(db, id)
101 if not dbservice:
102 raise HTTPException(status_code=404, detail="Service not found")
103 old_inbounds = {(i.node_id, i.protocol, i.tag) for i in dbservice.inbounds}
104 try:
105 response = crud.update_service(db, dbservice, modification)
106 except sqlalchemy.exc.IntegrityError:
107 db.rollback()
108 raise HTTPException(
109 status_code=409, detail="problem updating the service"
110 )
111 else:
112 for user in response.users:
113 if user.activated:
114 marznode.operations.update_user(
115 user, old_inbounds=old_inbounds
116 )
117 return response
118
119
120@router.delete("/{id}")
121def remove_service(id: int, db: DBDep, admin: SudoAdminDep):
122 dbservice = crud.get_service(db, id)
123 if not dbservice:
124 raise HTTPException(status_code=404, detail="Service not found")
125
126 crud.remove_service(db, dbservice)
127 return dict()