add website config.
This commit is contained in:
@@ -4,8 +4,10 @@ import {
|
|||||||
BlankContest,
|
BlankContest,
|
||||||
BlankProblem,
|
BlankProblem,
|
||||||
Contest,
|
Contest,
|
||||||
|
Server,
|
||||||
TestcaseUploadedReturns,
|
TestcaseUploadedReturns,
|
||||||
User,
|
User,
|
||||||
|
WebsiteConfig,
|
||||||
} from "~/utils/types"
|
} from "~/utils/types"
|
||||||
|
|
||||||
export function getBaseInfo() {
|
export function getBaseInfo() {
|
||||||
@@ -149,3 +151,27 @@ export function addProblemForContest(
|
|||||||
display_id: displayID,
|
display_id: displayID,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getWebsite() {
|
||||||
|
return http.get<WebsiteConfig>("admin/website")
|
||||||
|
}
|
||||||
|
|
||||||
|
export function editWebsite(data: WebsiteConfig) {
|
||||||
|
return http.post("admin/website", data)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function listInvalidTestcases() {
|
||||||
|
return http.get("admin/prune_test_case")
|
||||||
|
}
|
||||||
|
|
||||||
|
export function pruneInvalidTestcases(id?: string) {
|
||||||
|
return http.delete("admin/prune_test_case", { params: { id } })
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getJudgeServer() {
|
||||||
|
return http.get<{ token: string; servers: Server[] }>("admin/judge_server")
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteJudgeServer(hostname: string) {
|
||||||
|
return http.delete("admin/judge_server", { params: { hostname } })
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { formatISO } from "date-fns"
|
import { formatISO } from "date-fns"
|
||||||
import TextEditor from "~/shared/TextEditor.vue"
|
import TextEditor from "~/shared/TextEditor.vue"
|
||||||
import { CONTEST_TYPE } from "~/utils/constants"
|
|
||||||
import { BlankContest } from "~/utils/types"
|
import { BlankContest } from "~/utils/types"
|
||||||
import { createContest, editContest, getContest } from "../api"
|
import { createContest, editContest, getContest } from "../api"
|
||||||
|
|
||||||
|
|||||||
@@ -118,7 +118,11 @@ watch(query, listProblems, { deep: true })
|
|||||||
<n-button v-if="isContestProblemList" @click="createContestProblem">
|
<n-button v-if="isContestProblemList" @click="createContestProblem">
|
||||||
新建比赛题目
|
新建比赛题目
|
||||||
</n-button>
|
</n-button>
|
||||||
<n-button v-if="isContestProblemList" @click="selectProblems">
|
<n-button
|
||||||
|
v-if="isContestProblemList"
|
||||||
|
type="primary"
|
||||||
|
@click="selectProblems"
|
||||||
|
>
|
||||||
从题库中选择
|
从题库中选择
|
||||||
</n-button>
|
</n-button>
|
||||||
<n-input v-model:value="query.keyword" placeholder="输入标题关键字" />
|
<n-input v-model:value="query.keyword" placeholder="输入标题关键字" />
|
||||||
|
|||||||
@@ -1,7 +1,233 @@
|
|||||||
<script setup lang="ts"></script>
|
<script setup lang="ts">
|
||||||
|
import { DataTableColumn, NButton, NTag } from "naive-ui"
|
||||||
|
import {
|
||||||
|
editWebsite,
|
||||||
|
getJudgeServer,
|
||||||
|
deleteJudgeServer,
|
||||||
|
getWebsite,
|
||||||
|
listInvalidTestcases,
|
||||||
|
pruneInvalidTestcases,
|
||||||
|
} from "../api"
|
||||||
|
import { Server } from "~/utils/types"
|
||||||
|
import { parseTime } from "~/utils/functions"
|
||||||
|
|
||||||
|
const message = useMessage()
|
||||||
|
|
||||||
|
const columns: DataTableColumn<any>[] = [
|
||||||
|
{ title: "上次修改时间", key: "create_time" },
|
||||||
|
{ title: "ID", key: "id" },
|
||||||
|
{
|
||||||
|
title: "选项",
|
||||||
|
key: "delete",
|
||||||
|
render: (row) =>
|
||||||
|
h(
|
||||||
|
NButton,
|
||||||
|
{ size: "small", onClick: () => deleteTestcase(row.id) },
|
||||||
|
() => "删除"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const statusMap: {
|
||||||
|
[key in "normal" | "abnormal"]: { color: "primary" | "error"; label: string }
|
||||||
|
} = {
|
||||||
|
normal: { color: "primary", label: "正常" },
|
||||||
|
abnormal: { color: "error", label: "异常" },
|
||||||
|
}
|
||||||
|
|
||||||
|
const serverColumns: DataTableColumn<Server>[] = [
|
||||||
|
{
|
||||||
|
title: "状态",
|
||||||
|
key: "status",
|
||||||
|
render: (row) =>
|
||||||
|
h(
|
||||||
|
NTag,
|
||||||
|
{ type: statusMap[row.status].color, size: "small" },
|
||||||
|
() => statusMap[row.status].label
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "选项",
|
||||||
|
key: "options",
|
||||||
|
render: (row) =>
|
||||||
|
h(
|
||||||
|
NButton,
|
||||||
|
{
|
||||||
|
type: "primary",
|
||||||
|
size: "small",
|
||||||
|
disabled: row.status === "normal",
|
||||||
|
onClick: () => delJudgeServer(row.hostname),
|
||||||
|
},
|
||||||
|
() => "删除"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{ title: "主机", key: "hostname", width: 130 },
|
||||||
|
{
|
||||||
|
title: "内存占用",
|
||||||
|
key: "memory_usage",
|
||||||
|
render: (row) => row.memory_usage + "%",
|
||||||
|
width: 80,
|
||||||
|
},
|
||||||
|
{ title: "IP", key: "ip", width: 100 },
|
||||||
|
{ title: "判题机版本", key: "judger_version" },
|
||||||
|
{ title: "服务器 URL", key: "service_url" },
|
||||||
|
{
|
||||||
|
title: "上一次心跳",
|
||||||
|
key: "last_heartbeat",
|
||||||
|
render: (row) => parseTime(row.last_heartbeat, "YYYY-MM-DD hh:mm:ss"),
|
||||||
|
width: 120,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "创建时间",
|
||||||
|
key: "create_time",
|
||||||
|
render: (row) => parseTime(row.create_time, "YYYY-MM-DD hh:mm:ss"),
|
||||||
|
width: 120,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const testcases = ref([])
|
||||||
|
const token = ref("")
|
||||||
|
const servers = ref<Server[]>([])
|
||||||
|
|
||||||
|
const websiteConfig = reactive({
|
||||||
|
website_base_url: "https://oj.hyyz.izhai.net",
|
||||||
|
website_name: "徐越的判题狗",
|
||||||
|
website_name_shortcut: "徐越的判题狗",
|
||||||
|
website_footer: "所有权归属于徐越,感谢青岛大学开源 OJ 系统,感谢开源社区",
|
||||||
|
allow_register: true,
|
||||||
|
submission_list_show_all: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
async function getWebsiteConfig() {
|
||||||
|
const res = await getWebsite()
|
||||||
|
websiteConfig.website_base_url = res.data.website_base_url
|
||||||
|
websiteConfig.website_name = res.data.website_name
|
||||||
|
websiteConfig.website_name_shortcut = res.data.website_name_shortcut
|
||||||
|
websiteConfig.website_footer = res.data.website_footer
|
||||||
|
websiteConfig.allow_register = res.data.allow_register
|
||||||
|
websiteConfig.submission_list_show_all = res.data.submission_list_show_all
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveWebsiteConfig() {
|
||||||
|
await editWebsite(websiteConfig)
|
||||||
|
message.success("网站配置保存成功")
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteTestcase(id?: string) {
|
||||||
|
await pruneInvalidTestcases(id)
|
||||||
|
message.success("删除成功")
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getTestcases() {
|
||||||
|
const res = await listInvalidTestcases()
|
||||||
|
testcases.value = res.data
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getJudgeServerData() {
|
||||||
|
const res = await getJudgeServer()
|
||||||
|
token.value = res.data.token
|
||||||
|
servers.value = res.data.servers
|
||||||
|
}
|
||||||
|
|
||||||
|
async function delJudgeServer(hostname: string) {
|
||||||
|
await deleteJudgeServer(hostname)
|
||||||
|
message.success("删除成功")
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getWebsiteConfig()
|
||||||
|
getTestcases()
|
||||||
|
getJudgeServerData()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>conf</div>
|
<n-card class="box">
|
||||||
|
<template #header>
|
||||||
|
<n-space align="center">
|
||||||
|
网站设置
|
||||||
|
<n-button type="primary" size="small" @click="saveWebsiteConfig">
|
||||||
|
保存
|
||||||
|
</n-button>
|
||||||
|
</n-space>
|
||||||
|
</template>
|
||||||
|
<n-form inline label-placement="left">
|
||||||
|
<n-form-item label="网站 URL">
|
||||||
|
<n-input class="url" v-model:value="websiteConfig.website_base_url" />
|
||||||
|
</n-form-item>
|
||||||
|
<n-form-item label="网站名">
|
||||||
|
<n-input v-model:value="websiteConfig.website_name" />
|
||||||
|
</n-form-item>
|
||||||
|
<n-form-item label="网站简称">
|
||||||
|
<n-input v-model:value="websiteConfig.website_name_shortcut" />
|
||||||
|
</n-form-item>
|
||||||
|
</n-form>
|
||||||
|
<n-form label-placement="left">
|
||||||
|
<n-form-item label="页脚">
|
||||||
|
<n-input v-model:value="websiteConfig.website_footer" />
|
||||||
|
</n-form-item>
|
||||||
|
</n-form>
|
||||||
|
<n-space align="center">
|
||||||
|
<n-space align="center">
|
||||||
|
<span>是否允许注册</span>
|
||||||
|
<n-switch v-model:value="websiteConfig.allow_register" />
|
||||||
|
</n-space>
|
||||||
|
<n-space align="center">
|
||||||
|
<span>显示全部题目的提交</span>
|
||||||
|
<n-switch v-model:value="websiteConfig.submission_list_show_all" />
|
||||||
|
</n-space>
|
||||||
|
</n-space>
|
||||||
|
</n-card>
|
||||||
|
<n-card class="box" title="判题服务器">
|
||||||
|
<div class="serverToken">
|
||||||
|
接口凭证 <n-tag size="small">{{ token }}</n-tag>
|
||||||
|
</div>
|
||||||
|
<n-data-table
|
||||||
|
:single-line="false"
|
||||||
|
striped
|
||||||
|
size="small"
|
||||||
|
:columns="serverColumns"
|
||||||
|
:data="servers"
|
||||||
|
/>
|
||||||
|
</n-card>
|
||||||
|
<n-card class="box">
|
||||||
|
<template #header>
|
||||||
|
<n-space align="center">
|
||||||
|
无效的测试用例
|
||||||
|
<n-button
|
||||||
|
v-if="testcases.length"
|
||||||
|
size="small"
|
||||||
|
type="primary"
|
||||||
|
@click="() => deleteTestcase()"
|
||||||
|
>
|
||||||
|
全部删除
|
||||||
|
</n-button>
|
||||||
|
</n-space>
|
||||||
|
</template>
|
||||||
|
<n-data-table
|
||||||
|
striped
|
||||||
|
size="small"
|
||||||
|
class="table"
|
||||||
|
:columns="columns"
|
||||||
|
:data="testcases"
|
||||||
|
/>
|
||||||
|
</n-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped>
|
||||||
|
.url {
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table {
|
||||||
|
width: 40%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.serverToken {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ export function getTagColor(
|
|||||||
}[tag]
|
}[tag]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 2023-04-03T02:43:28.673156Z
|
||||||
export function parseTime(utc: Date | string, format = "YYYY年M月D日") {
|
export function parseTime(utc: Date | string, format = "YYYY年M月D日") {
|
||||||
const time = useDateFormat(utc, format, { locales: "zh-CN" })
|
const time = useDateFormat(utc, format, { locales: "zh-CN" })
|
||||||
return time.value
|
return time.value
|
||||||
|
|||||||
@@ -312,3 +312,28 @@ export interface ContestRank {
|
|||||||
submission_info: { [key: string]: SubmissionInfo }
|
submission_info: { [key: string]: SubmissionInfo }
|
||||||
contest: number
|
contest: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface WebsiteConfig {
|
||||||
|
website_base_url: string
|
||||||
|
website_name: string
|
||||||
|
website_name_shortcut: string
|
||||||
|
website_footer: string
|
||||||
|
allow_register: boolean
|
||||||
|
submission_list_show_all: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Server {
|
||||||
|
id: number
|
||||||
|
status: "abnormal" | "normal"
|
||||||
|
hostname: string
|
||||||
|
ip: string
|
||||||
|
judger_version: string
|
||||||
|
cpu_core: number
|
||||||
|
memory_usage: number
|
||||||
|
cpu_usage: number
|
||||||
|
last_heartbeat: Date
|
||||||
|
create_time: Date
|
||||||
|
task_number: number
|
||||||
|
service_url: string
|
||||||
|
is_disabled: boolean
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user