Complete phone-to-desktop photo ID upload pipeline

- API: POST /api/v1/id-upload/token generates upload token
- API: POST /api/v1/id-upload/:token receives base64 image, stores in MinIO
- API: GET /api/v1/id-upload/:token/status returns upload status + thumbnail
- Mobile page: sends image as base64 with upload_token
- Desktop intake: requests token, generates QR with upload URL, polls
  every 3s for phone upload, auto-shows thumbnail when detected
- MinIO storage with presigned URLs for thumbnails
- Compliance order intake_data updated with photo_id_uploaded flag

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
justin 2026-05-30 16:24:35 -05:00
parent 1611b67543
commit 1535acb413
3 changed files with 224 additions and 61 deletions

View file

@ -666,13 +666,69 @@
handleIdFile(idInput.files?.[0]);
});
// Auto-generate QR code for phone upload
// Generate upload token + QR code for phone photo upload
var qrImg = document.getElementById("dot-id-qr-img");
var uploadToken = null;
if (qrImg) {
qrImg.src = "https://api.qrserver.com/v1/create-qr-code/?size=160x160&data=" + encodeURIComponent(window.location.href);
}
var urlParams = new URLSearchParams(window.location.search);
var orderNum = urlParams.get("order") || "";
var API = window.__PW_API || "";
var jwtToken = urlParams.get("token") || "";
// (webcam capture handled above via getUserMedia)
if (orderNum && API) {
// Request an upload token from the API
fetch(API + "/api/v1/id-upload/token", {
method: "POST",
headers: {
"Content-Type": "application/json",
...(jwtToken ? { "Authorization": "Bearer " + jwtToken } : {}),
},
body: JSON.stringify({ order_number: orderNum }),
})
.then(function(r) { return r.json(); })
.then(function(d) {
if (d.token) {
uploadToken = d.token;
var uploadUrl = window.location.origin + "/portal/upload-id/?order=" + orderNum + "&upload_token=" + d.token;
qrImg.src = "https://api.qrserver.com/v1/create-qr-code/?size=160x160&data=" + encodeURIComponent(uploadUrl);
// Start polling for phone upload
var pollInterval = setInterval(function() {
fetch(API + "/api/v1/id-upload/" + d.token + "/status")
.then(function(r) { return r.json(); })
.then(function(s) {
if (s.uploaded) {
clearInterval(pollInterval);
window.__dotPhotoId = "uploaded-via-phone";
// Show confirmation in the preview area
if (idImg && s.thumbnail_url) idImg.src = s.thumbnail_url;
else if (idImg) idImg.alt = "ID uploaded from phone";
if (idPreview) idPreview.hidden = false;
if (idUploadOpts) idUploadOpts.style.display = "none";
// Simplify preview — skip quality check since phone already validated
var qualCheck = document.getElementById("dot-id-quality-check");
if (qualCheck) qualCheck.hidden = true;
var acceptBtn = document.getElementById("dot-id-accept");
if (acceptBtn) acceptBtn.hidden = true;
var retakeBtn = document.getElementById("dot-id-retake");
if (retakeBtn) { retakeBtn.textContent = "Change ID"; retakeBtn.style.fontSize = "11px"; }
if (idImg) { idImg.style.maxWidth = "150px"; idImg.style.maxHeight = "100px"; }
var qualOk = document.getElementById("dot-id-quality-ok");
if (qualOk) { qualOk.hidden = false; qualOk.querySelector("p").textContent = "✓ Photo ID uploaded from your phone."; }
}
})
.catch(function() {});
}, 3000);
}
})
.catch(function() {
// Fallback: use current page URL
qrImg.src = "https://api.qrserver.com/v1/create-qr-code/?size=160x160&data=" + encodeURIComponent(window.location.href);
});
} else {
qrImg.src = "https://api.qrserver.com/v1/create-qr-code/?size=160x160&data=" + encodeURIComponent(window.location.href);
}
}
} // end guard
</script>