fix: panel init without werkzeug; stop tracking .env
Use stdlib pbkdf2 for admin passwords so init_db works reliably. Remove .env from git to avoid pull conflicts on VPS. Verify flask install before database init. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,14 +0,0 @@
|
|||||||
# VPS 环境配置(66.hyf2.cc @ 47.76.87.111)
|
|
||||||
# 部署路径:/opt/jiedian
|
|
||||||
|
|
||||||
VPS_IP=47.76.87.111
|
|
||||||
DOMAIN=66.hyf2.cc
|
|
||||||
ACME_EMAIL=admin@hyf2.cc
|
|
||||||
REALITY_SERVER_NAME=www.microsoft.com
|
|
||||||
|
|
||||||
PANEL_USERNAME=dekun
|
|
||||||
PANEL_PASSWORD=Woaini521@
|
|
||||||
|
|
||||||
REALITY_PRIVATE_KEY=IPKtaw1aVb4fS0TPcimu8zwaVGml-JJ5H1rj-_TFQHM
|
|
||||||
REALITY_PUBLIC_KEY=51H_ikqYdDRgCpjq3pvMYNbqrX8S3zuow1UEjqTN-nI
|
|
||||||
REALITY_SHORT_ID=e126b4ef9d36adfc
|
|
||||||
@@ -2,6 +2,9 @@
|
|||||||
client/generated/
|
client/generated/
|
||||||
data/
|
data/
|
||||||
panel/venv/
|
panel/venv/
|
||||||
|
|
||||||
|
# 本地环境配置(勿提交密钥)
|
||||||
|
.env
|
||||||
# 临时文件
|
# 临时文件
|
||||||
*.log
|
*.log
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|||||||
+25
-6
@@ -1,16 +1,36 @@
|
|||||||
"""SQLite 数据库:管理员账号与节点。"""
|
"""SQLite 数据库:管理员账号与节点。"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
import hmac
|
||||||
import os
|
import os
|
||||||
import secrets
|
import secrets
|
||||||
import sqlite3
|
import sqlite3
|
||||||
import subprocess
|
import subprocess
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from werkzeug.security import check_password_hash, generate_password_hash
|
|
||||||
|
|
||||||
ROOT = Path(os.environ.get("JIEDIAN_ROOT", Path(__file__).resolve().parents[1]))
|
ROOT = Path(os.environ.get("JIEDIAN_ROOT", Path(__file__).resolve().parents[1]))
|
||||||
DB_FILE = ROOT / "data" / "nodes.db"
|
DB_FILE = ROOT / "data" / "nodes.db"
|
||||||
|
_PBKDF2_ITERATIONS = 600000
|
||||||
|
|
||||||
|
|
||||||
|
def _hash_password(password: str) -> str:
|
||||||
|
salt = secrets.token_hex(16)
|
||||||
|
digest = hashlib.pbkdf2_hmac(
|
||||||
|
"sha256", password.encode(), salt.encode(), _PBKDF2_ITERATIONS
|
||||||
|
)
|
||||||
|
return f"pbkdf2:sha256:{_PBKDF2_ITERATIONS}${salt}${digest.hex()}"
|
||||||
|
|
||||||
|
|
||||||
|
def _verify_password(stored: str, password: str) -> bool:
|
||||||
|
if not stored or stored.count("$") < 2:
|
||||||
|
return False
|
||||||
|
method, salt, expected = stored.split("$", 2)
|
||||||
|
if not method.startswith("pbkdf2:sha256:"):
|
||||||
|
return False
|
||||||
|
iterations = int(method.rsplit(":", 1)[1])
|
||||||
|
digest = hashlib.pbkdf2_hmac("sha256", password.encode(), salt.encode(), iterations)
|
||||||
|
return hmac.compare_digest(digest.hex(), expected)
|
||||||
|
|
||||||
|
|
||||||
def connect() -> sqlite3.Connection:
|
def connect() -> sqlite3.Connection:
|
||||||
@@ -49,7 +69,7 @@ def init_db(env: dict[str, str]) -> None:
|
|||||||
conn.execute("DELETE FROM admin")
|
conn.execute("DELETE FROM admin")
|
||||||
conn.execute(
|
conn.execute(
|
||||||
"INSERT INTO admin (username, password_hash) VALUES (?, ?)",
|
"INSERT INTO admin (username, password_hash) VALUES (?, ?)",
|
||||||
(username, generate_password_hash(password)),
|
(username, _hash_password(password)),
|
||||||
)
|
)
|
||||||
|
|
||||||
count = conn.execute("SELECT COUNT(*) AS c FROM nodes").fetchone()["c"]
|
count = conn.execute("SELECT COUNT(*) AS c FROM nodes").fetchone()["c"]
|
||||||
@@ -72,7 +92,7 @@ def verify_admin(username: str, password: str) -> bool:
|
|||||||
conn.close()
|
conn.close()
|
||||||
if row is None:
|
if row is None:
|
||||||
return False
|
return False
|
||||||
return check_password_hash(row["password_hash"], password)
|
return _verify_password(row["password_hash"], password)
|
||||||
|
|
||||||
|
|
||||||
def list_nodes() -> list[dict]:
|
def list_nodes() -> list[dict]:
|
||||||
@@ -117,7 +137,6 @@ def node_count() -> int:
|
|||||||
|
|
||||||
|
|
||||||
def _generate_credentials() -> tuple[str, str]:
|
def _generate_credentials() -> tuple[str, str]:
|
||||||
sb = "sing-box"
|
uuid = subprocess.check_output(["sing-box", "generate", "uuid"], text=True).strip()
|
||||||
uuid = subprocess.check_output([sb, "generate", "uuid"], text=True).strip()
|
|
||||||
hy2 = secrets.token_urlsafe(18)[:24]
|
hy2 = secrets.token_urlsafe(18)[:24]
|
||||||
return uuid, hy2
|
return uuid, hy2
|
||||||
|
|||||||
@@ -140,7 +140,10 @@ nginx -t && systemctl enable nginx && systemctl restart nginx
|
|||||||
|
|
||||||
log "安装 Python 面板依赖 ..."
|
log "安装 Python 面板依赖 ..."
|
||||||
python3 -m venv "$ROOT_DIR/panel/venv"
|
python3 -m venv "$ROOT_DIR/panel/venv"
|
||||||
|
"$ROOT_DIR/panel/venv/bin/pip" install -q --upgrade pip
|
||||||
"$ROOT_DIR/panel/venv/bin/pip" install -q -r "$ROOT_DIR/panel/requirements.txt"
|
"$ROOT_DIR/panel/venv/bin/pip" install -q -r "$ROOT_DIR/panel/requirements.txt"
|
||||||
|
"$ROOT_DIR/panel/venv/bin/python" -c "import flask" \
|
||||||
|
|| err "面板依赖安装失败,请检查网络后重试"
|
||||||
|
|
||||||
log "初始化节点数据库 ..."
|
log "初始化节点数据库 ..."
|
||||||
"$ROOT_DIR/panel/venv/bin/python" "$ROOT_DIR/panel/init_db.py"
|
"$ROOT_DIR/panel/venv/bin/python" "$ROOT_DIR/panel/init_db.py"
|
||||||
|
|||||||
Reference in New Issue
Block a user