Skip to content

Commit 2d8f66c

Browse files
committed
fix(profile-editor): render legacy file-manager ids via public URL fallback
1 parent a535fcf commit 2d8f66c

4 files changed

Lines changed: 46 additions & 15 deletions

File tree

platforms/profile-editor/client/src/lib/components/profile/DocumentsSection.svelte

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
<script lang="ts">
22
import { updateProfile } from '$lib/stores/profile';
3-
import { uploadFile } from '$lib/utils/file-manager';
3+
import { resolveAssetUrl, uploadFile } from '$lib/utils/file-manager';
44
import { toast } from 'svelte-sonner';
55
import { Card, CardContent, CardHeader, CardTitle } from '@metastate-foundation/ui/card';
66
import { Button } from '@metastate-foundation/ui/button';
77
import { Progress } from '@metastate-foundation/ui/progress';
88
99
let { ename, cvFileId, videoIntroFileId, editable = false }: { ename: string; cvFileId?: string; videoIntroFileId?: string; editable?: boolean } = $props();
1010
11+
// New uploads store a public eVault-blob URL; legacy profiles stored a bare
12+
// file-manager id. resolveAssetUrl handles both so old CVs/videos still render.
13+
let cvUrl = $derived(resolveAssetUrl(cvFileId));
14+
let videoUrl = $derived(resolveAssetUrl(videoIntroFileId));
15+
1116
let uploadingCv = $state(false);
1217
let uploadingVideo = $state(false);
1318
let cvProgress = $state(0);
@@ -87,20 +92,20 @@
8792
<h3 class="text-sm font-medium text-foreground">CV / Resume</h3>
8893
</div>
8994

