Deepfake Authenticator commited on
Commit
1dfcfc7
·
1 Parent(s): 70348ce

feat: split backend (HF) and frontend (Vercel)

Browse files

- Backend: remove static file serving, API-only
- Backend: CORS updated to allow Vercel domains
- Dockerfile: remove frontend copy, pure API image
- Frontend: VITE_API_URL env var for HF Space URL
- Frontend: vercel.json with SPA rewrite rules
- Frontend: .env.example with setup instructions

.gitignore CHANGED
@@ -68,3 +68,4 @@ logs/
68
  node_modules/
69
  frontend-react/dist/
70
  frontend-react/.vite/
 
 
68
  node_modules/
69
  frontend-react/dist/
70
  frontend-react/.vite/
71
+ frontend-react/.env.local
Dockerfile CHANGED
@@ -20,15 +20,14 @@ ENV HOME=/home/user \
20
 
21
  WORKDIR /home/user/app
22
 
23
- # Copy files
24
  COPY --chown=user backend/ ./backend/
25
- COPY --chown=user frontend-vanilla/ ./frontend-vanilla/
26
 
27
  # Install dependencies
28
  RUN pip install --no-cache-dir --upgrade pip && \
29
  pip install --no-cache-dir -r backend/requirements.txt
30
 
31
- # Pre-cache HuggingFace models
32
  RUN python -c "\
33
  from transformers import ViTForImageClassification, ViTImageProcessor; \
34
  ViTImageProcessor.from_pretrained('dima806/deepfake_vs_real_image_detection'); \
 
20
 
21
  WORKDIR /home/user/app
22
 
23
+ # Copy only the backend
24
  COPY --chown=user backend/ ./backend/
 
25
 
26
  # Install dependencies
27
  RUN pip install --no-cache-dir --upgrade pip && \
28
  pip install --no-cache-dir -r backend/requirements.txt
29
 
30
+ # Pre-cache HuggingFace models at build time
31
  RUN python -c "\
32
  from transformers import ViTForImageClassification, ViTImageProcessor; \
33
  ViTImageProcessor.from_pretrained('dima806/deepfake_vs_real_image_detection'); \
backend/main.py CHANGED
@@ -11,8 +11,6 @@ from pathlib import Path
11
 
12
  from fastapi import FastAPI, File, UploadFile, HTTPException, Header
13
  from fastapi.middleware.cors import CORSMiddleware
14
- from fastapi.staticfiles import StaticFiles
15
- from fastapi.responses import FileResponse
16
  from typing import Optional
17
 
18
  # ── Logging (must be set up before any logger usage) ─────────────────────────
