Spaces:
Building
Building
Upload 19 files
Browse files- lagacy_ui/api_controller.py +80 -0
- lagacy_ui/auth_controller.py +26 -0
- lagacy_ui/config_controller.py +34 -0
- lagacy_ui/project_controller.py +103 -0
- lagacy_ui/spark_controller.py +48 -0
- lagacy_ui/static/addIntentModal.html +27 -0
- lagacy_ui/static/index.html +47 -0
- lagacy_ui/static/js/admin.js +12 -0
- lagacy_ui/static/js/api.js +111 -0
- lagacy_ui/static/js/auth.js +17 -0
- lagacy_ui/static/js/common.js +31 -0
- lagacy_ui/static/js/config.js +32 -0
- lagacy_ui/static/js/project.js +203 -0
- lagacy_ui/static/js/spark.js +41 -0
- lagacy_ui/static/js/test.js +5 -0
- lagacy_ui/static/newProjectModal.html +17 -0
- lagacy_ui/static/newVersionModal.html +20 -0
- lagacy_ui/static/style.css +59 -0
- lagacy_ui/test_controller.py +21 -0
lagacy_ui/api_controller.py
ADDED
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import APIRouter, HTTPException, Request, Depends
|
2 |
+
from config_provider import get_config, ServiceConfig
|
3 |
+
from service_config import ServiceConfig
|
4 |
+
import json
|
5 |
+
|
6 |
+
router = APIRouter()
|
7 |
+
|
8 |
+
@router.get("/list")
|
9 |
+
def list_apis(config: ServiceConfig = Depends(get_config)):
|
10 |
+
return config.apis
|
11 |
+
|
12 |
+
@router.post("/add")
|
13 |
+
async def add_api(request: Request, config: ServiceConfig = Depends(get_config)):
|
14 |
+
data = await request.json()
|
15 |
+
api_name = data.get("api_name")
|
16 |
+
api_data = data.get("api_data")
|
17 |
+
|
18 |
+
if not api_name or not api_data:
|
19 |
+
raise HTTPException(status_code=400, detail="api_name and api_data are required")
|
20 |
+
|
21 |
+
if api_name in config.apis:
|
22 |
+
raise HTTPException(status_code=400, detail="API with this name already exists")
|
23 |
+
|
24 |
+
config.apis[api_name] = api_data
|
25 |
+
|
26 |
+
with open(config.config_path, "w", encoding="utf-8") as f:
|
27 |
+
json.dump(config, f, indent=2)
|
28 |
+
|
29 |
+
return {"message": f"API {api_name} added"}
|
30 |
+
|
31 |
+
@router.post("/update")
|
32 |
+
async def update_api(request: Request, config: ServiceConfig = Depends(get_config)):
|
33 |
+
data = await request.json()
|
34 |
+
api_name = data.get("api_name")
|
35 |
+
api_data = data.get("api_data")
|
36 |
+
|
37 |
+
if not api_name or not api_data:
|
38 |
+
raise HTTPException(status_code=400, detail="api_name and api_data are required")
|
39 |
+
|
40 |
+
if api_name not in config.apis:
|
41 |
+
raise HTTPException(status_code=404, detail="API not found")
|
42 |
+
|
43 |
+
config.apis[api_name] = api_data
|
44 |
+
|
45 |
+
with open(config.config_path, "w", encoding="utf-8") as f:
|
46 |
+
json.dump(config, f, indent=2)
|
47 |
+
|
48 |
+
return {"message": f"API {api_name} updated"}
|
49 |
+
|
50 |
+
@router.post("/delete")
|
51 |
+
async def delete_api(request: Request, config: ServiceConfig = Depends(get_config)):
|
52 |
+
data = await request.json()
|
53 |
+
api_name = data.get("api_name")
|
54 |
+
|
55 |
+
if not api_name:
|
56 |
+
raise HTTPException(status_code=400, detail="api_name is required")
|
57 |
+
|
58 |
+
if api_name not in config.apis:
|
59 |
+
raise HTTPException(status_code=404, detail="API not found")
|
60 |
+
|
61 |
+
# Check if API is used in any intent
|
62 |
+
used_in = []
|
63 |
+
for project_name, project in config.projects.items():
|
64 |
+
for version in project.get("versions", []):
|
65 |
+
for intent in version.get("intents", []):
|
66 |
+
if intent.get("action") == api_name:
|
67 |
+
used_in.append(f"{project_name} → v{version.get('version_number')} → {intent.get('name')}")
|
68 |
+
|
69 |
+
if used_in:
|
70 |
+
raise HTTPException(
|
71 |
+
status_code=400,
|
72 |
+
detail=f"API '{api_name}' is used in intents: {', '.join(used_in)}. Cannot delete."
|
73 |
+
)
|
74 |
+
|
75 |
+
del config.apis[api_name]
|
76 |
+
|
77 |
+
with open(config.config_path, "w", encoding="utf-8") as f:
|
78 |
+
json.dump(config, f, indent=2)
|
79 |
+
|
80 |
+
return {"message": f"API {api_name} deleted"}
|
lagacy_ui/auth_controller.py
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import APIRouter, Depends, HTTPException
|
2 |
+
from config_provider import get_config, ServiceConfig
|
3 |
+
from pydantic import BaseModel
|
4 |
+
import hashlib
|
5 |
+
|
6 |
+
router = APIRouter()
|
7 |
+
|
8 |
+
class LoginRequest(BaseModel):
|
9 |
+
username: str
|
10 |
+
password: str
|
11 |
+
|
12 |
+
def verify_password(stored_hash, input_password):
|
13 |
+
# Basit SHA256 hash kontrolü (salt + hash mekanizması uygulanabilir)
|
14 |
+
input_hash = hashlib.sha256(input_password.encode()).hexdigest()
|
15 |
+
return stored_hash == input_hash
|
16 |
+
|
17 |
+
@router.post("/auth/login")
|
18 |
+
def login(request: LoginRequest, config: ServiceConfig = Depends(get_config)):
|
19 |
+
user = next((u for u in config.data.get('users', []) if u['username'] == request.username), None)
|
20 |
+
if not user:
|
21 |
+
raise HTTPException(status_code=401, detail="Invalid username or password")
|
22 |
+
|
23 |
+
if not verify_password(user['password_hash'], request.password):
|
24 |
+
raise HTTPException(status_code=401, detail="Invalid username or password")
|
25 |
+
|
26 |
+
return { "status": "success" }
|
lagacy_ui/config_controller.py
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import APIRouter, HTTPException, Request, Depends
|
2 |
+
from config_provider import get_config, ServiceConfig
|
3 |
+
from service_config import ServiceConfig
|
4 |
+
import json
|
5 |
+
|
6 |
+
router = APIRouter()
|
7 |
+
|
8 |
+
@router.get("/get")
|
9 |
+
def get_config_values(config: ServiceConfig = Depends(get_config)):
|
10 |
+
return {
|
11 |
+
"work_mode": config.work_mode,
|
12 |
+
"cloud_token": config.cloud_token
|
13 |
+
}
|
14 |
+
|
15 |
+
@router.post("/update")
|
16 |
+
async def update_config(request: Request, config: ServiceConfig = Depends(get_config)):
|
17 |
+
data = await request.json()
|
18 |
+
work_mode = data.get("work_mode")
|
19 |
+
cloud_token = data.get("cloud_token")
|
20 |
+
|
21 |
+
if work_mode not in ["hfcloud", "cloud", "on-premise"]:
|
22 |
+
raise HTTPException(status_code=400, detail="Invalid work mode")
|
23 |
+
|
24 |
+
if (work_mode in ["hfcloud", "cloud"]) and not cloud_token:
|
25 |
+
raise HTTPException(status_code=400, detail="Cloud token required for selected mode")
|
26 |
+
|
27 |
+
config.work_mode = work_mode
|
28 |
+
config.cloud_token = cloud_token
|
29 |
+
|
30 |
+
with open(config.config_path, "w", encoding="utf-8") as f:
|
31 |
+
json.dump(config, f, indent=2)
|
32 |
+
|
33 |
+
return {"message": "Configuration updated"}
|
34 |
+
|
lagacy_ui/project_controller.py
ADDED
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import APIRouter, Depends, HTTPException
|
2 |
+
from config_provider import get_config, ServiceConfig
|
3 |
+
from utils import save_service_config, log
|
4 |
+
import copy
|
5 |
+
|
6 |
+
router = APIRouter()
|
7 |
+
|
8 |
+
@router.get("/project/details")
|
9 |
+
def get_project_details(config: ServiceConfig = Depends(get_config)):
|
10 |
+
project = config.get_current_project()
|
11 |
+
return project
|
12 |
+
|
13 |
+
@router.post("/project/update")
|
14 |
+
def update_project(config: ServiceConfig = Depends(get_config)):
|
15 |
+
# Implement project update logic here if needed
|
16 |
+
save_service_config(config)
|
17 |
+
log("✅ Project updated")
|
18 |
+
return { "status": "updated" }
|
19 |
+
|
20 |
+
@router.post("/project/publish")
|
21 |
+
def publish_project(config: ServiceConfig = Depends(get_config)):
|
22 |
+
project = config.get_current_project()
|
23 |
+
current_version = next((v for v in project['versions'] if not v.get('published', False)), None)
|
24 |
+
if not current_version:
|
25 |
+
raise HTTPException(status_code=400, detail="No unpublished version found")
|
26 |
+
current_version['published'] = True
|
27 |
+
save_service_config(config)
|
28 |
+
log(f"✅ Version {current_version['no']} published for project {project['name']}")
|
29 |
+
return { "status": "published" }
|
30 |
+
|
31 |
+
@router.post("/project/new")
|
32 |
+
def create_project(name: str, config: ServiceConfig = Depends(get_config)):
|
33 |
+
if any(p['name'] == name for p in config.data['projects']):
|
34 |
+
raise HTTPException(status_code=400, detail="Project already exists")
|
35 |
+
new_project = {
|
36 |
+
"name": name,
|
37 |
+
"versions": []
|
38 |
+
}
|
39 |
+
config.data['projects'].append(new_project)
|
40 |
+
save_service_config(config)
|
41 |
+
log(f"✅ New project '{name}' created")
|
42 |
+
return { "status": "success" }
|
43 |
+
|
44 |
+
@router.post("/project/delete")
|
45 |
+
def delete_project(name: str, config: ServiceConfig = Depends(get_config)):
|
46 |
+
project = next((p for p in config.data['projects'] if p['name'] == name), None)
|
47 |
+
if not project:
|
48 |
+
raise HTTPException(status_code=404, detail="Project not found")
|
49 |
+
config.data['projects'].remove(project)
|
50 |
+
save_service_config(config)
|
51 |
+
log(f"🗑️ Project '{name}' deleted")
|
52 |
+
return { "status": "success" }
|
53 |
+
|
54 |
+
@router.get("/project/versions")
|
55 |
+
def get_project_versions(config: ServiceConfig = Depends(get_config)):
|
56 |
+
project = config.get_current_project()
|
57 |
+
return project['versions']
|
58 |
+
|
59 |
+
@router.post("/project/new-version")
|
60 |
+
def create_new_version(project_name: str, base_version_no: int, config: ServiceConfig = Depends(get_config)):
|
61 |
+
project = next((p for p in config.data['projects'] if p['name'] == project_name), None)
|
62 |
+
if not project:
|
63 |
+
raise HTTPException(status_code=404, detail="Project not found")
|
64 |
+
base_version = next((v for v in project['versions'] if v['no'] == base_version_no), None)
|
65 |
+
if not base_version:
|
66 |
+
raise HTTPException(status_code=404, detail="Base version not found")
|
67 |
+
new_version_no = max((v['no'] for v in project['versions']), default=0) + 1
|
68 |
+
new_version = copy.deepcopy(base_version)
|
69 |
+
new_version['no'] = new_version_no
|
70 |
+
new_version['published'] = False
|
71 |
+
project['versions'].append(new_version)
|
72 |
+
save_service_config(config)
|
73 |
+
log(f"✅ New version {new_version_no} created for project {project_name}")
|
74 |
+
return { "status": "success", "new_version": new_version_no }
|
75 |
+
|
76 |
+
@router.post("/project/delete-version")
|
77 |
+
def delete_version(project_name: str, version_no: int, config: ServiceConfig = Depends(get_config)):
|
78 |
+
project = next((p for p in config.data['projects'] if p['name'] == project_name), None)
|
79 |
+
if not project:
|
80 |
+
raise HTTPException(status_code=404, detail="Project not found")
|
81 |
+
version = next((v for v in project['versions'] if v['no'] == version_no), None)
|
82 |
+
if not version:
|
83 |
+
raise HTTPException(status_code=404, detail="Version not found")
|
84 |
+
project['versions'].remove(version)
|
85 |
+
save_service_config(config)
|
86 |
+
log(f"🗑️ Version {version_no} deleted from project {project_name}")
|
87 |
+
return { "status": "success" }
|
88 |
+
|
89 |
+
@router.post("/project/update-api")
|
90 |
+
def update_api(project_name: str, version_no: int, api_data: dict, config: ServiceConfig = Depends(get_config)):
|
91 |
+
project = next((p for p in config.data['projects'] if p['name'] == project_name), None)
|
92 |
+
if not project:
|
93 |
+
raise HTTPException(status_code=404, detail="Project not found")
|
94 |
+
version = next((v for v in project['versions'] if v['no'] == version_no), None)
|
95 |
+
if not version:
|
96 |
+
raise HTTPException(status_code=404, detail="Version not found")
|
97 |
+
existing_api = next((a for a in version['apis'] if a['name'] == api_data['name']), None)
|
98 |
+
if existing_api:
|
99 |
+
version['apis'].remove(existing_api)
|
100 |
+
version['apis'].append(api_data)
|
101 |
+
save_service_config(config)
|
102 |
+
log(f"🔧 API '{api_data['name']}' updated in project {project_name} version {version_no}")
|
103 |
+
return { "status": "success" }
|
lagacy_ui/spark_controller.py
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import APIRouter, Depends, HTTPException
|
2 |
+
from config_provider import get_config, ServiceConfig
|
3 |
+
from utils import save_service_config, log
|
4 |
+
import requests
|
5 |
+
|
6 |
+
router = APIRouter()
|
7 |
+
|
8 |
+
@router.get("/spark/projects")
|
9 |
+
def get_spark_projects(config: ServiceConfig = Depends(get_config)):
|
10 |
+
try:
|
11 |
+
spark_url = config.spark_endpoint + "/projects"
|
12 |
+
response = requests.get(spark_url, timeout=5)
|
13 |
+
response.raise_for_status()
|
14 |
+
projects = response.json()
|
15 |
+
return { "projects": projects }
|
16 |
+
except Exception as e:
|
17 |
+
log(f"⚠️ Spark service unreachable: {str(e)}")
|
18 |
+
return { "error": True, "projects": [] }
|
19 |
+
|
20 |
+
@router.post("/spark/enable")
|
21 |
+
def enable_project(project_name: str, config: ServiceConfig = Depends(get_config)):
|
22 |
+
project = next((p for p in config.data['projects'] if p['name'] == project_name), None)
|
23 |
+
if not project:
|
24 |
+
raise HTTPException(status_code=404, detail="Project not found")
|
25 |
+
project['enabled'] = True
|
26 |
+
save_service_config(config)
|
27 |
+
log(f"✅ Project '{project_name}' enabled")
|
28 |
+
return { "status": "enabled" }
|
29 |
+
|
30 |
+
@router.post("/spark/disable")
|
31 |
+
def disable_project(project_name: str, config: ServiceConfig = Depends(get_config)):
|
32 |
+
project = next((p for p in config.data['projects'] if p['name'] == project_name), None)
|
33 |
+
if not project:
|
34 |
+
raise HTTPException(status_code=404, detail="Project not found")
|
35 |
+
project['enabled'] = False
|
36 |
+
save_service_config(config)
|
37 |
+
log(f"✅ Project '{project_name}' disabled")
|
38 |
+
return { "status": "disabled" }
|
39 |
+
|
40 |
+
@router.post("/spark/delete")
|
41 |
+
def delete_project_spark(project_name: str, config: ServiceConfig = Depends(get_config)):
|
42 |
+
project = next((p for p in config.data['projects'] if p['name'] == project_name), None)
|
43 |
+
if not project:
|
44 |
+
raise HTTPException(status_code=404, detail="Project not found")
|
45 |
+
config.data['projects'].remove(project)
|
46 |
+
save_service_config(config)
|
47 |
+
log(f"🗑️ Project '{project_name}' deleted from Spark")
|
48 |
+
return { "status": "deleted" }
|
lagacy_ui/static/addIntentModal.html
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!-- Add/Edit API Modal -->
|
2 |
+
<div class="modal fade" id="addIntentModal" tabindex="-1" aria-labelledby="addIntentModalLabel" aria-hidden="true">
|
3 |
+
<div class="modal-dialog">
|
4 |
+
<div class="modal-content">
|
5 |
+
<div class="modal-header">
|
6 |
+
<h5 class="modal-title">Add / Edit API</h5>
|
7 |
+
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
8 |
+
</div>
|
9 |
+
<div class="modal-body">
|
10 |
+
<input type="text" id="apiNameInput" class="form-control mb-2" placeholder="API Name">
|
11 |
+
<input type="text" id="apiUrlInput" class="form-control mb-2" placeholder="API URL">
|
12 |
+
<input type="text" id="apiAuthInput" class="form-control mb-2" placeholder="Auth (if any)">
|
13 |
+
<select id="apiMethodSelect" class="form-control mb-2">
|
14 |
+
<option value="GET">GET</option>
|
15 |
+
<option value="POST">POST</option>
|
16 |
+
</select>
|
17 |
+
<div id="headerContainer" class="mb-2">
|
18 |
+
<!-- Dynamic header rows -->
|
19 |
+
</div>
|
20 |
+
<button class="btn btn-secondary mb-2" onclick="addHeaderRow()">+ Add Header</button>
|
21 |
+
</div>
|
22 |
+
<div class="modal-footer">
|
23 |
+
<button class="btn btn-primary" onclick="saveAPI()">Save API</button>
|
24 |
+
</div>
|
25 |
+
</div>
|
26 |
+
</div>
|
27 |
+
</div>
|
lagacy_ui/static/index.html
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html lang="en">
|
3 |
+
<head>
|
4 |
+
<meta charset="UTF-8">
|
5 |
+
<title>Flare Project Admin</title>
|
6 |
+
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
|
7 |
+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
|
8 |
+
<script src="project.js" defer></script>
|
9 |
+
</head>
|
10 |
+
<body>
|
11 |
+
<div class="container mt-4">
|
12 |
+
<div id="loginPanel">
|
13 |
+
<h1>Login</h1>
|
14 |
+
<input type="text" id="usernameInput" class="form-control mb-2" placeholder="Username">
|
15 |
+
<input type="password" id="passwordInput" class="form-control mb-2" placeholder="Password">
|
16 |
+
<button class="btn btn-primary" onclick="login()">Login</button>
|
17 |
+
<div id="loginError" class="text-danger mt-2" style="display:none;">Invalid credentials</div>
|
18 |
+
</div>
|
19 |
+
|
20 |
+
<div id="mainPanel" style="display:none;">
|
21 |
+
<h1>Flare Project Admin Panel</h1>
|
22 |
+
|
23 |
+
<div class="mb-3">
|
24 |
+
<button class="btn btn-success" id="newProjectBtn">+ New Project</button>
|
25 |
+
<button class="btn btn-primary" id="newVersionBtn">+ New Version</button>
|
26 |
+
<button class="btn btn-secondary" id="saveChangesBtn">Save Project</button>
|
27 |
+
<button class="btn btn-warning" id="publishVersionBtn">Publish Version</button>
|
28 |
+
</div>
|
29 |
+
|
30 |
+
<div id="projectDetails" class="mb-4">
|
31 |
+
<!-- Project details will be injected here -->
|
32 |
+
</div>
|
33 |
+
|
34 |
+
<div>
|
35 |
+
<h3>Intents</h3>
|
36 |
+
<button class="btn btn-outline-primary mb-2" id="addIntentBtn">+ Add Intent / API</button>
|
37 |
+
</div>
|
38 |
+
|
39 |
+
<div>
|
40 |
+
<h3>Spark Project List</h3>
|
41 |
+
<ul id="sparkProjectList" class="list-group mb-4"></ul>
|
42 |
+
<button class="btn btn-info" onclick="loadSparkProjects()">Get Spark Project List</button>
|
43 |
+
</div>
|
44 |
+
</div>
|
45 |
+
</div>
|
46 |
+
</body>
|
47 |
+
</html>
|
lagacy_ui/static/js/admin.js
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
function seedTestData() {
|
2 |
+
apiPost('/project/seed/test_data', {})
|
3 |
+
.then(data => showResult('admin-result', data))
|
4 |
+
.catch(err => console.error(err));
|
5 |
+
}
|
6 |
+
|
7 |
+
function clearAllProjects() {
|
8 |
+
if (!confirm('Are you sure you want to clear all projects? This cannot be undone!')) return;
|
9 |
+
apiPost('/project/clear/all', {})
|
10 |
+
.then(data => showResult('admin-result', data))
|
11 |
+
.catch(err => console.error(err));
|
12 |
+
}
|
lagacy_ui/static/js/api.js
ADDED
@@ -0,0 +1,111 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
function listApis() {
|
2 |
+
apiGet('/api/list')
|
3 |
+
.then(data => {
|
4 |
+
const body = document.getElementById('api-body');
|
5 |
+
body.innerHTML = '';
|
6 |
+
const keys = Object.keys(data);
|
7 |
+
if (keys.length === 0) {
|
8 |
+
const row = document.createElement('tr');
|
9 |
+
row.innerHTML = `<td colspan="4" class="text-danger">No APIs defined.</td>`;
|
10 |
+
body.appendChild(row);
|
11 |
+
return;
|
12 |
+
}
|
13 |
+
|
14 |
+
keys.forEach(apiName => {
|
15 |
+
const api = data[apiName];
|
16 |
+
const row = document.createElement('tr');
|
17 |
+
row.innerHTML = `
|
18 |
+
<td>${apiName}</td>
|
19 |
+
<td>${api.url}</td>
|
20 |
+
<td>${api.method}</td>
|
21 |
+
<td>
|
22 |
+
<button class="btn btn-sm btn-warning" onclick="editApi('${apiName}')">Edit</button>
|
23 |
+
<button class="btn btn-sm btn-danger" onclick="deleteApi('${apiName}')">Delete</button>
|
24 |
+
</td>
|
25 |
+
`;
|
26 |
+
body.appendChild(row);
|
27 |
+
});
|
28 |
+
})
|
29 |
+
.catch(err => {
|
30 |
+
console.error(err);
|
31 |
+
showResult('api-result', { detail: 'Failed to load API list.' });
|
32 |
+
});
|
33 |
+
}
|
34 |
+
|
35 |
+
function addApi() {
|
36 |
+
const apiName = prompt('Enter new API name:');
|
37 |
+
if (!apiName) return;
|
38 |
+
|
39 |
+
const url = prompt('Enter API URL:');
|
40 |
+
const method = prompt('Enter HTTP method (GET, POST, etc.):');
|
41 |
+
|
42 |
+
if (!url || !method) {
|
43 |
+
alert('URL and method are required.');
|
44 |
+
return;
|
45 |
+
}
|
46 |
+
|
47 |
+
const apiData = {
|
48 |
+
url: url,
|
49 |
+
method: method,
|
50 |
+
headers: [],
|
51 |
+
body: {},
|
52 |
+
timeout: 5,
|
53 |
+
retry_count: 0
|
54 |
+
};
|
55 |
+
|
56 |
+
apiPost('/api/add', { api_name: apiName, api_data: apiData })
|
57 |
+
.then(data => {
|
58 |
+
showResult('api-result', data);
|
59 |
+
listApis();
|
60 |
+
})
|
61 |
+
.catch(err => {
|
62 |
+
console.error(err);
|
63 |
+
showResult('api-result', { detail: 'Failed to add API.' });
|
64 |
+
});
|
65 |
+
}
|
66 |
+
|
67 |
+
function editApi(apiName) {
|
68 |
+
const url = prompt('Enter new API URL:');
|
69 |
+
const method = prompt('Enter new HTTP method:');
|
70 |
+
|
71 |
+
if (!url || !method) {
|
72 |
+
alert('URL and method are required.');
|
73 |
+
return;
|
74 |
+
}
|
75 |
+
|
76 |
+
const apiData = {
|
77 |
+
url: url,
|
78 |
+
method: method,
|
79 |
+
headers: [],
|
80 |
+
body: {},
|
81 |
+
timeout: 5,
|
82 |
+
retry_count: 0
|
83 |
+
};
|
84 |
+
|
85 |
+
apiPost('/api/update', { api_name: apiName, api_data: apiData })
|
86 |
+
.then(data => {
|
87 |
+
showResult('api-result', data);
|
88 |
+
listApis();
|
89 |
+
})
|
90 |
+
.catch(err => {
|
91 |
+
console.error(err);
|
92 |
+
showResult('api-result', { detail: 'Failed to update API.' });
|
93 |
+
});
|
94 |
+
}
|
95 |
+
|
96 |
+
function deleteApi(apiName) {
|
97 |
+
if (!confirm(`Are you sure you want to delete API '${apiName}'?`)) return;
|
98 |
+
|
99 |
+
apiPost('/api/delete', { api_name: apiName })
|
100 |
+
.then(data => {
|
101 |
+
showResult('api-result', data);
|
102 |
+
listApis();
|
103 |
+
})
|
104 |
+
.catch(err => {
|
105 |
+
console.error(err);
|
106 |
+
if (err.detail) {
|
107 |
+
alert(`❗ ${err.detail}`);
|
108 |
+
}
|
109 |
+
showResult('api-result', { detail: 'Failed to delete API.' });
|
110 |
+
});
|
111 |
+
}
|
lagacy_ui/static/js/auth.js
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
function login() {
|
2 |
+
const username = document.getElementById('login-username').value;
|
3 |
+
const password = document.getElementById('login-password').value;
|
4 |
+
apiPost('/auth/login', { username, password })
|
5 |
+
.then(data => {
|
6 |
+
if (data.message === "Login successful") {
|
7 |
+
document.getElementById('login-panel').classList.add('d-none');
|
8 |
+
document.getElementById('main-tabs').classList.remove('d-none');
|
9 |
+
} else {
|
10 |
+
showResult('login-result', data);
|
11 |
+
}
|
12 |
+
})
|
13 |
+
.catch(err => {
|
14 |
+
console.error(err);
|
15 |
+
document.getElementById('login-result').innerText = "Error connecting to server.";
|
16 |
+
});
|
17 |
+
}
|
lagacy_ui/static/js/common.js
ADDED
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
const apiBase = 'https://ucsturkey-flare.hf.space';
|
2 |
+
|
3 |
+
function apiPost(path, body) {
|
4 |
+
return fetch(`${apiBase}${path}`, {
|
5 |
+
method: 'POST',
|
6 |
+
headers: { 'Content-Type': 'application/json' },
|
7 |
+
body: JSON.stringify(body)
|
8 |
+
}).then(res => {
|
9 |
+
if (!res.ok) {
|
10 |
+
return res.json().then(err => { throw err; });
|
11 |
+
}
|
12 |
+
return res.json();
|
13 |
+
});
|
14 |
+
}
|
15 |
+
|
16 |
+
|
17 |
+
function apiGet(path) {
|
18 |
+
return fetch(`${apiBase}${path}`)
|
19 |
+
.then(res => res.json());
|
20 |
+
}
|
21 |
+
|
22 |
+
function showResult(id, data) {
|
23 |
+
const el = document.getElementById(id);
|
24 |
+
if (data.detail) {
|
25 |
+
el.innerText = data.detail;
|
26 |
+
el.classList.add('text-danger');
|
27 |
+
} else {
|
28 |
+
el.innerText = data.message || JSON.stringify(data, null, 2);
|
29 |
+
el.classList.remove('text-danger');
|
30 |
+
}
|
31 |
+
}
|
lagacy_ui/static/js/config.js
ADDED
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
function getConfig() {
|
2 |
+
apiGet('/config/get')
|
3 |
+
.then(data => {
|
4 |
+
document.getElementById('work-mode').value = data.work_mode;
|
5 |
+
document.getElementById('cloud-token').value = data.cloud_token;
|
6 |
+
toggleCloudToken();
|
7 |
+
})
|
8 |
+
.catch(err => console.error(err));
|
9 |
+
}
|
10 |
+
|
11 |
+
function updateConfig() {
|
12 |
+
const workMode = document.getElementById('work-mode').value;
|
13 |
+
const cloudToken = document.getElementById('cloud-token').value;
|
14 |
+
|
15 |
+
if ((workMode === 'hfcloud' || workMode === 'cloud') && cloudToken.trim() === '') {
|
16 |
+
alert('Cloud token cannot be empty for selected work mode.');
|
17 |
+
return;
|
18 |
+
}
|
19 |
+
|
20 |
+
apiPost('/config/update', { work_mode: workMode, cloud_token: cloudToken })
|
21 |
+
.then(data => showResult('config-result', data))
|
22 |
+
.catch(err => console.error(err));
|
23 |
+
}
|
24 |
+
|
25 |
+
function toggleCloudToken() {
|
26 |
+
const workMode = document.getElementById('work-mode').value;
|
27 |
+
const cloudTokenField = document.getElementById('cloud-token');
|
28 |
+
cloudTokenField.disabled = (workMode === 'on-premise');
|
29 |
+
if (cloudTokenField.disabled) {
|
30 |
+
cloudTokenField.value = '';
|
31 |
+
}
|
32 |
+
}
|
lagacy_ui/static/js/project.js
ADDED
@@ -0,0 +1,203 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
document.addEventListener('DOMContentLoaded', function() {
|
2 |
+
loadProjectDetails();
|
3 |
+
setupEventListeners();
|
4 |
+
});
|
5 |
+
|
6 |
+
let CURRENT_PROJECT = null;
|
7 |
+
let CURRENT_VERSION = null;
|
8 |
+
|
9 |
+
function setupEventListeners() {
|
10 |
+
document.getElementById('saveChangesBtn').addEventListener('click', saveProject);
|
11 |
+
document.getElementById('publishVersionBtn').addEventListener('click', publishProject);
|
12 |
+
document.getElementById('addIntentBtn').addEventListener('click', showAddIntentModal);
|
13 |
+
document.getElementById('newVersionBtn').addEventListener('click', showNewVersionModal);
|
14 |
+
document.getElementById('newProjectBtn').addEventListener('click', showNewProjectModal);
|
15 |
+
}
|
16 |
+
|
17 |
+
function loadProjectDetails() {
|
18 |
+
fetch('/project/details')
|
19 |
+
.then(response => response.json())
|
20 |
+
.then(data => {
|
21 |
+
CURRENT_PROJECT = data.name;
|
22 |
+
CURRENT_VERSION = data.version;
|
23 |
+
renderProjectDetails(data);
|
24 |
+
})
|
25 |
+
.catch(error => {
|
26 |
+
console.error('Error loading project details:', error);
|
27 |
+
alert('Failed to load project details.');
|
28 |
+
});
|
29 |
+
}
|
30 |
+
|
31 |
+
function renderProjectDetails(data) {
|
32 |
+
const container = document.getElementById('projectDetails');
|
33 |
+
container.innerHTML = '';
|
34 |
+
|
35 |
+
const published = data.published ? '✅ Yes' : '❌ No';
|
36 |
+
container.innerHTML += `<p><strong>Project:</strong> ${data.name}</p>`;
|
37 |
+
container.innerHTML += `<p><strong>Version:</strong> ${data.version}</p>`;
|
38 |
+
container.innerHTML += `<p><strong>Published:</strong> ${published}</p>`;
|
39 |
+
|
40 |
+
const list = document.createElement('ul');
|
41 |
+
data.intents.forEach(intent => {
|
42 |
+
const li = document.createElement('li');
|
43 |
+
li.style.marginBottom = '8px';
|
44 |
+
li.innerHTML = `${intent.name}
|
45 |
+
<button class="btn btn-sm btn-primary" onclick="editIntent('${intent.id}')">Edit</button>
|
46 |
+
<button class="btn btn-sm btn-danger" onclick="removeIntent('${intent.id}')">-</button>`;
|
47 |
+
list.appendChild(li);
|
48 |
+
});
|
49 |
+
container.appendChild(list);
|
50 |
+
|
51 |
+
document.getElementById('publishVersionBtn').disabled = data.published;
|
52 |
+
}
|
53 |
+
|
54 |
+
function saveProject() {
|
55 |
+
fetch('/project/update', { method: 'POST' })
|
56 |
+
.then(response => response.json())
|
57 |
+
.then(() => alert('Project saved!'))
|
58 |
+
.catch(() => alert('Failed to save project.'));
|
59 |
+
}
|
60 |
+
|
61 |
+
function publishProject() {
|
62 |
+
fetch('/project/publish', { method: 'POST' })
|
63 |
+
.then(response => response.json())
|
64 |
+
.then(() => alert('Project published!'))
|
65 |
+
.catch(() => alert('Failed to publish project.'));
|
66 |
+
}
|
67 |
+
|
68 |
+
function showNewProjectModal() {
|
69 |
+
const name = prompt('Enter new project name:');
|
70 |
+
if (name) {
|
71 |
+
fetch('/project/new', {
|
72 |
+
method: 'POST',
|
73 |
+
headers: { 'Content-Type': 'application/json' },
|
74 |
+
body: JSON.stringify({ name })
|
75 |
+
})
|
76 |
+
.then(r => r.json())
|
77 |
+
.then(() => alert('Project created!'))
|
78 |
+
.catch(() => alert('Failed to create project.'));
|
79 |
+
}
|
80 |
+
}
|
81 |
+
|
82 |
+
function showNewVersionModal() {
|
83 |
+
fetch('/project/versions')
|
84 |
+
.then(response => response.json())
|
85 |
+
.then(versions => {
|
86 |
+
const select = document.getElementById('baseVersionSelect');
|
87 |
+
select.innerHTML = '';
|
88 |
+
versions.forEach(v => {
|
89 |
+
const option = document.createElement('option');
|
90 |
+
option.value = v.no;
|
91 |
+
option.textContent = `Version ${v.no}`;
|
92 |
+
select.appendChild(option);
|
93 |
+
});
|
94 |
+
$('#newVersionModal').modal('show');
|
95 |
+
})
|
96 |
+
.catch(() => alert('Failed to load versions.'));
|
97 |
+
}
|
98 |
+
|
99 |
+
function createNewVersion() {
|
100 |
+
const baseVersionId = document.getElementById('baseVersionSelect').value;
|
101 |
+
fetch('/project/new-version', {
|
102 |
+
method: 'POST',
|
103 |
+
headers: { 'Content-Type': 'application/json' },
|
104 |
+
body: JSON.stringify({
|
105 |
+
project_name: CURRENT_PROJECT,
|
106 |
+
base_version_no: parseInt(baseVersionId)
|
107 |
+
})
|
108 |
+
})
|
109 |
+
.then(r => r.json())
|
110 |
+
.then(() => {
|
111 |
+
alert('New version created!');
|
112 |
+
loadProjectDetails();
|
113 |
+
})
|
114 |
+
.catch(() => alert('Failed to create new version.'));
|
115 |
+
}
|
116 |
+
|
117 |
+
function loadSparkProjects() {
|
118 |
+
fetch('/spark/projects')
|
119 |
+
.then(response => response.json())
|
120 |
+
.then(data => {
|
121 |
+
if (data.error) {
|
122 |
+
alert('⚠ Spark service unreachable.');
|
123 |
+
return;
|
124 |
+
}
|
125 |
+
const list = document.getElementById('sparkProjectList');
|
126 |
+
list.innerHTML = '';
|
127 |
+
data.projects.forEach(p => {
|
128 |
+
const li = document.createElement('li');
|
129 |
+
li.textContent = p.name;
|
130 |
+
list.appendChild(li);
|
131 |
+
});
|
132 |
+
})
|
133 |
+
.catch(() => alert('⚠ Spark service unreachable.'));
|
134 |
+
}
|
135 |
+
|
136 |
+
function showAddIntentModal() {
|
137 |
+
$('#addIntentModal').modal('show');
|
138 |
+
}
|
139 |
+
|
140 |
+
function addHeaderRow() {
|
141 |
+
const container = document.getElementById('headerContainer');
|
142 |
+
const row = document.createElement('div');
|
143 |
+
row.className = 'header-row mb-2';
|
144 |
+
row.innerHTML = `
|
145 |
+
<input type="text" class="form-control d-inline-block header-key" placeholder="Key" style="width: 40%">
|
146 |
+
<input type="text" class="form-control d-inline-block header-value" placeholder="Value" style="width: 40%">
|
147 |
+
<button class="btn btn-danger btn-sm" onclick="this.parentElement.remove()">-</button>`;
|
148 |
+
container.appendChild(row);
|
149 |
+
}
|
150 |
+
|
151 |
+
function saveAPI() {
|
152 |
+
const name = document.getElementById('apiNameInput').value;
|
153 |
+
const url = document.getElementById('apiUrlInput').value;
|
154 |
+
const method = document.getElementById('apiMethodSelect').value;
|
155 |
+
const auth = document.getElementById('apiAuthInput').value;
|
156 |
+
const headers = [];
|
157 |
+
|
158 |
+
document.querySelectorAll('.header-row').forEach(row => {
|
159 |
+
const key = row.querySelector('.header-key').value;
|
160 |
+
const value = row.querySelector('.header-value').value;
|
161 |
+
if (key && value) {
|
162 |
+
headers.push({ key, value });
|
163 |
+
}
|
164 |
+
});
|
165 |
+
|
166 |
+
const apiData = { name, url, method, auth, headers };
|
167 |
+
|
168 |
+
fetch('/project/update-api', {
|
169 |
+
method: 'POST',
|
170 |
+
headers: { 'Content-Type': 'application/json' },
|
171 |
+
body: JSON.stringify({
|
172 |
+
project_name: CURRENT_PROJECT,
|
173 |
+
version_no: CURRENT_VERSION,
|
174 |
+
api_data: apiData
|
175 |
+
})
|
176 |
+
})
|
177 |
+
.then(r => r.json())
|
178 |
+
.then(() => alert('API updated!'))
|
179 |
+
.catch(() => alert('Failed to update API.'));
|
180 |
+
}
|
181 |
+
|
182 |
+
function login() {
|
183 |
+
const username = document.getElementById('usernameInput').value;
|
184 |
+
const password = document.getElementById('passwordInput').value;
|
185 |
+
|
186 |
+
fetch('/auth/login', {
|
187 |
+
method: 'POST',
|
188 |
+
headers: { 'Content-Type': 'application/json' },
|
189 |
+
body: JSON.stringify({ username, password })
|
190 |
+
})
|
191 |
+
.then(response => {
|
192 |
+
if (response.ok) {
|
193 |
+
document.getElementById('loginPanel').style.display = 'none';
|
194 |
+
document.getElementById('mainPanel').style.display = 'block';
|
195 |
+
loadProjectDetails();
|
196 |
+
} else {
|
197 |
+
document.getElementById('loginError').style.display = 'block';
|
198 |
+
}
|
199 |
+
})
|
200 |
+
.catch(() => {
|
201 |
+
document.getElementById('loginError').style.display = 'block';
|
202 |
+
});
|
203 |
+
}
|
lagacy_ui/static/js/spark.js
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
function sparkProjectList() {
|
2 |
+
apiGet('/spark/project_list')
|
3 |
+
.then(data => {
|
4 |
+
const body = document.getElementById('spark-body');
|
5 |
+
body.innerHTML = '';
|
6 |
+
|
7 |
+
if (data && Array.isArray(data.projects) && data.projects.length > 0) {
|
8 |
+
data.projects.forEach(proj => {
|
9 |
+
const row = document.createElement('tr');
|
10 |
+
|
11 |
+
row.innerHTML = `
|
12 |
+
<td>${proj.project_name}</td>
|
13 |
+
<td>${proj.version}</td>
|
14 |
+
<td>${proj.enabled ? '🟢' : '🔴'}</td>
|
15 |
+
<td>${getStatusIcon(proj.status)} ${proj.status}</td>
|
16 |
+
<td>${proj.last_accessed}</td>
|
17 |
+
`;
|
18 |
+
body.appendChild(row);
|
19 |
+
});
|
20 |
+
} else {
|
21 |
+
const row = document.createElement('tr');
|
22 |
+
row.innerHTML = `<td colspan="5" class="text-danger">⚠️ Failed to load Spark project list or no projects available.</td>`;
|
23 |
+
body.appendChild(row);
|
24 |
+
}
|
25 |
+
})
|
26 |
+
.catch(err => {
|
27 |
+
console.error(err);
|
28 |
+
const body = document.getElementById('spark-body');
|
29 |
+
body.innerHTML = '';
|
30 |
+
const row = document.createElement('tr');
|
31 |
+
row.innerHTML = `<td colspan="5" class="text-danger">❌ Error connecting to Spark service.</td>`;
|
32 |
+
body.appendChild(row);
|
33 |
+
});
|
34 |
+
}
|
35 |
+
|
36 |
+
function getStatusIcon(status) {
|
37 |
+
if (status === 'loading') return '⚙️';
|
38 |
+
if (status === 'ready') return '✅';
|
39 |
+
if (status === 'error') return '❌';
|
40 |
+
return 'ℹ️';
|
41 |
+
}
|
lagacy_ui/static/js/test.js
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
function runTests() {
|
2 |
+
apiPost('/test/run', {})
|
3 |
+
.then(data => showResult('test-result', data))
|
4 |
+
.catch(err => console.error(err));
|
5 |
+
}
|
lagacy_ui/static/newProjectModal.html
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!-- New Project Modal (if using a modal, otherwise use prompt in JS) -->
|
2 |
+
<div class="modal fade" id="newProjectModal" tabindex="-1" aria-labelledby="newProjectModalLabel" aria-hidden="true">
|
3 |
+
<div class="modal-dialog">
|
4 |
+
<div class="modal-content">
|
5 |
+
<div class="modal-header">
|
6 |
+
<h5 class="modal-title">Create New Project</h5>
|
7 |
+
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
8 |
+
</div>
|
9 |
+
<div class="modal-body">
|
10 |
+
<input type="text" id="newProjectNameInput" class="form-control mb-2" placeholder="Project Name">
|
11 |
+
</div>
|
12 |
+
<div class="modal-footer">
|
13 |
+
<button class="btn btn-success" onclick="createNewProject()">Create</button>
|
14 |
+
</div>
|
15 |
+
</div>
|
16 |
+
</div>
|
17 |
+
</div>
|
lagacy_ui/static/newVersionModal.html
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!-- New Version Modal -->
|
2 |
+
<div class="modal fade" id="newVersionModal" tabindex="-1" aria-labelledby="newVersionModalLabel" aria-hidden="true">
|
3 |
+
<div class="modal-dialog">
|
4 |
+
<div class="modal-content">
|
5 |
+
<div class="modal-header">
|
6 |
+
<h5 class="modal-title">Create New Version</h5>
|
7 |
+
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
8 |
+
</div>
|
9 |
+
<div class="modal-body">
|
10 |
+
<label for="baseVersionSelect">Select Base Version</label>
|
11 |
+
<select id="baseVersionSelect" class="form-control">
|
12 |
+
<!-- Dynamically populated -->
|
13 |
+
</select>
|
14 |
+
</div>
|
15 |
+
<div class="modal-footer">
|
16 |
+
<button class="btn btn-success" onclick="createNewVersion()">Create</button>
|
17 |
+
</div>
|
18 |
+
</div>
|
19 |
+
</div>
|
20 |
+
</div>
|
lagacy_ui/static/style.css
ADDED
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
body {
|
2 |
+
background-color: #f8f9fa;
|
3 |
+
font-family: Arial, sans-serif;
|
4 |
+
}
|
5 |
+
|
6 |
+
h1 {
|
7 |
+
color: #343a40;
|
8 |
+
}
|
9 |
+
|
10 |
+
.card {
|
11 |
+
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
12 |
+
}
|
13 |
+
|
14 |
+
.card-header {
|
15 |
+
background-color: #343a40;
|
16 |
+
color: white;
|
17 |
+
font-weight: bold;
|
18 |
+
}
|
19 |
+
|
20 |
+
.btn-block {
|
21 |
+
width: 100%;
|
22 |
+
}
|
23 |
+
|
24 |
+
textarea, pre {
|
25 |
+
font-family: monospace;
|
26 |
+
}
|
27 |
+
|
28 |
+
.tab-content {
|
29 |
+
padding: 1rem;
|
30 |
+
background-color: white;
|
31 |
+
border: 1px solid #dee2e6;
|
32 |
+
border-top: none;
|
33 |
+
}
|
34 |
+
|
35 |
+
#main-tabs .nav-link {
|
36 |
+
cursor: pointer;
|
37 |
+
}
|
38 |
+
|
39 |
+
.text-danger {
|
40 |
+
font-weight: bold;
|
41 |
+
}
|
42 |
+
|
43 |
+
table th {
|
44 |
+
cursor: pointer;
|
45 |
+
}
|
46 |
+
|
47 |
+
table th:hover {
|
48 |
+
background-color: #f1f1f1;
|
49 |
+
}
|
50 |
+
|
51 |
+
.table-responsive {
|
52 |
+
max-height: 400px;
|
53 |
+
overflow-y: auto;
|
54 |
+
}
|
55 |
+
|
56 |
+
/* intent list buttons spacing */
|
57 |
+
#projectDetails li button {
|
58 |
+
margin-left: 8px;
|
59 |
+
}
|
lagacy_ui/test_controller.py
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import APIRouter, HTTPException, Request, Depends
|
2 |
+
from config_provider import get_config, ServiceConfig
|
3 |
+
from service_config import ServiceConfig
|
4 |
+
import random
|
5 |
+
|
6 |
+
router = APIRouter()
|
7 |
+
|
8 |
+
@router.get("/run_all")
|
9 |
+
def run_all_tests(config: ServiceConfig = Depends(get_config)):
|
10 |
+
# Mock test results
|
11 |
+
test_results = []
|
12 |
+
for project_name in config.projects.keys():
|
13 |
+
result = {
|
14 |
+
"project": project_name,
|
15 |
+
"status": random.choice(["passed", "failed"]),
|
16 |
+
"details": f"Mock test for {project_name}"
|
17 |
+
}
|
18 |
+
test_results.append(result)
|
19 |
+
|
20 |
+
return {"results": test_results}
|
21 |
+
|