90-
{#if cvFileId}
95+
{#if cvUrl}
9196
<div class="mb-2 overflow-hidden rounded border">
9297
<object
93-
data={cvFileId}
98+
data={cvUrl}
9499
type="application/pdf"
95100
class="h-48 w-full"
96101
>
97-
<a href={cvFileId} target="_blank" rel="noopener noreferrer" class="flex items-center justify-center h-48 text-sm text-muted-foreground hover:underline">
102+
<a href={cvUrl} target="_blank" rel="noopener noreferrer" class="flex items-center justify-center h-48 text-sm text-muted-foreground hover:underline">
98103
Download CV
99104
</a>
100105
</object>
101106
</div>
102107
<div class="flex items-center gap-2">
103-
<Button variant="link" size="sm" href={cvFileId} class="h-auto p-0">Download CV</Button>
108+
<Button variant="link" size="sm" href={cvUrl} class="h-auto p-0">Download CV</Button>
104109
{#if editable}
105110
<Button variant="link" size="sm" class="h-auto p-0 text-destructive" onclick={removeCv}>Remove</Button>
106111
{/if}
@@ -131,20 +136,20 @@
131136
<h3 class="text-sm font-medium text-foreground">Video Introduction</h3>
132137
</div>
133138

134-
{#if videoIntroFileId}
139+
{#if videoUrl}
135140
<div class="mb-2 overflow-hidden rounded border">
136141
<!-- svelte-ignore a11y_media_has_caption -->
137142
<video
138-
src={videoIntroFileId}
143+
src={videoUrl}
139144
controls
140145
preload="metadata"
141146
class="h-48 w-full bg-black"
142147
>
143-
<a href={videoIntroFileId} target="_blank" rel="noopener noreferrer">Download Video</a>
148+
<a href={videoUrl} target="_blank" rel="noopener noreferrer">Download Video</a>
144149
</video>
145150
</div>
146151
<div class="flex items-center gap-2">
147-
<Button variant="link" size="sm" href={videoIntroFileId} class="h-auto p-0">Download Video</Button>
152+
<Button variant="link" size="sm" href={videoUrl} class="h-auto p-0">Download Video</Button>
148153
{#if editable}
149154
<Button variant="link" size="sm" class="h-auto p-0 text-destructive" onclick={removeVideo}>Remove</Button>
150155
{/if}

platforms/profile-editor/client/src/lib/components/profile/ProfileCard.svelte

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,22 @@
11
<script lang="ts">
22
import type { ProfileSearchResult } from '$lib/stores/discovery';
3+
import { resolveAssetUrl } from '$lib/utils/file-manager';
34
import { Card, CardContent } from '@metastate-foundation/ui/card';
45
import { Avatar, AvatarImage, AvatarFallback } from '@metastate-foundation/ui/avatar';
56
import { Badge } from '@metastate-foundation/ui/badge';
67
78
let { result }: { result: ProfileSearchResult } = $props();
9+
10+
// New uploads are public eVault-blob URLs; legacy ones are file-manager ids.
11+
let avatarUrl = $derived(resolveAssetUrl(result.avatar));
812
</script>
913

1014
<a href="/profile/{result.ename}" class="block">
1115
<Card class="h-full transition-shadow hover:shadow-md">
1216
<CardContent class="flex flex-col items-center p-5 text-center">
1317
<Avatar class="mb-3 h-16 w-16">
14-
{#if result.avatar}
15-
<AvatarImage src={result.avatar} alt={result.name} />
18+
{#if avatarUrl}
19+
<AvatarImage src={avatarUrl} alt={result.name} />
1620
{/if}
1721
<AvatarFallback class="text-xl font-bold">
1822
{(result.name || result.ename || '?')[0]?.toUpperCase()}

platforms/profile-editor/client/src/lib/components/profile/ProfileHeader.svelte

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script lang="ts">
22
import type { ProfileData } from '$lib/stores/profile';
33
import { updateProfile } from '$lib/stores/profile';
4-
import { uploadFile } from '$lib/utils/file-manager';
4+
import { resolveAssetUrl, uploadFile } from '$lib/utils/file-manager';
55
import { toast } from 'svelte-sonner';
66
import { Card, CardContent } from '@metastate-foundation/ui/card';
77
import { Button } from '@metastate-foundation/ui/button';
@@ -26,9 +26,10 @@
2626
let bannerBroken = $state(false);
2727
let avatarBroken = $state(false);
2828
29-
// avatar/banner are public CDN URLs (eVault blob) — render directly.
30-
let avatarSrc = $derived(avatarPreview ?? profile.professional.avatar ?? null);
31-
let bannerSrc = $derived(bannerPreview ?? profile.professional.banner ?? null);
29+
// New uploads are public eVault-blob URLs; legacy profiles stored a bare
30+
// file-manager id. resolveAssetUrl renders either.
31+
let avatarSrc = $derived(avatarPreview ?? resolveAssetUrl(profile.professional.avatar));
32+
let bannerSrc = $derived(bannerPreview ?? resolveAssetUrl(profile.professional.banner));
3233
3334
async function handleAvatarUpload(e: Event) {
3435
const input = e.target as HTMLInputElement;

platforms/profile-editor/client/src/lib/utils/file-manager.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,26 @@
1+
import { env as pubEnv } from '$env/dynamic/public';
12
import { apiClient } from './axios';
23

4+
const FILE_MANAGER_BASE = (
5+
pubEnv.PUBLIC_FILE_MANAGER_BASE_URL || 'http://localhost:3005'
6+
).replace(/\/$/, '');
7+
8+
export function isHttpUrl(value?: string | null): boolean {
9+
return !!value && /^(https?:)?\/\//i.test(value);
10+
}
11+
12+
/**
13+
* Resolve a stored profile asset (avatar/banner/cv/video) to a renderable URL.
14+
* New uploads store a public eVault-blob URL and are used as-is. Legacy
15+
* profiles stored a bare file-manager id — fall back to its public
16+
* file-manager URL so the asset still renders instead of breaking.
17+
*/
18+
export function resolveAssetUrl(value?: string | null): string | null {
19+
if (!value) return null;
20+
if (isHttpUrl(value)) return value;
21+
return `${FILE_MANAGER_BASE}/api/public/files/${encodeURIComponent(value)}`;
22+
}
23+
324
export async function uploadFile(
425
file: File,
526
onProgress?: (progress: number) => void

0 commit comments

Comments
 (0)