fix(docker): resolve credentials save permission issue on NAS platforms

Made-with: Cursor
This commit is contained in:
tmwgsicp 2026-03-25 10:08:42 +08:00
parent ad62e8b8bb
commit 9cfa0ac5b1
3 changed files with 78 additions and 26 deletions

View File

@ -21,7 +21,7 @@ FROM python:3.11-slim
LABEL maintainer="tmwgsicp"
LABEL description="WeChat Official Account Article Download API with RSS Support"
LABEL version="1.0.3"
LABEL version="1.0.4"
WORKDIR /app

View File

@ -6,6 +6,11 @@
# 2. Edit .env and set SITE_URL to your actual URL
# 3. Run: docker-compose up -d
# 4. Visit http://localhost:5000/login.html to scan QR code
#
# Note for NAS users (Synology/QNAP):
# If you encounter permission issues, run on NAS:
# - chmod -R 777 ./data
# - Credentials are automatically saved to ./data directory
services:
wechat-api:
@ -17,10 +22,10 @@ services:
ports:
- "5000:5000"
volumes:
# Persist SQLite database
# Persist SQLite database and credentials
- ./data:/app/data
# Config file (writable - login saves credentials here)
- ./.env:/app/.env
# Config file (read-only - credentials saved to data/)
- ./.env:/app/.env:ro
environment:
- TZ=Asia/Shanghai
healthcheck:

View File

@ -34,12 +34,31 @@ class AuthManager:
self.base_dir = Path(__file__).parent.parent
self.env_path = self.base_dir / ".env"
# Docker环境下的凭证文件存储在data目录权限更可靠
self.credentials_file = self.base_dir / "data" / ".credentials.json"
# 加载环境变量
self._load_credentials()
self._initialized = True
def _load_credentials(self):
"""从.env文件加载凭证"""
"""
从多个来源加载凭证优先级
1. data/.credentials.json (Docker环境推荐)
2. .env 文件 (本地部署)
3. 环境变量
"""
# 先尝试从 JSON 凭证文件加载Docker 环境)
if self.credentials_file.exists():
try:
import json
with open(self.credentials_file, 'r', encoding='utf-8') as f:
self.credentials = json.load(f)
return
except Exception as e:
print(f"Warning: Failed to load credentials from {self.credentials_file}: {e}")
# 回退到 .env 文件(本地部署)
if self.env_path.exists():
load_dotenv(self.env_path, override=True)
@ -54,7 +73,9 @@ class AuthManager:
def save_credentials(self, token: str, cookie: str, fakeid: str,
nickname: str, expire_time: int) -> bool:
"""
保存凭证到.env文件
保存凭证支持双存储策略
1. 优先保存到 data/.credentials.json (Docker环境推荐权限可靠)
2. 同时尝试保存到 .env (本地部署兼容)
Args:
token: 微信Token
@ -66,21 +87,33 @@ class AuthManager:
Returns:
保存是否成功
"""
# 更新内存中的凭证
self.credentials.update({
"token": token,
"cookie": cookie,
"fakeid": fakeid,
"nickname": nickname,
"expire_time": expire_time
})
success = False
# 策略1: 保存到 data/.credentials.json (Docker 环境优先)
try:
import json
self.credentials_file.parent.mkdir(parents=True, exist_ok=True)
with open(self.credentials_file, 'w', encoding='utf-8') as f:
json.dump(self.credentials, f, indent=2, ensure_ascii=False)
print(f"[OK] 凭证已保存到: {self.credentials_file}")
success = True
except Exception as e:
print(f"[WARN] 无法保存到凭证文件: {e}")
# 策略2: 同时尝试保存到 .env 文件(本地部署兼容)
try:
# 更新内存中的凭证
self.credentials.update({
"token": token,
"cookie": cookie,
"fakeid": fakeid,
"nickname": nickname,
"expire_time": expire_time
})
# 确保.env文件存在
if not self.env_path.exists():
self.env_path.touch()
# 保存到.env文件
env_file = str(self.env_path)
set_key(env_file, "WECHAT_TOKEN", token)
set_key(env_file, "WECHAT_COOKIE", cookie)
@ -88,11 +121,17 @@ class AuthManager:
set_key(env_file, "WECHAT_NICKNAME", nickname)
set_key(env_file, "WECHAT_EXPIRE_TIME", str(expire_time))
print(f"✅ 凭证已保存到: {self.env_path}")
return True
print(f"[OK] 凭证已同步到: {self.env_path}")
success = True
except Exception as e:
print(f"❌ 保存凭证失败: {e}")
print(f"[WARN] 无法写入 .env 文件 (Docker环境正常): {e}")
# Docker 环境下 .env 可能只读,不影响功能
if not success:
print(f"[ERROR] 凭证保存完全失败")
return False
return True
def get_credentials(self) -> Optional[Dict[str, any]]:
"""
@ -155,7 +194,7 @@ class AuthManager:
def clear_credentials(self) -> bool:
"""
清除凭证
清除凭证双存储都清除
Returns:
清除是否成功
@ -178,12 +217,20 @@ class AuthManager:
for key in env_keys:
os.environ.pop(key, None)
# 删除凭证文件
if self.credentials_file.exists():
self.credentials_file.unlink()
print(f"[OK] 凭证文件已删除: {self.credentials_file}")
# 清空 .env 文件中的凭证字段(保留其他配置)
if self.env_path.exists():
env_file = str(self.env_path)
for key in env_keys:
set_key(env_file, key, "")
print(f"✅ 凭证已清除: {self.env_path}")
try:
if self.env_path.exists():
env_file = str(self.env_path)
for key in env_keys:
set_key(env_file, key, "")
print(f"[OK] .env 凭证已清除: {self.env_path}")
except Exception as e:
print(f"[WARN] 无法清除 .env 文件 (Docker环境正常): {e}")
return True
except Exception as e: