feat: mentor workspace files end-to-end with secure presign

Adds generateMentorObjectKey helper producing
<projectName>/mentorship/<timestamp>-<file>. Replaces the
client-supplied bucket/objectKey on workspaceUploadFile with an
HMAC-signed upload token that binds bucket, objectKey, uploader,
and a 1h expiry — paths can no longer be forged from the client.

Adds workspaceGetUploadUrl, workspaceGetFiles,
workspaceGetFileDownloadUrl, workspaceDeleteFile procedures with
mentor-or-team-member auth. Builds <WorkspaceFilesPanel> and
wires it into the mentor workspace Files tab and the applicant
/applicant/mentor page. Replaces the file-promotion-panel mock
array with a real workspaceGetFiles query.

Tests cover token sign/verify (5), key construction (5), and
end-to-end procedure flow including auth + tampered tokens (7).

Spec: docs/superpowers/specs/2026-04-28-mentor-round-readiness-design.md §F.1
Plan: docs/superpowers/plans/2026-04-28-pr2-mentor-workspace-files.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matt
2026-04-28 13:33:18 +02:00
parent dd48db5eea
commit 2e7b545a1b
11 changed files with 699 additions and 30 deletions

View File

@@ -11,6 +11,7 @@ import {
} from '@/components/ui/card'
import { Skeleton } from '@/components/ui/skeleton'
import { MentorChat } from '@/components/shared/mentor-chat'
import { WorkspaceFilesPanel } from '@/components/mentor/workspace-files-panel'
import {
MessageSquare,
UserCircle,
@@ -133,6 +134,14 @@ export default function ApplicantMentorPage() {
</CardContent>
</Card>
)}
{/* Files */}
{dashboardData?.project?.mentorAssignment?.id && (
<WorkspaceFilesPanel
mentorAssignmentId={dashboardData.project.mentorAssignment.id}
asApplicant
/>
)}
</div>
)
}