* Migrates to pinia * Does more pinia migration * Fixes types * Move pinia * Adds search cleans up pinia * Removes unused files * Removes vuex * More clean up * Makes js tests pass * Fixes bugs and removing active container
134 lines
3.2 KiB
Vue
134 lines
3.2 KiB
Vue
<template>
|
|
<infinite-loader :onLoadMore="loadOlderLogs" :enabled="messages.length > 100"></infinite-loader>
|
|
<slot :messages="messages"></slot>
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
import { toRefs, ref, watch, onUnmounted } from "vue";
|
|
import debounce from "lodash.debounce";
|
|
|
|
import { LogEntry } from "@/types/LogEntry";
|
|
import InfiniteLoader from "./InfiniteLoader.vue";
|
|
import config from "@/stores/config";
|
|
import { useContainerStore } from "@/stores/container";
|
|
|
|
const props = defineProps({
|
|
id: {
|
|
type: String,
|
|
required: true,
|
|
},
|
|
});
|
|
|
|
const { id } = toRefs(props);
|
|
const emit = defineEmits(["loading-more"]);
|
|
const store = useContainerStore();
|
|
const container = store.currentContainer(id);
|
|
|
|
const messages = ref<LogEntry[]>([]);
|
|
const buffer = ref<LogEntry[]>([]);
|
|
|
|
function flushNow() {
|
|
messages.value.push(...buffer.value);
|
|
buffer.value = [];
|
|
}
|
|
|
|
const flushBuffer = debounce(flushNow, 250, { maxWait: 1000 });
|
|
|
|
let es: EventSource | null = null;
|
|
let lastEventId = "";
|
|
|
|
function connect({ clear } = { clear: true }) {
|
|
es?.close();
|
|
|
|
if (clear) {
|
|
flushBuffer.cancel();
|
|
messages.value = [];
|
|
buffer.value = [];
|
|
lastEventId = "";
|
|
}
|
|
|
|
es = new EventSource(`${config.base}/api/logs/stream?id=${props.id}&lastEventId=${lastEventId}`);
|
|
es.addEventListener("container-stopped", () => {
|
|
es?.close();
|
|
es = null;
|
|
buffer.value.push({
|
|
event: "container-stopped",
|
|
message: "Container stopped",
|
|
date: new Date(),
|
|
key: new Date().toString(),
|
|
});
|
|
flushBuffer();
|
|
flushBuffer.flush();
|
|
});
|
|
es.addEventListener("error", (e) => console.error("EventSource failed: " + JSON.stringify(e)));
|
|
es.onmessage = (e) => {
|
|
lastEventId = e.lastEventId;
|
|
if (e.data) {
|
|
buffer.value.push(parseMessage(e.data));
|
|
flushBuffer();
|
|
}
|
|
};
|
|
}
|
|
|
|
async function loadOlderLogs() {
|
|
if (messages.value.length < 300) return;
|
|
|
|
emit("loading-more", true);
|
|
const to = messages.value[0].date;
|
|
const last = messages.value[299].date;
|
|
const delta = to.getTime() - last.getTime();
|
|
const from = new Date(to.getTime() + delta);
|
|
const logs = await (
|
|
await fetch(`${config.base}/api/logs?id=${props.id}&from=${from.toISOString()}&to=${to.toISOString()}`)
|
|
).text();
|
|
if (logs) {
|
|
const newMessages = logs
|
|
.trim()
|
|
.split("\n")
|
|
.map((line) => parseMessage(line));
|
|
messages.value.unshift(...newMessages);
|
|
}
|
|
emit("loading-more", false);
|
|
}
|
|
|
|
function parseMessage(data: String): LogEntry {
|
|
let i = data.indexOf(" ");
|
|
if (i == -1) {
|
|
i = data.length;
|
|
}
|
|
const key = data.substring(0, i);
|
|
const date = new Date(key);
|
|
const message = data.substring(i + 1);
|
|
return { key, date, message };
|
|
}
|
|
|
|
watch(
|
|
() => container.value.state,
|
|
(newValue, oldValue) => {
|
|
console.log("LogEventSource: container changed", newValue, oldValue);
|
|
if (newValue == "running" && newValue != oldValue) {
|
|
buffer.value.push({
|
|
event: "container-started",
|
|
message: "Container started",
|
|
date: new Date(),
|
|
key: new Date().toString(),
|
|
});
|
|
connect({ clear: false });
|
|
}
|
|
}
|
|
);
|
|
|
|
onUnmounted(() => {
|
|
if (es) {
|
|
es.close();
|
|
}
|
|
});
|
|
|
|
connect();
|
|
watch(id, () => connect());
|
|
|
|
defineExpose({
|
|
clear: () => (messages.value = []),
|
|
});
|
|
</script>
|