fix: 认证系统、登录兼容性、运行时配置、钉钉通知及代码清理

- 实现 JWT 认证中间件,保护配置管理和任务删除接口
- 修复 ConfigView 登录按钮(Vue 3 inject 替代 $root)
- 页面刷新时通过 /api/auth/me 校验 token 有效性
- max_concurrent_builds 修改后运行时即时生效
- 实现钉钉 webhook 通知(构建成功/失败自动推送)
- 删除未使用的 useWebSocket composable
- log_streamer 使用 LOG_QUEUE_MAX_SIZE 配置常量
This commit is contained in:
shen
2026-06-08 20:35:05 +08:00
parent f5919a8229
commit e18f33d60f
14 changed files with 262 additions and 91 deletions
+37 -26
View File
@@ -3,7 +3,7 @@
<div v-if="!isLoggedIn" class="access-denied">
<h3>需要管理员权限</h3>
<p>请先登录管理员账号以访问配置管理页面</p>
<button class="btn-login" @click="$root.showLogin = true">登录管理员账号</button>
<button class="btn-login" @click="showLogin = true">登录管理员账号</button>
</div>
<div v-else class="admin-page">
<div class="sidebar">
@@ -443,10 +443,21 @@
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { ref, onMounted, inject } from 'vue'
const isLoggedIn = ref(localStorage.getItem('isAdmin') === 'true')
const showLogin = inject('showLogin')
const getToken = inject('getToken')
const isLoggedIn = inject('isLoggedIn')
const tab = ref('servers')
// 带认证的 fetch 封装
const authFetch = (url, options = {}) => {
const token = getToken()
if (token) {
options.headers = { ...options.headers, 'Authorization': `Bearer ${token}` }
}
return fetch(url, options)
}
const apps = ref({})
const schemes = ref({})
const servers = ref({})
@@ -475,14 +486,14 @@ onMounted(async () => {
const loadData = async () => {
const [appsRes, schemesRes, serversRes, branchesRes, buildRes, uploadRes, configRes, versionsRes] = await Promise.all([
fetch('/api/config/apps'),
fetch('/api/config/schemes'),
fetch('/api/config/servers'),
fetch('/api/config/branches'),
fetch('/api/config/build'),
fetch('/api/config/upload'),
fetch('/api/config'),
fetch('/api/config/versions'),
authFetch('/api/config/apps'),
authFetch('/api/config/schemes'),
authFetch('/api/config/servers'),
authFetch('/api/config/branches'),
authFetch('/api/config/build'),
authFetch('/api/config/upload'),
authFetch('/api/config'),
authFetch('/api/config/versions'),
])
apps.value = await appsRes.json()
schemes.value = await schemesRes.json()
@@ -509,7 +520,7 @@ const saveServer = async () => {
try {
if (editingServerName.value) {
const res = await fetch(`/api/config/servers/${encodeURIComponent(editingServerName.value)}`, {
const res = await authFetch(`/api/config/servers/${encodeURIComponent(editingServerName.value)}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(serverForm.value),
@@ -519,7 +530,7 @@ const saveServer = async () => {
throw new Error(err.detail || '保存失败')
}
} else {
const res = await fetch('/api/config/servers', {
const res = await authFetch('/api/config/servers', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(serverForm.value),
@@ -540,7 +551,7 @@ const saveServer = async () => {
const deleteServer = async (name) => {
if (!confirm(`确定删除环境「${name}」?`)) return
try {
const res = await fetch(`/api/config/servers/${encodeURIComponent(name)}`, { method: 'DELETE' })
const res = await authFetch(`/api/config/servers/${encodeURIComponent(name)}`, { method: 'DELETE' })
if (!res.ok) {
const err = await res.json()
throw new Error(err.detail || '删除失败')
@@ -626,13 +637,13 @@ const saveApp = async () => {
try {
let res
if (editingAppId.value) {
res = await fetch(`/api/config/apps/${editingAppId.value}`, {
res = await authFetch(`/api/config/apps/${editingAppId.value}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(cleanData),
})
} else {
res = await fetch('/api/config/apps', {
res = await authFetch('/api/config/apps', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(cleanData),
@@ -652,7 +663,7 @@ const saveApp = async () => {
const deleteApp = async (id) => {
if (!confirm('确定删除此 App')) return
await fetch(`/api/config/apps/${id}`, { method: 'DELETE' })
await authFetch(`/api/config/apps/${id}`, { method: 'DELETE' })
await loadData()
}
@@ -671,14 +682,14 @@ const saveScheme = async () => {
try {
if (editingSchemeId.value) {
const res = await fetch(`/api/config/schemes/${editingSchemeId.value}`, {
const res = await authFetch(`/api/config/schemes/${editingSchemeId.value}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(schemeForm.value),
})
if (!res.ok) throw new Error('保存失败')
} else {
const res = await fetch('/api/config/schemes', {
const res = await authFetch('/api/config/schemes', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(schemeForm.value),
@@ -695,7 +706,7 @@ const saveScheme = async () => {
const deleteScheme = async (id) => {
if (!confirm('确定删除此 Scheme')) return
await fetch(`/api/config/schemes/${id}`, { method: 'DELETE' })
await authFetch(`/api/config/schemes/${id}`, { method: 'DELETE' })
await loadData()
}
@@ -704,7 +715,7 @@ const addBranch = async () => {
const name = newBranch.value.trim()
if (!name) return
try {
const res = await fetch('/api/config/branches', {
const res = await authFetch('/api/config/branches', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name }),
@@ -723,7 +734,7 @@ const addBranch = async () => {
const deleteBranch = async (name) => {
if (!confirm(`确定删除分支「${name}」?`)) return
try {
const res = await fetch(`/api/config/branches/${encodeURIComponent(name)}`, { method: 'DELETE' })
const res = await authFetch(`/api/config/branches/${encodeURIComponent(name)}`, { method: 'DELETE' })
if (!res.ok) throw new Error('删除失败')
await loadData()
} catch (e) {
@@ -733,7 +744,7 @@ const deleteBranch = async (name) => {
// 其他
const saveBuildSettings = async () => {
await fetch('/api/config/build', {
await authFetch('/api/config/build', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(buildSettings.value),
@@ -743,7 +754,7 @@ const saveBuildSettings = async () => {
const saveVersions = async () => {
try {
const res = await fetch('/api/config/versions', {
const res = await authFetch('/api/config/versions', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(versions.value),
@@ -763,7 +774,7 @@ const saveVersions = async () => {
const saveUploadConfig = async () => {
try {
await fetch('/api/config/upload', {
await authFetch('/api/config/upload', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(uploadConfig.value),
@@ -777,7 +788,7 @@ const saveUploadConfig = async () => {
const saveJson = async () => {
try {
const config = JSON.parse(jsonContent.value)
await fetch('/api/config', {
await authFetch('/api/config', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(config),