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,