merge v0.23.0-rc changes

This commit is contained in:
Gani Georgiev
2024-09-29 19:23:19 +03:00
parent ad92992324
commit 844f18cac3
753 changed files with 85141 additions and 63396 deletions
+43 -8
View File
@@ -66,12 +66,13 @@
"execTime",
"type",
"auth",
"authId",
"status",
"method",
"url",
"referer",
"remoteIp",
"userIp",
"remoteIP",
"userIP",
"userAgent",
"error",
"details",
@@ -135,19 +136,29 @@
<tr>
<td class="min-width txt-hint txt-bold">id</td>
<td>
<div class="label">
<span class="txt">{log.id}</span>
<div class="copy-icon-wrapper">
<CopyIcon value={log.id} />
<div class="txt">{log.id}</div>
</div>
</td>
</tr>
<tr>
<td class="min-width txt-hint txt-bold">level</td>
<td><LogLevel level={log.level} /></td>
<td>
<LogLevel level={log.level} />
<div class="copy-icon-wrapper">
<CopyIcon value={log.level} />
</div>
</td>
</tr>
<tr>
<td class="min-width txt-hint txt-bold">created</td>
<td><LogDate date={log.created} /></td>
<td>
<LogDate date={log.created} />
<div class="copy-icon-wrapper">
<CopyIcon value={log.created} />
</div>
</td>
</tr>
{#if !isRequest}
<tr>
@@ -155,6 +166,10 @@
<td>
{#if log.message}
<span class="txt">{log.message}</span>
<div class="copy-icon-wrapper">
<CopyIcon value={log.message} />
</div>
{:else}
<span class="txt txt-hint">N/A</span>
{/if}
@@ -163,13 +178,14 @@
{/if}
{#each extractKeys(log.data) as key}
{@const value = log.data[key]}
{@const isJson = value !== null && typeof value == "object"}
{@const isEmpty = CommonHelper.isEmpty(value)}
{@const isJson = !isEmpty && value !== null && typeof value == "object"}
<tr>
<td class="min-width txt-hint txt-bold" class:v-align-top={isJson}>
data.{key}
</td>
<td>
{#if CommonHelper.isEmpty(value)}
{#if isEmpty}
<span class="txt txt-hint">N/A</span>
{:else if isJson}
<CodeBlock content={JSON.stringify(value, null, 2)} />
@@ -184,6 +200,12 @@
{value}{isRequest && key == "execTime" ? "ms" : ""}
</span>
{/if}
{#if !isEmpty}
<div class="copy-icon-wrapper">
<CopyIcon {value} />
</div>
{/if}
</td>
</tr>
{/each}
@@ -207,4 +229,17 @@
.log-error-label {
white-space: normal;
}
.copy-icon-wrapper {
position: absolute;
right: 12px;
top: 12px;
opacity: 0;
transition: opacity var(--baseAnimationSpeed);
}
tr:hover .copy-icon-wrapper {
opacity: 1;
}
td:has(.copy-icon-wrapper) {
padding-right: 30px;
}
</style>
+57 -1
View File
@@ -14,8 +14,10 @@
Tooltip,
} from "chart.js";
import "chartjs-adapter-luxon";
import zoomPlugin from "chartjs-plugin-zoom";
export let filter = "";
export let zoom = {};
export let presets = "";
let chartCanvas;
@@ -23,6 +25,7 @@
let chartData = [];
let totalLogs = 0;
let isLoading = false;
let isZoomedOrPanned = false;
$: if (typeof filter !== "undefined" || typeof presets !== "undefined") {
load();
@@ -74,8 +77,13 @@
totalLogs = 0;
}
function resetZoom() {
chartInst?.resetZoom();
}
onMount(() => {
Chart.register(LineElement, PointElement, LineController, LinearScale, TimeScale, Filler, Tooltip);
Chart.register(zoomPlugin);
chartInst = new Chart(chartCanvas, {
type: "line",
@@ -143,6 +151,41 @@
legend: {
display: false,
},
zoom: {
enabled: true,
zoom: {
mode: "x",
pinch: {
enabled: true,
},
drag: {
enabled: true,
backgroundColor: "rgba(255, 99, 132, 0.2)",
borderWidth: 0,
threshold: 10,
},
limits: {
x: { minRange: 100000000 },
y: { minRange: 100000000 },
},
onZoomComplete: ({ chart }) => {
isZoomedOrPanned = chart.isZoomedOrPanned();
if (!isZoomedOrPanned) {
if (zoom.min || zoom.max) {
zoom = {}; // reset
}
} else {
// trim minutes and seconds since the statistic is hourly based
zoom.min =
CommonHelper.formatToUTCDate(chart.scales.x.min, "yyyy-MM-dd HH") +
":00:00.000Z";
zoom.max =
CommonHelper.formatToUTCDate(chart.scales.x.max, "yyyy-MM-dd HH") +
":59:59.999Z";
}
},
},
},
},
},
});
@@ -156,10 +199,18 @@
Found {totalLogs}
{totalLogs == 1 ? "log" : "logs"}
</div>
{#if isLoading}
<div class="chart-loader loader" transition:scale={{ duration: 150 }} />
{/if}
<canvas bind:this={chartCanvas} class="chart-canvas" />
<canvas bind:this={chartCanvas} class="chart-canvas" on:dblclick={resetZoom} />
{#if isZoomedOrPanned}
<button type="button" class="btn btn-secondary btn-sm btn-chart-zoom" on:click={resetZoom}>
Reset zoom
</button>
{/if}
</div>
<style>
@@ -187,4 +238,9 @@
font-size: var(--smFontSize);
color: var(--txtHintColor);
}
.btn-chart-zoom {
position: absolute;
right: 10px;
top: 20px;
}
</style>
+22 -14
View File
@@ -1,12 +1,12 @@
<script>
import { createEventDispatcher } from "svelte";
import { fly } from "svelte/transition";
import Scroller from "@/components/base/Scroller.svelte";
import SortHeader from "@/components/base/SortHeader.svelte";
import LogDate from "@/components/logs/LogDate.svelte";
import LogLevel from "@/components/logs/LogLevel.svelte";
import ApiClient from "@/utils/ApiClient";
import CommonHelper from "@/utils/CommonHelper";
import SortHeader from "@/components/base/SortHeader.svelte";
import Scroller from "@/components/base/Scroller.svelte";
import LogLevel from "@/components/logs/LogLevel.svelte";
import LogDate from "@/components/logs/LogDate.svelte";
import { createEventDispatcher } from "svelte";
import { fly } from "svelte/transition";
const dispatch = createEventDispatcher();
@@ -14,7 +14,8 @@
export let filter = "";
export let presets = "";
export let sort = "-rowid";
export let zoom = {};
export let sort = "-@rowid";
let logs = [];
let currentPage = 1;
@@ -23,7 +24,12 @@
let yieldedId = 0;
let bulkSelected = {};
$: if (typeof sort !== "undefined" || typeof filter !== "undefined" || typeof presets !== "undefined") {
$: if (
typeof sort !== "undefined" ||
typeof filter !== "undefined" ||
typeof presets !== "undefined" ||
typeof zoom !== "undefined"
) {
clearList();
load(1);
}
@@ -37,15 +43,17 @@
export async function load(page = 1, breakTasks = true) {
isLoading = true;
const normalizedFilter = [presets, CommonHelper.normalizeLogsFilter(filter)]
.filter(Boolean)
.join("&&");
const normalizedFilter = [presets, CommonHelper.normalizeLogsFilter(filter)];
if (zoom.min && zoom.max) {
normalizedFilter.push(`created >= "${zoom.min}" && created <= "${zoom.max}"`);
}
return ApiClient.logs
.getList(page, perPage, {
sort: sort,
skipTotal: 1,
filter: normalizedFilter,
filter: normalizedFilter.filter(Boolean).join("&&"),
})
.then(async (result) => {
if (page <= 1) {
@@ -174,7 +182,7 @@
}
if (log.data.type == "request") {
const requestKeys = ["status", "execTime", "auth", "userIp"];
const requestKeys = ["status", "execTime", "auth", "authId", "userIP"];
for (let key of requestKeys) {
if (typeof log.data[key] != "undefined") {
keys.push({ key });
@@ -298,7 +306,7 @@
{:else}
{keyItem.key}: {CommonHelper.stringifyValue(
log.data[keyItem.key],
"-",
"N/A",
80,
)}
{/if}
@@ -4,7 +4,6 @@
import ApiClient from "@/utils/ApiClient";
import { setErrors } from "@/stores/errors";
import { addSuccessToast } from "@/stores/toasts";
import tooltip from "@/actions/tooltip";
import Field from "@/components/base/Field.svelte";
import OverlayPanel from "@/components/base/OverlayPanel.svelte";
import LogsLevelsInfo from "@/components/logs/LogsLevelsInfo.svelte";
@@ -86,7 +85,7 @@
}
</script>
<OverlayPanel bind:this={panel} popup class="admin-panel" beforeHide={() => !isSaving} on:hide on:show>
<OverlayPanel bind:this={panel} popup class="superuser-panel" beforeHide={() => !isSaving} on:hide on:show>
<svelte:fragment slot="header">
<h4>Logs settings</h4>
</svelte:fragment>
@@ -114,10 +113,15 @@
</div>
</Field>
<Field class="form-field form-field-toggle" name="logs.logIp" let:uniqueId>
<input type="checkbox" id={uniqueId} bind:checked={formSettings.logs.logIp} />
<Field class="form-field form-field-toggle" name="logs.logIP" let:uniqueId>
<input type="checkbox" id={uniqueId} bind:checked={formSettings.logs.logIP} />
<label for={uniqueId}>Enable IP logging</label>
</Field>
<Field class="form-field form-field-toggle" name="logs.logAuthId" let:uniqueId>
<input type="checkbox" id={uniqueId} bind:checked={formSettings.logs.logAuthId} />
<label for={uniqueId}>Enable Auth Id logging</label>
</Field>
</form>
{/if}
+14 -13
View File
@@ -16,8 +16,8 @@
$pageTitle = "Logs";
const LOG_QUERY_KEY = "logId";
const ADMIN_REQUESTS_QUERY_KEY = "adminRequests";
const ADMIN_REQUESTS_STORAGE_KEY = "adminLogRequests";
const ADMIN_REQUESTS_QUERY_KEY = "superuserRequests";
const ADMIN_REQUESTS_STORAGE_KEY = "superuserLogRequests";
const initialQueryParams = new URLSearchParams($querystring);
@@ -25,20 +25,21 @@
let logsSettingsPanel;
let refreshKey = 1;
let filter = initialQueryParams.get("filter") || "";
let withAdminLogs =
let zoom = {};
let withSuperuserLogs =
(initialQueryParams.get(ADMIN_REQUESTS_QUERY_KEY) ||
window.localStorage?.getItem(ADMIN_REQUESTS_STORAGE_KEY)) << 0;
let initialWithAdminLogs = withAdminLogs;
let initialWithSuperuserLogs = withSuperuserLogs;
$: if (initialQueryParams.get(LOG_QUERY_KEY) && logViewPanel) {
logViewPanel.show(initialQueryParams.get(LOG_QUERY_KEY));
}
$: presets = !withAdminLogs ? 'data.auth!="admin"' : "";
$: presets = !withSuperuserLogs ? 'data.auth!="_superusers"' : "";
$: if (initialWithAdminLogs != withAdminLogs) {
initialWithAdminLogs = withAdminLogs;
window.localStorage?.setItem(ADMIN_REQUESTS_STORAGE_KEY, withAdminLogs << 0);
$: if (initialWithSuperuserLogs != withSuperuserLogs) {
initialWithSuperuserLogs = withSuperuserLogs;
window.localStorage?.setItem(ADMIN_REQUESTS_STORAGE_KEY, withSuperuserLogs << 0);
updateQueryParams();
}
@@ -53,7 +54,7 @@
function updateQueryParams(extra = {}) {
let queryParams = {};
queryParams.filter = filter || null;
queryParams[ADMIN_REQUESTS_QUERY_KEY] = withAdminLogs << 0 || null;
queryParams[ADMIN_REQUESTS_QUERY_KEY] = withSuperuserLogs << 0 || null;
CommonHelper.replaceHashQueryParams(Object.assign(queryParams, extra));
}
</script>
@@ -81,8 +82,8 @@
<div class="inline-flex">
<Field class="form-field form-field-toggle m-0" let:uniqueId>
<input type="checkbox" id={uniqueId} bind:checked={withAdminLogs} />
<label for={uniqueId}>Include requests by admins</label>
<input type="checkbox" id={uniqueId} bind:checked={withSuperuserLogs} />
<label for={uniqueId}>Include requests by superusers</label>
</Field>
</div>
</header>
@@ -96,12 +97,12 @@
<LogsLevelsInfo class="block txt-sm txt-hint m-t-xs m-b-base" />
{#key refreshKey}
<LogsChart {filter} {presets} />
<LogsChart bind:zoom {filter} {presets} />
{/key}
</div>
{#key refreshKey}
<LogsList bind:filter {presets} on:select={(e) => logViewPanel?.show(e?.detail)} />
<LogsList bind:filter bind:zoom {presets} on:select={(e) => logViewPanel?.show(e?.detail)} />
{/key}
</PageWrapper>