From 11629fba807e956ae890212355e47f86f6b18910 Mon Sep 17 00:00:00 2001 From: farhadh Date: Mon, 11 May 2026 21:10:58 +0200 Subject: [PATCH] chore: frontend UX improvements, CI pipeline, and dev tooling - AppSidebar: logout via POST /logout instead of navigating to GET - InboundList: persist filter state (search, protocol, node) to localStorage across page reloads; add protocol and node filter dropdowns - IndexPage: add health status strip (Xray, CPU, Memory, Update) with quick-action buttons - dependabot: weekly go mod and npm update schedule - ci.yml: add GitHub Actions workflow for build and vet - .nvmrc: pin Node 22 for local development - frontend: bump package.json and package-lock.json - SubPage, DnsPresetsModal, api-docs: minor fixes --- .github/dependabot.yml | 8 +++ .github/workflows/ci.yml | 59 ++++++++++++++++ .nvmrc | 1 + frontend/package-lock.json | 4 ++ frontend/package.json | 4 ++ frontend/src/components/AppSidebar.vue | 10 ++- frontend/src/pages/api-docs/endpoints.js | 8 +-- frontend/src/pages/inbounds/InboundList.vue | 77 ++++++++++++++++++--- frontend/src/pages/index/IndexPage.vue | 76 ++++++++++++++++++++ frontend/src/pages/sub/SubPage.vue | 1 - frontend/src/pages/xray/DnsPresetsModal.vue | 2 +- 11 files changed, 234 insertions(+), 16 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 .nvmrc diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 0d08e261..e9b37e82 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -9,3 +9,11 @@ updates: directory: "/" # Location of package manifests schedule: interval: "weekly" + - package-ecosystem: "gomod" + directory: "/" + schedule: + interval: "weekly" + - package-ecosystem: "npm" + directory: "/frontend" + schedule: + interval: "weekly" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..7d3be9a8 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,59 @@ +name: CI + +on: + pull_request: + push: + branches: + - main + +permissions: + contents: read + +jobs: + go-test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - uses: actions/setup-go@v6 + with: + go-version-file: go.mod + cache: true + - name: Test + run: | + go list ./... | grep -v '/frontend/node_modules/' > /tmp/go-packages.txt + go test $(cat /tmp/go-packages.txt) + + govulncheck: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - uses: actions/setup-go@v6 + with: + go-version-file: go.mod + cache: true + - name: Install govulncheck + run: go install golang.org/x/vuln/cmd/govulncheck@latest + - name: Run govulncheck + run: govulncheck ./... + + frontend: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - uses: actions/setup-node@v5 + with: + node-version-file: .nvmrc + cache: npm + cache-dependency-path: frontend/package-lock.json + - name: Install + run: npm ci + working-directory: frontend + - name: Lint + run: npm run lint + working-directory: frontend + - name: Build + run: npm run build + working-directory: frontend + - name: Audit + run: npm audit --audit-level=high + working-directory: frontend diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 00000000..2bd5a0a9 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +22 diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 3bfbce76..365a2324 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -7,6 +7,10 @@ "": { "name": "3x-ui-frontend", "version": "0.0.2", + "engines": { + "node": ">=22.0.0", + "npm": ">=10.0.0" + }, "dependencies": { "@ant-design/icons-vue": "^7.0.1", "ant-design-vue": "^4.2.6", diff --git a/frontend/package.json b/frontend/package.json index bf0ca9c1..03c381f5 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -4,6 +4,10 @@ "version": "0.0.2", "type": "module", "description": "3x-ui panel frontend (Vue 3 + Ant Design Vue 4 + Vite 8).", + "engines": { + "node": ">=22.0.0", + "npm": ">=10.0.0" + }, "scripts": { "dev": "vite", "build": "vite build", diff --git a/frontend/src/components/AppSidebar.vue b/frontend/src/components/AppSidebar.vue index 7763b715..bf625f65 100644 --- a/frontend/src/components/AppSidebar.vue +++ b/frontend/src/components/AppSidebar.vue @@ -14,6 +14,7 @@ import { } from '@ant-design/icons-vue'; import { theme, currentTheme, toggleTheme, toggleUltra, pauseAnimationsUntilLeave } from '@/composables/useTheme.js'; +import { HttpUtil } from '@/utils'; const { t } = useI18n(); @@ -45,7 +46,7 @@ const tabs = computed(() => [ { key: `${prefix}panel/settings`, icon: 'setting', title: t('menu.settings') }, { key: `${prefix}panel/xray`, icon: 'tool', title: t('menu.xray') }, { key: `${prefix}panel/api-docs`, icon: 'apidocs', title: t('menu.apiDocs') }, - { key: `${prefix}logout`, icon: 'logout', title: t('logout') }, + { key: 'logout', icon: 'logout', title: t('logout') }, ]); const navTabs = computed(() => tabs.value.filter((tab) => tab.icon !== 'logout')); @@ -55,7 +56,12 @@ const drawerOpen = ref(false); const collapsed = ref(JSON.parse(localStorage.getItem(SIDEBAR_COLLAPSED_KEY) || 'false')); const drawerWidth = 'min(82vw, 320px)'; -function openLink(key) { +async function openLink(key) { + if (key === 'logout') { + await HttpUtil.post('/logout'); + window.location.href = props.basePath || '/'; + return; + } if (key.startsWith('http')) { window.open(key); } else { diff --git a/frontend/src/pages/api-docs/endpoints.js b/frontend/src/pages/api-docs/endpoints.js index baf2bda5..48d8b9fe 100644 --- a/frontend/src/pages/api-docs/endpoints.js +++ b/frontend/src/pages/api-docs/endpoints.js @@ -19,9 +19,9 @@ export const sections = [ '{\n "success": true,\n "msg": "Logged in successfully"\n}', }, { - method: 'GET', + method: 'POST', path: '/logout', - summary: 'Clear the session cookie. Redirects back to the login page; not useful from non-browser clients.', + summary: 'Clear the session cookie. Requires the CSRF header for browser sessions.', }, { method: 'GET', @@ -43,7 +43,7 @@ export const sections = [ id: 'inbounds', title: 'Inbounds API', description: - 'Manage inbound configurations and their clients. All endpoints live under /panel/api/inbounds and require a logged-in session or Bearer token. Link-generating endpoints honour X-Forwarded-Host / X-Forwarded-Proto, so callers behind a reverse proxy get the correct external host in returned URLs.', + 'Manage inbound configurations and their clients. All endpoints live under /panel/api/inbounds and require a logged-in session or Bearer token. Link-generating endpoints honour forwarded headers only when the request comes from a configured trusted proxy.', endpoints: [ { method: 'GET', @@ -531,7 +531,7 @@ export const sections = [ description: 'Operations that interact with the configured Telegram bot.', endpoints: [ { - method: 'GET', + method: 'POST', path: '/panel/api/backuptotgbot', summary: 'Send a fresh DB backup to every Telegram chat configured as an admin recipient. No body, no params.', }, diff --git a/frontend/src/pages/inbounds/InboundList.vue b/frontend/src/pages/inbounds/InboundList.vue index 88b39bc8..2e8a7a06 100644 --- a/frontend/src/pages/inbounds/InboundList.vue +++ b/frontend/src/pages/inbounds/InboundList.vue @@ -1,5 +1,5 @@