mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-09-02 16:37:14 +00:00
feat(xray/dns): align DNS settings with Xray docs + UI polish
- DNS server modal: rename expectIPs -> expectedIPs (per docs); add per-server tag, clientIP, serveStale, serveExpiredTTL, timeoutMs; flip skipFallback default to false; hydration still accepts legacy expectIPs for back-compat. - DNS tab: add hosts editor (domain -> IP/array), serveStale + serveExpiredTTL controls, "Use Preset" button bringing back the legacy preset gallery (Google / Cloudflare / AdGuard + Family variants — fixed AdGuard Family IPs that were wrong in legacy), and a "Delete All" button to wipe the server list at once. - i18n: add 15 new dns.* keys across all 13 locales. - Frontend-wide formatter pass on Vue components (whitespace and attribute layout only, no behavior changes). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -89,8 +89,8 @@ const modalWidth = computed(() => (isMobile.value ? '100vw' : '800px'));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-modal :open="open" :closable="true" :footer="null" :width="modalWidth"
|
||||
:class="{ 'logmodal-mobile': isMobile }" @cancel="close">
|
||||
<a-modal :open="open" :closable="true" :footer="null" :width="modalWidth" :class="{ 'logmodal-mobile': isMobile }"
|
||||
@cancel="close">
|
||||
<template #title>
|
||||
{{ t('pages.index.logs') }}
|
||||
<SyncOutlined :spin="loading" class="reload-icon" @click="refresh" />
|
||||
@@ -178,6 +178,7 @@ const modalWidth = computed(() => (isMobile.value ? '100vw' : '800px'));
|
||||
flex-wrap: wrap;
|
||||
row-gap: 8px;
|
||||
}
|
||||
|
||||
.log-toolbar .download-item {
|
||||
margin-left: auto;
|
||||
}
|
||||
@@ -185,12 +186,12 @@ const modalWidth = computed(() => (isMobile.value ? '100vw' : '800px'));
|
||||
.log-container {
|
||||
/* Per-theme palette — overridden in body.dark / [data-theme="ultra-dark"]
|
||||
below so each level keeps ≥4.5:1 contrast against the container. */
|
||||
--log-stamp: #3c89e8;
|
||||
--log-debug: #3c89e8;
|
||||
--log-info: #008771;
|
||||
--log-notice: #008771;
|
||||
--log-stamp: #3c89e8;
|
||||
--log-debug: #3c89e8;
|
||||
--log-info: #008771;
|
||||
--log-notice: #008771;
|
||||
--log-warning: #f37b24;
|
||||
--log-error: #e04141;
|
||||
--log-error: #e04141;
|
||||
--log-unknown: #595959;
|
||||
--log-divider: rgba(128, 128, 128, 0.18);
|
||||
|
||||
@@ -208,14 +209,37 @@ const modalWidth = computed(() => (isMobile.value ? '100vw' : '800px'));
|
||||
background: rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.log-stamp { color: var(--log-stamp); }
|
||||
.log-level { margin-left: 4px; }
|
||||
.level-debug { color: var(--log-debug); }
|
||||
.level-info { color: var(--log-info); }
|
||||
.level-notice { color: var(--log-notice); }
|
||||
.level-warning { color: var(--log-warning); }
|
||||
.level-error { color: var(--log-error); }
|
||||
.level-unknown { color: var(--log-unknown); }
|
||||
.log-stamp {
|
||||
color: var(--log-stamp);
|
||||
}
|
||||
|
||||
.log-level {
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.level-debug {
|
||||
color: var(--log-debug);
|
||||
}
|
||||
|
||||
.level-info {
|
||||
color: var(--log-info);
|
||||
}
|
||||
|
||||
.level-notice {
|
||||
color: var(--log-notice);
|
||||
}
|
||||
|
||||
.level-warning {
|
||||
color: var(--log-warning);
|
||||
}
|
||||
|
||||
.level-error {
|
||||
color: var(--log-error);
|
||||
}
|
||||
|
||||
.level-unknown {
|
||||
color: var(--log-unknown);
|
||||
}
|
||||
|
||||
.log-container-mobile {
|
||||
padding: 8px;
|
||||
@@ -229,7 +253,7 @@ const modalWidth = computed(() => (isMobile.value ? '100vw' : '800px'));
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.log-line + .log-line {
|
||||
.log-line+.log-line {
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
@@ -237,7 +261,11 @@ const modalWidth = computed(() => (isMobile.value ? '100vw' : '800px'));
|
||||
border-bottom: 1px solid var(--log-divider);
|
||||
padding: 8px 0;
|
||||
}
|
||||
.log-card:last-child { border-bottom: 0; }
|
||||
|
||||
.log-card:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.log-card-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -245,6 +273,7 @@ const modalWidth = computed(() => (isMobile.value ? '100vw' : '800px'));
|
||||
gap: 8px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.log-time {
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
@@ -253,11 +282,13 @@ const modalWidth = computed(() => (isMobile.value ? '100vw' : '800px'));
|
||||
font-size: 12px;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
|
||||
.log-date {
|
||||
font-size: 10px;
|
||||
font-weight: 500;
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
.log-level-badge {
|
||||
display: inline-block;
|
||||
font-size: 10px;
|
||||
@@ -270,10 +301,12 @@ const modalWidth = computed(() => (isMobile.value ? '100vw' : '800px'));
|
||||
white-space: nowrap;
|
||||
background: color-mix(in srgb, currentColor 14%, transparent);
|
||||
}
|
||||
|
||||
.log-body {
|
||||
font-size: 12px;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.log-body-text {
|
||||
margin-left: 4px;
|
||||
}
|
||||
@@ -283,23 +316,23 @@ const modalWidth = computed(() => (isMobile.value ? '100vw' : '800px'));
|
||||
border-color: rgba(255, 255, 255, 0.1);
|
||||
color: rgba(255, 255, 255, 0.88);
|
||||
|
||||
--log-stamp: #6aa6ee;
|
||||
--log-debug: #6aa6ee;
|
||||
--log-info: #4ed3a6;
|
||||
--log-notice: #4ed3a6;
|
||||
--log-stamp: #6aa6ee;
|
||||
--log-debug: #6aa6ee;
|
||||
--log-info: #4ed3a6;
|
||||
--log-notice: #4ed3a6;
|
||||
--log-warning: #ffb872;
|
||||
--log-error: #ff7575;
|
||||
--log-error: #ff7575;
|
||||
--log-unknown: #b5b5b5;
|
||||
--log-divider: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
:global([data-theme="ultra-dark"]) .log-container {
|
||||
--log-stamp: #7fb6f1;
|
||||
--log-debug: #7fb6f1;
|
||||
--log-info: #5fd9b0;
|
||||
--log-notice: #5fd9b0;
|
||||
--log-stamp: #7fb6f1;
|
||||
--log-debug: #7fb6f1;
|
||||
--log-info: #5fd9b0;
|
||||
--log-notice: #5fd9b0;
|
||||
--log-warning: #ffcc88;
|
||||
--log-error: #ff8a8a;
|
||||
--log-error: #ff8a8a;
|
||||
--log-unknown: #c4c4c4;
|
||||
--log-divider: rgba(255, 255, 255, 0.12);
|
||||
}
|
||||
@@ -310,10 +343,12 @@ const modalWidth = computed(() => (isMobile.value ? '100vw' : '800px'));
|
||||
padding-bottom: 0 !important;
|
||||
max-width: 100vw !important;
|
||||
}
|
||||
|
||||
:global(.logmodal-mobile .ant-modal-content) {
|
||||
border-radius: 0;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
:global(.logmodal-mobile .ant-modal-body) {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
@@ -30,8 +30,8 @@ const trailColor = 'rgba(128, 128, 128, 0.25)';
|
||||
<a-col :xs="24" :md="12">
|
||||
<a-row>
|
||||
<a-col :span="12" class="text-center">
|
||||
<a-progress type="dashboard" status="normal" :stroke-color="status.cpu.color"
|
||||
:trail-color="trailColor" :percent="status.cpu.percent" :width="gaugeSize" />
|
||||
<a-progress type="dashboard" status="normal" :stroke-color="status.cpu.color" :trail-color="trailColor"
|
||||
:percent="status.cpu.percent" :width="gaugeSize" />
|
||||
<div>
|
||||
<b>{{ t('pages.index.cpu') }}:</b> {{ CPUFormatter.cpuCoreFormat(status.cpuCores) }}
|
||||
<a-tooltip>
|
||||
@@ -46,8 +46,8 @@ const trailColor = 'rgba(128, 128, 128, 0.25)';
|
||||
</a-col>
|
||||
|
||||
<a-col :span="12" class="text-center">
|
||||
<a-progress type="dashboard" status="normal" :stroke-color="status.mem.color"
|
||||
:trail-color="trailColor" :percent="status.mem.percent" :width="gaugeSize" />
|
||||
<a-progress type="dashboard" status="normal" :stroke-color="status.mem.color" :trail-color="trailColor"
|
||||
:percent="status.mem.percent" :width="gaugeSize" />
|
||||
<div>
|
||||
<b>{{ t('pages.index.memory') }}:</b> {{ SizeFormatter.sizeFormat(status.mem.current) }} /
|
||||
{{ SizeFormatter.sizeFormat(status.mem.total) }}
|
||||
@@ -60,8 +60,8 @@ const trailColor = 'rgba(128, 128, 128, 0.25)';
|
||||
<a-col :xs="24" :md="12">
|
||||
<a-row>
|
||||
<a-col :span="12" class="text-center">
|
||||
<a-progress type="dashboard" status="normal" :stroke-color="status.swap.color"
|
||||
:trail-color="trailColor" :percent="status.swap.percent" :width="gaugeSize" />
|
||||
<a-progress type="dashboard" status="normal" :stroke-color="status.swap.color" :trail-color="trailColor"
|
||||
:percent="status.swap.percent" :width="gaugeSize" />
|
||||
<div>
|
||||
<b>{{ t('pages.index.swap') }}:</b> {{ SizeFormatter.sizeFormat(status.swap.current) }} /
|
||||
{{ SizeFormatter.sizeFormat(status.swap.total) }}
|
||||
@@ -69,8 +69,8 @@ const trailColor = 'rgba(128, 128, 128, 0.25)';
|
||||
</a-col>
|
||||
|
||||
<a-col :span="12" class="text-center">
|
||||
<a-progress type="dashboard" status="normal" :stroke-color="status.disk.color"
|
||||
:trail-color="trailColor" :percent="status.disk.percent" :width="gaugeSize" />
|
||||
<a-progress type="dashboard" status="normal" :stroke-color="status.disk.color" :trail-color="trailColor"
|
||||
:percent="status.disk.percent" :width="gaugeSize" />
|
||||
<div>
|
||||
<b>{{ t('pages.index.storage') }}:</b> {{ SizeFormatter.sizeFormat(status.disk.current) }} /
|
||||
{{ SizeFormatter.sizeFormat(status.disk.total) }}
|
||||
|
||||
@@ -130,12 +130,9 @@ watch([activeKey, bucket], () => {
|
||||
<div class="cpu-chart-meta">
|
||||
Timeframe: {{ bucket }} sec per point (total {{ points.length }} points)
|
||||
</div>
|
||||
<Sparkline :data="points" :labels="labels" :vb-width="840" :height="220"
|
||||
:stroke="strokeColor" :stroke-width="2.2"
|
||||
:show-grid="true" :show-axes="true" :tick-count-x="5"
|
||||
:max-points="points.length || 1"
|
||||
:fill-opacity="0.18" :marker-radius="3.2" :show-tooltip="true"
|
||||
:value-min="0" :value-max="activeMetric?.valueMax ?? null"
|
||||
<Sparkline :data="points" :labels="labels" :vb-width="840" :height="220" :stroke="strokeColor" :stroke-width="2.2"
|
||||
:show-grid="true" :show-axes="true" :tick-count-x="5" :max-points="points.length || 1" :fill-opacity="0.18"
|
||||
:marker-radius="3.2" :show-tooltip="true" :value-min="0" :value-max="activeMetric?.valueMax ?? null"
|
||||
:y-formatter="yFormatter" />
|
||||
</div>
|
||||
</a-modal>
|
||||
|
||||
@@ -161,7 +161,12 @@ const modalWidth = computed(() => (isMobile.value ? '100vw' : '80vw'));
|
||||
<table v-else class="xraylog-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th><th>From</th><th>To</th><th>Inbound</th><th>Outbound</th><th>Email</th>
|
||||
<th>Date</th>
|
||||
<th>From</th>
|
||||
<th>To</th>
|
||||
<th>Inbound</th>
|
||||
<th>Outbound</th>
|
||||
<th>Email</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -190,9 +195,11 @@ const modalWidth = computed(() => (isMobile.value ? '100vw' : '80vw'));
|
||||
flex-wrap: wrap;
|
||||
row-gap: 8px;
|
||||
}
|
||||
|
||||
.log-toolbar .filter-item {
|
||||
flex: 1 1 160px;
|
||||
}
|
||||
|
||||
.log-toolbar .download-item {
|
||||
margin-left: auto;
|
||||
}
|
||||
@@ -201,7 +208,7 @@ const modalWidth = computed(() => (isMobile.value ? '100vw' : '80vw'));
|
||||
/* Per-theme palette — overridden in body.dark / [data-theme="ultra-dark"]
|
||||
below so blocked/proxy rows keep ≥4.5:1 contrast on darker surfaces. */
|
||||
--log-blocked: #e04141;
|
||||
--log-proxy: #3c89e8;
|
||||
--log-proxy: #3c89e8;
|
||||
--log-divider: rgba(128, 128, 128, 0.18);
|
||||
|
||||
margin-top: 12px;
|
||||
@@ -215,6 +222,7 @@ const modalWidth = computed(() => (isMobile.value ? '100vw' : '80vw'));
|
||||
border-radius: 6px;
|
||||
background: rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.log-container-mobile {
|
||||
padding: 8px;
|
||||
font-size: 12px;
|
||||
@@ -231,7 +239,10 @@ const modalWidth = computed(() => (isMobile.value ? '100vw' : '80vw'));
|
||||
border-bottom: 1px solid var(--log-divider);
|
||||
padding: 8px 0;
|
||||
}
|
||||
.log-card:last-child { border-bottom: 0; }
|
||||
|
||||
.log-card:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.log-card-head {
|
||||
display: flex;
|
||||
@@ -240,11 +251,13 @@ const modalWidth = computed(() => (isMobile.value ? '100vw' : '80vw'));
|
||||
gap: 8px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.log-time {
|
||||
font-weight: 600;
|
||||
font-size: 12px;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
|
||||
.log-event-tag {
|
||||
margin: 0;
|
||||
font-size: 10px;
|
||||
@@ -260,9 +273,11 @@ const modalWidth = computed(() => (isMobile.value ? '100vw' : '80vw'));
|
||||
font-size: 12px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.log-addr {
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.log-arrow {
|
||||
opacity: 0.5;
|
||||
}
|
||||
@@ -274,12 +289,14 @@ const modalWidth = computed(() => (isMobile.value ? '100vw' : '80vw'));
|
||||
font-size: 11px;
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
.log-meta-pair {
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
gap: 4px;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.log-meta-key {
|
||||
font-size: 10px;
|
||||
text-transform: uppercase;
|
||||
@@ -293,13 +310,13 @@ const modalWidth = computed(() => (isMobile.value ? '100vw' : '80vw'));
|
||||
color: rgba(255, 255, 255, 0.88);
|
||||
|
||||
--log-blocked: #ff7575;
|
||||
--log-proxy: #6aa6ee;
|
||||
--log-proxy: #6aa6ee;
|
||||
--log-divider: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
:global([data-theme="ultra-dark"]) .log-container {
|
||||
--log-blocked: #ff8a8a;
|
||||
--log-proxy: #7fb6f1;
|
||||
--log-proxy: #7fb6f1;
|
||||
--log-divider: rgba(255, 255, 255, 0.12);
|
||||
}
|
||||
|
||||
@@ -309,10 +326,12 @@ const modalWidth = computed(() => (isMobile.value ? '100vw' : '80vw'));
|
||||
padding-bottom: 0 !important;
|
||||
max-width: 100vw !important;
|
||||
}
|
||||
|
||||
:global(.xraylog-modal-mobile .ant-modal-content) {
|
||||
border-radius: 0;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
:global(.xraylog-modal-mobile .ant-modal-body) {
|
||||
padding: 12px;
|
||||
}
|
||||
@@ -321,11 +340,18 @@ const modalWidth = computed(() => (isMobile.value ? '100vw' : '80vw'));
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.xraylog-table td,
|
||||
.xraylog-table th {
|
||||
padding: 2px 15px;
|
||||
text-align: left;
|
||||
}
|
||||
.xraylog-table .log-row-1 { color: var(--log-blocked); }
|
||||
.xraylog-table .log-row-2 { color: var(--log-proxy); }
|
||||
|
||||
.xraylog-table .log-row-1 {
|
||||
color: var(--log-blocked);
|
||||
}
|
||||
|
||||
.xraylog-table .log-row-2 {
|
||||
color: var(--log-proxy);
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user