feat: update README, enhance setup scripts, and add dev utilities for improved workflow

This commit is contained in:
gabdevele
2026-05-07 18:38:19 +02:00 Unverified
parent 9d6efe5e60
commit 7dbe698a49
11 changed files with 579 additions and 198 deletions
+1 -1
View File
@@ -10,7 +10,7 @@ RUN npm run build
FROM nginx:1.29-alpine
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY nginx.conf /etc/nginx/templates/default.conf.template
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80
+1 -1
View File
@@ -6,7 +6,7 @@ server {
index index.html;
location /api/ {
proxy_pass http://backend:3671/;
proxy_pass http://${API_UPSTREAM}/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
+85 -12
View File
@@ -1,4 +1,4 @@
import { computed, ref, watch, type Ref } from "vue";
import { computed, ref, shallowRef, watch, type Ref } from "vue";
import { api, buildSocketUrl } from "../api";
@@ -11,6 +11,31 @@ type LogPlaneOptions = {
onError: (error: unknown) => void;
};
const LOG_RECONNECT_DELAY_MS = 1200;
const LOG_FLUSH_DELAY_MS = 75;
const MAX_LOG_LINES = 2500;
const MAX_LOG_CHARS = 250_000;
function splitLogText(text: string) {
return text === "" ? [] : text.split("\n");
}
function trimLogLines(lines: string[]) {
let next = lines.length > MAX_LOG_LINES ? lines.slice(-MAX_LOG_LINES) : lines;
let totalChars = next.reduce(
(count, line, index) => count + line.length + (index > 0 ? 1 : 0),
0,
);
while (totalChars > MAX_LOG_CHARS && next.length > 1) {
const [removed = ""] = next;
next = next.slice(1);
totalChars -= removed.length + 1;
}
return next;
}
export function useControlPlaneLogs({
token,
selectedPort,
@@ -21,20 +46,63 @@ export function useControlPlaneLogs({
const logFilter = ref("");
const logsPaused = ref(false);
const logsCleared = ref(false);
const logLines = shallowRef<string[]>([]);
let logSocket: WebSocket | null = null;
let logReconnectTimer: number | null = null;
let logFlushTimer: number | null = null;
let pendingLogChunk = "";
const filteredLogs = computed(() => {
const source = logs.value || "";
const query = logFilter.value.trim().toLowerCase();
if (!query) return source;
return source
.split("\n")
if (!query) return logs.value;
return logLines.value
.filter((line) => line.toLowerCase().includes(query))
.join("\n");
});
function commitLogLines(lines: string[]) {
const trimmed = trimLogLines(lines);
logLines.value = trimmed;
logs.value = trimmed.join("\n");
}
function replaceLogs(text: string) {
commitLogLines(splitLogText(text));
}
function appendLogs(text: string) {
if (!text) return;
commitLogLines([...logLines.value, ...splitLogText(text)]);
}
function flushPendingLogs() {
if (!pendingLogChunk) return;
appendLogs(pendingLogChunk);
pendingLogChunk = "";
}
function stopPendingFlush() {
if (typeof window === "undefined" || logFlushTimer === null) return;
window.clearTimeout(logFlushTimer);
logFlushTimer = null;
}
function scheduleLogFlush() {
if (typeof window === "undefined" || logFlushTimer !== null) return;
logFlushTimer = window.setTimeout(() => {
logFlushTimer = null;
flushPendingLogs();
}, LOG_FLUSH_DELAY_MS);
}
function resetLogBuffer() {
stopPendingFlush();
pendingLogChunk = "";
logLines.value = [];
logs.value = "";
}
function parseLogFrame(raw: string): LogFrame | null {
try {
const payload = JSON.parse(raw);
@@ -55,7 +123,7 @@ export function useControlPlaneLogs({
if (port === null) return;
try {
if (!logsCleared.value) {
logs.value = (await api.logs(port)).log;
replaceLogs((await api.logs(port)).log);
}
} catch (error) {
if (!silent) onError(error);
@@ -72,7 +140,7 @@ export function useControlPlaneLogs({
logReconnectTimer = null;
if (!token.value || logsPaused.value || selectedPort.value !== proxyPort) return;
void openLogStream(proxyPort);
}, 1200);
}, LOG_RECONNECT_DELAY_MS);
}
async function openLogStream(proxyPort: number) {
@@ -98,10 +166,13 @@ export function useControlPlaneLogs({
if (frame === null) return;
if (frame.type === "snapshot") {
if (logsCleared.value) return;
logs.value = frame.log;
resetLogBuffer();
replaceLogs(frame.log);
return;
}
logs.value += frame.log;
pendingLogChunk += frame.log;
scheduleLogFlush();
};
socket.onclose = (event) => {
@@ -131,6 +202,8 @@ export function useControlPlaneLogs({
window.clearTimeout(logReconnectTimer);
logReconnectTimer = null;
}
stopPendingFlush();
pendingLogChunk = "";
if (logSocket === null) return;
logSocket.onclose = null;
logSocket.close();
@@ -142,13 +215,13 @@ export function useControlPlaneLogs({
}
function clearLogs() {
logs.value = "";
resetLogBuffer();
logsCleared.value = true;
}
function resetLogs() {
stopLogStream();
logs.value = "";
resetLogBuffer();
logFilter.value = "";
logsPaused.value = false;
logsCleared.value = false;
@@ -158,7 +231,7 @@ export function useControlPlaneLogs({
[selectedPort, token, logsPaused],
([port, authToken, paused], [previousPort]) => {
if (port !== previousPort) {
logs.value = "";
resetLogBuffer();
logFilter.value = "";
logsCleared.value = false;
}