diff --git a/web/src/views/RiskRules.vue b/web/src/views/RiskRules.vue index 9146e06..5065bbc 100644 --- a/web/src/views/RiskRules.vue +++ b/web/src/views/RiskRules.vue @@ -6,70 +6,73 @@ 刷新 - + - - + + + + - + + + + - - -
- - 表格编辑 - JSON 编辑 - -
- - -
- 保存 -
-
@@ -77,28 +80,25 @@ import api from '../api' const loading = ref(false), saving = ref(false) -const rulesData = ref([]) -const siteTypes = ['nexusphp','mtorrent','gazelle','unit3d'] -const editorVisible = ref(false), editorType = ref(''), editorMode = ref('table'), editorJson = ref('') +const siteTypes = ref([]), rulesData = ref([]) +const editType = ref(''), editorMode = ref('table'), editorJson = ref('') const editingRules = ref([]) -watch(editorMode, (m) => { - if (m === 'json') editorJson.value = JSON.stringify(editingRules.value, null, 2) - else { - try { editingRules.value = JSON.parse(editorJson.value) } catch { /* keep table */ } - } -}) - const fmt = (t) => t ? (t.substring(0,19)||'').replace('T',' ') : '-' +watch(editorMode, (m) => { + if (m === 'json') editorJson.value = JSON.stringify(editingRules.value, null, 2) + else { try { editingRules.value = JSON.parse(editorJson.value) } catch {} } +}) + const merged = computed(() => { const map = {} - const list = Array.isArray(rulesData.value) ? rulesData.value : [] - for (const r of list) { - const cnt = (() => { try { return JSON.parse(r.rulesJson || '[]').length } catch { return 0 } })() + const rules = Array.isArray(rulesData.value) ? rulesData.value : [] + for (const r of rules) { + const cnt = (() => { try { return JSON.parse(r.rulesJson||'[]').length } catch { return 0 } })() map[r.siteType] = { siteType: r.siteType, version: r.version, ruleCount: cnt, updatedAt: r.updatedAt, rulesJson: r.rulesJson } } - for (const t of siteTypes) { + for (const t of siteTypes.value) { if (!map[t]) map[t] = { siteType: t, version: null, ruleCount: 0, updatedAt: null, rulesJson: null } } return Object.values(map) @@ -107,31 +107,40 @@ const merged = computed(() => { const fetchAll = async () => { loading.value = true try { - const r = await api.raw.get('/admin/risk-rules') - rulesData.value = Array.isArray(r?.data) ? r.data : Array.isArray(r) ? r : [] + // 获取站点类型(从实际站点数据中提取) + const sr = await api.raw.get('/admin/sites').catch(() => ({})) + const siteList = (sr?.data || sr || []) + siteTypes.value = [...new Set(siteList.map(s => s.siteType).filter(Boolean))] + if (siteTypes.value.length === 0) siteTypes.value = ['nexusphp','mtorrent','gazelle','unit3d'] + + const rr = await api.raw.get('/admin/risk-rules').catch(() => ({})) + rulesData.value = Array.isArray(rr?.data) ? rr.data : Array.isArray(rr) ? rr : [] } catch { ElMessage.error('加载失败') } finally { loading.value = false } } -const showEditor = (row) => { - editorType.value = row.siteType - try { editingRules.value = JSON.parse(JSON.stringify(JSON.parse(row.rulesJson || '[]'))) } +const toggleEdit = (row) => { + if (editType.value === row.siteType) { editType.value = ''; return } + editType.value = row.siteType + editorMode.value = 'table' + try { editingRules.value = JSON.parse(JSON.stringify(JSON.parse(row.rulesJson||'[]'))) } catch { editingRules.value = [{type:'KILL',name:'',metric:'',threshold:0,unit:'DAY',condition:'GT',level:'CRITICAL'}] } - if (editingRules.value.length === 0) editingRules.value.push({type:'KILL',name:'',metric:'',threshold:0,unit:'DAY',condition:'GT',level:'CRITICAL'}) - editorVisible.value = true + if (editingRules.value.length===0) editingRules.value.push({type:'KILL',name:'',metric:'',threshold:0,unit:'DAY',condition:'GT',level:'CRITICAL'}) } const saveRules = async () => { saving.value = true try { - const rules = editorMode.value === 'json' ? JSON.parse(editorJson.value) : editingRules.value - await api.raw.post('/risk-rules/' + editorType.value, { rules }) - ElMessage.success('已保存') - editorVisible.value = false - fetchAll() + const rules = editorMode.value==='json' ? JSON.parse(editorJson.value) : editingRules.value + await api.raw.post('/risk-rules/'+editType.value, { rules }) + ElMessage.success('已保存'); editType.value = ''; fetchAll() } catch { ElMessage.error('保存失败') } finally { saving.value = false } } onMounted(fetchAll) + + diff --git a/web/src/views/SitePages.vue b/web/src/views/SitePages.vue index 7f66eb8..37081fd 100644 --- a/web/src/views/SitePages.vue +++ b/web/src/views/SitePages.vue @@ -11,59 +11,62 @@ - - - - + + + + - + + + +
- - -
- - 表格编辑 - JSON 编辑 - -
- - -
- 保存 -
-
@@ -72,21 +75,18 @@ import api from '../api' const loading = ref(false), saving = ref(false) const sites = ref([]), pagesMap = ref({}) -const editorVisible = ref(false), editorSiteId = ref(''), editorMode = ref('table'), editorJson = ref('') +const editSiteId = ref(''), editorMode = ref('table'), editorJson = ref('') const editingEntries = ref([]) -watch(editorMode, (m) => { - if (m === 'json') editorJson.value = JSON.stringify(editingEntries.value, null, 2) - else { - try { editingEntries.value = JSON.parse(editorJson.value) } catch { /* keep table data */ } - } -}) - const fmt = (t) => t ? (t.substring(0,19)||'').replace('T',' ') : '-' +watch(editorMode, (m) => { + if (m === 'json') editorJson.value = JSON.stringify(editingEntries.value, null, 2) + else { try { editingEntries.value = JSON.parse(editorJson.value) } catch {} } +}) + const merged = computed(() => { - const list = Array.isArray(sites.value) ? sites.value : [] - return list.map(s => ({ + return (Array.isArray(sites.value)?sites.value:[]).map(s => ({ siteId: s.siteId, names: s.names, version: pagesMap.value[s.siteId]?.version || null, @@ -99,41 +99,43 @@ const merged = computed(() => { const fetchAll = async () => { loading.value = true try { - const [sr, pr] = await Promise.all([ - api.sites.adminList().catch(() => ({})), - api.raw.get('/admin/site-pages').catch(() => ({})), + const [sr, pr] = await Promise.allSettled([ + api.raw.get('/admin/sites'), + api.raw.get('/admin/site-pages'), ]) - sites.value = sr.data || sr || [] - const pages = pr.data || pr || [] + sites.value = (sr.status==='fulfilled' ? (sr.value?.data || sr.value || []) : []) + const pages = pr.status==='fulfilled' ? (pr.value?.data || pr.value || []) : [] const map = {} for (const p of pages) { - const entries = JSON.parse(p.entriesJson || '[]') + const entries = (() => { try { return JSON.parse(p.entriesJson||'[]') } catch {return []} })() map[p.siteId] = { version: p.version, entryCount: entries.length, updatedAt: p.updatedAt, entriesJson: p.entriesJson } } pagesMap.value = map - } catch { ElMessage.error('加载失败') } - finally { loading.value = false } + } finally { loading.value = false } } -const showEditor = (row) => { - editorSiteId.value = row.siteId - try { editingEntries.value = JSON.parse(JSON.stringify(JSON.parse(row.entriesJson || '[]'))) } - catch { editingEntries.value = [{key:'home',label:'首页',url:'{base}','method':'GET'}] } - if (editingEntries.value.length === 0) editingEntries.value.push({key:'home',label:'首页',url:'{base}','method':'GET'}) - editorVisible.value = true +const toggleEdit = (row) => { + if (editSiteId.value === row.siteId) { editSiteId.value = ''; return } + editSiteId.value = row.siteId + editorMode.value = 'table' + try { editingEntries.value = JSON.parse(JSON.stringify(JSON.parse(row.entriesJson||'[]'))) } + catch { editingEntries.value = [{key:'home',label:'首页',url:'{base}',method:'GET'}] } + if (editingEntries.value.length===0) editingEntries.value.push({key:'home',label:'首页',url:'{base}',method:'GET'}) } const savePages = async () => { saving.value = true try { - const entries = editorMode.value === 'json' ? JSON.parse(editorJson.value) : editingEntries.value - await api.raw.put('/sites/' + editorSiteId.value + '/pages', { entries }) - ElMessage.success('已保存') - editorVisible.value = false - fetchAll() + const entries = editorMode.value==='json' ? JSON.parse(editorJson.value) : editingEntries.value + await api.raw.put('/sites/'+editSiteId.value+'/pages', { entries }) + ElMessage.success('已保存'); editSiteId.value = ''; fetchAll() } catch { ElMessage.error('保存失败') } finally { saving.value = false } } onMounted(fetchAll) + +