Skip to content

Commit

Permalink
Upgrade transmission option and add alpn to inbound configs (#51)
Browse files Browse the repository at this point in the history
* Add httpupgrade and other network to inbound config

* Fix json error

* Add alpn to subscription URL

* Improve default values in config.py
  • Loading branch information
eloravpn authored Nov 29, 2024
1 parent bd4e03f commit 16926f3
Show file tree
Hide file tree
Showing 11 changed files with 157 additions and 16 deletions.
13 changes: 6 additions & 7 deletions src/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,12 @@
XUI_DB_PATH = config("XUI_DB_URL", default="./x-ui.db")
OLD_BOT_DB_PATH = config("OLD_BOT_DB_PATH", default="./v2raybot.sqlite3")

ENABLE_SYNC_ACCOUNTS = config("ENABLE_SYNC_ACCOUNTS", cast=bool, default=False)
ENABLE_SYNC_ACCOUNTS = config("ENABLE_SYNC_ACCOUNTS", cast=bool, default=True)
ENABLE_REMOVE_DISABLED_ACCOUNTS = config(
"ENABLE_REMOVE_DISABLED_ACCOUNTS", cast=bool, default=False
)
REMOVE_DISABLED_ACCOUNTS_LAST_DAYS = config(
"REMOVE_DISABLED_ACCOUNTS_LAST_DAYS", cast=int, default=7
"REMOVE_DISABLED_ACCOUNTS_LAST_DAYS", cast=int, default=1
)
REMOVE_UNUSED_TEST_ACCOUNTS_LAST_DAYS = config(
"REMOVE_UNUSED_TEST_ACCOUNTS_LAST_DAYS", cast=int, default=1
Expand All @@ -97,9 +97,9 @@
)

ENABLE_NOTIFICATION_SEND_PENDING_JOBS = config(
"ENABLE_NOTIFICATION_SEND_PENDING_JOBS", cast=bool, default=False
"ENABLE_NOTIFICATION_SEND_PENDING_JOBS", cast=bool, default=True
)
ENABLE_NOTIFICATION_JOBS = config("ENABLE_NOTIFICATION_JOBS", cast=bool, default=False)
ENABLE_NOTIFICATION_JOBS = config("ENABLE_NOTIFICATION_JOBS", cast=bool, default=True)
REVIEW_ACCOUNTS_INTERVAL = config("REVIEW_ACCOUNTS_INTERVAL", cast=int, default=60)
SEND_PENDING_NOTIFICATION_INTERVAL = config(
"SEND_PENDING_NOTIFICATION_INTERVAL", cast=int, default=60
Expand All @@ -108,16 +108,15 @@
SEND_PENDING_NOTIFICATION_LIMIT = config(
"SEND_PENDING_NOTIFICATION_LIMIT", cast=int, default=60
)

USED_TRAFFIC_NOTIFICATION_INTERVAL = config(
"USED_TRAFFIC_NOTIFICATION_INTERVAL", cast=int, default=3600
"USED_TRAFFIC_NOTIFICATION_INTERVAL", cast=int, default=600
)
EXPIRE_TIME_NOTIFICATION_INTERVAL = config(
"EXPIRE_TIME_NOTIFICATION_INTERVAL", cast=int, default=10800
)
SYNC_ACCOUNTS_INTERVAL = config("SYNC_ACCOUNTS_INTERVAL", cast=int, default=60)
SYNC_ACCOUNTS_TRAFFIC_INTERVAL = config(
"SYNC_ACCOUNTS_TRAFFIC_INTERVAL", cast=int, default=3600
"SYNC_ACCOUNTS_TRAFFIC_INTERVAL", cast=int, default=600
)
GLOBAL_TRAFFIC_RATIO = config("GLOBAL_TRAFFIC_RATIO", cast=float, default=1.0)

Expand Down
8 changes: 6 additions & 2 deletions src/config_setting/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ async def update_config(

@router.post("/settings/bulk")
async def update_bulk_config(
configs: ConfigSettingsBulkUpdate, db: Session = Depends(get_db),admin: Admin = Depends(Admin.get_current)
configs: ConfigSettingsBulkUpdate,
db: Session = Depends(get_db),
admin: Admin = Depends(Admin.get_current),
):
"""Update or create configuration"""
for key, value in configs.settings.items():
Expand All @@ -66,7 +68,9 @@ async def update_bulk_config(


@router.delete("/settings/{key}")
async def delete_config(key: str, db: Session = Depends(get_db),admin: Admin = Depends(Admin.get_current)):
async def delete_config(
key: str, db: Session = Depends(get_db), admin: Admin = Depends(Admin.get_current)
):
"""Delete configuration"""
if not delete_setting(db, key):
raise HTTPException(status_code=404, detail="Configuration not found")
Expand Down
31 changes: 31 additions & 0 deletions src/inbound_configs/models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import json
from datetime import datetime

from sqlalchemy import (
Expand Down Expand Up @@ -60,7 +61,37 @@ class InboundConfig(Base):
default=InboundNetwork.ws.value,
)

alpn = Column(String(400))

type = Column(Enum(InboundType), nullable=False, default=InboundType.default.value)

created_at = Column(DateTime, default=datetime.utcnow)
modified_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)

@property
def alpns(self):
"""Deserialize JSON string into a Python list."""
if self.alpn:
try:
return json.loads(self.alpn)
except json.JSONDecodeError as e:
raise ValueError(
f"Invalid JSON stored in alpn: {self.alpn}. Error: {str(e)}"
)
return None

@alpns.setter
def alpns(self, value):
"""Serialize a Python list into a JSON string for alpn."""
if value is not None:
# Ensure the list is in a correct format
if isinstance(value, str):
# If the value is a string like "{h2,h3}", convert it into a list
value = [item.strip() for item in value.strip("{}").split(",")]

try:
self.alpn = json.dumps(value) # Store as a valid JSON string
except (TypeError, ValueError) as e:
raise ValueError(f"Invalid value for alpns: {value}. Error: {str(e)}")
else:
self.alpn = None # Set to None if no value is provided
2 changes: 2 additions & 0 deletions src/inbound_configs/router.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import json

from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import Session
Expand Down
1 change: 1 addition & 0 deletions src/inbound_configs/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class InboundConfigBase(BaseModel):
pbk: Optional[str]
sid: Optional[str]
spx: Optional[str]
alpns: Optional[List[str]] = None
enable: bool
develop: bool
network: InboundNetwork = InboundNetwork.ws
Expand Down
4 changes: 4 additions & 0 deletions src/inbound_configs/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ def create_inbound_config(
spx=inbound_config.spx,
sid=inbound_config.sid,
pbk=inbound_config.pbk,
alpns=inbound_config.alpns,
enable=inbound_config.enable,
develop=inbound_config.develop,
security=inbound_config.security,
Expand Down Expand Up @@ -71,6 +72,7 @@ def copy_inbound_config(db: Session, db_inbound_config: InboundConfig):
spx=db_inbound_config.spx,
sid=db_inbound_config.sid,
pbk=db_inbound_config.pbk,
alpns=db_inbound_config.alpns,
enable=db_inbound_config.enable,
develop=True,
security=db_inbound_config.security,
Expand All @@ -86,6 +88,7 @@ def copy_inbound_config(db: Session, db_inbound_config: InboundConfig):
def update_inbound_config(
db: Session, db_inbound_config: InboundConfig, modify: InboundConfigModify
):

db_inbound_config.inbound_id = modify.inbound_id
db_inbound_config.remark = modify.remark
db_inbound_config.port = modify.port
Expand All @@ -97,6 +100,7 @@ def update_inbound_config(
db_inbound_config.sid = modify.sid
db_inbound_config.pbk = modify.pbk
db_inbound_config.spx = modify.spx
db_inbound_config.alpns = modify.alpns
db_inbound_config.finger_print = modify.finger_print
db_inbound_config.enable = modify.enable
db_inbound_config.develop = modify.develop
Expand Down
10 changes: 10 additions & 0 deletions src/inbounds/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ class InboundNetwork(str, Enum):
ws = "ws"
tcp = "tcp"
grpc = "grpc"
kcp = "kcp"
http = "http"
httpupgrade = "httpupgrade"
splithttp = "splithttp"


class ALPN(str, Enum):
h2 = "h2"
h3 = "h3"
http1 = "http/1.1"


class InboundFingerPrint(str, Enum):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
"""Add alpn and other networks to InboundConfig
Revision ID: a00786a4da47
Revises: c78517599f01
Create Date: 2024-11-27 20:50:20.606559
"""

from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision = "a00786a4da47"
down_revision = "c78517599f01"
branch_labels = None
depends_on = None

# Describing of enum
enum_name = "inboundnetwork"
temp_enum_name = f"temp_{enum_name}"
old_values = ("tcp", "ws", "grpc")
new_values = ("kcp", "http", "httpupgrade", "splithttp", *old_values)

old_type = sa.Enum(*old_values, name=enum_name)
new_type = sa.Enum(*new_values, name=enum_name)
temp_type = sa.Enum(*new_values, name=temp_enum_name)

# Describing of table
table_name = "inbound_config"
column_name = "network"


def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.add_column(
"inbound_config", sa.Column("alpn", sa.String(length=400), nullable=True)
)

# temp type to use instead of old one
temp_type.create(op.get_bind(), checkfirst=False)
op.execute("ALTER TABLE inbound_config ALTER COLUMN network DROP DEFAULT")

# changing of column type from old enum to new one.
# SQLite will create temp table for this
with op.batch_alter_table(table_name) as batch_op:
batch_op.alter_column(
column_name,
existing_type=old_type,
type_=temp_type,
existing_nullable=False,
postgresql_using=f"{column_name}::text::{temp_enum_name}",
)

# remove old enum, create new enum
old_type.drop(op.get_bind(), checkfirst=False)
new_type.create(op.get_bind(), checkfirst=False)

# changing of column type from temp enum to new one.
# SQLite will create temp table for this
with op.batch_alter_table(table_name) as batch_op:
batch_op.alter_column(
column_name,
existing_type=temp_type,
type_=new_type,
existing_nullable=False,
postgresql_using=f"{column_name}::text::{enum_name}",
)

# remove temp enum
temp_type.drop(op.get_bind(), checkfirst=False)
# ### end Alembic commands ###


def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("inbound_config", "alpn")
# ### end Alembic commands ###
1 change: 1 addition & 0 deletions src/subscription/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ def sub(
else None
),
remark=remark,
alpns=inbound_config.alpns,
)
rows.append(link)

Expand Down
19 changes: 14 additions & 5 deletions src/users/schemas.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import secrets
import string
from datetime import datetime
from typing import List, TYPE_CHECKING, Optional

Expand Down Expand Up @@ -31,18 +33,25 @@ def validate_username(cls, username: str):


class UserCreate(UserBase):
password: str
password: Optional[str] = None
referral_user_id: Optional[int]

@property
def hashed_password(self):
return pwd_context.hash(self.password)

@validator("password")
def validate_password(cls, password: str):
if not password:
raise ValueError("Password can not be null")
@classmethod
def generate_password(cls, length: int = 12) -> str:
chars = string.ascii_letters + string.digits + "!@#$%^&*"
return "".join(secrets.choice(chars) for _ in range(length))

@validator("password")
def validate_password(cls, password: str = None):
print(password)
if password is None:
return cls.generate_password()
if len(password) < 8:
raise ValueError("Password must be at least 8 characters long")
return password


Expand Down
7 changes: 5 additions & 2 deletions src/utils/xray.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import urllib.parse
from typing import List

from src.inbounds.schemas import InboundSecurity

Expand All @@ -18,9 +19,8 @@ def generate_vless_config(
pbk: str,
flow: str,
network_type: str = "ws",
alpns: List[str] = None,
):
alpn = "h2,http/1.1,h3"

prefix_txt = "%s@%s:%s" % (uuid, address, port)
prefix = "vless://" + prefix_txt
postfix_list = [
Expand Down Expand Up @@ -49,6 +49,9 @@ def generate_vless_config(
if spx:
postfix_list.append("spx=%s" % urllib.parse.quote(spx.encode("utf8")))
# postfix_list.append('alpn=%s' % urllib.parse.quote(alpn.encode('utf8')))
if alpns:
alpns_str = ",".join(alpns) if alpns else None
postfix_list.append("alpn=%s" % urllib.parse.quote(alpns_str.encode("utf8")))
link = (
prefix
+ "?"
Expand Down

0 comments on commit 16926f3

Please sign in to comment.