Spaces:
Runtime error
Runtime error
File size: 6,002 Bytes
eaeb03e |
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 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
# app.py (Gate Space)
from flask import Flask, request, redirect, make_response
import html, os, time, requests, json
from itsdangerous import URLSafeSerializer
# ===== CONFIG (set as Space Secrets or edit here) =====
MODEL_API_URL = os.getenv("MODEL_API_URL", "") # e.g. https://<you>-JARVIS-ModelAPI.hf.space/verify
AP_WEBHOOK_URL = os.getenv("AP_WEBHOOK_URL", "") # optional Activepieces Catch Hook
CHATBOT_URL = os.getenv("CHATBOT_URL", "https://<you>-JARVIS-Chat.hf.space")
SECRET_KEY = os.getenv("SECRET_KEY", "change-me")
TOKEN_TTL = int(os.getenv("TOKEN_TTL", "3600")) # seconds
# ===== UI TEXT =====
TITLE = "Face Verify Gate"
CAPTION = "Verify your identity to continue"
BACKGROUND_GIF = "https://i.pinimg.com/originals/d9/09/57/d90957d7462b87ba8171fce62d2bf816.gif"
app = Flask(__name__)
signer = URLSafeSerializer(SECRET_KEY, salt="jarvis-facegate")
def make_token(name: str) -> str:
payload = {"name": name, "ts": int(time.time())}
return signer.dumps(payload)
def page(status_msg=""):
status_msg = html.escape(status_msg or "")
html_page = f"""<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>{TITLE}</title>
<style>
:root {{
--glass: rgba(10,16,24,0.55);
--border: rgba(62,231,255,0.35);
--accent: #3ee7ff;
--accent2: #7bf5c8;
--text: #e8f3ff;
--muted: #a9c2d0;
}}
* {{ box-sizing: border-box; }}
html, body {{
height: 100%; margin: 0;
font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial;
color: var(--text);
}}
body {{
background: #000 center/cover no-repeat fixed;
background-image: url('{BACKGROUND_GIF}');
}}
.shade {{
position: fixed; inset: 0;
background: radial-gradient(60% 60% at 50% 40%, rgba(0,0,32,0.10) 0%, rgba(0,0,0,0.65) 100%);
}}
.card {{
position: relative;
max-width: 520px;
margin: min(10vh, 8rem) auto 0;
padding: 1.75rem 1.75rem 1.25rem;
background: linear-gradient(180deg, rgba(10,16,24,0.72), var(--glass));
backdrop-filter: blur(10px) saturate(130%);
border: 1px solid var(--border);
border-radius: 16px;
box-shadow: 0 20px 60px rgba(0,0,0,0.6), inset 0 0 40px rgba(62,231,255,0.12);
}}
h1 {{ margin: 0 0 .25rem 0; font-weight: 800; letter-spacing: 0.5px; }}
.caption {{ margin: 0 0 1.0rem 0; color: var(--muted); }}
label {{ display:block; margin:.6rem 0 .35rem; color: var(--muted); font-weight:600; }}
input[type="text"], input[type="file"] {{
width: 100%; padding: .7rem .85rem;
border-radius: 10px; border: 1px solid rgba(255,255,255,0.18);
background: rgba(255,255,255,0.08); color: var(--text); outline: none;
}}
input[type="text"]:focus {{
border-color: var(--accent);
box-shadow: 0 0 0 3px rgba(62,231,255,0.25);
}}
button {{
margin-top: 1rem; padding: .8rem 1rem; width: 100%;
border-radius: 10px; border: none;
background: linear-gradient(90deg, var(--accent), var(--accent2));
color: #0a0f18; font-weight: 800; cursor: pointer;
}}
#status {{ min-height: 1.25rem; margin-top: .6rem; color: #ffd166; }}
#status.err {{ color: #ff7b7b; }}
#status.ok {{ color: #7bf5c8; }}
.stub {{ margin-top: .6rem; color: var(--muted); font-size:.9rem; }}
@media (max-width: 520px) {{ .card {{ margin: 8vh 1rem 0; }} }}
</style>
</head>
<body>
<div class="shade"></div>
<main class="card">
<h1>{TITLE}</h1>
<p class="caption">{CAPTION}</p>
<form method="POST" action="/verify" enctype="multipart/form-data" onsubmit="onSubmit()">
<label for="name">Name</label>
<input id="name" name="name" type="text" placeholder="Enter your name" required />
<label for="photo">Photo</label>
<input id="photo" name="photo" type="file" accept="image/*" required />
<button type="submit">Verify & Enter</button>
<p id="status" aria-live="polite">{status_msg}</p>
</form>
<p class="stub">Jarvis mode — verification runs server-side.</p>
</main>
<script>
function onSubmit() {{
const s = document.getElementById('status');
s.textContent = 'Verifying…';
s.className = '';
}}
</script>
</body>
</html>"""
resp = make_response(html_page)
resp.headers["Content-Type"] = "text/html; charset=utf-8"
return resp
@app.get("/")
def index():
return page("")
def call_model_api(name, file_storage):
# Call Activepieces (preferred) or Model API directly
url = AP_WEBHOOK_URL.strip() or MODEL_API_URL.strip()
if not url:
raise RuntimeError("Neither AP_WEBHOOK_URL nor MODEL_API_URL is set.")
files = {"image" if "verify" in url else "photo": (file_storage.filename, file_storage.stream, file_storage.mimetype or "application/octet-stream")}
data = {"name": name}
r = requests.post(url, data=data, files=files, timeout=45)
# Activepieces typically returns {status, redirect_url?}; Model API returns {status, score,...}
try:
return r.json()
except Exception:
return {"status": "error", "message": f"Non-JSON from backend ({r.status_code})"}
@app.post("/verify")
def verify():
name = (request.form.get("name") or "").strip()
file = request.files.get("photo")
if not name or not file or not file.filename.strip():
return page("Please enter your name and select a photo."), 400
try:
result = call_model_api(name, file)
except Exception as e:
return page(f"System error contacting verifier: {e}"), 502
status = str(result.get("status","")).lower()
if status == "accepted":
token = make_token(name)
# Prefer redirect_url from Activepieces; else use CHATBOT_URL
cb = result.get("redirect_url") or CHATBOT_URL
return redirect(f"{cb}?token={token}", code=302)
msg = result.get("message") or "Access denied by FaceGate."
return page(msg), 401
if __name__ == "__main__":
app.run(host="127.0.0.1", port=5000, debug=True)
|