jskelet 0.2.3 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/docs/06-cache.md +8 -1
- package/docs/en/06-caching.md +8 -1
- package/package.json +1 -1
- package/src/client/cache-panel/i18n.js +670 -0
- package/src/client/cache-panel/login.html +17 -14
- package/src/client/cache-panel/panel.css +16 -0
- package/src/client/cache-panel/panel.html +80 -79
- package/src/client/cache-panel/panel.js +201 -94
- package/src/server/cache-panel.js +104 -83
|
@@ -7,7 +7,11 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Yenileme WebSocket değil, kısa aralıklı `fetch` ile: panel nadiren ve kısa
|
|
9
9
|
* süre açık kalıyor, kalıcı bir kanal açmanın karşılığı yok.
|
|
10
|
+
*
|
|
11
|
+
* Bütün görünen metin `i18n.js` üzerinden geçiyor; sunucudan gelen cevaplar da
|
|
12
|
+
* metin değil `code` taşıyor, yani dil değişimi tek yerden hallediliyor.
|
|
10
13
|
*/
|
|
14
|
+
import { applyTranslations, languageSelect, t } from "./i18n.js";
|
|
11
15
|
|
|
12
16
|
const REFRESH_MS = 3000;
|
|
13
17
|
|
|
@@ -42,10 +46,14 @@ function formatBytes(bytes) {
|
|
|
42
46
|
*/
|
|
43
47
|
function formatDuration(seconds) {
|
|
44
48
|
const total = Math.max(0, Math.round(seconds));
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (total <
|
|
48
|
-
return `${Math.floor(total /
|
|
49
|
+
const [s, m, h, d] = [t("unit.second"), t("unit.minute"), t("unit.hour"), t("unit.day")];
|
|
50
|
+
|
|
51
|
+
if (total < 60) return `${total}${s}`;
|
|
52
|
+
if (total < 3600) return `${Math.floor(total / 60)}${m} ${total % 60}${s}`;
|
|
53
|
+
if (total < 86_400) {
|
|
54
|
+
return `${Math.floor(total / 3600)}${h} ${Math.floor((total % 3600) / 60)}${m}`;
|
|
55
|
+
}
|
|
56
|
+
return `${Math.floor(total / 86_400)}${d} ${Math.floor((total % 86_400) / 3600)}${h}`;
|
|
49
57
|
}
|
|
50
58
|
|
|
51
59
|
/**
|
|
@@ -117,14 +125,39 @@ async function act(body) {
|
|
|
117
125
|
|
|
118
126
|
if (!alive(response)) return;
|
|
119
127
|
|
|
120
|
-
const result = await response.json().catch(() =>
|
|
121
|
-
toast(
|
|
128
|
+
const result = await response.json().catch(() => null);
|
|
129
|
+
toast(describe(result), result?.ok !== false);
|
|
122
130
|
await load();
|
|
123
131
|
|
|
124
132
|
// Cloudflare ayarları değişmiş olabilir; genel bakış önbelleği atlanır.
|
|
125
133
|
if (String(body.type ?? "").startsWith("cf:")) await loadCloudflare(true);
|
|
126
134
|
}
|
|
127
135
|
|
|
136
|
+
/**
|
|
137
|
+
* Sunucu cevabını metne çevirir.
|
|
138
|
+
*
|
|
139
|
+
* `/action` dil taşımıyor: `{ ok, code, params }` dönüyor ve cümle burada
|
|
140
|
+
* kuruluyor. Redis sayımı gibi parçalı cevaplar `parts` ile geliyor, çünkü
|
|
141
|
+
* "veritabanında kaç anahtar var" bilgisi her zaman okunamıyor ve eksik parçayı
|
|
142
|
+
* cümleden düşürmek tek düzgün yol.
|
|
143
|
+
*
|
|
144
|
+
* @param {any} result
|
|
145
|
+
* @returns {string}
|
|
146
|
+
*/
|
|
147
|
+
function describe(result) {
|
|
148
|
+
if (!result) return t("toast.failed");
|
|
149
|
+
|
|
150
|
+
if (Array.isArray(result.parts)) {
|
|
151
|
+
const parts = result.parts
|
|
152
|
+
.map((/** @type {any} */ part) => t(`msg.${part.code}`, part.params))
|
|
153
|
+
.filter(Boolean);
|
|
154
|
+
if (parts.length) return parts.join(" · ");
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (result.code) return t(`msg.${result.code}`, result.params);
|
|
158
|
+
return t(result.ok ? "toast.done" : "toast.failed");
|
|
159
|
+
}
|
|
160
|
+
|
|
128
161
|
/* ------------------------------------------------------------------- çizim */
|
|
129
162
|
|
|
130
163
|
function render() {
|
|
@@ -144,16 +177,19 @@ function render() {
|
|
|
144
177
|
|
|
145
178
|
$("env").textContent = proc.env;
|
|
146
179
|
$("pid").textContent = `pid ${proc.pid}`;
|
|
147
|
-
$("uptime").textContent =
|
|
180
|
+
$("uptime").textContent = t("chip.up", { value: formatDuration(proc.uptime) });
|
|
148
181
|
$("memory").textContent = `rss ${formatBytes(proc.memory.rss)}`;
|
|
149
182
|
|
|
150
183
|
$("html-size").textContent = html.size.toLocaleString();
|
|
151
|
-
$("html-sub").textContent =
|
|
184
|
+
$("html-sub").textContent = t("card.limit", { count: html.maxEntries.toLocaleString() });
|
|
152
185
|
$("html-bytes").textContent = formatBytes(html.bytes);
|
|
153
|
-
$("html-stale").textContent =
|
|
186
|
+
$("html-stale").textContent = t("card.stale", { count: html.stale });
|
|
154
187
|
|
|
155
188
|
$("data-size").textContent = data.size.toLocaleString();
|
|
156
|
-
$("data-sub").textContent =
|
|
189
|
+
$("data-sub").textContent = t("card.staleAndLimit", {
|
|
190
|
+
stale: data.stale,
|
|
191
|
+
limit: data.maxEntries.toLocaleString(),
|
|
192
|
+
});
|
|
157
193
|
|
|
158
194
|
const redisState = !redis.enabled
|
|
159
195
|
? "off"
|
|
@@ -163,22 +199,22 @@ function render() {
|
|
|
163
199
|
? "connected"
|
|
164
200
|
: "disconnected";
|
|
165
201
|
|
|
166
|
-
$("redis-state").textContent = redisState;
|
|
202
|
+
$("redis-state").textContent = t(`state.${redisState}`);
|
|
167
203
|
$("redis-sub").textContent = redis.enabled
|
|
168
|
-
?
|
|
169
|
-
: "
|
|
204
|
+
? t("redis.sub", { address: redis.address, errors: redis.errors })
|
|
205
|
+
: t("redis.disabled");
|
|
170
206
|
|
|
171
207
|
renderRedis(redis, redisState);
|
|
172
208
|
renderHost(host, proc, redis);
|
|
173
209
|
|
|
174
210
|
const done = prewarm.done ?? 0;
|
|
175
211
|
const total = prewarm.total ?? 0;
|
|
176
|
-
$("prewarm-state").textContent = prewarm.active ? `${done}/${total}` : "idle";
|
|
212
|
+
$("prewarm-state").textContent = prewarm.active ? `${done}/${total}` : t("state.idle");
|
|
177
213
|
$("prewarm-sub").textContent = prewarm.active
|
|
178
|
-
? "
|
|
214
|
+
? t("prewarm.inProgress")
|
|
179
215
|
: total
|
|
180
|
-
?
|
|
181
|
-
: "
|
|
216
|
+
? t("prewarm.lastRound", { done, total })
|
|
217
|
+
: t("prewarm.never");
|
|
182
218
|
|
|
183
219
|
renderTable();
|
|
184
220
|
}
|
|
@@ -212,10 +248,10 @@ function renderCloudflare() {
|
|
|
212
248
|
const zone = cloudflare.zone ?? null;
|
|
213
249
|
|
|
214
250
|
$("cf-heading").textContent = ok
|
|
215
|
-
? [zone?.name, zone?.plan].filter(Boolean).join(" · ") || "connected"
|
|
251
|
+
? [zone?.name, zone?.plan].filter(Boolean).join(" · ") || t("state.connected")
|
|
216
252
|
: status.configured
|
|
217
|
-
?
|
|
218
|
-
: "
|
|
253
|
+
? t("cf.error", { error: cloudflare.error })
|
|
254
|
+
: t("cf.notConnected");
|
|
219
255
|
|
|
220
256
|
$("cf-tools").hidden = !ok;
|
|
221
257
|
$("cf-suggest").hidden = ok || status.configured;
|
|
@@ -231,24 +267,28 @@ function renderCloudflare() {
|
|
|
231
267
|
|
|
232
268
|
/** @type {[string, string, string?][]} */
|
|
233
269
|
const rows = [
|
|
234
|
-
["
|
|
235
|
-
["
|
|
236
|
-
["Token", status.tokenSource === "env" ? "from environment" : "from config", "on"],
|
|
270
|
+
[t("cf.zone"), `${zone?.name ?? "—"} (${status.zoneId ?? "?"})`],
|
|
271
|
+
[t("cf.plan"), zone?.plan ?? "—"],
|
|
237
272
|
[
|
|
238
|
-
"
|
|
273
|
+
t("cf.token"),
|
|
274
|
+
status.tokenSource === "env" ? t("cf.token.env") : t("cf.token.config"),
|
|
275
|
+
"on",
|
|
276
|
+
],
|
|
277
|
+
[
|
|
278
|
+
t("cf.devModeLabel"),
|
|
239
279
|
development
|
|
240
|
-
?
|
|
241
|
-
: "off",
|
|
280
|
+
? t("cf.devMode.on", { time: formatDuration(settings.developmentModeRemaining) })
|
|
281
|
+
: t("state.off"),
|
|
242
282
|
development ? "bad" : "on",
|
|
243
283
|
],
|
|
244
|
-
["
|
|
245
|
-
["
|
|
246
|
-
["
|
|
247
|
-
["
|
|
248
|
-
["
|
|
249
|
-
...feature("
|
|
250
|
-
...feature("
|
|
251
|
-
...feature("
|
|
284
|
+
[t("cf.cacheLevel"), String(settings.cacheLevel ?? "—")],
|
|
285
|
+
[t("cf.browserTtl"), ttl(settings.browserCacheTtl)],
|
|
286
|
+
[t("cf.edgeTtl"), ttl(settings.edgeCacheTtl)],
|
|
287
|
+
[t("cf.sortQuery"), String(settings.sortQueryString ?? "—")],
|
|
288
|
+
[t("cf.alwaysOnline"), String(settings.alwaysOnline ?? "—")],
|
|
289
|
+
...feature(t("cf.tiered"), settings.tieredCaching),
|
|
290
|
+
...feature(t("cf.regionalTiered"), settings.regionalTieredCache),
|
|
291
|
+
...feature(t("cf.reserve"), settings.cacheReserve),
|
|
252
292
|
];
|
|
253
293
|
|
|
254
294
|
$("cf-kv").replaceChildren(...rows.map(kvRow));
|
|
@@ -264,9 +304,10 @@ function renderCloudflare() {
|
|
|
264
304
|
*/
|
|
265
305
|
function feature(label, value) {
|
|
266
306
|
if (value === null || value === undefined) {
|
|
267
|
-
return [[label, "
|
|
307
|
+
return [[label, t("cf.unavailablePlan"), "off"]];
|
|
268
308
|
}
|
|
269
|
-
|
|
309
|
+
const on = value === "on";
|
|
310
|
+
return [[label, on ? t("state.on") : t("state.off"), on ? "on" : "off"]];
|
|
270
311
|
}
|
|
271
312
|
|
|
272
313
|
/**
|
|
@@ -277,7 +318,7 @@ function ttl(seconds) {
|
|
|
277
318
|
const value = Number(seconds);
|
|
278
319
|
if (!Number.isFinite(value)) return "—";
|
|
279
320
|
// Cloudflare 0'ı "Respect Existing Headers" olarak kullanıyor.
|
|
280
|
-
if (value === 0) return "
|
|
321
|
+
if (value === 0) return t("cf.respectOrigin");
|
|
281
322
|
return formatDuration(value);
|
|
282
323
|
}
|
|
283
324
|
|
|
@@ -305,7 +346,7 @@ async function loadCloudflareAnalytics() {
|
|
|
305
346
|
const target = $("cf-report");
|
|
306
347
|
|
|
307
348
|
target.hidden = false;
|
|
308
|
-
target.textContent = "
|
|
349
|
+
target.textContent = t("cf.querying");
|
|
309
350
|
|
|
310
351
|
const response = await fetch("cloudflare/analytics", {
|
|
311
352
|
method: "POST",
|
|
@@ -321,20 +362,35 @@ async function loadCloudflareAnalytics() {
|
|
|
321
362
|
const result = await response.json().catch(() => null);
|
|
322
363
|
|
|
323
364
|
if (!result?.ok) {
|
|
324
|
-
target.textContent =
|
|
365
|
+
target.textContent = t("msg.cf.failed", { error: result?.error ?? t("cf.queryFailed") });
|
|
325
366
|
return;
|
|
326
367
|
}
|
|
327
368
|
|
|
328
|
-
|
|
369
|
+
// Son dökümü dil değişiminde yeniden kurmak için saklanır: tablo ağdan
|
|
370
|
+
// geliyor ve dil değişti diye Cloudflare'e yeniden gitmek gereksiz.
|
|
371
|
+
lastReport = result;
|
|
372
|
+
renderReport();
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/** @type {any} */
|
|
376
|
+
let lastReport = null;
|
|
377
|
+
|
|
378
|
+
function renderReport() {
|
|
379
|
+
if (!lastReport) return;
|
|
380
|
+
const result = lastReport;
|
|
381
|
+
|
|
382
|
+
$("cf-report").replaceChildren(
|
|
329
383
|
result.colos ? edgeTable(result) : statusTable(result),
|
|
330
384
|
note(
|
|
331
385
|
result.colos
|
|
332
|
-
?
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
386
|
+
? t("cf.edgeNote", {
|
|
387
|
+
colos: result.colos.length,
|
|
388
|
+
path: result.path,
|
|
389
|
+
hours: result.hours,
|
|
390
|
+
hits: result.hits,
|
|
391
|
+
misses: result.misses,
|
|
392
|
+
})
|
|
393
|
+
: t("cf.zoneNote", { hours: result.hours }),
|
|
338
394
|
),
|
|
339
395
|
);
|
|
340
396
|
}
|
|
@@ -345,7 +401,7 @@ async function loadCloudflareAnalytics() {
|
|
|
345
401
|
*/
|
|
346
402
|
function edgeTable(result) {
|
|
347
403
|
return buildTable(
|
|
348
|
-
["
|
|
404
|
+
[t("cf.colo"), t("cf.fromCache"), t("cf.toOrigin")],
|
|
349
405
|
result.colos.map((/** @type {any} */ row) => [
|
|
350
406
|
row.colo,
|
|
351
407
|
String(row.hits),
|
|
@@ -362,7 +418,7 @@ function statusTable(result) {
|
|
|
362
418
|
const total = result.rows.reduce((sum, /** @type {any} */ row) => sum + row.requests, 0) || 1;
|
|
363
419
|
|
|
364
420
|
return buildTable(
|
|
365
|
-
["
|
|
421
|
+
[t("cf.cacheStatus"), t("cf.requests"), t("cf.share"), t("cf.edgeBytes")],
|
|
366
422
|
result.rows.map((/** @type {any} */ row) => [
|
|
367
423
|
row.status,
|
|
368
424
|
row.requests.toLocaleString(),
|
|
@@ -425,37 +481,53 @@ function note(text) {
|
|
|
425
481
|
* @param {string} state
|
|
426
482
|
*/
|
|
427
483
|
function renderRedis(redis, state) {
|
|
428
|
-
$("redis-heading").textContent = redis.enabled ? state : "
|
|
484
|
+
$("redis-heading").textContent = redis.enabled ? t(`state.${state}`) : t("state.notConfigured");
|
|
429
485
|
$("redis-actions").hidden = !redis.enabled;
|
|
430
486
|
$("redis-suggest").hidden = redis.enabled;
|
|
431
487
|
|
|
432
488
|
/** @type {[string, string, string?][]} */
|
|
433
489
|
const rows = redis.enabled
|
|
434
490
|
? [
|
|
435
|
-
["Connection", state, redis.connected && !redis.bypassed ? "on" : "bad"],
|
|
436
|
-
["Address", `${redis.address}${redis.secure ? " (TLS)" : ""}`],
|
|
437
|
-
["Database", redis.db ?? "default"],
|
|
438
|
-
["Key prefix", `${redis.keyPrefix}:${redis.namespace}`],
|
|
439
|
-
["Build id", redis.buildId || "dev"],
|
|
440
|
-
["Shares HTML", redis.html ? "yes" : "no", redis.html ? "on" : "off"],
|
|
441
|
-
["Shares data", redis.data ? "yes" : "no", redis.data ? "on" : "off"],
|
|
442
491
|
[
|
|
443
|
-
"
|
|
444
|
-
|
|
492
|
+
t("redis.connection"),
|
|
493
|
+
t(`state.${state}`),
|
|
494
|
+
redis.connected && !redis.bypassed ? "on" : "bad",
|
|
495
|
+
],
|
|
496
|
+
[t("redis.address"), `${redis.address}${redis.secure ? " (TLS)" : ""}`],
|
|
497
|
+
[t("redis.database"), redis.db ?? t("state.default")],
|
|
498
|
+
[t("redis.keyPrefix"), `${redis.keyPrefix}:${redis.namespace}`],
|
|
499
|
+
[t("redis.buildId"), redis.buildId || "dev"],
|
|
500
|
+
[
|
|
501
|
+
t("redis.sharesHtml"),
|
|
502
|
+
redis.html ? t("state.yes") : t("state.no"),
|
|
503
|
+
redis.html ? "on" : "off",
|
|
504
|
+
],
|
|
505
|
+
[
|
|
506
|
+
t("redis.sharesData"),
|
|
507
|
+
redis.data ? t("state.yes") : t("state.no"),
|
|
508
|
+
redis.data ? "on" : "off",
|
|
509
|
+
],
|
|
510
|
+
[
|
|
511
|
+
t("redis.compressed"),
|
|
512
|
+
redis.storeEncoded ? t("redis.compressed.shared") : t("redis.compressed.local"),
|
|
445
513
|
redis.storeEncoded ? "on" : "off",
|
|
446
514
|
],
|
|
447
515
|
[
|
|
448
|
-
"
|
|
449
|
-
redis.events
|
|
516
|
+
t("redis.broadcast"),
|
|
517
|
+
redis.events
|
|
518
|
+
? redis.subscribed
|
|
519
|
+
? t("redis.broadcast.subscribed")
|
|
520
|
+
: t("redis.broadcast.publish")
|
|
521
|
+
: t("state.off"),
|
|
450
522
|
redis.events && redis.subscribed ? "on" : "off",
|
|
451
523
|
],
|
|
452
|
-
["
|
|
453
|
-
["
|
|
524
|
+
[t("redis.timeout"), `${redis.commandTimeoutMs} ${t("unit.ms")}`],
|
|
525
|
+
[t("redis.errors"), String(redis.errors), redis.errors ? "bad" : "on"],
|
|
454
526
|
]
|
|
455
527
|
: [
|
|
456
|
-
["
|
|
457
|
-
["
|
|
458
|
-
["
|
|
528
|
+
[t("redis.tier"), t("redis.tier.inProcess")],
|
|
529
|
+
[t("redis.replicas"), t("state.none")],
|
|
530
|
+
[t("redis.broadcast"), t("redis.broadcast.local"), "off"],
|
|
459
531
|
];
|
|
460
532
|
|
|
461
533
|
$("redis-kv").replaceChildren(...rows.map(kvRow));
|
|
@@ -484,14 +556,16 @@ function renderHost(host, proc, redis) {
|
|
|
484
556
|
`${formatBytes(host.memory.used)} / ${formatBytes(host.memory.total)}`;
|
|
485
557
|
meter("ram-bar", ramUsed);
|
|
486
558
|
$("ram-note").textContent = redis.connected
|
|
487
|
-
?
|
|
488
|
-
:
|
|
489
|
-
|
|
559
|
+
? t("host.ramShared", { rss: formatBytes(proc.memory.rss) })
|
|
560
|
+
: t("host.ramOnly", {
|
|
561
|
+
rss: formatBytes(proc.memory.rss),
|
|
562
|
+
html: formatBytes(latest.html.bytes),
|
|
563
|
+
});
|
|
490
564
|
|
|
491
565
|
if (!host.disk) {
|
|
492
|
-
$("disk-text").textContent = "unavailable";
|
|
566
|
+
$("disk-text").textContent = t("state.unavailable");
|
|
493
567
|
meter("disk-bar", 0);
|
|
494
|
-
$("disk-note").textContent = "
|
|
568
|
+
$("disk-note").textContent = t("host.noStats");
|
|
495
569
|
return;
|
|
496
570
|
}
|
|
497
571
|
|
|
@@ -500,8 +574,10 @@ function renderHost(host, proc, redis) {
|
|
|
500
574
|
`${formatBytes(host.disk.total - host.disk.free)} / ${formatBytes(host.disk.total)}`;
|
|
501
575
|
meter("disk-bar", diskUsed);
|
|
502
576
|
// Önbellek diske yazılmıyor; disk build çıktısı ve log için önemli.
|
|
503
|
-
$("disk-note").textContent =
|
|
504
|
-
|
|
577
|
+
$("disk-note").textContent = t("host.diskNote", {
|
|
578
|
+
free: formatBytes(host.disk.free),
|
|
579
|
+
path: host.disk.path,
|
|
580
|
+
});
|
|
505
581
|
}
|
|
506
582
|
|
|
507
583
|
/**
|
|
@@ -522,13 +598,31 @@ function renderTable() {
|
|
|
522
598
|
|
|
523
599
|
$("listed").textContent =
|
|
524
600
|
source.matched > source.entries.length
|
|
525
|
-
?
|
|
526
|
-
:
|
|
601
|
+
? t("entries.shownPartial", { shown: source.entries.length, total: source.matched })
|
|
602
|
+
: t("entries.shown", { total: source.matched });
|
|
527
603
|
|
|
528
|
-
|
|
604
|
+
const columns =
|
|
529
605
|
tab === "html"
|
|
530
|
-
?
|
|
531
|
-
|
|
606
|
+
? [
|
|
607
|
+
["entries.key"],
|
|
608
|
+
["entries.state"],
|
|
609
|
+
["entries.size", "num"],
|
|
610
|
+
["entries.status", "num"],
|
|
611
|
+
["entries.expires", "num"],
|
|
612
|
+
["entries.deps", "num"],
|
|
613
|
+
["entries.encodings"],
|
|
614
|
+
[null],
|
|
615
|
+
]
|
|
616
|
+
: [["entries.key"], ["entries.state"], ["entries.expires", "num"], [null]];
|
|
617
|
+
|
|
618
|
+
const headRow = document.createElement("tr");
|
|
619
|
+
for (const [key, className] of columns) {
|
|
620
|
+
const th = document.createElement("th");
|
|
621
|
+
if (className) th.className = className;
|
|
622
|
+
if (key) th.textContent = t(key);
|
|
623
|
+
headRow.append(th);
|
|
624
|
+
}
|
|
625
|
+
thead.replaceChildren(headRow);
|
|
532
626
|
|
|
533
627
|
tbody.replaceChildren(
|
|
534
628
|
...source.entries.map((/** @type {any} */ entry) => row(entry)),
|
|
@@ -563,7 +657,7 @@ function row(entry) {
|
|
|
563
657
|
const copy = document.createElement("button");
|
|
564
658
|
copy.className = "as-text";
|
|
565
659
|
copy.type = "button";
|
|
566
|
-
copy.title = "
|
|
660
|
+
copy.title = t("entries.copyKey");
|
|
567
661
|
copy.textContent = entry.key;
|
|
568
662
|
copy.addEventListener("click", () => void copyKey(entry.key));
|
|
569
663
|
key.append(copy);
|
|
@@ -574,7 +668,7 @@ function row(entry) {
|
|
|
574
668
|
const state = document.createElement("td");
|
|
575
669
|
const tag = document.createElement("span");
|
|
576
670
|
tag.className = entry.stale ? "tag stale" : "tag fresh";
|
|
577
|
-
tag.textContent = entry.stale ? "stale" : "fresh";
|
|
671
|
+
tag.textContent = entry.stale ? t("entries.stale") : t("entries.fresh");
|
|
578
672
|
state.append(tag);
|
|
579
673
|
tr.append(state);
|
|
580
674
|
|
|
@@ -598,8 +692,8 @@ function row(entry) {
|
|
|
598
692
|
if (tab === "html" && cloudflare?.ok) {
|
|
599
693
|
const purge = document.createElement("button");
|
|
600
694
|
purge.className = "tiny";
|
|
601
|
-
purge.textContent = "cf
|
|
602
|
-
purge.title = "
|
|
695
|
+
purge.textContent = t("cf.rowPurge");
|
|
696
|
+
purge.title = t("cf.rowPurgeTitle");
|
|
603
697
|
purge.addEventListener("click", () => {
|
|
604
698
|
void act({ type: "cf:purge-urls", paths: [entry.url] });
|
|
605
699
|
});
|
|
@@ -608,7 +702,7 @@ function row(entry) {
|
|
|
608
702
|
|
|
609
703
|
const drop = document.createElement("button");
|
|
610
704
|
drop.className = "tiny danger";
|
|
611
|
-
drop.textContent = "drop";
|
|
705
|
+
drop.textContent = t("entries.drop");
|
|
612
706
|
drop.addEventListener("click", () => {
|
|
613
707
|
void act({ type: tab === "html" ? "html:drop" : "data:drop", key: entry.key });
|
|
614
708
|
});
|
|
@@ -628,11 +722,11 @@ function row(entry) {
|
|
|
628
722
|
async function copyKey(key) {
|
|
629
723
|
try {
|
|
630
724
|
await navigator.clipboard.writeText(key);
|
|
631
|
-
toast("
|
|
725
|
+
toast(t("toast.keyCopied"));
|
|
632
726
|
} catch {
|
|
633
727
|
$("prefix").value = key;
|
|
634
728
|
$("prefix").focus();
|
|
635
|
-
toast("
|
|
729
|
+
toast(t("toast.clipboard"), false);
|
|
636
730
|
}
|
|
637
731
|
}
|
|
638
732
|
|
|
@@ -694,7 +788,7 @@ for (const name of /** @type {const} */ (["html", "data"])) {
|
|
|
694
788
|
$("invalidate").addEventListener("click", () => {
|
|
695
789
|
const target = $("target").value.trim();
|
|
696
790
|
if (!target) {
|
|
697
|
-
toast("
|
|
791
|
+
toast(t("toast.needTarget"), false);
|
|
698
792
|
return;
|
|
699
793
|
}
|
|
700
794
|
|
|
@@ -702,13 +796,13 @@ $("invalidate").addEventListener("click", () => {
|
|
|
702
796
|
});
|
|
703
797
|
|
|
704
798
|
$("clear-html").addEventListener("click", () => {
|
|
705
|
-
if (!confirm("
|
|
799
|
+
if (!confirm(t("confirm.clearHtml"))) return;
|
|
706
800
|
void act({ type: "html:clear" });
|
|
707
801
|
});
|
|
708
802
|
|
|
709
803
|
$("clear-data").addEventListener("click", () => {
|
|
710
804
|
const prefix = $("prefix").value.trim();
|
|
711
|
-
if (!prefix && !confirm("
|
|
805
|
+
if (!prefix && !confirm(t("confirm.clearData"))) {
|
|
712
806
|
return;
|
|
713
807
|
}
|
|
714
808
|
|
|
@@ -734,7 +828,7 @@ $("redis-data").addEventListener("click", () =>
|
|
|
734
828
|
$("cf-refresh").addEventListener("click", () => void loadCloudflare(true));
|
|
735
829
|
|
|
736
830
|
$("cf-purge-all").addEventListener("click", () => {
|
|
737
|
-
if (!confirm("
|
|
831
|
+
if (!confirm(t("confirm.purgeAll"))) return;
|
|
738
832
|
void act({ type: "cf:purge-everything" });
|
|
739
833
|
});
|
|
740
834
|
|
|
@@ -743,18 +837,18 @@ $("cf-purge-all").addEventListener("click", () => {
|
|
|
743
837
|
$("cf-purge-cached").addEventListener("click", () => {
|
|
744
838
|
const paths = (latest?.html.entries ?? []).map((/** @type {any} */ entry) => entry.url);
|
|
745
839
|
if (!paths.length) {
|
|
746
|
-
toast("
|
|
840
|
+
toast(t("toast.noCachedPages"), false);
|
|
747
841
|
return;
|
|
748
842
|
}
|
|
749
843
|
|
|
750
|
-
if (!confirm(
|
|
844
|
+
if (!confirm(t("confirm.purgeUrls", { count: paths.length }))) return;
|
|
751
845
|
void act({ type: "cf:purge-urls", paths });
|
|
752
846
|
});
|
|
753
847
|
|
|
754
848
|
$("cf-purge-keys").addEventListener("click", () => {
|
|
755
849
|
const values = $("cf-values").value.trim();
|
|
756
850
|
if (!values) {
|
|
757
|
-
toast("
|
|
851
|
+
toast(t("toast.needValue"), false);
|
|
758
852
|
return;
|
|
759
853
|
}
|
|
760
854
|
|
|
@@ -777,9 +871,7 @@ $("cf-reserve").addEventListener("click", () => {
|
|
|
777
871
|
});
|
|
778
872
|
|
|
779
873
|
$("cf-reserve-clear").addEventListener("click", () => {
|
|
780
|
-
if (!confirm("
|
|
781
|
-
return;
|
|
782
|
-
}
|
|
874
|
+
if (!confirm(t("confirm.clearReserve"))) return;
|
|
783
875
|
void act({ type: "cf:clear-reserve" });
|
|
784
876
|
});
|
|
785
877
|
|
|
@@ -803,6 +895,21 @@ document.addEventListener("visibilitychange", () => {
|
|
|
803
895
|
}
|
|
804
896
|
});
|
|
805
897
|
|
|
898
|
+
/* -------------------------------------------------------------------- dil */
|
|
899
|
+
|
|
900
|
+
// Dil değişimi hiçbir isteğe yol açmaz: statik metinler sözlükten yeniden
|
|
901
|
+
// yazılır, dinamik olanlar elde duran son dökümle tekrar çizilir.
|
|
902
|
+
$("language").append(
|
|
903
|
+
languageSelect(() => {
|
|
904
|
+
applyTranslations();
|
|
905
|
+
if (latest) render();
|
|
906
|
+
renderCloudflare();
|
|
907
|
+
renderReport();
|
|
908
|
+
}),
|
|
909
|
+
);
|
|
910
|
+
|
|
911
|
+
applyTranslations();
|
|
912
|
+
|
|
806
913
|
void load();
|
|
807
914
|
void loadCloudflare();
|
|
808
915
|
startPolling();
|