ciyidogan commited on
Commit
833cf90
·
verified ·
1 Parent(s): c5de5b2

Upload 19 files

Browse files
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
+