Fix: 浏览器不再负责历史记录

This commit is contained in:
XOF
2025-11-29 14:52:52 +08:00
parent 079ce62ff0
commit 7dc556e4d0

57
main.go
View File

@@ -26,6 +26,7 @@ type Task struct {
LastCheck time.Time `json:"last_check"` LastCheck time.Time `json:"last_check"`
Status string `json:"status"` Status string `json:"status"`
Notified bool `json:"notified"` Notified bool `json:"notified"`
History []HistoryItem `json:"history"`
} }
type Config struct { type Config struct {
@@ -36,6 +37,11 @@ type Config struct {
NotifyEnabled bool `json:"notify_enabled"` NotifyEnabled bool `json:"notify_enabled"`
} }
type HistoryItem struct {
State string `json:"state"`
Time time.Time `json:"time"`
}
var ( var (
tasks = make(map[string]*Task) tasks = make(map[string]*Task)
config = &Config{Interval: 60, Timeout: 20, NotifyEnabled: true} config = &Config{Interval: 60, Timeout: 20, NotifyEnabled: true}
@@ -248,46 +254,24 @@ func handleIndex(w http.ResponseWriter, r *http.Request) {
const token = localStorage.getItem('token') || prompt('请输入访问令牌:'); const token = localStorage.getItem('token') || prompt('请输入访问令牌:');
if(token) localStorage.setItem('token', token); if(token) localStorage.setItem('token', token);
const headers = {'Authorization': 'Bearer ' + token}; const headers = {'Authorization': 'Bearer ' + token};
let historyData = {};
const MAX_BARS = 90;
function initHistory(id) {
if(!historyData[id]) {
historyData[id] = Array(MAX_BARS).fill({state: 'unknown', time: null});
}
}
function updateHistory(id, state, time) {
initHistory(id);
historyData[id].shift();
historyData[id].push({state, time});
}
function loadTasks() { function loadTasks() {
fetch('/api/tasks', {headers}) fetch('/api/tasks', {headers})
.then(r => r.json()) .then(r => r.json())
.then(tasks => { .then(tasks => {
tasks.forEach(t => {
initHistory(t.id);
const state = t.status === 'ok' ? (t.in_stock ? 'stock' : 'no-stock') : (t.status === 'error' ? 'error' : 'unknown');
const lastState = historyData[t.id][MAX_BARS - 1];
if(!lastState.time || lastState.time !== t.last_check) {
updateHistory(t.id, state, t.last_check || Date.now());
}
});
const tbody = document.querySelector('#taskTable tbody'); const tbody = document.querySelector('#taskTable tbody');
tbody.innerHTML = tasks.map(t => { tbody.innerHTML = tasks.map(t => {
const statusClass = t.status || 'checking'; const statusClass = t.status || 'checking';
const stockText = t.in_stock ? '<span class="stock-yes">有货</span>' : '<span class="stock-no">无货</span>'; const stockText = t.in_stock ? '<span class="stock-yes">有货</span>' : '<span class="stock-no">无货</span>';
const lastCheck = t.last_check ? new Date(t.last_check).toLocaleString('zh-CN') : '-'; const lastCheck = t.last_check ? new Date(t.last_check).toLocaleString('zh-CN') : '-';
const history = historyData[t.id] || [];
const history = t.history || [];
const uptimeBar = history.map(h => { const uptimeBar = history.map(h => {
const time = h.time ? new Date(h.time).toLocaleString('zh-CN') : '等待检测'; const time = new Date(h.time).toLocaleString('zh-CN');
const label = h.state === 'stock' ? '有货' : h.state === 'no-stock' ? '无货' : h.state === 'error' ? '错误' : '未知'; const label = h.state === 'stock' ? '有货' : h.state === 'no-stock' ? '无货' : h.state === 'error' ? '错误' : '未知';
return '<div class="uptime-item ' + h.state + '"><span class="uptime-tooltip">' + time + '<br>' + label + '</span></div>'; return '<div class="uptime-item ' + h.state + '"><span class="uptime-tooltip">' + time + '<br>' + label + '</span></div>';
}).join(''); }).join('');
return '<tr>' + return '<tr>' +
'<td class="status-cell"><span class="status ' + statusClass + '"></span></td>' + '<td class="status-cell"><span class="status ' + statusClass + '"></span></td>' +
'<td>' + t.name + '</td>' + '<td>' + t.name + '</td>' +
@@ -359,7 +343,6 @@ func handleIndex(w http.ResponseWriter, r *http.Request) {
if(!confirm('确认删除?')) return; if(!confirm('确认删除?')) return;
fetch('/api/task?id=' + id, {method: 'DELETE', headers}) fetch('/api/task?id=' + id, {method: 'DELETE', headers})
.then(() => { .then(() => {
delete historyData[id];
loadTasks(); loadTasks();
}); });
} }
@@ -490,8 +473,6 @@ func checker(ctx context.Context) {
} }
func checkTask(task *Task) { func checkTask(task *Task) {
task.Status = "checking"
body, err := fetch(task.URL) body, err := fetch(task.URL)
now := time.Now() now := time.Now()
@@ -504,6 +485,16 @@ func checkTask(task *Task) {
inStock = !strings.Contains(body, task.OutOfStock) inStock = !strings.Contains(body, task.OutOfStock)
} }
state := "unknown"
if status == "ok" {
state = "no-stock"
if inStock {
state = "stock"
}
} else if status == "error" {
state = "error"
}
mu.Lock() mu.Lock()
wasInStock := task.InStock wasInStock := task.InStock
wasNotified := task.Notified wasNotified := task.Notified
@@ -511,11 +502,19 @@ func checkTask(task *Task) {
task.InStock = inStock task.InStock = inStock
task.LastCheck = now task.LastCheck = now
if task.History == nil {
task.History = []HistoryItem{}
}
task.History = append(task.History, HistoryItem{State: state, Time: now})
if len(task.History) > 90 {
task.History = task.History[len(task.History)-90:]
}
if !inStock { if !inStock {
task.Notified = false task.Notified = false
} }
mu.Unlock() mu.Unlock()
saveTasks() saveTasks()
configMu.RLock() configMu.RLock()
notifyEnabled := config.NotifyEnabled notifyEnabled := config.NotifyEnabled