Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -12,7 +12,7 @@ logger = logging.getLogger(__name__)
|
|
12 |
# CONFIGURATION & DATA HANDLING
|
13 |
# ==============================================================================
|
14 |
|
15 |
-
DEFAULT_CONFIG_FILE = "
|
16 |
|
17 |
def load_config_from_file(filepath: str) -> list:
|
18 |
"""Loads and validates the JSON configuration from a file."""
|
@@ -31,7 +31,6 @@ def load_config_from_file(filepath: str) -> list:
|
|
31 |
return []
|
32 |
except Exception as e:
|
33 |
logger.error(f"Error loading config file {filepath}: {e}")
|
34 |
-
# Return an empty list on error to prevent crashing the app
|
35 |
return []
|
36 |
|
37 |
def save_config_to_file(filepath: str, config_data: list) -> str:
|
@@ -47,8 +46,6 @@ def save_config_to_file(filepath: str, config_data: list) -> str:
|
|
47 |
logger.error(status_msg)
|
48 |
return status_msg
|
49 |
|
50 |
-
# --- Load initial configuration from the file at startup ---
|
51 |
-
# The 'config_state' will hold our list of dictionaries
|
52 |
initial_config = load_config_from_file(DEFAULT_CONFIG_FILE)
|
53 |
|
54 |
# ==============================================================================
|
@@ -62,21 +59,17 @@ def add_new_service(current_config: list, name: str, link: str, public_key: str)
|
|
62 |
|
63 |
new_service = {"name": name, "link": link, "public_key": public_key}
|
64 |
|
65 |
-
# Check for duplicate names
|
66 |
if any(service['name'] == name for service in current_config):
|
67 |
raise gr.Error(f"A service with the name '{name}' already exists. Please use a unique name.")
|
68 |
|
69 |
updated_config = current_config + [new_service]
|
70 |
-
|
71 |
-
# Update dropdown choices
|
72 |
updated_choices = [service['name'] for service in updated_config]
|
73 |
|
74 |
-
# Clear the input forms and provide success feedback
|
75 |
status_update = f"✅ '{name}' added. You can now use it in the 'Create Image' tab. Click 'Save to File' to make it permanent."
|
76 |
return updated_config, gr.Dropdown(choices=updated_choices), status_update, "", "", ""
|
77 |
|
78 |
def generate_image(selected_service_name: str, secret_data_str: str, current_config: list):
|
79 |
-
"""The main image creation function
|
80 |
if not all([selected_service_name, secret_data_str]):
|
81 |
raise gr.Error("A selected service and secret data are both required.")
|
82 |
|
@@ -85,6 +78,7 @@ def generate_image(selected_service_name: str, secret_data_str: str, current_con
|
|
85 |
if not public_key:
|
86 |
raise gr.Error(f"Could not find the service '{selected_service_name}'. Please check the configuration.")
|
87 |
|
|
|
88 |
encrypted_image = core.create_encrypted_image(secret_data_str, public_key)
|
89 |
|
90 |
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp_file:
|
@@ -99,18 +93,17 @@ def generate_image(selected_service_name: str, secret_data_str: str, current_con
|
|
99 |
def get_endpoints_json() -> str:
|
100 |
"""Reads and returns the content of endpoints.json as a string."""
|
101 |
try:
|
102 |
-
with open(
|
103 |
return f.read()
|
104 |
except Exception as e:
|
105 |
-
logger.error(f"Could not read
|
106 |
-
raise gr.Error("Server could not read its
|
|
|
107 |
# ==============================================================================
|
108 |
# GRADIO INTERFACE
|
109 |
# ==============================================================================
|
110 |
|
111 |
with gr.Blocks(theme=gr.themes.Soft(), title="KeyLock Image Creator") as demo:
|
112 |
-
# This gr.State object holds the current configuration (list of dicts)
|
113 |
-
# It's initialized with the data loaded from endpoints.json
|
114 |
config_state = gr.State(initial_config)
|
115 |
|
116 |
gr.Markdown("# 🏭 KeyLock Image Creator")
|
@@ -118,6 +111,7 @@ with gr.Blocks(theme=gr.themes.Soft(), title="KeyLock Image Creator") as demo:
|
|
118 |
|
119 |
with gr.Tabs() as tabs:
|
120 |
with gr.TabItem("Create Image", id=0):
|
|
|
121 |
with gr.Row():
|
122 |
with gr.Column(scale=2):
|
123 |
gr.Markdown("### 1. Select Decoder Service")
|
@@ -141,62 +135,65 @@ with gr.Blocks(theme=gr.themes.Soft(), title="KeyLock Image Creator") as demo:
|
|
141 |
download_output = gr.File(label="Download Image")
|
142 |
|
143 |
with gr.TabItem("Manage Configuration", id=1):
|
|
|
144 |
gr.Markdown("## Decoder Service Configuration")
|
145 |
gr.Markdown("View the current configuration, add new services, and save your changes.")
|
146 |
-
|
147 |
with gr.Row():
|
148 |
with gr.Column(scale=2):
|
149 |
gr.Markdown("### Current Configuration")
|
150 |
-
config_display = gr.JSON(value=initial_config, label="Loaded from
|
151 |
-
|
152 |
with gr.Column(scale=1):
|
153 |
gr.Markdown("### Add a New Service")
|
154 |
new_service_name = gr.Textbox(label="Service Name", placeholder="e.g., My Production API")
|
155 |
new_service_link = gr.Textbox(label="Service Link", placeholder="https://huggingface.co/...")
|
156 |
new_service_key = gr.Textbox(label="Public Key (PEM Format)", lines=5, placeholder="-----BEGIN PUBLIC KEY-----...")
|
157 |
add_service_button = gr.Button("Add Service to Current Session")
|
158 |
-
|
159 |
gr.Markdown("---")
|
160 |
save_config_button = gr.Button("Save Current Configuration to File", variant="secondary")
|
161 |
config_status_output = gr.Textbox(label="Configuration Status", interactive=False)
|
162 |
-
|
163 |
-
#
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
182 |
|
183 |
# --- Interactivity ---
|
184 |
|
185 |
-
# Main image creation logic
|
186 |
create_button.click(
|
187 |
fn=generate_image,
|
188 |
inputs=[service_dropdown, secret_data, config_state],
|
189 |
outputs=[image_output, download_output, status_output]
|
190 |
)
|
191 |
|
192 |
-
# Logic for the "Manage Configuration" tab
|
193 |
add_service_button.click(
|
194 |
fn=add_new_service,
|
195 |
inputs=[config_state, new_service_name, new_service_link, new_service_key],
|
196 |
-
# Outputs: update the state, the dropdown, the status, and clear the form fields
|
197 |
outputs=[config_state, service_dropdown, config_status_output, new_service_name, new_service_link, new_service_key]
|
198 |
).then(
|
199 |
-
# After updating the state, also update the JSON display to reflect the change
|
200 |
lambda state: state,
|
201 |
inputs=[config_state],
|
202 |
outputs=[config_display]
|
@@ -208,8 +205,6 @@ with gr.Blocks(theme=gr.themes.Soft(), title="KeyLock Image Creator") as demo:
|
|
208 |
outputs=[config_status_output]
|
209 |
)
|
210 |
|
211 |
-
# When the configuration state changes (e.g., after adding a service),
|
212 |
-
# update the JSON display on the config tab. This keeps the UI in sync.
|
213 |
config_state.change(
|
214 |
fn=lambda state: state,
|
215 |
inputs=[config_state],
|
|
|
12 |
# CONFIGURATION & DATA HANDLING
|
13 |
# ==============================================================================
|
14 |
|
15 |
+
DEFAULT_CONFIG_FILE = "endpoints.json"
|
16 |
|
17 |
def load_config_from_file(filepath: str) -> list:
|
18 |
"""Loads and validates the JSON configuration from a file."""
|
|
|
31 |
return []
|
32 |
except Exception as e:
|
33 |
logger.error(f"Error loading config file {filepath}: {e}")
|
|
|
34 |
return []
|
35 |
|
36 |
def save_config_to_file(filepath: str, config_data: list) -> str:
|
|
|
46 |
logger.error(status_msg)
|
47 |
return status_msg
|
48 |
|
|
|
|
|
49 |
initial_config = load_config_from_file(DEFAULT_CONFIG_FILE)
|
50 |
|
51 |
# ==============================================================================
|
|
|
59 |
|
60 |
new_service = {"name": name, "link": link, "public_key": public_key}
|
61 |
|
|
|
62 |
if any(service['name'] == name for service in current_config):
|
63 |
raise gr.Error(f"A service with the name '{name}' already exists. Please use a unique name.")
|
64 |
|
65 |
updated_config = current_config + [new_service]
|
|
|
|
|
66 |
updated_choices = [service['name'] for service in updated_config]
|
67 |
|
|
|
68 |
status_update = f"✅ '{name}' added. You can now use it in the 'Create Image' tab. Click 'Save to File' to make it permanent."
|
69 |
return updated_config, gr.Dropdown(choices=updated_choices), status_update, "", "", ""
|
70 |
|
71 |
def generate_image(selected_service_name: str, secret_data_str: str, current_config: list):
|
72 |
+
"""The main image creation function for the UI."""
|
73 |
if not all([selected_service_name, secret_data_str]):
|
74 |
raise gr.Error("A selected service and secret data are both required.")
|
75 |
|
|
|
78 |
if not public_key:
|
79 |
raise gr.Error(f"Could not find the service '{selected_service_name}'. Please check the configuration.")
|
80 |
|
81 |
+
# This call is correct and should not cause an error
|
82 |
encrypted_image = core.create_encrypted_image(secret_data_str, public_key)
|
83 |
|
84 |
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp_file:
|
|
|
93 |
def get_endpoints_json() -> str:
|
94 |
"""Reads and returns the content of endpoints.json as a string."""
|
95 |
try:
|
96 |
+
with open(DEFAULT_CONFIG_FILE, "r") as f:
|
97 |
return f.read()
|
98 |
except Exception as e:
|
99 |
+
logger.error(f"Could not read {DEFAULT_CONFIG_FILE}: {e}")
|
100 |
+
raise gr.Error(f"Server could not read its {DEFAULT_CONFIG_FILE} configuration file.")
|
101 |
+
|
102 |
# ==============================================================================
|
103 |
# GRADIO INTERFACE
|
104 |
# ==============================================================================
|
105 |
|
106 |
with gr.Blocks(theme=gr.themes.Soft(), title="KeyLock Image Creator") as demo:
|
|
|
|
|
107 |
config_state = gr.State(initial_config)
|
108 |
|
109 |
gr.Markdown("# 🏭 KeyLock Image Creator")
|
|
|
111 |
|
112 |
with gr.Tabs() as tabs:
|
113 |
with gr.TabItem("Create Image", id=0):
|
114 |
+
# ... (Your existing UI code is fine, no changes needed here) ...
|
115 |
with gr.Row():
|
116 |
with gr.Column(scale=2):
|
117 |
gr.Markdown("### 1. Select Decoder Service")
|
|
|
135 |
download_output = gr.File(label="Download Image")
|
136 |
|
137 |
with gr.TabItem("Manage Configuration", id=1):
|
138 |
+
# ... (Your existing config management code is fine, no changes needed here) ...
|
139 |
gr.Markdown("## Decoder Service Configuration")
|
140 |
gr.Markdown("View the current configuration, add new services, and save your changes.")
|
|
|
141 |
with gr.Row():
|
142 |
with gr.Column(scale=2):
|
143 |
gr.Markdown("### Current Configuration")
|
144 |
+
config_display = gr.JSON(value=initial_config, label=f"Loaded from {DEFAULT_CONFIG_FILE}")
|
|
|
145 |
with gr.Column(scale=1):
|
146 |
gr.Markdown("### Add a New Service")
|
147 |
new_service_name = gr.Textbox(label="Service Name", placeholder="e.g., My Production API")
|
148 |
new_service_link = gr.Textbox(label="Service Link", placeholder="https://huggingface.co/...")
|
149 |
new_service_key = gr.Textbox(label="Public Key (PEM Format)", lines=5, placeholder="-----BEGIN PUBLIC KEY-----...")
|
150 |
add_service_button = gr.Button("Add Service to Current Session")
|
|
|
151 |
gr.Markdown("---")
|
152 |
save_config_button = gr.Button("Save Current Configuration to File", variant="secondary")
|
153 |
config_status_output = gr.Textbox(label="Configuration Status", interactive=False)
|
154 |
+
|
155 |
+
# FIX: Define API endpoints clearly instead of using a hidden row
|
156 |
+
with gr.TabItem("API", id=2):
|
157 |
+
gr.Markdown("## API Endpoints")
|
158 |
+
gr.Markdown("Use these endpoints for programmatic access.")
|
159 |
+
|
160 |
+
# API for creating an image
|
161 |
+
gr.Interface(
|
162 |
+
# By passing core.create_encrypted_image directly, we ensure it runs in the correct scope
|
163 |
+
fn=core.create_encrypted_image,
|
164 |
+
inputs=[
|
165 |
+
gr.Textbox(label="Secret Data", info="Key-value pairs, one per line."),
|
166 |
+
gr.Textbox(label="Public Key PEM", info="The full PEM-formatted public key.")
|
167 |
+
],
|
168 |
+
outputs=gr.Image(type="pil", label="Encrypted Image"),
|
169 |
+
title="Image Creation API",
|
170 |
+
description="Creates an encrypted image from secret data and a public key. Use via the `/api/create_image/` route.",
|
171 |
+
api_name="create_image"
|
172 |
+
)
|
173 |
+
|
174 |
+
# API to get the endpoint list
|
175 |
+
gr.Interface(
|
176 |
+
fn=get_endpoints_json,
|
177 |
+
inputs=[],
|
178 |
+
outputs=gr.Textbox(label="Endpoint Configuration"),
|
179 |
+
title="Get Endpoints API",
|
180 |
+
description=f"Retrieves the contents of `{DEFAULT_CONFIG_FILE}`. Use via the `/api/get_endpoints/` route.",
|
181 |
+
api_name="get_endpoints"
|
182 |
+
)
|
183 |
|
184 |
# --- Interactivity ---
|
185 |
|
|
|
186 |
create_button.click(
|
187 |
fn=generate_image,
|
188 |
inputs=[service_dropdown, secret_data, config_state],
|
189 |
outputs=[image_output, download_output, status_output]
|
190 |
)
|
191 |
|
|
|
192 |
add_service_button.click(
|
193 |
fn=add_new_service,
|
194 |
inputs=[config_state, new_service_name, new_service_link, new_service_key],
|
|
|
195 |
outputs=[config_state, service_dropdown, config_status_output, new_service_name, new_service_link, new_service_key]
|
196 |
).then(
|
|
|
197 |
lambda state: state,
|
198 |
inputs=[config_state],
|
199 |
outputs=[config_display]
|
|
|
205 |
outputs=[config_status_output]
|
206 |
)
|
207 |
|
|
|
|
|
208 |
config_state.change(
|
209 |
fn=lambda state: state,
|
210 |
inputs=[config_state],
|