Files
SimpleNav/admin/update_all.php
T
laoyang 4ba512b147 fix: 图标文件名按「整条网址」命名,同域名不同路径不再互相覆盖
https://nas.yangyq.net/calendar 与 https://nas.yangyq.net/ 这类「同域名、
不同路径」的条目,图标文件名原先只由域名决定(md5(域名)),两条记录指向
同一个文件:改其中一个的自定义图标,另一个跟着变;保存时还会 unlink /
覆盖掉对方的文件,直接把对方搞成破图。

- 新增 icon_key($url):按整条网址取键(协议与主机名转小写、空路径补 /、
  查询串与锚点保留),语义是「不同网址 → 不同图标文件」。
- 4 处调用点全部改用它:site_save(上传图标 / 自定义地址)、fetch_meta、
  update_all;键一律取自「记录里存的那条 URL」,不用跳转后的 final_url,
  保证同一个网站每次算出来的键都一样(否则每次重下 + 留下孤儿文件)。
- download_best_icon() / save_custom_icon() 增加空键保护,避免键为空时
  所有图标都落到同一个文件名上。
- README 新增「图标文件的命名规则」一节。
- .gitattributes 统一按 LF 存放,防止 Windows 的 core.autocrlf 在下次
  checkout 时把 PHP/JS/CSS 换成 CRLF。
- e2e 新增「同域名不同路径」回归场景:用三张颜色不同的图,比对两条记录
  落盘图标的内容,验证改 A 不会波及 B;并已做反向验证(把 icon_key 改回
  按域名取键后,这些断言确实失败)。
2026-09-28 17:26:40 +08:00

69 lines
2.2 KiB
PHP

<?php
/**
* 简航 SimpleNav —— 一键“全部更新”(分批)。
* 批量刷新每个网站的名称与图标;
* · **保留** 用户自定义简称 short_name
* · **跳过** 用户自定义图标(icon_custom=1)——只刷新名称
* 前端按 offset 分批调用,避免一次请求超时。
* 返回:{ done, total, offset, updated, skipped, failed }
*/
declare(strict_types=1);
require_once __DIR__ . '/_auth.php';
require_login();
send_no_cache();
header('Content-Type: application/json; charset=utf-8');
set_time_limit(90);
$offset = max(0, (int)($_GET['offset'] ?? 0));
$limit = min(12, max(1, (int)($_GET['limit'] ?? 3)));
$pdo = db();
$total = (int)$pdo->query('SELECT COUNT(*) FROM sites')->fetchColumn();
$rows = $pdo->query('SELECT id, url, icon_custom FROM sites ORDER BY id LIMIT ' . $limit . ' OFFSET ' . $offset)->fetchAll();
$updated = 0;
$skipped = 0;
$failed = 0;
$now = date('Y-m-d H:i:s');
foreach ($rows as $row) {
$url = norm_url((string)$row['url']);
$custom = (int)$row['icon_custom'] === 1;
$meta = fetch_site_meta($url);
$name = $meta['name'] !== '' ? $meta['name'] : $meta['title'];
$sets = ['updated_at=?'];
$args = [$now];
if ($name !== '') { $sets[] = 'name=?'; $args[] = $name; }
if ($custom) {
// 用户自定义图标:绝不覆盖
$skipped++;
} else {
// 键取自记录里存的那条 URL(不用 final_url),保证每次算出来都一样
$iconFile = download_best_icon($meta['icons'], icon_key($url));
if ($iconFile !== '') { $sets[] = 'icon=?'; $args[] = $iconFile; }
if ($iconFile !== '' || $name !== '') { $updated++; } else { $failed++; }
}
// 注意:这里绝不修改 short_name(用户自定义简称)
$args[] = (int)$row['id'];
try {
$pdo->prepare('UPDATE sites SET ' . implode(',', $sets) . ' WHERE id=?')->execute($args);
} catch (Throwable $e) {
$failed++;
}
}
$next = $offset + count($rows);
echo json_encode([
'done' => $next >= $total || count($rows) === 0,
'total' => $total,
'offset' => $next,
'updated' => $updated,
'skipped' => $skipped,
'failed' => $failed,
], JSON_UNESCAPED_UNICODE);