Fix job seeker document submission: hide raw storage URL, fix error masking
All checks were successful
build-and-release / build (push) Successful in 1m48s
All checks were successful
build-and-release / build (push) Successful in 1m48s
Two issues in the job seeker (and shared profile) dashboard: - The uploaded document's full Backblaze URL was rendered as visible text and linked directly; view-document now resolves a short-lived signed URL on demand instead of exposing the permanent storage link. - "Submit for Verification" always showed a generic "Network error" message on any failure because request() throws on non-2xx responses, making the destructured status/data unreachable in the catch-free path (e.g. a 409 "verification already in progress" looked identical to a network failure). Now surfaces the real backend error message. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
83b5072011
commit
cab192d946
3 changed files with 34 additions and 14 deletions
|
|
@ -859,7 +859,7 @@ export default function ProfilePage(props: Props) {
|
|||
setSubmitting(true);
|
||||
setSubmitMsg("");
|
||||
try {
|
||||
const { data, status } = await request("/api/profile/submit-for-verification", {
|
||||
const { status } = await request("/api/profile/submit-for-verification", {
|
||||
method: "POST",
|
||||
body: { roleKey: props.roleKey, profile_data: { ...form(), ...docUrls() } },
|
||||
});
|
||||
|
|
@ -867,13 +867,13 @@ export default function ProfilePage(props: Props) {
|
|||
setVerificationStatus("PENDING");
|
||||
props.onVerificationStatusChange?.("PENDING");
|
||||
setSubmitMsg("Submitted! We will review your profile and notify you.");
|
||||
} else if (status === 409) {
|
||||
setSubmitMsg(data?.error ?? "A verification is already in progress.");
|
||||
} else {
|
||||
setSubmitMsg(data?.error ?? "Submission failed. Please try again.");
|
||||
}
|
||||
} catch {
|
||||
setSubmitMsg("Network error. Please try again.");
|
||||
} catch (err: any) {
|
||||
// request() throws on any non-2xx response with the backend's actual error
|
||||
// message (e.g. "A verification is already in progress...") — surface that
|
||||
// instead of a generic message, otherwise every failure looks identical and
|
||||
// unactionable regardless of cause.
|
||||
setSubmitMsg(err?.message || "Submission failed. Please try again.");
|
||||
} finally {
|
||||
setSubmitting(false);
|
||||
}
|
||||
|
|
@ -1241,7 +1241,7 @@ export default function ProfilePage(props: Props) {
|
|||
"border-radius": "6px",
|
||||
}}
|
||||
>
|
||||
✓ {docUrls()[doc.key]}
|
||||
✓ Uploaded
|
||||
</span>
|
||||
<Show when={!isLocked()}>
|
||||
<button
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { For, Show, createMemo, createSignal, onMount } from 'solid-js';
|
|||
import { ShieldCheck, FileText, Activity } from 'lucide-solid';
|
||||
import { CARD, BTN_ORANGE, BTN_GHOST } from '~/components/DashboardShell';
|
||||
import { getDocFields } from '~/lib/profile-fields-config';
|
||||
import { presignDocumentUrl } from '~/lib/api';
|
||||
|
||||
const NAVY = '#0D0D2A';
|
||||
const ORANGE = '#FF5E13';
|
||||
|
|
@ -478,14 +479,20 @@ export default function VerificationStatusPage(props: Props) {
|
|||
<p style={{ margin: '4px 0 0', 'font-size': '12px', color: '#9CA3AF' }}>No file uploaded</p>
|
||||
}
|
||||
>
|
||||
<a
|
||||
href={url()!}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
style={{ margin: '4px 0 0', 'font-size': '12px', color: '#FF5E13', 'text-decoration': 'underline', display: 'inline-block' }}
|
||||
<button
|
||||
type="button"
|
||||
onClick={async () => {
|
||||
const signed = await presignDocumentUrl(url()!);
|
||||
window.open(signed, '_blank', 'noopener,noreferrer');
|
||||
}}
|
||||
style={{
|
||||
margin: '4px 0 0', 'font-size': '12px', color: '#FF5E13',
|
||||
'text-decoration': 'underline', display: 'inline-block',
|
||||
background: 'none', border: 'none', padding: '0', cursor: 'pointer',
|
||||
}}
|
||||
>
|
||||
View document
|
||||
</a>
|
||||
</button>
|
||||
</Show>
|
||||
</div>
|
||||
<p style={{
|
||||
|
|
|
|||
|
|
@ -90,6 +90,19 @@ export async function fetchDocuments(rolePrefix: string): Promise<any[]> {
|
|||
return res?.data ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a stored document reference into a short-lived, signed view URL.
|
||||
* Never render a stored document URL directly (e.g. as an <a href> or <img src>)
|
||||
* — always exchange it for a fresh one through this endpoint first.
|
||||
*/
|
||||
export async function presignDocumentUrl(url: string): Promise<string> {
|
||||
const res = await apiFetch('/api/profile/documents/presign', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ url }),
|
||||
});
|
||||
return res?.url ?? url;
|
||||
}
|
||||
|
||||
export async function submitProfileForVerification(rolePrefix: string): Promise<any> {
|
||||
return apiFetch(`/api/${rolePrefix}/profile/submit`, {
|
||||
method: 'POST',
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue