perf(stability): add request metrics and resilient API retries
This commit is contained in:
@@ -4,6 +4,10 @@ import { ElMessage, ElMessageBox } from 'element-plus'
|
|||||||
let lastToastKey = ''
|
let lastToastKey = ''
|
||||||
let lastToastAt = 0
|
let lastToastAt = 0
|
||||||
|
|
||||||
|
const RETRYABLE_STATUS = new Set([408, 425, 429, 500, 502, 503, 504])
|
||||||
|
const MAX_RETRY_COUNT = 1
|
||||||
|
const RETRY_BASE_DELAY_MS = 300
|
||||||
|
|
||||||
function toastErrorOnce(key, message, minIntervalMs = 1500) {
|
function toastErrorOnce(key, message, minIntervalMs = 1500) {
|
||||||
const now = Date.now()
|
const now = Date.now()
|
||||||
if (key === lastToastKey && now - lastToastAt < minIntervalMs) return
|
if (key === lastToastKey && now - lastToastAt < minIntervalMs) return
|
||||||
@@ -18,6 +22,41 @@ function getCookie(name) {
|
|||||||
return match ? decodeURIComponent(match[1]) : ''
|
return match ? decodeURIComponent(match[1]) : ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isIdempotentMethod(method) {
|
||||||
|
return ['GET', 'HEAD', 'OPTIONS'].includes(String(method || 'GET').toUpperCase())
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldRetryRequest(error) {
|
||||||
|
const config = error?.config
|
||||||
|
if (!config || config.__no_retry) return false
|
||||||
|
if (!isIdempotentMethod(config.method)) return false
|
||||||
|
|
||||||
|
const retried = Number(config.__retry_count || 0)
|
||||||
|
if (retried >= MAX_RETRY_COUNT) return false
|
||||||
|
|
||||||
|
const code = String(error?.code || '')
|
||||||
|
if (code === 'ECONNABORTED' || code === 'ERR_NETWORK') return true
|
||||||
|
|
||||||
|
const status = Number(error?.response?.status || 0)
|
||||||
|
return RETRYABLE_STATUS.has(status)
|
||||||
|
}
|
||||||
|
|
||||||
|
function delay(ms) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
window.setTimeout(resolve, Math.max(0, Number(ms || 0)))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function retryRequestOnce(error, client) {
|
||||||
|
const config = error?.config || {}
|
||||||
|
const retried = Number(config.__retry_count || 0)
|
||||||
|
config.__retry_count = retried + 1
|
||||||
|
|
||||||
|
const backoffMs = RETRY_BASE_DELAY_MS * (retried + 1)
|
||||||
|
await delay(backoffMs)
|
||||||
|
return client.request(config)
|
||||||
|
}
|
||||||
|
|
||||||
export const api = axios.create({
|
export const api = axios.create({
|
||||||
baseURL: '/yuyx/api',
|
baseURL: '/yuyx/api',
|
||||||
timeout: 30_000,
|
timeout: 30_000,
|
||||||
@@ -76,6 +115,10 @@ api.interceptors.response.use(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (shouldRetryRequest(error)) {
|
||||||
|
return retryRequestOnce(error, api)
|
||||||
|
}
|
||||||
|
|
||||||
if (status === 401) {
|
if (status === 401) {
|
||||||
toastErrorOnce('401', message || '登录已过期,请重新登录', 3000)
|
toastErrorOnce('401', message || '登录已过期,请重新登录', 3000)
|
||||||
const pathname = window.location?.pathname || ''
|
const pathname = window.location?.pathname || ''
|
||||||
|
|||||||
@@ -34,7 +34,11 @@ async function refreshStats() {
|
|||||||
|
|
||||||
const loadingBadges = ref(false)
|
const loadingBadges = ref(false)
|
||||||
const pendingFeedbackCount = ref(0)
|
const pendingFeedbackCount = ref(0)
|
||||||
let badgeTimer
|
|
||||||
|
const BADGE_POLL_ACTIVE_MS = 60_000
|
||||||
|
const BADGE_POLL_HIDDEN_MS = 180_000
|
||||||
|
|
||||||
|
let badgeTimer = null
|
||||||
|
|
||||||
async function refreshNavBadges(partial = null) {
|
async function refreshNavBadges(partial = null) {
|
||||||
if (partial && typeof partial === 'object') {
|
if (partial && typeof partial === 'object') {
|
||||||
@@ -55,6 +59,34 @@ async function refreshNavBadges(partial = null) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isPageHidden() {
|
||||||
|
if (typeof document === 'undefined') return false
|
||||||
|
return document.visibilityState === 'hidden'
|
||||||
|
}
|
||||||
|
|
||||||
|
function currentBadgePollDelay() {
|
||||||
|
return isPageHidden() ? BADGE_POLL_HIDDEN_MS : BADGE_POLL_ACTIVE_MS
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopBadgePolling() {
|
||||||
|
if (!badgeTimer) return
|
||||||
|
window.clearTimeout(badgeTimer)
|
||||||
|
badgeTimer = null
|
||||||
|
}
|
||||||
|
|
||||||
|
function scheduleBadgePolling() {
|
||||||
|
stopBadgePolling()
|
||||||
|
badgeTimer = window.setTimeout(async () => {
|
||||||
|
badgeTimer = null
|
||||||
|
await refreshNavBadges().catch(() => {})
|
||||||
|
scheduleBadgePolling()
|
||||||
|
}, currentBadgePollDelay())
|
||||||
|
}
|
||||||
|
|
||||||
|
function onVisibilityChange() {
|
||||||
|
scheduleBadgePolling()
|
||||||
|
}
|
||||||
|
|
||||||
provide('refreshStats', refreshStats)
|
provide('refreshStats', refreshStats)
|
||||||
provide('adminStats', stats)
|
provide('adminStats', stats)
|
||||||
provide('refreshNavBadges', refreshNavBadges)
|
provide('refreshNavBadges', refreshNavBadges)
|
||||||
@@ -75,12 +107,14 @@ onMounted(async () => {
|
|||||||
|
|
||||||
await refreshStats()
|
await refreshStats()
|
||||||
await refreshNavBadges()
|
await refreshNavBadges()
|
||||||
badgeTimer = window.setInterval(refreshNavBadges, 60_000)
|
scheduleBadgePolling()
|
||||||
|
window.addEventListener('visibilitychange', onVisibilityChange)
|
||||||
})
|
})
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
mediaQuery?.removeEventListener?.('change', syncIsMobile)
|
mediaQuery?.removeEventListener?.('change', syncIsMobile)
|
||||||
window.clearInterval(badgeTimer)
|
stopBadgePolling()
|
||||||
|
window.removeEventListener('visibilitychange', onVisibilityChange)
|
||||||
})
|
})
|
||||||
|
|
||||||
const menuItems = [
|
const menuItems = [
|
||||||
|
|||||||
@@ -4,6 +4,10 @@ import { ElMessage } from 'element-plus'
|
|||||||
let lastToastKey = ''
|
let lastToastKey = ''
|
||||||
let lastToastAt = 0
|
let lastToastAt = 0
|
||||||
|
|
||||||
|
const RETRYABLE_STATUS = new Set([408, 425, 429, 500, 502, 503, 504])
|
||||||
|
const MAX_RETRY_COUNT = 1
|
||||||
|
const RETRY_BASE_DELAY_MS = 300
|
||||||
|
|
||||||
function toastErrorOnce(key, message, minIntervalMs = 1500) {
|
function toastErrorOnce(key, message, minIntervalMs = 1500) {
|
||||||
const now = Date.now()
|
const now = Date.now()
|
||||||
if (key === lastToastKey && now - lastToastAt < minIntervalMs) return
|
if (key === lastToastKey && now - lastToastAt < minIntervalMs) return
|
||||||
@@ -18,6 +22,41 @@ function getCookie(name) {
|
|||||||
return match ? decodeURIComponent(match[1]) : ''
|
return match ? decodeURIComponent(match[1]) : ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isIdempotentMethod(method) {
|
||||||
|
return ['GET', 'HEAD', 'OPTIONS'].includes(String(method || 'GET').toUpperCase())
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldRetryRequest(error) {
|
||||||
|
const config = error?.config
|
||||||
|
if (!config || config.__no_retry) return false
|
||||||
|
if (!isIdempotentMethod(config.method)) return false
|
||||||
|
|
||||||
|
const retried = Number(config.__retry_count || 0)
|
||||||
|
if (retried >= MAX_RETRY_COUNT) return false
|
||||||
|
|
||||||
|
const code = String(error?.code || '')
|
||||||
|
if (code === 'ECONNABORTED' || code === 'ERR_NETWORK') return true
|
||||||
|
|
||||||
|
const status = Number(error?.response?.status || 0)
|
||||||
|
return RETRYABLE_STATUS.has(status)
|
||||||
|
}
|
||||||
|
|
||||||
|
function delay(ms) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
window.setTimeout(resolve, Math.max(0, Number(ms || 0)))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function retryRequestOnce(error, client) {
|
||||||
|
const config = error?.config || {}
|
||||||
|
const retried = Number(config.__retry_count || 0)
|
||||||
|
config.__retry_count = retried + 1
|
||||||
|
|
||||||
|
const backoffMs = RETRY_BASE_DELAY_MS * (retried + 1)
|
||||||
|
await delay(backoffMs)
|
||||||
|
return client.request(config)
|
||||||
|
}
|
||||||
|
|
||||||
export const publicApi = axios.create({
|
export const publicApi = axios.create({
|
||||||
baseURL: '/api',
|
baseURL: '/api',
|
||||||
timeout: 30_000,
|
timeout: 30_000,
|
||||||
@@ -39,6 +78,10 @@ publicApi.interceptors.request.use((config) => {
|
|||||||
publicApi.interceptors.response.use(
|
publicApi.interceptors.response.use(
|
||||||
(response) => response,
|
(response) => response,
|
||||||
(error) => {
|
(error) => {
|
||||||
|
if (shouldRetryRequest(error)) {
|
||||||
|
return retryRequestOnce(error, publicApi)
|
||||||
|
}
|
||||||
|
|
||||||
const status = error?.response?.status
|
const status = error?.response?.status
|
||||||
const payload = error?.response?.data
|
const payload = error?.response?.data
|
||||||
const message = payload?.error || payload?.message || error?.message || '请求失败'
|
const message = payload?.error || payload?.message || error?.message || '请求失败'
|
||||||
|
|||||||
59
app.py
59
app.py
@@ -17,8 +17,9 @@ import os
|
|||||||
import signal
|
import signal
|
||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
|
import time
|
||||||
|
|
||||||
from flask import Flask, jsonify, redirect, request, send_from_directory, session, url_for
|
from flask import Flask, g, jsonify, redirect, request, send_from_directory, session, url_for
|
||||||
from flask_login import LoginManager, current_user
|
from flask_login import LoginManager, current_user
|
||||||
from flask_socketio import SocketIO
|
from flask_socketio import SocketIO
|
||||||
|
|
||||||
@@ -35,6 +36,7 @@ from routes import register_blueprints
|
|||||||
from security import init_security_middleware
|
from security import init_security_middleware
|
||||||
from services.checkpoints import init_checkpoint_manager
|
from services.checkpoints import init_checkpoint_manager
|
||||||
from services.maintenance import start_cleanup_scheduler, start_kdocs_monitor
|
from services.maintenance import start_cleanup_scheduler, start_kdocs_monitor
|
||||||
|
from services.request_metrics import record_request_metric
|
||||||
from services.models import User
|
from services.models import User
|
||||||
from services.runtime import init_runtime
|
from services.runtime import init_runtime
|
||||||
from services.scheduler import scheduled_task_worker
|
from services.scheduler import scheduled_task_worker
|
||||||
@@ -98,6 +100,20 @@ init_logging(log_level=config.LOG_LEVEL, log_file=config.LOG_FILE)
|
|||||||
logger = get_logger("app")
|
logger = get_logger("app")
|
||||||
init_runtime(socketio=socketio, logger=logger)
|
init_runtime(socketio=socketio, logger=logger)
|
||||||
|
|
||||||
|
_API_DIAGNOSTIC_LOG = str(os.environ.get("API_DIAGNOSTIC_LOG", "0")).strip().lower() in {
|
||||||
|
"1",
|
||||||
|
"true",
|
||||||
|
"yes",
|
||||||
|
"on",
|
||||||
|
}
|
||||||
|
_API_DIAGNOSTIC_SLOW_MS = max(0.0, float(os.environ.get("API_DIAGNOSTIC_SLOW_MS", "0") or 0.0))
|
||||||
|
|
||||||
|
|
||||||
|
def _is_api_or_health_path(path: str) -> bool:
|
||||||
|
raw = str(path or "")
|
||||||
|
return raw.startswith("/api/") or raw.startswith("/yuyx/api/") or raw == "/health"
|
||||||
|
|
||||||
|
|
||||||
# 初始化安全中间件(需在其他中间件/Blueprint 之前注册)
|
# 初始化安全中间件(需在其他中间件/Blueprint 之前注册)
|
||||||
init_security_middleware(app)
|
init_security_middleware(app)
|
||||||
|
|
||||||
@@ -131,6 +147,11 @@ def unauthorized():
|
|||||||
return redirect(url_for("pages.login_page", next=request.url))
|
return redirect(url_for("pages.login_page", next=request.url))
|
||||||
|
|
||||||
|
|
||||||
|
@app.before_request
|
||||||
|
def track_request_start_time():
|
||||||
|
g.request_start_perf = time.perf_counter()
|
||||||
|
|
||||||
|
|
||||||
@app.before_request
|
@app.before_request
|
||||||
def enforce_csrf_protection():
|
def enforce_csrf_protection():
|
||||||
if request.method in {"GET", "HEAD", "OPTIONS"}:
|
if request.method in {"GET", "HEAD", "OPTIONS"}:
|
||||||
@@ -148,10 +169,40 @@ def enforce_csrf_protection():
|
|||||||
return jsonify({"error": "CSRF token missing or invalid"}), 403
|
return jsonify({"error": "CSRF token missing or invalid"}), 403
|
||||||
|
|
||||||
|
|
||||||
|
def _record_request_metric_after_response(response) -> None:
|
||||||
|
try:
|
||||||
|
started = float(getattr(g, "request_start_perf", 0.0) or 0.0)
|
||||||
|
if started <= 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
duration_ms = max(0.0, (time.perf_counter() - started) * 1000.0)
|
||||||
|
path = request.path or "/"
|
||||||
|
method = request.method or "GET"
|
||||||
|
status_code = int(getattr(response, "status_code", 0) or 0)
|
||||||
|
is_api = _is_api_or_health_path(path)
|
||||||
|
|
||||||
|
record_request_metric(
|
||||||
|
path=path,
|
||||||
|
method=method,
|
||||||
|
status_code=status_code,
|
||||||
|
duration_ms=duration_ms,
|
||||||
|
is_api=is_api,
|
||||||
|
)
|
||||||
|
|
||||||
|
if _API_DIAGNOSTIC_LOG and is_api:
|
||||||
|
is_slow = _API_DIAGNOSTIC_SLOW_MS > 0 and duration_ms >= _API_DIAGNOSTIC_SLOW_MS
|
||||||
|
is_server_error = status_code >= 500
|
||||||
|
if is_slow or is_server_error:
|
||||||
|
logger.warning(
|
||||||
|
f"[API-DIAG] {method} {path} -> {status_code} ({duration_ms:.1f}ms)"
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
@app.after_request
|
@app.after_request
|
||||||
def ensure_csrf_cookie(response):
|
def ensure_csrf_cookie(response):
|
||||||
if request.path.startswith("/static/"):
|
if not request.path.startswith("/static/"):
|
||||||
return response
|
|
||||||
token = session.get("csrf_token")
|
token = session.get("csrf_token")
|
||||||
if not token:
|
if not token:
|
||||||
token = generate_csrf_token()
|
token = generate_csrf_token()
|
||||||
@@ -162,6 +213,8 @@ def ensure_csrf_cookie(response):
|
|||||||
secure=bool(config.SESSION_COOKIE_SECURE),
|
secure=bool(config.SESSION_COOKIE_SECURE),
|
||||||
samesite=config.SESSION_COOKIE_SAMESITE,
|
samesite=config.SESSION_COOKIE_SAMESITE,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
_record_request_metric_after_response(response)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ from flask import Blueprint, jsonify
|
|||||||
|
|
||||||
import database
|
import database
|
||||||
import db_pool
|
import db_pool
|
||||||
|
from services.request_metrics import get_request_metrics_snapshot
|
||||||
from services.time_utils import get_beijing_now
|
from services.time_utils import get_beijing_now
|
||||||
|
|
||||||
health_bp = Blueprint("health", __name__)
|
health_bp = Blueprint("health", __name__)
|
||||||
@@ -57,6 +58,11 @@ def _build_runtime_metrics() -> dict:
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
metrics["requests"] = get_request_metrics_snapshot()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
return metrics
|
return metrics
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
171
services/request_metrics.py
Normal file
171
services/request_metrics.py
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
请求级运行指标(轻量内存版)
|
||||||
|
- 记录请求总量、状态分布、耗时
|
||||||
|
- 记录慢请求样本(环形队列)
|
||||||
|
- 输出健康检查可读快照
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
from collections import deque
|
||||||
|
from typing import Dict
|
||||||
|
|
||||||
|
_SLOW_REQUEST_MS = max(0.0, float(os.environ.get("REQUEST_METRICS_SLOW_MS", "1200") or 1200))
|
||||||
|
_PATH_STATS_LIMIT = max(20, int(os.environ.get("REQUEST_METRICS_PATH_LIMIT", "120") or 120))
|
||||||
|
_RECENT_SLOW_LIMIT = max(10, int(os.environ.get("REQUEST_METRICS_RECENT_SLOW_LIMIT", "20") or 20))
|
||||||
|
|
||||||
|
_lock = threading.Lock()
|
||||||
|
|
||||||
|
_state = {
|
||||||
|
"start_ts": time.time(),
|
||||||
|
"last_request_ts": 0.0,
|
||||||
|
"total_requests": 0,
|
||||||
|
"api_requests": 0,
|
||||||
|
"error_requests": 0,
|
||||||
|
"slow_requests": 0,
|
||||||
|
"duration_total_ms": 0.0,
|
||||||
|
"max_duration_ms": 0.0,
|
||||||
|
"status_counts": {},
|
||||||
|
"path_stats": {},
|
||||||
|
"recent_slow": deque(maxlen=_RECENT_SLOW_LIMIT),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _status_bucket(status_code: int) -> str:
|
||||||
|
code = int(status_code or 0)
|
||||||
|
if code <= 0:
|
||||||
|
return "unknown"
|
||||||
|
head = code // 100
|
||||||
|
if head in (1, 2, 3, 4, 5):
|
||||||
|
return f"{head}xx"
|
||||||
|
return str(code)
|
||||||
|
|
||||||
|
|
||||||
|
def _normalize_path(path: str) -> str:
|
||||||
|
text = str(path or "/")
|
||||||
|
if len(text) > 160:
|
||||||
|
return f"{text[:157]}..."
|
||||||
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
def _prune_path_stats(path_stats: Dict[str, dict]) -> None:
|
||||||
|
if len(path_stats) < _PATH_STATS_LIMIT:
|
||||||
|
return
|
||||||
|
|
||||||
|
# 删除最不活跃的路径,避免无限增长
|
||||||
|
removable_key = None
|
||||||
|
removable_score = None
|
||||||
|
for key, item in path_stats.items():
|
||||||
|
count = int(item.get("count", 0) or 0)
|
||||||
|
max_ms = float(item.get("max_ms", 0.0) or 0.0)
|
||||||
|
score = (count, max_ms)
|
||||||
|
if removable_score is None or score < removable_score:
|
||||||
|
removable_key = key
|
||||||
|
removable_score = score
|
||||||
|
|
||||||
|
if removable_key:
|
||||||
|
path_stats.pop(removable_key, None)
|
||||||
|
|
||||||
|
|
||||||
|
def record_request_metric(*, path: str, method: str, status_code: int, duration_ms: float, is_api: bool = False) -> None:
|
||||||
|
duration = max(0.0, float(duration_ms or 0.0))
|
||||||
|
code = int(status_code or 0)
|
||||||
|
method_name = str(method or "GET").upper()
|
||||||
|
normalized_path = _normalize_path(path)
|
||||||
|
route_key = f"{method_name} {normalized_path}"
|
||||||
|
now = time.time()
|
||||||
|
|
||||||
|
with _lock:
|
||||||
|
_state["total_requests"] += 1
|
||||||
|
if is_api:
|
||||||
|
_state["api_requests"] += 1
|
||||||
|
if code >= 500:
|
||||||
|
_state["error_requests"] += 1
|
||||||
|
|
||||||
|
_state["last_request_ts"] = now
|
||||||
|
_state["duration_total_ms"] += duration
|
||||||
|
if duration > _state["max_duration_ms"]:
|
||||||
|
_state["max_duration_ms"] = duration
|
||||||
|
|
||||||
|
bucket = _status_bucket(code)
|
||||||
|
status_counts = _state["status_counts"]
|
||||||
|
status_counts[bucket] = int(status_counts.get(bucket, 0) or 0) + 1
|
||||||
|
|
||||||
|
path_stats = _state["path_stats"]
|
||||||
|
if route_key not in path_stats:
|
||||||
|
_prune_path_stats(path_stats)
|
||||||
|
path_stats[route_key] = {
|
||||||
|
"count": 0,
|
||||||
|
"total_ms": 0.0,
|
||||||
|
"max_ms": 0.0,
|
||||||
|
"status_5xx": 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
item = path_stats[route_key]
|
||||||
|
item["count"] = int(item.get("count", 0) or 0) + 1
|
||||||
|
item["total_ms"] = float(item.get("total_ms", 0.0) or 0.0) + duration
|
||||||
|
if duration > float(item.get("max_ms", 0.0) or 0.0):
|
||||||
|
item["max_ms"] = duration
|
||||||
|
if code >= 500:
|
||||||
|
item["status_5xx"] = int(item.get("status_5xx", 0) or 0) + 1
|
||||||
|
|
||||||
|
if _SLOW_REQUEST_MS > 0 and duration >= _SLOW_REQUEST_MS:
|
||||||
|
_state["slow_requests"] += 1
|
||||||
|
_state["recent_slow"].append(
|
||||||
|
{
|
||||||
|
"path": normalized_path,
|
||||||
|
"method": method_name,
|
||||||
|
"status": code,
|
||||||
|
"duration_ms": round(duration, 2),
|
||||||
|
"time": int(now),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_request_metrics_snapshot() -> dict:
|
||||||
|
with _lock:
|
||||||
|
total_requests = int(_state["total_requests"])
|
||||||
|
duration_total_ms = float(_state["duration_total_ms"])
|
||||||
|
avg_duration_ms = round((duration_total_ms / total_requests), 2) if total_requests > 0 else 0.0
|
||||||
|
|
||||||
|
path_rows = []
|
||||||
|
for key, item in _state["path_stats"].items():
|
||||||
|
count = int(item.get("count", 0) or 0)
|
||||||
|
total_ms = float(item.get("total_ms", 0.0) or 0.0)
|
||||||
|
avg_ms = round((total_ms / count), 2) if count > 0 else 0.0
|
||||||
|
path_rows.append(
|
||||||
|
{
|
||||||
|
"path": key,
|
||||||
|
"count": count,
|
||||||
|
"avg_ms": avg_ms,
|
||||||
|
"max_ms": round(float(item.get("max_ms", 0.0) or 0.0), 2),
|
||||||
|
"status_5xx": int(item.get("status_5xx", 0) or 0),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
top_paths = sorted(
|
||||||
|
path_rows,
|
||||||
|
key=lambda row: (float(row.get("max_ms", 0.0)), float(row.get("avg_ms", 0.0)), int(row.get("count", 0))),
|
||||||
|
reverse=True,
|
||||||
|
)[:8]
|
||||||
|
|
||||||
|
return {
|
||||||
|
"since_ts": int(_state["start_ts"]),
|
||||||
|
"uptime_seconds": max(0, int(time.time() - float(_state["start_ts"]))),
|
||||||
|
"last_request_ts": int(_state["last_request_ts"] or 0),
|
||||||
|
"total_requests": total_requests,
|
||||||
|
"api_requests": int(_state["api_requests"]),
|
||||||
|
"error_requests": int(_state["error_requests"]),
|
||||||
|
"slow_requests": int(_state["slow_requests"]),
|
||||||
|
"avg_duration_ms": avg_duration_ms,
|
||||||
|
"max_duration_ms": round(float(_state["max_duration_ms"]), 2),
|
||||||
|
"status_counts": dict(_state["status_counts"]),
|
||||||
|
"top_paths": top_paths,
|
||||||
|
"recent_slow": list(_state["recent_slow"]),
|
||||||
|
"slow_threshold_ms": _SLOW_REQUEST_MS,
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"_MetricGrid-Cbhb9OGV.js": {
|
"_MetricGrid-D-x_tNsK.js": {
|
||||||
"file": "assets/MetricGrid-Cbhb9OGV.js",
|
"file": "assets/MetricGrid-D-x_tNsK.js",
|
||||||
"name": "MetricGrid",
|
"name": "MetricGrid",
|
||||||
"imports": [
|
"imports": [
|
||||||
"index.html",
|
"index.html",
|
||||||
@@ -14,29 +14,29 @@
|
|||||||
"file": "assets/MetricGrid-yP_dkP6X.css",
|
"file": "assets/MetricGrid-yP_dkP6X.css",
|
||||||
"src": "_MetricGrid-yP_dkP6X.css"
|
"src": "_MetricGrid-yP_dkP6X.css"
|
||||||
},
|
},
|
||||||
"_email-CqsrP_Ts.js": {
|
"_email-BYiWDIoy.js": {
|
||||||
"file": "assets/email-CqsrP_Ts.js",
|
"file": "assets/email-BYiWDIoy.js",
|
||||||
"name": "email",
|
"name": "email",
|
||||||
"imports": [
|
"imports": [
|
||||||
"index.html"
|
"index.html"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_system-In7Gh8x7.js": {
|
"_system-Bvj77zeB.js": {
|
||||||
"file": "assets/system-In7Gh8x7.js",
|
"file": "assets/system-Bvj77zeB.js",
|
||||||
"name": "system",
|
"name": "system",
|
||||||
"imports": [
|
"imports": [
|
||||||
"index.html"
|
"index.html"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_tasks-DZnsc1fC.js": {
|
"_tasks-BZsqSMnk.js": {
|
||||||
"file": "assets/tasks-DZnsc1fC.js",
|
"file": "assets/tasks-BZsqSMnk.js",
|
||||||
"name": "tasks",
|
"name": "tasks",
|
||||||
"imports": [
|
"imports": [
|
||||||
"index.html"
|
"index.html"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_users-CTuo5Ynz.js": {
|
"_users-Kctz2ziD.js": {
|
||||||
"file": "assets/users-CTuo5Ynz.js",
|
"file": "assets/users-Kctz2ziD.js",
|
||||||
"name": "users",
|
"name": "users",
|
||||||
"imports": [
|
"imports": [
|
||||||
"index.html"
|
"index.html"
|
||||||
@@ -74,7 +74,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"index.html": {
|
"index.html": {
|
||||||
"file": "assets/index-D8t2quK2.js",
|
"file": "assets/index-iyjFO6XY.js",
|
||||||
"name": "index",
|
"name": "index",
|
||||||
"src": "index.html",
|
"src": "index.html",
|
||||||
"isEntry": true,
|
"isEntry": true,
|
||||||
@@ -96,11 +96,11 @@
|
|||||||
"src/pages/SettingsPage.vue"
|
"src/pages/SettingsPage.vue"
|
||||||
],
|
],
|
||||||
"css": [
|
"css": [
|
||||||
"assets/index-Tk47UJAg.css"
|
"assets/index-a3a11Ghn.css"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/pages/AnnouncementsPage.vue": {
|
"src/pages/AnnouncementsPage.vue": {
|
||||||
"file": "assets/AnnouncementsPage-CaSWhmIw.js",
|
"file": "assets/AnnouncementsPage-BZGpQqUL.js",
|
||||||
"name": "AnnouncementsPage",
|
"name": "AnnouncementsPage",
|
||||||
"src": "src/pages/AnnouncementsPage.vue",
|
"src": "src/pages/AnnouncementsPage.vue",
|
||||||
"isDynamicEntry": true,
|
"isDynamicEntry": true,
|
||||||
@@ -116,14 +116,14 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/pages/EmailPage.vue": {
|
"src/pages/EmailPage.vue": {
|
||||||
"file": "assets/EmailPage-DlOkUKzK.js",
|
"file": "assets/EmailPage-CfIczE0i.js",
|
||||||
"name": "EmailPage",
|
"name": "EmailPage",
|
||||||
"src": "src/pages/EmailPage.vue",
|
"src": "src/pages/EmailPage.vue",
|
||||||
"isDynamicEntry": true,
|
"isDynamicEntry": true,
|
||||||
"imports": [
|
"imports": [
|
||||||
"_email-CqsrP_Ts.js",
|
"_email-BYiWDIoy.js",
|
||||||
"index.html",
|
"index.html",
|
||||||
"_MetricGrid-Cbhb9OGV.js",
|
"_MetricGrid-D-x_tNsK.js",
|
||||||
"_vendor-element-CJoVtPsD.js",
|
"_vendor-element-CJoVtPsD.js",
|
||||||
"_vendor-sLgkZK1v.js",
|
"_vendor-sLgkZK1v.js",
|
||||||
"_vendor-vue-CWkOjFoA.js",
|
"_vendor-vue-CWkOjFoA.js",
|
||||||
@@ -134,13 +134,13 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/pages/FeedbacksPage.vue": {
|
"src/pages/FeedbacksPage.vue": {
|
||||||
"file": "assets/FeedbacksPage-B8rr6rHD.js",
|
"file": "assets/FeedbacksPage-yGzwD6JV.js",
|
||||||
"name": "FeedbacksPage",
|
"name": "FeedbacksPage",
|
||||||
"src": "src/pages/FeedbacksPage.vue",
|
"src": "src/pages/FeedbacksPage.vue",
|
||||||
"isDynamicEntry": true,
|
"isDynamicEntry": true,
|
||||||
"imports": [
|
"imports": [
|
||||||
"index.html",
|
"index.html",
|
||||||
"_MetricGrid-Cbhb9OGV.js",
|
"_MetricGrid-D-x_tNsK.js",
|
||||||
"_vendor-element-CJoVtPsD.js",
|
"_vendor-element-CJoVtPsD.js",
|
||||||
"_vendor-sLgkZK1v.js",
|
"_vendor-sLgkZK1v.js",
|
||||||
"_vendor-vue-CWkOjFoA.js",
|
"_vendor-vue-CWkOjFoA.js",
|
||||||
@@ -151,13 +151,13 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/pages/LogsPage.vue": {
|
"src/pages/LogsPage.vue": {
|
||||||
"file": "assets/LogsPage-WfvkzS-6.js",
|
"file": "assets/LogsPage-WmQFhJZO.js",
|
||||||
"name": "LogsPage",
|
"name": "LogsPage",
|
||||||
"src": "src/pages/LogsPage.vue",
|
"src": "src/pages/LogsPage.vue",
|
||||||
"isDynamicEntry": true,
|
"isDynamicEntry": true,
|
||||||
"imports": [
|
"imports": [
|
||||||
"_users-CTuo5Ynz.js",
|
"_users-Kctz2ziD.js",
|
||||||
"_tasks-DZnsc1fC.js",
|
"_tasks-BZsqSMnk.js",
|
||||||
"index.html",
|
"index.html",
|
||||||
"_vendor-element-CJoVtPsD.js",
|
"_vendor-element-CJoVtPsD.js",
|
||||||
"_vendor-sLgkZK1v.js",
|
"_vendor-sLgkZK1v.js",
|
||||||
@@ -169,17 +169,17 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/pages/ReportPage.vue": {
|
"src/pages/ReportPage.vue": {
|
||||||
"file": "assets/ReportPage-GPt5J1Db.js",
|
"file": "assets/ReportPage-BkIhdzJa.js",
|
||||||
"name": "ReportPage",
|
"name": "ReportPage",
|
||||||
"src": "src/pages/ReportPage.vue",
|
"src": "src/pages/ReportPage.vue",
|
||||||
"isDynamicEntry": true,
|
"isDynamicEntry": true,
|
||||||
"imports": [
|
"imports": [
|
||||||
"_vendor-element-CJoVtPsD.js",
|
"_vendor-element-CJoVtPsD.js",
|
||||||
"index.html",
|
"index.html",
|
||||||
"_email-CqsrP_Ts.js",
|
"_email-BYiWDIoy.js",
|
||||||
"_tasks-DZnsc1fC.js",
|
"_tasks-BZsqSMnk.js",
|
||||||
"_system-In7Gh8x7.js",
|
"_system-Bvj77zeB.js",
|
||||||
"_MetricGrid-Cbhb9OGV.js",
|
"_MetricGrid-D-x_tNsK.js",
|
||||||
"_vendor-sLgkZK1v.js",
|
"_vendor-sLgkZK1v.js",
|
||||||
"_vendor-vue-CWkOjFoA.js",
|
"_vendor-vue-CWkOjFoA.js",
|
||||||
"_vendor-axios-B9ygI19o.js"
|
"_vendor-axios-B9ygI19o.js"
|
||||||
@@ -189,13 +189,13 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/pages/SecurityPage.vue": {
|
"src/pages/SecurityPage.vue": {
|
||||||
"file": "assets/SecurityPage-BWFFA3z9.js",
|
"file": "assets/SecurityPage-Ay9lQdJs.js",
|
||||||
"name": "SecurityPage",
|
"name": "SecurityPage",
|
||||||
"src": "src/pages/SecurityPage.vue",
|
"src": "src/pages/SecurityPage.vue",
|
||||||
"isDynamicEntry": true,
|
"isDynamicEntry": true,
|
||||||
"imports": [
|
"imports": [
|
||||||
"index.html",
|
"index.html",
|
||||||
"_MetricGrid-Cbhb9OGV.js",
|
"_MetricGrid-D-x_tNsK.js",
|
||||||
"_vendor-element-CJoVtPsD.js",
|
"_vendor-element-CJoVtPsD.js",
|
||||||
"_vendor-sLgkZK1v.js",
|
"_vendor-sLgkZK1v.js",
|
||||||
"_vendor-vue-CWkOjFoA.js",
|
"_vendor-vue-CWkOjFoA.js",
|
||||||
@@ -206,7 +206,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/pages/SettingsPage.vue": {
|
"src/pages/SettingsPage.vue": {
|
||||||
"file": "assets/SettingsPage-3cNfdgGx.js",
|
"file": "assets/SettingsPage-D1k6_4Nn.js",
|
||||||
"name": "SettingsPage",
|
"name": "SettingsPage",
|
||||||
"src": "src/pages/SettingsPage.vue",
|
"src": "src/pages/SettingsPage.vue",
|
||||||
"isDynamicEntry": true,
|
"isDynamicEntry": true,
|
||||||
@@ -222,12 +222,12 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/pages/SystemPage.vue": {
|
"src/pages/SystemPage.vue": {
|
||||||
"file": "assets/SystemPage-CJ2F6_EU.js",
|
"file": "assets/SystemPage-wAMFferr.js",
|
||||||
"name": "SystemPage",
|
"name": "SystemPage",
|
||||||
"src": "src/pages/SystemPage.vue",
|
"src": "src/pages/SystemPage.vue",
|
||||||
"isDynamicEntry": true,
|
"isDynamicEntry": true,
|
||||||
"imports": [
|
"imports": [
|
||||||
"_system-In7Gh8x7.js",
|
"_system-Bvj77zeB.js",
|
||||||
"index.html",
|
"index.html",
|
||||||
"_vendor-element-CJoVtPsD.js",
|
"_vendor-element-CJoVtPsD.js",
|
||||||
"_vendor-sLgkZK1v.js",
|
"_vendor-sLgkZK1v.js",
|
||||||
@@ -239,12 +239,12 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/pages/UsersPage.vue": {
|
"src/pages/UsersPage.vue": {
|
||||||
"file": "assets/UsersPage-KZ5WSZrO.js",
|
"file": "assets/UsersPage-B5MDfX7T.js",
|
||||||
"name": "UsersPage",
|
"name": "UsersPage",
|
||||||
"src": "src/pages/UsersPage.vue",
|
"src": "src/pages/UsersPage.vue",
|
||||||
"isDynamicEntry": true,
|
"isDynamicEntry": true,
|
||||||
"imports": [
|
"imports": [
|
||||||
"_users-CTuo5Ynz.js",
|
"_users-Kctz2ziD.js",
|
||||||
"index.html",
|
"index.html",
|
||||||
"_vendor-element-CJoVtPsD.js",
|
"_vendor-element-CJoVtPsD.js",
|
||||||
"_vendor-sLgkZK1v.js",
|
"_vendor-sLgkZK1v.js",
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
|||||||
import{_}from"./index-D8t2quK2.js";import{aW as c,z as s,A as t,R as r,ak as u,E as p,B as o,N as l,S as y,L as h,K as i,O as k,Q as n,P as v,D as f}from"./vendor-sLgkZK1v.js";const b={class:"metric-top"},x={key:0,class:"metric-icon"},B={class:"metric-label"},N={class:"metric-value"},g={key:0,class:"metric-hint app-muted"},C={__name:"MetricGrid",props:{items:{type:Array,default:()=>[]},loading:{type:Boolean,default:!1},minWidth:{type:Number,default:180}},setup(a){return(V,z)=>{const d=c("el-icon"),m=c("el-skeleton");return t(),s("div",{class:"metric-grid",style:f({"--metric-min":`${a.minWidth}px`})},[(t(!0),s(r,null,u(a.items,e=>(t(),s("div",{key:e?.key||e?.label,class:p(["metric-card",`metric-tone--${e?.tone||"blue"}`])},[o("div",b,[e?.icon?(t(),s("div",x,[y(d,null,{default:h(()=>[(t(),i(k(e.icon)))]),_:2},1024)])):l("",!0),o("div",B,n(e?.label||"-"),1)]),o("div",N,[a.loading?(t(),i(m,{key:0,rows:1,animated:""})):(t(),s(r,{key:1},[v(n(e?.value??0),1)],64))]),e?.hint||e?.sub?(t(),s("div",g,n(e?.hint||e?.sub),1)):l("",!0)],2))),128))],4)}}},S=_(C,[["__scopeId","data-v-00e217d4"]]);export{S as M};
|
import{_}from"./index-iyjFO6XY.js";import{aW as c,z as s,A as t,R as r,ak as u,E as p,B as o,N as l,S as y,L as h,K as i,O as k,Q as n,P as v,D as f}from"./vendor-sLgkZK1v.js";const b={class:"metric-top"},x={key:0,class:"metric-icon"},B={class:"metric-label"},N={class:"metric-value"},g={key:0,class:"metric-hint app-muted"},C={__name:"MetricGrid",props:{items:{type:Array,default:()=>[]},loading:{type:Boolean,default:!1},minWidth:{type:Number,default:180}},setup(a){return(V,z)=>{const d=c("el-icon"),m=c("el-skeleton");return t(),s("div",{class:"metric-grid",style:f({"--metric-min":`${a.minWidth}px`})},[(t(!0),s(r,null,u(a.items,e=>(t(),s("div",{key:e?.key||e?.label,class:p(["metric-card",`metric-tone--${e?.tone||"blue"}`])},[o("div",b,[e?.icon?(t(),s("div",x,[y(d,null,{default:h(()=>[(t(),i(k(e.icon)))]),_:2},1024)])):l("",!0),o("div",B,n(e?.label||"-"),1)]),o("div",N,[a.loading?(t(),i(m,{key:0,rows:1,animated:""})):(t(),s(r,{key:1},[v(n(e?.value??0),1)],64))]),e?.hint||e?.sub?(t(),s("div",g,n(e?.hint||e?.sub),1)):l("",!0)],2))),128))],4)}}},S=_(C,[["__scopeId","data-v-00e217d4"]]);export{S as M};
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
|||||||
import{a as m,_ as h}from"./index-D8t2quK2.js";import{f as u,E as x}from"./vendor-element-CJoVtPsD.js";import{r as p,aW as i,z as T,A as P,B as r,S as a,L as o,P as b}from"./vendor-sLgkZK1v.js";import"./vendor-vue-CWkOjFoA.js";import"./vendor-axios-B9ygI19o.js";async function S(l){const{data:s}=await m.put("/admin/username",{new_username:l});return s}async function A(l){const{data:s}=await m.put("/admin/password",{new_password:l});return s}async function C(){const{data:l}=await m.post("/logout");return l}const E={class:"page-stack"},U={__name:"SettingsPage",setup(l){const s=p(""),d=p(""),n=p(!1);function k(t){const e=String(t||"");return e.length<8?{ok:!1,message:"密码长度至少8位"}:e.length>128?{ok:!1,message:"密码长度不能超过128个字符"}:!/[a-zA-Z]/.test(e)||!/\d/.test(e)?{ok:!1,message:"密码必须包含字母和数字"}:{ok:!0,message:""}}async function f(){try{await C()}catch{}finally{window.location.href="/yuyx"}}async function B(){const t=s.value.trim();if(!t){u.error("请输入新用户名");return}try{await x.confirm(`确定将管理员用户名修改为「${t}」吗?修改后需要重新登录。`,"修改用户名",{confirmButtonText:"确认修改",cancelButtonText:"取消",type:"warning"})}catch{return}n.value=!0;try{await S(t),u.success("用户名修改成功,请重新登录"),s.value="",setTimeout(f,1200)}catch{}finally{n.value=!1}}async function V(){const t=d.value;if(!t){u.error("请输入新密码");return}const e=k(t);if(!e.ok){u.error(e.message);return}try{await x.confirm("确定修改管理员密码吗?修改后需要重新登录。","修改密码",{confirmButtonText:"确认修改",cancelButtonText:"取消",type:"warning"})}catch{return}n.value=!0;try{await A(t),u.success("密码修改成功,请重新登录"),d.value="",setTimeout(f,1200)}catch{}finally{n.value=!1}}return(t,e)=>{const g=i("el-input"),v=i("el-form-item"),w=i("el-form"),y=i("el-button"),_=i("el-card");return P(),T("div",E,[e[7]||(e[7]=r("div",{class:"app-page-title"},[r("h2",null,"设置"),r("span",{class:"app-muted"},"管理员账号设置")],-1)),a(_,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:o(()=>[e[3]||(e[3]=r("h3",{class:"section-title"},"修改管理员用户名",-1)),a(w,{"label-width":"120px"},{default:o(()=>[a(v,{label:"新用户名"},{default:o(()=>[a(g,{modelValue:s.value,"onUpdate:modelValue":e[0]||(e[0]=c=>s.value=c),placeholder:"输入新用户名",disabled:n.value},null,8,["modelValue","disabled"])]),_:1})]),_:1}),a(y,{type:"primary",loading:n.value,onClick:B},{default:o(()=>[...e[2]||(e[2]=[b("保存用户名",-1)])]),_:1},8,["loading"])]),_:1}),a(_,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:o(()=>[e[5]||(e[5]=r("h3",{class:"section-title"},"修改管理员密码",-1)),a(w,{"label-width":"120px"},{default:o(()=>[a(v,{label:"新密码"},{default:o(()=>[a(g,{modelValue:d.value,"onUpdate:modelValue":e[1]||(e[1]=c=>d.value=c),type:"password","show-password":"",placeholder:"输入新密码",disabled:n.value},null,8,["modelValue","disabled"])]),_:1})]),_:1}),a(y,{type:"primary",loading:n.value,onClick:V},{default:o(()=>[...e[4]||(e[4]=[b("保存密码",-1)])]),_:1},8,["loading"]),e[6]||(e[6]=r("div",{class:"help"},"建议使用更强密码(至少8位且包含字母与数字)。",-1))]),_:1})])}}},W=h(U,[["__scopeId","data-v-83d3840a"]]);export{W as default};
|
import{a as m,_ as h}from"./index-iyjFO6XY.js";import{f as u,E as x}from"./vendor-element-CJoVtPsD.js";import{r as p,aW as i,z as T,A as P,B as r,S as a,L as o,P as b}from"./vendor-sLgkZK1v.js";import"./vendor-vue-CWkOjFoA.js";import"./vendor-axios-B9ygI19o.js";async function S(l){const{data:s}=await m.put("/admin/username",{new_username:l});return s}async function A(l){const{data:s}=await m.put("/admin/password",{new_password:l});return s}async function C(){const{data:l}=await m.post("/logout");return l}const E={class:"page-stack"},U={__name:"SettingsPage",setup(l){const s=p(""),d=p(""),n=p(!1);function k(t){const e=String(t||"");return e.length<8?{ok:!1,message:"密码长度至少8位"}:e.length>128?{ok:!1,message:"密码长度不能超过128个字符"}:!/[a-zA-Z]/.test(e)||!/\d/.test(e)?{ok:!1,message:"密码必须包含字母和数字"}:{ok:!0,message:""}}async function f(){try{await C()}catch{}finally{window.location.href="/yuyx"}}async function B(){const t=s.value.trim();if(!t){u.error("请输入新用户名");return}try{await x.confirm(`确定将管理员用户名修改为「${t}」吗?修改后需要重新登录。`,"修改用户名",{confirmButtonText:"确认修改",cancelButtonText:"取消",type:"warning"})}catch{return}n.value=!0;try{await S(t),u.success("用户名修改成功,请重新登录"),s.value="",setTimeout(f,1200)}catch{}finally{n.value=!1}}async function V(){const t=d.value;if(!t){u.error("请输入新密码");return}const e=k(t);if(!e.ok){u.error(e.message);return}try{await x.confirm("确定修改管理员密码吗?修改后需要重新登录。","修改密码",{confirmButtonText:"确认修改",cancelButtonText:"取消",type:"warning"})}catch{return}n.value=!0;try{await A(t),u.success("密码修改成功,请重新登录"),d.value="",setTimeout(f,1200)}catch{}finally{n.value=!1}}return(t,e)=>{const g=i("el-input"),v=i("el-form-item"),w=i("el-form"),y=i("el-button"),_=i("el-card");return P(),T("div",E,[e[7]||(e[7]=r("div",{class:"app-page-title"},[r("h2",null,"设置"),r("span",{class:"app-muted"},"管理员账号设置")],-1)),a(_,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:o(()=>[e[3]||(e[3]=r("h3",{class:"section-title"},"修改管理员用户名",-1)),a(w,{"label-width":"120px"},{default:o(()=>[a(v,{label:"新用户名"},{default:o(()=>[a(g,{modelValue:s.value,"onUpdate:modelValue":e[0]||(e[0]=c=>s.value=c),placeholder:"输入新用户名",disabled:n.value},null,8,["modelValue","disabled"])]),_:1})]),_:1}),a(y,{type:"primary",loading:n.value,onClick:B},{default:o(()=>[...e[2]||(e[2]=[b("保存用户名",-1)])]),_:1},8,["loading"])]),_:1}),a(_,{shadow:"never","body-style":{padding:"16px"},class:"card"},{default:o(()=>[e[5]||(e[5]=r("h3",{class:"section-title"},"修改管理员密码",-1)),a(w,{"label-width":"120px"},{default:o(()=>[a(v,{label:"新密码"},{default:o(()=>[a(g,{modelValue:d.value,"onUpdate:modelValue":e[1]||(e[1]=c=>d.value=c),type:"password","show-password":"",placeholder:"输入新密码",disabled:n.value},null,8,["modelValue","disabled"])]),_:1})]),_:1}),a(y,{type:"primary",loading:n.value,onClick:V},{default:o(()=>[...e[4]||(e[4]=[b("保存密码",-1)])]),_:1},8,["loading"]),e[6]||(e[6]=r("div",{class:"help"},"建议使用更强密码(至少8位且包含字母与数字)。",-1))]),_:1})])}}},W=h(U,[["__scopeId","data-v-83d3840a"]]);export{W as default};
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import{f as Ve,u as Y}from"./system-In7Gh8x7.js";import{a as P,_ as ge}from"./index-D8t2quK2.js";import{E as ne,f as m}from"./vendor-element-CJoVtPsD.js";import{r as n,c as ke,w as xe,a1 as be,v as we,aW as v,b7 as Ue,M as Ce,A as V,z as g,B as s,S as l,L as t,P as k,N as Z,Q as ee}from"./vendor-sLgkZK1v.js";import"./vendor-vue-CWkOjFoA.js";import"./vendor-axios-B9ygI19o.js";async function le(r={}){const{data:c}=await P.get("/kdocs/status",{params:r});return c}async function Pe(r={}){const c={force:!0,...r},{data:x}=await P.post("/kdocs/qr",c);return x}async function Ie(){const{data:r}=await P.post("/kdocs/clear-login",{});return r}async function Ae(){const{data:r}=await P.get("/proxy/config");return r}async function Ne(r){const{data:c}=await P.post("/proxy/config",r);return c}async function Se(r){const{data:c}=await P.post("/proxy/test",r);return c}const De={class:"page-stack"},Ke={class:"config-grid"},Ee={class:"row-actions"},Be={class:"row-actions"},Te={class:"row-actions"},Le={class:"section-head"},Qe={class:"status-inline app-muted"},$e={key:0},he={key:1},qe={key:2},Me={class:"kdocs-inline"},Re={class:"kdocs-range"},ze={class:"row-actions"},Fe={key:0,class:"help"},He={key:1,class:"help"},Oe={class:"kdocs-qr"},Ge=["src"],We={__name:"SystemPage",setup(r){const c=n(!1),x=n(2),A=n(1),N=n(3),I=n(!1),f=n(""),S=n(3),D=n(!1),K=n(10),E=n(7),B=n(!1),T=n(""),L=n(""),Q=n(""),$=n(0),h=n("A"),q=n("D"),M=n(0),R=n(0),z=n(!1),F=n(""),p=n({}),b=n(!1),w=n(""),ae=n(!1),H=n(!1),U=n(!1),C=n(!1),O=n("");let G=null;const oe=ke(()=>H.value||U.value||C.value);function d(a){if(!a){O.value="";return}const e=new Date().toLocaleTimeString("zh-CN",{hour12:!1});O.value=`${a} (${e})`}async function ue(){c.value=!0;try{const[a,e,i]=await Promise.all([Ve(),Ae(),le().catch(()=>({}))]);x.value=a.max_concurrent_global??2,A.value=a.max_concurrent_per_account??1,N.value=a.max_screenshot_concurrent??3,D.value=(a.auto_approve_enabled??0)===1,K.value=a.auto_approve_hourly_limit??10,E.value=a.auto_approve_vip_days??7,I.value=(e.proxy_enabled??0)===1,f.value=e.proxy_api_url||"",S.value=e.proxy_expire_minutes??3,B.value=(a.kdocs_enabled??0)===1,T.value=a.kdocs_doc_url||"",L.value=a.kdocs_default_unit||"",Q.value=a.kdocs_sheet_name||"",$.value=a.kdocs_sheet_index??0,h.value=(a.kdocs_unit_column||"A").toUpperCase(),q.value=(a.kdocs_image_column||"D").toUpperCase(),M.value=a.kdocs_row_start??0,R.value=a.kdocs_row_end??0,z.value=(a.kdocs_admin_notify_enabled??0)===1,F.value=a.kdocs_admin_notify_email||"",p.value=i||{}}catch{}finally{c.value=!1}}async function de(){const a={max_concurrent_global:Number(x.value),max_concurrent_per_account:Number(A.value),max_screenshot_concurrent:Number(N.value)};try{await ne.confirm(`确定更新并发配置吗?
|
import{f as Ve,u as Y}from"./system-Bvj77zeB.js";import{a as P,_ as ge}from"./index-iyjFO6XY.js";import{E as ne,f as m}from"./vendor-element-CJoVtPsD.js";import{r as n,c as ke,w as xe,a1 as be,v as we,aW as v,b7 as Ue,M as Ce,A as V,z as g,B as s,S as l,L as t,P as k,N as Z,Q as ee}from"./vendor-sLgkZK1v.js";import"./vendor-vue-CWkOjFoA.js";import"./vendor-axios-B9ygI19o.js";async function le(r={}){const{data:c}=await P.get("/kdocs/status",{params:r});return c}async function Pe(r={}){const c={force:!0,...r},{data:x}=await P.post("/kdocs/qr",c);return x}async function Ie(){const{data:r}=await P.post("/kdocs/clear-login",{});return r}async function Ae(){const{data:r}=await P.get("/proxy/config");return r}async function Ne(r){const{data:c}=await P.post("/proxy/config",r);return c}async function Se(r){const{data:c}=await P.post("/proxy/test",r);return c}const De={class:"page-stack"},Ke={class:"config-grid"},Ee={class:"row-actions"},Be={class:"row-actions"},Te={class:"row-actions"},Le={class:"section-head"},Qe={class:"status-inline app-muted"},$e={key:0},he={key:1},qe={key:2},Me={class:"kdocs-inline"},Re={class:"kdocs-range"},ze={class:"row-actions"},Fe={key:0,class:"help"},He={key:1,class:"help"},Oe={class:"kdocs-qr"},Ge=["src"],We={__name:"SystemPage",setup(r){const c=n(!1),x=n(2),A=n(1),N=n(3),I=n(!1),f=n(""),S=n(3),D=n(!1),K=n(10),E=n(7),B=n(!1),T=n(""),L=n(""),Q=n(""),$=n(0),h=n("A"),q=n("D"),M=n(0),R=n(0),z=n(!1),F=n(""),p=n({}),b=n(!1),w=n(""),ae=n(!1),H=n(!1),U=n(!1),C=n(!1),O=n("");let G=null;const oe=ke(()=>H.value||U.value||C.value);function d(a){if(!a){O.value="";return}const e=new Date().toLocaleTimeString("zh-CN",{hour12:!1});O.value=`${a} (${e})`}async function ue(){c.value=!0;try{const[a,e,i]=await Promise.all([Ve(),Ae(),le().catch(()=>({}))]);x.value=a.max_concurrent_global??2,A.value=a.max_concurrent_per_account??1,N.value=a.max_screenshot_concurrent??3,D.value=(a.auto_approve_enabled??0)===1,K.value=a.auto_approve_hourly_limit??10,E.value=a.auto_approve_vip_days??7,I.value=(e.proxy_enabled??0)===1,f.value=e.proxy_api_url||"",S.value=e.proxy_expire_minutes??3,B.value=(a.kdocs_enabled??0)===1,T.value=a.kdocs_doc_url||"",L.value=a.kdocs_default_unit||"",Q.value=a.kdocs_sheet_name||"",$.value=a.kdocs_sheet_index??0,h.value=(a.kdocs_unit_column||"A").toUpperCase(),q.value=(a.kdocs_image_column||"D").toUpperCase(),M.value=a.kdocs_row_start??0,R.value=a.kdocs_row_end??0,z.value=(a.kdocs_admin_notify_enabled??0)===1,F.value=a.kdocs_admin_notify_email||"",p.value=i||{}}catch{}finally{c.value=!1}}async function de(){const a={max_concurrent_global:Number(x.value),max_concurrent_per_account:Number(A.value),max_screenshot_concurrent:Number(N.value)};try{await ne.confirm(`确定更新并发配置吗?
|
||||||
|
|
||||||
全局并发数: ${a.max_concurrent_global}
|
全局并发数: ${a.max_concurrent_global}
|
||||||
单账号并发数: ${a.max_concurrent_per_account}
|
单账号并发数: ${a.max_concurrent_per_account}
|
||||||
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
|||||||
import{a as n}from"./index-D8t2quK2.js";async function i(){const{data:a}=await n.get("/email/settings");return a}async function e(a){const{data:t}=await n.post("/email/settings",a);return t}async function c(){const{data:a}=await n.get("/email/stats");return a}async function o(a){const{data:t}=await n.get("/email/logs",{params:a});return t}async function l(a){const{data:t}=await n.post("/email/logs/cleanup",{days:a});return t}export{o as a,i as b,l as c,c as f,e as u};
|
import{a as n}from"./index-iyjFO6XY.js";async function i(){const{data:a}=await n.get("/email/settings");return a}async function e(a){const{data:t}=await n.post("/email/settings",a);return t}async function c(){const{data:a}=await n.get("/email/stats");return a}async function o(a){const{data:t}=await n.get("/email/logs",{params:a});return t}async function l(a){const{data:t}=await n.post("/email/logs/cleanup",{days:a});return t}export{o as a,i as b,l as c,c as f,e as u};
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
static/admin/assets/index-iyjFO6XY.js
Normal file
2
static/admin/assets/index-iyjFO6XY.js
Normal file
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
|||||||
import{a}from"./index-D8t2quK2.js";async function o(){const{data:t}=await a.get("/system/config");return t}async function e(t){const{data:n}=await a.post("/system/config",t);return n}export{o as f,e as u};
|
import{a}from"./index-iyjFO6XY.js";async function o(){const{data:t}=await a.get("/system/config");return t}async function e(t){const{data:n}=await a.post("/system/config",t);return n}export{o as f,e as u};
|
||||||
@@ -1 +1 @@
|
|||||||
import{a}from"./index-D8t2quK2.js";async function c(){const{data:t}=await a.get("/server/info");return t}async function e(){const{data:t}=await a.get("/docker_stats");return t}async function o(){const{data:t}=await a.get("/task/stats");return t}async function r(){const{data:t}=await a.get("/task/running");return t}async function i(t){const{data:s}=await a.get("/task/logs",{params:t});return s}async function f(t){const{data:s}=await a.post("/task/logs/clear",{days:t});return s}export{r as a,c as b,e as c,i as d,f as e,o as f};
|
import{a}from"./index-iyjFO6XY.js";async function c(){const{data:t}=await a.get("/server/info");return t}async function e(){const{data:t}=await a.get("/docker_stats");return t}async function o(){const{data:t}=await a.get("/task/stats");return t}async function r(){const{data:t}=await a.get("/task/running");return t}async function i(t){const{data:s}=await a.get("/task/logs",{params:t});return s}async function f(t){const{data:s}=await a.post("/task/logs/clear",{days:t});return s}export{r as a,c as b,e as c,i as d,f as e,o as f};
|
||||||
@@ -1 +1 @@
|
|||||||
import{a as t}from"./index-D8t2quK2.js";async function n(){const{data:s}=await t.get("/users");return s}async function o(s){const{data:a}=await t.post(`/users/${s}/approve`);return a}async function c(s){const{data:a}=await t.post(`/users/${s}/reject`);return a}async function i(s){const{data:a}=await t.delete(`/users/${s}`);return a}async function u(s,a){const{data:e}=await t.post(`/users/${s}/vip`,{days:a});return e}async function p(s){const{data:a}=await t.delete(`/users/${s}/vip`);return a}async function d(s,a){const{data:e}=await t.post(`/users/${s}/reset_password`,{new_password:a});return e}export{o as a,p as b,d as c,i as d,n as f,c as r,u as s};
|
import{a as t}from"./index-iyjFO6XY.js";async function n(){const{data:s}=await t.get("/users");return s}async function o(s){const{data:a}=await t.post(`/users/${s}/approve`);return a}async function c(s){const{data:a}=await t.post(`/users/${s}/reject`);return a}async function i(s){const{data:a}=await t.delete(`/users/${s}`);return a}async function u(s,a){const{data:e}=await t.post(`/users/${s}/vip`,{days:a});return e}async function p(s){const{data:a}=await t.delete(`/users/${s}/vip`);return a}async function d(s,a){const{data:e}=await t.post(`/users/${s}/reset_password`,{new_password:a});return e}export{o as a,p as b,d as c,i as d,n as f,c as r,u as s};
|
||||||
@@ -5,13 +5,13 @@
|
|||||||
<link rel="icon" type="image/svg+xml" href="./vite.svg" />
|
<link rel="icon" type="image/svg+xml" href="./vite.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>后台管理 - 知识管理平台</title>
|
<title>后台管理 - 知识管理平台</title>
|
||||||
<script type="module" crossorigin src="./assets/index-D8t2quK2.js"></script>
|
<script type="module" crossorigin src="./assets/index-iyjFO6XY.js"></script>
|
||||||
<link rel="modulepreload" crossorigin href="./assets/vendor-sLgkZK1v.js">
|
<link rel="modulepreload" crossorigin href="./assets/vendor-sLgkZK1v.js">
|
||||||
<link rel="modulepreload" crossorigin href="./assets/vendor-element-CJoVtPsD.js">
|
<link rel="modulepreload" crossorigin href="./assets/vendor-element-CJoVtPsD.js">
|
||||||
<link rel="modulepreload" crossorigin href="./assets/vendor-vue-CWkOjFoA.js">
|
<link rel="modulepreload" crossorigin href="./assets/vendor-vue-CWkOjFoA.js">
|
||||||
<link rel="modulepreload" crossorigin href="./assets/vendor-axios-B9ygI19o.js">
|
<link rel="modulepreload" crossorigin href="./assets/vendor-axios-B9ygI19o.js">
|
||||||
<link rel="stylesheet" crossorigin href="./assets/vendor-element-C68yOrAy.css">
|
<link rel="stylesheet" crossorigin href="./assets/vendor-element-C68yOrAy.css">
|
||||||
<link rel="stylesheet" crossorigin href="./assets/index-Tk47UJAg.css">
|
<link rel="stylesheet" crossorigin href="./assets/index-a3a11Ghn.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
"_accounts-DpRp0y7G.js": {
|
"_accounts-DWKsGalm.js": {
|
||||||
"file": "assets/accounts-DpRp0y7G.js",
|
"file": "assets/accounts-DWKsGalm.js",
|
||||||
"name": "accounts",
|
"name": "accounts",
|
||||||
"imports": [
|
"imports": [
|
||||||
"index.html"
|
"index.html"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"_auth-CtTBVBJk.js": {
|
"_auth-3qca7BnL.js": {
|
||||||
"file": "assets/auth-CtTBVBJk.js",
|
"file": "assets/auth-3qca7BnL.js",
|
||||||
"name": "auth",
|
"name": "auth",
|
||||||
"imports": [
|
"imports": [
|
||||||
"index.html"
|
"index.html"
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"index.html": {
|
"index.html": {
|
||||||
"file": "assets/index-C4rfJ09l.js",
|
"file": "assets/index-DIRUAu3R.js",
|
||||||
"name": "index",
|
"name": "index",
|
||||||
"src": "index.html",
|
"src": "index.html",
|
||||||
"isEntry": true,
|
"isEntry": true,
|
||||||
@@ -76,12 +76,12 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/pages/AccountsPage.vue": {
|
"src/pages/AccountsPage.vue": {
|
||||||
"file": "assets/AccountsPage-Cj2MTk-l.js",
|
"file": "assets/AccountsPage-BsKyQ62c.js",
|
||||||
"name": "AccountsPage",
|
"name": "AccountsPage",
|
||||||
"src": "src/pages/AccountsPage.vue",
|
"src": "src/pages/AccountsPage.vue",
|
||||||
"isDynamicEntry": true,
|
"isDynamicEntry": true,
|
||||||
"imports": [
|
"imports": [
|
||||||
"_accounts-DpRp0y7G.js",
|
"_accounts-DWKsGalm.js",
|
||||||
"index.html",
|
"index.html",
|
||||||
"_vendor-socket-ciQGyZ7q.js",
|
"_vendor-socket-ciQGyZ7q.js",
|
||||||
"_vendor-element-D5SbqSD5.js",
|
"_vendor-element-D5SbqSD5.js",
|
||||||
@@ -94,14 +94,14 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/pages/LoginPage.vue": {
|
"src/pages/LoginPage.vue": {
|
||||||
"file": "assets/LoginPage-B4Cm3mBQ.js",
|
"file": "assets/LoginPage-DjXmnzTj.js",
|
||||||
"name": "LoginPage",
|
"name": "LoginPage",
|
||||||
"src": "src/pages/LoginPage.vue",
|
"src": "src/pages/LoginPage.vue",
|
||||||
"isDynamicEntry": true,
|
"isDynamicEntry": true,
|
||||||
"imports": [
|
"imports": [
|
||||||
"_vendor-DR-vtVVc.js",
|
"_vendor-DR-vtVVc.js",
|
||||||
"_vendor-vue-l2lnRGj2.js",
|
"_vendor-vue-l2lnRGj2.js",
|
||||||
"_auth-CtTBVBJk.js",
|
"_auth-3qca7BnL.js",
|
||||||
"index.html",
|
"index.html",
|
||||||
"_vendor-element-D5SbqSD5.js",
|
"_vendor-element-D5SbqSD5.js",
|
||||||
"_vendor-axios-B9ygI19o.js"
|
"_vendor-axios-B9ygI19o.js"
|
||||||
@@ -111,14 +111,14 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/pages/RegisterPage.vue": {
|
"src/pages/RegisterPage.vue": {
|
||||||
"file": "assets/RegisterPage-DDkEhw5Z.js",
|
"file": "assets/RegisterPage-Nvd-XHp4.js",
|
||||||
"name": "RegisterPage",
|
"name": "RegisterPage",
|
||||||
"src": "src/pages/RegisterPage.vue",
|
"src": "src/pages/RegisterPage.vue",
|
||||||
"isDynamicEntry": true,
|
"isDynamicEntry": true,
|
||||||
"imports": [
|
"imports": [
|
||||||
"_vendor-DR-vtVVc.js",
|
"_vendor-DR-vtVVc.js",
|
||||||
"_vendor-vue-l2lnRGj2.js",
|
"_vendor-vue-l2lnRGj2.js",
|
||||||
"_auth-CtTBVBJk.js",
|
"_auth-3qca7BnL.js",
|
||||||
"index.html",
|
"index.html",
|
||||||
"_vendor-element-D5SbqSD5.js",
|
"_vendor-element-D5SbqSD5.js",
|
||||||
"_vendor-axios-B9ygI19o.js"
|
"_vendor-axios-B9ygI19o.js"
|
||||||
@@ -128,14 +128,14 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/pages/ResetPasswordPage.vue": {
|
"src/pages/ResetPasswordPage.vue": {
|
||||||
"file": "assets/ResetPasswordPage-DTBT5Kre.js",
|
"file": "assets/ResetPasswordPage-DluVIq7o.js",
|
||||||
"name": "ResetPasswordPage",
|
"name": "ResetPasswordPage",
|
||||||
"src": "src/pages/ResetPasswordPage.vue",
|
"src": "src/pages/ResetPasswordPage.vue",
|
||||||
"isDynamicEntry": true,
|
"isDynamicEntry": true,
|
||||||
"imports": [
|
"imports": [
|
||||||
"_vendor-DR-vtVVc.js",
|
"_vendor-DR-vtVVc.js",
|
||||||
"_vendor-vue-l2lnRGj2.js",
|
"_vendor-vue-l2lnRGj2.js",
|
||||||
"_auth-CtTBVBJk.js",
|
"_auth-3qca7BnL.js",
|
||||||
"index.html",
|
"index.html",
|
||||||
"_vendor-element-D5SbqSD5.js",
|
"_vendor-element-D5SbqSD5.js",
|
||||||
"_vendor-axios-B9ygI19o.js"
|
"_vendor-axios-B9ygI19o.js"
|
||||||
@@ -145,12 +145,12 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/pages/SchedulesPage.vue": {
|
"src/pages/SchedulesPage.vue": {
|
||||||
"file": "assets/SchedulesPage-DtRro62b.js",
|
"file": "assets/SchedulesPage-DglS5EqW.js",
|
||||||
"name": "SchedulesPage",
|
"name": "SchedulesPage",
|
||||||
"src": "src/pages/SchedulesPage.vue",
|
"src": "src/pages/SchedulesPage.vue",
|
||||||
"isDynamicEntry": true,
|
"isDynamicEntry": true,
|
||||||
"imports": [
|
"imports": [
|
||||||
"_accounts-DpRp0y7G.js",
|
"_accounts-DWKsGalm.js",
|
||||||
"index.html",
|
"index.html",
|
||||||
"_vendor-element-D5SbqSD5.js",
|
"_vendor-element-D5SbqSD5.js",
|
||||||
"_vendor-DR-vtVVc.js",
|
"_vendor-DR-vtVVc.js",
|
||||||
@@ -162,7 +162,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/pages/ScreenshotsPage.vue": {
|
"src/pages/ScreenshotsPage.vue": {
|
||||||
"file": "assets/ScreenshotsPage-BMPLt3Or.js",
|
"file": "assets/ScreenshotsPage-L5gmloAw.js",
|
||||||
"name": "ScreenshotsPage",
|
"name": "ScreenshotsPage",
|
||||||
"src": "src/pages/ScreenshotsPage.vue",
|
"src": "src/pages/ScreenshotsPage.vue",
|
||||||
"isDynamicEntry": true,
|
"isDynamicEntry": true,
|
||||||
@@ -178,7 +178,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"src/pages/VerifyResultPage.vue": {
|
"src/pages/VerifyResultPage.vue": {
|
||||||
"file": "assets/VerifyResultPage-CdTQeWQk.js",
|
"file": "assets/VerifyResultPage-CSJomZjC.js",
|
||||||
"name": "VerifyResultPage",
|
"name": "VerifyResultPage",
|
||||||
"src": "src/pages/VerifyResultPage.vue",
|
"src": "src/pages/VerifyResultPage.vue",
|
||||||
"isDynamicEntry": true,
|
"isDynamicEntry": true,
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
|||||||
import{a1 as A,r as d,c as S,v as M,z as N,Q as l,J as o,aY as v,A as b,B as n,I as U,L as B,aD as P,O as z,N as E}from"./vendor-DR-vtVVc.js";import{p as H}from"./vendor-vue-l2lnRGj2.js";import{g as J,f as O,b as Q}from"./auth-CtTBVBJk.js";import{_ as Y,v as j}from"./index-C4rfJ09l.js";import{E as c}from"./vendor-element-D5SbqSD5.js";import"./vendor-axios-B9ygI19o.js";const q={class:"auth-wrap"},F={class:"hint app-muted"},G={class:"captcha-row"},W=["src"],X={class:"actions"},Z={__name:"RegisterPage",setup($){const T=H(),a=A({username:"",password:"",confirm_password:"",email:"",captcha:""}),f=d(!1),w=d(""),h=d(""),V=d(!1),t=d(""),_=d(""),k=d(""),D=S(()=>f.value?"邮箱 *":"邮箱(可选)"),I=S(()=>f.value?"必填,用于账号验证":"选填,用于找回密码和接收通知");async function y(){try{const u=await J();h.value=u?.session_id||"",w.value=u?.captcha_image||"",a.captcha=""}catch{h.value="",w.value=""}}async function K(){try{const u=await O();f.value=!!u?.register_verify_enabled}catch{f.value=!1}}function L(){t.value="",_.value="",k.value=""}async function C(){L();const u=a.username.trim(),e=a.password,g=a.confirm_password,s=a.email.trim(),i=a.captcha.trim();if(u.length<3){t.value="用户名至少3个字符",c.error(t.value);return}const p=j(e);if(!p.ok){t.value=p.message||"密码格式不正确",c.error(t.value);return}if(e!==g){t.value="两次输入的密码不一致",c.error(t.value);return}if(f.value&&!s){t.value="请填写邮箱地址用于账号验证",c.error(t.value);return}if(s&&!s.includes("@")){t.value="邮箱格式不正确",c.error(t.value);return}if(!i){t.value="请输入验证码",c.error(t.value);return}V.value=!0;try{const m=await Q({username:u,password:e,email:s,captcha_session:h.value,captcha:i});_.value=m?.message||"注册成功",k.value=m?.need_verify?"请检查您的邮箱(包括垃圾邮件文件夹)":"",c.success("注册成功"),a.username="",a.password="",a.confirm_password="",a.email="",a.captcha="",setTimeout(()=>{window.location.href="/login"},3e3)}catch(m){const x=m?.response?.data;t.value=x?.error||"注册失败",c.error(t.value),await y()}finally{V.value=!1}}function R(){T.push("/login")}return M(async()=>{await y(),await K()}),(u,e)=>{const g=v("el-alert"),s=v("el-input"),i=v("el-form-item"),p=v("el-button"),m=v("el-form"),x=v("el-card");return b(),N("div",q,[l(x,{shadow:"never",class:"auth-card","body-style":{padding:"22px"}},{default:o(()=>[e[11]||(e[11]=n("div",{class:"brand"},[n("div",{class:"brand-title"},"知识管理平台"),n("div",{class:"brand-sub app-muted"},"用户注册")],-1)),t.value?(b(),U(g,{key:0,type:"error",closable:!1,title:t.value,"show-icon":"",class:"alert"},null,8,["title"])):B("",!0),_.value?(b(),U(g,{key:1,type:"success",closable:!1,title:_.value,description:k.value,"show-icon":"",class:"alert"},null,8,["title","description"])):B("",!0),l(m,{"label-position":"top"},{default:o(()=>[l(i,{label:"用户名 *"},{default:o(()=>[l(s,{modelValue:a.username,"onUpdate:modelValue":e[0]||(e[0]=r=>a.username=r),placeholder:"至少3个字符",autocomplete:"username"},null,8,["modelValue"]),e[5]||(e[5]=n("div",{class:"hint app-muted"},"至少3个字符",-1))]),_:1}),l(i,{label:"密码 *"},{default:o(()=>[l(s,{modelValue:a.password,"onUpdate:modelValue":e[1]||(e[1]=r=>a.password=r),type:"password","show-password":"",placeholder:"至少8位且包含字母和数字",autocomplete:"new-password"},null,8,["modelValue"]),e[6]||(e[6]=n("div",{class:"hint app-muted"},"至少8位且包含字母和数字",-1))]),_:1}),l(i,{label:"确认密码 *"},{default:o(()=>[l(s,{modelValue:a.confirm_password,"onUpdate:modelValue":e[2]||(e[2]=r=>a.confirm_password=r),type:"password","show-password":"",placeholder:"请再次输入密码",autocomplete:"new-password",onKeyup:P(C,["enter"])},null,8,["modelValue"])]),_:1}),l(i,{label:D.value},{default:o(()=>[l(s,{modelValue:a.email,"onUpdate:modelValue":e[3]||(e[3]=r=>a.email=r),placeholder:"name@example.com",autocomplete:"email"},null,8,["modelValue"]),n("div",F,z(I.value),1)]),_:1},8,["label"]),l(i,{label:"验证码 *"},{default:o(()=>[n("div",G,[l(s,{modelValue:a.captcha,"onUpdate:modelValue":e[4]||(e[4]=r=>a.captcha=r),placeholder:"请输入验证码",onKeyup:P(C,["enter"])},null,8,["modelValue"]),w.value?(b(),N("img",{key:0,class:"captcha-img",src:w.value,alt:"验证码",title:"点击刷新",onClick:y},null,8,W)):B("",!0),l(p,{onClick:y},{default:o(()=>[...e[7]||(e[7]=[E("刷新",-1)])]),_:1})])]),_:1})]),_:1}),l(p,{type:"primary",class:"submit-btn",loading:V.value,onClick:C},{default:o(()=>[...e[8]||(e[8]=[E("注册",-1)])]),_:1},8,["loading"]),n("div",X,[e[10]||(e[10]=n("span",{class:"app-muted"},"已有账号?",-1)),l(p,{link:"",type:"primary",onClick:R},{default:o(()=>[...e[9]||(e[9]=[E("立即登录",-1)])]),_:1})])]),_:1})])}}},re=Y(Z,[["__scopeId","data-v-a9d7804f"]]);export{re as default};
|
import{a1 as A,r as d,c as S,v as M,z as N,Q as l,J as o,aY as v,A as b,B as n,I as U,L as B,aD as P,O as z,N as E}from"./vendor-DR-vtVVc.js";import{p as H}from"./vendor-vue-l2lnRGj2.js";import{g as J,f as O,b as Q}from"./auth-3qca7BnL.js";import{_ as Y,v as j}from"./index-DIRUAu3R.js";import{E as c}from"./vendor-element-D5SbqSD5.js";import"./vendor-axios-B9ygI19o.js";const q={class:"auth-wrap"},F={class:"hint app-muted"},G={class:"captcha-row"},W=["src"],X={class:"actions"},Z={__name:"RegisterPage",setup($){const T=H(),a=A({username:"",password:"",confirm_password:"",email:"",captcha:""}),f=d(!1),w=d(""),h=d(""),V=d(!1),t=d(""),_=d(""),k=d(""),D=S(()=>f.value?"邮箱 *":"邮箱(可选)"),I=S(()=>f.value?"必填,用于账号验证":"选填,用于找回密码和接收通知");async function y(){try{const u=await J();h.value=u?.session_id||"",w.value=u?.captcha_image||"",a.captcha=""}catch{h.value="",w.value=""}}async function K(){try{const u=await O();f.value=!!u?.register_verify_enabled}catch{f.value=!1}}function L(){t.value="",_.value="",k.value=""}async function C(){L();const u=a.username.trim(),e=a.password,g=a.confirm_password,s=a.email.trim(),i=a.captcha.trim();if(u.length<3){t.value="用户名至少3个字符",c.error(t.value);return}const p=j(e);if(!p.ok){t.value=p.message||"密码格式不正确",c.error(t.value);return}if(e!==g){t.value="两次输入的密码不一致",c.error(t.value);return}if(f.value&&!s){t.value="请填写邮箱地址用于账号验证",c.error(t.value);return}if(s&&!s.includes("@")){t.value="邮箱格式不正确",c.error(t.value);return}if(!i){t.value="请输入验证码",c.error(t.value);return}V.value=!0;try{const m=await Q({username:u,password:e,email:s,captcha_session:h.value,captcha:i});_.value=m?.message||"注册成功",k.value=m?.need_verify?"请检查您的邮箱(包括垃圾邮件文件夹)":"",c.success("注册成功"),a.username="",a.password="",a.confirm_password="",a.email="",a.captcha="",setTimeout(()=>{window.location.href="/login"},3e3)}catch(m){const x=m?.response?.data;t.value=x?.error||"注册失败",c.error(t.value),await y()}finally{V.value=!1}}function R(){T.push("/login")}return M(async()=>{await y(),await K()}),(u,e)=>{const g=v("el-alert"),s=v("el-input"),i=v("el-form-item"),p=v("el-button"),m=v("el-form"),x=v("el-card");return b(),N("div",q,[l(x,{shadow:"never",class:"auth-card","body-style":{padding:"22px"}},{default:o(()=>[e[11]||(e[11]=n("div",{class:"brand"},[n("div",{class:"brand-title"},"知识管理平台"),n("div",{class:"brand-sub app-muted"},"用户注册")],-1)),t.value?(b(),U(g,{key:0,type:"error",closable:!1,title:t.value,"show-icon":"",class:"alert"},null,8,["title"])):B("",!0),_.value?(b(),U(g,{key:1,type:"success",closable:!1,title:_.value,description:k.value,"show-icon":"",class:"alert"},null,8,["title","description"])):B("",!0),l(m,{"label-position":"top"},{default:o(()=>[l(i,{label:"用户名 *"},{default:o(()=>[l(s,{modelValue:a.username,"onUpdate:modelValue":e[0]||(e[0]=r=>a.username=r),placeholder:"至少3个字符",autocomplete:"username"},null,8,["modelValue"]),e[5]||(e[5]=n("div",{class:"hint app-muted"},"至少3个字符",-1))]),_:1}),l(i,{label:"密码 *"},{default:o(()=>[l(s,{modelValue:a.password,"onUpdate:modelValue":e[1]||(e[1]=r=>a.password=r),type:"password","show-password":"",placeholder:"至少8位且包含字母和数字",autocomplete:"new-password"},null,8,["modelValue"]),e[6]||(e[6]=n("div",{class:"hint app-muted"},"至少8位且包含字母和数字",-1))]),_:1}),l(i,{label:"确认密码 *"},{default:o(()=>[l(s,{modelValue:a.confirm_password,"onUpdate:modelValue":e[2]||(e[2]=r=>a.confirm_password=r),type:"password","show-password":"",placeholder:"请再次输入密码",autocomplete:"new-password",onKeyup:P(C,["enter"])},null,8,["modelValue"])]),_:1}),l(i,{label:D.value},{default:o(()=>[l(s,{modelValue:a.email,"onUpdate:modelValue":e[3]||(e[3]=r=>a.email=r),placeholder:"name@example.com",autocomplete:"email"},null,8,["modelValue"]),n("div",F,z(I.value),1)]),_:1},8,["label"]),l(i,{label:"验证码 *"},{default:o(()=>[n("div",G,[l(s,{modelValue:a.captcha,"onUpdate:modelValue":e[4]||(e[4]=r=>a.captcha=r),placeholder:"请输入验证码",onKeyup:P(C,["enter"])},null,8,["modelValue"]),w.value?(b(),N("img",{key:0,class:"captcha-img",src:w.value,alt:"验证码",title:"点击刷新",onClick:y},null,8,W)):B("",!0),l(p,{onClick:y},{default:o(()=>[...e[7]||(e[7]=[E("刷新",-1)])]),_:1})])]),_:1})]),_:1}),l(p,{type:"primary",class:"submit-btn",loading:V.value,onClick:C},{default:o(()=>[...e[8]||(e[8]=[E("注册",-1)])]),_:1},8,["loading"]),n("div",X,[e[10]||(e[10]=n("span",{class:"app-muted"},"已有账号?",-1)),l(p,{link:"",type:"primary",onClick:R},{default:o(()=>[...e[9]||(e[9]=[E("立即登录",-1)])]),_:1})])]),_:1})])}}},re=Y(Z,[["__scopeId","data-v-a9d7804f"]]);export{re as default};
|
||||||
@@ -1 +1 @@
|
|||||||
import{r as n,a1 as L,c as M,v as U,a0 as D,z as v,Q as s,J as a,aY as l,A as m,B as w,P as h,N as k,I as K,L as B,aD as j,O as z}from"./vendor-DR-vtVVc.js";import{u as F,p as J}from"./vendor-vue-l2lnRGj2.js";import{c as O}from"./auth-CtTBVBJk.js";import{_ as Q,v as Y}from"./index-C4rfJ09l.js";import{E as y}from"./vendor-element-D5SbqSD5.js";import"./vendor-axios-B9ygI19o.js";const q={class:"auth-wrap"},G={class:"actions"},H={class:"actions"},W={key:0,class:"app-muted"},X={__name:"ResetPasswordPage",setup(Z){const x=F(),A=J(),r=n(String(x.params.token||"")),i=n(!0),b=n(""),t=L({newPassword:"",confirmPassword:""}),P=n(!1),f=n(""),d=n(0);let u=null;function N(){if(typeof window>"u")return null;const o=window.__APP_INITIAL_STATE__;return!o||typeof o!="object"?null:(window.__APP_INITIAL_STATE__=null,o)}const I=M(()=>!!(i.value&&r.value&&!f.value));function S(){A.push("/login")}function C(){d.value=3,u=window.setInterval(()=>{d.value-=1,d.value<=0&&(window.clearInterval(u),u=null,window.location.href="/login")},1e3)}async function V(){if(!I.value)return;const o=t.newPassword,e=t.confirmPassword,c=Y(o);if(!c.ok){y.error(c.message);return}if(o!==e){y.error("两次输入的密码不一致");return}P.value=!0;try{await O({token:r.value,new_password:o}),f.value="密码重置成功!3秒后跳转到登录页面...",y.success("密码重置成功"),C()}catch(p){const _=p?.response?.data;y.error(_?.error||"重置失败")}finally{P.value=!1}}return U(()=>{const o=N();o?.page==="reset_password"?(r.value=String(o?.token||r.value||""),i.value=!!o?.valid,b.value=o?.error_message||(i.value?"":"重置链接无效或已过期,请重新申请密码重置")):r.value||(i.value=!1,b.value="重置链接无效或已过期,请重新申请密码重置")}),D(()=>{u&&window.clearInterval(u)}),(o,e)=>{const c=l("el-alert"),p=l("el-button"),_=l("el-input"),T=l("el-form-item"),R=l("el-form"),E=l("el-card");return m(),v("div",q,[s(E,{shadow:"never",class:"auth-card","body-style":{padding:"22px"}},{default:a(()=>[e[5]||(e[5]=w("div",{class:"brand"},[w("div",{class:"brand-title"},"知识管理平台"),w("div",{class:"brand-sub app-muted"},"重置密码")],-1)),i.value?(m(),v(h,{key:1},[f.value?(m(),K(c,{key:0,type:"success",closable:!1,title:"重置成功",description:f.value,"show-icon":"",class:"alert"},null,8,["description"])):B("",!0),s(R,{"label-position":"top"},{default:a(()=>[s(T,{label:"新密码(至少8位且包含字母和数字)"},{default:a(()=>[s(_,{modelValue:t.newPassword,"onUpdate:modelValue":e[0]||(e[0]=g=>t.newPassword=g),type:"password","show-password":"",placeholder:"请输入新密码",autocomplete:"new-password"},null,8,["modelValue"])]),_:1}),s(T,{label:"确认密码"},{default:a(()=>[s(_,{modelValue:t.confirmPassword,"onUpdate:modelValue":e[1]||(e[1]=g=>t.confirmPassword=g),type:"password","show-password":"",placeholder:"请再次输入新密码",autocomplete:"new-password",onKeyup:j(V,["enter"])},null,8,["modelValue"])]),_:1})]),_:1}),s(p,{type:"primary",class:"submit-btn",loading:P.value,disabled:!I.value,onClick:V},{default:a(()=>[...e[3]||(e[3]=[k(" 确认重置 ",-1)])]),_:1},8,["loading","disabled"]),w("div",H,[s(p,{link:"",type:"primary",onClick:S},{default:a(()=>[...e[4]||(e[4]=[k("返回登录",-1)])]),_:1}),d.value>0?(m(),v("span",W,z(d.value)+" 秒后自动跳转…",1)):B("",!0)])],64)):(m(),v(h,{key:0},[s(c,{type:"error",closable:!1,title:"链接已失效",description:b.value,"show-icon":""},null,8,["description"]),w("div",G,[s(p,{type:"primary",onClick:S},{default:a(()=>[...e[2]||(e[2]=[k("返回登录",-1)])]),_:1})])],64))]),_:1})])}}},ne=Q(X,[["__scopeId","data-v-0bbb511c"]]);export{ne as default};
|
import{r as n,a1 as L,c as M,v as U,a0 as D,z as v,Q as s,J as a,aY as l,A as m,B as w,P as h,N as k,I as K,L as B,aD as j,O as z}from"./vendor-DR-vtVVc.js";import{u as F,p as J}from"./vendor-vue-l2lnRGj2.js";import{c as O}from"./auth-3qca7BnL.js";import{_ as Q,v as Y}from"./index-DIRUAu3R.js";import{E as y}from"./vendor-element-D5SbqSD5.js";import"./vendor-axios-B9ygI19o.js";const q={class:"auth-wrap"},G={class:"actions"},H={class:"actions"},W={key:0,class:"app-muted"},X={__name:"ResetPasswordPage",setup(Z){const x=F(),A=J(),r=n(String(x.params.token||"")),i=n(!0),b=n(""),t=L({newPassword:"",confirmPassword:""}),P=n(!1),f=n(""),d=n(0);let u=null;function N(){if(typeof window>"u")return null;const o=window.__APP_INITIAL_STATE__;return!o||typeof o!="object"?null:(window.__APP_INITIAL_STATE__=null,o)}const I=M(()=>!!(i.value&&r.value&&!f.value));function S(){A.push("/login")}function C(){d.value=3,u=window.setInterval(()=>{d.value-=1,d.value<=0&&(window.clearInterval(u),u=null,window.location.href="/login")},1e3)}async function V(){if(!I.value)return;const o=t.newPassword,e=t.confirmPassword,c=Y(o);if(!c.ok){y.error(c.message);return}if(o!==e){y.error("两次输入的密码不一致");return}P.value=!0;try{await O({token:r.value,new_password:o}),f.value="密码重置成功!3秒后跳转到登录页面...",y.success("密码重置成功"),C()}catch(p){const _=p?.response?.data;y.error(_?.error||"重置失败")}finally{P.value=!1}}return U(()=>{const o=N();o?.page==="reset_password"?(r.value=String(o?.token||r.value||""),i.value=!!o?.valid,b.value=o?.error_message||(i.value?"":"重置链接无效或已过期,请重新申请密码重置")):r.value||(i.value=!1,b.value="重置链接无效或已过期,请重新申请密码重置")}),D(()=>{u&&window.clearInterval(u)}),(o,e)=>{const c=l("el-alert"),p=l("el-button"),_=l("el-input"),T=l("el-form-item"),R=l("el-form"),E=l("el-card");return m(),v("div",q,[s(E,{shadow:"never",class:"auth-card","body-style":{padding:"22px"}},{default:a(()=>[e[5]||(e[5]=w("div",{class:"brand"},[w("div",{class:"brand-title"},"知识管理平台"),w("div",{class:"brand-sub app-muted"},"重置密码")],-1)),i.value?(m(),v(h,{key:1},[f.value?(m(),K(c,{key:0,type:"success",closable:!1,title:"重置成功",description:f.value,"show-icon":"",class:"alert"},null,8,["description"])):B("",!0),s(R,{"label-position":"top"},{default:a(()=>[s(T,{label:"新密码(至少8位且包含字母和数字)"},{default:a(()=>[s(_,{modelValue:t.newPassword,"onUpdate:modelValue":e[0]||(e[0]=g=>t.newPassword=g),type:"password","show-password":"",placeholder:"请输入新密码",autocomplete:"new-password"},null,8,["modelValue"])]),_:1}),s(T,{label:"确认密码"},{default:a(()=>[s(_,{modelValue:t.confirmPassword,"onUpdate:modelValue":e[1]||(e[1]=g=>t.confirmPassword=g),type:"password","show-password":"",placeholder:"请再次输入新密码",autocomplete:"new-password",onKeyup:j(V,["enter"])},null,8,["modelValue"])]),_:1})]),_:1}),s(p,{type:"primary",class:"submit-btn",loading:P.value,disabled:!I.value,onClick:V},{default:a(()=>[...e[3]||(e[3]=[k(" 确认重置 ",-1)])]),_:1},8,["loading","disabled"]),w("div",H,[s(p,{link:"",type:"primary",onClick:S},{default:a(()=>[...e[4]||(e[4]=[k("返回登录",-1)])]),_:1}),d.value>0?(m(),v("span",W,z(d.value)+" 秒后自动跳转…",1)):B("",!0)])],64)):(m(),v(h,{key:0},[s(c,{type:"error",closable:!1,title:"链接已失效",description:b.value,"show-icon":""},null,8,["description"]),w("div",G,[s(p,{type:"primary",onClick:S},{default:a(()=>[...e[2]||(e[2]=[k("返回登录",-1)])]),_:1})])],64))]),_:1})])}}},ne=Q(X,[["__scopeId","data-v-0bbb511c"]]);export{ne as default};
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
|||||||
import{p as U}from"./vendor-vue-l2lnRGj2.js";import{_ as E}from"./index-C4rfJ09l.js";import{r as o,c as h,v as R,a0 as z,z as B,Q as i,J as s,aY as d,A as _,B as l,L as k,I as W,N,O as v}from"./vendor-DR-vtVVc.js";import"./vendor-element-D5SbqSD5.js";import"./vendor-axios-B9ygI19o.js";const $={class:"auth-wrap"},j={class:"actions"},D={key:0,class:"countdown app-muted"},J={__name:"VerifyResultPage",setup(M){const T=U(),p=o(!1),f=o(""),m=o(""),w=o(""),y=o(""),r=o(""),u=o(""),c=o(""),n=o(0);let a=null;function x(){if(typeof window>"u")return null;const e=window.__APP_INITIAL_STATE__;return!e||typeof e!="object"?null:(window.__APP_INITIAL_STATE__=null,e)}function A(e){const t=!!e?.success;p.value=t,f.value=e?.title||(t?"验证成功":"验证失败"),m.value=e?.message||e?.error_message||(t?"操作已完成,现在可以继续使用系统。":"操作失败,请稍后重试。"),w.value=e?.primary_label||(t?"立即登录":"重新注册"),y.value=e?.primary_url||(t?"/login":"/register"),r.value=e?.secondary_label||(t?"":"返回登录"),u.value=e?.secondary_url||(t?"":"/login"),c.value=e?.redirect_url||(t?"/login":""),n.value=Number(e?.redirect_seconds||(t?5:0))||0}const C=h(()=>!!(r.value&&u.value)),b=h(()=>!!(c.value&&n.value>0));async function g(e){if(e){if(e.startsWith("http://")||e.startsWith("https://")){window.location.href=e;return}await T.push(e)}}function L(){b.value&&(a=window.setInterval(()=>{n.value-=1,n.value<=0&&(window.clearInterval(a),a=null,window.location.href=c.value)},1e3))}return R(()=>{const e=x();A(e),L()}),z(()=>{a&&window.clearInterval(a)}),(e,t)=>{const I=d("el-button"),P=d("el-result"),V=d("el-card");return _(),B("div",$,[i(V,{shadow:"never",class:"auth-card","body-style":{padding:"22px"}},{default:s(()=>[t[2]||(t[2]=l("div",{class:"brand"},[l("div",{class:"brand-title"},"知识管理平台"),l("div",{class:"brand-sub app-muted"},"验证结果")],-1)),i(P,{icon:p.value?"success":"error",title:f.value,"sub-title":m.value,class:"result"},{extra:s(()=>[l("div",j,[i(I,{type:"primary",onClick:t[0]||(t[0]=S=>g(y.value))},{default:s(()=>[N(v(w.value),1)]),_:1}),C.value?(_(),W(I,{key:0,onClick:t[1]||(t[1]=S=>g(u.value))},{default:s(()=>[N(v(r.value),1)]),_:1})):k("",!0)]),b.value?(_(),B("div",D,v(n.value)+" 秒后自动跳转... ",1)):k("",!0)]),_:1},8,["icon","title","sub-title"])]),_:1})])}}},G=E(J,[["__scopeId","data-v-1fc6b081"]]);export{G as default};
|
import{p as U}from"./vendor-vue-l2lnRGj2.js";import{_ as E}from"./index-DIRUAu3R.js";import{r as o,c as h,v as R,a0 as z,z as B,Q as i,J as s,aY as d,A as _,B as l,L as k,I as W,N,O as v}from"./vendor-DR-vtVVc.js";import"./vendor-element-D5SbqSD5.js";import"./vendor-axios-B9ygI19o.js";const $={class:"auth-wrap"},j={class:"actions"},D={key:0,class:"countdown app-muted"},J={__name:"VerifyResultPage",setup(M){const T=U(),p=o(!1),f=o(""),m=o(""),w=o(""),y=o(""),r=o(""),u=o(""),c=o(""),n=o(0);let a=null;function x(){if(typeof window>"u")return null;const e=window.__APP_INITIAL_STATE__;return!e||typeof e!="object"?null:(window.__APP_INITIAL_STATE__=null,e)}function A(e){const t=!!e?.success;p.value=t,f.value=e?.title||(t?"验证成功":"验证失败"),m.value=e?.message||e?.error_message||(t?"操作已完成,现在可以继续使用系统。":"操作失败,请稍后重试。"),w.value=e?.primary_label||(t?"立即登录":"重新注册"),y.value=e?.primary_url||(t?"/login":"/register"),r.value=e?.secondary_label||(t?"":"返回登录"),u.value=e?.secondary_url||(t?"":"/login"),c.value=e?.redirect_url||(t?"/login":""),n.value=Number(e?.redirect_seconds||(t?5:0))||0}const C=h(()=>!!(r.value&&u.value)),b=h(()=>!!(c.value&&n.value>0));async function g(e){if(e){if(e.startsWith("http://")||e.startsWith("https://")){window.location.href=e;return}await T.push(e)}}function L(){b.value&&(a=window.setInterval(()=>{n.value-=1,n.value<=0&&(window.clearInterval(a),a=null,window.location.href=c.value)},1e3))}return R(()=>{const e=x();A(e),L()}),z(()=>{a&&window.clearInterval(a)}),(e,t)=>{const I=d("el-button"),P=d("el-result"),V=d("el-card");return _(),B("div",$,[i(V,{shadow:"never",class:"auth-card","body-style":{padding:"22px"}},{default:s(()=>[t[2]||(t[2]=l("div",{class:"brand"},[l("div",{class:"brand-title"},"知识管理平台"),l("div",{class:"brand-sub app-muted"},"验证结果")],-1)),i(P,{icon:p.value?"success":"error",title:f.value,"sub-title":m.value,class:"result"},{extra:s(()=>[l("div",j,[i(I,{type:"primary",onClick:t[0]||(t[0]=S=>g(y.value))},{default:s(()=>[N(v(w.value),1)]),_:1}),C.value?(_(),W(I,{key:0,onClick:t[1]||(t[1]=S=>g(u.value))},{default:s(()=>[N(v(r.value),1)]),_:1})):k("",!0)]),b.value?(_(),B("div",D,v(n.value)+" 秒后自动跳转... ",1)):k("",!0)]),_:1},8,["icon","title","sub-title"])]),_:1})])}}},G=E(J,[["__scopeId","data-v-1fc6b081"]]);export{G as default};
|
||||||
@@ -1 +1 @@
|
|||||||
import{p as c}from"./index-C4rfJ09l.js";async function o(t={}){const{data:a}=await c.get("/accounts",{params:t});return a}async function u(t){const{data:a}=await c.post("/accounts",t);return a}async function r(t,a){const{data:n}=await c.put(`/accounts/${t}`,a);return n}async function e(t){const{data:a}=await c.delete(`/accounts/${t}`);return a}async function i(t,a){const{data:n}=await c.put(`/accounts/${t}/remark`,a);return n}async function p(t,a){const{data:n}=await c.post(`/accounts/${t}/start`,a);return n}async function d(t){const{data:a}=await c.post(`/accounts/${t}/stop`,{});return a}async function f(t){const{data:a}=await c.post("/accounts/batch/start",t);return a}async function w(t){const{data:a}=await c.post("/accounts/batch/stop",t);return a}async function y(){const{data:t}=await c.post("/accounts/clear",{});return t}async function A(t,a={}){const{data:n}=await c.post(`/accounts/${t}/screenshot`,a);return n}export{w as a,f as b,y as c,d,e,o as f,u as g,i as h,p as s,A as t,r as u};
|
import{p as c}from"./index-DIRUAu3R.js";async function o(t={}){const{data:a}=await c.get("/accounts",{params:t});return a}async function u(t){const{data:a}=await c.post("/accounts",t);return a}async function r(t,a){const{data:n}=await c.put(`/accounts/${t}`,a);return n}async function e(t){const{data:a}=await c.delete(`/accounts/${t}`);return a}async function i(t,a){const{data:n}=await c.put(`/accounts/${t}/remark`,a);return n}async function p(t,a){const{data:n}=await c.post(`/accounts/${t}/start`,a);return n}async function d(t){const{data:a}=await c.post(`/accounts/${t}/stop`,{});return a}async function f(t){const{data:a}=await c.post("/accounts/batch/start",t);return a}async function w(t){const{data:a}=await c.post("/accounts/batch/stop",t);return a}async function y(){const{data:t}=await c.post("/accounts/clear",{});return t}async function A(t,a={}){const{data:n}=await c.post(`/accounts/${t}/screenshot`,a);return n}export{w as a,f as b,y as c,d,e,o as f,u as g,i as h,p as s,A as t,r as u};
|
||||||
@@ -1 +1 @@
|
|||||||
import{p as s}from"./index-C4rfJ09l.js";async function r(){const{data:a}=await s.get("/email/verify-status");return a}async function o(){const{data:a}=await s.post("/generate_captcha",{});return a}async function e(a){const{data:t}=await s.post("/login",a);return t}async function i(a){const{data:t}=await s.post("/register",a);return t}async function c(a){const{data:t}=await s.post("/resend-verify-email",a);return t}async function f(a){const{data:t}=await s.post("/forgot-password",a);return t}async function u(a){const{data:t}=await s.post("/reset-password-confirm",a);return t}export{f as a,i as b,u as c,r as f,o as g,e as l,c as r};
|
import{p as s}from"./index-DIRUAu3R.js";async function r(){const{data:a}=await s.get("/email/verify-status");return a}async function o(){const{data:a}=await s.post("/generate_captcha",{});return a}async function e(a){const{data:t}=await s.post("/login",a);return t}async function i(a){const{data:t}=await s.post("/register",a);return t}async function c(a){const{data:t}=await s.post("/resend-verify-email",a);return t}async function f(a){const{data:t}=await s.post("/forgot-password",a);return t}async function u(a){const{data:t}=await s.post("/reset-password-confirm",a);return t}export{f as a,i as b,u as c,r as f,o as g,e as l,c as r};
|
||||||
File diff suppressed because one or more lines are too long
2
static/app/assets/index-DIRUAu3R.js
Normal file
2
static/app/assets/index-DIRUAu3R.js
Normal file
File diff suppressed because one or more lines are too long
@@ -4,7 +4,7 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
|
||||||
<title>知识管理平台</title>
|
<title>知识管理平台</title>
|
||||||
<script type="module" crossorigin src="./assets/index-C4rfJ09l.js"></script>
|
<script type="module" crossorigin src="./assets/index-DIRUAu3R.js"></script>
|
||||||
<link rel="modulepreload" crossorigin href="./assets/vendor-DR-vtVVc.js">
|
<link rel="modulepreload" crossorigin href="./assets/vendor-DR-vtVVc.js">
|
||||||
<link rel="modulepreload" crossorigin href="./assets/vendor-element-D5SbqSD5.js">
|
<link rel="modulepreload" crossorigin href="./assets/vendor-element-D5SbqSD5.js">
|
||||||
<link rel="modulepreload" crossorigin href="./assets/vendor-vue-l2lnRGj2.js">
|
<link rel="modulepreload" crossorigin href="./assets/vendor-vue-l2lnRGj2.js">
|
||||||
|
|||||||
Reference in New Issue
Block a user