fix(portal): one idempotency key per selected file
CI (SIAX Cloud) / sonar (pull_request) Skipped
masterplan-lock / check (push) Successful in 6s
masterplan-lock / check (pull_request) Successful in 6s
CI (SIAX Cloud) / security (pull_request) Successful in 17s
Security Scan (SIAX Cloud) / security (pull_request) Successful in 34s
CI (SIAX Cloud) / quality (pull_request) Successful in 10m37s
CI (SIAX Cloud) / integration (pull_request) Successful in 10m40s

A double click or a retry after a network error generated a new key each time
and therefore a duplicate document. The key is now stable until the file
selection changes, so the same upload returns the same verified receipt.
This commit is contained in:
2026-11-03 00:51:42 +01:00
parent 6652fde6df
commit 446b9aa299
+11 -2
View File
@@ -73,6 +73,10 @@ export default function DocumentsPage() {
const [uploading, setUploading] = useState(false);
const [unknownOutcome, setUnknownOutcome] = useState<unknown | null>(null);
const inputRef = useRef<HTMLInputElement>(null);
// One idempotency key per selected file: a retry (double click, network
// error, reload-and-pick-the-same-file) returns the SAME document instead of
// creating a duplicate. Picking a different file resets it.
const uploadKey = useRef<string | null>(null);
const [instruction, setInstruction] = useState('');
const [fields, setFields] = useState<FieldRow[]>([{ name: 'organization', type: 'string' }]);
@@ -119,7 +123,9 @@ export default function DocumentsPage() {
const form = new FormData();
form.set('file', file);
form.set('project_id', projectId.trim());
form.set('idempotency_key', crypto.randomUUID());
const key = uploadKey.current ?? crypto.randomUUID();
uploadKey.current = key;
form.set('idempotency_key', key);
form.set('classification', 'restricted');
try {
const response = await fetch('/api/0pt/documents/upload', {
@@ -293,7 +299,10 @@ export default function DocumentsPage() {
ref={inputRef}
type="file"
className="hidden"
onChange={(event) => setFile(event.target.files?.[0] ?? null)}
onChange={(event) => {
uploadKey.current = null;
setFile(event.target.files?.[0] ?? null);
}}
/>
</div>