Add optional expiration that starts on first config use.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
orohi
2026-07-26 05:07:50 +03:00
co-authored by Cursor
parent 16d14a7256
commit 745b5e5d8c
9 changed files with 245 additions and 35 deletions
+6
View File
@@ -93,6 +93,12 @@ def init_schema():
cur.execute(
"ALTER TABLE user_connections ADD COLUMN IF NOT EXISTS xui_panel_id TEXT NOT NULL DEFAULT ''"
)
cur.execute(
"ALTER TABLE users ADD COLUMN IF NOT EXISTS expire_after_first_use BOOLEAN NOT NULL DEFAULT FALSE"
)
cur.execute(
"ALTER TABLE users ADD COLUMN IF NOT EXISTS expiration_days INTEGER NOT NULL DEFAULT 0"
)
conn.commit()
_schema_ready = True
logger.info('PostgreSQL schema ready')
+2
View File
@@ -28,6 +28,8 @@ CREATE TABLE IF NOT EXISTS users (
traffic_reset_strategy TEXT NOT NULL DEFAULT 'never',
last_reset_at TIMESTAMPTZ,
expiration_date TIMESTAMPTZ,
expire_after_first_use BOOLEAN NOT NULL DEFAULT FALSE,
expiration_days INTEGER NOT NULL DEFAULT 0,
remnawave_uuid TEXT,
xui_email TEXT,
share_enabled BOOLEAN NOT NULL DEFAULT FALSE,
+8 -3
View File
@@ -151,6 +151,8 @@ def _row_to_user(row) -> dict:
'traffic_reset_strategy': row['traffic_reset_strategy'] or 'never',
'last_reset_at': _ts_iso(row['last_reset_at']),
'expiration_date': _ts_iso(row['expiration_date']),
'expire_after_first_use': bool(row.get('expire_after_first_use') if hasattr(row, 'get') else row['expire_after_first_use']),
'expiration_days': int((row.get('expiration_days') if hasattr(row, 'get') else row['expiration_days']) or 0),
'remnawave_uuid': row['remnawave_uuid'],
'xui_email': row.get('xui_email'),
'share_enabled': bool(row['share_enabled']),
@@ -222,7 +224,8 @@ def load_data() -> dict:
'SELECT id, username, password_hash, role, enabled, created_at, '
'telegram_id, email, description, traffic_limit, traffic_used, '
'traffic_total, traffic_reset_strategy, last_reset_at, '
'expiration_date, remnawave_uuid, xui_email, share_enabled, share_token, '
'expiration_date, expire_after_first_use, expiration_days, '
'remnawave_uuid, xui_email, share_enabled, share_token, '
'share_password_hash FROM users ORDER BY created_at NULLS LAST, username'
)
users = [_row_to_user(r) for r in cur.fetchall()]
@@ -316,10 +319,10 @@ def save_data(data: dict) -> None:
'id, username, password_hash, role, enabled, created_at, '
'telegram_id, email, description, traffic_limit, traffic_used, '
'traffic_total, traffic_reset_strategy, last_reset_at, '
'expiration_date, remnawave_uuid, xui_email, share_enabled, share_token, '
'expiration_date, expire_after_first_use, expiration_days, remnawave_uuid, xui_email, share_enabled, share_token, '
'share_password_hash'
') VALUES ('
'%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s'
'%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s'
')',
(
_as_uuid(user['id']),
@@ -337,6 +340,8 @@ def save_data(data: dict) -> None:
user.get('traffic_reset_strategy') or 'never',
_parse_ts(user.get('last_reset_at')),
_parse_ts(user.get('expiration_date')),
bool(user.get('expire_after_first_use', False)),
int(user.get('expiration_days') or 0),
user.get('remnawave_uuid'),
user.get('xui_email'),
bool(user.get('share_enabled', False)),