Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
8fd3346
PM-5923 - fix placement for winners
vas3a Aug 19, 2026
d90b3d4
PM-5923 - MyReview - make sure only "placement" type challenge winner…
vas3a Aug 19, 2026
7ab1485
Merge pull request #321 from topcoder-platform/hotfix-review-opportun…
vas3a Aug 19, 2026
0892954
PM-5601 - submission fingerprinting
vas3a Aug 19, 2026
c41445a
Merge pull request #320 from topcoder-platform/PM-5923_winners-placement
vas3a Aug 19, 2026
92b5584
Merge pull request #323 from topcoder-platform/PM-5601_submission-fin…
vas3a Aug 20, 2026
3a5cba8
PM-5955 - after first submission, prevent AIConfig updates
vas3a Aug 21, 2026
ce94009
PM-5955 - update message
vas3a Aug 21, 2026
cd019ce
Merge pull request #324 from topcoder-platform/PM-5955_prevent-ai-con…
vas3a Aug 21, 2026
df63a9f
Add logs around sha256 computation
vas3a Aug 21, 2026
a38d262
PR feedback
vas3a Aug 24, 2026
c03a753
Merge pull request #325 from topcoder-platform/log-sha256-traces
vas3a Aug 24, 2026
015a595
PM-5601 - compute file hash from s3 url
vas3a Aug 24, 2026
abdc953
Ensure filesize accuracy
vas3a Aug 24, 2026
26f7423
Merge pull request #327 from topcoder-platform/PM-5601_submissions-sh…
vas3a Aug 24, 2026
c057701
PM-5961: block submitters from deleting submissions after their submi…
jmgasper Aug 25, 2026
15ca00f
PM-5368: allow CANCELLED Marathon Match test status through to members
jmgasper Aug 25, 2026
92fad49
Merge pull request #329 from topcoder-platform/PM-5961
jmgasper Aug 25, 2026
464cb99
Merge pull request #330 from topcoder-platform/PM-5368
jmgasper Aug 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions packages/review-prisma-client/generated/edge.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions packages/review-prisma-client/generated/index-browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,7 @@ exports.Prisma.SubmissionScalarFieldEnum = {
fileType: 'fileType',
esId: 'esId',
submittedDate: 'submittedDate',
sha256Hash: 'sha256Hash',
createdAt: 'createdAt',
createdBy: 'createdBy',
updatedAt: 'updatedAt',
Expand Down
77 changes: 76 additions & 1 deletion packages/review-prisma-client/generated/index.d.ts

Large diffs are not rendered by default.

14 changes: 8 additions & 6 deletions packages/review-prisma-client/generated/index.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/review-prisma-client/generated/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "prisma-client-e90aa6b631027b154a2d1a0332313bbe8b7b2f3ddabbec274a5b034f4c829bc1",
"name": "prisma-client-36e90377f45cb35d9ca8cccf526d6146684b8db7b0f493bbbc604756de816d2c",
"main": "index.js",
"types": "index.d.ts",
"browser": "default.js",
Expand Down
4 changes: 4 additions & 0 deletions packages/review-prisma-client/generated/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,9 @@ model submission {
esId String? @db.Uuid
submittedDate DateTime?

/// Lowercase hex SHA-256 digest of the uploaded file contents; null for URL-only submissions.
sha256Hash String? @db.VarChar(64)

createdAt DateTime @default(now())
createdBy String?
updatedAt DateTime? @updatedAt
Expand All @@ -464,6 +467,7 @@ model submission {
@@index([legacySubmissionId])
@@index([challengeId, memberId, status])
@@index([submittedDate])
@@index([sha256Hash])
}

/// Durable, idempotent processing state for a design submission preview.
Expand Down
14 changes: 8 additions & 6 deletions packages/review-prisma-client/generated/wasm.js

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-- Add SHA-256 content hash on submission for uploaded file identification.
ALTER TABLE "submission"
ADD COLUMN "sha256Hash" VARCHAR(64);

CREATE INDEX "submission_sha256Hash_idx" ON "submission"("sha256Hash");
4 changes: 4 additions & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,9 @@ model submission {
esId String? @db.Uuid
submittedDate DateTime?

/// Lowercase hex SHA-256 digest of the submission file contents; null for non-file submissions.
sha256Hash String? @db.VarChar(64)

createdAt DateTime @default(now())
createdBy String?
updatedAt DateTime? @updatedAt
Expand All @@ -464,6 +467,7 @@ model submission {
@@index([legacySubmissionId])
@@index([challengeId, memberId, status])
@@index([submittedDate])
@@index([sha256Hash])
}

/// Durable, idempotent processing state for a design submission preview.
Expand Down
3 changes: 2 additions & 1 deletion src/api/ai-review-config/ai-review-config.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ export class AiReviewConfigService {
});
if (count > 0) {
throw new ConflictException(
`Cannot create AI review config: challenge ${challengeId} already has submissions.`,
`Cannot create or update AI review config: challenge ${challengeId} already has submissions.`,
);
}
}
Expand Down Expand Up @@ -545,6 +545,7 @@ export class AiReviewConfigService {
const challengeId = config.challengeId;

await this.validateCanManageConfigForChallenge(challengeId, authUser);
await this.validateNoSubmissionsExistForChallenge(challengeId);
await this.validateChallengeNotCompleted(challengeId);
await this.validateNoDecisionsForConfig(id);
await this.validateNoAiRunsExistForChallenge(challengeId);
Expand Down
3 changes: 3 additions & 0 deletions src/api/my-review/myReview.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,7 @@ export class MyReviewService {
) AS winners
FROM challenges."ChallengeWinner" w
WHERE w."challengeId" = c.id
AND w.type = 'PLACEMENT'
) cw ON TRUE
`,
);
Expand Down Expand Up @@ -857,6 +858,7 @@ export class MyReviewService {
) AS winners
FROM challenges."ChallengeWinner" w
WHERE w."challengeId" = bp."challengeId"
AND w.type = 'PLACEMENT'
) cw ON TRUE
LEFT JOIN LATERAL (
SELECT
Expand Down Expand Up @@ -968,6 +970,7 @@ export class MyReviewService {
) AS winners
FROM challenges."ChallengeWinner" w
WHERE w."challengeId" = bp."challengeId"
AND w.type = 'PLACEMENT'
) cw ON TRUE
LEFT JOIN LATERAL (
SELECT
Expand Down
19 changes: 16 additions & 3 deletions src/api/submission/submission.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,16 @@ export class SubmissionController {
@UploadedFile() file: Express.Multer.File,
@Body() body: SubmissionRequestDto,
): Promise<SubmissionResponseDto> {
console.log(
this.logger.debug(
`Creating submission with request body: ${JSON.stringify(body)}`,
);
// Diagnostic for sha256Hash: a null digest almost always means no multipart file arrived
this.logger.debug(
`Creating submission multipart file: present=${!!file}, fieldname=${file?.fieldname ?? 'n/a'}, ` +
`originalname=${file?.originalname ?? 'n/a'}, size=${file?.size ?? 'n/a'}, ` +
`bufferBytes=${Buffer.isBuffer(file?.buffer) ? file.buffer.length : 'n/a'}, ` +
`contentType=${req.headers['content-type'] ?? 'n/a'}`,
);
const authUser: JwtUser = req['user'] as JwtUser;
return this.service.createSubmission(authUser, body, file);
}
Expand Down Expand Up @@ -473,8 +480,10 @@ export class SubmissionController {
@Scopes(Scope.DeleteSubmission)
@HttpCode(HttpStatus.NO_CONTENT)
@ApiOperation({
summary: 'Delete a submission',
description: 'Roles: Admin, User | Scopes: delete:submission',
summary:
'Delete a submission (submitters only while the phase that created it is open)',
description:
'Roles: Admin, User | Scopes: delete:submission. Submitters can only delete their own submission while the matching submission phase (Submission, Checkpoint Submission, or Final Fix) is still open. Admins and M2M tokens are not restricted by the phase window.',
})
@ApiParam({
name: 'submissionId',
Expand All @@ -484,6 +493,10 @@ export class SubmissionController {
status: 204,
description: 'Submission deleted successfully.',
})
@ApiResponse({
status: 400,
description: 'The submission phase is already closed.',
})
@ApiResponse({ status: 403, description: 'Forbidden.' })
@ApiResponse({ status: 404, description: 'Submission not found.' })
async deleteSubmission(
Expand Down
Loading
Loading