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 改回 按域名取键后,这些断言确实失败)。
69 lines
2.2 KiB
PHP
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);
|