Spaces:
Running
Running
Upload folder using huggingface_hub
Browse files- Dockerfile +40 -0
- app/server.py +9 -2
- static/dashboard.js +16 -4
- static/login.js +17 -4
Dockerfile
CHANGED
|
@@ -63,6 +63,46 @@ RUN uv pip install --system --no-cache -r requirements.txt
|
|
| 63 |
# Copy application
|
| 64 |
COPY . .
|
| 65 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 66 |
# Expose port 7860 for HF Spaces
|
| 67 |
EXPOSE 7860
|
| 68 |
|
|
|
|
| 63 |
# Copy application
|
| 64 |
COPY . .
|
| 65 |
|
| 66 |
+
# Bundle Supabase JS locally so Spaces can run without external CDNs.
|
| 67 |
+
RUN node - <<'NODE'
|
| 68 |
+
const fs = require('fs');
|
| 69 |
+
const path = require('path');
|
| 70 |
+
|
| 71 |
+
function walk(dir, out = []) {
|
| 72 |
+
for (const ent of fs.readdirSync(dir, { withFileTypes: true })) {
|
| 73 |
+
const p = path.join(dir, ent.name);
|
| 74 |
+
if (ent.isDirectory()) walk(p, out);
|
| 75 |
+
else out.push(p);
|
| 76 |
+
}
|
| 77 |
+
return out;
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
const base = path.join(process.cwd(), 'node_modules', '@supabase', 'supabase-js');
|
| 81 |
+
if (!fs.existsSync(base)) {
|
| 82 |
+
console.log('[build] @supabase/supabase-js not installed; skipping bundle copy');
|
| 83 |
+
process.exit(0);
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
const dist = path.join(base, 'dist');
|
| 87 |
+
const files = fs.existsSync(dist) ? walk(dist) : walk(base);
|
| 88 |
+
const candidates = files.filter((f) => /supabase(\.min)?\.js$/i.test(f)).sort();
|
| 89 |
+
if (!candidates.length) {
|
| 90 |
+
console.log('[build] No supabase JS bundle found in package; skipping');
|
| 91 |
+
process.exit(0);
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
const src =
|
| 95 |
+
candidates.find((f) => /umd[\\/].*supabase\.min\.js$/i.test(f)) ||
|
| 96 |
+
candidates.find((f) => /umd[\\/].*supabase\.js$/i.test(f)) ||
|
| 97 |
+
candidates.find((f) => /supabase\.min\.js$/i.test(f)) ||
|
| 98 |
+
candidates[0];
|
| 99 |
+
|
| 100 |
+
const dst = path.join(process.cwd(), 'static', 'vendor', 'supabase-js.min.js');
|
| 101 |
+
fs.mkdirSync(path.dirname(dst), { recursive: true });
|
| 102 |
+
fs.copyFileSync(src, dst);
|
| 103 |
+
console.log(`[build] Copied ${src} -> ${dst}`);
|
| 104 |
+
NODE
|
| 105 |
+
|
| 106 |
# Expose port 7860 for HF Spaces
|
| 107 |
EXPOSE 7860
|
| 108 |
|
app/server.py
CHANGED
|
@@ -36,8 +36,15 @@ def _ensure_supabase_asset() -> None:
|
|
| 36 |
target = _ROOT / "static" / "vendor" / "supabase-js.min.js"
|
| 37 |
if target.exists():
|
| 38 |
return
|
| 39 |
-
|
| 40 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
return
|
| 42 |
target.parent.mkdir(parents=True, exist_ok=True)
|
| 43 |
try:
|
|
|
|
| 36 |
target = _ROOT / "static" / "vendor" / "supabase-js.min.js"
|
| 37 |
if target.exists():
|
| 38 |
return
|
| 39 |
+
pkg = _ROOT / "node_modules" / "@supabase" / "supabase-js"
|
| 40 |
+
candidates = [
|
| 41 |
+
pkg / "dist" / "umd" / "supabase.min.js",
|
| 42 |
+
pkg / "dist" / "umd" / "supabase.js",
|
| 43 |
+
pkg / "dist" / "supabase.min.js",
|
| 44 |
+
pkg / "dist" / "supabase.js",
|
| 45 |
+
]
|
| 46 |
+
source = next((p for p in candidates if p.exists()), None)
|
| 47 |
+
if not source:
|
| 48 |
return
|
| 49 |
target.parent.mkdir(parents=True, exist_ok=True)
|
| 50 |
try:
|
static/dashboard.js
CHANGED
|
@@ -325,10 +325,22 @@ let supabase;
|
|
| 325 |
if (el) el.checked = !!enabled;
|
| 326 |
}
|
| 327 |
|
| 328 |
-
function requireSupabaseLibrary() {
|
| 329 |
-
if (
|
| 330 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 331 |
}
|
|
|
|
| 332 |
}
|
| 333 |
|
| 334 |
function configEndpoint(path) {
|
|
@@ -1022,7 +1034,7 @@ let supabase;
|
|
| 1022 |
try {
|
| 1023 |
const config = await fetchConfig();
|
| 1024 |
if (!config.supabase_url || !config.supabase_key) throw new Error('Supabase Config Missing');
|
| 1025 |
-
requireSupabaseLibrary();
|
| 1026 |
supabase = window.__supabaseClient || window.supabase.createClient(config.supabase_url, config.supabase_key);
|
| 1027 |
window.__supabaseClient = supabase;
|
| 1028 |
|
|
|
|
| 325 |
if (el) el.checked = !!enabled;
|
| 326 |
}
|
| 327 |
|
| 328 |
+
async function requireSupabaseLibrary() {
|
| 329 |
+
if (window.supabase && typeof window.supabase.createClient === 'function') return;
|
| 330 |
+
try {
|
| 331 |
+
const url = configEndpoint('static/vendor/supabase-js.min.js');
|
| 332 |
+
const res = await fetch(url, { method: 'GET' });
|
| 333 |
+
if (!res.ok) {
|
| 334 |
+
throw new Error(`Supabase JS bundle missing (${res.status}) at ${url}`);
|
| 335 |
+
}
|
| 336 |
+
const text = await res.text();
|
| 337 |
+
if (text.trimStart().startsWith('<')) {
|
| 338 |
+
throw new Error(`Supabase JS bundle URL returned HTML (likely 404 page): ${url}`);
|
| 339 |
+
}
|
| 340 |
+
} catch (e) {
|
| 341 |
+
throw new Error(e?.message || String(e));
|
| 342 |
}
|
| 343 |
+
throw new Error('Supabase client library failed to load (bundle fetched but window.supabase is missing).');
|
| 344 |
}
|
| 345 |
|
| 346 |
function configEndpoint(path) {
|
|
|
|
| 1034 |
try {
|
| 1035 |
const config = await fetchConfig();
|
| 1036 |
if (!config.supabase_url || !config.supabase_key) throw new Error('Supabase Config Missing');
|
| 1037 |
+
await requireSupabaseLibrary();
|
| 1038 |
supabase = window.__supabaseClient || window.supabase.createClient(config.supabase_url, config.supabase_key);
|
| 1039 |
window.__supabaseClient = supabase;
|
| 1040 |
|
static/login.js
CHANGED
|
@@ -67,10 +67,23 @@ var supabaseReady = false;
|
|
| 67 |
throw lastError || new Error('Config fetch failed');
|
| 68 |
}
|
| 69 |
|
| 70 |
-
function requireSupabaseLibrary() {
|
| 71 |
-
if (
|
| 72 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 73 |
}
|
|
|
|
| 74 |
}
|
| 75 |
|
| 76 |
function isRecoveryUrl() {
|
|
@@ -113,7 +126,7 @@ var supabaseReady = false;
|
|
| 113 |
if (!config.supabase_url || !config.supabase_key) {
|
| 114 |
throw new Error('Supabase configuration missing (supabase_url/supabase_key).');
|
| 115 |
}
|
| 116 |
-
requireSupabaseLibrary();
|
| 117 |
supabase = window.__supabaseClient || window.supabase.createClient(config.supabase_url, config.supabase_key);
|
| 118 |
window.__supabaseClient = supabase;
|
| 119 |
supabaseReady = true;
|
|
|
|
| 67 |
throw lastError || new Error('Config fetch failed');
|
| 68 |
}
|
| 69 |
|
| 70 |
+
async function requireSupabaseLibrary() {
|
| 71 |
+
if (window.supabase && typeof window.supabase.createClient === 'function') return;
|
| 72 |
+
// Help debug the most common cause in Spaces: the bundle isn't being served.
|
| 73 |
+
try {
|
| 74 |
+
const url = configEndpoint('static/vendor/supabase-js.min.js');
|
| 75 |
+
const res = await fetch(url, { method: 'GET' });
|
| 76 |
+
if (!res.ok) {
|
| 77 |
+
throw new Error(`Supabase JS bundle missing (${res.status}) at ${url}`);
|
| 78 |
+
}
|
| 79 |
+
const text = await res.text();
|
| 80 |
+
if (text.trimStart().startsWith('<')) {
|
| 81 |
+
throw new Error(`Supabase JS bundle URL returned HTML (likely 404 page): ${url}`);
|
| 82 |
+
}
|
| 83 |
+
} catch (e) {
|
| 84 |
+
throw new Error(e?.message || String(e));
|
| 85 |
}
|
| 86 |
+
throw new Error('Supabase client library failed to load (bundle fetched but window.supabase is missing).');
|
| 87 |
}
|
| 88 |
|
| 89 |
function isRecoveryUrl() {
|
|
|
|
| 126 |
if (!config.supabase_url || !config.supabase_key) {
|
| 127 |
throw new Error('Supabase configuration missing (supabase_url/supabase_key).');
|
| 128 |
}
|
| 129 |
+
await requireSupabaseLibrary();
|
| 130 |
supabase = window.__supabaseClient || window.supabase.createClient(config.supabase_url, config.supabase_key);
|
| 131 |
window.__supabaseClient = supabase;
|
| 132 |
supabaseReady = true;
|