darkfire514 commited on
Commit
6d8ba32
·
verified ·
1 Parent(s): 8eee908

Update openspace/dashboard_server.py

Browse files
Files changed (1) hide show
  1. openspace/dashboard_server.py +95 -1
openspace/dashboard_server.py CHANGED
@@ -71,7 +71,12 @@ def create_app() -> Flask:
71
  expected_key = os.environ.get("OPENSPACE_API_KEY")
72
  if expected_key:
73
  auth_header = request.headers.get("Authorization")
74
- if not auth_header or auth_header != f"Bearer {expected_key}":
 
 
 
 
 
75
  abort(401, description="Unauthorized: Invalid or missing API Key")
76
 
77
  @app.route(f"{API_PREFIX}/health", methods=["GET"])
@@ -182,6 +187,95 @@ def create_app() -> Flask:
182
  abort(404, description=f"Unknown skill_id: {skill_id}")
183
  return jsonify(_load_skill_source(record))
184
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
185
  @app.route(f"{API_PREFIX}/workflows", methods=["GET"])
186
  def list_workflows() -> Any:
187
  items = [_build_workflow_summary(path) for path in _discover_workflow_dirs()]
 
71
  expected_key = os.environ.get("OPENSPACE_API_KEY")
72
  if expected_key:
73
  auth_header = request.headers.get("Authorization")
74
+ x_api_key = request.headers.get("X-API-Key")
75
+
76
+ is_valid_auth = auth_header and auth_header == f"Bearer {expected_key}"
77
+ is_valid_x = x_api_key and x_api_key == expected_key
78
+
79
+ if not (is_valid_auth or is_valid_x):
80
  abort(401, description="Unauthorized: Invalid or missing API Key")
81
 
82
  @app.route(f"{API_PREFIX}/health", methods=["GET"])
 
187
  abort(404, description=f"Unknown skill_id: {skill_id}")
188
  return jsonify(_load_skill_source(record))
189
 
190
+ @app.route(f"{API_PREFIX}/artifacts/stage", methods=["POST"])
191
+ def stage_artifact() -> Any:
192
+ if 'files' not in request.files:
193
+ abort(400, description="No files part in the request")
194
+
195
+ files = request.files.getlist('files')
196
+ if not files:
197
+ abort(400, description="No files selected for uploading")
198
+
199
+ import uuid
200
+ artifact_id = f"art_{uuid.uuid4().hex[:8]}"
201
+ stage_dir = PROJECT_ROOT / "skills" / artifact_id
202
+ stage_dir.mkdir(parents=True, exist_ok=True)
203
+
204
+ saved_count = 0
205
+ for file in files:
206
+ if file.filename:
207
+ # Need to handle nested paths like sub/file.txt
208
+ # request.files filename could contain slashes if using webkitRelativePath
209
+ # But typically multipart form-data filename is just the basename
210
+ # However client.py sets: filename="{rel_path}"
211
+ safe_path = (stage_dir / file.filename).resolve()
212
+ if stage_dir not in safe_path.parents and safe_path != stage_dir:
213
+ continue
214
+ safe_path.parent.mkdir(parents=True, exist_ok=True)
215
+ file.save(str(safe_path))
216
+ saved_count += 1
217
+
218
+ return jsonify({
219
+ "artifact_id": artifact_id,
220
+ "stats": {"file_count": saved_count}
221
+ })
222
+
223
+ @app.route(f"{API_PREFIX}/records", methods=["POST"])
224
+ def create_record() -> Any:
225
+ data = request.json
226
+ if not data:
227
+ abort(400, description="Invalid JSON payload")
228
+
229
+ artifact_id = data.get("artifact_id")
230
+ if not artifact_id:
231
+ abort(400, description="Missing artifact_id")
232
+
233
+ stage_dir = PROJECT_ROOT / "skills" / artifact_id
234
+ if not stage_dir.exists():
235
+ abort(404, description="Artifact not found")
236
+
237
+ # The skill folder name should ideally be the skill name
238
+ skill_name = data.get("name", artifact_id)
239
+ final_dir = PROJECT_ROOT / "skills" / skill_name
240
+
241
+ # If final_dir exists, we might need to overwrite or abort
242
+ if final_dir.exists() and final_dir != stage_dir:
243
+ import shutil
244
+ shutil.rmtree(final_dir, ignore_errors=True)
245
+
246
+ try:
247
+ stage_dir.rename(final_dir)
248
+ except OSError:
249
+ import shutil
250
+ shutil.copytree(stage_dir, final_dir, dirs_exist_ok=True)
251
+ shutil.rmtree(stage_dir, ignore_errors=True)
252
+
253
+ # Register the skill using SkillRegistry
254
+ from openspace.skill_engine import SkillRegistry
255
+ registry = SkillRegistry([PROJECT_ROOT / "skills"])
256
+ meta = registry.register_skill_dir(final_dir)
257
+
258
+ if not meta:
259
+ abort(400, description="Failed to register skill (missing SKILL.md or invalid)")
260
+
261
+ store = _get_store()
262
+
263
+ # Sync to DB
264
+ import asyncio
265
+ loop = asyncio.new_event_loop()
266
+ asyncio.set_event_loop(loop)
267
+ try:
268
+ loop.run_until_complete(store.sync_from_registry([meta]))
269
+ finally:
270
+ loop.close()
271
+
272
+ return jsonify({
273
+ "status": "success",
274
+ "skill_id": meta.skill_id,
275
+ "name": meta.name,
276
+ "local_path": str(final_dir)
277
+ })
278
+
279
  @app.route(f"{API_PREFIX}/workflows", methods=["GET"])
280
  def list_workflows() -> Any:
281
  items = [_build_workflow_summary(path) for path in _discover_workflow_dirs()]