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, ServiceDep
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
22 if name:
23 query = query.filter(Service.name.ilike(f"%{name}%"))
24
25 if not admin.is_sudo and not admin.all_services_access:
26 query = query.filter(Service.id.in_(admin.service_ids))
27
28 return paginate(query)
29
30
31@router.post("", response_model=ServiceResponse)
32def add_service(new_service: ServiceCreate, db: DBDep, admin: SudoAdminDep):
33 """
34 Add a new service
35
36 - **name** service name
37 - **inbounds** list of inbound ids
38 """
39 try:
40 return crud.create_service(db, new_service)
41 except sqlalchemy.exc.IntegrityError:
42 db.rollback()
43 raise HTTPException(
44 status_code=409, detail="Service by this name already exists"
45 )
46
47
48@router.get("/{id}", response_model=ServiceResponse)
49def get_service(service: ServiceDep, db: DBDep, admin: AdminDep):
50 """
51 Get Service information with id
52 """
53 if not (
54 admin.is_sudo or admin.all_services_access or id in admin.service_ids
55 ):
56 raise HTTPException(status_code=403, detail="You're not allowed")
57
58 return service
59
60
61@router.get("/{id}/users", response_model=Page[UserResponse])
62def get_service_users(service: ServiceDep, db: DBDep, admin: SudoAdminDep):
63 """
64 Get service users
65 """
66 query = (
67 db.query(User)
68 .join(User.services)
69 .where(Service.id == service.id)
70 .filter(User.removed != True)
71 )
72
73 return paginate(query)
74
75
76@router.get("/{id}/inbounds", response_model=Page[Inbound])
77def get_service_inbounds(service: ServiceDep, db: DBDep, admin: SudoAdminDep):
78 """
79 Get service inbounds
80 """
81 query = (
82 db.query(Inbound)
83 .join(Inbound.services)
84 .where(Service.id == service.id)
85 )
86
87 return paginate(query)
88
89
90@router.put("/{id}", response_model=ServiceResponse)
91async def modify_service(
92 service: ServiceDep,
93 modification: ServiceModify,
94 db: DBDep,
95 admin: SudoAdminDep,
96):
97 """
98 Modify Service
99
100 - **name** can be up to 64 characters
101 - **inbounds** list of inbound ids. if not specified no change will be applied;
102 in case of an empty list all inbounds would be removed.
103 """
104 old_inbounds = {(i.node_id, i.protocol, i.tag) for i in service.inbounds}
105 try:
106 response = crud.update_service(db, service, modification)
107 except sqlalchemy.exc.IntegrityError:
108 db.rollback()
109 raise HTTPException(
110 status_code=409, detail="problem updating the service"
111 )
112 else:
113 for user in response.users:
114 if user.activated:
115 marznode.operations.update_user(
116 user, old_inbounds=old_inbounds
117 )
118 return response
119
120
121@router.delete("/{id}")
122def remove_service(service: ServiceDep, db: DBDep, admin: SudoAdminDep):
123 crud.remove_service(db, service)
124 return dict()