PotholeIQ / box-upload.js
Varun10000's picture
PotholeIQ: initial deployable build (Box + Supabase, HF Spaces Docker)
45e997f
Raw
History Blame Contribute Delete
5.09 kB
'use strict';
const API_PREFIX = '/api';
let __boxStatusCache = null;
let __boxStatusFetchedAt = 0;
function stripPhotoDataUrl(value) {
if (Array.isArray(value)) {
return value.map((item) => stripPhotoDataUrl(item));
}
if (!value || typeof value !== 'object') {
return value;
}
const clone = {};
for (const [key, child] of Object.entries(value)) {
if (key === 'photoDataUrl') continue;
clone[key] = stripPhotoDataUrl(child);
}
return clone;
}
async function parseApiResponse(response) {
let payload = null;
try {
payload = await response.json();
} catch {
payload = null;
}
if (!response.ok) {
throw new Error(payload?.error || payload?.message || `Request failed with HTTP ${response.status}`);
}
return payload;
}
async function apiRequest(path, options = {}) {
const response = await fetch(`${API_PREFIX}${path}`, {
method: options.method || 'GET',
headers: {
...(options.rawBody ? {} : { 'Content-Type': 'application/json' }),
...(options.headers || {}),
},
...(options.body !== undefined ? { body: options.body } : {}),
});
return parseApiResponse(response);
}
async function getBoxStatus(options = {}) {
const { force = false } = options;
if (__boxStatusCache && !force && (Date.now() - __boxStatusFetchedAt) < 10000) {
return __boxStatusCache;
}
const payload = await apiRequest('/box/status', { method: 'GET' });
__boxStatusCache = payload?.status || {};
__boxStatusFetchedAt = Date.now();
return __boxStatusCache;
}
function buildMissingConfigMessage(status = {}) {
if (!status.hasAuth) {
return 'Box authentication is not configured on the backend. Open settings.html and save the Box integration first.';
}
if (!status.hasIntakeFolderId) {
return 'Incoming intake folder ID is not configured. Open settings.html and save the Box integration first.';
}
return 'Box is not configured on the backend. Open settings.html and save the Box integration first.';
}
async function testBoxConnection() {
try {
const payload = await apiRequest('/box/test-connection', {
method: 'POST',
body: JSON.stringify({}),
});
__boxStatusCache = null;
__boxStatusFetchedAt = 0;
return payload;
} catch (error) {
return { ok: false, error: error.message };
}
}
async function uploadPhoto(file, caseId) {
if (!file) {
throw new Error('Photo file is required.');
}
const status = await getBoxStatus();
if (!status.configured) {
throw new Error(buildMissingConfigMessage(status));
}
const payload = await apiRequest(`/box/upload-photo?caseId=${encodeURIComponent(caseId)}`, {
method: 'POST',
rawBody: true,
headers: {
'Content-Type': file.type || 'application/octet-stream',
},
body: file,
});
return {
fileId: payload.fileId || '',
sharedLink: payload.sharedLink || null,
photoUrl: payload.photoUrl || '',
};
}
async function uploadSidecar(metadata, caseId) {
const status = await getBoxStatus();
if (!status.configured) {
throw new Error(buildMissingConfigMessage(status));
}
const payload = await apiRequest('/box/upload-sidecar', {
method: 'POST',
body: JSON.stringify({
caseId,
metadata: stripPhotoDataUrl(metadata || {}),
}),
});
return {
fileId: payload.fileId || '',
sidecarPayload: payload.sidecarPayload || null,
};
}
async function submitToBox(photoFile, metadata) {
const providedCaseId = metadata && typeof metadata.caseId === 'string'
? metadata.caseId.trim()
: '';
const randomSuffix = Math.random()
.toString(36)
.toUpperCase()
.replace(/[^A-Z]/g, '')
.slice(0, 4)
.padEnd(4, 'X');
const caseId = providedCaseId || `PHX-${Date.now()}-${randomSuffix}`;
const submittedAt = metadata?.submittedAt || new Date().toISOString();
const sanitizedMetadata = stripPhotoDataUrl({
...metadata,
caseId,
submittedAt,
});
try {
const photoResult = await uploadPhoto(photoFile, caseId);
const sidecarResult = await uploadSidecar({
...sanitizedMetadata,
photoFileId: photoResult.fileId || '',
photoUrl: photoResult.photoUrl || '',
}, caseId);
return {
success: true,
caseId,
photoFileId: photoResult.fileId || '',
sidecarFileId: sidecarResult.fileId || '',
sidecarPayload: sidecarResult.sidecarPayload || sanitizedMetadata,
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : String(error),
};
}
}
if (typeof module !== 'undefined' && module.exports) {
module.exports = {
getBoxStatus,
testBoxConnection,
uploadPhoto,
uploadSidecar,
submitToBox,
stripPhotoDataUrl,
};
}
if (typeof window !== 'undefined') {
window.getBoxStatus = getBoxStatus;
window.testBoxConnection = testBoxConnection;
window.submitToBox = submitToBox;
window.BoxUpload = {
getBoxStatus,
testBoxConnection,
uploadPhoto,
uploadSidecar,
submitToBox,
stripPhotoDataUrl,
};
}