fix(install): Docker .env 持久化与 entrypoint 覆盖 Compose 占位符
向导 init-db 后立即同步 nexus-state 卷;entrypoint 跳过不完整 env 并在启动前 source /app/.env,避免缺 DATABASE_URL 与无密码 REDIS_URL 导致容器崩溃。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+26
-4
@@ -45,11 +45,28 @@ s.close()
|
||||
}
|
||||
|
||||
restore_persisted_env() {
|
||||
if [ -f "${PERSIST_ENV}" ]; then
|
||||
cp "${PERSIST_ENV}" /app/.env
|
||||
chmod 600 /app/.env
|
||||
echo "entrypoint: restored .env from ${PERSIST_ENV}"
|
||||
if [ ! -f "${PERSIST_ENV}" ]; then
|
||||
return 0
|
||||
fi
|
||||
if ! grep -q '^NEXUS_DATABASE_URL=' "${PERSIST_ENV}" 2>/dev/null; then
|
||||
echo "entrypoint: warn — ${PERSIST_ENV} missing NEXUS_DATABASE_URL; skip restore (complete install wizard)"
|
||||
return 0
|
||||
fi
|
||||
cp "${PERSIST_ENV}" /app/.env
|
||||
chmod 600 /app/.env
|
||||
echo "entrypoint: restored .env from ${PERSIST_ENV}"
|
||||
}
|
||||
|
||||
# Compose may inject placeholder NEXUS_REDIS_URL (no password). Wizard .env wins.
|
||||
export_app_env() {
|
||||
if [ ! -f /app/.env ]; then
|
||||
return 0
|
||||
fi
|
||||
set -a
|
||||
# shellcheck disable=SC1091
|
||||
. /app/.env
|
||||
set +a
|
||||
echo "entrypoint: exported NEXUS_* from /app/.env (overrides Compose placeholders)"
|
||||
}
|
||||
|
||||
write_env_from_environment() {
|
||||
@@ -66,6 +83,10 @@ write_env_from_environment() {
|
||||
echo "entrypoint: install wizard pending — skip writing /app/.env"
|
||||
return 0
|
||||
fi
|
||||
if [ -z "${NEXUS_DATABASE_URL:-}" ]; then
|
||||
echo "entrypoint: skip writing /app/.env — NEXUS_DATABASE_URL not set (use /app/install.html)"
|
||||
return 0
|
||||
fi
|
||||
mkdir -p "${PERSIST_DIR}"
|
||||
{
|
||||
echo "# Generated by docker/entrypoint.sh — do not commit"
|
||||
@@ -101,5 +122,6 @@ fi
|
||||
|
||||
restore_persisted_env
|
||||
write_env_from_environment
|
||||
export_app_env
|
||||
|
||||
exec "$@"
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# 2026-06-06 — Docker .env 持久化与 entrypoint 覆盖修复
|
||||
|
||||
## 摘要
|
||||
|
||||
修复 1Panel 安装向导完成后 `nx update`/容器重启导致 `nexus-state` 卷内 `.env` 缺少 `NEXUS_DATABASE_URL`、应用连 `127.0.0.1` 崩溃的问题。
|
||||
|
||||
## 动机
|
||||
|
||||
- `init-db` 仅写 `/app/.env`,卷同步依赖容器 EXIT trap,重建容器时 `restore_persisted_env` 可能恢复 entrypoint 生成的简版 env。
|
||||
- `write_env_from_environment` 在 `NEXUS_INSTALL_WIZARD_PENDING=0` 时用 `env | grep NEXUS_` 写卷,Compose 无 `NEXUS_DATABASE_URL`。
|
||||
|
||||
## 涉及文件
|
||||
|
||||
- `server/api/install.py` — `_sync_env_to_persist_volume`;`init-db` / Redis 修补 / lock 后立即同步卷
|
||||
- `docker/entrypoint.sh` — 无 `NEXUS_DATABASE_URL` 的卷不 restore;无 `NEXUS_DATABASE_URL` 环境变量时不生成 `.env`;`export_app_env` 在启动前 source `/app/.env`,使向导写入的 `DATABASE_URL` / `REDIS_URL` 覆盖 Compose 占位符
|
||||
|
||||
## 迁移 / 重启
|
||||
|
||||
已损坏的卷内 `.env` 需手工补全 `NEXUS_DATABASE_URL` 与 Redis URL 后 `docker start`;新装/重装向导自动同步。
|
||||
|
||||
## 验证
|
||||
|
||||
```bash
|
||||
.venv/bin/pytest tests/test_security_unit.py -q
|
||||
# 向导 init-db 后:
|
||||
docker exec nexus-prod-nexus-1 grep NEXUS_DATABASE_URL /var/lib/nexus/.env
|
||||
```
|
||||
@@ -0,0 +1,29 @@
|
||||
# 2026-06-06 — 安装向导:1Panel Docker 进程守护
|
||||
|
||||
## 摘要
|
||||
|
||||
1Panel + Docker 部署路径下,安装向导步骤 5 展示 Docker/Compose 守护清单(替代 Supervisor/venv/Nginx 裸机清单);`init-db` 调用 `_configure_docker_guardian` 检测容器 health 与 1Panel 网络。
|
||||
|
||||
## 动机
|
||||
|
||||
- 1Panel 生产使用 `docker-compose.prod.yml`,进程守护由 **restart + healthcheck** 负责,容器内写 Supervisor 配置无意义且易误导。
|
||||
- 步骤 5 原清单面向宝塔/裸机,与 1Panel 运维路径不符。
|
||||
|
||||
## 涉及文件
|
||||
|
||||
- `server/api/install.py` — `_configure_docker_guardian`、`_configure_guardian` 分支、`docker_mode` 响应字段
|
||||
- `web/app/install.html` — 步骤 1 准备项、步骤 5 1Panel 清单(OpenResty/nx update)
|
||||
- `docs/project/nexus-1panel-operations-knowledge.md` — §3.5 守护说明
|
||||
- `tests/test_security_unit.py` — Docker 守护分支测试
|
||||
|
||||
## 迁移 / 重启
|
||||
|
||||
无 schema 变更。部署后 `bash deploy/sync-install-wizard-to-container.sh` 或 `nx update`。
|
||||
|
||||
## 验证
|
||||
|
||||
```bash
|
||||
.venv/bin/pytest tests/test_security_unit.py -k configure_guardian
|
||||
```
|
||||
|
||||
浏览器完成向导步骤 5,确认显示「1Panel 后续配置」而非 Supervisor 清单。
|
||||
@@ -289,7 +289,17 @@ docker compose -f docker/docker-compose.prod.yml exec mysql \
|
||||
|
||||
## 6. 日常运维
|
||||
|
||||
### 6.1 升级(Gitea 有新 commit)
|
||||
### 6.1 一键升级(推荐)
|
||||
|
||||
```bash
|
||||
# 可选:echo 'NEXUS_GITEA_TOKEN=...' > /root/.nexus-deploy.env && chmod 600 /root/.nexus-deploy.env
|
||||
bash /opt/nexus/deploy/upgrade-1panel-docker.sh --check # 仅查是否有更新
|
||||
bash /opt/nexus/deploy/upgrade-1panel-docker.sh # 备份 DB + pull + 重建 + /health
|
||||
```
|
||||
|
||||
脚本:`deploy/upgrade-1panel-docker.sh`(`--no-backup` / `--prune` / `--dry-run`)。
|
||||
|
||||
### 6.2 手工升级
|
||||
|
||||
```bash
|
||||
cd /opt/nexus
|
||||
|
||||
@@ -109,7 +109,7 @@ entrypoint EXIT trap
|
||||
| 3 | 初始化 | `POST /api/install/init-db` | 建表、三写、发 `install_token`;**.env 已存在则 403** |
|
||||
| 4 | 管理员 | `GET /api/install/connection-check` | 读 `.env` 验 MySQL+Redis |
|
||||
| 4 | 创建 admin | `POST /api/install/create-admin` | 需 `install_token` |
|
||||
| 5 | 完成 | `POST /api/install/lock`(兜底) | 写 `.install_locked` |
|
||||
| 5 | 完成 | `POST /api/install/lock`(兜底) | 写 `.install_locked`;**Docker 模式展示 1Panel 守护清单** |
|
||||
|
||||
辅助:`GET /status`、`GET /state`(断点续装)。
|
||||
|
||||
@@ -125,7 +125,17 @@ entrypoint EXIT trap
|
||||
| 用户名 | `nexus` | **无**(1Panel Redis 无账号;向导仅填密码) |
|
||||
| 密码 | 1Panel「数据库」里建的 nexus 密码 | 1Panel Redis **应用参数**中的口令 |
|
||||
|
||||
### 3.3 Docker 预填来源
|
||||
### 3.3 进程守护(1Panel Docker)
|
||||
|
||||
| 层级 | 机制 | 说明 |
|
||||
|------|------|------|
|
||||
| Layer 1 | Compose `restart: unless-stopped` + `healthcheck` | 容器崩溃自动拉起 |
|
||||
| Layer 2 | Python `self_monitor`(30s) | 向导完成后正常模式自动运行 |
|
||||
| Layer 3 | 宿主机 OpenResty + 可选 `health_monitor.sh` | 反代 `127.0.0.1:8600`;见 `deploy/1panel/openresty-nexus.conf.example` |
|
||||
|
||||
`init-db` 在 `NEXUS_DOCKER_WRITE_ENV=1` 时调用 `_configure_docker_guardian`(**不**在容器内写 Supervisor)。
|
||||
|
||||
### 3.4 Docker 预填来源
|
||||
|
||||
优先级(`_onepanel_service_host`):
|
||||
|
||||
@@ -135,7 +145,7 @@ entrypoint EXIT trap
|
||||
|
||||
`env-check` 在 `NEXUS_DOCKER_WRITE_ENV=1` 时返回 `docker_defaults`(主机/端口/库名,**不含** Redis 用户名)。前端会覆盖 stale 的 `127.0.0.1` / `host.docker.internal`,并隐藏 Redis 用户名字段。
|
||||
|
||||
### 3.4 连接 URL 格式(必读)
|
||||
### 3.5 连接 URL 格式(必读)
|
||||
|
||||
**MySQL**(写入 `NEXUS_DATABASE_URL`):
|
||||
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
# 1Panel 安装向导验收报告(待服务器执行)
|
||||
|
||||
| 项 | 内容 |
|
||||
|----|------|
|
||||
| 日期 | 2026-06-06 |
|
||||
| 目标 | 完成 `https://api.synaglobal.vip/app/install.html` 五步安装 |
|
||||
| 代码基线 | `main` @ `b25d079`(1Panel 容器名 sync) |
|
||||
| DNS | `api.synaglobal.vip` → `20.24.218.235`(新 VPS) |
|
||||
|
||||
## Agent 侧探测(本机)
|
||||
|
||||
| 检查 | 结果 | 说明 |
|
||||
|------|------|------|
|
||||
| SSH `Host nexus` | 失败 | 本机无 `~/.ssh/id_rsa_nexus`,无法代登服务器 |
|
||||
| `https://api.synaglobal.vip/health` | 连接重置 | 外网 HTTPS 未通或 OpenResty 未反代 |
|
||||
| `20.24.218.235:8600` | 不可达 | 预期:仅 `127.0.0.1:8600` 监听 |
|
||||
|
||||
**结论**:安装向导必须在 **1Panel 终端** 本机验证;外网访问依赖 OpenResty 反代配置。
|
||||
|
||||
## 服务器执行清单(复制到 1Panel 终端)
|
||||
|
||||
### 1. 更新到 b25d079+ 并 sync 1Panel 主机名
|
||||
|
||||
```bash
|
||||
cd /opt/nexus
|
||||
git fetch && git log -1 --oneline
|
||||
nx update --no-cache
|
||||
```
|
||||
|
||||
### 2. 一键验收
|
||||
|
||||
```bash
|
||||
bash deploy/verify-1panel-install-wizard.sh
|
||||
```
|
||||
|
||||
关键 `[PASS]`:
|
||||
|
||||
- 已包含 b25d079
|
||||
- `NEXUS_1PANEL_DB_HOST=1Panel-mysql-*`
|
||||
- `NEXUS_1PANEL_REDIS_HOST=1Panel-redis-*`
|
||||
- Nexus 已接入 `1panel-network`
|
||||
- 容器内 TCP → MySQL/Redis
|
||||
- `/app/.env` 不存在
|
||||
- 步骤 3 预填 `db_host=1Panel-mysql-*`(非 `host.docker.internal`)
|
||||
|
||||
### 3. 配置 OpenResty(外网访问)
|
||||
|
||||
1. 1Panel → 应用商店 → **OpenResty**
|
||||
2. 网站 → 域名 `api.synaglobal.vip` → 反代 `http://127.0.0.1:8600`
|
||||
3. HTTPS → Let's Encrypt
|
||||
4. 参考 `deploy/1panel/openresty-nexus.conf.example`(`/ws/` 超时 ≥ 3600s)
|
||||
|
||||
验证:
|
||||
|
||||
```bash
|
||||
curl -sk https://api.synaglobal.vip/health # → ok
|
||||
```
|
||||
|
||||
### 4. 1Panel 数据库准备
|
||||
|
||||
- 库名:`nexus`
|
||||
- 用户:`nexus`(仅授权 `nexus` 库)
|
||||
- 记下密码(步骤 3 填写)
|
||||
|
||||
### 5. 浏览器完成安装向导
|
||||
|
||||
打开:`https://api.synaglobal.vip/app/install.html`
|
||||
|
||||
| 步骤 | 要点 |
|
||||
|------|------|
|
||||
| 2 环境检测 | MySQL/Redis 显示 `✓ 已安装(1Panel-mysql-xxxx:3306)` |
|
||||
| 3 数据库 | `db_host` / `redis_host` 为容器名;填 MySQL 密码;Redis 有密码则填 `redis_pass` |
|
||||
| 4 连接 | MySQL + Redis 全绿 |
|
||||
| 4 管理员 | 创建 admin 账号 |
|
||||
| 5 锁定 | 完成安装 |
|
||||
|
||||
### 6. 安装后 L5 抽测
|
||||
|
||||
```bash
|
||||
curl -sk https://api.synaglobal.vip/health
|
||||
curl -sk -o /dev/null -w '%{http_code}\n' https://api.synaglobal.vip/app/
|
||||
# 未登录 API → 401
|
||||
curl -sk -o /dev/null -w '%{http_code}\n' https://api.synaglobal.vip/api/servers/
|
||||
```
|
||||
|
||||
## 常见故障
|
||||
|
||||
| 现象 | 处理 |
|
||||
|------|------|
|
||||
| 步骤 3 仍是 `host.docker.internal` | `nx update --no-cache`;确认 `git log -1` ≥ b25d079 |
|
||||
| MySQL `Can't connect on host.docker.internal` | `bash deploy/detect-1panel-services.sh` → `nx update` |
|
||||
| `/app/install.html` 404 | `bash deploy/sync-install-wizard-to-container.sh` |
|
||||
| `/app/.env` 已存在但向导未完成 | 删 `nexus-state` 卷内 `.env`,设 `NEXUS_INSTALL_WIZARD_PENDING=1`,`nx update` |
|
||||
| 外网 HTTPS 失败、本机 8600 正常 | 配置 OpenResty 反代 + SSL |
|
||||
|
||||
## DoD(待填)
|
||||
|
||||
- [ ] `verify-1panel-install-wizard.sh` 全 PASS
|
||||
- [ ] `https://api.synaglobal.vip/health` → `ok`
|
||||
- [ ] 安装向导步骤 5 锁定完成
|
||||
- [ ] `/app/` 可登录
|
||||
+81
-1
@@ -443,6 +443,7 @@ def _patch_env_redis_url(new_url: str) -> None:
|
||||
out.append(new_line)
|
||||
write_utf8_lf(ENV_FILE, "\n".join(out) + "\n")
|
||||
logger.info("Patched NEXUS_REDIS_URL in %s", ENV_FILE)
|
||||
_sync_env_to_persist_volume()
|
||||
|
||||
|
||||
def _resolve_redis_url(req: InitDbRequest) -> tuple[bool, str, str | None]:
|
||||
@@ -553,6 +554,28 @@ def _write_env(req: InitDbRequest, secret_key: str, api_key: str, encryption_key
|
||||
|
||||
write_utf8_lf(ENV_FILE, "\n".join(lines) + "\n")
|
||||
logger.info(f".env written to {ENV_FILE}")
|
||||
_sync_env_to_persist_volume()
|
||||
|
||||
|
||||
def _persist_env_path() -> Path:
|
||||
persist_dir = os.environ.get("NEXUS_PERSIST_DIR", "/var/lib/nexus").strip() or "/var/lib/nexus"
|
||||
return Path(persist_dir) / ".env"
|
||||
|
||||
|
||||
def _sync_env_to_persist_volume() -> None:
|
||||
"""Copy /app/.env to nexus-state volume immediately (Docker install wizard)."""
|
||||
if not _is_docker_install_mode() or not ENV_FILE.is_file():
|
||||
return
|
||||
dest = _persist_env_path()
|
||||
try:
|
||||
dest.parent.mkdir(parents=True, exist_ok=True)
|
||||
import shutil
|
||||
|
||||
shutil.copy2(ENV_FILE, dest)
|
||||
dest.chmod(0o600)
|
||||
logger.info("Synced .env to persist volume %s", dest)
|
||||
except OSError as e:
|
||||
logger.warning("Failed to sync .env to persist volume: %s", e)
|
||||
|
||||
|
||||
def _write_config_json(req: InitDbRequest, api_key: str, site_url: str) -> None:
|
||||
@@ -586,8 +609,62 @@ def _detect_bt_panel() -> bool:
|
||||
return Path("/www/server/panel/class/common.py").exists()
|
||||
|
||||
|
||||
def _configure_docker_guardian(install_dir: str, api_port: str) -> list[str]:
|
||||
"""1Panel + Docker: Compose restart/healthcheck + in-app self_monitor (no host Supervisor)."""
|
||||
results: list[str] = []
|
||||
|
||||
if Path("/.dockerenv").is_file():
|
||||
results.append("✓ Layer 1: Docker 容器运行中(Compose restart: unless-stopped)")
|
||||
else:
|
||||
results.append("⚠ Layer 1: 未检测到 /.dockerenv — 请确认使用 docker-compose.prod.yml 部署")
|
||||
|
||||
results.append("✓ Layer 2: Python self_monitor 随正常模式启动(每 30s 自检 Redis/MySQL/WebSocket)")
|
||||
|
||||
try:
|
||||
import urllib.request
|
||||
|
||||
with urllib.request.urlopen(
|
||||
f"http://127.0.0.1:{api_port}/health", timeout=3.0
|
||||
) as resp:
|
||||
body = resp.read().decode().strip()
|
||||
if body == "ok":
|
||||
results.append(f"✓ 容器内 /health 正常(127.0.0.1:{api_port})")
|
||||
else:
|
||||
results.append(f"⚠ /health 响应异常: {body[:80]}")
|
||||
except Exception as e:
|
||||
results.append(f"⚠ 容器内 /health 暂不可达: {e}")
|
||||
|
||||
db_host = _onepanel_service_host("DB")
|
||||
redis_host = _onepanel_service_host("REDIS")
|
||||
if db_host and redis_host:
|
||||
results.append(f"✓ 1panel-network 容器名: MySQL={db_host}, Redis={redis_host}")
|
||||
elif db_host or redis_host:
|
||||
results.append(
|
||||
f"⚠ 1Panel 主机名不完整(DB={db_host or '未设置'}, Redis={redis_host or '未设置'})"
|
||||
)
|
||||
else:
|
||||
results.append("ℹ 未配置 NEXUS_1PANEL_*_HOST — 宿主机执行 nx update 可自动探测")
|
||||
|
||||
site_url = os.environ.get("NEXUS_API_BASE_URL", "").strip()
|
||||
publish_port = os.environ.get("NEXUS_PUBLISH_PORT", api_port).strip() or api_port
|
||||
if site_url:
|
||||
results.append(
|
||||
f"ℹ Layer 3(宿主机): 1Panel OpenResty 反代 {site_url} → 127.0.0.1:{publish_port}"
|
||||
)
|
||||
else:
|
||||
results.append(
|
||||
f"ℹ Layer 3(宿主机): 配置 OpenResty 反代 → 127.0.0.1:{publish_port}(见 deploy/1panel/openresty-nexus.conf.example)"
|
||||
)
|
||||
|
||||
results.append(f"ℹ 日常升级: 宿主机 cd {install_dir} && nx update")
|
||||
return results
|
||||
|
||||
|
||||
def _configure_guardian(install_dir: str, api_port: str) -> list[str]:
|
||||
"""Best-effort: configure Supervisor, crontab, health_monitor.sh"""
|
||||
"""Best-effort process guardian: Docker/1Panel vs bare-metal Supervisor stack."""
|
||||
if _is_docker_install_mode():
|
||||
return _configure_docker_guardian(install_dir, api_port)
|
||||
|
||||
results: list[str] = []
|
||||
bt_panel = _detect_bt_panel()
|
||||
|
||||
@@ -687,6 +764,7 @@ def _configure_guardian(install_dir: str, api_port: str) -> list[str]:
|
||||
|
||||
def _finalize_install_lock() -> None:
|
||||
"""Write install lock marker and remove wizard state file."""
|
||||
_sync_env_to_persist_volume()
|
||||
write_utf8_lf(
|
||||
INSTALL_LOCK,
|
||||
f"Nexus installation locked at {datetime.now(timezone.utc).isoformat()}\n",
|
||||
@@ -1047,6 +1125,7 @@ async def init_db(req: InitDbRequest):
|
||||
"site_url": site_url,
|
||||
"api_port": req.api_port,
|
||||
"guardian_results": guardian_results,
|
||||
"docker_mode": _is_docker_install_mode(),
|
||||
"step": 4,
|
||||
}
|
||||
write_utf8_lf(STATE_FILE, json.dumps(state, ensure_ascii=False) + "\n")
|
||||
@@ -1059,6 +1138,7 @@ async def init_db(req: InitDbRequest):
|
||||
"tables_created": 14,
|
||||
"guardian_results": guardian_results,
|
||||
"bt_panel": _detect_bt_panel(),
|
||||
"docker_mode": _is_docker_install_mode(),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -274,6 +274,28 @@ def test_redis_url_candidates_never_tries_root_in_docker_mode(monkeypatch):
|
||||
assert not any("root:" in u for u in urls)
|
||||
|
||||
|
||||
def test_configure_guardian_docker_mode_skips_supervisor(monkeypatch):
|
||||
from server.api import install as install_api
|
||||
from server.api.install import _configure_docker_guardian, _configure_guardian
|
||||
|
||||
monkeypatch.setenv("NEXUS_DOCKER_WRITE_ENV", "1")
|
||||
monkeypatch.setenv("NEXUS_API_BASE_URL", "https://api.example.com")
|
||||
monkeypatch.setattr(
|
||||
install_api.Path,
|
||||
"is_file",
|
||||
lambda self: str(self) == "/.dockerenv",
|
||||
)
|
||||
|
||||
docker_results = _configure_docker_guardian("/opt/nexus", "8600")
|
||||
docker_text = "\n".join(docker_results)
|
||||
assert "Supervisor" not in docker_text
|
||||
assert "self_monitor" in docker_text
|
||||
assert "OpenResty" in docker_text or "反代" in docker_text
|
||||
|
||||
bare_results = _configure_guardian("/opt/nexus", "8600")
|
||||
assert bare_results == docker_results
|
||||
|
||||
|
||||
def test_init_db_request_from_redis_url_legacy_and_colon_pass():
|
||||
from server.api.install import _init_db_request_from_redis_url
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
import{$n as e,A as t,Ar as n,Br as r,Cr as i,F as a,N as o,Rt as s,X as c,Xn as l,_r as u,ar as d,er as f,hr as p,ir as m,k as h,nr as g,or as _,pr as v,w as y}from"./index-C_dP75wD.js";import{o as b,t as x}from"./VContainer-DGQjsCTw.js";import{t as S}from"./VChip-oig-b64O.js";import{t as C}from"./VSelect-V_uWG8_q.js";import{t as w}from"./VDataTableServer-BMEL-4WM.js";import{n as T,t as E}from"./VRow-MZyk7N4E.js";import{t as D}from"./VSpacer-Cl6Zcy_X.js";var O={class:`font-weight-medium`},k={class:`text-body-2`},A={class:`text-caption text-medium-emphasis`},j=_({__name:`AlertsPage`,setup(_){let j=b(),M=n(!1),N=n([]),P=n(0),F=n(1),I=n(null),L=n(null),R=n([{label:`今日告警`,value:`0`,color:`blue`,icon:`mdi-alert`},{label:`活跃告警`,value:`0`,color:`orange`,icon:`mdi-bell-ring`},{label:`已恢复`,value:`0`,color:`green`,icon:`mdi-check-circle`},{label:`Top 服务器`,value:`—`,color:`red`,icon:`mdi-server`}]),z=[{label:`CPU`,value:`cpu`},{label:`内存`,value:`memory`},{label:`磁盘`,value:`disk`},{label:`连接`,value:`connection`}],B=[{label:`活跃`,value:`active`},{label:`已恢复`,value:`recovered`}],V=[{title:`类型`,key:`alert_type`,width:100},{title:`服务器`,key:`server_name`},{title:`详情`,key:`value`},{title:`状态`,key:`is_recovery`,width:100},{title:`时间`,key:`created_at`,width:160}];async function H(){try{let e=await s.get(`/alert-history/stats`);R.value[0].value=String(e.today||0),R.value[1].value=String(e.active||0),R.value[2].value=String(e.recovered||0),R.value[3].value=e.top_server||`—`}catch{j(`加载统计失败`,`error`)}}async function U(){M.value=!0;try{let e=await s.get(`/alert-history/`,{page:F.value,per_page:20,type:I.value||void 0,status:L.value||void 0});N.value=e.items||[],P.value=e.total||0}catch{N.value=[]}finally{M.value=!1}}function W(e){return e===`cpu`||e===`CPU`?`error`:e===`memory`||e===`内存`?`warning`:e===`disk`||e===`磁盘`?`info`:`primary`}function G(e){return e===`cpu`||e===`CPU`?`mdi-chip`:e===`memory`||e===`内存`?`mdi-memory`:e===`disk`||e===`磁盘`?`mdi-harddisk`:`mdi-lan-disconnect`}return v(()=>{H(),U()}),(n,s)=>(p(),f(x,{fluid:``,class:`pa-6`},{default:i(()=>[d(E,{class:`mb-4`},{default:i(()=>[(p(!0),g(l,null,u(R.value,e=>(p(),f(T,{cols:`12`,sm:`6`,lg:`3`,key:e.label},{default:i(()=>[d(y,{elevation:`0`,lines:`two`,rounded:`lg`,border:``},{default:i(()=>[d(h,null,{append:i(()=>[d(c,{color:e.color,size:`30`},{default:i(()=>[m(r(e.icon),1)]),_:2},1032,[`color`])]),default:i(()=>[d(t,{class:`text-body-small`},{default:i(()=>[m(r(e.label),1)]),_:2},1024),d(t,null,{default:i(()=>[m(r(e.value),1)]),_:2},1024)]),_:2},1024)]),_:2},1024)]),_:2},1024))),128))]),_:1}),d(o,{elevation:`0`,border:``,rounded:`lg`},{default:i(()=>[d(a,{class:`d-flex align-center`},{default:i(()=>[s[5]||=m(` 告警历史 `,-1),d(D),d(C,{modelValue:I.value,"onUpdate:modelValue":[s[0]||=e=>I.value=e,s[1]||=e=>{F.value=1,U()}],items:z,"item-title":`label`,"item-value":`value`,label:`类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},clearable:``},null,8,[`modelValue`]),d(C,{modelValue:L.value,"onUpdate:modelValue":[s[2]||=e=>L.value=e,s[3]||=e=>{F.value=1,U()}],items:B,"item-title":`label`,"item-value":`value`,label:`状态`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`])]),_:1}),d(w,{items:N.value,headers:V,"items-length":P.value,loading:M.value,page:F.value,"items-per-page":20,hover:``,density:`comfortable`,"onUpdate:page":s[4]||=e=>{F.value=e,U()}},{"item.alert_type":i(({item:e})=>[d(S,{color:W(e.alert_type),size:`small`,variant:`tonal`,label:``},{prepend:i(()=>[d(c,{size:`12`},{default:i(()=>[m(r(G(e.alert_type)),1)]),_:2},1024)]),default:i(()=>[m(` `+r(e.alert_type),1)]),_:2},1032,[`color`])]),"item.server_name":i(({item:t})=>[e(`span`,O,r(t.server_name),1)]),"item.value":i(({item:t})=>[e(`span`,k,r(t.value),1)]),"item.is_recovery":i(({item:e})=>[e.is_recovery?(p(),f(S,{key:0,color:`success`,size:`small`,variant:`tonal`,label:``},{default:i(()=>[...s[6]||=[m(`已恢复`,-1)]]),_:1})):(p(),f(S,{key:1,color:`error`,size:`small`,variant:`tonal`,label:``},{default:i(()=>[...s[7]||=[m(`告警中`,-1)]]),_:1}))]),"item.created_at":i(({item:t})=>[e(`span`,A,r(t.created_at),1)]),"no-data":i(()=>[...s[8]||=[e(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{j as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{$n as e,Ar as t,Br as n,Cr as r,F as i,Ir as a,N as o,Rt as s,ar as c,er as l,hr as u,ir as d,or as f,pr as p,t as m}from"./index-C_dP75wD.js";import{o as h,t as g}from"./VContainer-DGQjsCTw.js";import{t as _}from"./VChip-oig-b64O.js";import{t as v}from"./VSelect-V_uWG8_q.js";import{t as y}from"./VDataTableServer-BMEL-4WM.js";import{t as b}from"./VSpacer-Cl6Zcy_X.js";import{a as x,n as S,r as C,t as w}from"./auditNormalize-BHJPJFP9.js";var T={class:`font-weight-medium`},E={class:`text-body-2`},D={class:`text-body-2 text-medium-emphasis text-truncate`,style:{"max-width":`300px`,display:`inline-block`}},O={class:`text-caption text-medium-emphasis`},k={class:`text-caption text-medium-emphasis`},A=f({__name:`AuditPage`,setup(f){let A=h(),j=t(!1),M=t([]),N=t(0),P=t(1),F=t(null),I=t(``),L=t(``),R=S,z=[{title:`操作`,key:`action`,width:100},{title:`用户`,key:`admin_username`,width:100},{title:`目标`,key:`target`,width:140},{title:`详情`,key:`detail`},{title:`IP`,key:`ip_address`,width:120},{title:`时间`,key:`created_at`,width:160}];async function B(){j.value=!0;try{let e=(P.value-1)*50,t=await s.get(`/audit/`,{limit:50,offset:e,action:F.value||void 0,admin_username:I.value||void 0,date_from:L.value||void 0});M.value=w(t.items||[]),N.value=t.total||0}catch{M.value=[],A(`加载审计日志失败`,`error`)}finally{j.value=!1}}function V(e){return e.startsWith(`create`)||e===`login`?`success`:e.startsWith(`update`)?`info`:e.startsWith(`delete`)||e===`logout`?`error`:e.includes(`execute`)||e.includes(`push`)||e.includes(`retry`)?`warning`:`primary`}return p(()=>B()),(t,s)=>(u(),l(g,{fluid:``,class:`pa-6`},{default:r(()=>[c(o,{elevation:`0`,border:``,rounded:`lg`},{default:r(()=>[c(i,{class:`d-flex align-center`},{default:r(()=>[s[7]||=d(` 审计日志 `,-1),c(b),c(v,{modelValue:F.value,"onUpdate:modelValue":[s[0]||=e=>F.value=e,s[1]||=e=>{P.value=1,B()}],items:a(R),"item-title":`label`,"item-value":`value`,label:`操作类型`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`200px`},clearable:``},null,8,[`modelValue`,`items`]),c(m,{modelValue:I.value,"onUpdate:modelValue":[s[2]||=e=>I.value=e,s[3]||=e=>{P.value=1,B()}],"prepend-inner-icon":`mdi-account`,label:`用户`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`140px`},class:`ml-3`,clearable:``},null,8,[`modelValue`]),c(m,{modelValue:L.value,"onUpdate:modelValue":[s[4]||=e=>L.value=e,s[5]||=e=>{P.value=1,B()}],"prepend-inner-icon":`mdi-calendar`,label:`日期`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`160px`},class:`ml-3`,type:`date`,clearable:``},null,8,[`modelValue`])]),_:1}),c(y,{items:M.value,headers:z,"items-length":N.value,loading:j.value,page:P.value,"items-per-page":50,hover:``,density:`compact`,"onUpdate:page":s[6]||=e=>{P.value=e,B()}},{"item.action":r(({item:e})=>[c(_,{color:V(e.action),size:`small`,variant:`tonal`,label:``},{default:r(()=>[d(n(a(C)(e.action)),1)]),_:2},1032,[`color`])]),"item.admin_username":r(({item:t})=>[e(`span`,T,n(t.admin_username),1)]),"item.target":r(({item:t})=>[e(`span`,E,n(a(x)(t.resource_type))+n(t.resource_id?` #${t.resource_id}`:``),1)]),"item.detail":r(({item:t})=>[e(`span`,D,n(t.detail||`—`),1)]),"item.ip_address":r(({item:t})=>[e(`span`,O,n(t.ip_address||`—`),1)]),"item.created_at":r(({item:t})=>[e(`span`,k,n(t.created_at),1)]),"no-data":r(()=>[...s[8]||=[e(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`items-length`,`loading`,`page`])]),_:1})]),_:1}))}});export{A as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{$n as e,Ar as t,Br as n,Cr as r,F as i,Ir as a,M as o,N as s,Rt as c,ar as l,er as u,hr as d,ir as f,or as p,pr as m}from"./index-C_dP75wD.js";import{o as h,t as g}from"./VContainer-DGQjsCTw.js";import{t as _}from"./VChip-oig-b64O.js";import{t as v}from"./VSelect-V_uWG8_q.js";import{t as y}from"./VDataTableServer-BMEL-4WM.js";import{t as b}from"./VSpacer-Cl6Zcy_X.js";import{t as x}from"./apiError-BgZlS-MZ.js";import{t as S}from"./useServerList-DS-TV_aM.js";var C={class:`text-body-2`},w={class:`text-medium-emphasis`},T={class:`text-medium-emphasis`},E={class:`text-caption text-medium-emphasis`},D={class:`font-weight-medium`},O={class:`text-caption text-medium-emphasis`},k=p({__name:`CommandsPage`,setup(p){let k=h(),{servers:A,loadServers:j}=S(),M=t(!1),N=t(`commands`),P=t(1),F=t(0),I=t(30),L=t(null),R=t([]),z=t([]),B=[{title:`命令`,key:`command`},{title:`服务器`,key:`server_name`},{title:`用户`,key:`admin_username`,width:100},{title:`时间`,key:`created_at`,width:160}],V=[{title:`服务器`,key:`server_name`},{title:`状态`,key:`status`,width:80},{title:`来源`,key:`remote_addr`,width:120},{title:`开始时间`,key:`started_at`,width:160}];async function H(){M.value=!0;try{if(N.value===`commands`){let e=await c.getList(`/assets/command-logs`,{server_id:L.value||void 0,page:P.value,per_page:I.value});R.value=e.items,F.value=e.total}else{let e=await c.getList(`/assets/ssh-sessions`,{server_id:L.value||void 0,page:P.value,per_page:I.value});z.value=e.items,F.value=e.total}}catch(e){R.value=[],z.value=[],k(x(e,`加载命令日志失败`),`error`)}finally{M.value=!1}}return m(()=>{j(),H()}),(t,c)=>(d(),u(g,{fluid:``,class:`pa-6`},{default:r(()=>[l(s,{elevation:`0`,border:``,rounded:`lg`},{default:r(()=>[l(i,{class:`d-flex align-center`},{default:r(()=>[c[4]||=f(` 命令日志 `,-1),l(b),l(v,{modelValue:N.value,"onUpdate:modelValue":[c[0]||=e=>N.value=e,H],items:[{title:`命令视图`,value:`commands`},{title:`会话视图`,value:`sessions`}],"item-title":`title`,"item-value":`value`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`160px`}},null,8,[`modelValue`]),l(v,{modelValue:L.value,"onUpdate:modelValue":[c[1]||=e=>L.value=e,H],items:a(A),"item-title":`name`,"item-value":`id`,label:`服务器`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`180px`},class:`ml-3`,clearable:``},null,8,[`modelValue`,`items`])]),_:1}),l(o),N.value===`commands`?(d(),u(y,{key:0,items:R.value,headers:B,"items-length":F.value,loading:M.value,page:P.value,"items-per-page":30,hover:``,density:`compact`,"onUpdate:page":c[2]||=e=>{P.value=e,H()}},{"item.command":r(({item:t})=>[e(`code`,C,n(t.command),1)]),"item.server_name":r(({item:t})=>[e(`span`,w,n(t.server_name||`—`),1)]),"item.admin_username":r(({item:t})=>[e(`span`,T,n(t.admin_username||`—`),1)]),"item.created_at":r(({item:t})=>[e(`span`,E,n(t.created_at),1)]),"no-data":r(()=>[...c[5]||=[e(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`items-length`,`loading`,`page`])):(d(),u(y,{key:1,items:z.value,headers:V,"items-length":F.value,loading:M.value,page:P.value,"items-per-page":30,hover:``,density:`compact`,"onUpdate:page":c[3]||=e=>{P.value=e,H()}},{"item.server_name":r(({item:t})=>[e(`span`,D,n(t.server_name||`—`),1)]),"item.status":r(({item:e})=>[l(_,{color:e.status===`closed`?`grey`:`success`,size:`small`,variant:`tonal`,label:``},{default:r(()=>[f(n(e.status),1)]),_:2},1032,[`color`])]),"item.started_at":r(({item:t})=>[e(`span`,O,n(t.started_at),1)]),"no-data":r(()=>[...c[6]||=[e(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`items-length`,`loading`,`page`]))]),_:1})]),_:1}))}});export{k as default};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
.dashboard-root[data-v-1a05ebac]{min-width:0;max-width:100%}.dashboard-root[data-v-1a05ebac] .v-table{overflow-x:auto}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
import{$n as e,Ar as t,Br as n,Cr as r,Ft as i,Ht as a,It as o,N as s,Pt as c,Qn as l,R as u,Rr as d,Rt as f,Vt as p,Yn as m,ar as h,c as g,cn as _,dn as v,er as y,fn as b,hr as x,ir as S,l as C,mr as w,nn as T,nr as E,or as D,pr as O,t as k,tr as A,v as j,xr as M,zr as N}from"./index-C_dP75wD.js";import{t as P}from"./VChip-oig-b64O.js";import{t as F}from"./_plugin-vue_export-helper-BDNMzG2s.js";import{t as I}from"./VAlert-CNuOH9h_.js";var L=b({...v(),...C()},`VForm`),R=_()({name:`VForm`,props:L(),emits:{"update:modelValue":e=>!0,submit:e=>!0},setup(n,{slots:r,emit:i}){let a=g(n),o=t();function s(e){e.preventDefault(),a.reset()}function c(e){let t=e,n=a.validate();t.then=n.then.bind(n),t.catch=n.catch.bind(n),t.finally=n.finally.bind(n),i(`submit`,t),t.defaultPrevented||n.then(({valid:e})=>{e&&o.value?.submit()}),t.preventDefault()}return T(()=>e(`form`,{ref:o,class:d([`v-form`,n.class]),style:N(n.style),novalidate:!0,onReset:s,onSubmit:c},[r.default?.({get errors(){return a.errors.value},get isDisabled(){return a.isDisabled.value},get isReadonly(){return a.isReadonly.value},get isValidating(){return a.isValidating.value},get isValid(){return a.isValid.value},get items(){return a.items.value},validate:a.validate,reset:a.reset,resetValidation:a.resetValidation})])),j(a,o)}}),z={class:`login-page`},B={class:`login-center`},V={key:2,class:`text-center mt-4`},H=F(D({__name:`LoginPage`,setup(d){let g=p(),_=a(),v=c(),b=t(``),C=t(``),T=t(``),D=t(!1),j=t(!1),F=t(``),L=t(!1),H=t(0),U=t(null),W=t([]),G=t(0),K=null,q=l(()=>W.value.length===0?``:W.value[G.value]);async function J(){try{let e=await f.get(`/settings/bing-wallpapers`);e?.urls?.length&&(W.value=e.urls,G.value=Math.floor(Math.random()*e.urls.length))}catch{}}function Y(){K&&clearInterval(K),!(W.value.length<=1)&&(K=setInterval(()=>{G.value=(G.value+1)%W.value.length},36e5))}O(async()=>{await J(),Y()}),w(()=>{K&&clearInterval(K),Z&&clearInterval(Z)});let X=t(Date.now()),Z=null,Q=l(()=>{if(!U.value)return 0;let e=new Date(U.value).getTime()-X.value;return Math.max(0,Math.ceil(e/6e4))});M(U,e=>{Z&&=(clearInterval(Z),null),e&&(Z=setInterval(()=>{X.value=Date.now()},3e4))});async function $(){if(!b.value||!C.value){F.value=`请输入用户名和密码`;return}j.value=!0,F.value=``;try{await v.login(b.value,C.value,L.value?T.value:void 0);let e=g.query.redirect||`/`,t=e.startsWith(`/`)&&!e.startsWith(`//`)?e:`/`;_.push(t)}catch(e){e instanceof o?(L.value=!0,F.value=e.message):e instanceof i?e.status===429?(U.value=new Date(Date.now()+900*1e3).toISOString(),F.value=`登录尝试过多,账户已锁定 15 分钟`):(H.value++,F.value=e.message):F.value=`网络错误,请重试`}finally{j.value=!1}}return(t,i)=>(x(),E(`div`,z,[e(`div`,{class:`wallpaper-bg`,style:N({backgroundImage:`url(${q.value})`})},null,4),i[6]||=e(`div`,{class:`wallpaper-overlay`},null,-1),e(`div`,B,[h(s,{elevation:`0`,rounded:`xl`,class:`login-card pa-10`,border:``},{default:r(()=>[F.value?(x(),y(I,{key:0,type:`error`,density:`compact`,class:`mb-4`,closable:``,"onClick:close":i[0]||=e=>F.value=``,rounded:`lg`,variant:`tonal`},{default:r(()=>[S(n(F.value),1)]),_:1})):A(``,!0),U.value?(x(),y(I,{key:1,type:`warning`,density:`compact`,class:`mb-4`,rounded:`lg`,variant:`tonal`},{default:r(()=>[S(` 账户已锁定,请 `+n(Q.value)+` 分钟后重试 `,1)]),_:1})):A(``,!0),h(R,{onSubmit:m($,[`prevent`])},{default:r(()=>[h(k,{modelValue:b.value,"onUpdate:modelValue":i[1]||=e=>b.value=e,label:`用户名`,variant:`outlined`,density:`comfortable`,"prepend-inner-icon":`mdi-account-outline`,disabled:j.value,class:`mb-4`,autocomplete:`username`,rounded:`lg`,"hide-details":`auto`},null,8,[`modelValue`,`disabled`]),h(k,{modelValue:C.value,"onUpdate:modelValue":i[2]||=e=>C.value=e,label:`密码`,variant:`outlined`,density:`comfortable`,"prepend-inner-icon":`mdi-lock-outline`,"append-inner-icon":D.value?`mdi-eye`:`mdi-eye-off`,type:D.value?`text`:`password`,"onClick:appendInner":i[3]||=e=>D.value=!D.value,disabled:j.value,class:`mb-4`,autocomplete:`current-password`,rounded:`lg`,"hide-details":`auto`},null,8,[`modelValue`,`append-inner-icon`,`type`,`disabled`]),L.value?(x(),y(k,{key:0,modelValue:T.value,"onUpdate:modelValue":i[4]||=e=>T.value=e,label:`TOTP 验证码`,variant:`outlined`,density:`comfortable`,"prepend-inner-icon":`mdi-shield-key-outline`,disabled:j.value,class:`mb-4`,autocomplete:`one-time-code`,rounded:`lg`,"hide-details":`auto`},null,8,[`modelValue`,`disabled`])):A(``,!0),h(u,{type:`submit`,color:`primary`,variant:`flat`,block:``,size:`large`,loading:j.value,disabled:!!U.value,rounded:`lg`,class:`mt-2`,height:`48`},{default:r(()=>[...i[5]||=[S(` 登录 `,-1)]]),_:1},8,[`loading`,`disabled`])]),_:1}),H.value>0&&!U.value?(x(),E(`div`,V,[h(P,{size:`small`,variant:`tonal`,color:`warning`,label:``},{default:r(()=>[S(` 已失败 `+n(H.value)+` 次 `,1)]),_:1})])):A(``,!0)]),_:1})])]))}}),[[`__scopeId`,`data-v-c0c6156f`]]);export{H as default};
|
||||
@@ -0,0 +1 @@
|
||||
.login-page[data-v-c0c6156f]{width:100%;min-height:100vh;position:relative;overflow:hidden}.wallpaper-bg[data-v-c0c6156f]{z-index:0;background-position:50%;background-repeat:no-repeat;background-size:cover;transition:background-image 1.5s ease-in-out;position:fixed;inset:0}.wallpaper-overlay[data-v-c0c6156f]{z-index:1;background:linear-gradient(135deg,#00000073 0%,#0003 100%);position:fixed;inset:0}.login-center[data-v-c0c6156f]{z-index:2;justify-content:center;align-items:center;min-height:100vh;padding:24px;display:flex;position:relative}.login-card[data-v-c0c6156f]{-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);width:100%;max-width:420px;background:rgba(var(--v-theme-surface), .92)!important}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
import{$n as e,Ar as t,Br as n,Cr as r,F as i,I as a,N as o,P as s,R as c,Rt as l,ar as u,er as d,hr as f,ir as p,or as m,pr as h,tr as g}from"./index-C_dP75wD.js";import{o as _,t as v}from"./VContainer-DGQjsCTw.js";import{t as y}from"./VChip-oig-b64O.js";import{t as b}from"./VSelect-V_uWG8_q.js";import{t as x}from"./VDataTableServer-BMEL-4WM.js";import{t as S}from"./VSpacer-Cl6Zcy_X.js";import{t as C}from"./VDialog-CnSngTCg.js";var w={class:`font-weight-medium text-truncate d-inline-block`,style:{"max-width":`240px`}},T={class:`text-medium-emphasis`},E={class:`text-medium-emphasis`},D={class:`text-caption text-medium-emphasis`},O={class:`text-caption text-medium-emphasis`},k=m({__name:`RetriesPage`,setup(m){let k=_(),A=t(!1),j=t([]),M=t(0),N=t(1),P=t(null),F=t(!1),I=t(null),L=[{label:`待重试`,value:`pending`},{label:`重试中`,value:`retrying`},{label:`成功`,value:`success`},{label:`失败`,value:`failed`}],R=[{title:`状态`,key:`status`,width:100},{title:`任务`,key:`operation`},{title:`服务器`,key:`server_name`},{title:`次数`,key:`retry_count`,width:100},{title:`下次重试`,key:`next_retry_at`,width:140},{title:`创建时间`,key:`created_at`,width:160},{title:``,key:`actions`,width:120,align:`end`,sortable:!1}];async function z(){A.value=!0;try{let e=(await l.getList(`/retries/`,{limit:200})).items||[];P.value&&(e=e.filter(e=>e.status===P.value)),M.value=e.length;let t=(N.value-1)*20;j.value=e.slice(t,t+20)}catch(e){j.value=[],M.value=0,k(e instanceof Error?e.message:`加载重试队列失败`,`error`)}finally{A.value=!1}}async function B(e){try{await l.post(`/retries/${e.id}/retry`),z(),k(`重试已触发`)}catch(e){k(e.message||`操作失败`,`error`)}}function V(e){I.value=e.id,F.value=!0}async function H(){if(I.value)try{await l.delete(`/retries/${I.value}`),F.value=!1,z(),k(`已删除`)}catch(e){k(e.message||`删除失败`,`error`)}}function U(e){return e===`success`?`success`:e===`failed`?`error`:e===`retrying`?`warning`:`info`}function W(e){return e===`pending`?`待重试`:e===`retrying`?`重试中`:e===`success`?`成功`:e===`failed`?`失败`:e}function G(e){return e.source_path&&e.target_path?`${e.source_path} → ${e.target_path}`:e.source_path||e.target_path||`文件推送`}return h(()=>z()),(t,l)=>(f(),d(v,{fluid:``,class:`pa-6`},{default:r(()=>[u(o,{elevation:`0`,border:``,rounded:`lg`},{default:r(()=>[u(i,{class:`d-flex align-center text-h6`},{default:r(()=>[l[5]||=p(` 重试队列 `,-1),u(S),u(b,{modelValue:P.value,"onUpdate:modelValue":[l[0]||=e=>P.value=e,l[1]||=e=>{N.value=1,z()}],items:L,"item-title":`label`,"item-value":`value`,label:`状态筛选`,variant:`outlined`,density:`compact`,"hide-details":``,style:{"max-width":`160px`},clearable:``},null,8,[`modelValue`])]),_:1}),u(x,{items:j.value,headers:R,"items-length":M.value,loading:A.value,page:N.value,"items-per-page":20,hover:``,density:`comfortable`,"onUpdate:page":l[2]||=e=>{N.value=e,z()}},{"item.status":r(({item:e})=>[u(y,{color:U(e.status),size:`x-small`,variant:`tonal`,label:``,border:`sm`},{default:r(()=>[p(n(W(e.status)),1)]),_:2},1032,[`color`])]),"item.operation":r(({item:t})=>[e(`span`,w,n(G(t)),1)]),"item.server_name":r(({item:t})=>[e(`span`,T,n(t.server_name||`—`),1)]),"item.retry_count":r(({item:t})=>[e(`span`,E,n(t.retry_count||0)+` / `+n(t.max_retries||3),1)]),"item.next_retry_at":r(({item:t})=>[e(`span`,D,n(t.next_retry_at||`—`),1)]),"item.created_at":r(({item:t})=>[e(`span`,O,n(t.created_at),1)]),"item.actions":r(({item:e})=>[e.status===`pending`||e.status===`failed`?(f(),d(c,{key:0,variant:`text`,size:`x-small`,color:`primary`,density:`compact`,onClick:t=>B(e)},{default:r(()=>[...l[6]||=[p(` 重试 `,-1)]]),_:1},8,[`onClick`])):g(``,!0),u(c,{variant:`text`,size:`x-small`,color:`error`,density:`compact`,onClick:t=>V(e)},{default:r(()=>[...l[7]||=[p(` 删除 `,-1)]]),_:1},8,[`onClick`])]),"no-data":r(()=>[...l[8]||=[e(`div`,{class:`text-center text-medium-emphasis py-6`},`暂无数据`,-1)]]),_:1},8,[`items`,`items-length`,`loading`,`page`])]),_:1}),u(C,{modelValue:F.value,"onUpdate:modelValue":l[4]||=e=>F.value=e,"max-width":`400`},{default:r(()=>[u(o,{border:``},{default:r(()=>[u(i,null,{default:r(()=>[...l[9]||=[p(`确认删除`,-1)]]),_:1}),u(s,null,{default:r(()=>[...l[10]||=[p(`确定要删除此重试任务吗?`,-1)]]),_:1}),u(a,null,{default:r(()=>[u(S),u(c,{variant:`text`,onClick:l[3]||=e=>F.value=!1},{default:r(()=>[...l[11]||=[p(`取消`,-1)]]),_:1}),u(c,{color:`error`,variant:`flat`,onClick:H},{default:r(()=>[...l[12]||=[p(`删除`,-1)]]),_:1})]),_:1})]),_:1})]),_:1},8,[`modelValue`])]),_:1}))}});export{k as default};
|
||||
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
@@ -0,0 +1 @@
|
||||
import{$n as e,Ar as t,Br as n,Cr as r,L as i,N as a,P as o,Rr as s,Rt as c,X as l,Xn as u,_r as d,ar as f,er as p,hr as m,mr as h,nr as g,or as _}from"./index-C_dP75wD.js";import{n as v,t as y}from"./VRow-MZyk7N4E.js";import{t as b}from"./_plugin-vue_export-helper-BDNMzG2s.js";function x(){let e=t([]),n=t(!1),r=t(1),i=t(20),a=t(0),o=t(``);async function s(){n.value=!0;try{let t=await c.get(`/servers/`,{page:r.value,per_page:i.value,search:o.value||void 0});e.value=t.items||[],a.value=t.total||0}catch(t){throw e.value=[],a.value=0,t}finally{n.value=!1}}let l;h(()=>clearTimeout(l));function u(){clearTimeout(l),l=setTimeout(()=>{r.value=1,s()},300)}function d(e){r.value=e,s()}function f(e){i.value=e,r.value=1,s()}return{servers:e,loading:n,page:r,itemsPerPage:i,total:a,search:o,loadServers:s,onSearch:u,onPageChange:d,onItemsPerPageChange:f}}function S(e){switch(e){case`online`:return`success`;case`offline`:return`error`;default:return`warning`}}function C(e){switch(e){case`online`:return`在线`;case`offline`:return`离线`;default:return`未知`}}function w(e){if(!e)return`—`;let t=new Date(e);if(isNaN(t.getTime()))return`—`;let n=Date.now()-t.getTime();return n<6e4?`刚刚`:n<36e5?`${Math.floor(n/6e4)} 分钟前`:n<864e5?`${Math.floor(n/36e5)} 小时前`:`${Math.floor(n/864e5)} 天前`}var T={class:`text-caption text-medium-emphasis mb-1`,style:{"font-size":`14px`,"letter-spacing":`0.5px`,"text-transform":`uppercase`}},E=b(_({__name:`StatCardsRow`,props:{items:{}},setup(t){return(c,h)=>(m(),p(y,null,{default:r(()=>[(m(!0),g(u,null,d(t.items,(t,c)=>(m(),p(v,{key:c,cols:`6`,sm:`3`},{default:r(()=>[f(a,{elevation:`0`,rounded:`xl`,border:``,class:`stat-card`},{default:r(()=>[f(o,{class:`pa-4 d-flex align-center justify-space-between`},{default:r(()=>[e(`div`,null,[e(`div`,T,n(t.subtitle),1),e(`div`,{class:s([`font-weight-bold`,`text-`+t.color]),style:{"font-size":`36px`,"line-height":`1.1`}},n(t.title),3)]),f(i,{color:t.color,size:`48`,rounded:`lg`,class:`stat-icon`},{default:r(()=>[f(l,{icon:t.icon,size:`24`,color:`white`},null,8,[`icon`])]),_:2},1032,[`color`])]),_:2},1024)]),_:2},1024)]),_:2},1024))),128))]),_:1}))}}),[[`__scopeId`,`data-v-68b984da`]]);export{x as a,C as i,w as n,S as r,E as t};
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
import{$n as e,$t as t,Ct as n,Gt as r,Nr as i,Qn as a,R as o,Rr as s,St as c,Tt as l,U as u,W as d,Wt as f,X as p,Zt as m,ar as h,at as g,bt as _,cn as v,ct as y,dn as b,dt as x,fn as S,ft as C,it as w,jt as T,lr as E,on as D,ot as O,pt as k,st as A,tn as j,ut as M,wt as N,yt as P,zr as F}from"./index-C_dP75wD.js";var I=D(`v-alert-title`),L=S({iconSize:[Number,String],iconSizes:{type:Array,default:()=>[[`x-small`,10],[`small`,16],[`default`,24],[`large`,28],[`x-large`,32]]}},`iconSize`);function R(e,t){return{iconSize:a(()=>{let n=new Map(e.iconSizes),r=e.iconSize??t()??`default`;return n.has(r)?n.get(r):r})}}var z=[`success`,`info`,`warning`,`error`],B=S({border:{type:[Boolean,String],validator:e=>typeof e==`boolean`||[`top`,`end`,`bottom`,`start`].includes(e)},borderColor:String,closable:Boolean,closeIcon:{type:j,default:`$close`},closeLabel:{type:String,default:`$vuetify.close`},icon:{type:[Boolean,String,Function,Object],default:null},modelValue:{type:Boolean,default:!0},prominent:Boolean,title:String,text:String,type:{type:String,validator:e=>z.includes(e)},...b(),...A(),...n(),...C(),...L(),...M(),...u(),...P(),...T(),...f(),...g({variant:`flat`})},`VAlert`),V=v()({name:`VAlert`,props:B(),emits:{"click:close":e=>!0,"update:modelValue":e=>!0},setup(n,{emit:a,slots:u}){let f=t(n,`modelValue`),g=i(()=>{if(n.icon!==!1)return n.type?n.icon??`$${n.type}`:n.icon}),{iconSize:v}=R(n,()=>n.prominent?44:void 0),{themeClasses:b}=r(n),{colorClasses:S,colorStyles:C,variantClasses:T}=O(()=>({color:n.color??n.type,variant:n.variant})),{densityClasses:D}=y(n),{dimensionStyles:A}=N(n),{elevationClasses:j}=k(n),{locationStyles:M}=x(n),{positionClasses:P}=d(n),{roundedClasses:L}=_(n),{textColorClasses:z,textColorStyles:B}=c(()=>n.borderColor),{t:V}=m(),H=i(()=>({"aria-label":V(n.closeLabel),onClick(e){f.value=!1,a(`click:close`,e)}}));return()=>{let t=!!(u.prepend||g.value),r=!!(u.title||n.title),i=!!(u.close||n.closable),a={density:n.density,icon:g.value,size:n.iconSize||n.prominent?v.value:void 0};return f.value&&h(n.tag,{class:s([`v-alert`,n.border&&{"v-alert--border":!!n.border,[`v-alert--border-${n.border===!0?`start`:n.border}`]:!0},{"v-alert--prominent":n.prominent},b.value,S.value,D.value,j.value,P.value,L.value,T.value,n.class]),style:F([C.value,A.value,M.value,n.style]),role:`alert`},{default:()=>[w(!1,`v-alert`),n.border&&e(`div`,{key:`border`,class:s([`v-alert__border`,z.value]),style:F(B.value)},null),t&&e(`div`,{key:`prepend`,class:`v-alert__prepend`},[u.prepend?h(l,{key:`prepend-defaults`,disabled:!g.value,defaults:{VIcon:{...a}}},u.prepend):h(p,E({key:`prepend-icon`},a),null)]),e(`div`,{class:`v-alert__content`},[r&&h(I,{key:`title`},{default:()=>[u.title?.()??n.title]}),u.text?.()??n.text,u.default?.()]),u.append&&e(`div`,{key:`append`,class:`v-alert__append`},[u.append()]),i&&e(`div`,{key:`close`,class:`v-alert__close`},[u.close?h(l,{key:`close-defaults`,defaults:{VBtn:{icon:n.closeIcon,size:`x-small`,variant:`text`}}},{default:()=>[u.close?.({props:H.value})]}):h(o,E({key:`close-btn`,icon:n.closeIcon,size:`x-small`,variant:`text`},H.value),null)])]})}}});export{V as t};
|
||||
@@ -0,0 +1 @@
|
||||
import{$t as e,Ar as t,Rn as n,ar as r,br as i,cn as a,fn as o,lr as s,nn as c,o as l,p as u,s as d,v as f,wn as p}from"./index-C_dP75wD.js";import{f as m,p as h}from"./VSelect-V_uWG8_q.js";var g=o({...n(d(),[`direction`]),...n(h(),[`inline`])},`VCheckbox`),_=a()({name:`VCheckbox`,inheritAttrs:!1,props:g(),emits:{"update:modelValue":e=>!0,"update:focused":e=>!0},setup(n,{attrs:a,slots:o}){let d=e(n,`modelValue`),{isFocused:h,focus:g,blur:_}=u(n),v=t(),y=i();return c(()=>{let[e,t]=p(a),i=l.filterProps(n),c=m.filterProps(n);return r(l,s({ref:v,class:[`v-checkbox`,n.class]},e,i,{modelValue:d.value,"onUpdate:modelValue":e=>d.value=e,id:n.id||`checkbox-${y}`,focused:h.value,style:n.style}),{...o,default:({id:e,messagesId:n,isDisabled:i,isReadonly:a,isValid:l})=>r(m,s(c,{id:e.value,"aria-describedby":n.value,disabled:i.value,readonly:a.value},t,{error:l.value===!1,modelValue:d.value,"onUpdate:modelValue":e=>d.value=e,onFocus:g,onBlur:_}),o)})}),f({},v)}});export{_ as t};
|
||||
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
@@ -0,0 +1 @@
|
||||
import{_ as e,a as t,b as n,c as r,d as i,f as a,g as o,h as s,i as c,l,m as u,n as d,o as f,p,r as m,s as h,u as ee,v as te,y as g}from"./VDataTable-RUszusjq.js";import{$n as _,M as v,Nr as y,Pr as b,Qn as x,Rn as S,Xn as C,ar as w,cn as T,fn as E,gr as ne,ln as D,lr as O,nn as k}from"./index-C_dP75wD.js";var A=E({itemsLength:{type:[Number,String],required:!0},...g(),...d(),...e()},`VDataTableServer`),j=T()({name:`VDataTableServer`,props:A(),emits:{"update:modelValue":e=>!0,"update:page":e=>!0,"update:itemsPerPage":e=>!0,"update:sortBy":e=>!0,"update:options":e=>!0,"update:expanded":e=>!0,"update:groupBy":e=>!0},setup(e,{attrs:d,slots:g}){let{groupBy:T}=r(e),{initialSortOrder:E,sortBy:A,multiSort:j,mustSort:M}=a(e),{page:N,itemsPerPage:P}=te(e),{disableSort:F}=b(e),I=x(()=>parseInt(e.itemsLength,10)),{columns:L,headers:R}=s(e,{groupBy:T,showSelect:y(()=>e.showSelect),showExpand:y(()=>e.showExpand)}),{items:z}=c(e,L),{toggleSort:B}=p({initialSortOrder:E,sortBy:A,multiSort:j,mustSort:M,page:N}),{opened:V,isGroupOpen:H,toggleGroup:re,extractRows:U}=l({groupBy:T,sortBy:A,disableSort:F}),{pageCount:W,setItemsPerPage:G,prevPage:K,nextPage:q,setPage:J}=n({page:N,itemsPerPage:P,itemsLength:I}),{flatItems:Y}=ee(z,T,V,()=>!!g[`group-summary`]),{isSelected:X,select:ie,selectAll:ae,toggleSelect:oe,someSelected:se,allSelected:ce}=u(e,{allItems:z,currentPage:z}),{isExpanded:Z,toggleExpand:le}=h(e),Q=x(()=>U(z.value));m({page:N,itemsPerPage:P,sortBy:A,groupBy:T,search:y(()=>e.search)}),ne(`v-data-table`,{toggleSort:B,sortBy:A}),D({VDataTableRows:{hideNoData:y(()=>e.hideNoData),noDataText:y(()=>e.noDataText),loading:y(()=>e.loading),loadingText:y(()=>e.loadingText)}});let $=x(()=>({page:N.value,itemsPerPage:P.value,sortBy:A.value,pageCount:W.value,toggleSort:B,setItemsPerPage:G,prevPage:K,nextPage:q,setPage:J,someSelected:se.value,allSelected:ce.value,isSelected:X,select:ie,selectAll:ae,toggleSelect:oe,isExpanded:Z,toggleExpand:le,isGroupOpen:H,toggleGroup:re,items:Q.value.map(e=>e.raw),internalItems:Q.value,groupedItems:Y.value,columns:L.value,headers:R.value}));k(()=>{let n=o.filterProps(e),r=i.filterProps(S(e,[`multiSort`])),a=f.filterProps(e),s=t.filterProps(e);return w(t,O({class:[`v-data-table`,{"v-data-table--loading":e.loading},e.class],style:e.style},s,{fixedHeader:e.fixedHeader||e.sticky}),{top:()=>g.top?.($.value),default:()=>g.default?g.default($.value):_(C,null,[g.colgroup?.($.value),!e.hideDefaultHeader&&_(`thead`,{key:`thead`,class:`v-data-table__thead`,role:`rowgroup`},[w(i,O(r,{multiSort:!!e.multiSort}),g)]),g.thead?.($.value),!e.hideDefaultBody&&_(`tbody`,{class:`v-data-table__tbody`,role:`rowgroup`},[g[`body.prepend`]?.($.value),g.body?g.body($.value):w(f,O(d,a,{items:Y.value}),g),g[`body.append`]?.($.value)]),g.tbody?.($.value),g.tfoot?.($.value)]),bottom:()=>g.bottom?g.bottom($.value):!e.hideDefaultFooter&&_(C,null,[w(v,null,null),w(o,n,{prepend:g[`footer.prepend`]})])})})}});export{j as t};
|
||||
@@ -0,0 +1 @@
|
||||
import{$t as e,Ar as t,At as n,Rn as r,Tt as i,ar as a,b as o,cn as s,fn as c,lr as l,nn as u,ur as d,v as f,x as p,xr as m,y as h}from"./index-C_dP75wD.js";var g=c({fullscreen:Boolean,scrollable:Boolean,...r(o({captureFocus:!0,origin:`center center`,scrollStrategy:`block`,transition:{component:n},zIndex:2400,retainFocus:!0}),[`disableInitialFocus`])},`VDialog`),_=s()({name:`VDialog`,props:g(),emits:{"update:modelValue":e=>!0,afterEnter:()=>!0,afterLeave:()=>!0},setup(n,{emit:r,slots:o}){let s=e(n,`modelValue`),{scopeId:c}=p(),g=t();function _(){r(`afterEnter`),(n.scrim||n.retainFocus)&&g.value?.contentEl&&!g.value.contentEl.contains(document.activeElement)&&g.value.contentEl.focus({preventScroll:!0})}function v(){r(`afterLeave`)}return m(s,async e=>{e||(await d(),g.value.activatorEl?.focus({preventScroll:!0}))}),u(()=>{let e=h.filterProps(n),t=l({"aria-haspopup":`dialog`},n.activatorProps),r=l({tabindex:-1},n.contentProps);return a(h,l({ref:g,class:[`v-dialog`,{"v-dialog--fullscreen":n.fullscreen,"v-dialog--scrollable":n.scrollable},n.class],style:n.style},e,{modelValue:s.value,"onUpdate:modelValue":e=>s.value=e,"aria-modal":`true`,activatorProps:t,contentProps:r,height:n.fullscreen?void 0:n.height,width:n.fullscreen?void 0:n.width,maxHeight:n.fullscreen?void 0:n.maxHeight,maxWidth:n.fullscreen?void 0:n.maxWidth,role:`dialog`,onAfterEnter:_,onAfterLeave:v},c),{activator:o.activator,default:(...e)=>a(i,{root:`VDialog`},{default:()=>[o.default?.(...e)]})})}),f({},g)}});export{_ as t};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
import{Fn as e,Gn as t,Jt as n,Lr as r,Qn as i,cn as a,dn as o,fn as s,jt as c,sr as l,st as u,yn as d}from"./index-C_dP75wD.js";var f=n.reduce((e,t)=>(e[t]={type:[Boolean,String,Number],default:!1},e),{}),p=n.reduce((e,t)=>{let n=`offset`+r(t);return e[n]={type:[String,Number],default:null},e},{}),m={col:e(f),offset:e(p),order:[`order`,`orderSm`,`orderMd`,`orderLg`,`orderXl`,`orderXxl`]};function h(e){if(typeof e==`string`&&e.includes(`/`)){let[t,n]=e.split(`/`);return{cols:Number(t),size:Number(n)}}return{cols:e}}function g(e,t,n){if(n==null||n===!1)return{};let{cols:r,size:i}=h(n),a=t.replace(e,``).toLowerCase();return e===`offset`?{className:`v-col--offset-${a}-${r}`,variables:[{[`--v-col-offset-base-${a}`]:i}]}:e===`order`?{className:`order-${a}-${r}`}:{className:r===``||r===!0?`v-col--${a}`:`v-col--cols-${a}-${r}`,variables:[{[`--v-col-size-base-${a}`]:i}]}}var _=[`auto`,`start`,`end`,`center`,`baseline`,`stretch`],v=e=>_.includes(e),y=s({cols:{type:[Boolean,String,Number],default:!1},...f,offset:{type:[String,Number],default:null},...p,order:{type:[String,Number],default:null},orderSm:{type:[String,Number],default:null},orderMd:{type:[String,Number],default:null},orderLg:{type:[String,Number],default:null},orderXl:{type:[String,Number],default:null},orderXxl:{type:[String,Number],default:null},alignSelf:{type:String,default:null,validator:v},...o(),...c()},`VCol`),b=a()({name:`VCol`,props:y(),setup(e,{slots:t}){let n=i(()=>h(e.cols).size),r=i(()=>h(e.offset).size),a=i(()=>{let t=[`v-col`],n=[],r;for(r in m)m[r].forEach(i=>{let a=e[i],{className:o,variables:s}=g(r,i,a);o&&t.push(o),s&&n.push(...s)});let{cols:i}=h(e.cols),{cols:a}=h(e.offset);return t.push({[`v-col--cols-${i}`]:i,[`v-col--offset-${a}`]:a,[`order-${e.order}`]:e.order,[`align-self-${e.alignSelf}`]:e.alignSelf}),{classes:t,variables:n}});return()=>l(e.tag,{class:[a.value.classes,e.class],style:[{"--v-col-size-base":n.value},{"--v-col-offset-base":r.value},a.value.variables,e.style]},t.default?.())}}),x=[`start`,`end`,`center`],S=[`space-between`,`space-around`,`space-evenly`],C=[...x,`baseline`,`stretch`],w=e=>C.includes(e),T=[...x,...S],E=e=>T.includes(e),D=[...x,...S,`stretch`],O=e=>D.includes(e),k={align:[`align`,`alignSm`,`alignMd`,`alignLg`,`alignXl`,`alignXxl`],justify:[`justify`,`justifySm`,`justifyMd`,`justifyLg`,`justifyXl`,`justifyXxl`],alignContent:[`alignContent`,`alignContentSm`,`alignContentMd`,`alignContentLg`,`alignContentXl`,`alignContentXxl`]},A={align:`align`,justify:`justify`,alignContent:`align-content`};function j(e,t,n){let r=A[e];if(n!=null){if(t){let n=t.replace(e,``);r+=`-${n}`}return r+=`-${n}`,r.toLowerCase()}}var M=s({dense:Boolean,align:{type:String,default:null,validator:w},alignSm:{type:String,default:null,validator:w},alignMd:{type:String,default:null,validator:w},alignLg:{type:String,default:null,validator:w},alignXl:{type:String,default:null,validator:w},alignXxl:{type:String,default:null,validator:w},justify:{type:String,default:null,validator:E},justifySm:{type:String,default:null,validator:E},justifyMd:{type:String,default:null,validator:E},justifyLg:{type:String,default:null,validator:E},justifyXl:{type:String,default:null,validator:E},justifyXxl:{type:String,default:null,validator:E},alignContent:{type:String,default:null,validator:O},alignContentSm:{type:String,default:null,validator:O},alignContentMd:{type:String,default:null,validator:O},alignContentLg:{type:String,default:null,validator:O},alignContentXl:{type:String,default:null,validator:O},alignContentXxl:{type:String,default:null,validator:O},noGutters:Boolean,gap:[Number,String,Array],size:[Number,String],...o(),...u(),...c()},`VRow`),N=a()({name:`VRow`,props:M(),setup(e,{slots:n}){e.dense&&t(`dense`,`density="comfortable"`);let r=i(()=>{let t=[],n;for(n in k)k[n].forEach(r=>{let i=e[r],a=j(n,r,i);a&&t.push(a)});return t.push({"v-row--no-gutters":e.noGutters,"v-row--density-default":e.density===`default`&&!e.noGutters&&!e.dense,"v-row--density-compact":e.density===`compact`,"v-row--density-comfortable":e.density===`comfortable`||e.dense,[`align-${e.align}`]:e.align,[`justify-${e.justify}`]:e.justify,[`align-content-${e.alignContent}`]:e.alignContent}),t}),a=i(()=>Array.isArray(e.gap)?d(e.gap[0]||0):d(e.gap)),o=i(()=>Array.isArray(e.gap)?d(e.gap[1]||0):a.value);return()=>l(e.tag,{class:[`v-row`,r.value,e.class],style:[{"--v-col-gap-x":a.value,"--v-col-gap-y":o.value,"--v-row-columns":e.size},e.style]},n.default?.())}});export{b as n,N as t};
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
import{on as e}from"./index-C_dP75wD.js";var t=e(`v-spacer`,`div`,`VSpacer`);export{t};
|
||||
@@ -0,0 +1 @@
|
||||
import{$n as e,$t as t,Ar as n,G as r,Nr as i,Rr as a,Tt as o,Un as s,X as c,Xn as l,Y as u,ar as d,br as f,cn as p,fn as m,kt as h,lr as g,nn as _,o as v,p as y,q as b,s as x,v as S,wn as C,zr as w}from"./index-C_dP75wD.js";import{n as T,r as E}from"./VContainer-DGQjsCTw.js";var D=m({indeterminate:Boolean,inset:Boolean,flat:Boolean,loading:{type:[Boolean,String],default:!1},...x(),...E()},`VSwitch`),O=p()({name:`VSwitch`,inheritAttrs:!1,props:D(),emits:{"update:focused":e=>!0,"update:modelValue":e=>!0,"update:indeterminate":e=>!0},setup(p,{attrs:m,slots:x}){let E=t(p,`indeterminate`),D=t(p,`modelValue`),{loaderClasses:O}=b(p),{isFocused:k,focus:A,blur:j}=y(p),M=n(),N=n(),P=s&&window.matchMedia(`(forced-colors: active)`).matches,F=i(()=>typeof p.loading==`string`&&p.loading!==``?p.loading:p.color),I=f(),L=i(()=>p.id||`switch-${I}`);function R(){E.value&&=!1}function z(e){e.stopPropagation(),e.preventDefault(),M.value?.input?.click()}return _(()=>{let[t,n]=C(m),i=v.filterProps(p),s=T.filterProps(p);return d(v,g({ref:N,class:[`v-switch`,{"v-switch--flat":p.flat},{"v-switch--inset":p.inset},{"v-switch--indeterminate":E.value},O.value,p.class]},t,i,{modelValue:D.value,"onUpdate:modelValue":e=>D.value=e,id:L.value,focused:k.value,style:p.style}),{...x,default:({id:t,messagesId:i,isDisabled:f,isReadonly:m,isValid:_})=>{let v={model:D,isValid:_};return d(T,g({ref:M},s,{modelValue:D.value,"onUpdate:modelValue":[e=>D.value=e,R],id:t.value,"aria-describedby":i.value,type:`checkbox`,"aria-checked":E.value?`mixed`:void 0,disabled:f.value,readonly:m.value,onFocus:A,onBlur:j},n),{...x,default:({backgroundColorClasses:t,backgroundColorStyles:n})=>e(`div`,{class:a([`v-switch__track`,P?void 0:t.value]),style:w(n.value),onClick:z},[x[`track-true`]&&e(`div`,{key:`prepend`,class:`v-switch__track-true`},[x[`track-true`](v)]),x[`track-false`]&&e(`div`,{key:`append`,class:`v-switch__track-false`},[x[`track-false`](v)])]),input:({inputNode:t,icon:n,backgroundColorClasses:i,backgroundColorStyles:s})=>e(l,null,[t,e(`div`,{class:a([`v-switch__thumb`,{"v-switch__thumb--filled":n||p.loading},p.inset||P?void 0:i.value]),style:w(p.inset?void 0:s.value)},[x.thumb?d(o,{defaults:{VIcon:{icon:n,size:`x-small`}}},{default:()=>[x.thumb({...v,icon:n})]}):d(h,null,{default:()=>[p.loading?d(r,{name:`v-switch`,active:!0,color:_.value===!1?void 0:F.value},{default:e=>x.loader?x.loader(e):d(u,{active:e.isActive,color:e.color,indeterminate:!0,size:`16`,width:`2`},null)}):n&&d(c,{key:String(n),icon:n,size:`x-small`},null)]})])])})}})}),S({},N)}});export{O as t};
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
import{Ft as e}from"./index-C_dP75wD.js";function t(t,n){if(t instanceof e){let e=t.detail??t.message;return typeof e==`string`?e:Array.isArray(e)?e.map(e=>e?.msg||JSON.stringify(e)).join(`; `):e&&typeof e==`object`?JSON.stringify(e):t.message||n}return t instanceof Error&&t.message||n}export{t};
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
import{n as e}from"./FilesPage-DHKaHWZF.js";export{e as initMonaco};
|
||||
@@ -0,0 +1,2 @@
|
||||
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/tsMode-8TpA7NJ9.js","assets/editor.api2-CLbMfXfG.js","assets/editor-DX2BVZF3.css","assets/workers-DM14Vrt9.js"])))=>i.map(i=>d[i]);
|
||||
import{g as e,h as t,n}from"./editor.api2-CLbMfXfG.js";import{Nt as r}from"./index-C_dP75wD.js";var i=`5.9.3`,a=e({JsxEmit:()=>s,ModuleKind:()=>o,ModuleResolutionKind:()=>u,NewLineKind:()=>c,ScriptTarget:()=>l,getJavaScriptWorker:()=>_,getTypeScriptWorker:()=>g,javascriptDefaults:()=>h,typescriptDefaults:()=>m,typescriptVersion:()=>f}),o=(e=>(e[e.None=0]=`None`,e[e.CommonJS=1]=`CommonJS`,e[e.AMD=2]=`AMD`,e[e.UMD=3]=`UMD`,e[e.System=4]=`System`,e[e.ES2015=5]=`ES2015`,e[e.ESNext=99]=`ESNext`,e))(o||{}),s=(e=>(e[e.None=0]=`None`,e[e.Preserve=1]=`Preserve`,e[e.React=2]=`React`,e[e.ReactNative=3]=`ReactNative`,e[e.ReactJSX=4]=`ReactJSX`,e[e.ReactJSXDev=5]=`ReactJSXDev`,e))(s||{}),c=(e=>(e[e.CarriageReturnLineFeed=0]=`CarriageReturnLineFeed`,e[e.LineFeed=1]=`LineFeed`,e))(c||{}),l=(e=>(e[e.ES3=0]=`ES3`,e[e.ES5=1]=`ES5`,e[e.ES2015=2]=`ES2015`,e[e.ES2016=3]=`ES2016`,e[e.ES2017=4]=`ES2017`,e[e.ES2018=5]=`ES2018`,e[e.ES2019=6]=`ES2019`,e[e.ES2020=7]=`ES2020`,e[e.ESNext=99]=`ESNext`,e[e.JSON=100]=`JSON`,e[e.Latest=99]=`Latest`,e))(l||{}),u=(e=>(e[e.Classic=1]=`Classic`,e[e.NodeJs=2]=`NodeJs`,e))(u||{}),d=class{constructor(e,t,r,i,a){this._onDidChange=new n,this._onDidExtraLibsChange=new n,this._extraLibs=Object.create(null),this._removedExtraLibs=Object.create(null),this._eagerModelSync=!1,this.setCompilerOptions(e),this.setDiagnosticsOptions(t),this.setWorkerOptions(r),this.setInlayHintsOptions(i),this.setModeConfiguration(a),this._onDidExtraLibsChangeTimeout=-1}get onDidChange(){return this._onDidChange.event}get onDidExtraLibsChange(){return this._onDidExtraLibsChange.event}get modeConfiguration(){return this._modeConfiguration}get workerOptions(){return this._workerOptions}get inlayHintsOptions(){return this._inlayHintsOptions}getExtraLibs(){return this._extraLibs}addExtraLib(e,t){let n;if(n=t===void 0?`ts:extralib-${Math.random().toString(36).substring(2,15)}`:t,this._extraLibs[n]&&this._extraLibs[n].content===e)return{dispose:()=>{}};let r=1;return this._removedExtraLibs[n]&&(r=this._removedExtraLibs[n]+1),this._extraLibs[n]&&(r=this._extraLibs[n].version+1),this._extraLibs[n]={content:e,version:r},this._fireOnDidExtraLibsChangeSoon(),{dispose:()=>{let e=this._extraLibs[n];e&&e.version===r&&(delete this._extraLibs[n],this._removedExtraLibs[n]=r,this._fireOnDidExtraLibsChangeSoon())}}}setExtraLibs(e){for(let e in this._extraLibs)this._removedExtraLibs[e]=this._extraLibs[e].version;if(this._extraLibs=Object.create(null),e&&e.length>0)for(let t of e){let e=t.filePath||`ts:extralib-${Math.random().toString(36).substring(2,15)}`,n=t.content,r=1;this._removedExtraLibs[e]&&(r=this._removedExtraLibs[e]+1),this._extraLibs[e]={content:n,version:r}}this._fireOnDidExtraLibsChangeSoon()}_fireOnDidExtraLibsChangeSoon(){this._onDidExtraLibsChangeTimeout===-1&&(this._onDidExtraLibsChangeTimeout=window.setTimeout(()=>{this._onDidExtraLibsChangeTimeout=-1,this._onDidExtraLibsChange.fire(void 0)},0))}getCompilerOptions(){return this._compilerOptions}setCompilerOptions(e){this._compilerOptions=e||Object.create(null),this._onDidChange.fire(void 0)}getDiagnosticsOptions(){return this._diagnosticsOptions}setDiagnosticsOptions(e){this._diagnosticsOptions=e||Object.create(null),this._onDidChange.fire(void 0)}setWorkerOptions(e){this._workerOptions=e||Object.create(null),this._onDidChange.fire(void 0)}setInlayHintsOptions(e){this._inlayHintsOptions=e||Object.create(null),this._onDidChange.fire(void 0)}setMaximumWorkerIdleTime(e){}setEagerModelSync(e){this._eagerModelSync=e}getEagerModelSync(){return this._eagerModelSync}setModeConfiguration(e){this._modeConfiguration=e||Object.create(null),this._onDidChange.fire(void 0)}},f=i,p={completionItems:!0,hovers:!0,documentSymbols:!0,definitions:!0,references:!0,documentHighlights:!0,rename:!0,diagnostics:!0,documentRangeFormattingEdits:!0,signatureHelp:!0,onTypeFormattingEdits:!0,codeActions:!0,inlayHints:!0},m=new d({allowNonTsExtensions:!0,target:99},{noSemanticValidation:!1,noSyntaxValidation:!1,onlyVisible:!1},{},{},p),h=new d({allowNonTsExtensions:!0,allowJs:!0,target:99},{noSemanticValidation:!0,noSyntaxValidation:!1,onlyVisible:!1},{},{},p),g=()=>v().then(e=>e.getTypeScriptWorker()),_=()=>v().then(e=>e.getJavaScriptWorker());function v(){return r(()=>import(`./tsMode-8TpA7NJ9.js`),__vite__mapDeps([0,1,2,3]))}t.onLanguage(`typescript`,()=>v().then(e=>e.setupTypeScript(m))),t.onLanguage(`javascript`,()=>v().then(e=>e.setupJavaScript(h)));export{m as n,a as t};
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
import{Ar as e,Rt as t}from"./index-C_dP75wD.js";import{o as n}from"./VContainer-DGQjsCTw.js";import{t as r}from"./apiError-BgZlS-MZ.js";function i(){let i=e([]),a=e(!1),o=n();async function s(e){a.value=!0;try{i.value=(await t.getList(`/servers/`,{per_page:e?.perPage??200})).items}catch(e){i.value=[],o(r(e,`加载服务器列表失败`),`error`)}finally{a.value=!1}}return{servers:i,loading:a,loadServers:s}}export{i as t};
|
||||
@@ -0,0 +1,2 @@
|
||||
import{Ar as e,Qn as t,Rr as n,Rt as r,er as i,hr as a,nr as o,or as s,tr as c,ur as l,yr as u}from"./index-C_dP75wD.js";import{t as d}from"./_plugin-vue_export-helper-BDNMzG2s.js";var f=new Set(`if.then.else.elif.fi.for.while.do.done.case.esac.in.function.return.break.continue.select.until.cd.ls.grep.egrep.fgrep.find.cat.chmod.chown.docker.podman.kubectl.apt.apt-get.yum.dnf.apk.pip.pip3.npm.npx.ssh.scp.rsync.curl.wget.tar.gzip.gunzip.kill.ps.top.htop.nano.vim.vi.journalctl.systemctl.service.supervisorctl.crontab.nginx.df.du.free.mount.uname.hostname.whoami.id.pwd.head.tail.wc.sort.uniq.awk.sed.cut.tr.xargs.tee.netstat.ss.ip.ping.dig.git.make.nohup.timeout.watch`.split(`.`)),p=new Set([`echo`,`printf`,`test`,`read`,`pushd`,`popd`,`exit`,`set`,`unset`,`export`,`source`,`alias`,`type`,`command`,`hash`,`help`,`history`]);function m(e){return e===`sudo`?`sudo`:f.has(e)?`keyword`:p.has(e)?`builtin`:/^[/~]/.test(e)||e.startsWith(`./`)||e.startsWith(`../`)?`path`:/^\d+(?:\.\d+)?$/.test(e)?`number`:`text`}function h(e){let t=[],n=0;for(;n<e.length;){let r=e[n];if(r===`#`){let r=n+1;for(;r<e.length&&e[r]!==`
|
||||
`;)r++;t.push({type:`comment`,value:e.slice(n,r)}),n=r;continue}if(r===`'`||r===`"`){let i=r,a=n+1;for(;a<e.length;){if(e[a]===`\\`&&i===`"`){a+=2;continue}if(e[a]===i){a++;break}a++}t.push({type:`string`,value:e.slice(n,a)}),n=a;continue}if(r==="`"){let r=n+1;for(;r<e.length&&e[r]!=="`";)r++;r<e.length&&r++,t.push({type:`backtick`,value:e.slice(n,r)}),n=r;continue}if(r===`$`){let r=n+1;if(e[r]===`{`){for(;r<e.length&&e[r]!==`}`;)r++;r<e.length&&r++}else if(e[r]===`(`)r++;else for(;r<e.length&&/[\w]/.test(e[r]);)r++;t.push({type:`variable`,value:e.slice(n,r)}),n=r;continue}if(r===`|`||r===`&`||r===`;`){let r=n;e[r]===`|`&&e[r+1]===`|`||e[r]===`&`&&e[r+1]===`&`?r+=2:r+=1;let i=e.slice(n,r);t.push({type:i===`|`||i===`||`?`pipe`:`operator`,value:i}),n=r;continue}if(r===`>`||r===`<`){let r=n;e[r]===`>`&&e[r+1]===`>`||e[r]===`<`&&e[r+1]===`<`?r+=2:r+=1,e[r]===`&`&&r++,t.push({type:`redirect`,value:e.slice(n,r)}),n=r;continue}if(/\s/.test(r)){let r=n;for(;r<e.length&&/\s/.test(e[r]);)r++;t.push({type:`text`,value:e.slice(n,r)}),n=r;continue}if(r===`-`&&e[n+1]===`-`){let r=n+2;for(;r<e.length&&/[\w-]/.test(e[r]);)r++;t.push({type:`flag`,value:e.slice(n,r)}),n=r;continue}if(r===`-`&&/[a-zA-Z]/.test(e[n+1]||``)){let r=n+1;for(;r<e.length&&/[a-zA-Z]/.test(e[r]);)r++;t.push({type:`flag`,value:e.slice(n,r)}),n=r;continue}let i=n;for(;i<e.length&&!/[\s#"'`$|;&<>]/.test(e[i]);)i++;let a=e.slice(n,i);t.push({type:m(a),value:a}),n=i}return t}function g(e){return e.replace(/&/g,`&`).replace(/</g,`<`).replace(/>/g,`>`)}var _={keyword:`sh-keyword`,builtin:`sh-builtin`,sudo:`sh-sudo`,pipe:`sh-pipe`,operator:`sh-operator`,redirect:`sh-redirect`,string:`sh-string`,flag:`sh-flag`,variable:`sh-variable`,backtick:`sh-backtick`,comment:`sh-comment`,number:`sh-number`,path:`sh-path`,text:`sh-text`};function v(e){return e?h(e).map(e=>`<span class="${_[e.type]}">${g(e.value)}</span>`).join(``):``}var y=[`innerHTML`],b=d(s({__name:`ShellHighlightField`,props:{modelValue:{},placeholder:{default:``},disabled:{type:Boolean,default:!1},multiline:{type:Boolean,default:!1},rows:{default:3},size:{default:`default`}},emits:[`update:modelValue`,`keydown`],setup(r,{expose:s,emit:d}){let f=e(null),p=r,m=d,h=t(()=>v(p.modelValue));function g(e){let t=e.target;m(`update:modelValue`,t.value)}function _(e){m(`keydown`,e)}function b(){return f.value}function x(){f.value?.focus()}function S(){let e=f.value;return e?e.value.substring(e.selectionStart??0,e.selectionEnd??0):``}function C(){let e=f.value;e&&(e.focus(),e.select())}async function w(e){let t=f.value;if(!t)return;let n=t.selectionStart??t.value.length,r=t.selectionEnd??n;m(`update:modelValue`,t.value.slice(0,n)+e+t.value.slice(r)),await l(),t.focus();let i=n+e.length;t.setSelectionRange(i,i)}return s({getInputEl:b,focusInput:x,getSelectionText:S,selectAllInput:C,insertAtSelection:w}),(e,t)=>(a(),o(`div`,{class:n([`sh-field position-relative`,{"sh-field--disabled":r.disabled,"sh-field--empty":!r.modelValue,"sh-field--large":r.size===`large`}])},[r.modelValue?(a(),o(`div`,{key:0,class:n([`sh-field__highlight font-mono`,r.multiline?`sh-field__highlight--multi`:`sh-field__highlight--single`]),"aria-hidden":`true`,innerHTML:h.value},null,10,y)):c(``,!0),(a(),i(u(r.multiline?`textarea`:`input`),{ref_key:`inputRef`,ref:f,value:r.modelValue,placeholder:r.placeholder,disabled:r.disabled,rows:r.multiline?r.rows:void 0,class:n([`sh-field__input font-mono`,r.multiline?`sh-field__input--multi`:`sh-field__input--single`]),spellcheck:`false`,autocomplete:`off`,onInput:g,onKeydown:_},null,40,[`value`,`placeholder`,`disabled`,`rows`,`class`]))],2))}}),[[`__scopeId`,`data-v-b73e3452`]]);function x(e){return[...e].sort((e,t)=>e.is_builtin===t.is_builtin?e.sort_order===t.sort_order?e.id-t.id:e.sort_order-t.sort_order:e.is_builtin?1:-1)}function S(){let t=e([]),n=e(!1);async function i(){n.value=!0;try{t.value=x(await r.get(`/terminal/quick-commands`)||[])}catch{throw t.value=[],Error(`加载快捷命令失败`)}finally{n.value=!1}}async function a(e,t){await r.post(`/terminal/quick-commands`,{name:e.trim(),cmd:t.trim()}),await i()}async function o(e,t,n){await r.put(`/terminal/quick-commands/${e}`,{name:t.trim(),cmd:n.trim()}),await i()}async function s(e){await r.delete(`/terminal/quick-commands/${e}`),await i()}async function c(e){t.value=x((await r.put(`/terminal/quick-commands/reorder`,{ids:e})).items||[])}return{commands:t,loading:n,load:i,create:a,update:o,remove:s,reorder:c,customCommands:()=>t.value.filter(e=>!e.is_builtin),builtinCommands:()=>t.value.filter(e=>e.is_builtin)}}export{S as n,b as r,x as t};
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
<link rel="icon" href="/app/favicon.ico">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Nexus</title>
|
||||
<script type="module" crossorigin src="/app/assets/index-DPtWdFsS.js"></script>
|
||||
<script type="module" crossorigin src="/app/assets/index-C_dP75wD.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/app/assets/index-BGzIPEZO.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
+108
-10
@@ -84,12 +84,18 @@
|
||||
</p>
|
||||
<div class="bg-slate-50 rounded-lg p-5 text-left mb-6">
|
||||
<h3 class="font-semibold text-slate-700 mb-3">📋 安装前准备</h3>
|
||||
<ul class="text-[var(--text-dim)] text-sm ml-5 list-disc space-y-1">
|
||||
<ul x-show="!dockerDefaults" class="text-[var(--text-dim)] text-sm ml-5 list-disc space-y-1">
|
||||
<li>Python 3.12+ (FastAPI + uvicorn)</li>
|
||||
<li>MySQL 8.0+ (需提前创建数据库和用户)</li>
|
||||
<li>Redis 6+ (心跳缓冲和实时数据)</li>
|
||||
<li>Web 目录和根目录可写权限</li>
|
||||
</ul>
|
||||
<ul x-show="dockerDefaults" class="text-[var(--text-dim)] text-sm ml-5 list-disc space-y-1">
|
||||
<li>1Panel 已安装,应用商店已部署 <strong>MySQL</strong> + <strong>Redis</strong>(<code class="bg-slate-100 px-1 rounded">1panel-network</code>)</li>
|
||||
<li>Nexus Docker 容器已运行(<code class="bg-slate-100 px-1 rounded">nx install</code> / Compose)</li>
|
||||
<li>1Panel 数据库:库 <code class="bg-slate-100 px-1 rounded">nexus</code>、用户 <code class="bg-slate-100 px-1 rounded">nexus</code></li>
|
||||
<li>进程守护由 <strong>Docker restart + healthcheck</strong> 负责(无需 Supervisor)</li>
|
||||
</ul>
|
||||
</div>
|
||||
<button @click="step = 2; checkEnv()" class="bg-blue-500 hover:bg-blue-600 text-white px-6 py-2.5 rounded-lg font-semibold transition">
|
||||
开始安装 →
|
||||
@@ -390,14 +396,92 @@
|
||||
<div x-show="initResult?.guardian_results?.length" class="text-left mt-6 mb-4 rounded-lg p-4"
|
||||
:class="guardianAllOk ? 'bg-green-50 border-l-4 border-green-500' : 'bg-amber-50 border-l-4 border-amber-500'">
|
||||
<div class="font-semibold text-sm mb-2" :class="guardianAllOk ? 'text-green-700' : 'text-amber-700'"
|
||||
x-text="guardianAllOk ? '✓ 进程守护已自动配置' : '⚠ 进程守护部分配置失败'"></div>
|
||||
x-text="guardianAllOk ? (isDockerInstall ? '✓ 1Panel Docker 进程守护已就绪' : '✓ 进程守护已自动配置') : (isDockerInstall ? '⚠ Docker 守护检查有项需处理' : '⚠ 进程守护部分配置失败')"></div>
|
||||
<template x-for="r in (initResult?.guardian_results || [])" :key="r">
|
||||
<div class="text-xs leading-relaxed" x-text="r"></div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- Post-install checklist -->
|
||||
<div class="text-left mt-6 space-y-3">
|
||||
<!-- Post-install checklist: 1Panel Docker -->
|
||||
<div x-show="isDockerInstall" class="text-left mt-6 space-y-3">
|
||||
<h3 class="text-sm font-bold text-slate-700 text-center mb-3">━━━ 1Panel 后续配置(必须完成)━━━</h3>
|
||||
|
||||
<div class="bg-slate-50 border border-slate-200 rounded-lg p-4">
|
||||
<div class="font-bold text-sm text-slate-800 mb-2">
|
||||
<span class="inline-flex items-center justify-center w-6 h-6 rounded-full bg-blue-500 text-white text-xs font-bold mr-2">1</span>
|
||||
Docker 进程守护(Layer 1)
|
||||
</div>
|
||||
<div class="text-xs text-[var(--text-muted)] space-y-1">
|
||||
<p>Compose 已配置 <code class="bg-slate-100 px-1 rounded">restart: unless-stopped</code> + 容器 <code class="bg-slate-100 px-1 rounded">healthcheck</code>(每 30s 探测 <code class="bg-slate-100 px-1 rounded">/health</code>)。</p>
|
||||
<p>查看状态: <code class="bg-[var(--bg-surface)] text-slate-200 px-1.5 py-0.5 rounded">docker ps</code> · 日志: <code class="bg-[var(--bg-surface)] text-slate-200 px-1.5 py-0.5 rounded">docker logs -f nexus-prod-nexus-1</code></p>
|
||||
<p>异常重启: <code class="bg-[var(--bg-surface)] text-slate-200 px-1.5 py-0.5 rounded">cd /opt/nexus && nx update</code></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-slate-50 border border-slate-200 rounded-lg p-4">
|
||||
<div class="font-bold text-sm text-slate-800 mb-2">
|
||||
<span class="inline-flex items-center justify-center w-6 h-6 rounded-full bg-blue-500 text-white text-xs font-bold mr-2">2</span>
|
||||
OpenResty 反向代理(Layer 3 · 宿主机)
|
||||
</div>
|
||||
<div class="text-xs text-[var(--text-muted)]">
|
||||
<p class="mb-1">1Panel → 网站 → 你的域名 → 配置 → 反向代理 / 自定义配置,目标 <code class="bg-slate-100 px-1 rounded">http://127.0.0.1:<span x-text="form.api_port"></span></code></p>
|
||||
<p class="mb-1">参考仓库: <code class="bg-slate-100 px-1 rounded">deploy/1panel/openresty-nexus.conf.example</code></p>
|
||||
<div class="code-block"><button class="copy-btn" @click="copyCode($event, 'openresty1p')">复制核心片段</button><pre id="openresty1p">upstream nexus_api {
|
||||
server 127.0.0.1:<span x-text="form.api_port"></span>;
|
||||
keepalive 32;
|
||||
}
|
||||
location /api/ {
|
||||
proxy_pass http://nexus_api;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
location /ws/ {
|
||||
proxy_pass http://nexus_api;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_read_timeout 3600s;
|
||||
}
|
||||
location /app/ {
|
||||
proxy_pass http://nexus_api;
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
location /health {
|
||||
proxy_pass http://nexus_api;
|
||||
}</pre></div>
|
||||
<p>SSL: 1Panel → 网站 → SSL → Let's Encrypt → 强制 HTTPS</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-slate-50 border border-slate-200 rounded-lg p-4">
|
||||
<div class="font-bold text-sm text-slate-800 mb-2">
|
||||
<span class="inline-flex items-center justify-center w-6 h-6 rounded-full bg-blue-500 text-white text-xs font-bold mr-2">3</span>
|
||||
应用内自检(Layer 2)
|
||||
</div>
|
||||
<div class="text-xs text-[var(--text-muted)]">
|
||||
<p>完成本向导后,Nexus 正常模式自动运行 <code class="bg-slate-100 px-1 rounded">self_monitor</code>(每 30s 检查 Redis/MySQL/WebSocket)。</p>
|
||||
<p>验收: <code class="bg-[var(--bg-surface)] text-slate-200 px-1.5 py-0.5 rounded">curl -s http://127.0.0.1:<span x-text="form.api_port"></span>/health</code> → <code class="bg-slate-100 px-1 rounded">ok</code></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-slate-50 border border-slate-200 rounded-lg p-4">
|
||||
<div class="font-bold text-sm text-slate-800 mb-2">
|
||||
<span class="inline-flex items-center justify-center w-6 h-6 rounded-full bg-blue-500 text-white text-xs font-bold mr-2">4</span>
|
||||
日常运维
|
||||
</div>
|
||||
<div class="text-xs text-[var(--text-muted)] space-y-1">
|
||||
<p><code class="bg-[var(--bg-surface)] text-slate-200 px-1.5 py-0.5 rounded">nx update</code> — 拉代码、重建镜像、同步安装向导</p>
|
||||
<p><code class="bg-[var(--bg-surface)] text-slate-200 px-1.5 py-0.5 rounded">bash deploy/verify-1panel-install-wizard.sh</code> — 一键验收</p>
|
||||
<p>可选宿主机 crontab: <code class="bg-[var(--bg-surface)] text-slate-200 px-1.5 py-0.5 rounded">* * * * * /opt/nexus/deploy/health_monitor.sh</code></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Post-install checklist: bare metal / 宝塔 -->
|
||||
<div x-show="!isDockerInstall" class="text-left mt-6 space-y-3">
|
||||
<h3 class="text-sm font-bold text-slate-700 text-center mb-3">━━━ 后续配置清单(必须完成)━━━</h3>
|
||||
|
||||
<!-- 1. Supervisor -->
|
||||
@@ -594,17 +678,21 @@ location / {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 7. Security -->
|
||||
</div>
|
||||
|
||||
<!-- Security (all deployment modes) -->
|
||||
<div class="text-left mt-6">
|
||||
<div class="bg-slate-50 border border-slate-200 rounded-lg p-4">
|
||||
<div class="font-bold text-sm text-slate-800 mb-2">
|
||||
<span class="inline-flex items-center justify-center w-6 h-6 rounded-full bg-blue-500 text-white text-xs font-bold mr-2">7</span>
|
||||
<span class="inline-flex items-center justify-center w-6 h-6 rounded-full bg-blue-500 text-white text-xs font-bold mr-2">✓</span>
|
||||
安全收尾
|
||||
</div>
|
||||
<div class="text-xs text-[var(--text-muted)] space-y-1">
|
||||
<p>✓ 安装向导已锁定 — 删除 <code class="bg-[var(--bg-surface)] text-slate-200 px-1.5 py-0.5 rounded">.env</code> 文件可重新安装</p>
|
||||
<p>✓ <code class="bg-[var(--bg-surface)] text-slate-200 px-1.5 py-0.5 rounded">.env</code> 包含 SECRET_KEY/API_KEY — <b>安装后不可修改</b>(加密一致性)</p>
|
||||
<p>✓ 防火墙限制端口 <span x-text="form.api_port"></span> (仅允许本机和子服务器IP)</p>
|
||||
<p>✓ 修改管理员默认密码</p>
|
||||
<p>✓ 安装向导已锁定 — Docker 重装见知识文档 §7.4(删卷内 .env)</p>
|
||||
<p>✓ <code class="bg-[var(--bg-surface)] text-slate-200 px-1.5 py-0.5 rounded">.env</code> 含 SECRET_KEY/API_KEY — <b>安装后不可修改</b></p>
|
||||
<p x-show="!isDockerInstall">✓ 防火墙限制端口 <span x-text="form.api_port"></span>(仅本机与子机 IP)</p>
|
||||
<p x-show="isDockerInstall">✓ Compose 已绑定 <code class="bg-slate-100 px-1 rounded">127.0.0.1:<span x-text="form.api_port"></span></code>,外网经 OpenResty 访问</p>
|
||||
<p>✓ 请使用强密码登录管理后台</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -639,6 +727,7 @@ function installWizard() {
|
||||
redisRepaired: false,
|
||||
resolvedRedisUrl: '',
|
||||
dockerDefaults: null,
|
||||
dockerMode: false,
|
||||
credLoading: false,
|
||||
credChecks: [],
|
||||
credAllPass: false,
|
||||
@@ -671,9 +760,16 @@ function installWizard() {
|
||||
system_title: 'Nexus — 服务器运维管理平台',
|
||||
},
|
||||
|
||||
get isDockerInstall() {
|
||||
return !!(this.dockerMode || this.dockerDefaults);
|
||||
},
|
||||
|
||||
get guardianAllOk() {
|
||||
const results = this.initResult?.guardian_results || [];
|
||||
if (!results.length) return false;
|
||||
if (this.isDockerInstall) {
|
||||
return results.every(r => !r.includes('✗'));
|
||||
}
|
||||
return results.every(r => !r.includes('✗') && !r.includes('⚠'));
|
||||
},
|
||||
|
||||
@@ -744,6 +840,7 @@ function installWizard() {
|
||||
});
|
||||
if (d.install_dir) this.installDir = d.install_dir;
|
||||
if (d.pool_size) this.initResult = { pool_size: d.pool_size, max_overflow: d.max_overflow, guardian_results: d.guardian_results || [] };
|
||||
if (d.docker_mode) this.dockerMode = true;
|
||||
if (d.step >= 4) this.checkConnections();
|
||||
}
|
||||
} catch(e) {}
|
||||
@@ -851,6 +948,7 @@ function installWizard() {
|
||||
this.initResult = d;
|
||||
this.installToken = d.install_token || '';
|
||||
this.btPanel = !!d.bt_panel;
|
||||
this.dockerMode = !!d.docker_mode;
|
||||
this.step = 4;
|
||||
await this.checkConnections();
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user