feat: switch config to JSONC + split secrets.json

Load business config as JSONC (comments + trailing commas) via an
in-repo stripper then stdlib json — no new deps. Default path prefers
config.jsonc, falls back to legacy config.json. Auth write-back targets
only sibling secrets.json so JSONC comments are never wiped; secrets
overlay merges on load. Examples, compose, Docker, docs, favsort and
tools all share the same loader. Phase A only — no DB settings migration.
This commit was merged in pull request #4.
This commit is contained in:
bot-ci committed 2026-11-02 12:58:20 +08:00
1 parent b4f0f45c4e
commit 8e7ead1669
21 files changed
+652 -182

No files matched your search

+36 -7
View File
@@ -58,28 +58,56 @@ def test_cookie_helpers():
def test_finish_updates_only_selected_account_and_rolls_back():
with tempfile.TemporaryDirectory() as tmp:
path = Path(tmp) / "config.json"
# 主配置为 JSONC(含注释);auth 只写 secrets.json
config_path = Path(tmp) / "config.jsonc"
secrets_path = Path(tmp) / "secrets.json"
original = raw_config()
path.write_text(json.dumps(original, ensure_ascii=False), encoding="utf-8")
service = AuthService(path, account="write")
config_body = (
"// keep me\n"
+ json.dumps({"folders": [], "telegram": {"enabled": False}}, ensure_ascii=False)
)
config_path.write_text(config_body, encoding="utf-8")
secrets_path.write_text(json.dumps(original, ensure_ascii=False), encoding="utf-8")
service = AuthService(config_path, account="write")
assert service.secrets_path == secrets_path
service._get_json = lambda credentials, url, auth: { # type: ignore[method-assign]
"code": 0, "data": {"isLogin": True, "mid": 99, "uname": "writer"}
}
assert service.finish(service._load_raw(), NEW) == 0
saved = json.loads(path.read_text(encoding="utf-8"))
saved = json.loads(secrets_path.read_text(encoding="utf-8"))
assert saved["bilibili"]["read"] == original["bilibili"]["read"]
assert saved["bilibili"]["write"]["mid"] == 99
assert saved["bilibili"]["write"]["csrf"] == "newcsrf"
assert cookie_value(saved["bilibili"]["write"]["cookie"], "buvid3") == "WRITE-B3"
assert path.with_name("config.json.bak").exists()
assert secrets_path.with_name("secrets.json.bak").exists()
assert config_path.read_text(encoding="utf-8") == config_body
assert "// keep me" in config_path.read_text(encoding="utf-8")
before = path.read_text(encoding="utf-8")
before = secrets_path.read_text(encoding="utf-8")
service._get_json = lambda credentials, url, auth: { # type: ignore[method-assign]
"code": -101, "data": {"isLogin": False}
}
assert service.finish(service._load_raw(), NEW) == 4
assert secrets_path.read_text(encoding="utf-8") == before
assert config_path.read_text(encoding="utf-8") == config_body
def test_finish_seeds_secrets_from_legacy_config_without_touching_it():
with tempfile.TemporaryDirectory() as tmp:
path = Path(tmp) / "config.json"
original = raw_config()
path.write_text(json.dumps(original, ensure_ascii=False), encoding="utf-8")
service = AuthService(path, account="write")
service._get_json = lambda credentials, url, auth: { # type: ignore[method-assign]
"code": 0, "data": {"isLogin": True, "mid": 99, "uname": "writer"}
}
before = path.read_text(encoding="utf-8")
assert service.finish(service._load_raw(), NEW) == 0
assert path.read_text(encoding="utf-8") == before
secrets = json.loads((Path(tmp) / "secrets.json").read_text(encoding="utf-8"))
assert secrets["bilibili"]["write"]["mid"] == 99
assert secrets["bilibili"]["read"] == original["bilibili"]["read"]
def test_qr_png():
@@ -207,12 +235,13 @@ def test_make_headers_drops_placeholder_and_non_latin1_cookies():
def main():
test_cookie_helpers()
test_finish_updates_only_selected_account_and_rolls_back()
test_finish_seeds_secrets_from_legacy_config_without_touching_it()
test_qr_png()
test_render_qr_ascii_roundtrip_and_quiet_zone()
test_run_prints_ascii_qr_without_network()
test_make_headers_matches_raw_request()
test_make_headers_drops_placeholder_and_non_latin1_cookies()
print("OK: 双账号 auth Cookie 合并、写回隔离、终端二维码与回滚测试全部通过")
print("OK: 双账号 auth Cookie 合并、secrets 写回隔离、终端二维码与回滚测试全部通过")
if __name__ == "__main__":