From ac9373ddae04a940a59f026682d3be6fecff9ba2 Mon Sep 17 00:00:00 2001 From: Ap_Tx <3045929398@qq.com> Date: Sun, 18 Oct 2026 07:43:37 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E7=9A=AE=E8=82=A4?= =?UTF-8?q?=E7=AB=99=E7=AE=A1=E7=90=86=E5=8F=B0=E6=9C=8D=E5=8A=A1=E5=99=A8?= =?UTF-8?q?=E4=B8=8E=E5=AE=A1=E8=AE=A1=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/skin-site/SkinSiteAdminView.vue | 4 + .../skin-site/admin/AdminAccessAuditPage.vue | 273 ++++++++++++++++++ .../ui/skin-site/admin/AdminServersPage.vue | 188 ++++++++++++ .../app-frontend/src/helpers/skin-site-api.ts | 61 ++++ .../app-frontend/src/locales/en-US/index.json | 41 ++- .../app-frontend/src/locales/zh-CN/index.json | 41 ++- 6 files changed, 606 insertions(+), 2 deletions(-) create mode 100644 apps/app-frontend/src/components/ui/skin-site/admin/AdminAccessAuditPage.vue create mode 100644 apps/app-frontend/src/components/ui/skin-site/admin/AdminServersPage.vue diff --git a/apps/app-frontend/src/components/ui/skin-site/SkinSiteAdminView.vue b/apps/app-frontend/src/components/ui/skin-site/SkinSiteAdminView.vue index 498c808..0b03ac2 100644 --- a/apps/app-frontend/src/components/ui/skin-site/SkinSiteAdminView.vue +++ b/apps/app-frontend/src/components/ui/skin-site/SkinSiteAdminView.vue @@ -4,11 +4,13 @@ import { computed, ref } from 'vue' import { skinSiteUser } from '@/composables/skin-site-session' +import AdminAccessAuditPage from './admin/AdminAccessAuditPage.vue' import AdminBroadcastPage from './admin/AdminBroadcastPage.vue' import AdminDownloadsPage from './admin/AdminDownloadsPage.vue' import AdminLauncherPage from './admin/AdminLauncherPage.vue' import AdminPlayersPage from './admin/AdminPlayersPage.vue' import AdminReviewPage from './admin/AdminReviewPage.vue' +import AdminServersPage from './admin/AdminServersPage.vue' import AdminSummaryPage from './admin/AdminSummaryPage.vue' import AdminUsersPage from './admin/AdminUsersPage.vue' @@ -160,6 +162,8 @@ const current = ref('summary') class="min-w-0 flex-1 overflow-y-auto rounded-xl border border-solid border-surface-5 bg-bg-raised p-4" > + + diff --git a/apps/app-frontend/src/components/ui/skin-site/admin/AdminAccessAuditPage.vue b/apps/app-frontend/src/components/ui/skin-site/admin/AdminAccessAuditPage.vue new file mode 100644 index 0000000..b09a2e9 --- /dev/null +++ b/apps/app-frontend/src/components/ui/skin-site/admin/AdminAccessAuditPage.vue @@ -0,0 +1,273 @@ + + diff --git a/apps/app-frontend/src/components/ui/skin-site/admin/AdminServersPage.vue b/apps/app-frontend/src/components/ui/skin-site/admin/AdminServersPage.vue new file mode 100644 index 0000000..d48eb38 --- /dev/null +++ b/apps/app-frontend/src/components/ui/skin-site/admin/AdminServersPage.vue @@ -0,0 +1,188 @@ + + diff --git a/apps/app-frontend/src/helpers/skin-site-api.ts b/apps/app-frontend/src/helpers/skin-site-api.ts index 88c2c21..47f97b5 100644 --- a/apps/app-frontend/src/helpers/skin-site-api.ts +++ b/apps/app-frontend/src/helpers/skin-site-api.ts @@ -1391,3 +1391,64 @@ export function skinSiteUnbindDownloadCategory( method: 'POST', }) } + +export interface SkinSiteMapServer { + name: string + internal: boolean + maps: Array<{ name: string; url: string }> +} + +export function skinSiteMapServers(): Promise> { + return skinSiteRequest('maps') +} + +export function skinSiteSaveMapServers( + servers: SkinSiteMapServer[], +): Promise> { + return skinSiteRequest('maps', { method: 'PUT', body: servers }) +} + +export interface SkinSiteAccessAuditEntry { + id: number + subject: string + action: string + sourceIp: string + proxy: boolean + reportedPlayerIp: string | null + internalServer: boolean + count: number + firstAt: number + lastAt: number +} + +export interface SkinSiteAccessAuditPage { + items: SkinSiteAccessAuditEntry[] + nextCursor: number | null + retentionDays: number + recordLimit: number +} + +export function skinSiteAccessAudit(params?: { + before?: number + subject?: string + ip?: string + action?: string +}): Promise> { + const query = new URLSearchParams() + if (params) { + for (const [key, value] of Object.entries(params)) { + if (value !== undefined && value !== '') query.set(key, String(value)) + } + } + const suffix = query.toString() ? '?' + query.toString() : '' + return skinSiteRequest('admin/access-audit' + suffix) +} + +export function skinSiteUpdateAccessAuditSettings( + retentionDays: number, +): Promise> { + return skinSiteRequest<{ retentionDays: number }>('admin/access-audit/settings', { + method: 'PUT', + body: { retentionDays }, + }) +} diff --git a/apps/app-frontend/src/locales/en-US/index.json b/apps/app-frontend/src/locales/en-US/index.json index 6fb540e..db1b46e 100644 --- a/apps/app-frontend/src/locales/en-US/index.json +++ b/apps/app-frontend/src/locales/en-US/index.json @@ -11984,5 +11984,44 @@ "app.skin-site.admin.downloads.category-added": {"message": "Category added."}, "app.skin-site.admin.downloads.category-deleted": {"message": "Category deleted."}, "app.skin-site.admin.downloads.confirm-delete-item": {"message": "Delete “{name}”? This cannot be undone."}, - "app.skin-site.admin.downloads.confirm-delete-category": {"message": "Delete category “{name}”?"} + "app.skin-site.admin.downloads.confirm-delete-category": {"message": "Delete category “{name}”?"}, + "app.skin-site.admin.servers.title": {"message": "Map servers"}, + "app.skin-site.admin.servers.loading": {"message": "Loading…"}, + "app.skin-site.admin.servers.failed": {"message": "Could not load servers."}, + "app.skin-site.admin.servers.empty": {"message": "No map servers configured."}, + "app.skin-site.admin.servers.server-name": {"message": "Server name"}, + "app.skin-site.admin.servers.internal": {"message": "Internal"}, + "app.skin-site.admin.servers.maps": {"message": "Maps"}, + "app.skin-site.admin.servers.map-name": {"message": "Map name"}, + "app.skin-site.admin.servers.map-url": {"message": "Map URL"}, + "app.skin-site.admin.servers.add-map": {"message": "Add map"}, + "app.skin-site.admin.servers.remove-map": {"message": "Remove"}, + "app.skin-site.admin.servers.add-server": {"message": "Add server"}, + "app.skin-site.admin.servers.remove-server": {"message": "Remove server"}, + "app.skin-site.admin.servers.save": {"message": "Save changes"}, + "app.skin-site.admin.servers.saving": {"message": "Saving…"}, + "app.skin-site.admin.servers.saved": {"message": "Map servers saved."}, + "app.skin-site.admin.audit.title": {"message": "Access audit"}, + "app.skin-site.admin.audit.loading": {"message": "Loading…"}, + "app.skin-site.admin.audit.failed": {"message": "Could not load audit log."}, + "app.skin-site.admin.audit.empty": {"message": "No audit entries."}, + "app.skin-site.admin.audit.subject": {"message": "Subject"}, + "app.skin-site.admin.audit.action": {"message": "Action"}, + "app.skin-site.admin.audit.source-ip": {"message": "Source IP"}, + "app.skin-site.admin.audit.reported-ip": {"message": "Reported player IP"}, + "app.skin-site.admin.audit.count": {"message": "Count"}, + "app.skin-site.admin.audit.first-at": {"message": "First seen"}, + "app.skin-site.admin.audit.last-at": {"message": "Last seen"}, + "app.skin-site.admin.audit.proxy": {"message": "Proxy"}, + "app.skin-site.admin.audit.internal": {"message": "Internal"}, + "app.skin-site.admin.audit.search-subject": {"message": "Subject"}, + "app.skin-site.admin.audit.search-ip": {"message": "IP"}, + "app.skin-site.admin.audit.search-action": {"message": "Action"}, + "app.skin-site.admin.audit.search": {"message": "Search"}, + "app.skin-site.admin.audit.clear": {"message": "Clear"}, + "app.skin-site.admin.audit.load-more": {"message": "Load more"}, + "app.skin-site.admin.audit.retention": {"message": "Retention (days)"}, + "app.skin-site.admin.audit.record-limit": {"message": "Record limit"}, + "app.skin-site.admin.audit.save-retention": {"message": "Save"}, + "app.skin-site.admin.audit.retention-saved": {"message": "Retention updated."} } diff --git a/apps/app-frontend/src/locales/zh-CN/index.json b/apps/app-frontend/src/locales/zh-CN/index.json index 9c62c58..f5f3409 100644 --- a/apps/app-frontend/src/locales/zh-CN/index.json +++ b/apps/app-frontend/src/locales/zh-CN/index.json @@ -11815,5 +11815,44 @@ "app.skin-site.admin.downloads.category-added": {"message": "分类已添加。"}, "app.skin-site.admin.downloads.category-deleted": {"message": "分类已删除。"}, "app.skin-site.admin.downloads.confirm-delete-item": {"message": "确定删除「{name}」吗?此操作不可撤销。"}, - "app.skin-site.admin.downloads.confirm-delete-category": {"message": "确定删除分类「{name}」吗?"} + "app.skin-site.admin.downloads.confirm-delete-category": {"message": "确定删除分类「{name}」吗?"}, + "app.skin-site.admin.servers.title": {"message": "地图服务器"}, + "app.skin-site.admin.servers.loading": {"message": "加载中…"}, + "app.skin-site.admin.servers.failed": {"message": "无法加载服务器列表。"}, + "app.skin-site.admin.servers.empty": {"message": "尚未配置地图服务器。"}, + "app.skin-site.admin.servers.server-name": {"message": "服务器名称"}, + "app.skin-site.admin.servers.internal": {"message": "内部"}, + "app.skin-site.admin.servers.maps": {"message": "地图"}, + "app.skin-site.admin.servers.map-name": {"message": "地图名称"}, + "app.skin-site.admin.servers.map-url": {"message": "地图地址"}, + "app.skin-site.admin.servers.add-map": {"message": "添加地图"}, + "app.skin-site.admin.servers.remove-map": {"message": "移除"}, + "app.skin-site.admin.servers.add-server": {"message": "添加服务器"}, + "app.skin-site.admin.servers.remove-server": {"message": "删除服务器"}, + "app.skin-site.admin.servers.save": {"message": "保存更改"}, + "app.skin-site.admin.servers.saving": {"message": "保存中…"}, + "app.skin-site.admin.servers.saved": {"message": "地图服务器已保存。"}, + "app.skin-site.admin.audit.title": {"message": "登录与认证审计"}, + "app.skin-site.admin.audit.loading": {"message": "加载中…"}, + "app.skin-site.admin.audit.failed": {"message": "无法加载审计日志。"}, + "app.skin-site.admin.audit.empty": {"message": "没有审计记录。"}, + "app.skin-site.admin.audit.subject": {"message": "主体"}, + "app.skin-site.admin.audit.action": {"message": "操作"}, + "app.skin-site.admin.audit.source-ip": {"message": "来源 IP"}, + "app.skin-site.admin.audit.reported-ip": {"message": "上报玩家 IP"}, + "app.skin-site.admin.audit.count": {"message": "次数"}, + "app.skin-site.admin.audit.first-at": {"message": "首次出现"}, + "app.skin-site.admin.audit.last-at": {"message": "最近出现"}, + "app.skin-site.admin.audit.proxy": {"message": "代理"}, + "app.skin-site.admin.audit.internal": {"message": "内部"}, + "app.skin-site.admin.audit.search-subject": {"message": "主体"}, + "app.skin-site.admin.audit.search-ip": {"message": "IP"}, + "app.skin-site.admin.audit.search-action": {"message": "操作"}, + "app.skin-site.admin.audit.search": {"message": "搜索"}, + "app.skin-site.admin.audit.clear": {"message": "清空"}, + "app.skin-site.admin.audit.load-more": {"message": "加载更多"}, + "app.skin-site.admin.audit.retention": {"message": "保留天数"}, + "app.skin-site.admin.audit.record-limit": {"message": "记录上限"}, + "app.skin-site.admin.audit.save-retention": {"message": "保存"}, + "app.skin-site.admin.audit.retention-saved": {"message": "保留天数已更新。"} }