Spaces:
Sleeping
Sleeping
Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,111 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
import requests
|
3 |
+
import base64
|
4 |
+
import json
|
5 |
+
from PIL import Image
|
6 |
+
import io
|
7 |
+
|
8 |
+
# ==============================================================================
|
9 |
+
# CONFIGURATION
|
10 |
+
# ==============================================================================
|
11 |
+
|
12 |
+
# This is the full URL to the MCP (base64) endpoint of your SERVER Space.
|
13 |
+
# Replace this with the URL of your deployed Python API server.
|
14 |
+
DECODER_API_ENDPOINT = "https://https://broadfield-dev-keylock-auth.hf.space/run/mcp_decode"
|
15 |
+
# Example: "https://broadfield-dev-keylock-api-python.hf.space/run/mcp_decode"
|
16 |
+
|
17 |
+
# Link to the server space for user reference
|
18 |
+
SERVER_SPACE_URL = "https://huggingface.co/spaces/broadfield-dev/keylock-auth"
|
19 |
+
# Example: "https://huggingface.co/spaces/broadfield-dev/KeyLock-API-Python"
|
20 |
+
|
21 |
+
|
22 |
+
# ==============================================================================
|
23 |
+
# API CALL LOGIC
|
24 |
+
# ==============================================================================
|
25 |
+
|
26 |
+
def call_decoder_api(image: Image.Image) -> dict:
|
27 |
+
"""
|
28 |
+
Takes a PIL image, converts it to base64, sends it to the decoder API,
|
29 |
+
and returns the decrypted JSON data.
|
30 |
+
"""
|
31 |
+
if image is None:
|
32 |
+
raise gr.Error("Please upload an encrypted image.")
|
33 |
+
|
34 |
+
try:
|
35 |
+
# 1. Convert the PIL Image to a base64 string
|
36 |
+
buffer = io.BytesIO()
|
37 |
+
image.save(buffer, format="PNG")
|
38 |
+
image_bytes = buffer.getvalue()
|
39 |
+
image_base64 = base64.b64encode(image_bytes).decode('utf-8')
|
40 |
+
|
41 |
+
# 2. Prepare the JSON payload for the Gradio API
|
42 |
+
# Gradio's /run/predict endpoint expects this specific structure
|
43 |
+
payload = {
|
44 |
+
"data": [
|
45 |
+
image_base64
|
46 |
+
]
|
47 |
+
}
|
48 |
+
|
49 |
+
# 3. Make the POST request to the server API
|
50 |
+
headers = {"Content-Type": "application/json"}
|
51 |
+
response = requests.post(DECODER_API_ENDPOINT, headers=headers, json=payload)
|
52 |
+
|
53 |
+
# Raise an error for bad status codes (4xx or 5xx)
|
54 |
+
response.raise_for_status()
|
55 |
+
|
56 |
+
# 4. Process the response
|
57 |
+
response_json = response.json()
|
58 |
+
|
59 |
+
# Gradio API wraps results in a "data" list. The actual result is the first element.
|
60 |
+
if "data" in response_json and len(response_json["data"]) > 0:
|
61 |
+
return response_json["data"][0]
|
62 |
+
elif "error" in response_json:
|
63 |
+
# Handle application-level errors returned by the server
|
64 |
+
raise gr.Error(f"API Error: {response_json['error']}")
|
65 |
+
else:
|
66 |
+
raise gr.Error(f"API returned an unexpected response format: {response_json}")
|
67 |
+
|
68 |
+
except requests.exceptions.RequestException as e:
|
69 |
+
logger.error(f"Network error calling API: {e}")
|
70 |
+
raise gr.Error(f"Could not connect to the decoder API. Please check the URL and server status. Error: {e}")
|
71 |
+
except Exception as e:
|
72 |
+
logger.error(f"An unexpected error occurred: {e}")
|
73 |
+
raise gr.Error(f"An unexpected error occurred: {e}")
|
74 |
+
|
75 |
+
# ==============================================================================
|
76 |
+
# GRADIO INTERFACE
|
77 |
+
# ==============================================================================
|
78 |
+
|
79 |
+
with gr.Blocks(theme=gr.themes.Soft(), title="KeyLock API Client") as demo:
|
80 |
+
gr.Markdown(f"""
|
81 |
+
# 🔑 KeyLock API Client
|
82 |
+
This is a user interface for the [Secure Decoder API]({SERVER_SPACE_URL}).
|
83 |
+
|
84 |
+
Upload an image that was encrypted with the service's public key, and this client will send it to the API for decryption.
|
85 |
+
""")
|
86 |
+
|
87 |
+
with gr.Row():
|
88 |
+
with gr.Column(scale=1):
|
89 |
+
image_input = gr.Image(
|
90 |
+
type="pil",
|
91 |
+
label="Upload Encrypted PNG Image",
|
92 |
+
sources=["upload", "clipboard"]
|
93 |
+
)
|
94 |
+
submit_button = gr.Button("Decrypt via API", variant="primary")
|
95 |
+
|
96 |
+
gr.Markdown("---")
|
97 |
+
gr.Markdown(f"This client sends requests to the following API endpoint: `{DECODER_API_ENDPOINT}`")
|
98 |
+
|
99 |
+
with gr.Column(scale=2):
|
100 |
+
gr.Markdown("## Decrypted Data")
|
101 |
+
json_output = gr.JSON(label="Result from Server")
|
102 |
+
|
103 |
+
# Connect the UI components to the API call function
|
104 |
+
submit_button.click(
|
105 |
+
fn=call_decoder_api,
|
106 |
+
inputs=[image_input],
|
107 |
+
outputs=[json_output]
|
108 |
+
)
|
109 |
+
|
110 |
+
if __name__ == "__main__":
|
111 |
+
demo.launch()
|