diff --git a/.yarn/versions/ecb1a3b6.yml b/.yarn/versions/ecb1a3b6.yml
new file mode 100644
index 0000000..85ee0e5
--- /dev/null
+++ b/.yarn/versions/ecb1a3b6.yml
@@ -0,0 +1,3 @@
+releases:
+ "@aoi-js/frontend": patch
+ "@aoi-js/server": patch
diff --git a/apps/frontend/src/components/solution/SolutionList.vue b/apps/frontend/src/components/solution/SolutionList.vue
index 764eef0..606b92e 100644
--- a/apps/frontend/src/components/solution/SolutionList.vue
+++ b/apps/frontend/src/components/solution/SolutionList.vue
@@ -50,6 +50,7 @@ import { computed, ref } from 'vue'
import { useAppState } from '@/stores/app'
import { useContestProblemTitle } from '@/utils/contest/problem/inject'
import type { ISolutionDTO } from './types'
+import { useRouteQuery } from '@vueuse/router'
const { t } = useI18n()
const app = useAppState()
@@ -78,7 +79,7 @@ const headersProblem = [
{ title: t('common.submitted-at'), key: 'submittedAt', align: 'start', sortable: false }
] as const
-const userId = ref(app.userId as string)
+const userId = useRouteQuery('userId')
const {
page,
diff --git a/apps/frontend/src/components/utils/RanklistViewer.vue b/apps/frontend/src/components/utils/RanklistViewer.vue
index b153777..5a65159 100644
--- a/apps/frontend/src/components/utils/RanklistViewer.vue
+++ b/apps/frontend/src/components/utils/RanklistViewer.vue
@@ -22,7 +22,10 @@
{{ man.rank }} |
-
+
@@ -55,11 +58,17 @@ import { renderMarkdown } from '@/utils/md'
import PrincipalProfile from '@/components/utils/PrincipalProfile.vue'
import { useI18n } from 'vue-i18n'
import RanklistTopstars from '../contest/RanklistTopstars.vue'
+import { useContestCapability, useContestData } from '@/utils/contest/inject'
const { t } = useI18n()
const props = defineProps<{
endpoint: string
}>()
+
+const admin = useContestCapability('admin')
+const contest = useContestData()
+const participantUrl = (userId: string) =>
+ `/org/${contest.value.orgId}/contest/${contest.value._id}/participant/${userId}`
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 1dfc8b7..30419c2 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
@@ -2,18 +2,27 @@
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+en:
+ goto-solutions: Go to solutions
+zh-Hans:
+ goto-solutions: 查看提交
+
diff --git a/apps/server/src/routes/contest/admin.ts b/apps/server/src/routes/contest/admin.ts
index 90a47e3..9ae57d2 100644
--- a/apps/server/src/routes/contest/admin.ts
+++ b/apps/server/src/routes/contest/admin.ts
@@ -60,21 +60,23 @@ export const contestAdminRoutes = defineRoutes(async (s) => {
}
},
async (req) => {
- const { modifiedCount } = await solutions.updateOne(
+ const { modifiedCount } = await solutions.updateMany(
{
contestId: req.inject(kContestContext)._contestId,
state: SolutionState.CREATED
},
- {
- $set: {
- state: SolutionState.PENDING,
- submittedAt: req._now,
- score: 0,
- status: '',
- metrics: {},
- message: ''
+ [
+ {
+ $set: {
+ state: SolutionState.PENDING,
+ submittedAt: { $convert: { input: '$$NOW', to: 'double' } },
+ score: 0,
+ status: '',
+ metrics: {},
+ message: ''
+ }
}
- },
+ ],
{ ignoreUndefined: true }
)
return { modifiedCount }
@@ -120,13 +122,15 @@ export const contestAdminRoutes = defineRoutes(async (s) => {
_id: req.inject(kContestContext)._contestId,
ranklists: { $exists: true, $ne: [] }
},
- {
- $set: {
- ranklistUpdatedAt: req._now,
- ranklistState: ContestRanklistState.INVALID
+ [
+ {
+ $set: {
+ ranklistUpdatedAt: { $convert: { input: '$$NOW', to: 'double' } },
+ ranklistState: ContestRanklistState.INVALID
+ }
},
- $unset: req.body.resetRunner ? { ranklistRunnerId: 1 } : {}
- },
+ ...(req.body.resetRunner ? [{ $unset: ['ranklistRunnerId'] }] : [])
+ ],
{ ignoreUndefined: true }
)
return 0
diff --git a/apps/server/src/routes/contest/participant/index.ts b/apps/server/src/routes/contest/participant/index.ts
index 5c8cb39..7040276 100644
--- a/apps/server/src/routes/contest/participant/index.ts
+++ b/apps/server/src/routes/contest/participant/index.ts
@@ -127,10 +127,14 @@ const contestParticipantAdminRoutes = defineRoutes(async (s) => {
async (req) => {
const ctx = req.inject(kContestContext)
const userId = new BSON.UUID(req.params.userId)
- await contestParticipants.updateOne(
- { contestId: ctx._contestId, userId },
- { $set: { tags: req.body.tags, updatedAt: req._now } }
- )
+ await contestParticipants.updateOne({ contestId: ctx._contestId, userId }, [
+ {
+ $set: {
+ tags: req.body.tags,
+ updatedAt: { $convert: { input: '$$NOW', to: 'double' } }
+ }
+ }
+ ])
return 0
}
)
diff --git a/apps/server/src/routes/contest/problem/index.ts b/apps/server/src/routes/contest/problem/index.ts
index 9d115ef..1914b31 100644
--- a/apps/server/src/routes/contest/problem/index.ts
+++ b/apps/server/src/routes/contest/problem/index.ts
@@ -264,6 +264,8 @@ const problemViewRoutes = defineRoutes(async (s) => {
metrics: {},
status: '',
message: '',
+ // createdAt is only a reference time,
+ // so use local time here
createdAt: req._now
})
const uploadUrl = await getUploadUrl(oss, solutionDataKey(insertedId), {
diff --git a/apps/server/src/routes/contest/solution/index.ts b/apps/server/src/routes/contest/solution/index.ts
index 844b715..7943426 100644
--- a/apps/server/src/routes/contest/solution/index.ts
+++ b/apps/server/src/routes/contest/solution/index.ts
@@ -52,16 +52,18 @@ const solutionScopedRoutes = defineRoutes(async (s) => {
userId: admin ? undefined : req.user.userId,
state: admin ? undefined : SolutionState.CREATED
},
- {
- $set: {
- state: SolutionState.PENDING,
- submittedAt: req._now,
- score: 0,
- status: '',
- metrics: {},
- message: ''
+ [
+ {
+ $set: {
+ state: SolutionState.PENDING,
+ submittedAt: { $convert: { input: '$$NOW', to: 'double' } },
+ score: 0,
+ status: '',
+ metrics: {},
+ message: ''
+ }
}
- },
+ ],
{ ignoreUndefined: true }
)
if (modifiedCount === 0) return rep.notFound()
diff --git a/apps/server/src/routes/problem/solution.ts b/apps/server/src/routes/problem/solution.ts
index c8a7d03..89e707f 100644
--- a/apps/server/src/routes/problem/solution.ts
+++ b/apps/server/src/routes/problem/solution.ts
@@ -33,6 +33,8 @@ const solutionScopedRoutes = defineRoutes(async (s) => {
{
$set: {
state: SolutionState.PENDING,
+ // Problem solutions are not synced with rankers,
+ // so we can use local time here
submittedAt: req._now,
score: 0,
status: '',
diff --git a/apps/server/src/routes/runner/solution.ts b/apps/server/src/routes/runner/solution.ts
index d65ca20..0cdb729 100644
--- a/apps/server/src/routes/runner/solution.ts
+++ b/apps/server/src/routes/runner/solution.ts
@@ -101,7 +101,6 @@ const runnerTaskRoutes = defineRoutes(async (s) => {
}
},
async (req, rep) => {
- const now = req._now
const ctx = req.inject(kRunnerSolutionContext)
const value = await solutions.findOneAndUpdate(
{
@@ -109,7 +108,9 @@ const runnerTaskRoutes = defineRoutes(async (s) => {
taskId: ctx._taskId,
state: { $in: [SolutionState.QUEUED, SolutionState.RUNNING] }
},
- { $set: { state: SolutionState.COMPLETED, completedAt: now } },
+ // Since completedAt is only for reference,
+ // use local time here
+ { $set: { state: SolutionState.COMPLETED, completedAt: req._now } },
{ projection: { userId: 1, problemId: 1, contestId: 1, score: 1, status: 1 } }
)
if (!value) return rep.conflict()
@@ -120,17 +121,19 @@ const runnerTaskRoutes = defineRoutes(async (s) => {
contestId: value.contestId,
[`results.${value.problemId}.lastSolutionId`]: value._id
},
- {
- $set: {
- [`results.${value.problemId}.lastSolution`]: {
- _id: value._id,
- score: value.score,
- status: value.status,
- completedAt: now
- },
- updatedAt: now
+ [
+ {
+ $set: {
+ [`results.${value.problemId}.lastSolution`]: {
+ _id: value._id,
+ score: value.score,
+ status: value.status,
+ completedAt: req._now
+ },
+ updatedAt: { $convert: { input: '$$NOW', to: 'double' } }
+ }
}
- }
+ ]
)
if (modifiedCount) {
// update contest ranklist state
@@ -139,12 +142,14 @@ const runnerTaskRoutes = defineRoutes(async (s) => {
_id: value.contestId,
ranklists: { $exists: true, $ne: [] }
},
- {
- $set: {
- ranklistUpdatedAt: now,
- ranklistState: ContestRanklistState.INVALID
+ [
+ {
+ $set: {
+ ranklistUpdatedAt: { $convert: { input: '$$NOW', to: 'double' } },
+ ranklistState: ContestRanklistState.INVALID
+ }
}
- }
+ ]
)
}
} else {
|