File size: 5,302 Bytes
3ab82f0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2850a82
 
 
3ab82f0
 
 
 
 
721add8
3ab82f0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2850a82
3ab82f0
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
const $ = selector => document.querySelector(selector);
let session = null;
let busy = false;
async function api(url, options = {}) {
  const response = await fetch(url, { credentials: 'same-origin', ...options });
  let data;
  try { data = await response.json(); } catch { throw new Error('The submission service is unavailable. Please try again later.'); }
  if (!response.ok) throw new Error(typeof data.detail === 'string' ? data.detail : 'Request failed. Please check the form.');
  return data;
}
function message(text, error = false) {
  $('#submission-message').textContent = text;
  $('#submission-message').className = `notice ${error ? 'error' : 'success'}`;
}
async function loadSession() {
  if (busy) return;
  try {
    session = await api('/api/submission-session');
    $('#hf-username').value = session.username || '';
    $('#identity-status').textContent = session.username ? `Signed in as ${session.username}` : 'Sign in to submit your outputs.';
    $('#sign-in').classList.toggle('hidden', Boolean(session.username));
    $('#sign-out').classList.toggle('hidden', !session.username);
    $('#submission-fields').disabled = !session.username || !session.storage_ready || session.storage_full;
    const unavailable = session.storage_full ? session.storage_message : !session.auth_enabled ? 'Hugging Face sign-in has not been configured yet.' : !session.storage_ready ? 'Submissions will open once private persistent storage is configured.' : '';
    $('#availability').textContent = unavailable;
    $('#availability').classList.toggle('hidden', !unavailable);
    $('#sign-in').classList.toggle('unavailable', !session.auth_enabled);
    $('#sign-in').setAttribute('aria-disabled', String(!session.auth_enabled));
  } catch (error) {
    $('#submission-fields').disabled = true;
    $('#identity-status').textContent = 'Submission service unavailable.';
    message(error.message, true);
  }
}
$('#sign-in').addEventListener('click', event => { if (!session?.auth_enabled) event.preventDefault(); });
$('#sign-out').addEventListener('click', async () => {
  try { await api('/auth/logout', { method: 'POST', headers: {'X-CSRF-Token': session.csrf} }); await loadSession(); }
  catch (error) { message(error.message, true); }
});
function upload(id, file) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open('PUT', `/api/submissions/${id}/archive`);
    xhr.setRequestHeader('X-CSRF-Token', session.csrf);
    xhr.setRequestHeader('Content-Type', 'application/zip');
    xhr.upload.onprogress = event => {
      if (!event.lengthComputable) return;
      const percent = Math.round(event.loaded / event.total * 100);
      $('#progress').value = percent;
      $('#progress-text').textContent = percent === 100 ? 'Upload complete. Checking ZIP contents; keep this page open…' : `Uploading… ${percent}%`;
    };
    xhr.onerror = () => reject(new Error(`Connection lost. Submission ID: ${id}. The file has not been confirmed as accepted.`));
    xhr.onload = () => {
      let data;
      try { data = JSON.parse(xhr.responseText); } catch { reject(new Error(`Unexpected server response. Submission ID: ${id}.`)); return; }
      if (xhr.status >= 200 && xhr.status < 300) resolve(data);
      else reject(new Error(data.detail || `Upload failed (${xhr.status}).`));
    };
    xhr.send(file);
  });
}
$('#archive').addEventListener('change', () => {
  $('#archive-name').textContent = $('#archive').files[0]?.name || 'No file selected';
});
$('#submission-form').addEventListener('submit', async event => {
  event.preventDefault();
  if (busy || !session?.username) return;
  const file = $('#archive').files[0];
  if (!file || !file.name.toLowerCase().endsWith('.zip') || file.size <= 0 || file.size > session.max_zip_bytes) {
    message('Choose a non-empty .zip file no larger than 10 GB.', true); return;
  }
  const values = Object.fromEntries(new FormData(event.currentTarget));
  values.username = session.username;
  busy = true;
  $('#submission-fields').disabled = true;
  $('#sign-out').disabled = true;
  $('#submission-message').className = 'notice hidden';
  $('#upload-progress').classList.remove('hidden');
  $('#progress').value = 0;
  $('#progress-text').textContent = 'Verifying model ownership, public access, and license…';
  try {
    const prepared = await api('/api/submissions', {method:'POST', headers:{'Content-Type':'application/json','X-CSRF-Token':session.csrf}, body:JSON.stringify({...values, filename:file.name, size:file.size})});
    const result = await upload(prepared.id, file);
    $('#progress-text').textContent = 'Upload and ZIP checks complete.';
    message(`Submission received: ${result.id}. ${result.images} images found. Status: pending review.`);
    event.target.reset();
    $('#archive-name').textContent = 'No file selected';
  } catch (error) { message(error.message, true); }
  finally { busy = false; $('#sign-out').disabled = false; await loadSession(); }
});
window.addEventListener('focus', loadSession);
window.addEventListener('beforeunload', event => { if (busy) {event.preventDefault(); event.returnValue = '';}});
loadSession();

if (new URLSearchParams(location.search).get("login") === "failed") message("Hugging Face sign-in failed. Please try again.", true);