fix(auth): 修复登录后反复报未配置账号并跳回登录页的死循环
根因:ny_save_config 只写不校验,Db::settingsSet 内部吞异常且返回 void。 data/ 目录不可写时登录页照常显示登录成功,但下一请求读到的账号仍是空的 → 抛未配置卷王登录账号 → 前端 auth 打标 → 跳登录页 → 无限循环。 修复: - Db::settingsSet 改返回 bool 并回读校验;新增 settingsDelete - ny_save_config 双写后均做回读校验,失败写入 ny_config_save_error() - 新增 ny_config_diag()/ny_session_usable(),暴露来源/可写性/是否已存账号 - ny_load_config 把 null 账号强制成空字符串(脏数据不再伪装成未配置) - login.php 存凭据后回读确认,存不住/会话不可写直接报错而不是假成功 - login.php 账号为空时页面直接给出 data 目录写权限提示 - config_save 合并逻辑抽成 ny_apply_config_input:账号/密码不许被改空 - 鉴权失败响应带 extra.diag,前端 401 也算 auth - 前端防死循环:刚登录 120s 内或已跳 2 次则停止自动跳转,弹窗给出真实原因+诊断 测试:新增 tests/test-config-persist.php(29) 与 tools/test-login-loop.js(19); run-all.sh 纳入两者。全套 148+48=196 项通过。
This commit is contained in:
+40
-3
@@ -170,12 +170,18 @@ class Db
|
||||
// ---------------- 设置 ----------------
|
||||
|
||||
|
||||
/** SQLite 用 ON CONFLICT 做 upsert(其它方言见历史版本) */
|
||||
public static function settingsSet(string $k, string $v): void
|
||||
/**
|
||||
* SQLite 用 ON CONFLICT 做 upsert(其它方言见历史版本)
|
||||
*
|
||||
* 返回是否真的写成功:以前这里 void + 内部吞异常,导致"配置没落盘但上层以为成功"
|
||||
* (典型后果:登录成功→下一请求仍报"未配置账号"→反复跳登录页的死循环)。
|
||||
* 因此改为回读校验后返回 bool,并把失败原因放进 self::lastError()。
|
||||
*/
|
||||
public static function settingsSet(string $k, string $v): bool
|
||||
{
|
||||
$pdo = self::pdo();
|
||||
if (!$pdo) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
$p = self::prefix();
|
||||
@@ -184,6 +190,37 @@ class Db
|
||||
ON CONFLICT(k) DO UPDATE SET v=excluded.v, updated_at=excluded.updated_at"
|
||||
);
|
||||
$st->execute([$k, $v, time()]);
|
||||
} catch (Throwable $e) {
|
||||
self::$err = $e->getMessage();
|
||||
return false;
|
||||
}
|
||||
// 回读校验:磁盘满 / 目录不可写 / 只读挂载时 execute 可能不报错但实际未落盘
|
||||
try {
|
||||
$p = self::prefix();
|
||||
$st = $pdo->prepare("SELECT v FROM {$p}settings WHERE k = ?");
|
||||
$st->execute([$k]);
|
||||
$got = $st->fetchColumn();
|
||||
if ($got === false || (string)$got !== $v) {
|
||||
self::$err = '写入后回读不一致(可能磁盘已满或数据库只读)';
|
||||
return false;
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
self::$err = $e->getMessage();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function settingsDelete(string $k): void
|
||||
{
|
||||
$pdo = self::pdo();
|
||||
if (!$pdo) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
$p = self::prefix();
|
||||
$st = $pdo->prepare("DELETE FROM {$p}settings WHERE k = ?");
|
||||
$st->execute([$k]);
|
||||
} catch (Throwable $e) {
|
||||
self::$err = $e->getMessage();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user