diff --git a/.yarn/versions/f94bc75b.yml b/.yarn/versions/f94bc75b.yml
new file mode 100644
index 0000000..85ee0e5
--- /dev/null
+++ b/.yarn/versions/f94bc75b.yml
@@ -0,0 +1,3 @@
+releases:
+ "@aoi-js/frontend": patch
+ "@aoi-js/server": patch
diff --git a/apps/frontend/src/components/contest/RanklistRenderer.vue b/apps/frontend/src/components/contest/RanklistRenderer.vue
index 7ae9527..c2005c8 100644
--- a/apps/frontend/src/components/contest/RanklistRenderer.vue
+++ b/apps/frontend/src/components/contest/RanklistRenderer.vue
@@ -64,9 +64,8 @@ import { ref, computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { RouterLink } from 'vue-router'
-import RanklistTopstars from '../contest/RanklistTopstars.vue'
-
import { useRanklistRenderer } from './RanklistRenderer'
+import RanklistTopstars from './RanklistTopstars.vue'
import PrincipalProfile from '@/components/utils/PrincipalProfile.vue'
import { renderMarkdown } from '@/utils/markdown'
@@ -85,3 +84,13 @@ const length = computed(() =>
const { admin, participantUrl, problemUrl, solutionUrl } = useRanklistRenderer()
+
+
diff --git a/apps/frontend/src/locales/en.yaml b/apps/frontend/src/locales/en.yaml
index b2736fe..19a5a52 100644
--- a/apps/frontend/src/locales/en.yaml
+++ b/apps/frontend/src/locales/en.yaml
@@ -128,6 +128,7 @@ term:
access-level: Access Level
rules: Rules
ip: IP
+ banned: Banned
common:
created-at: Created at
diff --git a/apps/frontend/src/locales/zh-Hans.yml b/apps/frontend/src/locales/zh-Hans.yml
index d02d1a3..2a58fdf 100644
--- a/apps/frontend/src/locales/zh-Hans.yml
+++ b/apps/frontend/src/locales/zh-Hans.yml
@@ -128,6 +128,7 @@ term:
access-level: 访问等级
rules: 规则
ip: IP 地址
+ banned: 封禁
common:
created-at: 创建于
diff --git a/apps/frontend/src/pages/org/[orgId]/contest/[contestId]/participant/[userId].vue b/apps/frontend/src/pages/org/[orgId]/contest/[contestId]/participant/[userId].vue
index e682dc9..5e8f4f6 100644
--- a/apps/frontend/src/pages/org/[orgId]/contest/[contestId]/participant/[userId].vue
+++ b/apps/frontend/src/pages/org/[orgId]/contest/[contestId]/participant/[userId].vue
@@ -7,6 +7,7 @@
+
@@ -26,7 +27,7 @@ import { useI18n } from 'vue-i18n'
import type { IContestDTO } from '@/components/contest/types'
import PrincipalProfile from '@/components/utils/PrincipalProfile.vue'
import SettingsEditor from '@/components/utils/SettingsEditor.vue'
-import { useContestCapability, useContestSettings } from '@/utils/contest/inject'
+import { useContestCapability } from '@/utils/contest/inject'
defineProps<{
orgId: string
diff --git a/apps/server/src/db/contest.ts b/apps/server/src/db/contest.ts
index 4ce9472..04ec488 100644
--- a/apps/server/src/db/contest.ts
+++ b/apps/server/src/db/contest.ts
@@ -42,6 +42,7 @@ export interface IContestParticipant {
createdAt: number
updatedAt: number
tags?: string[]
+ banned?: boolean
}
export interface IContestProblem {
diff --git a/apps/server/src/routes/contest/participant/index.ts b/apps/server/src/routes/contest/participant/index.ts
index fee9e84..dfa1f80 100644
--- a/apps/server/src/routes/contest/participant/index.ts
+++ b/apps/server/src/routes/contest/participant/index.ts
@@ -121,7 +121,8 @@ const contestParticipantAdminRoutes = defineRoutes(async (s) => {
userId: T.UUID()
}),
body: T.Object({
- tags: T.Optional(T.Array(T.String()))
+ tags: T.Optional(T.Array(T.String())),
+ banned: T.Optional(T.Boolean())
})
}
},
@@ -139,9 +140,4 @@ const contestParticipantAdminRoutes = defineRoutes(async (s) => {
return 0
}
)
-
- s.delete('/:userId', {}, async () => {
- // TODO: delete participant
- return s.httpErrors.notImplemented()
- })
})
diff --git a/apps/server/src/routes/contest/scoped.ts b/apps/server/src/routes/contest/scoped.ts
index 08b3265..fcbeb39 100644
--- a/apps/server/src/routes/contest/scoped.ts
+++ b/apps/server/src/routes/contest/scoped.ts
@@ -53,6 +53,8 @@ export const contestScopedRoutes = defineRoutes(async (s) => {
})
if (!participant) {
ensureCapability(capability, CONTEST_CAPS.CAP_ACCESS, s.httpErrors.forbidden())
+ } else if (participant.banned) {
+ ensureCapability(capability, CONTEST_CAPS.CAP_ADMIN, s.httpErrors.forbidden())
}
req.provide(kContestContext, {
_contestId: contestId,