Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -2,63 +2,85 @@ import gradio as gr
|
|
2 |
import json
|
3 |
import logging
|
4 |
import tempfile
|
5 |
-
import core # Import our
|
6 |
|
7 |
# --- Configure Logging ---
|
8 |
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
9 |
logger = logging.getLogger(__name__)
|
10 |
|
11 |
# ==============================================================================
|
12 |
-
#
|
13 |
# ==============================================================================
|
14 |
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
return gr.Dropdown(choices=[], value=None, label="Decoder Service (Paste JSON config)"), "Status: Waiting for JSON configuration."
|
19 |
|
|
|
|
|
|
|
20 |
try:
|
21 |
-
|
22 |
-
if not isinstance(
|
23 |
-
raise TypeError("JSON root must be a list of objects.")
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
|
|
28 |
if not all(k in service for k in ("name", "link", "public_key")):
|
29 |
raise ValueError("Each service object must contain 'name', 'link', and 'public_key' keys.")
|
30 |
-
|
|
|
|
|
|
|
|
|
31 |
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
43 |
"""
|
44 |
The main function called by the 'Create' button.
|
45 |
It finds the selected service's public key and calls the core encryption logic.
|
46 |
"""
|
47 |
-
if not all([
|
48 |
-
raise gr.Error("
|
49 |
|
50 |
try:
|
51 |
-
|
52 |
-
|
53 |
-
# Find the public key for the selected service
|
54 |
public_key = None
|
55 |
-
for service in
|
56 |
if service.get("name") == selected_service_name:
|
57 |
public_key = service.get("public_key")
|
58 |
break
|
59 |
|
60 |
if not public_key:
|
61 |
-
raise gr.Error(f"Could not find the service '{selected_service_name}'
|
62 |
|
63 |
# Call the core function to do the heavy lifting
|
64 |
encrypted_image = core.create_encrypted_image(secret_data_str, public_key)
|
@@ -71,74 +93,76 @@ def generate_image(json_config_string: str, selected_service_name: str, secret_d
|
|
71 |
|
72 |
except Exception as e:
|
73 |
logger.error(f"Image creation failed: {e}", exc_info=True)
|
74 |
-
# Return empty outputs on failure
|
75 |
return None, None, f"β Error: {e}"
|
76 |
|
77 |
# ==============================================================================
|
78 |
# GRADIO INTERFACE
|
79 |
# ==============================================================================
|
80 |
|
81 |
-
# Example JSON for the user to copy
|
82 |
-
EXAMPLE_JSON = """
|
83 |
-
[
|
84 |
-
{
|
85 |
-
"name": "My Python Decoder API",
|
86 |
-
"link": "https://huggingface.co/spaces/YOUR_USERNAME/KeyLock-API-Python",
|
87 |
-
"public_key": "-----BEGIN PUBLIC KEY-----\\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy2sWjB1iQ3vK03U7e/9E\\nO6J1K/s0tBq4Pz8F3r9/i8s7t9R1p8Z4Y6h4f4O7w9p9Z0c8t7m4J1e9g7K9m6f3\\nR1k3y7v1w0l7z6s5v2l8l4t9v8z7y6t5k2x1c9v7z3k1y9w8r5t3s1v9a8d7g6f5\\ne4d3c2b1a9f8e7d6c5b4a3f2e1d0c9b8a7f6e5d4c3b2a1f0e9d8c7b6a5f4e3d2\\nc1b0a9f8e7d6c5b4a3f2e1d0c9b8a7f6e5d4c3b2a1f0e9d8c7b6a5f4e3d2c1b0\\na9f8e7d6c5b4a3f2e1d0c9b8a7f6e5d4c3b2a1f0e9d8c7b6a5f4e3d2c1b0a9f8\\ne7d6\\n-----END PUBLIC KEY-----"
|
88 |
-
}
|
89 |
-
]
|
90 |
-
"""
|
91 |
-
|
92 |
with gr.Blocks(theme=gr.themes.Soft(), title="KeyLock Image Creator") as demo:
|
93 |
gr.Markdown("# π KeyLock Image Creator")
|
94 |
-
gr.Markdown("Create secure, encrypted images
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
99 |
json_config = gr.Textbox(
|
100 |
-
lines=
|
101 |
label="JSON Configuration",
|
102 |
-
|
103 |
-
info="Each service must
|
104 |
-
)
|
105 |
-
gr.Markdown("### 2. Enter Your Secret Data")
|
106 |
-
secret_data = gr.Textbox(
|
107 |
-
lines=5,
|
108 |
-
label="Secret Data (Key-Value Pairs)",
|
109 |
-
placeholder="USERNAME: [email protected]\nAPI_KEY: sk-12345..."
|
110 |
-
)
|
111 |
-
|
112 |
-
with gr.Column(scale=1):
|
113 |
-
gr.Markdown("### 3. Select Service and Create")
|
114 |
-
service_dropdown = gr.Dropdown(
|
115 |
-
label="Decoder Service (Paste JSON config)",
|
116 |
-
choices=[],
|
117 |
-
interactive=True
|
118 |
)
|
119 |
-
|
120 |
-
|
121 |
-
status_output = gr.Textbox(label="Status", interactive=False)
|
122 |
-
image_output = gr.Image(label="Generated Image", type="pil")
|
123 |
-
download_output = gr.File(label="Download Image")
|
124 |
|
125 |
# --- Interactivity ---
|
126 |
-
# When the
|
127 |
-
|
128 |
-
fn=
|
129 |
inputs=[json_config],
|
130 |
-
outputs=[
|
|
|
|
|
|
|
|
|
|
|
|
|
131 |
)
|
132 |
|
133 |
-
# When the
|
134 |
create_button.click(
|
135 |
fn=generate_image,
|
136 |
-
inputs=[
|
137 |
outputs=[image_output, download_output, status_output]
|
138 |
)
|
139 |
-
|
140 |
-
with gr.Accordion("Example JSON Configuration", open=False):
|
141 |
-
gr.Code(value=EXAMPLE_JSON, language="json")
|
142 |
|
143 |
if __name__ == "__main__":
|
144 |
demo.launch()
|
|
|
2 |
import json
|
3 |
import logging
|
4 |
import tempfile
|
5 |
+
import core # Import our core module
|
6 |
|
7 |
# --- Configure Logging ---
|
8 |
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
9 |
logger = logging.getLogger(__name__)
|
10 |
|
11 |
# ==============================================================================
|
12 |
+
# CONFIGURATION & DATA HANDLING
|
13 |
# ==============================================================================
|
14 |
|
15 |
+
ENDPOINTS = []
|
16 |
+
ENDPOINT_NAMES = []
|
17 |
+
DEFAULT_CONFIG_FILE = "endpoints.json"
|
|
|
18 |
|
19 |
+
def load_config_from_string(json_string: str) -> str:
|
20 |
+
"""Parses a JSON string, updates global config, and returns a status message."""
|
21 |
+
global ENDPOINTS, ENDPOINT_NAMES
|
22 |
try:
|
23 |
+
data = json.loads(json_string)
|
24 |
+
if not isinstance(data, list):
|
25 |
+
raise TypeError("JSON root must be a list of service objects.")
|
26 |
+
|
27 |
+
# Validate and extract data
|
28 |
+
temp_endpoints = []
|
29 |
+
temp_names = []
|
30 |
+
for service in data:
|
31 |
if not all(k in service for k in ("name", "link", "public_key")):
|
32 |
raise ValueError("Each service object must contain 'name', 'link', and 'public_key' keys.")
|
33 |
+
temp_endpoints.append(service)
|
34 |
+
temp_names.append(service["name"])
|
35 |
+
|
36 |
+
ENDPOINTS = temp_endpoints
|
37 |
+
ENDPOINT_NAMES = temp_names
|
38 |
|
39 |
+
status_msg = f"β
Success! Loaded {len(ENDPOINT_NAMES)} services from custom JSON."
|
40 |
+
logger.info(status_msg)
|
41 |
+
return status_msg
|
42 |
+
except Exception as e:
|
43 |
+
status_msg = f"β Error loading custom JSON: {e}"
|
44 |
+
logger.error(status_msg)
|
45 |
+
# On error, we don't change the existing valid configuration
|
46 |
+
return status_msg
|
47 |
+
|
48 |
+
# --- Load initial configuration from the file at startup ---
|
49 |
+
try:
|
50 |
+
with open(DEFAULT_CONFIG_FILE, "r") as f:
|
51 |
+
initial_config = f.read()
|
52 |
+
INITIAL_STATUS = load_config_from_string(initial_config)
|
53 |
+
except FileNotFoundError:
|
54 |
+
logger.warning(f"{DEFAULT_CONFIG_FILE} not found. App will start with an empty configuration.")
|
55 |
+
initial_config = "[]"
|
56 |
+
INITIAL_STATUS = f"Status: {DEFAULT_CONFIG_FILE} not found. Please provide a configuration."
|
57 |
+
except Exception as e:
|
58 |
+
logger.error(f"Error loading initial config file: {e}")
|
59 |
+
initial_config = "[]"
|
60 |
+
INITIAL_STATUS = f"Status: Error loading {DEFAULT_CONFIG_FILE}: {e}"
|
61 |
+
|
62 |
+
# ==============================================================================
|
63 |
+
# MAIN APPLICATION LOGIC
|
64 |
+
# ==============================================================================
|
65 |
+
|
66 |
+
def generate_image(selected_service_name: str, secret_data_str: str):
|
67 |
"""
|
68 |
The main function called by the 'Create' button.
|
69 |
It finds the selected service's public key and calls the core encryption logic.
|
70 |
"""
|
71 |
+
if not all([selected_service_name, secret_data_str]):
|
72 |
+
raise gr.Error("A selected service and secret data are both required.")
|
73 |
|
74 |
try:
|
75 |
+
# Find the public key for the selected service from the loaded global config
|
|
|
|
|
76 |
public_key = None
|
77 |
+
for service in ENDPOINTS:
|
78 |
if service.get("name") == selected_service_name:
|
79 |
public_key = service.get("public_key")
|
80 |
break
|
81 |
|
82 |
if not public_key:
|
83 |
+
raise gr.Error(f"Could not find the service '{selected_service_name}'. Please check the configuration.")
|
84 |
|
85 |
# Call the core function to do the heavy lifting
|
86 |
encrypted_image = core.create_encrypted_image(secret_data_str, public_key)
|
|
|
93 |
|
94 |
except Exception as e:
|
95 |
logger.error(f"Image creation failed: {e}", exc_info=True)
|
|
|
96 |
return None, None, f"β Error: {e}"
|
97 |
|
98 |
# ==============================================================================
|
99 |
# GRADIO INTERFACE
|
100 |
# ==============================================================================
|
101 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
102 |
with gr.Blocks(theme=gr.themes.Soft(), title="KeyLock Image Creator") as demo:
|
103 |
gr.Markdown("# π KeyLock Image Creator")
|
104 |
+
gr.Markdown("Create secure, encrypted PNG images for various decoder services.")
|
105 |
+
|
106 |
+
# This invisible textbox holds the current state of the dropdown choices
|
107 |
+
# This allows us to update the dropdown on the main tab from the config tab
|
108 |
+
dropdown_state = gr.State(ENDPOINT_NAMES)
|
109 |
+
|
110 |
+
with gr.Tabs():
|
111 |
+
with gr.TabItem("Create Image", id=0):
|
112 |
+
with gr.Row():
|
113 |
+
with gr.Column(scale=2):
|
114 |
+
gr.Markdown("### 1. Select Decoder Service")
|
115 |
+
service_dropdown = gr.Dropdown(
|
116 |
+
label="Target Service",
|
117 |
+
choices=ENDPOINT_NAMES,
|
118 |
+
value=ENDPOINT_NAMES[0] if ENDPOINT_NAMES else None,
|
119 |
+
interactive=True
|
120 |
+
)
|
121 |
+
gr.Markdown("### 2. Enter Your Secret Data")
|
122 |
+
secret_data = gr.Textbox(
|
123 |
+
lines=7,
|
124 |
+
label="Secret Data (Key-Value Pairs)",
|
125 |
+
placeholder="USERNAME: [email protected]\nAPI_KEY: sk-12345..."
|
126 |
+
)
|
127 |
+
with gr.Column(scale=1):
|
128 |
+
gr.Markdown("### 3. Generate")
|
129 |
+
create_button = gr.Button("Create Encrypted Image", variant="primary")
|
130 |
+
status_output = gr.Textbox(label="Status", interactive=False)
|
131 |
+
image_output = gr.Image(label="Generated Image", type="pil")
|
132 |
+
download_output = gr.File(label="Download Image")
|
133 |
+
|
134 |
+
with gr.TabItem("Manage Configuration", id=1):
|
135 |
+
gr.Markdown("## Decoder Service Configuration")
|
136 |
+
gr.Markdown("You can override the default `endpoints.json` by pasting a new JSON configuration below.")
|
137 |
json_config = gr.Textbox(
|
138 |
+
lines=15,
|
139 |
label="JSON Configuration",
|
140 |
+
value=initial_config,
|
141 |
+
info="Each service must be an object in a list with 'name', 'link', and 'public_key'."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
142 |
)
|
143 |
+
update_config_button = gr.Button("Apply New Configuration")
|
144 |
+
config_status_output = gr.Textbox(label="Configuration Status", value=INITIAL_STATUS, interactive=False)
|
|
|
|
|
|
|
145 |
|
146 |
# --- Interactivity ---
|
147 |
+
# When the "Apply" button on the config tab is clicked...
|
148 |
+
update_config_button.click(
|
149 |
+
fn=load_config_from_string,
|
150 |
inputs=[json_config],
|
151 |
+
outputs=[config_status_output]
|
152 |
+
).then(
|
153 |
+
# ...then, update the dropdown on the main tab with the new choices.
|
154 |
+
# We use a lambda to get the latest global state.
|
155 |
+
lambda: gr.Dropdown(choices=ENDPOINT_NAMES, value=ENDPOINT_NAMES[0] if ENDPOINT_NAMES else None),
|
156 |
+
inputs=[],
|
157 |
+
outputs=[service_dropdown]
|
158 |
)
|
159 |
|
160 |
+
# When the "Create" button on the main tab is clicked...
|
161 |
create_button.click(
|
162 |
fn=generate_image,
|
163 |
+
inputs=[service_dropdown, secret_data],
|
164 |
outputs=[image_output, download_output, status_output]
|
165 |
)
|
|
|
|
|
|
|
166 |
|
167 |
if __name__ == "__main__":
|
168 |
demo.launch()
|