Spaces:
Sleeping
Sleeping
| """ | |
| InfoShield — Hugging Face Space entry point (Gradio SDK, no Gradio UI). | |
| This Space is declared `sdk: gradio` (see README.md) so it builds and runs, but | |
| the user-facing surface is the InfoShield React site — there is NO Gradio | |
| interface. A minimal, hidden Gradio app is mounted only at /_gradio to satisfy | |
| the Gradio SDK runtime; nothing links to it. | |
| Flat layout note: Hugging Face's web uploader cannot preserve subfolders, so all | |
| files (the React build + cache data + this script) live in the repo root. This | |
| file serves the static site from its own directory and reads the cache from the | |
| same place — cache-first, no torch, no Gemini, no network. | |
| """ | |
| from __future__ import annotations | |
| import json | |
| import os | |
| from pathlib import Path | |
| import gradio as gr | |
| from fastapi import FastAPI | |
| from fastapi.responses import FileResponse, JSONResponse | |
| from fastapi.staticfiles import StaticFiles | |
| ROOT = Path(__file__).parent.resolve() | |
| PORT = int(os.environ.get("PORT", os.environ.get("GRADIO_SERVER_PORT", 7860))) | |
| def _load_json(name: str, default): | |
| try: | |
| return json.loads((ROOT / name).read_text(encoding="utf-8")) | |
| except Exception: | |
| return default | |
| _POSTS = _load_json("posts.json", []) | |
| _CACHE = _load_json("cache.json", {"meta": {}, "posts": {}}) | |
| _CACHE_POSTS = _CACHE.get("posts", {}) | |
| def _merged_feed(): | |
| out = [] | |
| for p in _POSTS: | |
| c = _CACHE_POSTS.get(p["id"], {}) | |
| cls = c.get("classification", {}) or {} | |
| out.append({**p, "classification": cls, "analysis": c.get("analysis"), "flagged": bool(cls.get("flagged"))}) | |
| return out | |
| def _stats(): | |
| feed = _merged_feed() | |
| flagged = [p for p in feed if p["flagged"]] | |
| techniques, confidences = {}, [] | |
| for p in flagged: | |
| if p["classification"].get("confidence") is not None: | |
| confidences.append(p["classification"]["confidence"]) | |
| for t in ((p.get("analysis") or {}).get("techniques") or []): | |
| name = t.get("name") if isinstance(t, dict) else t | |
| if name: | |
| techniques[name] = techniques.get(name, 0) + 1 | |
| avg = round(sum(confidences) / len(confidences), 4) if confidences else 0.0 | |
| return { | |
| "total_posts": len(feed), | |
| "flagged_posts": len(flagged), | |
| "clear_posts": len(feed) - len(flagged), | |
| "technique_distribution": techniques, | |
| "avg_confidence": avg, | |
| "model_meta": _CACHE.get("meta", {}), | |
| "mode": "cache-first (read-only, no model)", | |
| } | |
| _ACTIVATION = {"target_url": "", "enabled": False} | |
| app = FastAPI(title="InfoShield (cache-first, static)", docs_url=None, redoc_url=None) | |
| def health(): | |
| return {"status": "ok", "mode": "cache-first", "model_loaded": False, | |
| "device": "none (serving cache)", "posts": len(_POSTS)} | |
| def feed(): | |
| return {"posts": _merged_feed()} | |
| def stats(): | |
| return _stats() | |
| def post_analysis(post_id: str): | |
| c = _CACHE_POSTS.get(post_id) | |
| if not c: | |
| return JSONResponse({"error": "unknown post id"}, status_code=404) | |
| return {"id": post_id, "classification": c.get("classification", {}), "analysis": c.get("analysis")} | |
| def get_activation_target(): | |
| return _ACTIVATION | |
| def set_activation_target(payload: dict): | |
| _ACTIVATION["target_url"] = str(payload.get("target_url", "")) | |
| _ACTIVATION["enabled"] = bool(payload.get("enabled", True)) | |
| return _ACTIVATION | |
| # Hidden Gradio app — present only to satisfy the Gradio SDK runtime. Mounted | |
| # before the static catch-all so its routes resolve first. | |
| with gr.Blocks(analytics_enabled=False, title="InfoShield (internal)") as _hidden: | |
| gr.Markdown("InfoShield runs as a static site at `/`. This Gradio mount is internal.") | |
| app = gr.mount_gradio_app(app, _hidden, path="/_gradio") | |
| # Serve index.html for "/" explicitly, then mount the flat static dir LAST so the | |
| # API routes and /_gradio above take precedence over the "/" catch-all. | |
| def _index(): | |
| return FileResponse(ROOT / "index.html") | |
| app.mount("/", StaticFiles(directory=str(ROOT), html=True), name="static") | |
| if __name__ == "__main__": | |
| import uvicorn | |
| uvicorn.run(app, host="0.0.0.0", port=PORT) | |