├── app
├── dashboard
│ ├── example.env
│ ├── .prettierrc.json
│ ├── src
│ │ ├── types
│ │ │ ├── ProxyHosts.ts
│ │ │ └── User.ts
│ │ ├── vite-env.d.ts
│ │ ├── constants
│ │ │ ├── Project.ts
│ │ │ ├── ProxyHosts.tsx
│ │ │ └── UserSettings.tsx
│ │ ├── utils
│ │ │ ├── authStorage.ts
│ │ │ ├── dateFormatter.ts
│ │ │ └── formatByte.tsx
│ │ ├── App.tsx
│ │ ├── service
│ │ │ └── http.ts
│ │ ├── components
│ │ │ ├── Footer.tsx
│ │ │ ├── modules
│ │ │ │ └── Router.tsx
│ │ │ ├── Icon.tsx
│ │ │ ├── UserBadge.tsx
│ │ │ └── DeleteUserModal.tsx
│ │ ├── index.tsx
│ │ ├── index.scss
│ │ ├── pages
│ │ │ └── Dashboard.tsx
│ │ └── assets
│ │ │ └── logo.svg
│ ├── build
│ │ ├── favicon
│ │ │ ├── favicon.ico
│ │ │ ├── favicon-16x16.png
│ │ │ ├── favicon-32x32.png
│ │ │ ├── mstile-150x150.png
│ │ │ ├── apple-touch-icon.png
│ │ │ ├── android-chrome-192x192.png
│ │ │ ├── android-chrome-512x512.png
│ │ │ ├── browserconfig.xml
│ │ │ ├── site.webmanifest
│ │ │ └── safari-pinned-tab.svg
│ │ ├── assets
│ │ │ ├── ajax-loader.e7b44c86.gif
│ │ │ ├── slick.12459f22.svg
│ │ │ └── logo.2507bd68.svg
│ │ ├── 404.html
│ │ └── index.html
│ ├── public
│ │ └── favicon
│ │ │ ├── favicon.ico
│ │ │ ├── favicon-16x16.png
│ │ │ ├── favicon-32x32.png
│ │ │ ├── mstile-150x150.png
│ │ │ ├── apple-touch-icon.png
│ │ │ ├── android-chrome-192x192.png
│ │ │ ├── android-chrome-512x512.png
│ │ │ ├── browserconfig.xml
│ │ │ ├── site.webmanifest
│ │ │ └── safari-pinned-tab.svg
│ ├── tsconfig.node.json
│ ├── .gitignore
│ ├── vite.config.ts
│ ├── tsconfig.json
│ ├── index.html
│ ├── README.md
│ ├── __init__.py
│ └── package.json
├── db
│ ├── migrations
│ │ ├── README
│ │ ├── versions
│ │ │ ├── ece13c4c6f65_.py
│ │ │ ├── b15eba6e5867_add_host_sni.py
│ │ │ ├── 7cbe9d91ac11_proxyhost_security_added.py
│ │ │ ├── 9d5a518ae432_init_jwt_table.py
│ │ │ ├── 9b60be6cd0a2_add_created_at_field_to_users_table.py
│ │ │ ├── b3378dc6de01_hosts.py
│ │ │ ├── 3cf36a5fde73_init_system_table.py
│ │ │ ├── e91236993f1a_inbounds_table_excluded_inbounds.py
│ │ │ ├── d02dcfbf1517_add_userusageresetlogs_model_and_data_.py
│ │ │ ├── 94a5cc12c0d6_init_user_table.py
│ │ │ ├── 671621870b02_init_admin.py
│ │ │ ├── 5b84d88804a1_fix.py
│ │ │ └── fad8b1997c3a_case_insensitive_username.py
│ │ ├── script.py.mako
│ │ └── env.py
│ ├── base.py
│ └── __init__.py
├── views
│ ├── __init__.py
│ └── subscription.py
├── models
│ ├── system.py
│ └── admin.py
├── jobs
│ ├── 0_start_xray.py
│ ├── __init__.py
│ ├── reset_user_data_usage.py
│ ├── review_users.py
│ └── record_usages.py
├── xray
│ └── __init__.py
├── utils
│ ├── system.py
│ ├── xray.py
│ ├── jwt.py
│ └── store.py
├── telegram
│ ├── __init__.py
│ └── user.py
└── __init__.py
├── .dockerignore
├── xray_api
├── types
│ ├── __init__.py
│ ├── message.py
│ └── account.py
├── proto
│ ├── core
│ │ └── config_pb2_grpc.py
│ ├── app
│ │ ├── dns
│ │ │ ├── config_pb2_grpc.py
│ │ │ └── fakedns
│ │ │ │ ├── fakedns_pb2_grpc.py
│ │ │ │ └── fakedns_pb2.py
│ │ ├── log
│ │ │ ├── config_pb2_grpc.py
│ │ │ └── config_pb2.py
│ │ ├── stats
│ │ │ ├── config_pb2_grpc.py
│ │ │ └── config_pb2.py
│ │ ├── commander
│ │ │ ├── config_pb2_grpc.py
│ │ │ └── config_pb2.py
│ │ ├── dispatcher
│ │ │ ├── config_pb2_grpc.py
│ │ │ └── config_pb2.py
│ │ ├── metrics
│ │ │ ├── config_pb2_grpc.py
│ │ │ └── config_pb2.py
│ │ ├── policy
│ │ │ └── config_pb2_grpc.py
│ │ ├── proxyman
│ │ │ └── config_pb2_grpc.py
│ │ ├── reverse
│ │ │ └── config_pb2_grpc.py
│ │ ├── router
│ │ │ └── config_pb2_grpc.py
│ │ └── observatory
│ │ │ └── config_pb2_grpc.py
│ ├── common
│ │ ├── log
│ │ │ ├── log_pb2_grpc.py
│ │ │ └── log_pb2.py
│ │ ├── net
│ │ │ ├── port_pb2_grpc.py
│ │ │ ├── address_pb2_grpc.py
│ │ │ ├── network_pb2_grpc.py
│ │ │ ├── destination_pb2_grpc.py
│ │ │ ├── address_pb2.py
│ │ │ ├── destination_pb2.py
│ │ │ ├── port_pb2.py
│ │ │ └── network_pb2.py
│ │ ├── protocol
│ │ │ ├── user_pb2_grpc.py
│ │ │ ├── headers_pb2_grpc.py
│ │ │ ├── server_spec_pb2_grpc.py
│ │ │ ├── user_pb2.py
│ │ │ ├── server_spec_pb2.py
│ │ │ └── headers_pb2.py
│ │ └── serial
│ │ │ ├── typed_message_pb2_grpc.py
│ │ │ └── typed_message_pb2.py
│ ├── proxy
│ │ ├── dns
│ │ │ ├── config_pb2_grpc.py
│ │ │ └── config_pb2.py
│ │ ├── dokodemo
│ │ │ ├── config_pb2_grpc.py
│ │ │ └── config_pb2.py
│ │ ├── freedom
│ │ │ └── config_pb2_grpc.py
│ │ ├── http
│ │ │ └── config_pb2_grpc.py
│ │ ├── loopback
│ │ │ ├── config_pb2_grpc.py
│ │ │ └── config_pb2.py
│ │ ├── mtproto
│ │ │ ├── config_pb2_grpc.py
│ │ │ └── config_pb2.py
│ │ ├── socks
│ │ │ └── config_pb2_grpc.py
│ │ ├── trojan
│ │ │ └── config_pb2_grpc.py
│ │ ├── vless
│ │ │ ├── account_pb2_grpc.py
│ │ │ ├── inbound
│ │ │ │ ├── config_pb2_grpc.py
│ │ │ │ └── config_pb2.py
│ │ │ ├── encoding
│ │ │ │ ├── addons_pb2_grpc.py
│ │ │ │ └── addons_pb2.py
│ │ │ ├── outbound
│ │ │ │ ├── config_pb2_grpc.py
│ │ │ │ └── config_pb2.py
│ │ │ └── account_pb2.py
│ │ ├── vmess
│ │ │ ├── account_pb2_grpc.py
│ │ │ ├── inbound
│ │ │ │ └── config_pb2_grpc.py
│ │ │ ├── outbound
│ │ │ │ ├── config_pb2_grpc.py
│ │ │ │ └── config_pb2.py
│ │ │ └── account_pb2.py
│ │ ├── blackhole
│ │ │ ├── config_pb2_grpc.py
│ │ │ └── config_pb2.py
│ │ ├── shadowsocks
│ │ │ └── config_pb2_grpc.py
│ │ └── shadowsocks_2022
│ │ │ └── config_pb2_grpc.py
│ └── transport
│ │ ├── global
│ │ ├── config_pb2_grpc.py
│ │ └── config_pb2.py
│ │ └── internet
│ │ ├── config_pb2_grpc.py
│ │ ├── grpc
│ │ ├── config_pb2_grpc.py
│ │ ├── config_pb2.py
│ │ └── encoding
│ │ │ └── stream_pb2.py
│ │ ├── http
│ │ ├── config_pb2_grpc.py
│ │ └── config_pb2.py
│ │ ├── kcp
│ │ └── config_pb2_grpc.py
│ │ ├── quic
│ │ ├── config_pb2_grpc.py
│ │ └── config_pb2.py
│ │ ├── tcp
│ │ ├── config_pb2_grpc.py
│ │ └── config_pb2.py
│ │ ├── tls
│ │ └── config_pb2_grpc.py
│ │ ├── udp
│ │ ├── config_pb2_grpc.py
│ │ └── config_pb2.py
│ │ ├── xtls
│ │ └── config_pb2_grpc.py
│ │ ├── websocket
│ │ ├── config_pb2_grpc.py
│ │ └── config_pb2.py
│ │ ├── domainsocket
│ │ ├── config_pb2_grpc.py
│ │ └── config_pb2.py
│ │ └── headers
│ │ ├── http
│ │ └── config_pb2_grpc.py
│ │ ├── noop
│ │ ├── config_pb2_grpc.py
│ │ └── config_pb2.py
│ │ ├── srtp
│ │ ├── config_pb2_grpc.py
│ │ └── config_pb2.py
│ │ ├── tls
│ │ ├── config_pb2_grpc.py
│ │ └── config_pb2.py
│ │ ├── utp
│ │ ├── config_pb2_grpc.py
│ │ └── config_pb2.py
│ │ ├── wechat
│ │ ├── config_pb2_grpc.py
│ │ └── config_pb2.py
│ │ └── wireguard
│ │ ├── config_pb2_grpc.py
│ │ └── config_pb2.py
├── base.py
├── __init__.py
└── exceptions.py
├── .env.example
├── Dockerfile
├── .github
├── ISSUE_TEMPLATE
│ ├── feature_request.md
│ └── bug_report.md
└── workflows
│ ├── build.yml
│ └── build-dev.yml
├── main.py
├── requirements.txt
└── config.py
/app/dashboard/example.env:
--------------------------------------------------------------------------------
1 | VITE_BASE_API=
2 |
--------------------------------------------------------------------------------
/app/db/migrations/README:
--------------------------------------------------------------------------------
1 | Generic single-database configuration.
--------------------------------------------------------------------------------
/app/dashboard/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 80
3 | }
4 |
--------------------------------------------------------------------------------
/app/dashboard/src/types/ProxyHosts.ts:
--------------------------------------------------------------------------------
1 | export type ProxyHostSecurity = "inbound_default" | "none" | "tls"
2 |
--------------------------------------------------------------------------------
/app/dashboard/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
--------------------------------------------------------------------------------
/app/dashboard/build/favicon/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Musixal/Marzban/HEAD/app/dashboard/build/favicon/favicon.ico
--------------------------------------------------------------------------------
/app/dashboard/public/favicon/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Musixal/Marzban/HEAD/app/dashboard/public/favicon/favicon.ico
--------------------------------------------------------------------------------
/app/dashboard/build/favicon/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Musixal/Marzban/HEAD/app/dashboard/build/favicon/favicon-16x16.png
--------------------------------------------------------------------------------
/app/dashboard/build/favicon/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Musixal/Marzban/HEAD/app/dashboard/build/favicon/favicon-32x32.png
--------------------------------------------------------------------------------
/app/dashboard/build/favicon/mstile-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Musixal/Marzban/HEAD/app/dashboard/build/favicon/mstile-150x150.png
--------------------------------------------------------------------------------
/app/dashboard/public/favicon/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Musixal/Marzban/HEAD/app/dashboard/public/favicon/favicon-16x16.png
--------------------------------------------------------------------------------
/app/dashboard/public/favicon/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Musixal/Marzban/HEAD/app/dashboard/public/favicon/favicon-32x32.png
--------------------------------------------------------------------------------
/app/dashboard/build/favicon/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Musixal/Marzban/HEAD/app/dashboard/build/favicon/apple-touch-icon.png
--------------------------------------------------------------------------------
/app/dashboard/public/favicon/mstile-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Musixal/Marzban/HEAD/app/dashboard/public/favicon/mstile-150x150.png
--------------------------------------------------------------------------------
/app/dashboard/build/assets/ajax-loader.e7b44c86.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Musixal/Marzban/HEAD/app/dashboard/build/assets/ajax-loader.e7b44c86.gif
--------------------------------------------------------------------------------
/app/dashboard/public/favicon/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Musixal/Marzban/HEAD/app/dashboard/public/favicon/apple-touch-icon.png
--------------------------------------------------------------------------------
/app/dashboard/build/favicon/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Musixal/Marzban/HEAD/app/dashboard/build/favicon/android-chrome-192x192.png
--------------------------------------------------------------------------------
/app/dashboard/build/favicon/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Musixal/Marzban/HEAD/app/dashboard/build/favicon/android-chrome-512x512.png
--------------------------------------------------------------------------------
/app/dashboard/public/favicon/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Musixal/Marzban/HEAD/app/dashboard/public/favicon/android-chrome-192x192.png
--------------------------------------------------------------------------------
/app/dashboard/public/favicon/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Musixal/Marzban/HEAD/app/dashboard/public/favicon/android-chrome-512x512.png
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | **/node_modules
2 | __pycache__
3 | *.pyc
4 | *.pyo
5 | *.pyd
6 | .env*
7 | db.sqlite3
8 | db.sqlite3-journal
9 | *.log
10 | v2ray-core
11 | xray-core
--------------------------------------------------------------------------------
/xray_api/types/__init__.py:
--------------------------------------------------------------------------------
1 | from .account import (Account, ShadowsocksAccount, TrojanAccount, VLESSAccount,
2 | VMessAccount)
3 | from .message import Message, TypedMessage
4 |
--------------------------------------------------------------------------------
/xray_api/proto/core/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/app/dns/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/app/log/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/app/stats/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/common/log/log_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/common/net/port_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/proxy/dns/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/app/commander/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/app/dispatcher/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/app/metrics/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/app/policy/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/app/proxyman/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/app/reverse/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/app/router/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/common/net/address_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/common/net/network_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/common/protocol/user_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/proxy/dokodemo/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/proxy/freedom/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/proxy/http/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/proxy/loopback/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/proxy/mtproto/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/proxy/socks/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/proxy/trojan/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/proxy/vless/account_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/proxy/vmess/account_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/app/dns/fakedns/fakedns_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/app/observatory/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/common/net/destination_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/common/protocol/headers_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/proxy/blackhole/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/proxy/shadowsocks/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/proxy/vless/inbound/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/proxy/vmess/inbound/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/transport/global/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/transport/internet/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/common/protocol/server_spec_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/common/serial/typed_message_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/proxy/shadowsocks_2022/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/proxy/vless/encoding/addons_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/proxy/vless/outbound/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/proxy/vmess/outbound/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/transport/internet/grpc/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/transport/internet/http/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/transport/internet/kcp/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/transport/internet/quic/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/transport/internet/tcp/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/transport/internet/tls/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/transport/internet/udp/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/transport/internet/xtls/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/transport/internet/websocket/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/transport/internet/domainsocket/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/transport/internet/headers/http/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/transport/internet/headers/noop/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/transport/internet/headers/srtp/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/transport/internet/headers/tls/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/transport/internet/headers/utp/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/xray_api/proto/transport/internet/headers/wechat/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/app/views/__init__.py:
--------------------------------------------------------------------------------
1 | from app import app
2 |
3 | from .admin import *
4 | from .system import *
5 | from .user import *
6 | from .subscription import *
7 |
8 |
9 | @app.get("/", status_code=204)
10 | def base():
11 | return
12 |
--------------------------------------------------------------------------------
/xray_api/base.py:
--------------------------------------------------------------------------------
1 | import grpc
2 |
3 |
4 | class XRayBase(object):
5 | def __init__(self, address, port):
6 | self.address = address
7 | self.port = port
8 | self._channel = grpc.insecure_channel(f"{address}:{port}")
9 |
--------------------------------------------------------------------------------
/xray_api/proto/transport/internet/headers/wireguard/config_pb2_grpc.py:
--------------------------------------------------------------------------------
1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2 | """Client and server classes corresponding to protobuf-defined services."""
3 | import grpc
4 |
5 |
--------------------------------------------------------------------------------
/app/dashboard/src/constants/Project.ts:
--------------------------------------------------------------------------------
1 | export const REPO_URL = "https://github.com/Gozargah/Marzban";
2 | export const ORGANIZATION_URL = "https://github.com/Gozargah";
3 | export const DONATION_URL = "https://github.com/Gozargah/Marzban#donation";
4 |
--------------------------------------------------------------------------------
/app/dashboard/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "module": "ESNext",
5 | "moduleResolution": "Node",
6 | "allowSyntheticDefaultImports": true
7 | },
8 | "include": ["vite.config.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/app/models/system.py:
--------------------------------------------------------------------------------
1 | from pydantic import BaseModel
2 |
3 |
4 | class SystemStats(BaseModel):
5 | mem_total: int
6 | mem_used: int
7 | total_user: int
8 | users_active: int
9 | incoming_bandwidth: int
10 | outgoing_bandwidth: int
11 |
--------------------------------------------------------------------------------
/xray_api/types/message.py:
--------------------------------------------------------------------------------
1 | from ..proto.common.serial.typed_message_pb2 import TypedMessage
2 |
3 |
4 | class Message:
5 | def __new__(cls, message) -> TypedMessage:
6 | return TypedMessage(
7 | type=message.DESCRIPTOR.full_name,
8 | value=message.SerializeToString()
9 | )
10 |
--------------------------------------------------------------------------------
/app/dashboard/build/favicon/browserconfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | #2b5797
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/app/dashboard/public/favicon/browserconfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | #2b5797
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/app/dashboard/src/utils/authStorage.ts:
--------------------------------------------------------------------------------
1 | export const getAuthToken = () => {
2 | return localStorage.getItem("token");
3 | };
4 |
5 | export const setAuthToken = (token: string) => {
6 | localStorage.setItem("token", token);
7 | };
8 |
9 | export const removeAuthToken = () => {
10 | localStorage.removeItem("token");
11 | };
12 |
--------------------------------------------------------------------------------
/xray_api/__init__.py:
--------------------------------------------------------------------------------
1 | from . import exceptions
2 | from . import exceptions as exc
3 | from . import types
4 | from .proxyman import Proxyman
5 | from .stats import Stats
6 |
7 |
8 | class XRay(Proxyman, Stats):
9 | pass
10 |
11 |
12 | __all__ = [
13 | "XRay",
14 | "exceptions",
15 | "exc",
16 | "types"
17 | ]
18 |
--------------------------------------------------------------------------------
/app/jobs/0_start_xray.py:
--------------------------------------------------------------------------------
1 | from app import app, xray
2 | from app.utils.xray import xray_config_include_db_clients
3 |
4 |
5 | @app.on_event("startup")
6 | def app_startup():
7 | xray.core.start(
8 | xray_config_include_db_clients(xray.config)
9 | )
10 |
11 |
12 | @app.on_event("shutdown")
13 | def app_shutdown():
14 | xray.core.stop()
15 |
--------------------------------------------------------------------------------
/app/dashboard/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | *.local
12 |
13 | # Editor directories and files
14 | .vscode/*
15 | !.vscode/extensions.json
16 | .idea
17 | .DS_Store
18 | *.suo
19 | *.ntvs*
20 | *.njsproj
21 | *.sln
22 | *.sw?
23 |
24 | .env
25 |
26 | stats.html
27 |
--------------------------------------------------------------------------------
/app/dashboard/src/App.tsx:
--------------------------------------------------------------------------------
1 | import "react-datepicker/dist/react-datepicker.css";
2 | import "react-loading-skeleton/dist/skeleton.css";
3 | import { RouterProvider } from "react-router-dom";
4 | import { router } from "./components/modules/Router";
5 |
6 | function App() {
7 | return (
8 |
9 |
10 |
11 | );
12 | }
13 |
14 | export default App;
15 |
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | SUDO_USERNAME = "admin"
2 | SUDO_PASSWORD = "admin"
3 |
4 | UVICORN_HOST = "0.0.0.0"
5 | UVICORN_PORT = 8000
6 |
7 | XRAY_JSON = "xray.json"
8 | XRAY_EXECUTABLE_PATH = "/usr/local/bin/xray"
9 | XRAY_ASSETS_PATH = "/usr/local/share/xray"
10 |
11 |
12 | # XRAY_SUBSCRIPTION_URL_PREFIX = "http://example.com"
13 |
14 | # TELEGRAM_API_TOKEN = 123456789:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
15 | # TELEGRAM_ADMIN_ID = 987654321
--------------------------------------------------------------------------------
/app/dashboard/src/constants/ProxyHosts.tsx:
--------------------------------------------------------------------------------
1 | import { ProxyHostSecurity } from "types/ProxyHosts";
2 |
3 | export const proxyHostSecurity: {title: string, value: ProxyHostSecurity}[] = [
4 | {
5 | "title": "Inbound's default",
6 | "value": "inbound_default"
7 | },
8 | {
9 | "title": "TLS",
10 | "value": "tls"
11 | },
12 | {
13 | "title": "None",
14 | "value": "none"
15 | }
16 | ]
--------------------------------------------------------------------------------
/app/jobs/__init__.py:
--------------------------------------------------------------------------------
1 | import glob
2 | import importlib.util
3 | from os.path import basename, dirname, join
4 |
5 | modules = glob.glob(join(dirname(__file__), "*.py"))
6 |
7 | for file in modules:
8 | name = basename(file).replace('.py', '')
9 | if name.startswith('_'):
10 | continue
11 |
12 | spec = importlib.util.spec_from_file_location(name, file)
13 | spec.loader.exec_module(importlib.util.module_from_spec(spec))
14 |
--------------------------------------------------------------------------------
/app/db/base.py:
--------------------------------------------------------------------------------
1 | from config import SQLALCHEMY_DATABASE_URL
2 | from sqlalchemy import create_engine
3 | from sqlalchemy.ext.declarative import declarative_base
4 | from sqlalchemy.orm import sessionmaker
5 |
6 | engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args=(
7 | {"check_same_thread": False} if SQLALCHEMY_DATABASE_URL.startswith('sqlite') else {}
8 | ))
9 | SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
10 | Base = declarative_base()
11 |
--------------------------------------------------------------------------------
/app/dashboard/src/utils/dateFormatter.ts:
--------------------------------------------------------------------------------
1 | import dayjs from "dayjs";
2 |
3 | export const relativeExpiryDate = (expiryDate: number | null | undefined) => {
4 | let date = "";
5 | if (expiryDate) {
6 | if (
7 | dayjs(expiryDate * 1000)
8 | .utc()
9 | .isAfter(dayjs().utc())
10 | ) {
11 | date = "Expires " + dayjs().to(dayjs(expiryDate * 1000).utc());
12 | } else {
13 | date = "Expired " + dayjs().to(dayjs(expiryDate * 1000).utc());
14 | }
15 | }
16 | return date;
17 | };
18 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.10-slim
2 |
3 | ENV PYTHONUNBUFFERED 1
4 |
5 | WORKDIR /code
6 |
7 | RUN apt-get update \
8 | && apt-get install -y curl unzip gcc python3-dev \
9 | && rm -rf /var/lib/apt/lists/*
10 |
11 | RUN curl -L https://github.com/Gozargah/Marzban-scripts/raw/master/install_latest_xray.sh | bash
12 |
13 | COPY . /code
14 |
15 | RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
16 |
17 | RUN apt-get remove -y curl unzip gcc python3-dev
18 |
19 | CMD ["bash", "-c", "alembic upgrade head; python main.py"]
--------------------------------------------------------------------------------
/app/db/migrations/versions/ece13c4c6f65_.py:
--------------------------------------------------------------------------------
1 | """empty message
2 |
3 | Revision ID: ece13c4c6f65
4 | Revises: d02dcfbf1517, b3378dc6de01
5 | Create Date: 2023-02-23 14:55:19.953972
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'ece13c4c6f65'
14 | down_revision = ('d02dcfbf1517', 'b3378dc6de01')
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade() -> None:
20 | pass
21 |
22 |
23 | def downgrade() -> None:
24 | pass
25 |
--------------------------------------------------------------------------------
/app/dashboard/vite.config.ts:
--------------------------------------------------------------------------------
1 | import react from "@vitejs/plugin-react";
2 | import { defineConfig, splitVendorChunkPlugin } from "vite";
3 | import svgr from "vite-plugin-svgr";
4 | import { visualizer } from "rollup-plugin-visualizer";
5 | import tsconfigPaths from "vite-tsconfig-paths";
6 |
7 | // https://vitejs.dev/config/
8 | export default defineConfig({
9 | plugins: [
10 | tsconfigPaths(),
11 | react({
12 | include: "**/*.tsx",
13 | }),
14 | svgr(),
15 | visualizer(),
16 | splitVendorChunkPlugin(),
17 | ],
18 | });
19 |
--------------------------------------------------------------------------------
/app/dashboard/build/favicon/site.webmanifest:
--------------------------------------------------------------------------------
1 | {
2 | "name": "",
3 | "short_name": "",
4 | "icons": [
5 | {
6 | "src": "/favicon/android-chrome-192x192.png",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | },
10 | {
11 | "src": "/favicon/android-chrome-512x512.png",
12 | "sizes": "512x512",
13 | "type": "image/png"
14 | }
15 | ],
16 | "theme_color": "#ffffff",
17 | "background_color": "#ffffff",
18 | "display": "standalone"
19 | }
20 |
--------------------------------------------------------------------------------
/app/dashboard/public/favicon/site.webmanifest:
--------------------------------------------------------------------------------
1 | {
2 | "name": "",
3 | "short_name": "",
4 | "icons": [
5 | {
6 | "src": "/favicon/android-chrome-192x192.png",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | },
10 | {
11 | "src": "/favicon/android-chrome-512x512.png",
12 | "sizes": "512x512",
13 | "type": "image/png"
14 | }
15 | ],
16 | "theme_color": "#ffffff",
17 | "background_color": "#ffffff",
18 | "display": "standalone"
19 | }
20 |
--------------------------------------------------------------------------------
/app/dashboard/src/service/http.ts:
--------------------------------------------------------------------------------
1 | import { $fetch as ohMyFetch, FetchOptions } from "ohmyfetch";
2 | import { getAuthToken } from "utils/authStorage";
3 |
4 | export const $fetch = ohMyFetch.create({
5 | baseURL: import.meta.env.VITE_BASE_API,
6 | });
7 |
8 | export const fetcher = (url: string, ops: FetchOptions<"json"> = {}) => {
9 | const token = getAuthToken();
10 | if (token) {
11 | ops["headers"] = {
12 | ...(ops?.headers || {}),
13 | Authorization: `Bearer ${getAuthToken()}`,
14 | };
15 | }
16 | return $fetch(url, ops);
17 | };
18 |
19 | export const fetch = fetcher;
20 |
--------------------------------------------------------------------------------
/app/db/migrations/script.py.mako:
--------------------------------------------------------------------------------
1 | """${message}
2 |
3 | Revision ID: ${up_revision}
4 | Revises: ${down_revision | comma,n}
5 | Create Date: ${create_date}
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 | ${imports if imports else ""}
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = ${repr(up_revision)}
14 | down_revision = ${repr(down_revision)}
15 | branch_labels = ${repr(branch_labels)}
16 | depends_on = ${repr(depends_on)}
17 |
18 |
19 | def upgrade() -> None:
20 | ${upgrades if upgrades else "pass"}
21 |
22 |
23 | def downgrade() -> None:
24 | ${downgrades if downgrades else "pass"}
25 |
--------------------------------------------------------------------------------
/app/dashboard/src/utils/formatByte.tsx:
--------------------------------------------------------------------------------
1 | export function formatBytes(bytes: number, decimals = 2, asArray = false) {
2 | if (!+bytes) return "0 B";
3 |
4 | const k = 1024;
5 | const dm = decimals < 0 ? 0 : decimals;
6 | const sizes = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
7 |
8 | const i = Math.floor(Math.log(bytes) / Math.log(k));
9 | if (!asArray)
10 | return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
11 | else return [parseFloat((bytes / Math.pow(k, i)).toFixed(dm)), sizes[i]];
12 | }
13 |
14 | export const numberWithCommas = (x: number) => {
15 | if (x !== null) return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
16 | };
17 |
--------------------------------------------------------------------------------
/app/dashboard/src/components/Footer.tsx:
--------------------------------------------------------------------------------
1 | import { BoxProps, HStack, Link, Text } from "@chakra-ui/react";
2 | import { ORGANIZATION_URL } from "constants/Project";
3 | import { FC } from "react";
4 |
5 | export const Footer: FC = (props) => {
6 | return (
7 |
8 |
15 | Made with ❤️ in{" "}
16 |
17 | Gozargah
18 |
19 |
20 |
21 | );
22 | };
23 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: Feature title
5 | labels: enhancement
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/app/dashboard/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "lib": [
6 | "DOM",
7 | "DOM.Iterable",
8 | "ESNext"
9 | ],
10 | "allowJs": false,
11 | "skipLibCheck": true,
12 | "esModuleInterop": false,
13 | "allowSyntheticDefaultImports": true,
14 | "strict": true,
15 | "forceConsistentCasingInFileNames": true,
16 | "module": "ESNext",
17 | "moduleResolution": "Node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx",
22 | "baseUrl": "./src"
23 | },
24 | "include": [
25 | "src"
26 | ],
27 | "references": [
28 | {
29 | "path": "./tsconfig.node.json"
30 | }
31 | ]
32 | }
33 |
--------------------------------------------------------------------------------
/app/db/migrations/versions/b15eba6e5867_add_host_sni.py:
--------------------------------------------------------------------------------
1 | """add host and sni columns to hosts table
2 |
3 | Revision ID: b15eba6e5867
4 | Revises: ece13c4c6f65
5 | Create Date: 2023-02-28 18:34:19.100255
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = "b15eba6e5867"
14 | down_revision = "ece13c4c6f65"
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade() -> None:
20 | op.add_column("hosts", sa.Column("sni", sa.String(256), nullable=True))
21 | op.add_column("hosts", sa.Column("host", sa.String(256), nullable=True))
22 |
23 |
24 | def downgrade() -> None:
25 | op.drop_column("hosts", "host")
26 | op.drop_column("hosts", "sni")
27 |
--------------------------------------------------------------------------------
/app/dashboard/src/components/modules/Router.tsx:
--------------------------------------------------------------------------------
1 | import { createBrowserRouter, createHashRouter } from "react-router-dom";
2 | import { Dashboard } from "../../pages/Dashboard";
3 | import { Login } from "../../pages/Login";
4 | import { fetch } from "../../service/http";
5 | import { getAuthToken } from "../../utils/authStorage";
6 |
7 | const fetchAdminLoader = () => {
8 | return fetch("/admin", {
9 | headers: {
10 | Authorization: `Bearer ${getAuthToken()}`,
11 | },
12 | });
13 | };
14 |
15 | export const router = createBrowserRouter(
16 | [
17 | {
18 | path: "/",
19 | element: ,
20 | errorElement: ,
21 | loader: fetchAdminLoader,
22 | },
23 | {
24 | path: "/login/",
25 | element: ,
26 | },
27 | ],
28 | { basename: import.meta.env.BASE_URL }
29 | );
30 |
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | import uvicorn
2 | from app import app
3 | from config import (
4 | DEBUG,
5 | UVICORN_HOST,
6 | UVICORN_PORT,
7 | UVICORN_UDS,
8 | UVICORN_SSL_CERTFILE,
9 | UVICORN_SSL_KEYFILE
10 | )
11 |
12 |
13 | if __name__ == "__main__":
14 | # Do NOT change workers count for now
15 | # multi-workers support isn't implemented yet for APScheduler and XRay module
16 | try:
17 | uvicorn.run(
18 | "main:app",
19 | host=('127.0.0.1' if DEBUG else UVICORN_HOST),
20 | port=UVICORN_PORT,
21 | uds=(None if DEBUG else UVICORN_UDS),
22 | ssl_certfile=UVICORN_SSL_CERTFILE,
23 | ssl_keyfile=UVICORN_SSL_KEYFILE,
24 | workers=1,
25 | reload=DEBUG
26 | )
27 | except FileNotFoundError: # to prevent error on removing unix sock
28 | pass
29 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: Bug title
5 | labels: bug
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Machine details (please complete the following information):**
27 | - OS: [e.g. ubuntu 20]
28 | - Python version: [e.g 3.8]
29 | - Nodejs version: [e.g 16.17]
30 | - Browser [e.g. chrome, safari]
31 |
32 | **Additional context**
33 | Add any other context about the problem here.
34 |
--------------------------------------------------------------------------------
/app/xray/__init__.py:
--------------------------------------------------------------------------------
1 | from random import randint
2 |
3 | from app.utils.system import check_port
4 | from app.xray.config import XRayConfig
5 | from app.xray.core import XRayCore
6 | from xray_api import XRay
7 | from xray_api import exceptions
8 | from xray_api import exceptions as exc
9 | from xray_api import types
10 |
11 | from config import XRAY_ASSETS_PATH, XRAY_EXECUTABLE_PATH, XRAY_JSON
12 |
13 | # Search for a free API port
14 | try:
15 | for api_port in range(randint(10000, 60000), 65536):
16 | if not check_port(api_port):
17 | break
18 | finally:
19 | config = XRayConfig(XRAY_JSON, api_port=api_port)
20 | del api_port
21 |
22 |
23 | core = XRayCore(XRAY_EXECUTABLE_PATH, XRAY_ASSETS_PATH)
24 | api = XRay(config.api_host, config.api_port)
25 |
26 |
27 | __all__ = [
28 | "config",
29 | "core",
30 | "api",
31 | "exceptions",
32 | "exc",
33 | "types"
34 | ]
35 |
--------------------------------------------------------------------------------
/app/db/migrations/versions/7cbe9d91ac11_proxyhost_security_added.py:
--------------------------------------------------------------------------------
1 | """ProxyHost: security added
2 |
3 | Revision ID: 7cbe9d91ac11
4 | Revises: b15eba6e5867
5 | Create Date: 2023-03-07 23:30:49.678157
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = "7cbe9d91ac11"
14 | down_revision = "e3f0e888a563"
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade() -> None:
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column(
22 | "hosts",
23 | sa.Column(
24 | "security",
25 | sa.Enum("inbound_default", "none", "tls", name="proxyhostsecurity"),
26 | nullable=False,
27 | server_default="inbound_default"
28 | ),
29 | )
30 |
31 | # ### end Alembic commands ###
32 |
33 |
34 | def downgrade() -> None:
35 | # ### commands auto generated by Alembic - please adjust! ###
36 | op.drop_column("hosts", "security")
37 | # ### end Alembic commands ###
38 |
--------------------------------------------------------------------------------
/app/dashboard/src/constants/UserSettings.tsx:
--------------------------------------------------------------------------------
1 | import { DataLimitResetStrategy } from "types/User";
2 |
3 | export const resetStrategy: { title: string; value: DataLimitResetStrategy }[] =
4 | [
5 | {
6 | title: "No",
7 | value: "no_reset",
8 | },
9 | {
10 | title: "Daily",
11 | value: "day",
12 | },
13 | {
14 | title: "Weekly",
15 | value: "week",
16 | },
17 | {
18 | title: "Monthly",
19 | value: "month",
20 | },
21 | {
22 | title: "Annually",
23 | value: "year",
24 | },
25 | ];
26 |
27 | export const statusColors: {
28 | [key: string]: {
29 | statusColor: string;
30 | bandWidthColor: string;
31 | };
32 | } = {
33 | active: {
34 | statusColor: "green",
35 | bandWidthColor: "primary",
36 | },
37 | disabled: {
38 | statusColor: "gray",
39 | bandWidthColor: "gray",
40 | },
41 | expired: {
42 | statusColor: "orange",
43 | bandWidthColor: "orange",
44 | },
45 | limited: {
46 | statusColor: "red",
47 | bandWidthColor: "red",
48 | },
49 | };
50 |
--------------------------------------------------------------------------------
/app/db/migrations/versions/9d5a518ae432_init_jwt_table.py:
--------------------------------------------------------------------------------
1 | """init jwt table
2 |
3 | Revision ID: 9d5a518ae432
4 | Revises: 3cf36a5fde73
5 | Create Date: 2022-11-24 21:02:44.278773
6 |
7 | """
8 | import os
9 | from alembic import op
10 | import sqlalchemy as sa
11 |
12 |
13 | # revision identifiers, used by Alembic.
14 | revision = '9d5a518ae432'
15 | down_revision = '3cf36a5fde73'
16 | branch_labels = None
17 | depends_on = None
18 |
19 |
20 | def upgrade() -> None:
21 | # ### commands auto generated by Alembic - please adjust! ###
22 | table = op.create_table('jwt',
23 | sa.Column('id', sa.Integer(), nullable=False),
24 | sa.Column('secret_key', sa.String(length=64), nullable=False),
25 | sa.PrimaryKeyConstraint('id')
26 | )
27 |
28 | # INSERT DEFAULT ROW
29 | op.bulk_insert(table, [{"id": 1, "secret_key": os.urandom(32).hex()}])
30 |
31 | # ### end Alembic commands ###
32 |
33 |
34 | def downgrade() -> None:
35 | # ### commands auto generated by Alembic - please adjust! ###
36 | op.drop_table('jwt')
37 | # ### end Alembic commands ###
38 |
--------------------------------------------------------------------------------
/app/db/migrations/versions/9b60be6cd0a2_add_created_at_field_to_users_table.py:
--------------------------------------------------------------------------------
1 | """add created_at field to users table
2 |
3 | Revision ID: 9b60be6cd0a2
4 | Revises: 9d5a518ae432
5 | Create Date: 2022-12-25 18:45:46.743506
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '9b60be6cd0a2'
14 | down_revision = '9d5a518ae432'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade() -> None:
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | with op.batch_alter_table('users') as batch_op:
22 | batch_op.add_column(sa.Column(
23 | 'created_at', sa.DateTime(),
24 | nullable=False,
25 | server_default=sa.func.current_timestamp()))
26 | # ### end Alembic commands ###
27 |
28 |
29 | def downgrade() -> None:
30 | # ### commands auto generated by Alembic - please adjust! ###
31 | with op.batch_alter_table('users') as batch_op:
32 | batch_op.drop_column('created_at')
33 | # ### end Alembic commands ###
34 |
--------------------------------------------------------------------------------
/app/dashboard/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { ChakraProvider } from "@chakra-ui/react";
2 | import React from "react";
3 | import ReactDOM from "react-dom/client";
4 | import { SWRConfig } from "swr";
5 | import { theme } from "../chakra.config";
6 | import App from "./App";
7 | import "index.scss";
8 | import { fetcher } from "service/http";
9 | import dayjs from "dayjs";
10 | import LocalizedFormat from "dayjs/plugin/localizedFormat";
11 | import Timezone from "dayjs/plugin/timezone";
12 | import utc from "dayjs/plugin/utc";
13 | import RelativeTime from "dayjs/plugin/relativeTime";
14 |
15 | dayjs.extend(Timezone);
16 | dayjs.extend(LocalizedFormat);
17 | dayjs.extend(utc);
18 | dayjs.extend(RelativeTime);
19 |
20 | ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
21 |
22 |
23 |
31 |
32 |
33 |
34 |
35 | );
36 |
--------------------------------------------------------------------------------
/app/db/migrations/versions/b3378dc6de01_hosts.py:
--------------------------------------------------------------------------------
1 | """hosts
2 |
3 | Revision ID: b3378dc6de01
4 | Revises: e91236993f1a
5 | Create Date: 2023-02-14 18:22:26.791353
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'b3378dc6de01'
14 | down_revision = 'e91236993f1a'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade() -> None:
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.create_table('hosts',
22 | sa.Column('id', sa.Integer(), nullable=False),
23 | sa.Column('remark', sa.String(256), nullable=False),
24 | sa.Column('address', sa.String(256), nullable=False),
25 | sa.Column('port', sa.Integer(), nullable=True),
26 | sa.Column('inbound_tag', sa.String(256), nullable=False),
27 | sa.ForeignKeyConstraint(['inbound_tag'], ['inbounds.tag'], ),
28 | sa.PrimaryKeyConstraint('id')
29 | )
30 | # ### end Alembic commands ###
31 |
32 |
33 | def downgrade() -> None:
34 | # ### commands auto generated by Alembic - please adjust! ###
35 | op.drop_table('hosts')
36 | # ### end Alembic commands ###
37 |
--------------------------------------------------------------------------------
/app/dashboard/src/types/User.ts:
--------------------------------------------------------------------------------
1 | export type UserStatus = "active" | "disabled" | "limited" | "expired";
2 | export type ProxyKeys = ("vmess" | "vless" | "trojan" | "shadowsocks")[];
3 | export type ProxyType = {
4 | vmess?: {
5 | id?: string;
6 | };
7 | vless?: {
8 | id?: string;
9 | };
10 | trojan?: { password?: string };
11 | shadowsocks?: {
12 | password?: string;
13 | };
14 | };
15 |
16 | export type DataLimitResetStrategy =
17 | | "no_reset"
18 | | "day"
19 | | "week"
20 | | "month"
21 | | "year";
22 |
23 | export type UserInbounds = {
24 | [key: string]: string[];
25 | };
26 | export type User = {
27 | proxies: ProxyType;
28 | expire: number | null;
29 | data_limit: number | null;
30 | data_limit_reset_strategy: DataLimitResetStrategy;
31 | lifetime_used_traffic: number;
32 | username: string;
33 | used_traffic: number;
34 | status: UserStatus;
35 | links: string[];
36 | subscription_url: string;
37 | inbounds: UserInbounds;
38 | };
39 |
40 | export type UserCreate = Pick<
41 | User,
42 | | "inbounds"
43 | | "proxies"
44 | | "expire"
45 | | "data_limit"
46 | | "data_limit_reset_strategy"
47 | | "username"
48 | | "status"
49 | >;
50 |
--------------------------------------------------------------------------------
/app/utils/system.py:
--------------------------------------------------------------------------------
1 | import math
2 | import secrets
3 | import socket
4 | from dataclasses import dataclass
5 |
6 | import psutil
7 |
8 |
9 | @dataclass
10 | class MemoryStat():
11 | total: int
12 | used: int
13 | free: int
14 |
15 |
16 | @dataclass
17 | class CPUStat():
18 | cores: int
19 | percent: int
20 |
21 |
22 | def cpu_usage() -> CPUStat:
23 | return CPUStat(cores=psutil.cpu_count(), percent=psutil.cpu_percent())
24 |
25 |
26 | def memory_usage() -> MemoryStat:
27 | mem = psutil.virtual_memory()
28 | return MemoryStat(total=mem.total, used=mem.used, free=mem.available)
29 |
30 |
31 | def random_password() -> str:
32 | return secrets.token_urlsafe(16)
33 |
34 |
35 | def check_port(port: int) -> bool:
36 | s = socket.socket()
37 | try:
38 | s.connect(('127.0.0.1', port))
39 | return True
40 | except socket.error:
41 | return False
42 | finally:
43 | s.close()
44 |
45 |
46 | def readable_size(size_bytes):
47 | if size_bytes == 0:
48 | return "0B"
49 | size_name = ("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
50 | i = int(math.floor(math.log(size_bytes, 1024)))
51 | p = math.pow(1024, i)
52 | s = round(size_bytes / p, 2)
53 | return f'{s} {size_name[i]}'
54 |
--------------------------------------------------------------------------------
/app/dashboard/src/index.scss:
--------------------------------------------------------------------------------
1 | @import url("https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap");
2 |
3 | .bg-blue-animate {
4 | animation: blur-animate 200ms ease-in;
5 | backdrop-filter: blur(10px);
6 | }
7 |
8 | @keyframes blur-animate {
9 | from {
10 | backdrop-filter: blur(1px);
11 | }
12 | to {
13 | backdrop-filter: blur(10px);
14 | }
15 | }
16 |
17 | .animate-spin {
18 | animation: spin 1s linear infinite;
19 | }
20 |
21 | @keyframes spin {
22 | from {
23 | transform: rotate(0deg);
24 | }
25 | to {
26 | transform: rotate(360deg);
27 | }
28 | }
29 |
30 | table thead {
31 | th:first-of-type {
32 | border-top-left-radius: 8px;
33 | }
34 | th:last-of-type {
35 | border-top-right-radius: 8px;
36 | }
37 | }
38 |
39 | .last-row {
40 | td:first-of-type {
41 | border-bottom-left-radius: 8px;
42 | }
43 | td:last-of-type {
44 | border-bottom-right-radius: 8px;
45 | }
46 | }
47 |
48 | .slick-prev {
49 | left: -40px;
50 | }
51 | .slick-next {
52 | right: -40px;
53 | }
54 | .slick-prev,
55 | .slick-next {
56 | z-index: 100;
57 | }
58 | .chakra-popover__popper {
59 | z-index: 9999 !important;
60 | }
61 | .inbound-item .chakra-checkbox__label {
62 | max-width: 100%;
63 | width: 100%;
64 | }
65 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | alembic==1.8.1
2 | anyio==3.6.2
3 | APScheduler==3.9.1.post1
4 | async-timeout==4.0.2
5 | backports.zoneinfo==0.2.1;python_version<"3.9"
6 | bcrypt==4.0.1
7 | certifi==2022.12.7
8 | cffi==1.15.1
9 | charset-normalizer==2.1.1
10 | click==8.1.3
11 | cryptography==39.0.1
12 | Deprecated==1.2.13
13 | ecdsa==0.18.0
14 | fastapi==0.92.0
15 | fastapi-responses==0.2.1
16 | greenlet==2.0.1
17 | grpcio==1.50.0
18 | grpcio-tools==1.44.0
19 | h11==0.14.0
20 | httptools==0.5.0
21 | idna==3.4
22 | importlib-metadata==5.1.0
23 | importlib-resources==5.10.0
24 | Mako==1.2.4
25 | MarkupSafe==2.1.1
26 | packaging==21.3
27 | passlib==1.7.4
28 | protobuf==3.20.3
29 | psutil==5.9.4
30 | pyasn1==0.4.8
31 | pycparser==2.21
32 | pydantic==1.10.2
33 | pyparsing==3.0.9
34 | pyTelegramBotAPI==4.9.0
35 | python-decouple==3.6
36 | python-dotenv==0.21.0
37 | python-jose==3.3.0
38 | python-multipart==0.0.5
39 | pytz==2022.6
40 | pytz-deprecation-shim==0.1.0.post0
41 | PyYAML==6.0
42 | redis==4.3.5
43 | requests==2.28.1
44 | rsa==4.9
45 | six==1.16.0
46 | sniffio==1.3.0
47 | SQLAlchemy==1.4.44
48 | starlette==0.25.0
49 | typing_extensions==4.4.0
50 | tzdata==2022.6
51 | tzlocal==4.2
52 | urllib3==1.26.12
53 | uvicorn==0.19.0
54 | uvloop==0.17.0
55 | watchfiles==0.18.1
56 | websockets==10.4
57 | wrapt==1.14.1
58 | zipp==3.10.0
59 | python-dateutil==2.8.2
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build
2 |
3 | on:
4 | push:
5 | tags:
6 | - 'v*.*.*'
7 |
8 | jobs:
9 | build-docker-image:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Checkout
13 | uses: actions/checkout@v3
14 |
15 | - name: Set up QEMU
16 | uses: docker/setup-qemu-action@v2
17 |
18 | - name: Set up Docker Buildx
19 | uses: docker/setup-buildx-action@v2
20 |
21 | - name: Login to Docker Hub
22 | uses: docker/login-action@v2
23 | with:
24 | username: ${{ secrets.DOCKERHUB_USERNAME }}
25 | password: ${{ secrets.DOCKERHUB_TOKEN }}
26 |
27 | - name: Login to GitHub Container Registry
28 | uses: docker/login-action@v2
29 | with:
30 | registry: ghcr.io
31 | username: ${{ github.repository_owner }}
32 | password: ${{ secrets.GITHUB_TOKEN }}
33 |
34 | - name: Build and push
35 | uses: docker/build-push-action@v3
36 | with:
37 | context: .
38 | platforms: linux/amd64,linux/arm64
39 | push: true
40 | tags: |
41 | gozargah/marzban:latest
42 | gozargah/marzban:${{github.ref_name}}
43 | ghcr.io/gozargah/marzban:latest
44 | ghcr.io/gozargah/marzban:${{github.ref_name}}
45 |
--------------------------------------------------------------------------------
/app/dashboard/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Marzban
7 |
12 |
18 |
24 |
25 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/app/telegram/__init__.py:
--------------------------------------------------------------------------------
1 | import glob
2 | import importlib.util
3 | from os.path import basename, dirname, join
4 | from threading import Thread
5 | from config import TELEGRAM_API_TOKEN, TELEGRAM_PROXY_URL
6 | from app import app
7 | from telebot import TeleBot, apihelper
8 |
9 |
10 | bot = None
11 | if TELEGRAM_API_TOKEN:
12 | apihelper.proxy = {'http': TELEGRAM_PROXY_URL, 'https': TELEGRAM_PROXY_URL}
13 | bot = TeleBot(TELEGRAM_API_TOKEN)
14 |
15 |
16 | @app.on_event("startup")
17 | def start_bot():
18 | if bot:
19 | handler = glob.glob(join(dirname(__file__), "*.py"))
20 | for file in handler:
21 | name = basename(file).replace('.py', '')
22 | if name.startswith('_'):
23 | continue
24 | spec = importlib.util.spec_from_file_location(name, file)
25 | spec.loader.exec_module(importlib.util.module_from_spec(spec))
26 |
27 | thread = Thread(target=bot.infinity_polling, daemon=True)
28 | thread.start()
29 |
30 |
31 | from .report import ( # noqa
32 | report,
33 | report_new_user,
34 | report_user_modification,
35 | report_user_deletion,
36 | report_status_change
37 | )
38 |
39 | __all__ = [
40 | "bot",
41 | "report",
42 | "report_new_user",
43 | "report_user_modification",
44 | "report_user_deletion",
45 | "report_status_change"
46 | ]
47 |
--------------------------------------------------------------------------------
/app/dashboard/README.md:
--------------------------------------------------------------------------------
1 | # Dashboard UI for marzban
2 |
3 | ## Requirements
4 |
5 | For development, you will only need Node.js installed on your environement.
6 |
7 | ### Node
8 |
9 | [Node](http://nodejs.org/) is really easy to install & now include [NPM](https://npmjs.org/).
10 | This project has been developed on the Nodejs v16.17.0 so if you faced any issue during installation that may related to the node version, install Node with version >= v16.17.0.
11 |
12 | ## Install
13 |
14 | git clone https://github.com/gozargah/marz-manager.git
15 | cd marz-manager
16 | yarn install
17 |
18 | ### Configure app
19 |
20 | Copy `example.env` to `.env` then set the backend api address:
21 |
22 | VITE_BASE_API=https://somewhere.com/
23 |
24 | #### Environment variables
25 |
26 | | Name | Description |
27 | | ------------- | ------------------------------------------------------------------------------------ |
28 | | VITE_BASE_API | The api url of the deployed backend ([Marzban](https://github.com/gozargah/Marzban)) |
29 |
30 | ## Start development server
31 |
32 | yarn dev
33 |
34 | ## Simple build for production
35 |
36 | yarn build
37 |
38 | ## Contribution
39 |
40 | Feel free to contribute. Go on and fork the project. After commiting the changes, make a PR. It means a lot to us.
41 |
--------------------------------------------------------------------------------
/app/db/migrations/versions/3cf36a5fde73_init_system_table.py:
--------------------------------------------------------------------------------
1 | """init system table
2 |
3 | Revision ID: 3cf36a5fde73
4 | Revises: 94a5cc12c0d6
5 | Create Date: 2022-11-22 04:48:55.227490
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '3cf36a5fde73'
14 | down_revision = '94a5cc12c0d6'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade() -> None:
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | table = op.create_table('system',
22 | sa.Column('id', sa.Integer(), nullable=False),
23 | sa.Column('uplink', sa.BigInteger(), nullable=True),
24 | sa.Column('downlink', sa.BigInteger(), nullable=True),
25 | sa.PrimaryKeyConstraint('id')
26 | )
27 | op.create_index(op.f('ix_system_id'), 'system', ['id'], unique=False)
28 |
29 | # INSERT DEFAULT ROW
30 | op.bulk_insert(table, [{"id": 1, "uplink": 0, "downlink": 0}])
31 |
32 | # ### end Alembic commands ###
33 |
34 |
35 | def downgrade() -> None:
36 | # ### commands auto generated by Alembic - please adjust! ###
37 | op.drop_index(op.f('ix_system_id'), table_name='system')
38 | op.drop_table('system')
39 | # ### end Alembic commands ###
40 |
--------------------------------------------------------------------------------
/app/dashboard/src/pages/Dashboard.tsx:
--------------------------------------------------------------------------------
1 | import { Box, Button, VStack } from "@chakra-ui/react";
2 | import { Statistics } from "../components/Statistics";
3 | import { FC, useEffect } from "react";
4 | import { Footer } from "components/Footer";
5 | import { Header } from "components/Header";
6 | import { UsersTable } from "components/UsersTable";
7 | import { Filters } from "components/Filters";
8 | import { fetchInbounds, useDashboard } from "contexts/DashboardContext";
9 | import { UserDialog } from "components/UserDialog";
10 | import { DeleteUserModal } from "components/DeleteUserModal";
11 | import { QRCodeDialog } from "components/QRCodeDialog";
12 | import { HostsDialog } from "components/HostsDialog";
13 | import { ResetUserUsageModal } from "components/ResetUserUsageModal";
14 |
15 | export const Dashboard: FC = () => {
16 | useEffect(() => {
17 | useDashboard.getState().refetchUsers();
18 | fetchInbounds();
19 | }, []);
20 | return (
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | );
36 | };
37 |
38 | export default Dashboard;
39 |
--------------------------------------------------------------------------------
/app/jobs/reset_user_data_usage.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime
2 |
3 | from app import logger, scheduler
4 | from app.db import crud, get_db, get_users
5 | from app.models.user import UserDataLimitResetStrategy, UserStatus
6 | from app.utils.xray import xray_add_user
7 |
8 |
9 | reset_strategy_to_days = {
10 | UserDataLimitResetStrategy.day.value: 1,
11 | UserDataLimitResetStrategy.week.value: 7,
12 | UserDataLimitResetStrategy.month.value: 30,
13 | UserDataLimitResetStrategy.year.value: 365,
14 | }
15 |
16 |
17 | def reset_user_data_usage():
18 | now = datetime.utcnow()
19 | db = next(get_db())
20 | for user in get_users(db, reset_strategy=[
21 | UserDataLimitResetStrategy.day.value,
22 | UserDataLimitResetStrategy.week.value,
23 | UserDataLimitResetStrategy.month.value,
24 | UserDataLimitResetStrategy.year.value,
25 | ]):
26 | last_reset_time = user.last_traffic_reset_time
27 | num_days_to_reset = reset_strategy_to_days[user.data_limit_reset_strategy]
28 |
29 | if not (now - last_reset_time).days >= num_days_to_reset:
30 | continue
31 |
32 | crud.reset_user_data_usage(db, user)
33 | if user.status == UserStatus.limited:
34 | xray_add_user(user)
35 |
36 | logger.info(f"User data usage reset for User \"{user.username}\"")
37 |
38 |
39 | scheduler.add_job(reset_user_data_usage, 'interval', hours=1)
40 |
--------------------------------------------------------------------------------
/app/dashboard/__init__.py:
--------------------------------------------------------------------------------
1 | import atexit
2 | import os
3 | import subprocess
4 | from pathlib import Path
5 |
6 | from app import app
7 | from config import DEBUG, UVICORN_PORT
8 | from fastapi.staticfiles import StaticFiles
9 |
10 | path = '/dashboard/'
11 | base_dir = Path(__file__).parent
12 | build_dir = base_dir / 'build'
13 |
14 |
15 | def build():
16 | proc = subprocess.Popen(
17 | ['npm', 'run', 'build', '--', '--base', path, '--outDir', build_dir],
18 | env={**os.environ, 'VITE_BASE_API': '/api/'},
19 | cwd=base_dir
20 | )
21 | proc.wait()
22 | with open(build_dir / 'index.html', 'r') as file:
23 | html = file.read()
24 | with open(build_dir / '404.html', 'w') as file:
25 | file.write(html)
26 |
27 |
28 | def run_dev():
29 | proc = subprocess.Popen(
30 | ['npm', 'run', 'dev', '--', '--base', path, '--clearScreen', 'false'],
31 | env={**os.environ, 'VITE_BASE_API': f'http://127.0.0.1:{UVICORN_PORT}/api/'},
32 | cwd=base_dir
33 | )
34 |
35 | atexit.register(proc.terminate)
36 |
37 |
38 | def run_build():
39 | if not build_dir.is_dir():
40 | build()
41 |
42 | app.mount(
43 | path,
44 | StaticFiles(directory=build_dir, html=True),
45 | name="dashboard"
46 | )
47 |
48 |
49 | @app.on_event("startup")
50 | def startup():
51 | if DEBUG is True:
52 | run_dev()
53 | else:
54 | run_build()
55 |
--------------------------------------------------------------------------------
/app/db/migrations/versions/e91236993f1a_inbounds_table_excluded_inbounds.py:
--------------------------------------------------------------------------------
1 | """inbounds table & excluded_inbounds
2 |
3 | Revision ID: e91236993f1a
4 | Revises: 671621870b02
5 | Create Date: 2023-02-05 23:21:27.828558
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'e91236993f1a'
14 | down_revision = '671621870b02'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade() -> None:
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.create_table('inbounds',
22 | sa.Column('id', sa.Integer(), nullable=False),
23 | sa.Column('tag', sa.String(256), nullable=False),
24 | sa.PrimaryKeyConstraint('id')
25 | )
26 | op.create_index(op.f('ix_inbounds_tag'), 'inbounds', ['tag'], unique=True)
27 | op.create_table('exclude_inbounds_association',
28 | sa.Column('proxy_id', sa.Integer(), nullable=True),
29 | sa.Column('inbound_tag', sa.String(256), nullable=True),
30 | sa.ForeignKeyConstraint(['inbound_tag'], ['inbounds.tag'], ),
31 | sa.ForeignKeyConstraint(['proxy_id'], ['proxies.id'], )
32 | )
33 | # ### end Alembic commands ###
34 |
35 |
36 | def downgrade() -> None:
37 | # ### commands auto generated by Alembic - please adjust! ###
38 | op.drop_table('exclude_inbounds_association')
39 | op.drop_index(op.f('ix_inbounds_tag'), table_name='inbounds')
40 | op.drop_table('inbounds')
41 | # ### end Alembic commands ###
42 |
--------------------------------------------------------------------------------
/app/db/__init__.py:
--------------------------------------------------------------------------------
1 | from sqlalchemy.orm import Session
2 | from .base import Base, SessionLocal, engine # noqa
3 |
4 | from fastapi import Depends
5 |
6 |
7 | class GetDB: # Context Manager
8 | def __init__(self):
9 | self.db = SessionLocal()
10 |
11 | def __enter__(self):
12 | return self.db
13 |
14 | def __exit__(self, exc_type, exc_value, traceback):
15 | self.db.close()
16 |
17 |
18 | def get_db(): # Dependency
19 | with GetDB() as db:
20 | yield db
21 |
22 |
23 | from .models import User, System, JWT # noqa
24 | from .crud import ( # noqa
25 | get_or_create_inbound,
26 | get_user,
27 | get_user_by_id,
28 | get_users,
29 | get_users_count,
30 | create_user,
31 | remove_user,
32 | update_user,
33 | update_user_status,
34 | get_system_usage,
35 | get_jwt_secret_key,
36 | get_admin,
37 | create_admin,
38 | update_admin,
39 | remove_admin,
40 | get_admins
41 | )
42 |
43 |
44 | __all__ = [
45 | "get_or_create_inbound",
46 | "get_user",
47 | "get_user_by_id",
48 | "get_users",
49 | "get_users_count",
50 | "create_user",
51 | "remove_user",
52 | "update_user",
53 | "update_user_status",
54 | "get_system_usage",
55 | "get_jwt_secret_key",
56 | "get_admin",
57 | "create_admin",
58 | "update_admin",
59 | "remove_admin",
60 | "get_admins",
61 |
62 | "GetDB",
63 | "get_db",
64 |
65 | "User",
66 | "System",
67 | "JWT",
68 |
69 | "Base",
70 | "Session",
71 | ]
72 |
--------------------------------------------------------------------------------
/xray_api/proto/app/metrics/config_pb2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by the protocol buffer compiler. DO NOT EDIT!
3 | # source: app/metrics/config.proto
4 | """Generated protocol buffer code."""
5 | from google.protobuf import descriptor as _descriptor
6 | from google.protobuf import descriptor_pool as _descriptor_pool
7 | from google.protobuf import message as _message
8 | from google.protobuf import reflection as _reflection
9 | from google.protobuf import symbol_database as _symbol_database
10 | # @@protoc_insertion_point(imports)
11 |
12 | _sym_db = _symbol_database.Default()
13 |
14 |
15 |
16 |
17 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18\x61pp/metrics/config.proto\x12\x10xray.app.metrics\"\x15\n\x06\x43onfig\x12\x0b\n\x03tag\x18\x01 \x01(\tBR\n\x14\x63om.xray.app.metricsP\x01Z%github.com/xtls/xray-core/app/metrics\xaa\x02\x10Xray.App.Metricsb\x06proto3')
18 |
19 |
20 |
21 | _CONFIG = DESCRIPTOR.message_types_by_name['Config']
22 | Config = _reflection.GeneratedProtocolMessageType('Config', (_message.Message,), {
23 | 'DESCRIPTOR' : _CONFIG,
24 | '__module__' : 'app.metrics.config_pb2'
25 | # @@protoc_insertion_point(class_scope:xray.app.metrics.Config)
26 | })
27 | _sym_db.RegisterMessage(Config)
28 |
29 | if _descriptor._USE_C_DESCRIPTORS == False:
30 |
31 | DESCRIPTOR._options = None
32 | DESCRIPTOR._serialized_options = b'\n\024com.xray.app.metricsP\001Z%github.com/xtls/xray-core/app/metrics\252\002\020Xray.App.Metrics'
33 | _CONFIG._serialized_start=46
34 | _CONFIG._serialized_end=67
35 | # @@protoc_insertion_point(module_scope)
36 |
--------------------------------------------------------------------------------
/xray_api/proto/common/log/log_pb2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by the protocol buffer compiler. DO NOT EDIT!
3 | # source: common/log/log.proto
4 | """Generated protocol buffer code."""
5 | from google.protobuf.internal import enum_type_wrapper
6 | from google.protobuf import descriptor as _descriptor
7 | from google.protobuf import descriptor_pool as _descriptor_pool
8 | from google.protobuf import message as _message
9 | from google.protobuf import reflection as _reflection
10 | from google.protobuf import symbol_database as _symbol_database
11 | # @@protoc_insertion_point(imports)
12 |
13 | _sym_db = _symbol_database.Default()
14 |
15 |
16 |
17 |
18 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14\x63ommon/log/log.proto\x12\x0fxray.common.log*D\n\x08Severity\x12\x0b\n\x07Unknown\x10\x00\x12\t\n\x05\x45rror\x10\x01\x12\x0b\n\x07Warning\x10\x02\x12\x08\n\x04Info\x10\x03\x12\t\n\x05\x44\x65\x62ug\x10\x04\x42O\n\x13\x63om.xray.common.logP\x01Z$github.com/xtls/xray-core/common/log\xaa\x02\x0fXray.Common.Logb\x06proto3')
19 |
20 | _SEVERITY = DESCRIPTOR.enum_types_by_name['Severity']
21 | Severity = enum_type_wrapper.EnumTypeWrapper(_SEVERITY)
22 | Unknown = 0
23 | Error = 1
24 | Warning = 2
25 | Info = 3
26 | Debug = 4
27 |
28 |
29 | if _descriptor._USE_C_DESCRIPTORS == False:
30 |
31 | DESCRIPTOR._options = None
32 | DESCRIPTOR._serialized_options = b'\n\023com.xray.common.logP\001Z$github.com/xtls/xray-core/common/log\252\002\017Xray.Common.Log'
33 | _SEVERITY._serialized_start=41
34 | _SEVERITY._serialized_end=109
35 | # @@protoc_insertion_point(module_scope)
36 |
--------------------------------------------------------------------------------
/app/__init__.py:
--------------------------------------------------------------------------------
1 | from datetime import timezone
2 | import logging
3 |
4 | from apscheduler.schedulers.background import BackgroundScheduler
5 | from config import DOCS
6 | from fastapi import FastAPI, Request, status
7 | from fastapi.encoders import jsonable_encoder
8 | from fastapi.exceptions import RequestValidationError
9 | from fastapi.middleware.cors import CORSMiddleware
10 | from fastapi.responses import JSONResponse
11 | from fastapi_responses import custom_openapi
12 |
13 | app = FastAPI(
14 | docs_url='/docs' if DOCS else None,
15 | redoc_url='/redoc' if DOCS else None
16 | )
17 | app.openapi = custom_openapi(app)
18 | scheduler = BackgroundScheduler({'apscheduler.job_defaults.max_instances': 5}, timezone='UTC')
19 | logger = logging.getLogger('uvicorn.error')
20 | app.add_middleware(
21 | CORSMiddleware,
22 | allow_origins=["*"],
23 | allow_credentials=True,
24 | allow_methods=["*"],
25 | allow_headers=["*"],
26 | )
27 |
28 |
29 | from app import dashboard, jobs, views, telegram # noqa
30 |
31 |
32 | @app.on_event("startup")
33 | def on_startup():
34 | scheduler.start()
35 |
36 |
37 | @app.on_event("shutdown")
38 | def on_shutdown():
39 | scheduler.shutdown()
40 |
41 |
42 | @app.exception_handler(RequestValidationError)
43 | def validation_exception_handler(request: Request, exc: RequestValidationError):
44 | details = {}
45 | for error in exc.errors():
46 | details[error["loc"][1]] = error["msg"]
47 | return JSONResponse(
48 | status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
49 | content=jsonable_encoder({"detail": details}),
50 | )
51 |
--------------------------------------------------------------------------------
/app/dashboard/build/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Marzban
7 |
12 |
18 |
24 |
25 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/app/dashboard/build/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Marzban
7 |
12 |
18 |
24 |
25 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/xray_api/proto/proxy/loopback/config_pb2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by the protocol buffer compiler. DO NOT EDIT!
3 | # source: proxy/loopback/config.proto
4 | """Generated protocol buffer code."""
5 | from google.protobuf import descriptor as _descriptor
6 | from google.protobuf import descriptor_pool as _descriptor_pool
7 | from google.protobuf import message as _message
8 | from google.protobuf import reflection as _reflection
9 | from google.protobuf import symbol_database as _symbol_database
10 | # @@protoc_insertion_point(imports)
11 |
12 | _sym_db = _symbol_database.Default()
13 |
14 |
15 |
16 |
17 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1bproxy/loopback/config.proto\x12\x13xray.proxy.loopback\"\x1d\n\x06\x43onfig\x12\x13\n\x0binbound_tag\x18\x01 \x01(\tB[\n\x17\x63om.xray.proxy.loopbackP\x01Z(github.com/xtls/xray-core/proxy/loopback\xaa\x02\x13Xray.Proxy.Loopbackb\x06proto3')
18 |
19 |
20 |
21 | _CONFIG = DESCRIPTOR.message_types_by_name['Config']
22 | Config = _reflection.GeneratedProtocolMessageType('Config', (_message.Message,), {
23 | 'DESCRIPTOR' : _CONFIG,
24 | '__module__' : 'proxy.loopback.config_pb2'
25 | # @@protoc_insertion_point(class_scope:xray.proxy.loopback.Config)
26 | })
27 | _sym_db.RegisterMessage(Config)
28 |
29 | if _descriptor._USE_C_DESCRIPTORS == False:
30 |
31 | DESCRIPTOR._options = None
32 | DESCRIPTOR._serialized_options = b'\n\027com.xray.proxy.loopbackP\001Z(github.com/xtls/xray-core/proxy/loopback\252\002\023Xray.Proxy.Loopback'
33 | _CONFIG._serialized_start=52
34 | _CONFIG._serialized_end=81
35 | # @@protoc_insertion_point(module_scope)
36 |
--------------------------------------------------------------------------------
/app/dashboard/src/components/Icon.tsx:
--------------------------------------------------------------------------------
1 | import { Box, Text } from "@chakra-ui/react";
2 | import { FC, PropsWithChildren } from "react";
3 |
4 | export type IconType = {
5 | color: string;
6 | };
7 |
8 | export const Icon: FC> = ({ children, color }) => {
9 | return (
10 |
51 |
57 | {children}
58 |
59 |
60 | );
61 | };
62 |
--------------------------------------------------------------------------------
/app/db/migrations/versions/d02dcfbf1517_add_userusageresetlogs_model_and_data_.py:
--------------------------------------------------------------------------------
1 | """add UserUsageResetLogs Model and data_limit_strategy field to user
2 |
3 | Revision ID: d02dcfbf1517
4 | Revises: 671621870b02
5 | Create Date: 2023-02-01 21:10:49.830928
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'd02dcfbf1517'
14 | down_revision = '671621870b02'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade() -> None:
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.create_table('user_usage_logs',
22 | sa.Column('id', sa.Integer(), nullable=False),
23 | sa.Column('user_id', sa.Integer(), nullable=True),
24 | sa.Column('used_traffic_at_reset', sa.BigInteger(), nullable=False),
25 | sa.Column('reset_at', sa.DateTime(), nullable=True),
26 | sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
27 | sa.PrimaryKeyConstraint('id')
28 | )
29 | op.create_index(op.f('ix_user_usage_logs_id'), 'user_usage_logs', ['id'], unique=False)
30 | op.add_column('users', sa.Column('data_limit_reset_strategy', sa.Enum("no_reset", "day","week", "month", "year", name="userdatalimitresetstrategy"), nullable=False, server_default="no_reset"))
31 | # ### end Alembic commands ###
32 |
33 |
34 | def downgrade() -> None:
35 | # ### commands auto generated by Alembic - please adjust! ###
36 | op.drop_column('users', 'data_limit_reset_strategy')
37 | op.drop_index(op.f('ix_user_usage_logs_id'), table_name='user_usage_logs')
38 | op.drop_table('user_usage_logs')
39 | # ### end Alembic commands ###
40 |
--------------------------------------------------------------------------------
/xray_api/exceptions.py:
--------------------------------------------------------------------------------
1 | import re
2 |
3 | import grpc
4 |
5 |
6 | class XrayError(Exception):
7 | REGEXP = ...
8 |
9 | def __init__(self, details):
10 | self.details = details
11 |
12 |
13 | class EmailExistsError(XrayError):
14 | REGEXP = re.compile(r"User (.*) already exists.")
15 |
16 | def __init__(self, details, email):
17 | self.email = email
18 | super().__init__(details)
19 |
20 |
21 | class EmailNotFoundError(XrayError):
22 | REGEXP = re.compile(r"User (.*) not found.")
23 |
24 | def __init__(self, details, email):
25 | self.email = email
26 | super().__init__(details)
27 |
28 |
29 | class TagNotFoundError(XrayError):
30 | REGEXP = re.compile(r"handler not found: (.*)")
31 |
32 | def __init__(self, details, tag):
33 | self.tag = tag
34 | super().__init__(details)
35 |
36 |
37 | class ConnectionError(XrayError):
38 | REGEXP = re.compile(r"Failed to connect to remote host|Socket closed|Broken pipe")
39 |
40 | def __init__(self, details, tag):
41 | self.tag = tag
42 | super().__init__(details)
43 |
44 |
45 | class UnkownError(XrayError):
46 | def __init__(self, details=''):
47 | super().__init__(details)
48 |
49 |
50 | class RelatedError(XrayError):
51 | def __new__(cls, error: grpc.RpcError):
52 | details = error.details()
53 | for e in (EmailExistsError, EmailNotFoundError, TagNotFoundError, ConnectionError):
54 | args = e.REGEXP.findall(details)
55 | if not args:
56 | continue
57 |
58 | return e(details, *args)
59 |
60 | return UnkownError(details)
61 |
--------------------------------------------------------------------------------
/xray_api/proto/proxy/vless/account_pb2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by the protocol buffer compiler. DO NOT EDIT!
3 | # source: proxy/vless/account.proto
4 | """Generated protocol buffer code."""
5 | from google.protobuf import descriptor as _descriptor
6 | from google.protobuf import descriptor_pool as _descriptor_pool
7 | from google.protobuf import message as _message
8 | from google.protobuf import reflection as _reflection
9 | from google.protobuf import symbol_database as _symbol_database
10 | # @@protoc_insertion_point(imports)
11 |
12 | _sym_db = _symbol_database.Default()
13 |
14 |
15 |
16 |
17 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x19proxy/vless/account.proto\x12\x10xray.proxy.vless\"7\n\x07\x41\x63\x63ount\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04\x66low\x18\x02 \x01(\t\x12\x12\n\nencryption\x18\x03 \x01(\tBR\n\x14\x63om.xray.proxy.vlessP\x01Z%github.com/xtls/xray-core/proxy/vless\xaa\x02\x10Xray.Proxy.Vlessb\x06proto3')
18 |
19 |
20 |
21 | _ACCOUNT = DESCRIPTOR.message_types_by_name['Account']
22 | Account = _reflection.GeneratedProtocolMessageType('Account', (_message.Message,), {
23 | 'DESCRIPTOR' : _ACCOUNT,
24 | '__module__' : 'proxy.vless.account_pb2'
25 | # @@protoc_insertion_point(class_scope:xray.proxy.vless.Account)
26 | })
27 | _sym_db.RegisterMessage(Account)
28 |
29 | if _descriptor._USE_C_DESCRIPTORS == False:
30 |
31 | DESCRIPTOR._options = None
32 | DESCRIPTOR._serialized_options = b'\n\024com.xray.proxy.vlessP\001Z%github.com/xtls/xray-core/proxy/vless\252\002\020Xray.Proxy.Vless'
33 | _ACCOUNT._serialized_start=47
34 | _ACCOUNT._serialized_end=102
35 | # @@protoc_insertion_point(module_scope)
36 |
--------------------------------------------------------------------------------
/xray_api/proto/transport/internet/udp/config_pb2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by the protocol buffer compiler. DO NOT EDIT!
3 | # source: transport/internet/udp/config.proto
4 | """Generated protocol buffer code."""
5 | from google.protobuf import descriptor as _descriptor
6 | from google.protobuf import descriptor_pool as _descriptor_pool
7 | from google.protobuf import message as _message
8 | from google.protobuf import reflection as _reflection
9 | from google.protobuf import symbol_database as _symbol_database
10 | # @@protoc_insertion_point(imports)
11 |
12 | _sym_db = _symbol_database.Default()
13 |
14 |
15 |
16 |
17 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n#transport/internet/udp/config.proto\x12\x1bxray.transport.internet.udp\"\x08\n\x06\x43onfigBs\n\x1f\x63om.xray.transport.internet.udpP\x01Z0github.com/xtls/xray-core/transport/internet/udp\xaa\x02\x1bXray.Transport.Internet.Udpb\x06proto3')
18 |
19 |
20 |
21 | _CONFIG = DESCRIPTOR.message_types_by_name['Config']
22 | Config = _reflection.GeneratedProtocolMessageType('Config', (_message.Message,), {
23 | 'DESCRIPTOR' : _CONFIG,
24 | '__module__' : 'transport.internet.udp.config_pb2'
25 | # @@protoc_insertion_point(class_scope:xray.transport.internet.udp.Config)
26 | })
27 | _sym_db.RegisterMessage(Config)
28 |
29 | if _descriptor._USE_C_DESCRIPTORS == False:
30 |
31 | DESCRIPTOR._options = None
32 | DESCRIPTOR._serialized_options = b'\n\037com.xray.transport.internet.udpP\001Z0github.com/xtls/xray-core/transport/internet/udp\252\002\033Xray.Transport.Internet.Udp'
33 | _CONFIG._serialized_start=68
34 | _CONFIG._serialized_end=76
35 | # @@protoc_insertion_point(module_scope)
36 |
--------------------------------------------------------------------------------
/xray_api/proto/common/net/address_pb2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by the protocol buffer compiler. DO NOT EDIT!
3 | # source: common/net/address.proto
4 | """Generated protocol buffer code."""
5 | from google.protobuf import descriptor as _descriptor
6 | from google.protobuf import descriptor_pool as _descriptor_pool
7 | from google.protobuf import message as _message
8 | from google.protobuf import reflection as _reflection
9 | from google.protobuf import symbol_database as _symbol_database
10 | # @@protoc_insertion_point(imports)
11 |
12 | _sym_db = _symbol_database.Default()
13 |
14 |
15 |
16 |
17 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18\x63ommon/net/address.proto\x12\x0fxray.common.net\"7\n\nIPOrDomain\x12\x0c\n\x02ip\x18\x01 \x01(\x0cH\x00\x12\x10\n\x06\x64omain\x18\x02 \x01(\tH\x00\x42\t\n\x07\x61\x64\x64ressBO\n\x13\x63om.xray.common.netP\x01Z$github.com/xtls/xray-core/common/net\xaa\x02\x0fXray.Common.Netb\x06proto3')
18 |
19 |
20 |
21 | _IPORDOMAIN = DESCRIPTOR.message_types_by_name['IPOrDomain']
22 | IPOrDomain = _reflection.GeneratedProtocolMessageType('IPOrDomain', (_message.Message,), {
23 | 'DESCRIPTOR' : _IPORDOMAIN,
24 | '__module__' : 'common.net.address_pb2'
25 | # @@protoc_insertion_point(class_scope:xray.common.net.IPOrDomain)
26 | })
27 | _sym_db.RegisterMessage(IPOrDomain)
28 |
29 | if _descriptor._USE_C_DESCRIPTORS == False:
30 |
31 | DESCRIPTOR._options = None
32 | DESCRIPTOR._serialized_options = b'\n\023com.xray.common.netP\001Z$github.com/xtls/xray-core/common/net\252\002\017Xray.Common.Net'
33 | _IPORDOMAIN._serialized_start=45
34 | _IPORDOMAIN._serialized_end=100
35 | # @@protoc_insertion_point(module_scope)
36 |
--------------------------------------------------------------------------------
/app/utils/xray.py:
--------------------------------------------------------------------------------
1 | from app import xray
2 | from app.db import GetDB, User, get_users
3 | from app.models.proxy import ProxySettings
4 | from app.models.user import User, UserResponse, UserStatus
5 | from app.xray.config import XRayConfig
6 |
7 |
8 | def xray_add_user(user: User):
9 | if not isinstance(user, User):
10 | user = UserResponse.from_orm(user)
11 | for proxy_type, inbound_tags in user.inbounds.items():
12 | account = user.get_account(proxy_type)
13 | for inbound_tag in inbound_tags:
14 | try:
15 | xray.api.add_inbound_user(tag=inbound_tag, user=account)
16 | except xray.exc.EmailExistsError:
17 | pass
18 |
19 |
20 | def xray_remove_user(user: User):
21 | for inbound_tag in xray.config.inbounds_by_tag:
22 | try:
23 | xray.api.remove_inbound_user(tag=inbound_tag, email=user.username)
24 | except xray.exc.EmailNotFoundError:
25 | pass
26 |
27 |
28 | def xray_config_include_db_clients(config: XRayConfig):
29 | config = config.copy()
30 |
31 | with GetDB() as db:
32 | for user in get_users(db, status=UserStatus.active):
33 | proxies_settings = {
34 | p.type: ProxySettings.from_dict(p.type, p.settings).dict(no_obj=True)
35 | for p in user.proxies
36 | }
37 | for proxy_type, inbound_tags in user.inbounds.items():
38 | for inbound_tag in inbound_tags:
39 | config.add_inbound_client(inbound_tag,
40 | user.username,
41 | proxies_settings[proxy_type])
42 |
43 | return config
44 |
--------------------------------------------------------------------------------
/xray_api/proto/common/serial/typed_message_pb2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by the protocol buffer compiler. DO NOT EDIT!
3 | # source: common/serial/typed_message.proto
4 | """Generated protocol buffer code."""
5 | from google.protobuf import descriptor as _descriptor
6 | from google.protobuf import descriptor_pool as _descriptor_pool
7 | from google.protobuf import message as _message
8 | from google.protobuf import reflection as _reflection
9 | from google.protobuf import symbol_database as _symbol_database
10 | # @@protoc_insertion_point(imports)
11 |
12 | _sym_db = _symbol_database.Default()
13 |
14 |
15 |
16 |
17 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n!common/serial/typed_message.proto\x12\x12xray.common.serial\"+\n\x0cTypedMessage\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x0c\x42X\n\x16\x63om.xray.common.serialP\x01Z\'github.com/xtls/xray-core/common/serial\xaa\x02\x12Xray.Common.Serialb\x06proto3')
18 |
19 |
20 |
21 | _TYPEDMESSAGE = DESCRIPTOR.message_types_by_name['TypedMessage']
22 | TypedMessage = _reflection.GeneratedProtocolMessageType('TypedMessage', (_message.Message,), {
23 | 'DESCRIPTOR' : _TYPEDMESSAGE,
24 | '__module__' : 'common.serial.typed_message_pb2'
25 | # @@protoc_insertion_point(class_scope:xray.common.serial.TypedMessage)
26 | })
27 | _sym_db.RegisterMessage(TypedMessage)
28 |
29 | if _descriptor._USE_C_DESCRIPTORS == False:
30 |
31 | DESCRIPTOR._options = None
32 | DESCRIPTOR._serialized_options = b'\n\026com.xray.common.serialP\001Z\'github.com/xtls/xray-core/common/serial\252\002\022Xray.Common.Serial'
33 | _TYPEDMESSAGE._serialized_start=57
34 | _TYPEDMESSAGE._serialized_end=100
35 | # @@protoc_insertion_point(module_scope)
36 |
--------------------------------------------------------------------------------
/xray_api/proto/proxy/vless/encoding/addons_pb2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by the protocol buffer compiler. DO NOT EDIT!
3 | # source: proxy/vless/encoding/addons.proto
4 | """Generated protocol buffer code."""
5 | from google.protobuf import descriptor as _descriptor
6 | from google.protobuf import descriptor_pool as _descriptor_pool
7 | from google.protobuf import message as _message
8 | from google.protobuf import reflection as _reflection
9 | from google.protobuf import symbol_database as _symbol_database
10 | # @@protoc_insertion_point(imports)
11 |
12 | _sym_db = _symbol_database.Default()
13 |
14 |
15 |
16 |
17 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n!proxy/vless/encoding/addons.proto\x12\x19xray.proxy.vless.encoding\"$\n\x06\x41\x64\x64ons\x12\x0c\n\x04\x46low\x18\x01 \x01(\t\x12\x0c\n\x04Seed\x18\x02 \x01(\x0c\x42m\n\x1d\x63om.xray.proxy.vless.encodingP\x01Z.github.com/xtls/xray-core/proxy/vless/encoding\xaa\x02\x19Xray.Proxy.Vless.Encodingb\x06proto3')
18 |
19 |
20 |
21 | _ADDONS = DESCRIPTOR.message_types_by_name['Addons']
22 | Addons = _reflection.GeneratedProtocolMessageType('Addons', (_message.Message,), {
23 | 'DESCRIPTOR' : _ADDONS,
24 | '__module__' : 'proxy.vless.encoding.addons_pb2'
25 | # @@protoc_insertion_point(class_scope:xray.proxy.vless.encoding.Addons)
26 | })
27 | _sym_db.RegisterMessage(Addons)
28 |
29 | if _descriptor._USE_C_DESCRIPTORS == False:
30 |
31 | DESCRIPTOR._options = None
32 | DESCRIPTOR._serialized_options = b'\n\035com.xray.proxy.vless.encodingP\001Z.github.com/xtls/xray-core/proxy/vless/encoding\252\002\031Xray.Proxy.Vless.Encoding'
33 | _ADDONS._serialized_start=64
34 | _ADDONS._serialized_end=100
35 | # @@protoc_insertion_point(module_scope)
36 |
--------------------------------------------------------------------------------
/app/telegram/user.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime
2 | from app.db import GetDB, crud
3 | from app.models.user import UserResponse
4 | from app.telegram import bot
5 | from pytz import UTC
6 | from telebot.custom_filters import ChatFilter
7 | from telebot.util import extract_arguments
8 |
9 | from app.utils.system import readable_size
10 |
11 | bot.add_custom_filter(ChatFilter())
12 |
13 | get_user_text = """
14 | *Username*: `{username}`
15 | *Status*: `{status}`
16 | *Traffic limit*: `{traffic_limit}`
17 | *Used traffic*: `{used_traffic}`
18 | *Expiry date*: `{expires_at}`
19 | *Created at*: `{created_at}`
20 | *Proxy protocols*: `{protocols}`
21 | """
22 |
23 |
24 | @bot.message_handler(commands=['usage'])
25 | def usage_command(message):
26 | username = extract_arguments(message.text)
27 | if not username:
28 | return bot.reply_to(message, 'Usage: `/usage `', parse_mode='MarkdownV2')
29 |
30 | with GetDB() as db:
31 | dbuser = crud.get_user(db, username)
32 |
33 | if not dbuser:
34 | return bot.reply_to(message, "No user found with this username")
35 | user = UserResponse.from_orm(dbuser)
36 |
37 | text = get_user_text.format(
38 | username=user.username,
39 | status=user.status,
40 | traffic_limit=readable_size(user.data_limit) if user.data_limit else '-',
41 | used_traffic=readable_size(user.used_traffic),
42 | expires_at=datetime.fromtimestamp(user.expire, UTC).strftime('%m/%d/%Y') if user.expire else '-',
43 | created_at=user.created_at.strftime('%m/%d/%Y'),
44 | protocols=','.join(user.proxies.keys())
45 | )
46 |
47 | return bot.reply_to(message, text, parse_mode='MarkdownV2')
48 |
--------------------------------------------------------------------------------
/xray_api/proto/proxy/dns/config_pb2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by the protocol buffer compiler. DO NOT EDIT!
3 | # source: proxy/dns/config.proto
4 | """Generated protocol buffer code."""
5 | from google.protobuf import descriptor as _descriptor
6 | from google.protobuf import descriptor_pool as _descriptor_pool
7 | from google.protobuf import message as _message
8 | from google.protobuf import reflection as _reflection
9 | from google.protobuf import symbol_database as _symbol_database
10 | # @@protoc_insertion_point(imports)
11 |
12 | _sym_db = _symbol_database.Default()
13 |
14 |
15 | from xray_api.proto.common.net import destination_pb2 as common_dot_net_dot_destination__pb2
16 |
17 |
18 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16proxy/dns/config.proto\x12\x0exray.proxy.dns\x1a\x1c\x63ommon/net/destination.proto\"G\n\x06\x43onfig\x12)\n\x06server\x18\x01 \x01(\x0b\x32\x19.xray.common.net.Endpoint\x12\x12\n\nuser_level\x18\x02 \x01(\rBL\n\x12\x63om.xray.proxy.dnsP\x01Z#github.com/xtls/xray-core/proxy/dns\xaa\x02\x0eXray.Proxy.Dnsb\x06proto3')
19 |
20 |
21 |
22 | _CONFIG = DESCRIPTOR.message_types_by_name['Config']
23 | Config = _reflection.GeneratedProtocolMessageType('Config', (_message.Message,), {
24 | 'DESCRIPTOR' : _CONFIG,
25 | '__module__' : 'proxy.dns.config_pb2'
26 | # @@protoc_insertion_point(class_scope:xray.proxy.dns.Config)
27 | })
28 | _sym_db.RegisterMessage(Config)
29 |
30 | if _descriptor._USE_C_DESCRIPTORS == False:
31 |
32 | DESCRIPTOR._options = None
33 | DESCRIPTOR._serialized_options = b'\n\022com.xray.proxy.dnsP\001Z#github.com/xtls/xray-core/proxy/dns\252\002\016Xray.Proxy.Dns'
34 | _CONFIG._serialized_start=72
35 | _CONFIG._serialized_end=143
36 | # @@protoc_insertion_point(module_scope)
37 |
--------------------------------------------------------------------------------
/xray_api/proto/transport/internet/headers/utp/config_pb2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by the protocol buffer compiler. DO NOT EDIT!
3 | # source: transport/internet/headers/utp/config.proto
4 | """Generated protocol buffer code."""
5 | from google.protobuf import descriptor as _descriptor
6 | from google.protobuf import descriptor_pool as _descriptor_pool
7 | from google.protobuf import message as _message
8 | from google.protobuf import reflection as _reflection
9 | from google.protobuf import symbol_database as _symbol_database
10 | # @@protoc_insertion_point(imports)
11 |
12 | _sym_db = _symbol_database.Default()
13 |
14 |
15 |
16 |
17 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n+transport/internet/headers/utp/config.proto\x12#xray.transport.internet.headers.utp\"\x19\n\x06\x43onfig\x12\x0f\n\x07version\x18\x01 \x01(\rB\x8b\x01\n\'com.xray.transport.internet.headers.utpP\x01Z8github.com/xtls/xray-core/transport/internet/headers/utp\xaa\x02#Xray.Transport.Internet.Headers.Utpb\x06proto3')
18 |
19 |
20 |
21 | _CONFIG = DESCRIPTOR.message_types_by_name['Config']
22 | Config = _reflection.GeneratedProtocolMessageType('Config', (_message.Message,), {
23 | 'DESCRIPTOR' : _CONFIG,
24 | '__module__' : 'transport.internet.headers.utp.config_pb2'
25 | # @@protoc_insertion_point(class_scope:xray.transport.internet.headers.utp.Config)
26 | })
27 | _sym_db.RegisterMessage(Config)
28 |
29 | if _descriptor._USE_C_DESCRIPTORS == False:
30 |
31 | DESCRIPTOR._options = None
32 | DESCRIPTOR._serialized_options = b'\n\'com.xray.transport.internet.headers.utpP\001Z8github.com/xtls/xray-core/transport/internet/headers/utp\252\002#Xray.Transport.Internet.Headers.Utp'
33 | _CONFIG._serialized_start=84
34 | _CONFIG._serialized_end=109
35 | # @@protoc_insertion_point(module_scope)
36 |
--------------------------------------------------------------------------------
/.github/workflows/build-dev.yml:
--------------------------------------------------------------------------------
1 | name: Build-dev
2 |
3 | on:
4 | push:
5 | branches:
6 | - 'dev'
7 |
8 | jobs:
9 | build-dev:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Checkout
13 | uses: actions/checkout@v3
14 |
15 | - name: Setup nodejs
16 | uses: actions/setup-node@v1
17 | with:
18 | node-version: 16.17.0
19 |
20 | - name: Install dependencies
21 | working-directory: ./app/dashboard
22 | run: npm ci
23 |
24 | - name: Build project
25 | working-directory: ./app/dashboard
26 | run: VITE_BASE_API=/api/ npm run build --if-present -- --outDir build --base '/dashboard/'
27 |
28 | - name: Create 404.html
29 | working-directory: ./app/dashboard
30 | run: cp ./build/index.html ./build/404.html
31 |
32 | - name: Set up QEMU
33 | uses: docker/setup-qemu-action@v2
34 |
35 | - name: Set up Docker Buildx
36 | uses: docker/setup-buildx-action@v2
37 |
38 | - name: Login to Docker Hub
39 | uses: docker/login-action@v2
40 | with:
41 | username: ${{ secrets.DOCKERHUB_USERNAME }}
42 | password: ${{ secrets.DOCKERHUB_TOKEN }}
43 |
44 | - name: Login to GitHub Container Registry
45 | uses: docker/login-action@v2
46 | with:
47 | registry: ghcr.io
48 | username: ${{ github.repository_owner }}
49 | password: ${{ secrets.GITHUB_TOKEN }}
50 |
51 | - name: Build and push
52 | uses: docker/build-push-action@v3
53 | with:
54 | context: .
55 | platforms: linux/amd64,linux/arm64
56 | push: true
57 | tags: |
58 | gozargah/marzban:${{github.ref_name}}
59 | ghcr.io/gozargah/marzban:${{github.ref_name}}
60 |
--------------------------------------------------------------------------------
/xray_api/proto/transport/internet/headers/tls/config_pb2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by the protocol buffer compiler. DO NOT EDIT!
3 | # source: transport/internet/headers/tls/config.proto
4 | """Generated protocol buffer code."""
5 | from google.protobuf import descriptor as _descriptor
6 | from google.protobuf import descriptor_pool as _descriptor_pool
7 | from google.protobuf import message as _message
8 | from google.protobuf import reflection as _reflection
9 | from google.protobuf import symbol_database as _symbol_database
10 | # @@protoc_insertion_point(imports)
11 |
12 | _sym_db = _symbol_database.Default()
13 |
14 |
15 |
16 |
17 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n+transport/internet/headers/tls/config.proto\x12#xray.transport.internet.headers.tls\"\x0e\n\x0cPacketConfigB\x8b\x01\n\'com.xray.transport.internet.headers.tlsP\x01Z8github.com/xtls/xray-core/transport/internet/headers/tls\xaa\x02#Xray.Transport.Internet.Headers.Tlsb\x06proto3')
18 |
19 |
20 |
21 | _PACKETCONFIG = DESCRIPTOR.message_types_by_name['PacketConfig']
22 | PacketConfig = _reflection.GeneratedProtocolMessageType('PacketConfig', (_message.Message,), {
23 | 'DESCRIPTOR' : _PACKETCONFIG,
24 | '__module__' : 'transport.internet.headers.tls.config_pb2'
25 | # @@protoc_insertion_point(class_scope:xray.transport.internet.headers.tls.PacketConfig)
26 | })
27 | _sym_db.RegisterMessage(PacketConfig)
28 |
29 | if _descriptor._USE_C_DESCRIPTORS == False:
30 |
31 | DESCRIPTOR._options = None
32 | DESCRIPTOR._serialized_options = b'\n\'com.xray.transport.internet.headers.tlsP\001Z8github.com/xtls/xray-core/transport/internet/headers/tls\252\002#Xray.Transport.Internet.Headers.Tls'
33 | _PACKETCONFIG._serialized_start=84
34 | _PACKETCONFIG._serialized_end=98
35 | # @@protoc_insertion_point(module_scope)
36 |
--------------------------------------------------------------------------------
/app/db/migrations/versions/94a5cc12c0d6_init_user_table.py:
--------------------------------------------------------------------------------
1 | """init user table
2 |
3 | Revision ID: 94a5cc12c0d6
4 | Revises:
5 | Create Date: 2022-11-18 20:54:05.616546
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '94a5cc12c0d6'
14 | down_revision = None
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade() -> None:
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.create_table('users',
22 | sa.Column('id', sa.Integer(), nullable=False),
23 | sa.Column('username', sa.String(34), nullable=True),
24 | sa.Column('proxy_type', sa.Enum('VMess', 'VLESS', 'Trojan', 'Shadowsocks', name='proxytypes'), nullable=False),
25 | sa.Column('settings', sa.JSON(), nullable=False),
26 | sa.Column('status', sa.Enum('active', 'limited', 'expired', name='userstatus'), nullable=True),
27 | sa.Column('used_traffic', sa.BigInteger(), nullable=True),
28 | sa.Column('data_limit', sa.BigInteger(), nullable=True),
29 | sa.Column('expire', sa.Integer(), nullable=True),
30 | sa.PrimaryKeyConstraint('id')
31 | )
32 | op.create_index(op.f('ix_users_id'), 'users', ['id'], unique=False)
33 | op.create_index(op.f('ix_users_username'), 'users', ['username'], unique=True)
34 | # ### end Alembic commands ###
35 |
36 |
37 | def downgrade() -> None:
38 | # ### commands auto generated by Alembic - please adjust! ###
39 | op.drop_index(op.f('ix_users_username'), table_name='users')
40 | op.drop_index(op.f('ix_users_id'), table_name='users')
41 | op.drop_table('users')
42 | # ### end Alembic commands ###
43 |
--------------------------------------------------------------------------------
/xray_api/proto/transport/internet/headers/wechat/config_pb2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by the protocol buffer compiler. DO NOT EDIT!
3 | # source: transport/internet/headers/wechat/config.proto
4 | """Generated protocol buffer code."""
5 | from google.protobuf import descriptor as _descriptor
6 | from google.protobuf import descriptor_pool as _descriptor_pool
7 | from google.protobuf import message as _message
8 | from google.protobuf import reflection as _reflection
9 | from google.protobuf import symbol_database as _symbol_database
10 | # @@protoc_insertion_point(imports)
11 |
12 | _sym_db = _symbol_database.Default()
13 |
14 |
15 |
16 |
17 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n.transport/internet/headers/wechat/config.proto\x12&xray.transport.internet.headers.wechat\"\r\n\x0bVideoConfigB\x94\x01\n*com.xray.transport.internet.headers.wechatP\x01Z;github.com/xtls/xray-core/transport/internet/headers/wechat\xaa\x02&Xray.Transport.Internet.Headers.Wechatb\x06proto3')
18 |
19 |
20 |
21 | _VIDEOCONFIG = DESCRIPTOR.message_types_by_name['VideoConfig']
22 | VideoConfig = _reflection.GeneratedProtocolMessageType('VideoConfig', (_message.Message,), {
23 | 'DESCRIPTOR' : _VIDEOCONFIG,
24 | '__module__' : 'transport.internet.headers.wechat.config_pb2'
25 | # @@protoc_insertion_point(class_scope:xray.transport.internet.headers.wechat.VideoConfig)
26 | })
27 | _sym_db.RegisterMessage(VideoConfig)
28 |
29 | if _descriptor._USE_C_DESCRIPTORS == False:
30 |
31 | DESCRIPTOR._options = None
32 | DESCRIPTOR._serialized_options = b'\n*com.xray.transport.internet.headers.wechatP\001Z;github.com/xtls/xray-core/transport/internet/headers/wechat\252\002&Xray.Transport.Internet.Headers.Wechat'
33 | _VIDEOCONFIG._serialized_start=90
34 | _VIDEOCONFIG._serialized_end=103
35 | # @@protoc_insertion_point(module_scope)
36 |
--------------------------------------------------------------------------------
/xray_api/proto/common/protocol/user_pb2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by the protocol buffer compiler. DO NOT EDIT!
3 | # source: common/protocol/user.proto
4 | """Generated protocol buffer code."""
5 | from google.protobuf import descriptor as _descriptor
6 | from google.protobuf import descriptor_pool as _descriptor_pool
7 | from google.protobuf import message as _message
8 | from google.protobuf import reflection as _reflection
9 | from google.protobuf import symbol_database as _symbol_database
10 | # @@protoc_insertion_point(imports)
11 |
12 | _sym_db = _symbol_database.Default()
13 |
14 |
15 | from xray_api.proto.common.serial import typed_message_pb2 as common_dot_serial_dot_typed__message__pb2
16 |
17 |
18 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1a\x63ommon/protocol/user.proto\x12\x14xray.common.protocol\x1a!common/serial/typed_message.proto\"W\n\x04User\x12\r\n\x05level\x18\x01 \x01(\r\x12\r\n\x05\x65mail\x18\x02 \x01(\t\x12\x31\n\x07\x61\x63\x63ount\x18\x03 \x01(\x0b\x32 .xray.common.serial.TypedMessageB^\n\x18\x63om.xray.common.protocolP\x01Z)github.com/xtls/xray-core/common/protocol\xaa\x02\x14Xray.Common.Protocolb\x06proto3')
19 |
20 |
21 |
22 | _USER = DESCRIPTOR.message_types_by_name['User']
23 | User = _reflection.GeneratedProtocolMessageType('User', (_message.Message,), {
24 | 'DESCRIPTOR' : _USER,
25 | '__module__' : 'common.protocol.user_pb2'
26 | # @@protoc_insertion_point(class_scope:xray.common.protocol.User)
27 | })
28 | _sym_db.RegisterMessage(User)
29 |
30 | if _descriptor._USE_C_DESCRIPTORS == False:
31 |
32 | DESCRIPTOR._options = None
33 | DESCRIPTOR._serialized_options = b'\n\030com.xray.common.protocolP\001Z)github.com/xtls/xray-core/common/protocol\252\002\024Xray.Common.Protocol'
34 | _USER._serialized_start=87
35 | _USER._serialized_end=174
36 | # @@protoc_insertion_point(module_scope)
37 |
--------------------------------------------------------------------------------
/xray_api/proto/transport/internet/grpc/config_pb2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by the protocol buffer compiler. DO NOT EDIT!
3 | # source: transport/internet/grpc/config.proto
4 | """Generated protocol buffer code."""
5 | from google.protobuf import descriptor as _descriptor
6 | from google.protobuf import descriptor_pool as _descriptor_pool
7 | from google.protobuf import message as _message
8 | from google.protobuf import reflection as _reflection
9 | from google.protobuf import symbol_database as _symbol_database
10 | # @@protoc_insertion_point(imports)
11 |
12 | _sym_db = _symbol_database.Default()
13 |
14 |
15 |
16 |
17 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n$transport/internet/grpc/config.proto\x12%xray.transport.internet.grpc.encoding\"\xb1\x01\n\x06\x43onfig\x12\x0c\n\x04host\x18\x01 \x01(\t\x12\x14\n\x0cservice_name\x18\x02 \x01(\t\x12\x12\n\nmulti_mode\x18\x03 \x01(\x08\x12\x14\n\x0cidle_timeout\x18\x04 \x01(\x05\x12\x1c\n\x14health_check_timeout\x18\x05 \x01(\x05\x12\x1d\n\x15permit_without_stream\x18\x06 \x01(\x08\x12\x1c\n\x14initial_windows_size\x18\x07 \x01(\x05\x42\x33Z1github.com/xtls/xray-core/transport/internet/grpcb\x06proto3')
18 |
19 |
20 |
21 | _CONFIG = DESCRIPTOR.message_types_by_name['Config']
22 | Config = _reflection.GeneratedProtocolMessageType('Config', (_message.Message,), {
23 | 'DESCRIPTOR' : _CONFIG,
24 | '__module__' : 'transport.internet.grpc.config_pb2'
25 | # @@protoc_insertion_point(class_scope:xray.transport.internet.grpc.encoding.Config)
26 | })
27 | _sym_db.RegisterMessage(Config)
28 |
29 | if _descriptor._USE_C_DESCRIPTORS == False:
30 |
31 | DESCRIPTOR._options = None
32 | DESCRIPTOR._serialized_options = b'Z1github.com/xtls/xray-core/transport/internet/grpc'
33 | _CONFIG._serialized_start=80
34 | _CONFIG._serialized_end=257
35 | # @@protoc_insertion_point(module_scope)
36 |
--------------------------------------------------------------------------------
/app/db/migrations/versions/671621870b02_init_admin.py:
--------------------------------------------------------------------------------
1 | """init admin
2 |
3 | Revision ID: 671621870b02
4 | Revises: 8e849e06f131
5 | Create Date: 2023-01-13 18:57:40.199730
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '671621870b02'
14 | down_revision = '8e849e06f131'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade() -> None:
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.create_table('admins',
22 | sa.Column('id', sa.Integer(), nullable=False),
23 | sa.Column('username', sa.String(34), nullable=True),
24 | sa.Column('hashed_password', sa.String(128), nullable=True),
25 | sa.Column('created_at', sa.DateTime(), nullable=True),
26 | sa.PrimaryKeyConstraint('id')
27 | )
28 | op.create_index(op.f('ix_admins_id'), 'admins', ['id'], unique=False)
29 | op.create_index(op.f('ix_admins_username'), 'admins', ['username'], unique=True)
30 | with op.batch_alter_table('users') as batch_op:
31 | batch_op.add_column(sa.Column('admin_id', sa.Integer(), nullable=True))
32 | batch_op.create_foreign_key('fk_users_admin_id_admins', 'admins', ['admin_id'], ['id'])
33 | # ### end Alembic commands ###
34 |
35 |
36 | def downgrade() -> None:
37 | # ### commands auto generated by Alembic - please adjust! ###
38 | with op.batch_alter_table('users') as batch_op:
39 | batch_op.drop_column('admin_id')
40 | batch_op.drop_constraint('fk_users_admin_id_admins', type_='foreignkey')
41 | op.drop_index(op.f('ix_admins_username'), table_name='admins')
42 | op.drop_index(op.f('ix_admins_id'), table_name='admins')
43 | op.drop_table('admins')
44 | # ### end Alembic commands ###
45 |
--------------------------------------------------------------------------------
/xray_api/proto/proxy/vless/outbound/config_pb2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by the protocol buffer compiler. DO NOT EDIT!
3 | # source: proxy/vless/outbound/config.proto
4 | """Generated protocol buffer code."""
5 | from google.protobuf import descriptor as _descriptor
6 | from google.protobuf import descriptor_pool as _descriptor_pool
7 | from google.protobuf import message as _message
8 | from google.protobuf import reflection as _reflection
9 | from google.protobuf import symbol_database as _symbol_database
10 | # @@protoc_insertion_point(imports)
11 |
12 | _sym_db = _symbol_database.Default()
13 |
14 |
15 | from xray_api.proto.common.protocol import server_spec_pb2 as common_dot_protocol_dot_server__spec__pb2
16 |
17 |
18 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n!proxy/vless/outbound/config.proto\x12\x19xray.proxy.vless.outbound\x1a!common/protocol/server_spec.proto\"=\n\x06\x43onfig\x12\x33\n\x05vnext\x18\x01 \x03(\x0b\x32$.xray.common.protocol.ServerEndpointBm\n\x1d\x63om.xray.proxy.vless.outboundP\x01Z.github.com/xtls/xray-core/proxy/vless/outbound\xaa\x02\x19Xray.Proxy.Vless.Outboundb\x06proto3')
19 |
20 |
21 |
22 | _CONFIG = DESCRIPTOR.message_types_by_name['Config']
23 | Config = _reflection.GeneratedProtocolMessageType('Config', (_message.Message,), {
24 | 'DESCRIPTOR' : _CONFIG,
25 | '__module__' : 'proxy.vless.outbound.config_pb2'
26 | # @@protoc_insertion_point(class_scope:xray.proxy.vless.outbound.Config)
27 | })
28 | _sym_db.RegisterMessage(Config)
29 |
30 | if _descriptor._USE_C_DESCRIPTORS == False:
31 |
32 | DESCRIPTOR._options = None
33 | DESCRIPTOR._serialized_options = b'\n\035com.xray.proxy.vless.outboundP\001Z.github.com/xtls/xray-core/proxy/vless/outbound\252\002\031Xray.Proxy.Vless.Outbound'
34 | _CONFIG._serialized_start=99
35 | _CONFIG._serialized_end=160
36 | # @@protoc_insertion_point(module_scope)
37 |
--------------------------------------------------------------------------------
/app/db/migrations/versions/5b84d88804a1_fix.py:
--------------------------------------------------------------------------------
1 | """fix
2 |
3 | Revision ID: 5b84d88804a1
4 | Revises: 7cbe9d91ac11
5 | Create Date: 2023-03-08 15:41:25.827671
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '5b84d88804a1'
14 | down_revision = '7cbe9d91ac11'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade() -> None:
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | with op.batch_alter_table('hosts') as batch_op:
22 | batch_op.alter_column('sni',
23 | existing_type=sa.VARCHAR(length=256),
24 | nullable=True)
25 | batch_op.alter_column('host',
26 | existing_type=sa.VARCHAR(length=256),
27 | nullable=True)
28 | with op.batch_alter_table('users') as batch_op:
29 | batch_op.alter_column('status',
30 | existing_type=sa.VARCHAR(length=8),
31 | nullable=False)
32 | # ### end Alembic commands ###
33 |
34 |
35 | def downgrade() -> None:
36 | # ### commands auto generated by Alembic - please adjust! ###
37 | with op.batch_alter_table('users') as batch_op:
38 | batch_op.alter_column('status',
39 | existing_type=sa.VARCHAR(length=8),
40 | nullable=True)
41 | with op.batch_alter_table('hosts') as batch_op:
42 | batch_op.alter_column('host',
43 | existing_type=sa.VARCHAR(length=256),
44 | nullable=False)
45 | batch_op.alter_column('sni',
46 | existing_type=sa.VARCHAR(length=256),
47 | nullable=False)
48 | # ### end Alembic commands ###
49 |
--------------------------------------------------------------------------------
/xray_api/proto/proxy/vmess/outbound/config_pb2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by the protocol buffer compiler. DO NOT EDIT!
3 | # source: proxy/vmess/outbound/config.proto
4 | """Generated protocol buffer code."""
5 | from google.protobuf import descriptor as _descriptor
6 | from google.protobuf import descriptor_pool as _descriptor_pool
7 | from google.protobuf import message as _message
8 | from google.protobuf import reflection as _reflection
9 | from google.protobuf import symbol_database as _symbol_database
10 | # @@protoc_insertion_point(imports)
11 |
12 | _sym_db = _symbol_database.Default()
13 |
14 |
15 | from xray_api.proto.common.protocol import server_spec_pb2 as common_dot_protocol_dot_server__spec__pb2
16 |
17 |
18 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n!proxy/vmess/outbound/config.proto\x12\x19xray.proxy.vmess.outbound\x1a!common/protocol/server_spec.proto\"@\n\x06\x43onfig\x12\x36\n\x08Receiver\x18\x01 \x03(\x0b\x32$.xray.common.protocol.ServerEndpointBm\n\x1d\x63om.xray.proxy.vmess.outboundP\x01Z.github.com/xtls/xray-core/proxy/vmess/outbound\xaa\x02\x19Xray.Proxy.Vmess.Outboundb\x06proto3')
19 |
20 |
21 |
22 | _CONFIG = DESCRIPTOR.message_types_by_name['Config']
23 | Config = _reflection.GeneratedProtocolMessageType('Config', (_message.Message,), {
24 | 'DESCRIPTOR' : _CONFIG,
25 | '__module__' : 'proxy.vmess.outbound.config_pb2'
26 | # @@protoc_insertion_point(class_scope:xray.proxy.vmess.outbound.Config)
27 | })
28 | _sym_db.RegisterMessage(Config)
29 |
30 | if _descriptor._USE_C_DESCRIPTORS == False:
31 |
32 | DESCRIPTOR._options = None
33 | DESCRIPTOR._serialized_options = b'\n\035com.xray.proxy.vmess.outboundP\001Z.github.com/xtls/xray-core/proxy/vmess/outbound\252\002\031Xray.Proxy.Vmess.Outbound'
34 | _CONFIG._serialized_start=99
35 | _CONFIG._serialized_end=163
36 | # @@protoc_insertion_point(module_scope)
37 |
--------------------------------------------------------------------------------
/app/dashboard/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "v2ray-dashboard",
3 | "private": true,
4 | "version": "0.0.0",
5 | "scripts": {
6 | "dev": "vite --port 3000",
7 | "build": "tsc && vite build",
8 | "preview": "vite preview --port 3000",
9 | "gen:theme-typings": "chakra-cli tokens ./chakra.config.ts",
10 | "postinstall": "npm run gen:theme-typings"
11 | },
12 | "dependencies": {
13 | "@chakra-ui/react": "^2.4.5",
14 | "@emotion/react": "^11.10.5",
15 | "@emotion/styled": "^11.10.5",
16 | "@heroicons/react": "^2.0.12",
17 | "@hookform/resolvers": "^2.9.10",
18 | "classnames": "^2.3.2",
19 | "dayjs": "^1.11.6",
20 | "framer-motion": "^7.6.6",
21 | "lodash.debounce": "^4.0.8",
22 | "ohmyfetch": "^0.4.21",
23 | "qrcode.react": "^3.1.0",
24 | "react": "^18.2.0",
25 | "react-copy-to-clipboard": "^5.1.0",
26 | "react-datepicker": "^4.8.0",
27 | "react-dom": "^18.2.0",
28 | "react-github-btn": "^1.4.0",
29 | "react-hook-form": "^7.39.1",
30 | "react-loading-skeleton": "^3.1.0",
31 | "react-router-dom": "^6.4.3",
32 | "react-slick": "^0.29.0",
33 | "rollup-plugin-visualizer": "^5.9.0",
34 | "slick-carousel": "^1.8.1",
35 | "swr": "^1.3.0",
36 | "vite-tsconfig-paths": "^4.0.3",
37 | "zod": "^3.19.1",
38 | "zustand-computed": "^1.3.3"
39 | },
40 | "devDependencies": {
41 | "@chakra-ui/cli": "^2.3.0",
42 | "@types/lodash.debounce": "^4.0.7",
43 | "@types/react": "^18.0.17",
44 | "@types/react-copy-to-clipboard": "^5.0.4",
45 | "@types/react-datepicker": "^4.8.0",
46 | "@types/react-dom": "^18.0.6",
47 | "@types/react-slick": "^0.23.10",
48 | "@vitejs/plugin-react": "^2.1.0",
49 | "autoprefixer": "^10.4.12",
50 | "sass": "^1.57.1",
51 | "typescript": "^4.6.4",
52 | "vite": "^3.1.0",
53 | "vite-plugin-svgr": "^2.2.2"
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/app/db/migrations/versions/fad8b1997c3a_case_insensitive_username.py:
--------------------------------------------------------------------------------
1 | """case insensitive username
2 |
3 | Revision ID: fad8b1997c3a
4 | Revises: 5b84d88804a1
5 | Create Date: 2023-03-17 22:46:32.833004
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'fad8b1997c3a'
14 | down_revision = '5b84d88804a1'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade() -> None:
20 | bind = op.get_bind()
21 |
22 | if bind.engine.name == 'mysql':
23 | pass # By default, MySQL is case-insensitive.
24 |
25 | if bind.engine.name == 'sqlite':
26 | op.drop_index('ix_users_username', table_name='users')
27 | while True:
28 | q = bind.execute(
29 | 'SELECT username, COUNT(*) FROM users GROUP BY username COLLATE NOCASE HAVING COUNT(*) > 1;'
30 | )
31 | if not (r := q.fetchall()):
32 | break
33 | for username, c in r:
34 | bind.execute(f"UPDATE users SET username = '{username}_{c}' WHERE username = '{username}';")
35 |
36 | with op.batch_alter_table('users') as batch_op:
37 | batch_op.alter_column('username', type_=sa.String(length=34, collation='NOCASE'))
38 | op.create_index(op.f('ix_users_username'), 'users', ['username'], unique=True)
39 |
40 |
41 | def downgrade() -> None:
42 | bind = op.get_bind()
43 |
44 | if bind.engine.name == 'mysql':
45 | pass # By default, MySQL is case-insensitive.
46 |
47 | if bind.engine.name == 'sqlite':
48 | with op.batch_alter_table('users') as batch_op:
49 | batch_op.alter_column('username', type_=sa.String(length=34))
50 | op.drop_index('ix_users_username', table_name='users')
51 | op.create_index(op.f('ix_users_username'), 'users', ['username'], unique=True)
52 |
--------------------------------------------------------------------------------
/xray_api/proto/transport/internet/domainsocket/config_pb2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by the protocol buffer compiler. DO NOT EDIT!
3 | # source: transport/internet/domainsocket/config.proto
4 | """Generated protocol buffer code."""
5 | from google.protobuf import descriptor as _descriptor
6 | from google.protobuf import descriptor_pool as _descriptor_pool
7 | from google.protobuf import message as _message
8 | from google.protobuf import reflection as _reflection
9 | from google.protobuf import symbol_database as _symbol_database
10 | # @@protoc_insertion_point(imports)
11 |
12 | _sym_db = _symbol_database.Default()
13 |
14 |
15 |
16 |
17 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n,transport/internet/domainsocket/config.proto\x12$xray.transport.internet.domainsocket\"9\n\x06\x43onfig\x12\x0c\n\x04path\x18\x01 \x01(\t\x12\x10\n\x08\x61\x62stract\x18\x02 \x01(\x08\x12\x0f\n\x07padding\x18\x03 \x01(\x08\x42\x8e\x01\n(com.xray.transport.internet.domainsocketP\x01Z9github.com/xtls/xray-core/transport/internet/domainsocket\xaa\x02$Xray.Transport.Internet.DomainSocketb\x06proto3')
18 |
19 |
20 |
21 | _CONFIG = DESCRIPTOR.message_types_by_name['Config']
22 | Config = _reflection.GeneratedProtocolMessageType('Config', (_message.Message,), {
23 | 'DESCRIPTOR' : _CONFIG,
24 | '__module__' : 'transport.internet.domainsocket.config_pb2'
25 | # @@protoc_insertion_point(class_scope:xray.transport.internet.domainsocket.Config)
26 | })
27 | _sym_db.RegisterMessage(Config)
28 |
29 | if _descriptor._USE_C_DESCRIPTORS == False:
30 |
31 | DESCRIPTOR._options = None
32 | DESCRIPTOR._serialized_options = b'\n(com.xray.transport.internet.domainsocketP\001Z9github.com/xtls/xray-core/transport/internet/domainsocket\252\002$Xray.Transport.Internet.DomainSocket'
33 | _CONFIG._serialized_start=86
34 | _CONFIG._serialized_end=143
35 | # @@protoc_insertion_point(module_scope)
36 |
--------------------------------------------------------------------------------
/xray_api/proto/proxy/vmess/account_pb2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by the protocol buffer compiler. DO NOT EDIT!
3 | # source: proxy/vmess/account.proto
4 | """Generated protocol buffer code."""
5 | from google.protobuf import descriptor as _descriptor
6 | from google.protobuf import descriptor_pool as _descriptor_pool
7 | from google.protobuf import message as _message
8 | from google.protobuf import reflection as _reflection
9 | from google.protobuf import symbol_database as _symbol_database
10 | # @@protoc_insertion_point(imports)
11 |
12 | _sym_db = _symbol_database.Default()
13 |
14 |
15 | from xray_api.proto.common.protocol import headers_pb2 as common_dot_protocol_dot_headers__pb2
16 |
17 |
18 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x19proxy/vmess/account.proto\x12\x10xray.proxy.vmess\x1a\x1d\x63ommon/protocol/headers.proto\"\x7f\n\x07\x41\x63\x63ount\x12\n\n\x02id\x18\x01 \x01(\t\x12\x10\n\x08\x61lter_id\x18\x02 \x01(\r\x12?\n\x11security_settings\x18\x03 \x01(\x0b\x32$.xray.common.protocol.SecurityConfig\x12\x15\n\rtests_enabled\x18\x04 \x01(\tBR\n\x14\x63om.xray.proxy.vmessP\x01Z%github.com/xtls/xray-core/proxy/vmess\xaa\x02\x10Xray.Proxy.Vmessb\x06proto3')
19 |
20 |
21 |
22 | _ACCOUNT = DESCRIPTOR.message_types_by_name['Account']
23 | Account = _reflection.GeneratedProtocolMessageType('Account', (_message.Message,), {
24 | 'DESCRIPTOR' : _ACCOUNT,
25 | '__module__' : 'proxy.vmess.account_pb2'
26 | # @@protoc_insertion_point(class_scope:xray.proxy.vmess.Account)
27 | })
28 | _sym_db.RegisterMessage(Account)
29 |
30 | if _descriptor._USE_C_DESCRIPTORS == False:
31 |
32 | DESCRIPTOR._options = None
33 | DESCRIPTOR._serialized_options = b'\n\024com.xray.proxy.vmessP\001Z%github.com/xtls/xray-core/proxy/vmess\252\002\020Xray.Proxy.Vmess'
34 | _ACCOUNT._serialized_start=78
35 | _ACCOUNT._serialized_end=205
36 | # @@protoc_insertion_point(module_scope)
37 |
--------------------------------------------------------------------------------
/app/jobs/review_users.py:
--------------------------------------------------------------------------------
1 | import itertools
2 | from datetime import datetime
3 |
4 | from app import logger, scheduler, telegram, xray
5 | from app.db import GetDB, get_users, update_user_status
6 | from app.models.user import UserStatus
7 | from app.utils.xray import xray_config_include_db_clients
8 |
9 |
10 | def review():
11 | now = datetime.utcnow().timestamp()
12 | with GetDB() as db:
13 | for user in get_users(db, status=UserStatus.active):
14 |
15 | limited = user.data_limit and user.used_traffic >= user.data_limit
16 | expired = user.expire and user.expire <= now
17 | if limited:
18 | status = UserStatus.limited
19 | elif expired:
20 | status = UserStatus.expired
21 | else:
22 | continue
23 |
24 | inbound_tags = itertools.chain.from_iterable(user.inbounds.values())
25 | for inbound_tag in inbound_tags:
26 | try:
27 | xray.api.remove_inbound_user(tag=inbound_tag, email=user.username)
28 | except xray.exc.EmailNotFoundError:
29 | pass
30 |
31 | except xray.exceptions.ConnectionError:
32 | try:
33 | xray.core.restart(
34 | xray_config_include_db_clients(xray.config)
35 | )
36 | except ProcessLookupError:
37 | pass
38 |
39 | return # stop reviewing temporarily
40 |
41 | update_user_status(db, user, status)
42 |
43 | try:
44 | telegram.report_status_change(user.username, status)
45 | except Exception:
46 | pass
47 |
48 | logger.info(f"User \"{user.username}\" status changed to {status}")
49 |
50 |
51 | scheduler.add_job(review, 'interval', seconds=5)
52 |
--------------------------------------------------------------------------------
/xray_api/proto/transport/internet/headers/wireguard/config_pb2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by the protocol buffer compiler. DO NOT EDIT!
3 | # source: transport/internet/headers/wireguard/config.proto
4 | """Generated protocol buffer code."""
5 | from google.protobuf import descriptor as _descriptor
6 | from google.protobuf import descriptor_pool as _descriptor_pool
7 | from google.protobuf import message as _message
8 | from google.protobuf import reflection as _reflection
9 | from google.protobuf import symbol_database as _symbol_database
10 | # @@protoc_insertion_point(imports)
11 |
12 | _sym_db = _symbol_database.Default()
13 |
14 |
15 |
16 |
17 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n1transport/internet/headers/wireguard/config.proto\x12)xray.transport.internet.headers.wireguard\"\x11\n\x0fWireguardConfigB\x9d\x01\n-com.xray.transport.internet.headers.wireguardP\x01Z>github.com/xtls/xray-core/transport/internet/headers/wireguard\xaa\x02)Xray.Transport.Internet.Headers.Wireguardb\x06proto3')
18 |
19 |
20 |
21 | _WIREGUARDCONFIG = DESCRIPTOR.message_types_by_name['WireguardConfig']
22 | WireguardConfig = _reflection.GeneratedProtocolMessageType('WireguardConfig', (_message.Message,), {
23 | 'DESCRIPTOR' : _WIREGUARDCONFIG,
24 | '__module__' : 'transport.internet.headers.wireguard.config_pb2'
25 | # @@protoc_insertion_point(class_scope:xray.transport.internet.headers.wireguard.WireguardConfig)
26 | })
27 | _sym_db.RegisterMessage(WireguardConfig)
28 |
29 | if _descriptor._USE_C_DESCRIPTORS == False:
30 |
31 | DESCRIPTOR._options = None
32 | DESCRIPTOR._serialized_options = b'\n-com.xray.transport.internet.headers.wireguardP\001Z>github.com/xtls/xray-core/transport/internet/headers/wireguard\252\002)Xray.Transport.Internet.Headers.Wireguard'
33 | _WIREGUARDCONFIG._serialized_start=96
34 | _WIREGUARDCONFIG._serialized_end=113
35 | # @@protoc_insertion_point(module_scope)
36 |
--------------------------------------------------------------------------------
/xray_api/proto/transport/global/config_pb2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by the protocol buffer compiler. DO NOT EDIT!
3 | # source: transport/global/config.proto
4 | """Generated protocol buffer code."""
5 | from google.protobuf import descriptor as _descriptor
6 | from google.protobuf import descriptor_pool as _descriptor_pool
7 | from google.protobuf import message as _message
8 | from google.protobuf import reflection as _reflection
9 | from google.protobuf import symbol_database as _symbol_database
10 | # @@protoc_insertion_point(imports)
11 |
12 | _sym_db = _symbol_database.Default()
13 |
14 |
15 | from xray_api.proto.transport.internet import config_pb2 as transport_dot_internet_dot_config__pb2
16 |
17 |
18 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1dtransport/global/config.proto\x12\x0exray.transport\x1a\x1ftransport/internet/config.proto\"R\n\x06\x43onfig\x12\x44\n\x12transport_settings\x18\x01 \x03(\x0b\x32(.xray.transport.internet.TransportConfig:\x02\x18\x01\x42\x61\n\x19\x63om.xray.transport.globalP\x01Z*github.com/xtls/xray-core/transport/global\xaa\x02\x15Xray.Transport.Globalb\x06proto3')
19 |
20 |
21 |
22 | _CONFIG = DESCRIPTOR.message_types_by_name['Config']
23 | Config = _reflection.GeneratedProtocolMessageType('Config', (_message.Message,), {
24 | 'DESCRIPTOR' : _CONFIG,
25 | '__module__' : 'transport.global.config_pb2'
26 | # @@protoc_insertion_point(class_scope:xray.transport.Config)
27 | })
28 | _sym_db.RegisterMessage(Config)
29 |
30 | if _descriptor._USE_C_DESCRIPTORS == False:
31 |
32 | DESCRIPTOR._options = None
33 | DESCRIPTOR._serialized_options = b'\n\031com.xray.transport.globalP\001Z*github.com/xtls/xray-core/transport/global\252\002\025Xray.Transport.Global'
34 | _CONFIG._options = None
35 | _CONFIG._serialized_options = b'\030\001'
36 | _CONFIG._serialized_start=82
37 | _CONFIG._serialized_end=164
38 | # @@protoc_insertion_point(module_scope)
39 |
--------------------------------------------------------------------------------
/xray_api/proto/transport/internet/tcp/config_pb2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by the protocol buffer compiler. DO NOT EDIT!
3 | # source: transport/internet/tcp/config.proto
4 | """Generated protocol buffer code."""
5 | from google.protobuf import descriptor as _descriptor
6 | from google.protobuf import descriptor_pool as _descriptor_pool
7 | from google.protobuf import message as _message
8 | from google.protobuf import reflection as _reflection
9 | from google.protobuf import symbol_database as _symbol_database
10 | # @@protoc_insertion_point(imports)
11 |
12 | _sym_db = _symbol_database.Default()
13 |
14 |
15 | from xray_api.proto.common.serial import typed_message_pb2 as common_dot_serial_dot_typed__message__pb2
16 |
17 |
18 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n#transport/internet/tcp/config.proto\x12\x1bxray.transport.internet.tcp\x1a!common/serial/typed_message.proto\"h\n\x06\x43onfig\x12\x39\n\x0fheader_settings\x18\x02 \x01(\x0b\x32 .xray.common.serial.TypedMessage\x12\x1d\n\x15\x61\x63\x63\x65pt_proxy_protocol\x18\x03 \x01(\x08J\x04\x08\x01\x10\x02\x42s\n\x1f\x63om.xray.transport.internet.tcpP\x01Z0github.com/xtls/xray-core/transport/internet/tcp\xaa\x02\x1bXray.Transport.Internet.Tcpb\x06proto3')
19 |
20 |
21 |
22 | _CONFIG = DESCRIPTOR.message_types_by_name['Config']
23 | Config = _reflection.GeneratedProtocolMessageType('Config', (_message.Message,), {
24 | 'DESCRIPTOR' : _CONFIG,
25 | '__module__' : 'transport.internet.tcp.config_pb2'
26 | # @@protoc_insertion_point(class_scope:xray.transport.internet.tcp.Config)
27 | })
28 | _sym_db.RegisterMessage(Config)
29 |
30 | if _descriptor._USE_C_DESCRIPTORS == False:
31 |
32 | DESCRIPTOR._options = None
33 | DESCRIPTOR._serialized_options = b'\n\037com.xray.transport.internet.tcpP\001Z0github.com/xtls/xray-core/transport/internet/tcp\252\002\033Xray.Transport.Internet.Tcp'
34 | _CONFIG._serialized_start=103
35 | _CONFIG._serialized_end=207
36 | # @@protoc_insertion_point(module_scope)
37 |
--------------------------------------------------------------------------------
/xray_api/proto/transport/internet/headers/srtp/config_pb2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by the protocol buffer compiler. DO NOT EDIT!
3 | # source: transport/internet/headers/srtp/config.proto
4 | """Generated protocol buffer code."""
5 | from google.protobuf import descriptor as _descriptor
6 | from google.protobuf import descriptor_pool as _descriptor_pool
7 | from google.protobuf import message as _message
8 | from google.protobuf import reflection as _reflection
9 | from google.protobuf import symbol_database as _symbol_database
10 | # @@protoc_insertion_point(imports)
11 |
12 | _sym_db = _symbol_database.Default()
13 |
14 |
15 |
16 |
17 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n,transport/internet/headers/srtp/config.proto\x12$xray.transport.internet.headers.srtp\"w\n\x06\x43onfig\x12\x0f\n\x07version\x18\x01 \x01(\r\x12\x0f\n\x07padding\x18\x02 \x01(\x08\x12\x11\n\textension\x18\x03 \x01(\x08\x12\x12\n\ncsrc_count\x18\x04 \x01(\r\x12\x0e\n\x06marker\x18\x05 \x01(\x08\x12\x14\n\x0cpayload_type\x18\x06 \x01(\rB\x8e\x01\n(com.xray.transport.internet.headers.srtpP\x01Z9github.com/xtls/xray-core/transport/internet/headers/srtp\xaa\x02$Xray.Transport.Internet.Headers.Srtpb\x06proto3')
18 |
19 |
20 |
21 | _CONFIG = DESCRIPTOR.message_types_by_name['Config']
22 | Config = _reflection.GeneratedProtocolMessageType('Config', (_message.Message,), {
23 | 'DESCRIPTOR' : _CONFIG,
24 | '__module__' : 'transport.internet.headers.srtp.config_pb2'
25 | # @@protoc_insertion_point(class_scope:xray.transport.internet.headers.srtp.Config)
26 | })
27 | _sym_db.RegisterMessage(Config)
28 |
29 | if _descriptor._USE_C_DESCRIPTORS == False:
30 |
31 | DESCRIPTOR._options = None
32 | DESCRIPTOR._serialized_options = b'\n(com.xray.transport.internet.headers.srtpP\001Z9github.com/xtls/xray-core/transport/internet/headers/srtp\252\002$Xray.Transport.Internet.Headers.Srtp'
33 | _CONFIG._serialized_start=86
34 | _CONFIG._serialized_end=205
35 | # @@protoc_insertion_point(module_scope)
36 |
--------------------------------------------------------------------------------
/xray_api/proto/common/net/destination_pb2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by the protocol buffer compiler. DO NOT EDIT!
3 | # source: common/net/destination.proto
4 | """Generated protocol buffer code."""
5 | from google.protobuf import descriptor as _descriptor
6 | from google.protobuf import descriptor_pool as _descriptor_pool
7 | from google.protobuf import message as _message
8 | from google.protobuf import reflection as _reflection
9 | from google.protobuf import symbol_database as _symbol_database
10 | # @@protoc_insertion_point(imports)
11 |
12 | _sym_db = _symbol_database.Default()
13 |
14 |
15 | from xray_api.proto.common.net import network_pb2 as common_dot_net_dot_network__pb2
16 | from xray_api.proto.common.net import address_pb2 as common_dot_net_dot_address__pb2
17 |
18 |
19 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1c\x63ommon/net/destination.proto\x12\x0fxray.common.net\x1a\x18\x63ommon/net/network.proto\x1a\x18\x63ommon/net/address.proto\"q\n\x08\x45ndpoint\x12)\n\x07network\x18\x01 \x01(\x0e\x32\x18.xray.common.net.Network\x12,\n\x07\x61\x64\x64ress\x18\x02 \x01(\x0b\x32\x1b.xray.common.net.IPOrDomain\x12\x0c\n\x04port\x18\x03 \x01(\rBO\n\x13\x63om.xray.common.netP\x01Z$github.com/xtls/xray-core/common/net\xaa\x02\x0fXray.Common.Netb\x06proto3')
20 |
21 |
22 |
23 | _ENDPOINT = DESCRIPTOR.message_types_by_name['Endpoint']
24 | Endpoint = _reflection.GeneratedProtocolMessageType('Endpoint', (_message.Message,), {
25 | 'DESCRIPTOR' : _ENDPOINT,
26 | '__module__' : 'common.net.destination_pb2'
27 | # @@protoc_insertion_point(class_scope:xray.common.net.Endpoint)
28 | })
29 | _sym_db.RegisterMessage(Endpoint)
30 |
31 | if _descriptor._USE_C_DESCRIPTORS == False:
32 |
33 | DESCRIPTOR._options = None
34 | DESCRIPTOR._serialized_options = b'\n\023com.xray.common.netP\001Z$github.com/xtls/xray-core/common/net\252\002\017Xray.Common.Net'
35 | _ENDPOINT._serialized_start=101
36 | _ENDPOINT._serialized_end=214
37 | # @@protoc_insertion_point(module_scope)
38 |
--------------------------------------------------------------------------------
/config.py:
--------------------------------------------------------------------------------
1 | import requests
2 | from decouple import config
3 | from dotenv import load_dotenv
4 |
5 | load_dotenv()
6 |
7 |
8 | # Disable IPv6
9 | requests.packages.urllib3.util.connection.HAS_IPV6 = False
10 |
11 | try:
12 | SERVER_IP = requests.get("https://api.ipify.org", timeout=5).text.strip()
13 | except requests.exceptions.RequestException:
14 | print("Failed to get SERVER_IP, using 127.0.0.1 instead")
15 | SERVER_IP = "127.0.0.1"
16 |
17 |
18 | SQLALCHEMY_DATABASE_URL = config("SQLALCHEMY_DATABASE_URL", default="sqlite:///db.sqlite3")
19 |
20 |
21 | UVICORN_HOST = config("UVICORN_HOST", default="0.0.0.0")
22 | UVICORN_PORT = config("UVICORN_PORT", cast=int, default=8000)
23 | UVICORN_UDS = config("UVICORN_UDS", default=None)
24 | UVICORN_SSL_CERTFILE = config("UVICORN_SSL_CERTFILE", default=None)
25 | UVICORN_SSL_KEYFILE = config("UVICORN_SSL_KEYFILE", default=None)
26 |
27 |
28 | DEBUG = config("DEBUG", default=False, cast=bool)
29 | DOCS = config("DOCS", default=False, cast=bool)
30 |
31 |
32 | XRAY_JSON = config("XRAY_JSON", default="./xray.json")
33 | XRAY_FALLBACKS_INBOUND_TAG = config("XRAY_FALLBACKS_INBOUND_TAG", cast=str, default="") \
34 | or config("XRAY_FALLBACK_INBOUND_TAG", cast=str, default="")
35 | XRAY_EXECUTABLE_PATH = config("XRAY_EXECUTABLE_PATH", default="/usr/local/bin/xray")
36 | XRAY_ASSETS_PATH = config("XRAY_ASSETS_PATH", default="/usr/local/share/xray")
37 | XRAY_EXCLUDE_INBOUND_TAGS = config("XRAY_EXCLUDE_INBOUND_TAGS", default='').split()
38 | XRAY_SUBSCRIPTION_URL_PREFIX = config("XRAY_SUBSCRIPTION_URL_PREFIX", default="").strip("/")
39 |
40 |
41 | TELEGRAM_API_TOKEN = config("TELEGRAM_API_TOKEN", default=None)
42 | TELEGRAM_ADMIN_ID = config("TELEGRAM_ADMIN_ID", cast=int, default=0)
43 | TELEGRAM_PROXY_URL = config("TELEGRAM_PROXY_URL", default=None)
44 |
45 | JWT_ACCESS_TOKEN_EXPIRE_MINUTES = config("JWT_ACCESS_TOKEN_EXPIRE_MINUTES", cast=int, default=1440)
46 |
47 |
48 | # USERNAME: PASSWORD
49 | SUDOERS = {
50 | config("SUDO_USERNAME", default="admin"): config("SUDO_PASSWORD", default="admin")
51 | }
52 |
--------------------------------------------------------------------------------
/xray_api/types/account.py:
--------------------------------------------------------------------------------
1 | from abc import ABC, abstractproperty
2 | from enum import Enum
3 | from uuid import UUID
4 |
5 | from pydantic import BaseModel
6 |
7 | from ..proto.common.serial.typed_message_pb2 import TypedMessage
8 | from ..proto.proxy.shadowsocks.config_pb2 import \
9 | Account as ShadowsocksAccountPb2
10 | from ..proto.proxy.shadowsocks.config_pb2 import \
11 | CipherType as ShadowsocksCiphers
12 | from ..proto.proxy.trojan.config_pb2 import Account as TrojanAccountPb2
13 | from ..proto.proxy.vless.account_pb2 import Account as VLESSAccountPb2
14 | from ..proto.proxy.vmess.account_pb2 import Account as VMessAccountPb2
15 | from .message import Message
16 |
17 |
18 | class Account(BaseModel, ABC):
19 | email: str
20 | level: int = 0
21 |
22 | @abstractproperty
23 | def message(self) -> TypedMessage:
24 | pass
25 |
26 | def __repr__(self) -> str:
27 | return f"<{self.__class__.__name__} {self.email}>"
28 |
29 |
30 | class VMessAccount(Account):
31 | id: UUID
32 |
33 | @property
34 | def message(self):
35 | return Message(VMessAccountPb2(id=str(self.id), alter_id=0))
36 |
37 |
38 | class VLESSAccount(Account):
39 | id: UUID
40 | flow: str = ""
41 |
42 | @property
43 | def message(self):
44 | return Message(VLESSAccountPb2(id=str(self.id), flow=self.flow))
45 |
46 |
47 | class TrojanAccount(Account):
48 | password: str
49 |
50 | @property
51 | def message(self):
52 | return Message(TrojanAccountPb2(password=self.password))
53 |
54 |
55 | class ShadowsocksMethods(Enum):
56 | AES_128_GCM = 'aes-128-gcm'
57 | AES_256_GCM = 'aes-256-gcm'
58 | CHACHA20_POLY1305 = 'chacha20-poly1305'
59 |
60 |
61 | class ShadowsocksAccount(Account):
62 | password: str
63 | method: ShadowsocksMethods = ShadowsocksMethods.CHACHA20_POLY1305
64 |
65 | @property
66 | def cipher_type(self):
67 | return self.method.name
68 |
69 | @property
70 | def message(self):
71 | return Message(ShadowsocksAccountPb2(password=self.password, cipher_type=self.cipher_type))
72 |
--------------------------------------------------------------------------------
/xray_api/proto/common/protocol/server_spec_pb2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by the protocol buffer compiler. DO NOT EDIT!
3 | # source: common/protocol/server_spec.proto
4 | """Generated protocol buffer code."""
5 | from google.protobuf import descriptor as _descriptor
6 | from google.protobuf import descriptor_pool as _descriptor_pool
7 | from google.protobuf import message as _message
8 | from google.protobuf import reflection as _reflection
9 | from google.protobuf import symbol_database as _symbol_database
10 | # @@protoc_insertion_point(imports)
11 |
12 | _sym_db = _symbol_database.Default()
13 |
14 |
15 | from xray_api.proto.common.net import address_pb2 as common_dot_net_dot_address__pb2
16 | from xray_api.proto.common.protocol import user_pb2 as common_dot_protocol_dot_user__pb2
17 |
18 |
19 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n!common/protocol/server_spec.proto\x12\x14xray.common.protocol\x1a\x18\x63ommon/net/address.proto\x1a\x1a\x63ommon/protocol/user.proto\"v\n\x0eServerEndpoint\x12,\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0b\x32\x1b.xray.common.net.IPOrDomain\x12\x0c\n\x04port\x18\x02 \x01(\r\x12(\n\x04user\x18\x03 \x03(\x0b\x32\x1a.xray.common.protocol.UserB^\n\x18\x63om.xray.common.protocolP\x01Z)github.com/xtls/xray-core/common/protocol\xaa\x02\x14Xray.Common.Protocolb\x06proto3')
20 |
21 |
22 |
23 | _SERVERENDPOINT = DESCRIPTOR.message_types_by_name['ServerEndpoint']
24 | ServerEndpoint = _reflection.GeneratedProtocolMessageType('ServerEndpoint', (_message.Message,), {
25 | 'DESCRIPTOR' : _SERVERENDPOINT,
26 | '__module__' : 'common.protocol.server_spec_pb2'
27 | # @@protoc_insertion_point(class_scope:xray.common.protocol.ServerEndpoint)
28 | })
29 | _sym_db.RegisterMessage(ServerEndpoint)
30 |
31 | if _descriptor._USE_C_DESCRIPTORS == False:
32 |
33 | DESCRIPTOR._options = None
34 | DESCRIPTOR._serialized_options = b'\n\030com.xray.common.protocolP\001Z)github.com/xtls/xray-core/common/protocol\252\002\024Xray.Common.Protocol'
35 | _SERVERENDPOINT._serialized_start=113
36 | _SERVERENDPOINT._serialized_end=231
37 | # @@protoc_insertion_point(module_scope)
38 |
--------------------------------------------------------------------------------
/app/models/admin.py:
--------------------------------------------------------------------------------
1 | from app.db import Session, crud, get_db
2 | from app.utils.jwt import get_admin_payload
3 | from config import SUDOERS
4 | from fastapi import Depends, HTTPException, status
5 | from fastapi.security import OAuth2PasswordBearer
6 | from passlib.context import CryptContext
7 | from pydantic import BaseModel
8 |
9 | pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
10 | oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/admin/token") # Admin view url
11 |
12 |
13 | class Token(BaseModel):
14 | access_token: str
15 | token_type: str = "bearer"
16 |
17 |
18 | class Admin(BaseModel):
19 | username: str
20 | is_sudo: bool = False
21 |
22 | class Config:
23 | orm_mode = True
24 |
25 | @classmethod
26 | def get_current(cls,
27 | db: Session = Depends(get_db),
28 | token: str = Depends(oauth2_scheme)):
29 | exc = HTTPException(
30 | status_code=status.HTTP_401_UNAUTHORIZED,
31 | detail="Could not validate credentials",
32 | headers={"WWW-Authenticate": "Bearer"},
33 | )
34 |
35 | payload = get_admin_payload(token)
36 | if not payload:
37 | raise exc
38 |
39 | if payload['username'] in SUDOERS and payload['is_sudo'] is True:
40 | return cls(username=payload['username'], is_sudo=True)
41 |
42 | dbadmin = crud.get_admin(db, payload['username'])
43 | if not dbadmin:
44 | raise exc
45 |
46 | return cls.from_orm(dbadmin)
47 |
48 |
49 | class AdminCreate(Admin):
50 | password: str
51 |
52 | @property
53 | def hashed_password(self):
54 | return pwd_context.hash(self.password)
55 |
56 |
57 | class AdminModify(BaseModel):
58 | password: str
59 |
60 | @property
61 | def hashed_password(self):
62 | return pwd_context.hash(self.password)
63 |
64 |
65 | class AdminInDB(Admin):
66 | username: str
67 | hashed_password: str
68 |
69 | def verify_password(self, plain_password):
70 | return pwd_context.verify(plain_password, self.hashed_password)
71 |
--------------------------------------------------------------------------------
/xray_api/proto/common/net/port_pb2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by the protocol buffer compiler. DO NOT EDIT!
3 | # source: common/net/port.proto
4 | """Generated protocol buffer code."""
5 | from google.protobuf import descriptor as _descriptor
6 | from google.protobuf import descriptor_pool as _descriptor_pool
7 | from google.protobuf import message as _message
8 | from google.protobuf import reflection as _reflection
9 | from google.protobuf import symbol_database as _symbol_database
10 | # @@protoc_insertion_point(imports)
11 |
12 | _sym_db = _symbol_database.Default()
13 |
14 |
15 |
16 |
17 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15\x63ommon/net/port.proto\x12\x0fxray.common.net\"%\n\tPortRange\x12\x0c\n\x04\x46rom\x18\x01 \x01(\r\x12\n\n\x02To\x18\x02 \x01(\r\"5\n\x08PortList\x12)\n\x05range\x18\x01 \x03(\x0b\x32\x1a.xray.common.net.PortRangeBO\n\x13\x63om.xray.common.netP\x01Z$github.com/xtls/xray-core/common/net\xaa\x02\x0fXray.Common.Netb\x06proto3')
18 |
19 |
20 |
21 | _PORTRANGE = DESCRIPTOR.message_types_by_name['PortRange']
22 | _PORTLIST = DESCRIPTOR.message_types_by_name['PortList']
23 | PortRange = _reflection.GeneratedProtocolMessageType('PortRange', (_message.Message,), {
24 | 'DESCRIPTOR' : _PORTRANGE,
25 | '__module__' : 'common.net.port_pb2'
26 | # @@protoc_insertion_point(class_scope:xray.common.net.PortRange)
27 | })
28 | _sym_db.RegisterMessage(PortRange)
29 |
30 | PortList = _reflection.GeneratedProtocolMessageType('PortList', (_message.Message,), {
31 | 'DESCRIPTOR' : _PORTLIST,
32 | '__module__' : 'common.net.port_pb2'
33 | # @@protoc_insertion_point(class_scope:xray.common.net.PortList)
34 | })
35 | _sym_db.RegisterMessage(PortList)
36 |
37 | if _descriptor._USE_C_DESCRIPTORS == False:
38 |
39 | DESCRIPTOR._options = None
40 | DESCRIPTOR._serialized_options = b'\n\023com.xray.common.netP\001Z$github.com/xtls/xray-core/common/net\252\002\017Xray.Common.Net'
41 | _PORTRANGE._serialized_start=42
42 | _PORTRANGE._serialized_end=79
43 | _PORTLIST._serialized_start=81
44 | _PORTLIST._serialized_end=134
45 | # @@protoc_insertion_point(module_scope)
46 |
--------------------------------------------------------------------------------
/app/utils/jwt.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime, timedelta
2 | from typing import Union
3 |
4 | import sqlalchemy
5 | from app import app
6 | from config import JWT_ACCESS_TOKEN_EXPIRE_MINUTES
7 | from jose import JWTError, jwt
8 |
9 | global JWT_SECRET_KEY
10 |
11 |
12 | @app.on_event("startup")
13 | def set_jwt_secret_key():
14 | from app.db import JWT, engine, GetDB, get_jwt_secret_key
15 | if sqlalchemy.inspect(engine).has_table(JWT.__tablename__):
16 | with GetDB() as db:
17 | global JWT_SECRET_KEY
18 | JWT_SECRET_KEY = get_jwt_secret_key(db)
19 |
20 |
21 | def create_admin_token(username: str, is_sudo=False) -> str:
22 | data = {"sub": username, "access": "sudo" if is_sudo else "admin"}
23 | if JWT_ACCESS_TOKEN_EXPIRE_MINUTES > 0:
24 | expire = datetime.utcnow() + timedelta(minutes=JWT_ACCESS_TOKEN_EXPIRE_MINUTES)
25 | data["exp"] = expire
26 | encoded_jwt = jwt.encode(data, JWT_SECRET_KEY, algorithm="HS256")
27 | return encoded_jwt
28 |
29 |
30 | def get_admin_payload(token: str) -> Union[dict, None]:
31 | try:
32 | payload = jwt.decode(token, JWT_SECRET_KEY, algorithms=["HS256"])
33 | username: str = payload.get("sub")
34 | access: str = payload.get("access")
35 | if not username or access not in ('admin', 'sudo'):
36 | return
37 |
38 | return {"username": username, "is_sudo": access == "sudo"}
39 | except JWTError:
40 | return
41 |
42 |
43 | def create_subscription_token(username: str) -> str:
44 | data = {"sub": username, "access": "subscription", "iat": datetime.utcnow()+timedelta(seconds=1)}
45 | encoded_jwt = jwt.encode(data, JWT_SECRET_KEY, algorithm="HS256")
46 | return encoded_jwt
47 |
48 |
49 | def get_subscription_payload(token: str) -> Union[dict, None]:
50 | try:
51 | payload = jwt.decode(token, JWT_SECRET_KEY, algorithms=["HS256"])
52 | if payload.get("access") != "subscription":
53 | return
54 |
55 | return {"username": payload['sub'], "created_at": datetime.utcfromtimestamp(payload['iat'])}
56 | except JWTError:
57 | return
58 |
--------------------------------------------------------------------------------
/xray_api/proto/transport/internet/http/config_pb2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by the protocol buffer compiler. DO NOT EDIT!
3 | # source: transport/internet/http/config.proto
4 | """Generated protocol buffer code."""
5 | from google.protobuf import descriptor as _descriptor
6 | from google.protobuf import descriptor_pool as _descriptor_pool
7 | from google.protobuf import message as _message
8 | from google.protobuf import reflection as _reflection
9 | from google.protobuf import symbol_database as _symbol_database
10 | # @@protoc_insertion_point(imports)
11 |
12 | _sym_db = _symbol_database.Default()
13 |
14 |
15 | from xray_api.proto.transport.internet.headers.http import config_pb2 as transport_dot_internet_dot_headers_dot_http_dot_config__pb2
16 |
17 |
18 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n$transport/internet/http/config.proto\x12\x1cxray.transport.internet.http\x1a,transport/internet/headers/http/config.proto\"\xa6\x01\n\x06\x43onfig\x12\x0c\n\x04host\x18\x01 \x03(\t\x12\x0c\n\x04path\x18\x02 \x01(\t\x12\x14\n\x0cidle_timeout\x18\x03 \x01(\x05\x12\x1c\n\x14health_check_timeout\x18\x04 \x01(\x05\x12\x0e\n\x06method\x18\x05 \x01(\t\x12<\n\x06header\x18\x06 \x03(\x0b\x32,.xray.transport.internet.headers.http.HeaderBv\n com.xray.transport.internet.httpP\x01Z1github.com/xtls/xray-core/transport/internet/http\xaa\x02\x1cXray.Transport.Internet.Httpb\x06proto3')
19 |
20 |
21 |
22 | _CONFIG = DESCRIPTOR.message_types_by_name['Config']
23 | Config = _reflection.GeneratedProtocolMessageType('Config', (_message.Message,), {
24 | 'DESCRIPTOR' : _CONFIG,
25 | '__module__' : 'transport.internet.http.config_pb2'
26 | # @@protoc_insertion_point(class_scope:xray.transport.internet.http.Config)
27 | })
28 | _sym_db.RegisterMessage(Config)
29 |
30 | if _descriptor._USE_C_DESCRIPTORS == False:
31 |
32 | DESCRIPTOR._options = None
33 | DESCRIPTOR._serialized_options = b'\n com.xray.transport.internet.httpP\001Z1github.com/xtls/xray-core/transport/internet/http\252\002\034Xray.Transport.Internet.Http'
34 | _CONFIG._serialized_start=117
35 | _CONFIG._serialized_end=283
36 | # @@protoc_insertion_point(module_scope)
37 |
--------------------------------------------------------------------------------
/xray_api/proto/app/stats/config_pb2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by the protocol buffer compiler. DO NOT EDIT!
3 | # source: app/stats/config.proto
4 | """Generated protocol buffer code."""
5 | from google.protobuf import descriptor as _descriptor
6 | from google.protobuf import descriptor_pool as _descriptor_pool
7 | from google.protobuf import message as _message
8 | from google.protobuf import reflection as _reflection
9 | from google.protobuf import symbol_database as _symbol_database
10 | # @@protoc_insertion_point(imports)
11 |
12 | _sym_db = _symbol_database.Default()
13 |
14 |
15 |
16 |
17 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16\x61pp/stats/config.proto\x12\x0exray.app.stats\"\x08\n\x06\x43onfig\"N\n\rChannelConfig\x12\x10\n\x08\x42locking\x18\x01 \x01(\x08\x12\x17\n\x0fSubscriberLimit\x18\x02 \x01(\x05\x12\x12\n\nBufferSize\x18\x03 \x01(\x05\x42L\n\x12\x63om.xray.app.statsP\x01Z#github.com/xtls/xray-core/app/stats\xaa\x02\x0eXray.App.Statsb\x06proto3')
18 |
19 |
20 |
21 | _CONFIG = DESCRIPTOR.message_types_by_name['Config']
22 | _CHANNELCONFIG = DESCRIPTOR.message_types_by_name['ChannelConfig']
23 | Config = _reflection.GeneratedProtocolMessageType('Config', (_message.Message,), {
24 | 'DESCRIPTOR' : _CONFIG,
25 | '__module__' : 'app.stats.config_pb2'
26 | # @@protoc_insertion_point(class_scope:xray.app.stats.Config)
27 | })
28 | _sym_db.RegisterMessage(Config)
29 |
30 | ChannelConfig = _reflection.GeneratedProtocolMessageType('ChannelConfig', (_message.Message,), {
31 | 'DESCRIPTOR' : _CHANNELCONFIG,
32 | '__module__' : 'app.stats.config_pb2'
33 | # @@protoc_insertion_point(class_scope:xray.app.stats.ChannelConfig)
34 | })
35 | _sym_db.RegisterMessage(ChannelConfig)
36 |
37 | if _descriptor._USE_C_DESCRIPTORS == False:
38 |
39 | DESCRIPTOR._options = None
40 | DESCRIPTOR._serialized_options = b'\n\022com.xray.app.statsP\001Z#github.com/xtls/xray-core/app/stats\252\002\016Xray.App.Stats'
41 | _CONFIG._serialized_start=42
42 | _CONFIG._serialized_end=50
43 | _CHANNELCONFIG._serialized_start=52
44 | _CHANNELCONFIG._serialized_end=130
45 | # @@protoc_insertion_point(module_scope)
46 |
--------------------------------------------------------------------------------
/xray_api/proto/transport/internet/quic/config_pb2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by the protocol buffer compiler. DO NOT EDIT!
3 | # source: transport/internet/quic/config.proto
4 | """Generated protocol buffer code."""
5 | from google.protobuf import descriptor as _descriptor
6 | from google.protobuf import descriptor_pool as _descriptor_pool
7 | from google.protobuf import message as _message
8 | from google.protobuf import reflection as _reflection
9 | from google.protobuf import symbol_database as _symbol_database
10 | # @@protoc_insertion_point(imports)
11 |
12 | _sym_db = _symbol_database.Default()
13 |
14 |
15 | from xray_api.proto.common.serial import typed_message_pb2 as common_dot_serial_dot_typed__message__pb2
16 | from xray_api.proto.common.protocol import headers_pb2 as common_dot_protocol_dot_headers__pb2
17 |
18 |
19 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n$transport/internet/quic/config.proto\x12\x1cxray.transport.internet.quic\x1a!common/serial/typed_message.proto\x1a\x1d\x63ommon/protocol/headers.proto\"\x7f\n\x06\x43onfig\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x36\n\x08security\x18\x02 \x01(\x0b\x32$.xray.common.protocol.SecurityConfig\x12\x30\n\x06header\x18\x03 \x01(\x0b\x32 .xray.common.serial.TypedMessageBv\n com.xray.transport.internet.quicP\x01Z1github.com/xtls/xray-core/transport/internet/quic\xaa\x02\x1cXray.Transport.Internet.Quicb\x06proto3')
20 |
21 |
22 |
23 | _CONFIG = DESCRIPTOR.message_types_by_name['Config']
24 | Config = _reflection.GeneratedProtocolMessageType('Config', (_message.Message,), {
25 | 'DESCRIPTOR' : _CONFIG,
26 | '__module__' : 'transport.internet.quic.config_pb2'
27 | # @@protoc_insertion_point(class_scope:xray.transport.internet.quic.Config)
28 | })
29 | _sym_db.RegisterMessage(Config)
30 |
31 | if _descriptor._USE_C_DESCRIPTORS == False:
32 |
33 | DESCRIPTOR._options = None
34 | DESCRIPTOR._serialized_options = b'\n com.xray.transport.internet.quicP\001Z1github.com/xtls/xray-core/transport/internet/quic\252\002\034Xray.Transport.Internet.Quic'
35 | _CONFIG._serialized_start=136
36 | _CONFIG._serialized_end=263
37 | # @@protoc_insertion_point(module_scope)
38 |
--------------------------------------------------------------------------------
/xray_api/proto/app/dispatcher/config_pb2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by the protocol buffer compiler. DO NOT EDIT!
3 | # source: app/dispatcher/config.proto
4 | """Generated protocol buffer code."""
5 | from google.protobuf import descriptor as _descriptor
6 | from google.protobuf import descriptor_pool as _descriptor_pool
7 | from google.protobuf import message as _message
8 | from google.protobuf import reflection as _reflection
9 | from google.protobuf import symbol_database as _symbol_database
10 | # @@protoc_insertion_point(imports)
11 |
12 | _sym_db = _symbol_database.Default()
13 |
14 |
15 |
16 |
17 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1b\x61pp/dispatcher/config.proto\x12\x13xray.app.dispatcher\"\x15\n\rSessionConfigJ\x04\x08\x01\x10\x02\">\n\x06\x43onfig\x12\x34\n\x08settings\x18\x01 \x01(\x0b\x32\".xray.app.dispatcher.SessionConfigB[\n\x17\x63om.xray.app.dispatcherP\x01Z(github.com/xtls/xray-core/app/dispatcher\xaa\x02\x13Xray.App.Dispatcherb\x06proto3')
18 |
19 |
20 |
21 | _SESSIONCONFIG = DESCRIPTOR.message_types_by_name['SessionConfig']
22 | _CONFIG = DESCRIPTOR.message_types_by_name['Config']
23 | SessionConfig = _reflection.GeneratedProtocolMessageType('SessionConfig', (_message.Message,), {
24 | 'DESCRIPTOR' : _SESSIONCONFIG,
25 | '__module__' : 'app.dispatcher.config_pb2'
26 | # @@protoc_insertion_point(class_scope:xray.app.dispatcher.SessionConfig)
27 | })
28 | _sym_db.RegisterMessage(SessionConfig)
29 |
30 | Config = _reflection.GeneratedProtocolMessageType('Config', (_message.Message,), {
31 | 'DESCRIPTOR' : _CONFIG,
32 | '__module__' : 'app.dispatcher.config_pb2'
33 | # @@protoc_insertion_point(class_scope:xray.app.dispatcher.Config)
34 | })
35 | _sym_db.RegisterMessage(Config)
36 |
37 | if _descriptor._USE_C_DESCRIPTORS == False:
38 |
39 | DESCRIPTOR._options = None
40 | DESCRIPTOR._serialized_options = b'\n\027com.xray.app.dispatcherP\001Z(github.com/xtls/xray-core/app/dispatcher\252\002\023Xray.App.Dispatcher'
41 | _SESSIONCONFIG._serialized_start=52
42 | _SESSIONCONFIG._serialized_end=73
43 | _CONFIG._serialized_start=75
44 | _CONFIG._serialized_end=137
45 | # @@protoc_insertion_point(module_scope)
46 |
--------------------------------------------------------------------------------
/app/dashboard/build/assets/slick.12459f22.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
15 |
--------------------------------------------------------------------------------
/app/utils/store.py:
--------------------------------------------------------------------------------
1 | from typing import TYPE_CHECKING, Sequence
2 | from app import app, xray
3 | from app.models.proxy import ProxyHostSecurity
4 |
5 | if TYPE_CHECKING:
6 | from app.db.models import ProxyHost
7 |
8 |
9 | class XrayStore:
10 | HOSTS = {
11 | "INBOUND_TAG": [
12 | {
13 | "remark": "",
14 | "address": "",
15 | "port": None,
16 | "sni": "",
17 | "host": "",
18 | "tls": None,
19 | }
20 | ]
21 | }
22 |
23 | @classmethod
24 | def update_hosts(cls):
25 | from app.db import GetDB, crud
26 |
27 | cls.HOSTS = {}
28 | with GetDB() as db:
29 | for inbound_tag in xray.config.inbounds_by_tag:
30 | hosts: Sequence[ProxyHost] = crud.get_hosts(db, inbound_tag)
31 | cls.HOSTS[inbound_tag] = []
32 | for host in hosts:
33 | cls.HOSTS[inbound_tag].append(
34 | {
35 | "remark": host.remark,
36 | "address": host.address,
37 | "port": host.port,
38 | "sni": host.sni,
39 | "host": host.host,
40 | # None means the tls is not specified by host itself and
41 | # complies with its inbound's settings.
42 | "tls": None
43 | if host.security == ProxyHostSecurity.inbound_default
44 | else host.security == ProxyHostSecurity.tls,
45 | }
46 | )
47 |
48 |
49 | class MemoryStorage:
50 | def __init__(self):
51 | self._data = {}
52 |
53 | def set(self, key, value):
54 | self._data[key] = value
55 |
56 | def get(self, key, default=None):
57 | return self._data.get(key, default)
58 |
59 | def delete(self, key):
60 | self._data.pop(key, None)
61 |
62 | def clear(self):
63 | self._data.clear()
64 |
65 |
66 | @app.on_event("startup")
67 | def app_startup():
68 | XrayStore.update_hosts()
69 |
--------------------------------------------------------------------------------
/app/jobs/record_usages.py:
--------------------------------------------------------------------------------
1 | from operator import attrgetter
2 |
3 | from app import scheduler, xray
4 | from app.db import engine
5 | from app.db.models import System, User
6 | from sqlalchemy import bindparam, update
7 | from app.utils.xray import xray_config_include_db_clients
8 |
9 |
10 | def record_users_usage():
11 | with engine.connect() as conn:
12 | try:
13 | params = [
14 | {"name": stat.name, "value": stat.value}
15 | for stat in filter(attrgetter('value'), xray.api.get_users_stats(reset=True))
16 | ]
17 |
18 | except xray.exceptions.ConnectionError:
19 | try:
20 | xray.core.restart(
21 | xray_config_include_db_clients(xray.config)
22 | )
23 | except ProcessLookupError:
24 | pass
25 |
26 | return
27 |
28 | if not params:
29 | return
30 |
31 | stmt = update(User). \
32 | where(User.username == bindparam('name')). \
33 | values(used_traffic=User.used_traffic + bindparam('value'))
34 |
35 | conn.execute(stmt, params)
36 |
37 |
38 | scheduler.add_job(record_users_usage, 'interval', seconds=10)
39 |
40 |
41 | def record_outbounds_usage():
42 | with engine.connect() as conn:
43 | try:
44 | params = [
45 | {"up": stat.value, "down": 0} if stat.link == "uplink" else {"up": 0, "down": stat.value}
46 | for stat in filter(attrgetter('value'), xray.api.get_outbounds_stats(reset=True))
47 | ]
48 |
49 | except xray.exceptions.ConnectionError:
50 | try:
51 | xray.core.restart(
52 | xray_config_include_db_clients(xray.config)
53 | )
54 | except ProcessLookupError:
55 | pass
56 |
57 | return
58 |
59 | if not params:
60 | return
61 |
62 | stmt = update(System).values(
63 | uplink=System.uplink + bindparam('up'),
64 | downlink=System.downlink + bindparam('down')
65 | )
66 |
67 | conn.execute(stmt, params)
68 |
69 |
70 | scheduler.add_job(record_outbounds_usage, 'interval', seconds=5)
71 |
--------------------------------------------------------------------------------
/app/dashboard/src/components/UserBadge.tsx:
--------------------------------------------------------------------------------
1 | import { Badge, chakra, Text } from "@chakra-ui/react";
2 | import {
3 | ExclamationCircleIcon,
4 | ClockIcon,
5 | NoSymbolIcon,
6 | WifiIcon,
7 | } from "@heroicons/react/24/outline";
8 | import { statusColors } from "constants/UserSettings";
9 | import { FC } from "react";
10 | import { UserStatus as UserStatusType } from "types/User";
11 | import { relativeExpiryDate } from "utils/dateFormatter";
12 | const iconProps = {
13 | baseStyle: {
14 | strokeWidth: "2px",
15 | w: 4,
16 | h: 4,
17 | },
18 | };
19 | const ActiveStatusIcon = chakra(WifiIcon, iconProps);
20 | const DisabledStatusIcon = chakra(NoSymbolIcon, iconProps);
21 | const LimitedStatusIcon = chakra(ExclamationCircleIcon, iconProps);
22 | const ExpiredStatusIcon = chakra(ClockIcon, iconProps);
23 |
24 | type UserStatusProps = {
25 | expiryDate?: number | null;
26 | status: UserStatusType;
27 | };
28 | export const UserBadge: FC = ({
29 | expiryDate,
30 | status: userStatus,
31 | }) => {
32 | let date = relativeExpiryDate(expiryDate);
33 | return (
34 | <>
35 |
44 | {userStatus === "active" && }
45 | {userStatus === "disabled" && }
46 | {userStatus === "limited" && }
47 | {userStatus === "expired" && }
48 |
55 | {userStatus}
56 |
57 |
58 | {expiryDate && (
59 |
69 | {date}
70 |
71 | )}
72 | >
73 | );
74 | };
75 |
--------------------------------------------------------------------------------
/xray_api/proto/common/net/network_pb2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by the protocol buffer compiler. DO NOT EDIT!
3 | # source: common/net/network.proto
4 | """Generated protocol buffer code."""
5 | from google.protobuf.internal import enum_type_wrapper
6 | from google.protobuf import descriptor as _descriptor
7 | from google.protobuf import descriptor_pool as _descriptor_pool
8 | from google.protobuf import message as _message
9 | from google.protobuf import reflection as _reflection
10 | from google.protobuf import symbol_database as _symbol_database
11 | # @@protoc_insertion_point(imports)
12 |
13 | _sym_db = _symbol_database.Default()
14 |
15 |
16 |
17 |
18 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18\x63ommon/net/network.proto\x12\x0fxray.common.net\"8\n\x0bNetworkList\x12)\n\x07network\x18\x01 \x03(\x0e\x32\x18.xray.common.net.Network*B\n\x07Network\x12\x0b\n\x07Unknown\x10\x00\x12\x0e\n\x06RawTCP\x10\x01\x1a\x02\x08\x01\x12\x07\n\x03TCP\x10\x02\x12\x07\n\x03UDP\x10\x03\x12\x08\n\x04UNIX\x10\x04\x42O\n\x13\x63om.xray.common.netP\x01Z$github.com/xtls/xray-core/common/net\xaa\x02\x0fXray.Common.Netb\x06proto3')
19 |
20 | _NETWORK = DESCRIPTOR.enum_types_by_name['Network']
21 | Network = enum_type_wrapper.EnumTypeWrapper(_NETWORK)
22 | Unknown = 0
23 | RawTCP = 1
24 | TCP = 2
25 | UDP = 3
26 | UNIX = 4
27 |
28 |
29 | _NETWORKLIST = DESCRIPTOR.message_types_by_name['NetworkList']
30 | NetworkList = _reflection.GeneratedProtocolMessageType('NetworkList', (_message.Message,), {
31 | 'DESCRIPTOR' : _NETWORKLIST,
32 | '__module__' : 'common.net.network_pb2'
33 | # @@protoc_insertion_point(class_scope:xray.common.net.NetworkList)
34 | })
35 | _sym_db.RegisterMessage(NetworkList)
36 |
37 | if _descriptor._USE_C_DESCRIPTORS == False:
38 |
39 | DESCRIPTOR._options = None
40 | DESCRIPTOR._serialized_options = b'\n\023com.xray.common.netP\001Z$github.com/xtls/xray-core/common/net\252\002\017Xray.Common.Net'
41 | _NETWORK.values_by_name["RawTCP"]._options = None
42 | _NETWORK.values_by_name["RawTCP"]._serialized_options = b'\010\001'
43 | _NETWORK._serialized_start=103
44 | _NETWORK._serialized_end=169
45 | _NETWORKLIST._serialized_start=45
46 | _NETWORKLIST._serialized_end=101
47 | # @@protoc_insertion_point(module_scope)
48 |
--------------------------------------------------------------------------------
/app/views/subscription.py:
--------------------------------------------------------------------------------
1 |
2 | from typing import Union
3 |
4 | from app import app
5 | from app.db import Session, crud, get_db
6 | from app.models.user import UserResponse
7 | from app.utils.jwt import get_subscription_payload
8 | from app.utils.share import (generate_clash_subscription,
9 | generate_v2ray_subscription)
10 | from fastapi import Depends, Header, Response
11 |
12 |
13 | @app.get("/sub/{token}/", tags=['Subscription'])
14 | @app.get("/sub/{token}", include_in_schema=False)
15 | def user_subcription(token: str,
16 | db: Session = Depends(get_db),
17 | user_agent: Union[str, None] = Header(default=None)):
18 | """
19 | Subscription link, V2ray and Clash supported
20 | """
21 |
22 | application = user_agent.split('/')[0]
23 |
24 | sub = get_subscription_payload(token)
25 | if not sub:
26 | return Response(status_code=204)
27 |
28 | dbuser = crud.get_user(db, sub['username'])
29 | if not dbuser or dbuser.created_at > sub['created_at']:
30 | return Response(status_code=204)
31 | user = UserResponse.from_orm(dbuser)
32 |
33 | response_headers = {
34 | "content-disposition": f'attachment; filename="{user.username}"',
35 | "profile-update-interval": "12",
36 | "subscription-userinfo": f"upload=0; download={user.used_traffic}; total={user.data_limit}; expire={user.expire}"
37 | }
38 |
39 | if application.startswith('Clash'):
40 | conf = generate_clash_subscription(user.proxies, user.inbounds, user.dict())
41 | return Response(content=conf, media_type="text/yaml", headers=response_headers)
42 |
43 | else:
44 | conf = generate_v2ray_subscription(user.links)
45 | return Response(content=conf, media_type="text/plain", headers=response_headers)
46 |
47 |
48 | @app.get("/sub/{token}/info", tags=['Subscription'], response_model=UserResponse)
49 | def user_subcription_info(token: str,
50 | db: Session = Depends(get_db)):
51 | sub = get_subscription_payload(token)
52 | if not sub:
53 | return Response(status_code=404)
54 |
55 | dbuser = crud.get_user(db, sub['username'])
56 | if not dbuser or dbuser.created_at > sub['created_at']:
57 | return Response(status_code=404)
58 |
59 | return dbuser
60 |
--------------------------------------------------------------------------------
/xray_api/proto/app/dns/fakedns/fakedns_pb2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by the protocol buffer compiler. DO NOT EDIT!
3 | # source: app/dns/fakedns/fakedns.proto
4 | """Generated protocol buffer code."""
5 | from google.protobuf import descriptor as _descriptor
6 | from google.protobuf import descriptor_pool as _descriptor_pool
7 | from google.protobuf import message as _message
8 | from google.protobuf import reflection as _reflection
9 | from google.protobuf import symbol_database as _symbol_database
10 | # @@protoc_insertion_point(imports)
11 |
12 | _sym_db = _symbol_database.Default()
13 |
14 |
15 |
16 |
17 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1d\x61pp/dns/fakedns/fakedns.proto\x12\x14xray.app.dns.fakedns\"/\n\x0b\x46\x61keDnsPool\x12\x0f\n\x07ip_pool\x18\x01 \x01(\t\x12\x0f\n\x07lruSize\x18\x02 \x01(\x03\"D\n\x10\x46\x61keDnsPoolMulti\x12\x30\n\x05pools\x18\x01 \x03(\x0b\x32!.xray.app.dns.fakedns.FakeDnsPoolB^\n\x18\x63om.xray.app.dns.fakednsP\x01Z)github.com/xtls/xray-core/app/dns/fakedns\xaa\x02\x14Xray.App.Dns.Fakednsb\x06proto3')
18 |
19 |
20 |
21 | _FAKEDNSPOOL = DESCRIPTOR.message_types_by_name['FakeDnsPool']
22 | _FAKEDNSPOOLMULTI = DESCRIPTOR.message_types_by_name['FakeDnsPoolMulti']
23 | FakeDnsPool = _reflection.GeneratedProtocolMessageType('FakeDnsPool', (_message.Message,), {
24 | 'DESCRIPTOR' : _FAKEDNSPOOL,
25 | '__module__' : 'app.dns.fakedns.fakedns_pb2'
26 | # @@protoc_insertion_point(class_scope:xray.app.dns.fakedns.FakeDnsPool)
27 | })
28 | _sym_db.RegisterMessage(FakeDnsPool)
29 |
30 | FakeDnsPoolMulti = _reflection.GeneratedProtocolMessageType('FakeDnsPoolMulti', (_message.Message,), {
31 | 'DESCRIPTOR' : _FAKEDNSPOOLMULTI,
32 | '__module__' : 'app.dns.fakedns.fakedns_pb2'
33 | # @@protoc_insertion_point(class_scope:xray.app.dns.fakedns.FakeDnsPoolMulti)
34 | })
35 | _sym_db.RegisterMessage(FakeDnsPoolMulti)
36 |
37 | if _descriptor._USE_C_DESCRIPTORS == False:
38 |
39 | DESCRIPTOR._options = None
40 | DESCRIPTOR._serialized_options = b'\n\030com.xray.app.dns.fakednsP\001Z)github.com/xtls/xray-core/app/dns/fakedns\252\002\024Xray.App.Dns.Fakedns'
41 | _FAKEDNSPOOL._serialized_start=55
42 | _FAKEDNSPOOL._serialized_end=102
43 | _FAKEDNSPOOLMULTI._serialized_start=104
44 | _FAKEDNSPOOLMULTI._serialized_end=172
45 | # @@protoc_insertion_point(module_scope)
46 |
--------------------------------------------------------------------------------
/xray_api/proto/transport/internet/headers/noop/config_pb2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by the protocol buffer compiler. DO NOT EDIT!
3 | # source: transport/internet/headers/noop/config.proto
4 | """Generated protocol buffer code."""
5 | from google.protobuf import descriptor as _descriptor
6 | from google.protobuf import descriptor_pool as _descriptor_pool
7 | from google.protobuf import message as _message
8 | from google.protobuf import reflection as _reflection
9 | from google.protobuf import symbol_database as _symbol_database
10 | # @@protoc_insertion_point(imports)
11 |
12 | _sym_db = _symbol_database.Default()
13 |
14 |
15 |
16 |
17 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n,transport/internet/headers/noop/config.proto\x12$xray.transport.internet.headers.noop\"\x08\n\x06\x43onfig\"\x12\n\x10\x43onnectionConfigB\x8e\x01\n(com.xray.transport.internet.headers.noopP\x01Z9github.com/xtls/xray-core/transport/internet/headers/noop\xaa\x02$Xray.Transport.Internet.Headers.Noopb\x06proto3')
18 |
19 |
20 |
21 | _CONFIG = DESCRIPTOR.message_types_by_name['Config']
22 | _CONNECTIONCONFIG = DESCRIPTOR.message_types_by_name['ConnectionConfig']
23 | Config = _reflection.GeneratedProtocolMessageType('Config', (_message.Message,), {
24 | 'DESCRIPTOR' : _CONFIG,
25 | '__module__' : 'transport.internet.headers.noop.config_pb2'
26 | # @@protoc_insertion_point(class_scope:xray.transport.internet.headers.noop.Config)
27 | })
28 | _sym_db.RegisterMessage(Config)
29 |
30 | ConnectionConfig = _reflection.GeneratedProtocolMessageType('ConnectionConfig', (_message.Message,), {
31 | 'DESCRIPTOR' : _CONNECTIONCONFIG,
32 | '__module__' : 'transport.internet.headers.noop.config_pb2'
33 | # @@protoc_insertion_point(class_scope:xray.transport.internet.headers.noop.ConnectionConfig)
34 | })
35 | _sym_db.RegisterMessage(ConnectionConfig)
36 |
37 | if _descriptor._USE_C_DESCRIPTORS == False:
38 |
39 | DESCRIPTOR._options = None
40 | DESCRIPTOR._serialized_options = b'\n(com.xray.transport.internet.headers.noopP\001Z9github.com/xtls/xray-core/transport/internet/headers/noop\252\002$Xray.Transport.Internet.Headers.Noop'
41 | _CONFIG._serialized_start=86
42 | _CONFIG._serialized_end=94
43 | _CONNECTIONCONFIG._serialized_start=96
44 | _CONNECTIONCONFIG._serialized_end=114
45 | # @@protoc_insertion_point(module_scope)
46 |
--------------------------------------------------------------------------------
/app/dashboard/build/favicon/safari-pinned-tab.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
37 |
--------------------------------------------------------------------------------
/app/dashboard/public/favicon/safari-pinned-tab.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
37 |
--------------------------------------------------------------------------------
/xray_api/proto/app/commander/config_pb2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by the protocol buffer compiler. DO NOT EDIT!
3 | # source: app/commander/config.proto
4 | """Generated protocol buffer code."""
5 | from google.protobuf import descriptor as _descriptor
6 | from google.protobuf import descriptor_pool as _descriptor_pool
7 | from google.protobuf import message as _message
8 | from google.protobuf import reflection as _reflection
9 | from google.protobuf import symbol_database as _symbol_database
10 | # @@protoc_insertion_point(imports)
11 |
12 | _sym_db = _symbol_database.Default()
13 |
14 |
15 | from xray_api.proto.common.serial import typed_message_pb2 as common_dot_serial_dot_typed__message__pb2
16 |
17 |
18 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1a\x61pp/commander/config.proto\x12\x12xray.app.commander\x1a!common/serial/typed_message.proto\"H\n\x06\x43onfig\x12\x0b\n\x03tag\x18\x01 \x01(\t\x12\x31\n\x07service\x18\x02 \x03(\x0b\x32 .xray.common.serial.TypedMessage\"\x12\n\x10ReflectionConfigBX\n\x16\x63om.xray.app.commanderP\x01Z\'github.com/xtls/xray-core/app/commander\xaa\x02\x12Xray.App.Commanderb\x06proto3')
19 |
20 |
21 |
22 | _CONFIG = DESCRIPTOR.message_types_by_name['Config']
23 | _REFLECTIONCONFIG = DESCRIPTOR.message_types_by_name['ReflectionConfig']
24 | Config = _reflection.GeneratedProtocolMessageType('Config', (_message.Message,), {
25 | 'DESCRIPTOR' : _CONFIG,
26 | '__module__' : 'app.commander.config_pb2'
27 | # @@protoc_insertion_point(class_scope:xray.app.commander.Config)
28 | })
29 | _sym_db.RegisterMessage(Config)
30 |
31 | ReflectionConfig = _reflection.GeneratedProtocolMessageType('ReflectionConfig', (_message.Message,), {
32 | 'DESCRIPTOR' : _REFLECTIONCONFIG,
33 | '__module__' : 'app.commander.config_pb2'
34 | # @@protoc_insertion_point(class_scope:xray.app.commander.ReflectionConfig)
35 | })
36 | _sym_db.RegisterMessage(ReflectionConfig)
37 |
38 | if _descriptor._USE_C_DESCRIPTORS == False:
39 |
40 | DESCRIPTOR._options = None
41 | DESCRIPTOR._serialized_options = b'\n\026com.xray.app.commanderP\001Z\'github.com/xtls/xray-core/app/commander\252\002\022Xray.App.Commander'
42 | _CONFIG._serialized_start=85
43 | _CONFIG._serialized_end=157
44 | _REFLECTIONCONFIG._serialized_start=159
45 | _REFLECTIONCONFIG._serialized_end=177
46 | # @@protoc_insertion_point(module_scope)
47 |
--------------------------------------------------------------------------------
/xray_api/proto/common/protocol/headers_pb2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by the protocol buffer compiler. DO NOT EDIT!
3 | # source: common/protocol/headers.proto
4 | """Generated protocol buffer code."""
5 | from google.protobuf.internal import enum_type_wrapper
6 | from google.protobuf import descriptor as _descriptor
7 | from google.protobuf import descriptor_pool as _descriptor_pool
8 | from google.protobuf import message as _message
9 | from google.protobuf import reflection as _reflection
10 | from google.protobuf import symbol_database as _symbol_database
11 | # @@protoc_insertion_point(imports)
12 |
13 | _sym_db = _symbol_database.Default()
14 |
15 |
16 |
17 |
18 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1d\x63ommon/protocol/headers.proto\x12\x14xray.common.protocol\"B\n\x0eSecurityConfig\x12\x30\n\x04type\x18\x01 \x01(\x0e\x32\".xray.common.protocol.SecurityType*l\n\x0cSecurityType\x12\x0b\n\x07UNKNOWN\x10\x00\x12\n\n\x06LEGACY\x10\x01\x12\x08\n\x04\x41UTO\x10\x02\x12\x0e\n\nAES128_GCM\x10\x03\x12\x15\n\x11\x43HACHA20_POLY1305\x10\x04\x12\x08\n\x04NONE\x10\x05\x12\x08\n\x04ZERO\x10\x06\x42^\n\x18\x63om.xray.common.protocolP\x01Z)github.com/xtls/xray-core/common/protocol\xaa\x02\x14Xray.Common.Protocolb\x06proto3')
19 |
20 | _SECURITYTYPE = DESCRIPTOR.enum_types_by_name['SecurityType']
21 | SecurityType = enum_type_wrapper.EnumTypeWrapper(_SECURITYTYPE)
22 | UNKNOWN = 0
23 | LEGACY = 1
24 | AUTO = 2
25 | AES128_GCM = 3
26 | CHACHA20_POLY1305 = 4
27 | NONE = 5
28 | ZERO = 6
29 |
30 |
31 | _SECURITYCONFIG = DESCRIPTOR.message_types_by_name['SecurityConfig']
32 | SecurityConfig = _reflection.GeneratedProtocolMessageType('SecurityConfig', (_message.Message,), {
33 | 'DESCRIPTOR' : _SECURITYCONFIG,
34 | '__module__' : 'common.protocol.headers_pb2'
35 | # @@protoc_insertion_point(class_scope:xray.common.protocol.SecurityConfig)
36 | })
37 | _sym_db.RegisterMessage(SecurityConfig)
38 |
39 | if _descriptor._USE_C_DESCRIPTORS == False:
40 |
41 | DESCRIPTOR._options = None
42 | DESCRIPTOR._serialized_options = b'\n\030com.xray.common.protocolP\001Z)github.com/xtls/xray-core/common/protocol\252\002\024Xray.Common.Protocol'
43 | _SECURITYTYPE._serialized_start=123
44 | _SECURITYTYPE._serialized_end=231
45 | _SECURITYCONFIG._serialized_start=55
46 | _SECURITYCONFIG._serialized_end=121
47 | # @@protoc_insertion_point(module_scope)
48 |
--------------------------------------------------------------------------------
/xray_api/proto/app/log/config_pb2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by the protocol buffer compiler. DO NOT EDIT!
3 | # source: app/log/config.proto
4 | """Generated protocol buffer code."""
5 | from google.protobuf.internal import enum_type_wrapper
6 | from google.protobuf import descriptor as _descriptor
7 | from google.protobuf import descriptor_pool as _descriptor_pool
8 | from google.protobuf import message as _message
9 | from google.protobuf import reflection as _reflection
10 | from google.protobuf import symbol_database as _symbol_database
11 | # @@protoc_insertion_point(imports)
12 |
13 | _sym_db = _symbol_database.Default()
14 |
15 |
16 | from xray_api.proto.common.log import log_pb2 as common_dot_log_dot_log__pb2
17 |
18 |
19 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14\x61pp/log/config.proto\x12\x0cxray.app.log\x1a\x14\x63ommon/log/log.proto\"\xe4\x01\n\x06\x43onfig\x12-\n\x0e\x65rror_log_type\x18\x01 \x01(\x0e\x32\x15.xray.app.log.LogType\x12\x32\n\x0f\x65rror_log_level\x18\x02 \x01(\x0e\x32\x19.xray.common.log.Severity\x12\x16\n\x0e\x65rror_log_path\x18\x03 \x01(\t\x12.\n\x0f\x61\x63\x63\x65ss_log_type\x18\x04 \x01(\x0e\x32\x15.xray.app.log.LogType\x12\x17\n\x0f\x61\x63\x63\x65ss_log_path\x18\x05 \x01(\t\x12\x16\n\x0e\x65nable_dns_log\x18\x06 \x01(\x08*5\n\x07LogType\x12\x08\n\x04None\x10\x00\x12\x0b\n\x07\x43onsole\x10\x01\x12\x08\n\x04\x46ile\x10\x02\x12\t\n\x05\x45vent\x10\x03\x42\x46\n\x10\x63om.xray.app.logP\x01Z!github.com/xtls/xray-core/app/log\xaa\x02\x0cXray.App.Logb\x06proto3')
20 |
21 | _LOGTYPE = DESCRIPTOR.enum_types_by_name['LogType']
22 | LogType = enum_type_wrapper.EnumTypeWrapper(_LOGTYPE)
23 | globals()['None'] = 0
24 | Console = 1
25 | File = 2
26 | Event = 3
27 |
28 |
29 | _CONFIG = DESCRIPTOR.message_types_by_name['Config']
30 | Config = _reflection.GeneratedProtocolMessageType('Config', (_message.Message,), {
31 | 'DESCRIPTOR' : _CONFIG,
32 | '__module__' : 'app.log.config_pb2'
33 | # @@protoc_insertion_point(class_scope:xray.app.log.Config)
34 | })
35 | _sym_db.RegisterMessage(Config)
36 |
37 | if _descriptor._USE_C_DESCRIPTORS == False:
38 |
39 | DESCRIPTOR._options = None
40 | DESCRIPTOR._serialized_options = b'\n\020com.xray.app.logP\001Z!github.com/xtls/xray-core/app/log\252\002\014Xray.App.Log'
41 | _LOGTYPE._serialized_start=291
42 | _LOGTYPE._serialized_end=344
43 | _CONFIG._serialized_start=61
44 | _CONFIG._serialized_end=289
45 | # @@protoc_insertion_point(module_scope)
46 |
--------------------------------------------------------------------------------
/xray_api/proto/transport/internet/websocket/config_pb2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by the protocol buffer compiler. DO NOT EDIT!
3 | # source: transport/internet/websocket/config.proto
4 | """Generated protocol buffer code."""
5 | from google.protobuf import descriptor as _descriptor
6 | from google.protobuf import descriptor_pool as _descriptor_pool
7 | from google.protobuf import message as _message
8 | from google.protobuf import reflection as _reflection
9 | from google.protobuf import symbol_database as _symbol_database
10 | # @@protoc_insertion_point(imports)
11 |
12 | _sym_db = _symbol_database.Default()
13 |
14 |
15 |
16 |
17 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n)transport/internet/websocket/config.proto\x12!xray.transport.internet.websocket\"$\n\x06Header\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t\"\x82\x01\n\x06\x43onfig\x12\x0c\n\x04path\x18\x02 \x01(\t\x12\x39\n\x06header\x18\x03 \x03(\x0b\x32).xray.transport.internet.websocket.Header\x12\x1d\n\x15\x61\x63\x63\x65pt_proxy_protocol\x18\x04 \x01(\x08\x12\n\n\x02\x65\x64\x18\x05 \x01(\rJ\x04\x08\x01\x10\x02\x42\x85\x01\n%com.xray.transport.internet.websocketP\x01Z6github.com/xtls/xray-core/transport/internet/websocket\xaa\x02!Xray.Transport.Internet.Websocketb\x06proto3')
18 |
19 |
20 |
21 | _HEADER = DESCRIPTOR.message_types_by_name['Header']
22 | _CONFIG = DESCRIPTOR.message_types_by_name['Config']
23 | Header = _reflection.GeneratedProtocolMessageType('Header', (_message.Message,), {
24 | 'DESCRIPTOR' : _HEADER,
25 | '__module__' : 'transport.internet.websocket.config_pb2'
26 | # @@protoc_insertion_point(class_scope:xray.transport.internet.websocket.Header)
27 | })
28 | _sym_db.RegisterMessage(Header)
29 |
30 | Config = _reflection.GeneratedProtocolMessageType('Config', (_message.Message,), {
31 | 'DESCRIPTOR' : _CONFIG,
32 | '__module__' : 'transport.internet.websocket.config_pb2'
33 | # @@protoc_insertion_point(class_scope:xray.transport.internet.websocket.Config)
34 | })
35 | _sym_db.RegisterMessage(Config)
36 |
37 | if _descriptor._USE_C_DESCRIPTORS == False:
38 |
39 | DESCRIPTOR._options = None
40 | DESCRIPTOR._serialized_options = b'\n%com.xray.transport.internet.websocketP\001Z6github.com/xtls/xray-core/transport/internet/websocket\252\002!Xray.Transport.Internet.Websocket'
41 | _HEADER._serialized_start=80
42 | _HEADER._serialized_end=116
43 | _CONFIG._serialized_start=119
44 | _CONFIG._serialized_end=249
45 | # @@protoc_insertion_point(module_scope)
46 |
--------------------------------------------------------------------------------
/xray_api/proto/proxy/dokodemo/config_pb2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by the protocol buffer compiler. DO NOT EDIT!
3 | # source: proxy/dokodemo/config.proto
4 | """Generated protocol buffer code."""
5 | from google.protobuf import descriptor as _descriptor
6 | from google.protobuf import descriptor_pool as _descriptor_pool
7 | from google.protobuf import message as _message
8 | from google.protobuf import reflection as _reflection
9 | from google.protobuf import symbol_database as _symbol_database
10 | # @@protoc_insertion_point(imports)
11 |
12 | _sym_db = _symbol_database.Default()
13 |
14 |
15 | from xray_api.proto.common.net import address_pb2 as common_dot_net_dot_address__pb2
16 | from xray_api.proto.common.net import network_pb2 as common_dot_net_dot_network__pb2
17 |
18 |
19 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1bproxy/dokodemo/config.proto\x12\x13xray.proxy.dokodemo\x1a\x18\x63ommon/net/address.proto\x1a\x18\x63ommon/net/network.proto\"\xea\x01\n\x06\x43onfig\x12,\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0b\x32\x1b.xray.common.net.IPOrDomain\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x36\n\x0cnetwork_list\x18\x03 \x01(\x0b\x32\x1c.xray.common.net.NetworkListB\x02\x18\x01\x12*\n\x08networks\x18\x07 \x03(\x0e\x32\x18.xray.common.net.Network\x12\x13\n\x07timeout\x18\x04 \x01(\rB\x02\x18\x01\x12\x17\n\x0f\x66ollow_redirect\x18\x05 \x01(\x08\x12\x12\n\nuser_level\x18\x06 \x01(\rB[\n\x17\x63om.xray.proxy.dokodemoP\x01Z(github.com/xtls/xray-core/proxy/dokodemo\xaa\x02\x13Xray.Proxy.Dokodemob\x06proto3')
20 |
21 |
22 |
23 | _CONFIG = DESCRIPTOR.message_types_by_name['Config']
24 | Config = _reflection.GeneratedProtocolMessageType('Config', (_message.Message,), {
25 | 'DESCRIPTOR' : _CONFIG,
26 | '__module__' : 'proxy.dokodemo.config_pb2'
27 | # @@protoc_insertion_point(class_scope:xray.proxy.dokodemo.Config)
28 | })
29 | _sym_db.RegisterMessage(Config)
30 |
31 | if _descriptor._USE_C_DESCRIPTORS == False:
32 |
33 | DESCRIPTOR._options = None
34 | DESCRIPTOR._serialized_options = b'\n\027com.xray.proxy.dokodemoP\001Z(github.com/xtls/xray-core/proxy/dokodemo\252\002\023Xray.Proxy.Dokodemo'
35 | _CONFIG.fields_by_name['network_list']._options = None
36 | _CONFIG.fields_by_name['network_list']._serialized_options = b'\030\001'
37 | _CONFIG.fields_by_name['timeout']._options = None
38 | _CONFIG.fields_by_name['timeout']._serialized_options = b'\030\001'
39 | _CONFIG._serialized_start=105
40 | _CONFIG._serialized_end=339
41 | # @@protoc_insertion_point(module_scope)
42 |
--------------------------------------------------------------------------------
/app/dashboard/src/assets/logo.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/app/dashboard/build/assets/logo.2507bd68.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/xray_api/proto/transport/internet/grpc/encoding/stream_pb2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by the protocol buffer compiler. DO NOT EDIT!
3 | # source: transport/internet/grpc/encoding/stream.proto
4 | """Generated protocol buffer code."""
5 | from google.protobuf import descriptor as _descriptor
6 | from google.protobuf import descriptor_pool as _descriptor_pool
7 | from google.protobuf import message as _message
8 | from google.protobuf import reflection as _reflection
9 | from google.protobuf import symbol_database as _symbol_database
10 | # @@protoc_insertion_point(imports)
11 |
12 | _sym_db = _symbol_database.Default()
13 |
14 |
15 |
16 |
17 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n-transport/internet/grpc/encoding/stream.proto\x12%xray.transport.internet.grpc.encoding\"\x14\n\x04Hunk\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\"\x19\n\tMultiHunk\x12\x0c\n\x04\x64\x61ta\x18\x01 \x03(\x0c\x32\xe6\x01\n\x0bGRPCService\x12\x63\n\x03Tun\x12+.xray.transport.internet.grpc.encoding.Hunk\x1a+.xray.transport.internet.grpc.encoding.Hunk(\x01\x30\x01\x12r\n\x08TunMulti\x12\x30.xray.transport.internet.grpc.encoding.MultiHunk\x1a\x30.xray.transport.internet.grpc.encoding.MultiHunk(\x01\x30\x01\x42 None:
34 | """Run migrations in 'offline' mode.
35 |
36 | This configures the context with just a URL
37 | and not an Engine, though an Engine is acceptable
38 | here as well. By skipping the Engine creation
39 | we don't even need a DBAPI to be available.
40 |
41 | Calls to context.execute() here emit the given string to the
42 | script output.
43 |
44 | """
45 | url = config.get_main_option("sqlalchemy.url")
46 | context.configure(
47 | url=url,
48 | target_metadata=target_metadata,
49 | literal_binds=True,
50 | dialect_opts={"paramstyle": "named"},
51 | )
52 |
53 | with context.begin_transaction():
54 | context.run_migrations()
55 |
56 |
57 | def run_migrations_online() -> None:
58 | """Run migrations in 'online' mode.
59 |
60 | In this scenario we need to create an Engine
61 | and associate a connection with the context.
62 |
63 | """
64 | connectable = engine_from_config(
65 | config.get_section(config.config_ini_section),
66 | prefix="sqlalchemy.",
67 | poolclass=pool.NullPool,
68 | )
69 |
70 | with connectable.connect() as connection:
71 | context.configure(
72 | connection=connection, target_metadata=target_metadata
73 | )
74 |
75 | with context.begin_transaction():
76 | context.run_migrations()
77 |
78 |
79 | if context.is_offline_mode():
80 | run_migrations_offline()
81 | else:
82 | run_migrations_online()
83 |
--------------------------------------------------------------------------------
/xray_api/proto/proxy/vless/inbound/config_pb2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by the protocol buffer compiler. DO NOT EDIT!
3 | # source: proxy/vless/inbound/config.proto
4 | """Generated protocol buffer code."""
5 | from google.protobuf import descriptor as _descriptor
6 | from google.protobuf import descriptor_pool as _descriptor_pool
7 | from google.protobuf import message as _message
8 | from google.protobuf import reflection as _reflection
9 | from google.protobuf import symbol_database as _symbol_database
10 | # @@protoc_insertion_point(imports)
11 |
12 | _sym_db = _symbol_database.Default()
13 |
14 |
15 | from xray_api.proto.common.protocol import user_pb2 as common_dot_protocol_dot_user__pb2
16 |
17 |
18 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n proxy/vless/inbound/config.proto\x12\x18xray.proxy.vless.inbound\x1a\x1a\x63ommon/protocol/user.proto\"^\n\x08\x46\x61llback\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0c\n\x04\x61lpn\x18\x02 \x01(\t\x12\x0c\n\x04path\x18\x03 \x01(\t\x12\x0c\n\x04type\x18\x04 \x01(\t\x12\x0c\n\x04\x64\x65st\x18\x05 \x01(\t\x12\x0c\n\x04xver\x18\x06 \x01(\x04\"\x80\x01\n\x06\x43onfig\x12+\n\x07\x63lients\x18\x01 \x03(\x0b\x32\x1a.xray.common.protocol.User\x12\x12\n\ndecryption\x18\x02 \x01(\t\x12\x35\n\tfallbacks\x18\x03 \x03(\x0b\x32\".xray.proxy.vless.inbound.FallbackBj\n\x1c\x63om.xray.proxy.vless.inboundP\x01Z-github.com/xtls/xray-core/proxy/vless/inbound\xaa\x02\x18Xray.Proxy.Vless.Inboundb\x06proto3')
19 |
20 |
21 |
22 | _FALLBACK = DESCRIPTOR.message_types_by_name['Fallback']
23 | _CONFIG = DESCRIPTOR.message_types_by_name['Config']
24 | Fallback = _reflection.GeneratedProtocolMessageType('Fallback', (_message.Message,), {
25 | 'DESCRIPTOR' : _FALLBACK,
26 | '__module__' : 'proxy.vless.inbound.config_pb2'
27 | # @@protoc_insertion_point(class_scope:xray.proxy.vless.inbound.Fallback)
28 | })
29 | _sym_db.RegisterMessage(Fallback)
30 |
31 | Config = _reflection.GeneratedProtocolMessageType('Config', (_message.Message,), {
32 | 'DESCRIPTOR' : _CONFIG,
33 | '__module__' : 'proxy.vless.inbound.config_pb2'
34 | # @@protoc_insertion_point(class_scope:xray.proxy.vless.inbound.Config)
35 | })
36 | _sym_db.RegisterMessage(Config)
37 |
38 | if _descriptor._USE_C_DESCRIPTORS == False:
39 |
40 | DESCRIPTOR._options = None
41 | DESCRIPTOR._serialized_options = b'\n\034com.xray.proxy.vless.inboundP\001Z-github.com/xtls/xray-core/proxy/vless/inbound\252\002\030Xray.Proxy.Vless.Inbound'
42 | _FALLBACK._serialized_start=90
43 | _FALLBACK._serialized_end=184
44 | _CONFIG._serialized_start=187
45 | _CONFIG._serialized_end=315
46 | # @@protoc_insertion_point(module_scope)
47 |
--------------------------------------------------------------------------------
/xray_api/proto/proxy/blackhole/config_pb2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by the protocol buffer compiler. DO NOT EDIT!
3 | # source: proxy/blackhole/config.proto
4 | """Generated protocol buffer code."""
5 | from google.protobuf import descriptor as _descriptor
6 | from google.protobuf import descriptor_pool as _descriptor_pool
7 | from google.protobuf import message as _message
8 | from google.protobuf import reflection as _reflection
9 | from google.protobuf import symbol_database as _symbol_database
10 | # @@protoc_insertion_point(imports)
11 |
12 | _sym_db = _symbol_database.Default()
13 |
14 |
15 | from xray_api.proto.common.serial import typed_message_pb2 as common_dot_serial_dot_typed__message__pb2
16 |
17 |
18 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1cproxy/blackhole/config.proto\x12\x14xray.proxy.blackhole\x1a!common/serial/typed_message.proto\"\x0e\n\x0cNoneResponse\"\x0e\n\x0cHTTPResponse\"<\n\x06\x43onfig\x12\x32\n\x08response\x18\x01 \x01(\x0b\x32 .xray.common.serial.TypedMessageB^\n\x18\x63om.xray.proxy.blackholeP\x01Z)github.com/xtls/xray-core/proxy/blackhole\xaa\x02\x14Xray.Proxy.Blackholeb\x06proto3')
19 |
20 |
21 |
22 | _NONERESPONSE = DESCRIPTOR.message_types_by_name['NoneResponse']
23 | _HTTPRESPONSE = DESCRIPTOR.message_types_by_name['HTTPResponse']
24 | _CONFIG = DESCRIPTOR.message_types_by_name['Config']
25 | NoneResponse = _reflection.GeneratedProtocolMessageType('NoneResponse', (_message.Message,), {
26 | 'DESCRIPTOR' : _NONERESPONSE,
27 | '__module__' : 'proxy.blackhole.config_pb2'
28 | # @@protoc_insertion_point(class_scope:xray.proxy.blackhole.NoneResponse)
29 | })
30 | _sym_db.RegisterMessage(NoneResponse)
31 |
32 | HTTPResponse = _reflection.GeneratedProtocolMessageType('HTTPResponse', (_message.Message,), {
33 | 'DESCRIPTOR' : _HTTPRESPONSE,
34 | '__module__' : 'proxy.blackhole.config_pb2'
35 | # @@protoc_insertion_point(class_scope:xray.proxy.blackhole.HTTPResponse)
36 | })
37 | _sym_db.RegisterMessage(HTTPResponse)
38 |
39 | Config = _reflection.GeneratedProtocolMessageType('Config', (_message.Message,), {
40 | 'DESCRIPTOR' : _CONFIG,
41 | '__module__' : 'proxy.blackhole.config_pb2'
42 | # @@protoc_insertion_point(class_scope:xray.proxy.blackhole.Config)
43 | })
44 | _sym_db.RegisterMessage(Config)
45 |
46 | if _descriptor._USE_C_DESCRIPTORS == False:
47 |
48 | DESCRIPTOR._options = None
49 | DESCRIPTOR._serialized_options = b'\n\030com.xray.proxy.blackholeP\001Z)github.com/xtls/xray-core/proxy/blackhole\252\002\024Xray.Proxy.Blackhole'
50 | _NONERESPONSE._serialized_start=89
51 | _NONERESPONSE._serialized_end=103
52 | _HTTPRESPONSE._serialized_start=105
53 | _HTTPRESPONSE._serialized_end=119
54 | _CONFIG._serialized_start=121
55 | _CONFIG._serialized_end=181
56 | # @@protoc_insertion_point(module_scope)
57 |
--------------------------------------------------------------------------------
/xray_api/proto/proxy/mtproto/config_pb2.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by the protocol buffer compiler. DO NOT EDIT!
3 | # source: proxy/mtproto/config.proto
4 | """Generated protocol buffer code."""
5 | from google.protobuf import descriptor as _descriptor
6 | from google.protobuf import descriptor_pool as _descriptor_pool
7 | from google.protobuf import message as _message
8 | from google.protobuf import reflection as _reflection
9 | from google.protobuf import symbol_database as _symbol_database
10 | # @@protoc_insertion_point(imports)
11 |
12 | _sym_db = _symbol_database.Default()
13 |
14 |
15 | from xray_api.proto.common.protocol import user_pb2 as common_dot_protocol_dot_user__pb2
16 |
17 |
18 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1aproxy/mtproto/config.proto\x12\x12xray.proxy.mtproto\x1a\x1a\x63ommon/protocol/user.proto\"\x19\n\x07\x41\x63\x63ount\x12\x0e\n\x06secret\x18\x01 \x01(\x0c\"8\n\x0cServerConfig\x12(\n\x04user\x18\x01 \x03(\x0b\x32\x1a.xray.common.protocol.User\"\x0e\n\x0c\x43lientConfigBX\n\x16\x63om.xray.proxy.mtprotoP\x01Z\'github.com/xtls/xray-core/proxy/mtproto\xaa\x02\x12Xray.Proxy.Mtprotob\x06proto3')
19 |
20 |
21 |
22 | _ACCOUNT = DESCRIPTOR.message_types_by_name['Account']
23 | _SERVERCONFIG = DESCRIPTOR.message_types_by_name['ServerConfig']
24 | _CLIENTCONFIG = DESCRIPTOR.message_types_by_name['ClientConfig']
25 | Account = _reflection.GeneratedProtocolMessageType('Account', (_message.Message,), {
26 | 'DESCRIPTOR' : _ACCOUNT,
27 | '__module__' : 'proxy.mtproto.config_pb2'
28 | # @@protoc_insertion_point(class_scope:xray.proxy.mtproto.Account)
29 | })
30 | _sym_db.RegisterMessage(Account)
31 |
32 | ServerConfig = _reflection.GeneratedProtocolMessageType('ServerConfig', (_message.Message,), {
33 | 'DESCRIPTOR' : _SERVERCONFIG,
34 | '__module__' : 'proxy.mtproto.config_pb2'
35 | # @@protoc_insertion_point(class_scope:xray.proxy.mtproto.ServerConfig)
36 | })
37 | _sym_db.RegisterMessage(ServerConfig)
38 |
39 | ClientConfig = _reflection.GeneratedProtocolMessageType('ClientConfig', (_message.Message,), {
40 | 'DESCRIPTOR' : _CLIENTCONFIG,
41 | '__module__' : 'proxy.mtproto.config_pb2'
42 | # @@protoc_insertion_point(class_scope:xray.proxy.mtproto.ClientConfig)
43 | })
44 | _sym_db.RegisterMessage(ClientConfig)
45 |
46 | if _descriptor._USE_C_DESCRIPTORS == False:
47 |
48 | DESCRIPTOR._options = None
49 | DESCRIPTOR._serialized_options = b'\n\026com.xray.proxy.mtprotoP\001Z\'github.com/xtls/xray-core/proxy/mtproto\252\002\022Xray.Proxy.Mtproto'
50 | _ACCOUNT._serialized_start=78
51 | _ACCOUNT._serialized_end=103
52 | _SERVERCONFIG._serialized_start=105
53 | _SERVERCONFIG._serialized_end=161
54 | _CLIENTCONFIG._serialized_start=163
55 | _CLIENTCONFIG._serialized_end=177
56 | # @@protoc_insertion_point(module_scope)
57 |
--------------------------------------------------------------------------------
/app/dashboard/src/components/DeleteUserModal.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Box,
3 | Button,
4 | chakra,
5 | Modal,
6 | ModalBody,
7 | ModalCloseButton,
8 | ModalContent,
9 | ModalFooter,
10 | ModalHeader,
11 | ModalOverlay,
12 | Spinner,
13 | Text,
14 | useToast,
15 | } from "@chakra-ui/react";
16 | import { FC, useState } from "react";
17 | import { TrashIcon } from "@heroicons/react/24/outline";
18 | import { Icon } from "./Icon";
19 | import { useDashboard } from "contexts/DashboardContext";
20 |
21 | export const DeleteIcon = chakra(TrashIcon, {
22 | baseStyle: {
23 | w: 5,
24 | h: 5,
25 | },
26 | });
27 |
28 | export type DeleteUserModalProps = {};
29 |
30 | export const DeleteUserModal: FC = () => {
31 | const [loading, setLoading] = useState(false);
32 | const { deletingUser: user, onDeletingUser, deleteUser } = useDashboard();
33 | const toast = useToast();
34 | const onClose = () => {
35 | onDeletingUser(null);
36 | };
37 | const onDelete = () => {
38 | if (user) {
39 | setLoading(true);
40 | deleteUser(user)
41 | .then(() => {
42 | toast({
43 | title: `${user.username} deleted successfully.`,
44 | status: "success",
45 | isClosable: true,
46 | position: "top",
47 | duration: 3000,
48 | });
49 | })
50 | .then(onClose)
51 | .finally(setLoading.bind(null, false));
52 | }
53 | };
54 | return (
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | Delete User
67 |
68 | {user && (
69 |
75 | Are you sure you want to delete {user.username}?
76 |
77 | )}
78 |
79 |
80 |
83 | : undefined}
89 | >
90 | Delete
91 |
92 |
93 |
94 |
95 | );
96 | };
97 |
--------------------------------------------------------------------------------