@@ -94,7 +92,14 @@ app = FastAPI(
94
 
95
  app.add_middleware(
96
  CORSMiddleware,
97
- allow_origins=["*"],
 
 
 
 
 
 
 
98
  allow_methods=["*"],
99
  allow_headers=["*"],
100
  )
@@ -114,7 +119,6 @@ authenticator = None
114
  async def startup_event():
115
  global authenticator
116
  logger.info("Initializing DeepfakeAuthenticator...")
117
- logger.info(f"Frontend path: {_vanilla_dir} (exists={_vanilla_dir.exists()})")
118
  authenticator = DeepfakeAuthenticator()
119
  logger.info(
120
  f"DeepfakeAuthenticator ready — model: "
@@ -343,64 +347,6 @@ async def analyze_video(
343
  pass
344
 
345
 
346
- # ── Serve frontend ────────────────────────────
347
- _react_dist = Path(__file__).parent.parent / "frontend-dist"
348
- _vanilla_dir = Path(__file__).parent.parent / "frontend-vanilla"
349
-
350
- if _react_dist.exists():
351
- app.mount("/assets", StaticFiles(directory=str(_react_dist / "assets")), name="assets")
352
-
353
- @app.get("/")
354
- async def serve_index():
355
- return FileResponse(
356
- str(_react_dist / "index.html"),
357
- headers={"Cache-Control": "no-store, no-cache, must-revalidate"},
358
- )
359
-
360
- @app.get("/{full_path:path}")
361
- async def spa_fallback(full_path: str):
362
- index = _react_dist / "index.html"
363
- if index.exists():
364
- return FileResponse(str(index))
365
- return {"detail": "Not found"}
366
-
367
- elif _vanilla_dir.exists():
368
- from fastapi.staticfiles import StaticFiles as SF
369
-
370
- @app.get("/script.js")
371
- async def serve_script():
372
- return FileResponse(
373
- str(_vanilla_dir / "script.js"),
374
- media_type="application/javascript",
375
- headers={"Cache-Control": "no-store, no-cache, must-revalidate"},
376
- )
377
-
378
- @app.get("/pricing")
379
- async def serve_pricing():
380
- return FileResponse(
381
- str(_vanilla_dir / "pricing.html"),
382
- headers={"Cache-Control": "no-store, no-cache, must-revalidate"},
383
- )
384
-
385
- @app.get("/")
386
- async def serve_index():
387
- return FileResponse(
388
- str(_vanilla_dir / "index.html"),
389
- headers={"Cache-Control": "no-store, no-cache, must-revalidate"},
390
- )
391
-
392
- # Serve any other static file from frontend-vanilla/
393
- @app.get("/{filename:path}")
394
- async def serve_static(filename: str):
395
- file_path = _vanilla_dir / filename
396
- if file_path.exists() and file_path.is_file():
397
- return FileResponse(str(file_path))
398
- return FileResponse(
399
- str(_vanilla_dir / "index.html"),
400
- headers={"Cache-Control": "no-store, no-cache, must-revalidate"},
401
- )
402
-
403
-
404
  if __name__ == "__main__":
405
  import uvicorn
406
  uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=False)
 
11
 
12
  from fastapi import FastAPI, File, UploadFile, HTTPException, Header
13
  from fastapi.middleware.cors import CORSMiddleware
 
 
14
  from typing import Optional
15
 
16
  # ── Logging (must be set up before any logger usage) ─────────────────────────
 
92
 
93
  app.add_middleware(
94
  CORSMiddleware,
95
+ allow_origins=[
96
+ "http://localhost:5173",
97
+ "http://localhost:3000",
98
+ "https://*.vercel.app",
99
+ "https://authrix.vercel.app",
100
+ # Add your custom domain here if you have one
101
+ ],
102
+ allow_origin_regex=r"https://.*\.vercel\.app",
103
  allow_methods=["*"],
104
  allow_headers=["*"],
105
  )
 
119
  async def startup_event():
120
  global authenticator
121
  logger.info("Initializing DeepfakeAuthenticator...")
 
122
  authenticator = DeepfakeAuthenticator()
123
  logger.info(
124
  f"DeepfakeAuthenticator ready — model: "
 
347
  pass
348
 
349
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
350
  if __name__ == "__main__":
351
  import uvicorn
352
  uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=False)
frontend-react/.env.example ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ # Copy to .env.local for local dev (optional — dev proxy handles it automatically)
2
+ # Set this in Vercel dashboard: Settings → Environment Variables
3
+ VITE_API_URL=https://aarav13-authrix.hf.space
frontend-react/src/App.tsx CHANGED
@@ -1,5 +1,6 @@
1
  import { useState } from 'react';
2
  import type { AnalysisResult, AppState } from './types';
 
3
  import Background from './components/Background';
4
  import RadioNav from './components/RadioNav';
5
  import HeroSection from './components/HeroSection';
@@ -29,7 +30,7 @@ export default function App() {
29
  const fd = new FormData();
30
  fd.append('file', file);
31
  try {
32
- const res = await fetch('/analyze', { method: 'POST', body: fd });
33
  if (!res.ok) {
34
  const e = await res.json().catch(() => ({}));
35
  throw new Error((e as { detail?: string }).detail || `Server error ${res.status}`);
 
1
  import { useState } from 'react';
2
  import type { AnalysisResult, AppState } from './types';
3
+ import { API_BASE } from './api';
4
  import Background from './components/Background';
5
  import RadioNav from './components/RadioNav';
6
  import HeroSection from './components/HeroSection';
 
30
  const fd = new FormData();
31
  fd.append('file', file);
32
  try {
33
+ const res = await fetch(`${API_BASE}/analyze`, { method: 'POST', body: fd });
34
  if (!res.ok) {
35
  const e = await res.json().catch(() => ({}));
36
  throw new Error((e as { detail?: string }).detail || `Server error ${res.status}`);
frontend-react/src/api.ts ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ // In dev: uses Vite proxy (localhost:8000)
2
+ // In production (Vercel): uses VITE_API_URL env var pointing to HF Space
3
+ export const API_BASE =
4
+ import.meta.env.VITE_API_URL?.replace(/\/$/, '') ?? '';
frontend-react/src/components/Navbar.tsx CHANGED
@@ -1,5 +1,7 @@
1
  import { useEffect, useState } from 'react';
2
 
 
 
3
  interface NavbarProps {
4
  onDashboard: () => void;
5
  onGetStarted: () => void;
@@ -10,7 +12,7 @@ export default function Navbar({ onDashboard, onGetStarted, onOpenModal }: Navba
10
  const [health, setHealth] = useState<'checking' | 'online' | 'offline'>('checking');
11
 
12
  useEffect(() => {
13
- fetch('/health')
14
  .then(r => r.ok ? setHealth('online') : setHealth('offline'))
15
  .catch(() => setHealth('offline'));
16
  }, []);
 
1
  import { useEffect, useState } from 'react';
2
 
3
+ const API_BASE = import.meta.env.VITE_API_URL?.replace(/\/$/, '') ?? '';
4
+
5
  interface NavbarProps {
6
  onDashboard: () => void;
7
  onGetStarted: () => void;
 
12
  const [health, setHealth] = useState<'checking' | 'online' | 'offline'>('checking');
13
 
14
  useEffect(() => {
15
+ fetch(`${API_BASE}/health`)
16
  .then(r => r.ok ? setHealth('online') : setHealth('offline'))
17
  .catch(() => setHealth('offline'));
18
  }, []);
frontend-react/vercel.json ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "buildCommand": "npm run build",
3
+ "outputDirectory": "dist",
4
+ "framework": "vite",
5
+ "rewrites": [
6
+ { "source": "/(.*)", "destination": "/index.html" }
7
+ ]
8
+ }