Rebrand the project as Tinyforge to reflect its evolution from a Docker container watcher into a self-hosted mini CI/deployment platform. Rename covers: Go module path, Docker labels, DB/config filenames, JWT issuer, Dockerfile binary, docker-compose, CI workflows, frontend i18n, README with static sites docs, and all code comments.
This commit is contained in:
@@ -25,6 +25,29 @@
|
||||
let logContainer: HTMLDivElement | undefined = $state();
|
||||
let eventSource: EventSource | null = null;
|
||||
|
||||
// Batch incoming SSE log lines to avoid per-line re-renders.
|
||||
let pendingLines: string[] = [];
|
||||
let flushTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
function flushPendingLines() {
|
||||
flushTimer = null;
|
||||
if (pendingLines.length === 0) return;
|
||||
let updated = [...lines, ...pendingLines];
|
||||
pendingLines = [];
|
||||
if (updated.length > tailCount * 2) {
|
||||
updated = updated.slice(-tailCount);
|
||||
}
|
||||
lines = updated;
|
||||
scrollToBottom();
|
||||
}
|
||||
|
||||
function enqueueLine(line: string) {
|
||||
pendingLines.push(line);
|
||||
if (!flushTimer) {
|
||||
flushTimer = setTimeout(flushPendingLines, 150);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadLogs() {
|
||||
loading = true;
|
||||
error = '';
|
||||
@@ -49,12 +72,7 @@
|
||||
try {
|
||||
const data = JSON.parse(e.data);
|
||||
if (data.line) {
|
||||
lines = [...lines, data.line];
|
||||
// Trim to max lines.
|
||||
if (lines.length > tailCount * 2) {
|
||||
lines = lines.slice(-tailCount);
|
||||
}
|
||||
scrollToBottom();
|
||||
enqueueLine(data.line);
|
||||
}
|
||||
} catch { /* ignore parse errors */ }
|
||||
};
|
||||
@@ -69,6 +87,9 @@
|
||||
eventSource.close();
|
||||
eventSource = null;
|
||||
}
|
||||
// Flush any buffered lines before stopping.
|
||||
if (flushTimer) { clearTimeout(flushTimer); flushTimer = null; }
|
||||
flushPendingLines();
|
||||
following = false;
|
||||
}
|
||||
|
||||
@@ -90,7 +111,10 @@
|
||||
// Load on mount.
|
||||
$effect(() => { loadLogs(); });
|
||||
|
||||
onDestroy(() => { stopFollowing(); });
|
||||
onDestroy(() => {
|
||||
stopFollowing();
|
||||
if (flushTimer) { clearTimeout(flushTimer); flushTimer = null; }
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="rounded-xl border border-[var(--border-primary)] bg-[var(--surface-page)] shadow-lg">
|
||||
|
||||
@@ -19,8 +19,11 @@
|
||||
|
||||
$effect(() => {
|
||||
let cancelled = false;
|
||||
let inflight = false;
|
||||
|
||||
async function load() {
|
||||
if (inflight) return; // Skip if previous request still pending.
|
||||
inflight = true;
|
||||
try {
|
||||
const result = await api.fetchContainerStats(projectId, stageId, instanceId);
|
||||
if (!cancelled) {
|
||||
@@ -31,13 +34,15 @@
|
||||
if (!cancelled) {
|
||||
error = true;
|
||||
}
|
||||
} finally {
|
||||
inflight = false;
|
||||
}
|
||||
}
|
||||
|
||||
load();
|
||||
|
||||
// Poll every 10 seconds.
|
||||
const interval = setInterval(load, 10_000);
|
||||
// Poll every 30 seconds (reduced from 10s to limit concurrent connections).
|
||||
const interval = setInterval(load, 30_000);
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Lucide-based SVG icon components for Docker Watcher.
|
||||
* Lucide-based SVG icon components for Tinyforge.
|
||||
* Task 2: Inline SVGs from Lucide icon set as Svelte components.
|
||||
*
|
||||
* Each icon is a standalone .svelte component accepting size and class props.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"app": {
|
||||
"name": "Docker Watcher",
|
||||
"name": "Tinyforge",
|
||||
"version": "v0.1"
|
||||
},
|
||||
"health": {
|
||||
@@ -508,7 +508,7 @@
|
||||
"password": "Password"
|
||||
},
|
||||
"login": {
|
||||
"title": "Docker Watcher",
|
||||
"title": "Tinyforge",
|
||||
"subtitle": "Sign in to your account",
|
||||
"username": "Username",
|
||||
"password": "Password",
|
||||
@@ -819,7 +819,7 @@
|
||||
},
|
||||
"dns": {
|
||||
"title": "DNS Records",
|
||||
"description": "View and manage DNS records created by Docker Watcher.",
|
||||
"description": "View and manage DNS records created by Tinyforge.",
|
||||
"wildcardActive": "Wildcard DNS Mode Active",
|
||||
"wildcardActiveDesc": "DNS records are managed externally via wildcard DNS. Disable wildcard DNS in Settings to manage records individually.",
|
||||
"refresh": "Refresh",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"app": {
|
||||
"name": "Docker Watcher",
|
||||
"name": "Tinyforge",
|
||||
"version": "v0.1"
|
||||
},
|
||||
"health": {
|
||||
@@ -508,7 +508,7 @@
|
||||
"password": "Пароль"
|
||||
},
|
||||
"login": {
|
||||
"title": "Docker Watcher",
|
||||
"title": "Tinyforge",
|
||||
"subtitle": "Войдите в свой аккаунт",
|
||||
"username": "Имя пользователя",
|
||||
"password": "Пароль",
|
||||
@@ -819,7 +819,7 @@
|
||||
},
|
||||
"dns": {
|
||||
"title": "DNS-записи",
|
||||
"description": "Просмотр и управление DNS-записями, созданными Docker Watcher.",
|
||||
"description": "Просмотр и управление DNS-записями, созданными Tinyforge.",
|
||||
"wildcardActive": "Режим Wildcard DNS активен",
|
||||
"wildcardActiveDesc": "DNS-записи управляются внешне через wildcard DNS. Отключите wildcard DNS в настройках для индивидуального управления записями.",
|
||||
"refresh": "Обновить",
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* SSE client helper with auto-reconnect and exponential backoff.
|
||||
*
|
||||
* Provides type-safe event handling for Docker Watcher's real-time
|
||||
* Provides type-safe event handling for Tinyforge's real-time
|
||||
* event streams (deploy logs and instance status changes).
|
||||
*/
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Simple pub/sub bus for SSE event_log payloads.
|
||||
*
|
||||
* The layout component publishes events from the single global SSE connection.
|
||||
* Pages (e.g. /events) subscribe without opening a duplicate SSE connection.
|
||||
*/
|
||||
|
||||
import type { EventLogSSEPayload } from '$lib/sse';
|
||||
|
||||
type Listener = (payload: EventLogSSEPayload) => void;
|
||||
|
||||
const listeners = new Set<Listener>();
|
||||
|
||||
export function subscribeEventLog(fn: Listener): () => void {
|
||||
listeners.add(fn);
|
||||
return () => { listeners.delete(fn); };
|
||||
}
|
||||
|
||||
export function publishEventLog(payload: EventLogSSEPayload): void {
|
||||
for (const fn of listeners) {
|
||||
fn(payload);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user