diff --git a/app/db/__init__.py b/app/db/__init__.py index 67880dc..fc01894 100644 --- a/app/db/__init__.py +++ b/app/db/__init__.py @@ -1,11 +1,11 @@ from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.orm import Session -from sqlalchemy.schema import MetaData from .base import Base, SessionLocal, engine # noqa # meta_obj = MetaData() + class GetDB: # Context Manager def __init__(self): self.db = SessionLocal() @@ -39,7 +39,6 @@ from .models import JWT, System, User # noqa # meta_obj.create_all(engine) __all__ = [ - "get_or_create_inbound", "get_user", "get_user_by_id", "get_users", diff --git a/app/dependencies.py b/app/dependencies.py new file mode 100644 index 0000000..c63f416 --- /dev/null +++ b/app/dependencies.py @@ -0,0 +1,68 @@ +from typing import Annotated + +from fastapi import Depends, HTTPException, status +from sqlalchemy.orm import Session + +from app.db import crud, get_db, User +from app.models.admin import Admin, oauth2_scheme +from app.utils.jwt import get_admin_payload +from config import SUDOERS + + +def get_admin(db: Annotated[Session, Depends(get_db)], + token: Annotated[str, Depends(oauth2_scheme)]): + payload = get_admin_payload(token) + if not payload: + return + + if payload['username'] in SUDOERS and payload['is_sudo'] is True: + return Admin(username=payload['username'], is_sudo=True) + + dbadmin = crud.get_admin(db, payload['username']) + if not dbadmin: + return + + if dbadmin.password_reset_at: + if not payload.get("created_at"): + return + if dbadmin.password_reset_at > payload.get("created_at"): + return + + return Admin.model_validate(dbadmin) + + +def get_current_admin(admin: Annotated[Admin, Depends(get_admin)]): + if not admin: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Could not validate credentials", + headers={"WWW-Authenticate": "Bearer"}, + ) + + return admin + + +def sudo_admin(admin: Annotated[Admin, Depends(get_current_admin)]): + if not admin.is_sudo: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="Access Denied", + ) + + +def get_current_user(username: str, key: str, db: Annotated[Session, Depends(get_db)]): + try: + int(key, 16) + except ValueError: + raise HTTPException(404) + + db_user = crud.get_user(db, username) + if not db_user or db_user.key != key: + raise HTTPException(404) + return db_user + + +SubUserDep = Annotated[User, Depends(get_current_user)] +AdminDep = Annotated[Admin, Depends(get_current_admin)] +SudoAdminDep = Annotated[Admin, Depends(sudo_admin)] +DBDep = Annotated[Session, Depends(get_db)] diff --git a/app/models/admin.py b/app/models/admin.py index 2df71db..41e9982 100644 --- a/app/models/admin.py +++ b/app/models/admin.py @@ -1,14 +1,9 @@ from typing import Optional -from fastapi import Depends, HTTPException, status from fastapi.security import OAuth2PasswordBearer from passlib.context import CryptContext from pydantic import ConfigDict, BaseModel -from app.db import Session, crud, get_db -from app.utils.jwt import get_admin_payload -from config import SUDOERS - pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/admin/token") # Admin view url @@ -23,41 +18,6 @@ class Admin(BaseModel): is_sudo: bool model_config = ConfigDict(from_attributes=True) - @classmethod - def get_admin(cls, token: str, db: Session): - payload = get_admin_payload(token) - if not payload: - return - - if payload['username'] in SUDOERS and payload['is_sudo'] is True: - return cls(username=payload['username'], is_sudo=True) - - dbadmin = crud.get_admin(db, payload['username']) - if not dbadmin: - return - - if dbadmin.password_reset_at: - if not payload.get("created_at"): - return - if dbadmin.password_reset_at > payload.get("created_at"): - return - - return cls.from_orm(dbadmin) - - @classmethod - def get_current(cls, - db: Session = Depends(get_db), - token: str = Depends(oauth2_scheme)): - admin = cls.get_admin(token, db) - if not admin: - raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail="Could not validate credentials", - headers={"WWW-Authenticate": "Bearer"}, - ) - - return admin - class AdminCreate(Admin): password: str diff --git a/app/utils/jwt.py b/app/utils/jwt.py index aca1ad2..f238113 100644 --- a/app/utils/jwt.py +++ b/app/utils/jwt.py @@ -4,12 +4,12 @@ from typing import Union from jose import JWTError, jwt -from config import JWT_ACCESS_TOKEN_EXPIRE_MINUTES +from config import JWT_ACCESS_TOKEN_EXPIRE_MINUTES, SUDOERS +from app.db import GetDB, get_jwt_secret_key @lru_cache(maxsize=None) def get_secret_key(): - from app.db import GetDB, get_jwt_secret_key with GetDB() as db: return get_jwt_secret_key(db) diff --git a/app/views/admin.py b/app/views/admin.py index fce4cee..1d5a15f 100644 --- a/app/views/admin.py +++ b/app/views/admin.py @@ -1,10 +1,11 @@ -from typing import List, Optional +from typing import List, Optional, Annotated import sqlalchemy from app import app -from app.db import Session, crud, get_db +from app.db import Session, crud from app.models.admin import Admin, AdminCreate, AdminInDB, AdminModify, Token from app.utils.jwt import create_admin_token +from app.dependencies import AdminDep, SudoAdminDep, DBDep from config import SUDOERS from fastapi import Depends, HTTPException, status from fastapi.security import OAuth2PasswordRequestForm @@ -26,8 +27,8 @@ def authenticate_admin(db: Session, username: str, password: str) -> Optional[Ad @app.post("/api/admin/token", tags=['Admin'], response_model=Token) -def admin_token(form_data: OAuth2PasswordRequestForm = Depends(), - db: Session = Depends(get_db)): +def admin_token(form_data: Annotated[OAuth2PasswordRequestForm, Depends()], + db: DBDep): if authenticate_env_sudo(form_data.username, form_data.password): return Token(access_token=create_admin_token(form_data.username, is_sudo=True)) @@ -43,12 +44,8 @@ def admin_token(form_data: OAuth2PasswordRequestForm = Depends(), @app.post("/api/admin", tags=['Admin'], response_model=Admin) def create_admin(new_admin: AdminCreate, - db: Session = Depends(get_db), - admin: Admin = Depends(Admin.get_current)): - - if not admin.is_sudo: - raise HTTPException(status_code=403, detail="You're not allowed") - + db: DBDep, + admin: SudoAdminDep): try: dbadmin = crud.create_admin(db, new_admin) except sqlalchemy.exc.IntegrityError: @@ -61,8 +58,8 @@ def create_admin(new_admin: AdminCreate, @app.put("/api/admin/{username}", tags=['Admin'], response_model=Admin) def modify_admin(username: str, modified_admin: AdminModify, - db: Session = Depends(get_db), - admin: Admin = Depends(Admin.get_current)): + db: DBDep, + admin: AdminDep): if not (admin.is_sudo or admin.username == username): raise HTTPException(status_code=403, detail="You're not allowed") @@ -78,7 +75,7 @@ def modify_admin(username: str, if (username != admin.username) and dbadmin.is_sudo: raise HTTPException( status_code=403, - detail=("You're not allowed to edit another sudoers account. Use marzban-cli instead.",), + detail="You're not allowed to edit another sudoers account. Use marzban-cli instead.", ) dbadmin = crud.update_admin(db, dbadmin, modified_admin) @@ -87,11 +84,8 @@ def modify_admin(username: str, @app.delete("/api/admin/{username}", tags=['Admin']) def remove_admin(username: str, - db: Session = Depends(get_db), - admin: Admin = Depends(Admin.get_current)): - if not admin.is_sudo: - raise HTTPException(status_code=403, detail="You're not allowed") - + db: DBDep, + admin: SudoAdminDep): dbadmin = crud.get_admin(db, username) if not dbadmin: raise HTTPException(status_code=404, detail="Admin not found") @@ -99,26 +93,22 @@ def remove_admin(username: str, if dbadmin.is_sudo: raise HTTPException( status_code=403, - detail=("You're not allowed to delete sudoers accounts. Use marzban-cli instead."), + detail="You're not allowed to delete sudoers accounts. Use marzban-cli instead.", ) - dbadmin = crud.remove_admin(db, dbadmin) + crud.remove_admin(db, dbadmin) return {} @app.get("/api/admin", tags=["Admin"], response_model=Admin) -def get_current_admin(admin: Admin = Depends(Admin.get_current)): +def get_current_admin(admin: AdminDep): return admin @app.get("/api/admins", tags=['Admin'], response_model=List[Admin]) -def get_admins(offset: int = None, +def get_admins(db: DBDep, + admin: SudoAdminDep, + offset: int = None, limit: int = None, - username: str = None, - db: Session = Depends(get_db), - admin: Admin = Depends(Admin.get_current)): - - if not admin.is_sudo: - raise HTTPException(status_code=403, detail="You're not allowed") - + username: str = None): return crud.get_admins(db, offset, limit, username) diff --git a/app/views/node.py b/app/views/node.py index 9220fff..a1b946f 100644 --- a/app/views/node.py +++ b/app/views/node.py @@ -4,23 +4,21 @@ from datetime import datetime from typing import List import sqlalchemy -from fastapi import BackgroundTasks, Depends, HTTPException, WebSocket +from fastapi import HTTPException, WebSocket from app import app, logger, marznode -from app.db import Session, crud, get_db, get_tls_certificate +from app.db import crud, get_tls_certificate from app.marznode import MarzNodeGRPC from app.models.admin import Admin from app.models.node import (NodeCreate, NodeModify, NodeResponse, NodeSettings, NodeStatus, NodesUsageResponse) +from app.dependencies import DBDep, SudoAdminDep @app.get("/api/node/settings", tags=['Node'], response_model=NodeSettings) -def get_node_settings(db: Session = Depends(get_db), - admin: Admin = Depends(Admin.get_current)): - if not admin.is_sudo: - raise HTTPException(status_code=403, detail="You're not allowed") - +def get_node_settings(db: DBDep, + admin: SudoAdminDep): tls = crud.get_tls_certificate(db) return NodeSettings( @@ -30,12 +28,8 @@ def get_node_settings(db: Session = Depends(get_db), @app.post("/api/node", tags=['Node'], response_model=NodeResponse) async def add_node(new_node: NodeCreate, - db: Session = Depends(get_db), - admin: Admin = Depends(Admin.get_current)): - - if not admin.is_sudo: - raise HTTPException(status_code=403, detail="You're not allowed") - + db: DBDep, + admin: SudoAdminDep): try: db_node = crud.create_node(db, new_node) except sqlalchemy.exc.IntegrityError: @@ -54,20 +48,19 @@ async def add_node(new_node: NodeCreate, @app.get("/api/node/{node_id}", tags=['Node'], response_model=NodeResponse) def get_node(node_id: int, - db: Session = Depends(get_db), - admin: Admin = Depends(Admin.get_current)): - if not admin.is_sudo: - raise HTTPException(status_code=403, detail="You're not allowed") - - dbnode = crud.get_node_by_id(db, node_id) - if not dbnode: + db: DBDep, + admin: SudoAdminDep): + db_node = crud.get_node_by_id(db, node_id) + if not db_node: raise HTTPException(status_code=404, detail="Node not found") - return dbnode + return db_node @app.websocket("/api/node/{node_id}/logs") -async def node_logs(node_id: int, websocket: WebSocket, db: Session = Depends(get_db)): +async def node_logs(node_id: int, + websocket: WebSocket, + db: DBDep): token = ( websocket.query_params.get('token') or @@ -101,22 +94,16 @@ async def node_logs(node_id: int, websocket: WebSocket, db: Session = Depends(ge @app.get("/api/nodes", tags=['Node'], response_model=List[NodeResponse]) -def get_nodes(db: Session = Depends(get_db), - admin: Admin = Depends(Admin.get_current)): - if not admin.is_sudo: - raise HTTPException(status_code=403, detail="You're not allowed") +def get_nodes(db: DBDep, + admin: SudoAdminDep): return crud.get_nodes(db) @app.put("/api/node/{node_id}", tags=['Node'], response_model=NodeResponse) async def modify_node(node_id: int, modified_node: NodeModify, - db: Session = Depends(get_db), - admin: Admin = Depends(Admin.get_current)): - - if not admin.is_sudo: - raise HTTPException(status_code=403, detail="You're not allowed") - + db: DBDep, + admin: SudoAdminDep): db_node = crud.get_node_by_id(db, node_id) if not db_node: raise HTTPException(status_code=404, detail="Node not found") @@ -136,12 +123,8 @@ async def modify_node(node_id: int, @app.post("/api/node/{node_id}/resync", tags=['Node']) async def reconnect_node(node_id: int, - db: Session = Depends(get_db), - admin: Admin = Depends(Admin.get_current)): - - if not admin.is_sudo: - raise HTTPException(status_code=403, detail="You're not allowed") - + db: DBDep, + admin: SudoAdminDep): db_node = crud.get_node_by_id(db, node_id) if not db_node: raise HTTPException(status_code=404, detail="Node not found") @@ -151,12 +134,8 @@ async def reconnect_node(node_id: int, @app.delete("/api/node/{node_id}", tags=['Node']) async def remove_node(node_id: int, - db: Session = Depends(get_db), - admin: Admin = Depends(Admin.get_current)): - - if not admin.is_sudo: - raise HTTPException(status_code=403, detail="You're not allowed") - + db: DBDep, + admin: SudoAdminDep): db_node = crud.get_node_by_id(db, node_id) if not db_node: raise HTTPException(status_code=404, detail="Node not found") @@ -169,17 +148,13 @@ async def remove_node(node_id: int, @app.get("/api/nodes/usage", tags=['Node'], response_model=NodesUsageResponse) -def get_usage(db: Session = Depends(get_db), +def get_usage(db: DBDep, + admin: SudoAdminDep, start: str = None, - end: str = None, - admin: Admin = Depends(Admin.get_current)): + end: str = None): """ Get nodes usage """ - - if not admin.is_sudo: - raise HTTPException(status_code=403, detail="You're not allowed") - if start is None: start_date = datetime.fromtimestamp(datetime.utcnow().timestamp() - 30 * 24 * 3600) else: diff --git a/app/views/service.py b/app/views/service.py index d1af81d..52587ce 100644 --- a/app/views/service.py +++ b/app/views/service.py @@ -1,19 +1,19 @@ from typing import List import sqlalchemy -from fastapi import Depends, HTTPException +from fastapi import HTTPException from app import app -from app.db import Session, crud, get_db -from app.models.admin import Admin +from app.db import crud from app.models.service import (ServiceCreate, ServiceModify, - ServiceResponse) + ServiceResponse) +from app.dependencies import AdminDep, DBDep, SudoAdminDep @app.post("/api/service", tags=['Service'], response_model=ServiceResponse) def add_service(new_service: ServiceCreate, - db: Session = Depends(get_db), - admin: Admin = Depends(Admin.get_current)): + db: DBDep, + admin: SudoAdminDep): """ Add a new user template @@ -22,8 +22,6 @@ def add_service(new_service: ServiceCreate, - **expire_duration** must be in seconds and larger or equat to 0 - **inbounds** dictionary of protocol:inbound_tags, empty means all inbounds """ - if not (admin.is_sudo): - raise HTTPException(status_code=403, detail="You're not allowed") try: return crud.create_service(db, new_service) except sqlalchemy.exc.IntegrityError: @@ -32,7 +30,7 @@ def add_service(new_service: ServiceCreate, @app.get("/api/service/{id}", tags=['Service'], response_model=ServiceResponse) -def get_service(id: int, db: Session = Depends(get_db), admin: Admin = Depends(Admin.get_current)): +def get_service(id: int, db: DBDep, admin: SudoAdminDep): """ Get Service information with id """ @@ -44,9 +42,10 @@ def get_service(id: int, db: Session = Depends(get_db), admin: Admin = Depends(A @app.put("/api/service/{id}", tags=['Service'], response_model=ServiceResponse) -def modify_service(id: int, modify_service: ServiceModify, - db: Session = Depends(get_db), - admin: Admin = Depends(Admin.get_current)): +def modify_service(id: int, + modify_service: ServiceModify, + db: DBDep, + admin: SudoAdminDep): """ Modify Service @@ -59,7 +58,7 @@ def modify_service(id: int, modify_service: ServiceModify, raise HTTPException(status_code=403, detail="You're not allowed") dbservice = crud.get_service(db, id) - if not dbuser_template: + if not dbservice: raise HTTPException(status_code=404, detail="Service not found") try: @@ -71,20 +70,18 @@ def modify_service(id: int, modify_service: ServiceModify, @app.delete("/api/service/{id}", tags=['Service']) def remove_service(id: int, - db: Session = Depends(get_db), - admin: Admin = Depends(Admin.get_current)): - if not (admin.is_sudo): - raise HTTPException(status_code=403, detail="You're not allowed") - + db: DBDep, + admin: SudoAdminDep): dbservice = crud.get_service(db, id) - if not dbuser_template: + if not dbservice: raise HTTPException(status_code=404, detail="Service not found") return crud.remove_service(db, dbservice) @app.get("/api/services", tags=['Service'], response_model=List[ServiceResponse]) -def get_services( - offset: int = None, limit: int = None, db: Session = Depends(get_db), - admin: Admin = Depends(Admin.get_current)): +def get_services(db: DBDep, + admin: AdminDep, + offset: int = None, + limit: int = None): return crud.get_services(db) #, offset, limit) diff --git a/app/views/subscription.py b/app/views/subscription.py index 6b1d5b4..5e51165 100644 --- a/app/views/subscription.py +++ b/app/views/subscription.py @@ -1,11 +1,12 @@ import re from datetime import datetime -from fastapi import Depends, Header, HTTPException, Path, Request, Response +from fastapi import Header, HTTPException, Path, Request, Response from fastapi.responses import HTMLResponse from app import app -from app.db import Session, crud, get_db +from app.db import crud +from app.dependencies import DBDep, SubUserDep from app.models.user import UserResponse from app.templates import render_template from app.utils.share import encode_title, generate_subscription @@ -18,35 +19,27 @@ from config import ( ) +def get_subscription_user_info(user: UserResponse) -> dict: + return { + "upload": 0, + "download": user.used_traffic, + "total": user.data_limit, + "expire": user.expire, + } + + @app.get("/%s/{username}/{key}/" % XRAY_SUBSCRIPTION_PATH, tags=['Subscription']) @app.get("/%s/{username}/{key}" % XRAY_SUBSCRIPTION_PATH, include_in_schema=False) -def user_subscription(username: str, - key: str, +def user_subscription(db_user: SubUserDep, request: Request, - db: Session = Depends(get_db), + db: DBDep, user_agent: str = Header(default="")): """ Subscription link, V2ray and Clash supported """ accept_header = request.headers.get("Accept", "") - def get_subscription_user_info(user: UserResponse) -> dict: - return { - "upload": 0, - "download": user.used_traffic, - "total": user.data_limit, - "expire": user.expire, - } - try: - int(key, 16) - except ValueError: - return Response(status_code=404) - - dbuser = crud.get_user(db, username) - if not dbuser or dbuser.key != key: - return Response(status_code=404) - - user: UserResponse = UserResponse.model_validate(dbuser) + user: UserResponse = UserResponse.model_validate(db_user) if "text/html" in accept_header: return HTMLResponse( @@ -69,7 +62,7 @@ def user_subscription(username: str, ) } - crud.update_user_sub(db, dbuser, user_agent) + crud.update_user_sub(db, db_user, user_agent) if re.match('^([Cc]lash-verge|[Cc]lash-?[Mm]eta)', user_agent): conf = generate_subscription(user=user, config_format="clash-meta", as_base64=False) @@ -93,36 +86,15 @@ def user_subscription(username: str, @app.get("/%s/{username}/{key}/info" % XRAY_SUBSCRIPTION_PATH, tags=['Subscription'], response_model=UserResponse) -def user_subscription_info(username: str, - key: str, - db: Session = Depends(get_db)): - try: - int(key, 16) - except ValueError: - return Response(status_code=404) - - dbuser = crud.get_user(db, username) - if not dbuser or key != dbuser.key: - return Response(status_code=404) - - return dbuser +def user_subscription_info(db_user: SubUserDep): + return db_user @app.get("/%s/{username}/{key}/usage" % XRAY_SUBSCRIPTION_PATH, tags=['Subscription']) -def user_get_usage(username: str, - key: str, +def user_get_usage(db_user: SubUserDep, + db: DBDep, start: str = None, - end: str = None, - db: Session = Depends(get_db)): - - try: - int(key, 16) - except ValueError: - return Response(status_code=404) - - dbuser = crud.get_user(db, username) - if not dbuser or key != dbuser.key: - return Response(status_code=404) + end: str = None): if start is None: start_date = datetime.fromtimestamp(datetime.utcnow().timestamp() - 30 * 24 * 3600) @@ -134,41 +106,21 @@ def user_get_usage(username: str, else: end_date = datetime.fromisoformat(end) - usages = crud.get_user_usages(db, dbuser, start_date, end_date) + usages = crud.get_user_usages(db, db_user, start_date, end_date) - return {"usages": usages, "username": dbuser.username} + return {"usages": usages, "username": db_user.username} @app.get("/%s/{username}/{key}/{client_type}" % XRAY_SUBSCRIPTION_PATH, tags=['Subscription']) def user_subscription_with_client_type( - username: str, - key: str, + db_user: SubUserDep, request: Request, - client_type: str = Path(..., regex="sing-box|clash-meta|clash|outline|v2ray"), - db: Session = Depends(get_db), -): + client_type: str = Path(..., regex="sing-box|clash-meta|clash|outline|v2ray")): """ Subscription link, v2ray, clash, sing-box, outline and clash-meta supported """ - def get_subscription_user_info(user: UserResponse) -> dict: - return { - "upload": 0, - "download": user.used_traffic, - "total": user.data_limit, - "expire": user.expire, - } - - try: - int(key, 16) - except ValueError: - return Response(status_code=404) - - dbuser = crud.get_user(db, username) - if not dbuser or key != dbuser.key: - return Response(status_code=404) - - user: UserResponse = UserResponse.model_validate(dbuser) + user: UserResponse = UserResponse.model_validate(db_user) response_headers = { "content-disposition": f'attachment; filename="{user.username}"', diff --git a/app/views/system.py b/app/views/system.py index 9eadbab..2fab465 100644 --- a/app/views/system.py +++ b/app/views/system.py @@ -1,19 +1,18 @@ from typing import Dict, List, Union -from fastapi import Depends, HTTPException - from app import app -from app.db import Session, crud, get_db +from app.db import crud from app.models.admin import Admin from app.models.proxy import InboundHost, Inbound, ProxyTypes, InboundBase from app.models.system import SystemStats from app.models.user import UserStatus +from app.dependencies import DBDep, AdminDep, SudoAdminDep from app.utils.system import memory_usage, cpu_usage, realtime_bandwidth from app import __version__ @app.get("/api/system", tags=["System"], response_model=SystemStats) -def get_system_stats(db: Session = Depends(get_db), admin: Admin = Depends(Admin.get_current)): +def get_system_stats(db: DBDep, admin: AdminDep): mem = memory_usage() cpu = cpu_usage() system = crud.get_system_usage(db) @@ -39,43 +38,34 @@ def get_system_stats(db: Session = Depends(get_db), admin: Admin = Depends(Admin @app.get('/api/inbounds', tags=["System"], response_model=List[Inbound]) -def get_inbounds(db: Session = Depends(get_db), admin: Admin = Depends(Admin.get_current)): - inbounds = crud.get_all_inbounds(db)# xray.config.inbounds_by_protocol - return inbounds +def get_inbounds(db: DBDep, admin: SudoAdminDep): + inbounds = crud.get_all_inbounds(db) + return inbounds @app.get('/api/hosts', tags=["System"], response_model=Dict[int, List[InboundHost]]) -def get_hosts(db: Session = Depends(get_db), admin: Admin = Depends(Admin.get_current)): - if not admin.is_sudo: - raise HTTPException(status_code=403, detail="You're not allowed") - - # hosts = {} +def get_hosts(db: DBDep, admin: SudoAdminDep): hosts = crud.get_all_hosts(db) - #for inbound_tag in xray.config.inbounds_by_tag: - # hosts[inbound_tag] = crud.get_hosts(db, inbound_tag) return hosts @app.put('/api/hosts', tags=["System"], response_model=Dict[str, List[InboundHost]]) def modify_hosts(modified_hosts: Dict[str, List[InboundHost]], - db: Session = Depends(get_db), - admin: Admin = Depends(Admin.get_current)): - if not admin.is_sudo: - raise HTTPException(status_code=403, detail="You're not allowed") - + db: DBDep, + admin: SudoAdminDep): # validate - #for inbound_id, hosts in modified_hosts.items(): - # if not xray.config.inbounds_by_id.get(inbound_id): - # raise HTTPException(status_code=400, detail=f"Inbound {inbound_id} doesn't exist") + # for inbound_id, hosts in modified_hosts.items(): + # if not xray.config.inbounds_by_id.get(inbound_id): + # raise HTTPException(status_code=400, detail=f"Inbound {inbound_id} doesn't exist") for inbound_id, hosts in modified_hosts.items(): crud.update_hosts(db, inbound_id, hosts) - #xray.hosts.update() + # xray.hosts.update() hosts = {} - #for inbound_tag in xray.config.inbounds_by_tag: - # hosts[inbound_tag] = crud.get_hosts(db, inbound_tag) + # for inbound_tag in xray.config.inbounds_by_tag: + # hosts[inbound_tag] = crud.get_hosts(db, inbound_tag) return hosts diff --git a/app/views/user.py b/app/views/user.py index 17a6e44..9baea3c 100644 --- a/app/views/user.py +++ b/app/views/user.py @@ -11,12 +11,13 @@ from app.models.admin import Admin from app.models.user import (UserCreate, UserModify, UserResponse, UsersResponse, UserStatus, UserUsagesResponse) from app.utils import report +from app.dependencies import get_current_admin, DBDep, AdminDep, SudoAdminDep @app.post("/api/user", tags=['User'], response_model=UserResponse) async def add_user(new_user: UserCreate, - db: Session = Depends(get_db), - admin: Admin = Depends(Admin.get_current)): + db: DBDep, + admin: AdminDep): """ Add a new user @@ -49,15 +50,15 @@ async def add_user(new_user: UserCreate, @app.get("/api/user/{username}", tags=['User'], response_model=UserResponse) def get_user(username: str, - db: Session = Depends(get_db), - admin: Admin = Depends(Admin.get_current)): + db: DBDep, + admin: AdminDep): """ Get users information """ + dbuser = crud.get_user(db, username) if not (admin.is_sudo or (dbuser.admin and dbuser.admin.username == admin.username)): raise HTTPException(status_code=403, detail="You're not allowed") - dbuser = crud.get_user(db, username) if not dbuser: raise HTTPException(status_code=404, detail="User not found") @@ -67,8 +68,8 @@ def get_user(username: str, @app.put("/api/user/{username}", tags=['User'], response_model=UserResponse) async def modify_user(username: str, modified_user: UserModify, - db: Session = Depends(get_db), - admin: Admin = Depends(Admin.get_current)): + db: DBDep, + admin: AdminDep): """ Modify a user @@ -116,8 +117,8 @@ async def modify_user(username: str, @app.delete("/api/user/{username}", tags=['User']) async def remove_user(username: str, - db: Session = Depends(get_db), - admin: Admin = Depends(Admin.get_current)): + db: DBDep, + admin: AdminDep): """ Remove a user """ @@ -144,8 +145,8 @@ async def remove_user(username: str, @app.post("/api/user/{username}/reset", tags=['User'], response_model=UserResponse) async def reset_user_data_usage(username: str, - db: Session = Depends(get_db), - admin: Admin = Depends(Admin.get_current)): + db: DBDep, + admin: AdminDep): """ Reset user data usage """ @@ -174,8 +175,8 @@ async def reset_user_data_usage(username: str, @app.post("/api/user/{username}/revoke_sub", tags=['User'], response_model=UserResponse) async def revoke_user_subscription(username: str, - db: Session = Depends(get_db), - admin: Admin = Depends(Admin.get_current)): + db: DBDep, + admin: AdminDep): """ Revoke users subscription (Subscription link and proxies) """ @@ -205,13 +206,13 @@ async def revoke_user_subscription(username: str, @app.get("/api/users", tags=['User'], response_model=UsersResponse) -def get_users(offset: int = None, +def get_users(db: DBDep, + admin: AdminDep, + offset: int = None, limit: int = None, username: List[str] = Query(None), status: UserStatus = None, - sort: str = None, - db: Session = Depends(get_db), - admin: Admin = Depends(Admin.get_current)): + sort: str = None): """ Get all users """ @@ -240,14 +241,11 @@ def get_users(offset: int = None, @app.post("/api/users/reset", tags=['User']) -async def reset_users_data_usage(db: Session = Depends(get_db), - admin: Admin = Depends(Admin.get_current)): +async def reset_users_data_usage(db: DBDep, + admin: SudoAdminDep): """ Reset all users data usage """ - if not admin.is_sudo: - raise HTTPException(status_code=403, detail="You're not allowed") - dbadmin = crud.get_admin(db, admin.username) crud.reset_all_users_data_usage(db=db, admin=dbadmin) # startup_config = xray.config.include_db_users() @@ -259,11 +257,11 @@ async def reset_users_data_usage(db: Session = Depends(get_db), @app.get("/api/user/{username}/usage", tags=['User'], response_model=UserUsagesResponse) -def get_user_usage(username: str, +def get_user_usage(db: DBDep, + admin: AdminDep, + username: str, start: str = None, - end: str = None, - db: Session = Depends(get_db), - admin: Admin = Depends(Admin.get_current)): + end: str = None): """ Get users usage """ @@ -290,12 +288,8 @@ def get_user_usage(username: str, @app.put("/api/user/{username}/set-owner", tags=['User'], response_model=UserResponse) def set_owner(username: str, admin_username: str, - db: Session = Depends(get_db), - admin: Admin = Depends(Admin.get_current)): - - if not admin.is_sudo: - raise HTTPException(status_code=403, detail="You're not allowed") - + db: DBDep, + admin: SudoAdminDep): dbuser = crud.get_user(db, username) if not dbuser: raise HTTPException(status_code=404, detail="User not found") @@ -314,8 +308,8 @@ def set_owner(username: str, @app.delete("/api/users/expired", tags=['User']) def delete_expired(passed_time: int, - db: Session = Depends(get_db), - admin: Admin = Depends(Admin.get_current)): + db: DBDep, + admin: AdminDep): """ Delete expired users - **passed_time** must be a timestamp