001-fix-ui-ws-validation #2
@@ -6,6 +6,8 @@ Auto-generated from all feature plans. Last updated: 2025-12-19
|
||||
- Python 3.9+, Node.js 18+ + `uvicorn`, `npm`, `bash` (003-project-launch-script)
|
||||
- Python 3.9+, Node.js 18+ + SvelteKit, FastAPI, Tailwind CSS (inferred from existing frontend) (004-integrate-svelte-kit)
|
||||
- N/A (Frontend integration) (004-integrate-svelte-kit)
|
||||
- Python 3.9+, Node.js 18+ + FastAPI, SvelteKit, Tailwind CSS, Pydantic (001-fix-ui-ws-validation)
|
||||
- N/A (Configuration based) (001-fix-ui-ws-validation)
|
||||
|
||||
- Python 3.9+ (Backend), Node.js 18+ (Frontend Build) (001-plugin-arch-svelte-ui)
|
||||
|
||||
@@ -26,10 +28,10 @@ cd src; pytest; ruff check .
|
||||
Python 3.9+ (Backend), Node.js 18+ (Frontend Build): Follow standard conventions
|
||||
|
||||
## Recent Changes
|
||||
- 001-fix-ui-ws-validation: Added Python 3.9+, Node.js 18+ + FastAPI, SvelteKit, Tailwind CSS, Pydantic
|
||||
- 001-fix-ui-ws-validation: Added Python 3.9+, Node.js 18+ + FastAPI, SvelteKit, Tailwind CSS, Pydantic
|
||||
- 004-integrate-svelte-kit: Added Python 3.9+, Node.js 18+ + SvelteKit, FastAPI, Tailwind CSS (inferred from existing frontend)
|
||||
- 003-project-launch-script: Added Python 3.9+, Node.js 18+ + `uvicorn`, `npm`, `bash`
|
||||
|
||||
- 001-plugin-arch-svelte-ui: Added Python 3.9+ (Backend), Node.js 18+ (Frontend Build)
|
||||
|
||||
<!-- MANUAL ADDITIONS START -->
|
||||
<!-- MANUAL ADDITIONS END -->
|
||||
|
||||
@@ -1,50 +1,29 @@
|
||||
# [PROJECT_NAME] Constitution
|
||||
<!-- Example: Spec Constitution, TaskFlow Constitution, etc. -->
|
||||
# ss-tools Constitution
|
||||
|
||||
## Core Principles
|
||||
|
||||
### [PRINCIPLE_1_NAME]
|
||||
<!-- Example: I. Library-First -->
|
||||
[PRINCIPLE_1_DESCRIPTION]
|
||||
<!-- Example: Every feature starts as a standalone library; Libraries must be self-contained, independently testable, documented; Clear purpose required - no organizational-only libraries -->
|
||||
### I. SPA-First Architecture
|
||||
The frontend MUST be a Static Single Page Application (SPA) served by the Python backend. No Node.js server is permitted in production. The backend serves the `index.html` entry point for all non-API routes.
|
||||
|
||||
### [PRINCIPLE_2_NAME]
|
||||
<!-- Example: II. CLI Interface -->
|
||||
[PRINCIPLE_2_DESCRIPTION]
|
||||
<!-- Example: Every library exposes functionality via CLI; Text in/out protocol: stdin/args → stdout, errors → stderr; Support JSON + human-readable formats -->
|
||||
### II. API-Driven Communication
|
||||
All data retrieval and state changes MUST be performed via the backend REST API or WebSockets. The frontend should not access the database or filesystem directly.
|
||||
|
||||
### [PRINCIPLE_3_NAME]
|
||||
<!-- Example: III. Test-First (NON-NEGOTIABLE) -->
|
||||
[PRINCIPLE_3_DESCRIPTION]
|
||||
<!-- Example: TDD mandatory: Tests written → User approved → Tests fail → Then implement; Red-Green-Refactor cycle strictly enforced -->
|
||||
### III. Modern Stack Consistency
|
||||
The project strictly uses SvelteKit (Frontend), FastAPI (Backend), and Tailwind CSS (Styling). New dependencies must be justified and approved.
|
||||
|
||||
### [PRINCIPLE_4_NAME]
|
||||
<!-- Example: IV. Integration Testing -->
|
||||
[PRINCIPLE_4_DESCRIPTION]
|
||||
<!-- Example: Focus areas requiring integration tests: New library contract tests, Contract changes, Inter-service communication, Shared schemas -->
|
||||
|
||||
### [PRINCIPLE_5_NAME]
|
||||
<!-- Example: V. Observability, VI. Versioning & Breaking Changes, VII. Simplicity -->
|
||||
[PRINCIPLE_5_DESCRIPTION]
|
||||
<!-- Example: Text I/O ensures debuggability; Structured logging required; Or: MAJOR.MINOR.BUILD format; Or: Start simple, YAGNI principles -->
|
||||
|
||||
## [SECTION_2_NAME]
|
||||
<!-- Example: Additional Constraints, Security Requirements, Performance Standards, etc. -->
|
||||
|
||||
[SECTION_2_CONTENT]
|
||||
<!-- Example: Technology stack requirements, compliance standards, deployment policies, etc. -->
|
||||
|
||||
## [SECTION_3_NAME]
|
||||
<!-- Example: Development Workflow, Review Process, Quality Gates, etc. -->
|
||||
|
||||
[SECTION_3_CONTENT]
|
||||
<!-- Example: Code review requirements, testing gates, deployment approval process, etc. -->
|
||||
### IV. Semantic Protocol Adherence (GRACE-Poly)
|
||||
All code generation and modification MUST adhere to the Semantic Protocol defined in `semantic_protocol.md`.
|
||||
- **Anchors**: Use `[DEF:id:Type]` and `[/DEF:id]` to define semantic boundaries.
|
||||
- **Contracts**: Define `@PRE` and `@POST` conditions in headers.
|
||||
- **Logging**: Use structured logging with `[AnchorID][State]` format.
|
||||
- **Immutability**: Respect architectural decisions in headers.
|
||||
|
||||
## Governance
|
||||
<!-- Example: Constitution supersedes all other practices; Amendments require documentation, approval, migration plan -->
|
||||
|
||||
[GOVERNANCE_RULES]
|
||||
<!-- Example: All PRs/reviews must verify compliance; Complexity must be justified; Use [GUIDANCE_FILE] for runtime development guidance -->
|
||||
### Compliance
|
||||
All Pull Requests and code modifications must be verified against this Constitution. Violations of Core Principles are considered critical defects.
|
||||
|
||||
**Version**: [CONSTITUTION_VERSION] | **Ratified**: [RATIFICATION_DATE] | **Last Amended**: [LAST_AMENDED_DATE]
|
||||
<!-- Example: Version: 2.1.1 | Ratified: 2025-06-13 | Last Amended: 2025-07-16 -->
|
||||
### Amendments
|
||||
Changes to this Constitution require a formal RFC process and approval from the project lead.
|
||||
|
||||
**Version**: 1.0.0 | **Ratified**: 2025-12-20
|
||||
|
||||
@@ -33,3 +33,237 @@ pydantic_core._pydantic_core.ValidationError: 1 validation error for SupersetCon
|
||||
base_url
|
||||
Value error, Invalid URL format: https://superset.bebesh.ru. Must include '/api/v1'. [type=value_error, input_value='https://superset.bebesh.ru', input_type=str]
|
||||
For further information visit https://errors.pydantic.dev/2.12/v/value_error
|
||||
2025-12-20 22:42:32,538 - INFO - [BackupPlugin][Entry] Starting backup for superset.
|
||||
2025-12-20 22:42:32,538 - INFO - [setup_clients][Enter] Starting Superset clients initialization.
|
||||
2025-12-20 22:42:32,583 - INFO - [setup_clients][Action] Loading environments from ConfigManager
|
||||
2025-12-20 22:42:32,587 - CRITICAL - [setup_clients][Failure] Critical error during client initialization: 1 validation error for SupersetConfig
|
||||
base_url
|
||||
Value error, Invalid URL format: https://superset.bebesh.ru. Must include '/api/v1'. [type=value_error, input_value='https://superset.bebesh.ru', input_type=str]
|
||||
For further information visit https://errors.pydantic.dev/2.12/v/value_error
|
||||
Traceback (most recent call last):
|
||||
File "/home/user/ss-tools/superset_tool/utils/init_clients.py", line 66, in setup_clients
|
||||
config = SupersetConfig(
|
||||
^^^^^^^^^^^^^^^
|
||||
File "/home/user/ss-tools/backend/.venv/lib/python3.12/site-packages/pydantic/main.py", line 250, in __init__
|
||||
validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
pydantic_core._pydantic_core.ValidationError: 1 validation error for SupersetConfig
|
||||
base_url
|
||||
Value error, Invalid URL format: https://superset.bebesh.ru. Must include '/api/v1'. [type=value_error, input_value='https://superset.bebesh.ru', input_type=str]
|
||||
For further information visit https://errors.pydantic.dev/2.12/v/value_error
|
||||
2025-12-20 22:54:29,770 - INFO - [BackupPlugin][Entry] Starting backup for .
|
||||
2025-12-20 22:54:29,771 - INFO - [setup_clients][Enter] Starting Superset clients initialization.
|
||||
2025-12-20 22:54:29,831 - INFO - [setup_clients][Action] Loading environments from ConfigManager
|
||||
2025-12-20 22:54:29,833 - CRITICAL - [setup_clients][Failure] Critical error during client initialization: 1 validation error for SupersetConfig
|
||||
base_url
|
||||
Value error, Invalid URL format: https://superset.bebesh.ru. Must include '/api/v1'. [type=value_error, input_value='https://superset.bebesh.ru', input_type=str]
|
||||
For further information visit https://errors.pydantic.dev/2.12/v/value_error
|
||||
Traceback (most recent call last):
|
||||
File "/home/user/ss-tools/superset_tool/utils/init_clients.py", line 66, in setup_clients
|
||||
config = SupersetConfig(
|
||||
^^^^^^^^^^^^^^^
|
||||
File "/home/user/ss-tools/backend/.venv/lib/python3.12/site-packages/pydantic/main.py", line 250, in __init__
|
||||
validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
pydantic_core._pydantic_core.ValidationError: 1 validation error for SupersetConfig
|
||||
base_url
|
||||
Value error, Invalid URL format: https://superset.bebesh.ru. Must include '/api/v1'. [type=value_error, input_value='https://superset.bebesh.ru', input_type=str]
|
||||
For further information visit https://errors.pydantic.dev/2.12/v/value_error
|
||||
2025-12-20 22:54:34,078 - INFO - [BackupPlugin][Entry] Starting backup for superset.
|
||||
2025-12-20 22:54:34,078 - INFO - [setup_clients][Enter] Starting Superset clients initialization.
|
||||
2025-12-20 22:54:34,079 - INFO - [setup_clients][Action] Loading environments from ConfigManager
|
||||
2025-12-20 22:54:34,079 - CRITICAL - [setup_clients][Failure] Critical error during client initialization: 1 validation error for SupersetConfig
|
||||
base_url
|
||||
Value error, Invalid URL format: https://superset.bebesh.ru. Must include '/api/v1'. [type=value_error, input_value='https://superset.bebesh.ru', input_type=str]
|
||||
For further information visit https://errors.pydantic.dev/2.12/v/value_error
|
||||
Traceback (most recent call last):
|
||||
File "/home/user/ss-tools/superset_tool/utils/init_clients.py", line 66, in setup_clients
|
||||
config = SupersetConfig(
|
||||
^^^^^^^^^^^^^^^
|
||||
File "/home/user/ss-tools/backend/.venv/lib/python3.12/site-packages/pydantic/main.py", line 250, in __init__
|
||||
validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
pydantic_core._pydantic_core.ValidationError: 1 validation error for SupersetConfig
|
||||
base_url
|
||||
Value error, Invalid URL format: https://superset.bebesh.ru. Must include '/api/v1'. [type=value_error, input_value='https://superset.bebesh.ru', input_type=str]
|
||||
For further information visit https://errors.pydantic.dev/2.12/v/value_error
|
||||
2025-12-20 22:59:25,060 - INFO - [BackupPlugin][Entry] Starting backup for superset.
|
||||
2025-12-20 22:59:25,060 - INFO - [setup_clients][Enter] Starting Superset clients initialization.
|
||||
2025-12-20 22:59:25,114 - INFO - [setup_clients][Action] Loading environments from ConfigManager
|
||||
2025-12-20 22:59:25,117 - CRITICAL - [setup_clients][Failure] Critical error during client initialization: 1 validation error for SupersetConfig
|
||||
base_url
|
||||
Value error, Invalid URL format: https://superset.bebesh.ru. Must include '/api/v1'. [type=value_error, input_value='https://superset.bebesh.ru', input_type=str]
|
||||
For further information visit https://errors.pydantic.dev/2.12/v/value_error
|
||||
Traceback (most recent call last):
|
||||
File "/home/user/ss-tools/superset_tool/utils/init_clients.py", line 66, in setup_clients
|
||||
config = SupersetConfig(
|
||||
^^^^^^^^^^^^^^^
|
||||
File "/home/user/ss-tools/backend/.venv/lib/python3.12/site-packages/pydantic/main.py", line 250, in __init__
|
||||
validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
pydantic_core._pydantic_core.ValidationError: 1 validation error for SupersetConfig
|
||||
base_url
|
||||
Value error, Invalid URL format: https://superset.bebesh.ru. Must include '/api/v1'. [type=value_error, input_value='https://superset.bebesh.ru', input_type=str]
|
||||
For further information visit https://errors.pydantic.dev/2.12/v/value_error
|
||||
2025-12-20 23:00:31,156 - INFO - [BackupPlugin][Entry] Starting backup for superset.
|
||||
2025-12-20 23:00:31,156 - INFO - [setup_clients][Enter] Starting Superset clients initialization.
|
||||
2025-12-20 23:00:31,157 - INFO - [setup_clients][Action] Loading environments from ConfigManager
|
||||
2025-12-20 23:00:31,162 - CRITICAL - [setup_clients][Failure] Critical error during client initialization: 1 validation error for SupersetConfig
|
||||
base_url
|
||||
Value error, Invalid URL format: https://superset.bebesh.ru. Must include '/api/v1'. [type=value_error, input_value='https://superset.bebesh.ru', input_type=str]
|
||||
For further information visit https://errors.pydantic.dev/2.12/v/value_error
|
||||
Traceback (most recent call last):
|
||||
File "/home/user/ss-tools/superset_tool/utils/init_clients.py", line 66, in setup_clients
|
||||
config = SupersetConfig(
|
||||
^^^^^^^^^^^^^^^
|
||||
File "/home/user/ss-tools/backend/.venv/lib/python3.12/site-packages/pydantic/main.py", line 250, in __init__
|
||||
validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
pydantic_core._pydantic_core.ValidationError: 1 validation error for SupersetConfig
|
||||
base_url
|
||||
Value error, Invalid URL format: https://superset.bebesh.ru. Must include '/api/v1'. [type=value_error, input_value='https://superset.bebesh.ru', input_type=str]
|
||||
For further information visit https://errors.pydantic.dev/2.12/v/value_error
|
||||
2025-12-20 23:00:34,710 - INFO - [BackupPlugin][Entry] Starting backup for superset.
|
||||
2025-12-20 23:00:34,710 - INFO - [setup_clients][Enter] Starting Superset clients initialization.
|
||||
2025-12-20 23:00:34,710 - INFO - [setup_clients][Action] Loading environments from ConfigManager
|
||||
2025-12-20 23:00:34,711 - CRITICAL - [setup_clients][Failure] Critical error during client initialization: 1 validation error for SupersetConfig
|
||||
base_url
|
||||
Value error, Invalid URL format: https://superset.bebesh.ru. Must include '/api/v1'. [type=value_error, input_value='https://superset.bebesh.ru', input_type=str]
|
||||
For further information visit https://errors.pydantic.dev/2.12/v/value_error
|
||||
Traceback (most recent call last):
|
||||
File "/home/user/ss-tools/superset_tool/utils/init_clients.py", line 66, in setup_clients
|
||||
config = SupersetConfig(
|
||||
^^^^^^^^^^^^^^^
|
||||
File "/home/user/ss-tools/backend/.venv/lib/python3.12/site-packages/pydantic/main.py", line 250, in __init__
|
||||
validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
pydantic_core._pydantic_core.ValidationError: 1 validation error for SupersetConfig
|
||||
base_url
|
||||
Value error, Invalid URL format: https://superset.bebesh.ru. Must include '/api/v1'. [type=value_error, input_value='https://superset.bebesh.ru', input_type=str]
|
||||
For further information visit https://errors.pydantic.dev/2.12/v/value_error
|
||||
2025-12-20 23:01:43,894 - INFO - [BackupPlugin][Entry] Starting backup for superset.
|
||||
2025-12-20 23:01:43,894 - INFO - [setup_clients][Enter] Starting Superset clients initialization.
|
||||
2025-12-20 23:01:43,895 - INFO - [setup_clients][Action] Loading environments from ConfigManager
|
||||
2025-12-20 23:01:43,895 - CRITICAL - [setup_clients][Failure] Critical error during client initialization: 1 validation error for SupersetConfig
|
||||
base_url
|
||||
Value error, Invalid URL format: https://superset.bebesh.ru. Must include '/api/v1'. [type=value_error, input_value='https://superset.bebesh.ru', input_type=str]
|
||||
For further information visit https://errors.pydantic.dev/2.12/v/value_error
|
||||
Traceback (most recent call last):
|
||||
File "/home/user/ss-tools/superset_tool/utils/init_clients.py", line 66, in setup_clients
|
||||
config = SupersetConfig(
|
||||
^^^^^^^^^^^^^^^
|
||||
File "/home/user/ss-tools/backend/.venv/lib/python3.12/site-packages/pydantic/main.py", line 250, in __init__
|
||||
validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
pydantic_core._pydantic_core.ValidationError: 1 validation error for SupersetConfig
|
||||
base_url
|
||||
Value error, Invalid URL format: https://superset.bebesh.ru. Must include '/api/v1'. [type=value_error, input_value='https://superset.bebesh.ru', input_type=str]
|
||||
For further information visit https://errors.pydantic.dev/2.12/v/value_error
|
||||
2025-12-20 23:04:07,731 - INFO - [BackupPlugin][Entry] Starting backup for superset.
|
||||
2025-12-20 23:04:07,731 - INFO - [setup_clients][Enter] Starting Superset clients initialization.
|
||||
2025-12-20 23:04:07,732 - INFO - [setup_clients][Action] Loading environments from ConfigManager
|
||||
2025-12-20 23:04:07,732 - CRITICAL - [setup_clients][Failure] Critical error during client initialization: 1 validation error for SupersetConfig
|
||||
base_url
|
||||
Value error, Invalid URL format: https://superset.bebesh.ru. Must include '/api/v1'. [type=value_error, input_value='https://superset.bebesh.ru', input_type=str]
|
||||
For further information visit https://errors.pydantic.dev/2.12/v/value_error
|
||||
Traceback (most recent call last):
|
||||
File "/home/user/ss-tools/superset_tool/utils/init_clients.py", line 66, in setup_clients
|
||||
config = SupersetConfig(
|
||||
^^^^^^^^^^^^^^^
|
||||
File "/home/user/ss-tools/backend/.venv/lib/python3.12/site-packages/pydantic/main.py", line 250, in __init__
|
||||
validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
pydantic_core._pydantic_core.ValidationError: 1 validation error for SupersetConfig
|
||||
base_url
|
||||
Value error, Invalid URL format: https://superset.bebesh.ru. Must include '/api/v1'. [type=value_error, input_value='https://superset.bebesh.ru', input_type=str]
|
||||
For further information visit https://errors.pydantic.dev/2.12/v/value_error
|
||||
2025-12-20 23:06:39,641 - INFO - [BackupPlugin][Entry] Starting backup for superset.
|
||||
2025-12-20 23:06:39,642 - INFO - [setup_clients][Enter] Starting Superset clients initialization.
|
||||
2025-12-20 23:06:39,687 - INFO - [setup_clients][Action] Loading environments from ConfigManager
|
||||
2025-12-20 23:06:39,689 - CRITICAL - [setup_clients][Failure] Critical error during client initialization: 1 validation error for SupersetConfig
|
||||
base_url
|
||||
Value error, Invalid URL format: https://superset.bebesh.ru. Must include '/api/v1'. [type=value_error, input_value='https://superset.bebesh.ru', input_type=str]
|
||||
For further information visit https://errors.pydantic.dev/2.12/v/value_error
|
||||
Traceback (most recent call last):
|
||||
File "/home/user/ss-tools/superset_tool/utils/init_clients.py", line 66, in setup_clients
|
||||
config = SupersetConfig(
|
||||
^^^^^^^^^^^^^^^
|
||||
File "/home/user/ss-tools/backend/.venv/lib/python3.12/site-packages/pydantic/main.py", line 250, in __init__
|
||||
validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
pydantic_core._pydantic_core.ValidationError: 1 validation error for SupersetConfig
|
||||
base_url
|
||||
Value error, Invalid URL format: https://superset.bebesh.ru. Must include '/api/v1'. [type=value_error, input_value='https://superset.bebesh.ru', input_type=str]
|
||||
For further information visit https://errors.pydantic.dev/2.12/v/value_error
|
||||
2025-12-20 23:30:36,090 - INFO - [BackupPlugin][Entry] Starting backup for superset.
|
||||
2025-12-20 23:30:36,093 - INFO - [setup_clients][Enter] Starting Superset clients initialization.
|
||||
2025-12-20 23:30:36,128 - INFO - [setup_clients][Action] Loading environments from ConfigManager
|
||||
2025-12-20 23:30:36,129 - INFO - [SupersetClient.__init__][Enter] Initializing SupersetClient.
|
||||
2025-12-20 23:30:36,129 - INFO - [APIClient.__init__][Entry] Initializing APIClient.
|
||||
2025-12-20 23:30:36,130 - WARNING - [_init_session][State] SSL verification disabled.
|
||||
2025-12-20 23:30:36,130 - INFO - [APIClient.__init__][Exit] APIClient initialized.
|
||||
2025-12-20 23:30:36,130 - INFO - [SupersetClient.__init__][Exit] SupersetClient initialized.
|
||||
2025-12-20 23:30:36,130 - INFO - [get_dashboards][Enter] Fetching dashboards.
|
||||
2025-12-20 23:30:36,131 - INFO - [authenticate][Enter] Authenticating to https://superset.bebesh.ru/api/v1
|
||||
2025-12-20 23:30:36,897 - INFO - [authenticate][Exit] Authenticated successfully.
|
||||
2025-12-20 23:30:37,527 - INFO - [get_dashboards][Exit] Found 11 dashboards.
|
||||
2025-12-20 23:30:37,527 - INFO - [BackupPlugin][Progress] Found 11 dashboards to export in superset.
|
||||
2025-12-20 23:30:37,529 - INFO - [export_dashboard][Enter] Exporting dashboard 11.
|
||||
2025-12-20 23:30:38,224 - INFO - [export_dashboard][Exit] Exported dashboard 11 to dashboard_export_20251220T203037.zip.
|
||||
2025-12-20 23:30:38,225 - INFO - [save_and_unpack_dashboard][Enter] Processing dashboard. Unpack: False
|
||||
2025-12-20 23:30:38,226 - INFO - [save_and_unpack_dashboard][State] Dashboard saved to: backups/SUPERSET/FCC New Coder Survey 2018/dashboard_export_20251220T203037.zip
|
||||
2025-12-20 23:30:38,227 - INFO - [archive_exports][Enter] Managing archive in backups/SUPERSET/FCC New Coder Survey 2018
|
||||
2025-12-20 23:30:38,230 - INFO - [export_dashboard][Enter] Exporting dashboard 10.
|
||||
2025-12-20 23:30:38,438 - INFO - [export_dashboard][Exit] Exported dashboard 10 to dashboard_export_20251220T203038.zip.
|
||||
2025-12-20 23:30:38,438 - INFO - [save_and_unpack_dashboard][Enter] Processing dashboard. Unpack: False
|
||||
2025-12-20 23:30:38,439 - INFO - [save_and_unpack_dashboard][State] Dashboard saved to: backups/SUPERSET/COVID Vaccine Dashboard/dashboard_export_20251220T203038.zip
|
||||
2025-12-20 23:30:38,439 - INFO - [archive_exports][Enter] Managing archive in backups/SUPERSET/COVID Vaccine Dashboard
|
||||
2025-12-20 23:30:38,440 - INFO - [export_dashboard][Enter] Exporting dashboard 9.
|
||||
2025-12-20 23:30:38,853 - INFO - [export_dashboard][Exit] Exported dashboard 9 to dashboard_export_20251220T203038.zip.
|
||||
2025-12-20 23:30:38,853 - INFO - [save_and_unpack_dashboard][Enter] Processing dashboard. Unpack: False
|
||||
2025-12-20 23:30:38,856 - INFO - [save_and_unpack_dashboard][State] Dashboard saved to: backups/SUPERSET/Sales Dashboard/dashboard_export_20251220T203038.zip
|
||||
2025-12-20 23:30:38,856 - INFO - [archive_exports][Enter] Managing archive in backups/SUPERSET/Sales Dashboard
|
||||
2025-12-20 23:30:38,858 - INFO - [export_dashboard][Enter] Exporting dashboard 8.
|
||||
2025-12-20 23:30:38,939 - INFO - [export_dashboard][Exit] Exported dashboard 8 to dashboard_export_20251220T203038.zip.
|
||||
2025-12-20 23:30:38,940 - INFO - [save_and_unpack_dashboard][Enter] Processing dashboard. Unpack: False
|
||||
2025-12-20 23:30:38,941 - INFO - [save_and_unpack_dashboard][State] Dashboard saved to: backups/SUPERSET/Unicode Test/dashboard_export_20251220T203038.zip
|
||||
2025-12-20 23:30:38,941 - INFO - [archive_exports][Enter] Managing archive in backups/SUPERSET/Unicode Test
|
||||
2025-12-20 23:30:38,942 - INFO - [export_dashboard][Enter] Exporting dashboard 7.
|
||||
2025-12-20 23:30:39,148 - INFO - [export_dashboard][Exit] Exported dashboard 7 to dashboard_export_20251220T203038.zip.
|
||||
2025-12-20 23:30:39,148 - INFO - [save_and_unpack_dashboard][Enter] Processing dashboard. Unpack: False
|
||||
2025-12-20 23:30:39,149 - INFO - [save_and_unpack_dashboard][State] Dashboard saved to: backups/SUPERSET/Video Game Sales/dashboard_export_20251220T203038.zip
|
||||
2025-12-20 23:30:39,149 - INFO - [archive_exports][Enter] Managing archive in backups/SUPERSET/Video Game Sales
|
||||
2025-12-20 23:30:39,150 - INFO - [export_dashboard][Enter] Exporting dashboard 6.
|
||||
2025-12-20 23:30:39,689 - INFO - [export_dashboard][Exit] Exported dashboard 6 to dashboard_export_20251220T203039.zip.
|
||||
2025-12-20 23:30:39,689 - INFO - [save_and_unpack_dashboard][Enter] Processing dashboard. Unpack: False
|
||||
2025-12-20 23:30:39,690 - INFO - [save_and_unpack_dashboard][State] Dashboard saved to: backups/SUPERSET/Featured Charts/dashboard_export_20251220T203039.zip
|
||||
2025-12-20 23:30:39,691 - INFO - [archive_exports][Enter] Managing archive in backups/SUPERSET/Featured Charts
|
||||
2025-12-20 23:30:39,692 - INFO - [export_dashboard][Enter] Exporting dashboard 5.
|
||||
2025-12-20 23:30:39,960 - INFO - [export_dashboard][Exit] Exported dashboard 5 to dashboard_export_20251220T203039.zip.
|
||||
2025-12-20 23:30:39,960 - INFO - [save_and_unpack_dashboard][Enter] Processing dashboard. Unpack: False
|
||||
2025-12-20 23:30:39,961 - INFO - [save_and_unpack_dashboard][State] Dashboard saved to: backups/SUPERSET/Slack Dashboard/dashboard_export_20251220T203039.zip
|
||||
2025-12-20 23:30:39,961 - INFO - [archive_exports][Enter] Managing archive in backups/SUPERSET/Slack Dashboard
|
||||
2025-12-20 23:30:39,962 - INFO - [export_dashboard][Enter] Exporting dashboard 4.
|
||||
2025-12-20 23:30:40,196 - INFO - [export_dashboard][Exit] Exported dashboard 4 to dashboard_export_20251220T203039.zip.
|
||||
2025-12-20 23:30:40,196 - INFO - [save_and_unpack_dashboard][Enter] Processing dashboard. Unpack: False
|
||||
2025-12-20 23:30:40,197 - INFO - [save_and_unpack_dashboard][State] Dashboard saved to: backups/SUPERSET/deck.gl Demo/dashboard_export_20251220T203039.zip
|
||||
2025-12-20 23:30:40,197 - INFO - [archive_exports][Enter] Managing archive in backups/SUPERSET/deck.gl Demo
|
||||
2025-12-20 23:30:40,198 - INFO - [export_dashboard][Enter] Exporting dashboard 3.
|
||||
2025-12-20 23:30:40,745 - INFO - [export_dashboard][Exit] Exported dashboard 3 to dashboard_export_20251220T203040.zip.
|
||||
2025-12-20 23:30:40,746 - INFO - [save_and_unpack_dashboard][Enter] Processing dashboard. Unpack: False
|
||||
2025-12-20 23:30:40,760 - INFO - [save_and_unpack_dashboard][State] Dashboard saved to: backups/SUPERSET/Misc Charts/dashboard_export_20251220T203040.zip
|
||||
2025-12-20 23:30:40,761 - INFO - [archive_exports][Enter] Managing archive in backups/SUPERSET/Misc Charts
|
||||
2025-12-20 23:30:40,762 - INFO - [export_dashboard][Enter] Exporting dashboard 2.
|
||||
2025-12-20 23:30:40,928 - INFO - [export_dashboard][Exit] Exported dashboard 2 to dashboard_export_20251220T203040.zip.
|
||||
2025-12-20 23:30:40,929 - INFO - [save_and_unpack_dashboard][Enter] Processing dashboard. Unpack: False
|
||||
2025-12-20 23:30:40,930 - INFO - [save_and_unpack_dashboard][State] Dashboard saved to: backups/SUPERSET/USA Births Names/dashboard_export_20251220T203040.zip
|
||||
2025-12-20 23:30:40,931 - INFO - [archive_exports][Enter] Managing archive in backups/SUPERSET/USA Births Names
|
||||
2025-12-20 23:30:40,932 - INFO - [export_dashboard][Enter] Exporting dashboard 1.
|
||||
2025-12-20 23:30:41,582 - INFO - [export_dashboard][Exit] Exported dashboard 1 to dashboard_export_20251220T203040.zip.
|
||||
2025-12-20 23:30:41,582 - INFO - [save_and_unpack_dashboard][Enter] Processing dashboard. Unpack: False
|
||||
2025-12-20 23:30:41,749 - INFO - [save_and_unpack_dashboard][State] Dashboard saved to: backups/SUPERSET/World Bank's Data/dashboard_export_20251220T203040.zip
|
||||
2025-12-20 23:30:41,750 - INFO - [archive_exports][Enter] Managing archive in backups/SUPERSET/World Bank's Data
|
||||
2025-12-20 23:30:41,752 - INFO - [consolidate_archive_folders][Enter] Consolidating archives in backups/SUPERSET
|
||||
2025-12-20 23:30:41,753 - INFO - [remove_empty_directories][Enter] Starting cleanup of empty directories in backups/SUPERSET
|
||||
2025-12-20 23:30:41,758 - INFO - [remove_empty_directories][Exit] Removed 0 empty directories.
|
||||
2025-12-20 23:30:41,758 - INFO - [BackupPlugin][CoherenceCheck:Passed] Backup logic completed for superset.
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -42,9 +42,9 @@ app.add_middleware(
|
||||
|
||||
|
||||
# Include API routes
|
||||
app.include_router(plugins.router, prefix="/plugins", tags=["Plugins"])
|
||||
app.include_router(tasks.router, prefix="/tasks", tags=["Tasks"])
|
||||
app.include_router(settings.router, prefix="/settings", tags=["Settings"])
|
||||
app.include_router(plugins.router, prefix="/api/plugins", tags=["Plugins"])
|
||||
app.include_router(tasks.router, prefix="/api/tasks", tags=["Tasks"])
|
||||
app.include_router(settings.router, prefix="/api/settings", tags=["Settings"])
|
||||
|
||||
# [DEF:WebSocketEndpoint:Endpoint]
|
||||
# @SEMANTICS: websocket, logs, streaming, real-time
|
||||
|
||||
49
backend/tests/test_models.py
Normal file
49
backend/tests/test_models.py
Normal file
@@ -0,0 +1,49 @@
|
||||
import pytest
|
||||
from superset_tool.models import SupersetConfig
|
||||
|
||||
def test_superset_config_url_normalization():
|
||||
auth = {
|
||||
"provider": "db",
|
||||
"username": "admin",
|
||||
"password": "password",
|
||||
"refresh": "token"
|
||||
}
|
||||
|
||||
# Test with /api/v1 already present
|
||||
config = SupersetConfig(
|
||||
env="dev",
|
||||
base_url="http://localhost:8088/api/v1",
|
||||
auth=auth
|
||||
)
|
||||
assert config.base_url == "http://localhost:8088/api/v1"
|
||||
|
||||
# Test without /api/v1
|
||||
config = SupersetConfig(
|
||||
env="dev",
|
||||
base_url="http://localhost:8088",
|
||||
auth=auth
|
||||
)
|
||||
assert config.base_url == "http://localhost:8088/api/v1"
|
||||
|
||||
# Test with trailing slash
|
||||
config = SupersetConfig(
|
||||
env="dev",
|
||||
base_url="http://localhost:8088/",
|
||||
auth=auth
|
||||
)
|
||||
assert config.base_url == "http://localhost:8088/api/v1"
|
||||
|
||||
def test_superset_config_invalid_url():
|
||||
auth = {
|
||||
"provider": "db",
|
||||
"username": "admin",
|
||||
"password": "password",
|
||||
"refresh": "token"
|
||||
}
|
||||
|
||||
with pytest.raises(ValueError, match="Must start with http:// or https://"):
|
||||
SupersetConfig(
|
||||
env="dev",
|
||||
base_url="localhost:8088",
|
||||
auth=auth
|
||||
)
|
||||
@@ -12,7 +12,7 @@ The settings mechanism allows users to configure multiple Superset environments
|
||||
|
||||
Configuration is structured using Pydantic models in `backend/src/core/config_models.py`:
|
||||
|
||||
- `Environment`: Represents a Superset instance (URL, credentials).
|
||||
- `Environment`: Represents a Superset instance (URL, credentials). The `base_url` is automatically normalized to include the `/api/v1` suffix if missing.
|
||||
- `GlobalSettings`: Global application parameters (e.g., `backup_path`).
|
||||
- `AppConfig`: The root configuration object.
|
||||
|
||||
|
||||
27
frontend/.svelte-kit/ambient.d.ts
vendored
27
frontend/.svelte-kit/ambient.d.ts
vendored
@@ -27,20 +27,13 @@
|
||||
*/
|
||||
declare module '$env/static/private' {
|
||||
export const LESSOPEN: string;
|
||||
export const VSCODE_CWD: string;
|
||||
export const VSCODE_ESM_ENTRYPOINT: string;
|
||||
export const USER: string;
|
||||
export const VSCODE_NLS_CONFIG: string;
|
||||
export const npm_config_user_agent: string;
|
||||
export const VSCODE_WSL_EXT_LOCATION: string;
|
||||
export const VSCODE_HANDLES_UNCAUGHT_ERRORS: string;
|
||||
export const npm_node_execpath: string;
|
||||
export const SHLVL: string;
|
||||
export const npm_config_noproxy: string;
|
||||
export const HOME: string;
|
||||
export const OLDPWD: string;
|
||||
export const VSCODE_RECONNECTION_GRACE_TIME: string;
|
||||
export const VSCODE_IPC_HOOK_CLI: string;
|
||||
export const npm_package_json: string;
|
||||
export const PS1: string;
|
||||
export const npm_config_userconfig: string;
|
||||
@@ -49,11 +42,9 @@ declare module '$env/static/private' {
|
||||
export const WSL_DISTRO_NAME: string;
|
||||
export const COLOR: string;
|
||||
export const WAYLAND_DISPLAY: string;
|
||||
export const VSCODE_L10N_BUNDLE_LOCATION: string;
|
||||
export const LOGNAME: string;
|
||||
export const NAME: string;
|
||||
export const WSL_INTEROP: string;
|
||||
export const VSCODE_HANDLES_SIGPIPE: string;
|
||||
export const PULSE_SERVER: string;
|
||||
export const _: string;
|
||||
export const npm_config_prefix: string;
|
||||
@@ -73,15 +64,12 @@ declare module '$env/static/private' {
|
||||
export const SHELL: string;
|
||||
export const npm_package_version: string;
|
||||
export const npm_lifecycle_event: string;
|
||||
export const ELECTRON_RUN_AS_NODE: string;
|
||||
export const KILOCODE_POSTHOG_API_KEY: string;
|
||||
export const GOOGLE_CLOUD_PROJECT: string;
|
||||
export const LESSCLOSE: string;
|
||||
export const VIRTUAL_ENV: string;
|
||||
export const npm_config_globalconfig: string;
|
||||
export const npm_config_init_module: string;
|
||||
export const PWD: string;
|
||||
export const LC_ALL: string;
|
||||
export const npm_execpath: string;
|
||||
export const XDG_DATA_DIRS: string;
|
||||
export const npm_config_global_prefix: string;
|
||||
@@ -104,7 +92,7 @@ declare module '$env/static/private' {
|
||||
* ```
|
||||
*/
|
||||
declare module '$env/static/public' {
|
||||
|
||||
export const PUBLIC_WS_URL: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -122,20 +110,13 @@ declare module '$env/static/public' {
|
||||
declare module '$env/dynamic/private' {
|
||||
export const env: {
|
||||
LESSOPEN: string;
|
||||
VSCODE_CWD: string;
|
||||
VSCODE_ESM_ENTRYPOINT: string;
|
||||
USER: string;
|
||||
VSCODE_NLS_CONFIG: string;
|
||||
npm_config_user_agent: string;
|
||||
VSCODE_WSL_EXT_LOCATION: string;
|
||||
VSCODE_HANDLES_UNCAUGHT_ERRORS: string;
|
||||
npm_node_execpath: string;
|
||||
SHLVL: string;
|
||||
npm_config_noproxy: string;
|
||||
HOME: string;
|
||||
OLDPWD: string;
|
||||
VSCODE_RECONNECTION_GRACE_TIME: string;
|
||||
VSCODE_IPC_HOOK_CLI: string;
|
||||
npm_package_json: string;
|
||||
PS1: string;
|
||||
npm_config_userconfig: string;
|
||||
@@ -144,11 +125,9 @@ declare module '$env/dynamic/private' {
|
||||
WSL_DISTRO_NAME: string;
|
||||
COLOR: string;
|
||||
WAYLAND_DISPLAY: string;
|
||||
VSCODE_L10N_BUNDLE_LOCATION: string;
|
||||
LOGNAME: string;
|
||||
NAME: string;
|
||||
WSL_INTEROP: string;
|
||||
VSCODE_HANDLES_SIGPIPE: string;
|
||||
PULSE_SERVER: string;
|
||||
_: string;
|
||||
npm_config_prefix: string;
|
||||
@@ -168,15 +147,12 @@ declare module '$env/dynamic/private' {
|
||||
SHELL: string;
|
||||
npm_package_version: string;
|
||||
npm_lifecycle_event: string;
|
||||
ELECTRON_RUN_AS_NODE: string;
|
||||
KILOCODE_POSTHOG_API_KEY: string;
|
||||
GOOGLE_CLOUD_PROJECT: string;
|
||||
LESSCLOSE: string;
|
||||
VIRTUAL_ENV: string;
|
||||
npm_config_globalconfig: string;
|
||||
npm_config_init_module: string;
|
||||
PWD: string;
|
||||
LC_ALL: string;
|
||||
npm_execpath: string;
|
||||
XDG_DATA_DIRS: string;
|
||||
npm_config_global_prefix: string;
|
||||
@@ -204,6 +180,7 @@ declare module '$env/dynamic/private' {
|
||||
*/
|
||||
declare module '$env/dynamic/public' {
|
||||
export const env: {
|
||||
PUBLIC_WS_URL: string;
|
||||
[key: `PUBLIC_${string}`]: string | undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ export const options = {
|
||||
app: ({ head, body, assets, nonce, env }) => "<!DOCTYPE html>\n<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"utf-8\" />\n\t\t<link rel=\"icon\" href=\"" + assets + "/favicon.png\" />\n\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n\t\t" + head + "\n\t</head>\n\t<body data-sveltekit-preload-data=\"hover\">\n\t\t<div style=\"display: contents\">" + body + "</div>\n\t</body>\n</html>\n",
|
||||
error: ({ status, message }) => "<!doctype html>\n<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"utf-8\" />\n\t\t<title>" + message + "</title>\n\n\t\t<style>\n\t\t\tbody {\n\t\t\t\t--bg: white;\n\t\t\t\t--fg: #222;\n\t\t\t\t--divider: #ccc;\n\t\t\t\tbackground: var(--bg);\n\t\t\t\tcolor: var(--fg);\n\t\t\t\tfont-family:\n\t\t\t\t\tsystem-ui,\n\t\t\t\t\t-apple-system,\n\t\t\t\t\tBlinkMacSystemFont,\n\t\t\t\t\t'Segoe UI',\n\t\t\t\t\tRoboto,\n\t\t\t\t\tOxygen,\n\t\t\t\t\tUbuntu,\n\t\t\t\t\tCantarell,\n\t\t\t\t\t'Open Sans',\n\t\t\t\t\t'Helvetica Neue',\n\t\t\t\t\tsans-serif;\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tjustify-content: center;\n\t\t\t\theight: 100vh;\n\t\t\t\tmargin: 0;\n\t\t\t}\n\n\t\t\t.error {\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tmax-width: 32rem;\n\t\t\t\tmargin: 0 1rem;\n\t\t\t}\n\n\t\t\t.status {\n\t\t\t\tfont-weight: 200;\n\t\t\t\tfont-size: 3rem;\n\t\t\t\tline-height: 1;\n\t\t\t\tposition: relative;\n\t\t\t\ttop: -0.05rem;\n\t\t\t}\n\n\t\t\t.message {\n\t\t\t\tborder-left: 1px solid var(--divider);\n\t\t\t\tpadding: 0 0 0 1rem;\n\t\t\t\tmargin: 0 0 0 1rem;\n\t\t\t\tmin-height: 2.5rem;\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t}\n\n\t\t\t.message h1 {\n\t\t\t\tfont-weight: 400;\n\t\t\t\tfont-size: 1em;\n\t\t\t\tmargin: 0;\n\t\t\t}\n\n\t\t\t@media (prefers-color-scheme: dark) {\n\t\t\t\tbody {\n\t\t\t\t\t--bg: #222;\n\t\t\t\t\t--fg: #ddd;\n\t\t\t\t\t--divider: #666;\n\t\t\t\t}\n\t\t\t}\n\t\t</style>\n\t</head>\n\t<body>\n\t\t<div class=\"error\">\n\t\t\t<span class=\"status\">" + status + "</span>\n\t\t\t<div class=\"message\">\n\t\t\t\t<h1>" + message + "</h1>\n\t\t\t</div>\n\t\t</div>\n\t</body>\n</html>\n"
|
||||
},
|
||||
version_hash: "1ynwnul"
|
||||
version_hash: "cx7alu"
|
||||
};
|
||||
|
||||
export async function get_hooks() {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
{
|
||||
".svelte-kit/generated/client-optimized/app.js": {
|
||||
"file": "_app/immutable/entry/app.B-xBk5-0.js",
|
||||
"file": "_app/immutable/entry/app.BXnpILpp.js",
|
||||
"name": "entry/app",
|
||||
"src": ".svelte-kit/generated/client-optimized/app.js",
|
||||
"isEntry": true,
|
||||
"imports": [
|
||||
"_CQO205-B.js",
|
||||
"_CWb4Vnhz.js",
|
||||
"_CqZim_6h.js",
|
||||
"_C98uKxzC.js"
|
||||
"_BtL0wB3H.js",
|
||||
"_cv2LK44M.js",
|
||||
"_BxZpmA7Z.js",
|
||||
"_vVxDbqKK.js"
|
||||
],
|
||||
"dynamicImports": [
|
||||
".svelte-kit/generated/client-optimized/nodes/0.js",
|
||||
@@ -18,142 +18,145 @@
|
||||
]
|
||||
},
|
||||
".svelte-kit/generated/client-optimized/nodes/0.js": {
|
||||
"file": "_app/immutable/nodes/0.Cd4CVt-Z.js",
|
||||
"file": "_app/immutable/nodes/0.DZdF_zz-.js",
|
||||
"name": "nodes/0",
|
||||
"src": ".svelte-kit/generated/client-optimized/nodes/0.js",
|
||||
"isEntry": true,
|
||||
"isDynamicEntry": true,
|
||||
"imports": [
|
||||
"_CWb4Vnhz.js",
|
||||
"_CCsGeFPC.js",
|
||||
"_CQO205-B.js",
|
||||
"_DKg_yD9X.js",
|
||||
"_BEiADdeo.js",
|
||||
"_CsANhQOh.js"
|
||||
"_cv2LK44M.js",
|
||||
"_CRLlKr96.js",
|
||||
"_BtL0wB3H.js",
|
||||
"_xdjHc-A2.js",
|
||||
"_DXE57cnx.js",
|
||||
"_Dbod7Wv8.js"
|
||||
],
|
||||
"css": [
|
||||
"_app/immutable/assets/0.RZHRvmcL.css"
|
||||
]
|
||||
},
|
||||
".svelte-kit/generated/client-optimized/nodes/1.js": {
|
||||
"file": "_app/immutable/nodes/1.CppBCq8O.js",
|
||||
"file": "_app/immutable/nodes/1.Bh-fCbID.js",
|
||||
"name": "nodes/1",
|
||||
"src": ".svelte-kit/generated/client-optimized/nodes/1.js",
|
||||
"isEntry": true,
|
||||
"isDynamicEntry": true,
|
||||
"imports": [
|
||||
"_CWb4Vnhz.js",
|
||||
"_CCsGeFPC.js",
|
||||
"_CQO205-B.js",
|
||||
"_BEiADdeo.js"
|
||||
"_cv2LK44M.js",
|
||||
"_CRLlKr96.js",
|
||||
"_BtL0wB3H.js",
|
||||
"_DXE57cnx.js"
|
||||
]
|
||||
},
|
||||
".svelte-kit/generated/client-optimized/nodes/2.js": {
|
||||
"file": "_app/immutable/nodes/2.DbjHrap6.js",
|
||||
"file": "_app/immutable/nodes/2.BmiXdPHI.js",
|
||||
"name": "nodes/2",
|
||||
"src": ".svelte-kit/generated/client-optimized/nodes/2.js",
|
||||
"isEntry": true,
|
||||
"isDynamicEntry": true,
|
||||
"imports": [
|
||||
"_BGnnHgKo.js",
|
||||
"_CWb4Vnhz.js",
|
||||
"_CCsGeFPC.js",
|
||||
"_CQO205-B.js",
|
||||
"_C98uKxzC.js",
|
||||
"_CsANhQOh.js",
|
||||
"_CqZim_6h.js",
|
||||
"_DKg_yD9X.js"
|
||||
"_DyPeVqDG.js",
|
||||
"_cv2LK44M.js",
|
||||
"_CRLlKr96.js",
|
||||
"_BtL0wB3H.js",
|
||||
"_vVxDbqKK.js",
|
||||
"_Dbod7Wv8.js",
|
||||
"_BxZpmA7Z.js",
|
||||
"_xdjHc-A2.js"
|
||||
]
|
||||
},
|
||||
".svelte-kit/generated/client-optimized/nodes/3.js": {
|
||||
"file": "_app/immutable/nodes/3.BgpIj6zk.js",
|
||||
"file": "_app/immutable/nodes/3.guWMyWpk.js",
|
||||
"name": "nodes/3",
|
||||
"src": ".svelte-kit/generated/client-optimized/nodes/3.js",
|
||||
"isEntry": true,
|
||||
"isDynamicEntry": true,
|
||||
"imports": [
|
||||
"_BGnnHgKo.js",
|
||||
"_CWb4Vnhz.js",
|
||||
"_CCsGeFPC.js",
|
||||
"_CQO205-B.js",
|
||||
"_C98uKxzC.js",
|
||||
"_CsANhQOh.js"
|
||||
"_DyPeVqDG.js",
|
||||
"_cv2LK44M.js",
|
||||
"_CRLlKr96.js",
|
||||
"_BtL0wB3H.js",
|
||||
"_vVxDbqKK.js",
|
||||
"_Dbod7Wv8.js"
|
||||
]
|
||||
},
|
||||
"_BEiADdeo.js": {
|
||||
"file": "_app/immutable/chunks/BEiADdeo.js",
|
||||
"name": "stores",
|
||||
"imports": [
|
||||
"_CHnJS4Dz.js"
|
||||
]
|
||||
},
|
||||
"_BGnnHgKo.js": {
|
||||
"file": "_app/immutable/chunks/BGnnHgKo.js",
|
||||
"name": "api",
|
||||
"imports": [
|
||||
"_CQO205-B.js",
|
||||
"_CsANhQOh.js"
|
||||
]
|
||||
},
|
||||
"_C98uKxzC.js": {
|
||||
"file": "_app/immutable/chunks/C98uKxzC.js",
|
||||
"name": "props",
|
||||
"imports": [
|
||||
"_CQO205-B.js",
|
||||
"_CWb4Vnhz.js"
|
||||
]
|
||||
},
|
||||
"_CCsGeFPC.js": {
|
||||
"file": "_app/immutable/chunks/CCsGeFPC.js",
|
||||
"name": "legacy",
|
||||
"imports": [
|
||||
"_CQO205-B.js"
|
||||
]
|
||||
},
|
||||
"_CHnJS4Dz.js": {
|
||||
"file": "_app/immutable/chunks/CHnJS4Dz.js",
|
||||
"name": "entry",
|
||||
"imports": [
|
||||
"_CQO205-B.js",
|
||||
"_CqZim_6h.js"
|
||||
]
|
||||
},
|
||||
"_CQO205-B.js": {
|
||||
"file": "_app/immutable/chunks/CQO205-B.js",
|
||||
"_BtL0wB3H.js": {
|
||||
"file": "_app/immutable/chunks/BtL0wB3H.js",
|
||||
"name": "index"
|
||||
},
|
||||
"_CWb4Vnhz.js": {
|
||||
"file": "_app/immutable/chunks/CWb4Vnhz.js",
|
||||
"name": "disclose-version",
|
||||
"imports": [
|
||||
"_CQO205-B.js"
|
||||
]
|
||||
},
|
||||
"_CqZim_6h.js": {
|
||||
"file": "_app/immutable/chunks/CqZim_6h.js",
|
||||
"_BxZpmA7Z.js": {
|
||||
"file": "_app/immutable/chunks/BxZpmA7Z.js",
|
||||
"name": "index-client",
|
||||
"imports": [
|
||||
"_CQO205-B.js"
|
||||
"_BtL0wB3H.js"
|
||||
]
|
||||
},
|
||||
"_CsANhQOh.js": {
|
||||
"file": "_app/immutable/chunks/CsANhQOh.js",
|
||||
"_CRLlKr96.js": {
|
||||
"file": "_app/immutable/chunks/CRLlKr96.js",
|
||||
"name": "legacy",
|
||||
"imports": [
|
||||
"_BtL0wB3H.js"
|
||||
]
|
||||
},
|
||||
"_D0iaTcAo.js": {
|
||||
"file": "_app/immutable/chunks/D0iaTcAo.js",
|
||||
"name": "entry",
|
||||
"imports": [
|
||||
"_BtL0wB3H.js",
|
||||
"_BxZpmA7Z.js"
|
||||
]
|
||||
},
|
||||
"_DXE57cnx.js": {
|
||||
"file": "_app/immutable/chunks/DXE57cnx.js",
|
||||
"name": "stores",
|
||||
"imports": [
|
||||
"_D0iaTcAo.js"
|
||||
]
|
||||
},
|
||||
"_Dbod7Wv8.js": {
|
||||
"file": "_app/immutable/chunks/Dbod7Wv8.js",
|
||||
"name": "toasts",
|
||||
"imports": [
|
||||
"_CQO205-B.js"
|
||||
"_BtL0wB3H.js"
|
||||
]
|
||||
},
|
||||
"_DKg_yD9X.js": {
|
||||
"file": "_app/immutable/chunks/DKg_yD9X.js",
|
||||
"_DyPeVqDG.js": {
|
||||
"file": "_app/immutable/chunks/DyPeVqDG.js",
|
||||
"name": "api",
|
||||
"imports": [
|
||||
"_BtL0wB3H.js",
|
||||
"_Dbod7Wv8.js"
|
||||
]
|
||||
},
|
||||
"_cv2LK44M.js": {
|
||||
"file": "_app/immutable/chunks/cv2LK44M.js",
|
||||
"name": "disclose-version",
|
||||
"imports": [
|
||||
"_BtL0wB3H.js"
|
||||
]
|
||||
},
|
||||
"_vVxDbqKK.js": {
|
||||
"file": "_app/immutable/chunks/vVxDbqKK.js",
|
||||
"name": "props",
|
||||
"imports": [
|
||||
"_BtL0wB3H.js",
|
||||
"_cv2LK44M.js"
|
||||
]
|
||||
},
|
||||
"_xdjHc-A2.js": {
|
||||
"file": "_app/immutable/chunks/xdjHc-A2.js",
|
||||
"name": "class",
|
||||
"imports": [
|
||||
"_CQO205-B.js"
|
||||
"_BtL0wB3H.js"
|
||||
]
|
||||
},
|
||||
"node_modules/@sveltejs/kit/src/runtime/client/entry.js": {
|
||||
"file": "_app/immutable/entry/start.CiUb2lZD.js",
|
||||
"file": "_app/immutable/entry/start.BHAeOrfR.js",
|
||||
"name": "entry/start",
|
||||
"src": "node_modules/@sveltejs/kit/src/runtime/client/entry.js",
|
||||
"isEntry": true,
|
||||
"imports": [
|
||||
"_CHnJS4Dz.js"
|
||||
"_D0iaTcAo.js"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
import{s as e}from"./CHnJS4Dz.js";const r=()=>{const s=e;return{page:{subscribe:s.page.subscribe},navigating:{subscribe:s.navigating.subscribe},updated:s.updated}},b={subscribe(s){return r().page.subscribe(s)}};export{b as p};
|
||||
@@ -1 +0,0 @@
|
||||
import{h as v,Z as S,a5 as T,a6 as b,a7 as C,a8 as P,a9 as I,aa as w,ab as N,e as $,ac as p,J as i,ad as L}from"./CQO205-B.js";import{a as m}from"./CsANhQOh.js";const M=Symbol("is custom element"),q=Symbol("is html");function F(e){if(v){var t=!1,r=()=>{if(!t){if(t=!0,e.hasAttribute("value")){var s=e.value;k(e,"value",null),e.value=s}if(e.hasAttribute("checked")){var a=e.checked;k(e,"checked",null),e.checked=a}}};e.__on_r=r,S(r),T()}}function k(e,t,r,s){var a=j(e);v&&(a[t]=e.getAttribute(t),t==="src"||t==="srcset"||t==="href"&&e.nodeName==="LINK")||a[t]!==(a[t]=r)&&(t==="loading"&&(e[b]=r),r==null?e.removeAttribute(t):typeof r!="string"&&O(e).includes(t)?e[t]=r:e.setAttribute(t,r))}function j(e){return e.__attributes??={[M]:e.nodeName.includes("-"),[q]:e.namespaceURI===C}}var y=new Map;function O(e){var t=e.getAttribute("is")||e.nodeName,r=y.get(t);if(r)return r;y.set(t,r=[]);for(var s,a=e,o=Element.prototype;o!==a;){s=I(a);for(var n in s)s[n].set&&r.push(n);a=P(a)}return r}function U(e,t,r=t){var s=new WeakSet;w(e,"input",async a=>{var o=a?e.defaultValue:e.value;if(o=u(e)?h(o):o,r(o),i!==null&&s.add(i),await N(),o!==(o=t())){var n=e.selectionStart,f=e.selectionEnd,A=e.value.length;if(e.value=o??"",f!==null){var d=e.value.length;n===f&&f===A&&d>A?(e.selectionStart=d,e.selectionEnd=d):(e.selectionStart=n,e.selectionEnd=Math.min(f,d))}}}),(v&&e.defaultValue!==e.value||$(t)==null&&e.value)&&(r(u(e)?h(e.value):e.value),i!==null&&s.add(i)),p(()=>{var a=t();if(e===document.activeElement){var o=L??i;if(s.has(o))return}u(e)&&a===h(e.value)||e.type==="date"&&!a&&!e.value||a!==e.value&&(e.value=a??"")})}function B(e,t,r=t){w(e,"change",s=>{var a=s?e.defaultChecked:e.checked;r(a)}),(v&&e.defaultChecked!==e.checked||$(t)==null)&&r(e.checked),p(()=>{var s=t();e.checked=!!s})}function u(e){var t=e.type;return t==="number"||t==="range"}function h(e){return e===""?null:+e}const E="/api";async function c(e){try{console.log(`[api.fetchApi][Action] Fetching from context={{'endpoint': '${e}'}}`);const t=await fetch(`${E}${e}`);if(!t.ok)throw new Error(`API request failed with status ${t.status}`);return await t.json()}catch(t){throw console.error(`[api.fetchApi][Coherence:Failed] Error fetching from ${e}:`,t),m(t.message,"error"),t}}async function _(e,t){try{console.log(`[api.postApi][Action] Posting to context={{'endpoint': '${e}'}}`);const r=await fetch(`${E}${e}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)});if(!r.ok)throw new Error(`API request failed with status ${r.status}`);return await r.json()}catch(r){throw console.error(`[api.postApi][Coherence:Failed] Error posting to ${e}:`,r),m(r.message,"error"),r}}async function g(e,t="GET",r=null){try{console.log(`[api.requestApi][Action] ${t} to context={{'endpoint': '${e}'}}`);const s={method:t,headers:{"Content-Type":"application/json"}};r&&(s.body=JSON.stringify(r));const a=await fetch(`${E}${e}`,s);if(!a.ok){const o=await a.json().catch(()=>({}));throw new Error(o.detail||`API request failed with status ${a.status}`)}return await a.json()}catch(s){throw console.error(`[api.requestApi][Coherence:Failed] Error ${t} to ${e}:`,s),m(s.message,"error"),s}}const l={getPlugins:()=>c("/plugins/"),getTasks:()=>c("/tasks/"),getTask:e=>c(`/tasks/${e}`),createTask:(e,t)=>_("/tasks/",{plugin_id:e,params:t}),getSettings:()=>c("/settings/"),updateGlobalSettings:e=>g("/settings/global","PATCH",e),getEnvironments:()=>c("/settings/environments"),addEnvironment:e=>_("/settings/environments",e),updateEnvironment:(e,t)=>g(`/settings/environments/${e}`,"PUT",t),deleteEnvironment:e=>g(`/settings/environments/${e}`,"DELETE"),testEnvironmentConnection:e=>_(`/settings/environments/${e}/test`,{})},D=l.updateGlobalSettings,H=l.addEnvironment,J=l.updateEnvironment,R=l.deleteEnvironment,V=l.testEnvironmentConnection;export{l as a,U as b,B as c,J as d,H as e,R as f,F as r,k as s,V as t,D as u};
|
||||
@@ -1 +0,0 @@
|
||||
import{J as E,S as y,X as S,T as L,v as P,L as T,h as g,E as M,ae as k,M as w,w as B,z as N,af as x,B as Y,H as C,C as F,x as U,D as R,ag as j,ah as q,j as b,ai as z,o as H,aj as $,ak as G,A as J,d as X,al as Z,am as K,an as Q,ao as V,ap as W,a3 as ee,e as te,aq as ae,ar as se,as as re}from"./CQO205-B.js";import{d as ie}from"./CWb4Vnhz.js";class ne{anchor;#t=new Map;#a=new Map;#e=new Map;#s=new Set;#r=!0;constructor(e,a=!0){this.anchor=e,this.#r=a}#i=()=>{var e=E;if(this.#t.has(e)){var a=this.#t.get(e),t=this.#a.get(a);if(t)y(t),this.#s.delete(a);else{var n=this.#e.get(a);n&&(this.#a.set(a,n.effect),this.#e.delete(a),n.fragment.lastChild.remove(),this.anchor.before(n.fragment),t=n.effect)}for(const[s,i]of this.#t){if(this.#t.delete(s),s===e)break;const r=this.#e.get(i);r&&(S(r.effect),this.#e.delete(i))}for(const[s,i]of this.#a){if(s===a||this.#s.has(s))continue;const r=()=>{if(Array.from(this.#t.values()).includes(s)){var h=document.createDocumentFragment();k(i,h),h.append(P()),this.#e.set(s,{effect:i,fragment:h})}else S(i);this.#s.delete(s),this.#a.delete(s)};this.#r||!t?(this.#s.add(s),L(i,r,!1)):r()}}};#n=e=>{this.#t.delete(e);const a=Array.from(this.#t.values());for(const[t,n]of this.#e)a.includes(t)||(S(n.effect),this.#e.delete(t))};ensure(e,a){var t=E,n=w();if(a&&!this.#a.has(e)&&!this.#e.has(e))if(n){var s=document.createDocumentFragment(),i=P();s.append(i),this.#e.set(e,{effect:T(()=>a(i)),fragment:s})}else this.#a.set(e,T(()=>a(this.anchor)));if(this.#t.set(t,e),n){for(const[r,f]of this.#a)r===e?t.skipped_effects.delete(f):t.skipped_effects.add(f);for(const[r,f]of this.#e)r===e?t.skipped_effects.delete(f.effect):t.skipped_effects.add(f.effect);t.oncommit(this.#i),t.ondiscard(this.#n)}else g&&(this.anchor=M),this.#i()}}function de(d,e,a=!1){g&&N();var t=new ne(d),n=a?x:0;function s(i,r){if(g){const h=Y(d)===C;if(i===h){var f=F();U(f),t.anchor=f,R(!1),t.ensure(i,r),R(!0);return}}t.ensure(i,r)}B(()=>{var i=!1;e((r,f=!0)=>{i=!0,s(f,r)}),i||s(!1,null)},n)}function he(d,e,a,t){var n=!X||(a&Z)!==0,s=(a&K)!==0,i=(a&re)!==0,r=t,f=!0,h=()=>(f&&(f=!1,r=i?te(t):t),r),o;if(s){var I=V in d||W in d;o=j(d,e)?.set??(I&&e in d?c=>d[e]=c:void 0)}var _,p=!1;s?[_,p]=ie(()=>d[e]):_=d[e],_===void 0&&t!==void 0&&(_=h(),o&&(n&&ae(),o(_)));var u;if(n?u=()=>{var c=d[e];return c===void 0?h():(f=!0,c)}:u=()=>{var c=d[e];return c!==void 0&&(r=void 0),c===void 0?r:c},n&&(a&q)===0)return u;if(o){var D=d.$$legacy;return(function(c,v){return arguments.length>0?((!n||!v||D||p)&&o(v?u():c),c):u()})}var m=!1,l=((a&se)!==0?ee:J)(()=>(m=!1,u()));s&&b(l);var O=$;return(function(c,v){if(arguments.length>0){const A=v?b(l):n&&s?z(c):c;return H(l,A),m=!0,r!==void 0&&(r=A),c}return Q&&m||(O.f&G)!==0?l.v:b(l)})}export{ne as B,de as i,he as p};
|
||||
@@ -1 +0,0 @@
|
||||
import{b as d,a0 as g,u as c,e as b,a1 as i,a2 as m,j as p,k,a3 as v,a4 as h}from"./CQO205-B.js";function x(a=!1){const s=d,e=s.l.u;if(!e)return;let f=()=>k(s.s);if(a){let n=0,t={};const _=v(()=>{let l=!1;const r=s.s;for(const o in r)r[o]!==t[o]&&(t[o]=r[o],l=!0);return l&&n++,n});f=()=>p(_)}e.b.length&&g(()=>{u(s,f),i(e.b)}),c(()=>{const n=b(()=>e.m.map(m));return()=>{for(const t of n)typeof t=="function"&&t()}}),e.a.length&&c(()=>{u(s,f),i(e.a)})}function u(a,s){if(a.l.s)for(const e of a.l.s)p(e);s()}h();export{x as i};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
import{l as a,u as i,b as t,d as _,e as l,i as m}from"./CQO205-B.js";function p(e){t===null&&a(),_&&t.l!==null?d(t).m.push(e):i(()=>{const n=l(e);if(typeof n=="function")return n})}function x(e){t===null&&a(),p(()=>()=>l(e))}function v(e,n,{bubbles:o=!1,cancelable:s=!1}={}){return new CustomEvent(e,{detail:n,bubbles:o,cancelable:s})}function y(){const e=t;return e===null&&a(),(n,o,s)=>{const c=e.s.$$events?.[n];if(c){const r=m(c)?c.slice():[c],u=v(n,o,s);for(const f of r)f.call(e.x,u);return!u.defaultPrevented}return!0}}function d(e){var n=e.l;return n.u??={a:[],b:[],m:[]}}export{x as a,y as c,p as o};
|
||||
@@ -1 +0,0 @@
|
||||
import{v as H,w as W,x as O,h as R,y as Z,z as y,j as V,A as j,B as ee,H as ne,C as Y,D as k,E as D,F as re,G as fe,I as q,J as ae,K as m,L as F,M as ie,N as B,m as se,O as $,i as le,P as oe,Q as te,R as ue,S as X,T as G,U as z,V as de,W as ve,X as ce,Y as J,Z as pe,_ as ge,$ as he}from"./CQO205-B.js";function Se(e,r){return r}function _e(e,r,f){for(var t=[],g=r.length,o,l=r.length,p=0;p<g;p++){let _=r[p];G(_,()=>{if(o){if(o.pending.delete(_),o.done.add(_),o.pending.size===0){var u=e.outrogroups;L($(o.done)),u.delete(o),u.size===0&&(e.outrogroups=null)}}else l-=1},!1)}if(l===0){var i=t.length===0&&f!==null;if(i){var d=f,a=d.parentNode;ve(a),a.append(d),e.items.clear()}L(r,!i)}else o={pending:new Set(r),done:new Set},(e.outrogroups??=new Set).add(o)}function L(e,r=!0){for(var f=0;f<e.length;f++)ce(e[f],r)}var U;function we(e,r,f,t,g,o=null){var l=e,p=new Map,i=(r&J)!==0;if(i){var d=e;l=R?O(Z(d)):d.appendChild(H())}R&&y();var a=null,_=j(()=>{var s=f();return le(s)?s:s==null?[]:$(s)}),u,c=!0;function A(){n.fallback=a,Ee(n,u,l,r,t),a!==null&&(u.length===0?(a.f&m)===0?X(a):(a.f^=m,b(a,null,l)):G(a,()=>{a=null}))}var C=W(()=>{u=V(_);var s=u.length;let I=!1;if(R){var x=ee(l)===ne;x!==(s===0)&&(l=Y(),O(l),k(!1),I=!0)}for(var E=new Set,S=ae,N=ie(),h=0;h<s;h+=1){R&&D.nodeType===re&&D.data===fe&&(l=D,I=!0,k(!1));var w=u[h],M=t(w,h),v=c?null:p.get(M);v?(v.v&&q(v.v,w),v.i&&q(v.i,h),N&&S.skipped_effects.delete(v.e)):(v=me(p,c?l:U??=H(),w,M,h,g,r,f),c||(v.e.f|=m),p.set(M,v)),E.add(M)}if(s===0&&o&&!a&&(c?a=F(()=>o(l)):(a=F(()=>o(U??=H())),a.f|=m)),R&&s>0&&O(Y()),!c)if(N){for(const[P,Q]of p)E.has(P)||S.skipped_effects.add(Q.e);S.oncommit(A),S.ondiscard(()=>{})}else A();I&&k(!0),V(_)}),n={effect:C,items:p,outrogroups:null,fallback:a};c=!1,R&&(l=D)}function Ee(e,r,f,t,g){var o=(t&ge)!==0,l=r.length,p=e.items,i=e.effect.first,d,a=null,_,u=[],c=[],A,C,n,s;if(o)for(s=0;s<l;s+=1)A=r[s],C=g(A,s),n=p.get(C).e,(n.f&m)===0&&(n.nodes?.a?.measure(),(_??=new Set).add(n));for(s=0;s<l;s+=1){if(A=r[s],C=g(A,s),n=p.get(C).e,e.outrogroups!==null)for(const v of e.outrogroups)v.pending.delete(n),v.done.delete(n);if((n.f&m)!==0)if(n.f^=m,n===i)b(n,null,f);else{var I=a?a.next:i;n===e.effect.last&&(e.effect.last=n.prev),n.prev&&(n.prev.next=n.next),n.next&&(n.next.prev=n.prev),T(e,a,n),T(e,n,I),b(n,I,f),a=n,u=[],c=[],i=a.next;continue}if((n.f&z)!==0&&(X(n),o&&(n.nodes?.a?.unfix(),(_??=new Set).delete(n))),n!==i){if(d!==void 0&&d.has(n)){if(u.length<c.length){var x=c[0],E;a=x.prev;var S=u[0],N=u[u.length-1];for(E=0;E<u.length;E+=1)b(u[E],x,f);for(E=0;E<c.length;E+=1)d.delete(c[E]);T(e,S.prev,N.next),T(e,a,S),T(e,N,x),i=x,a=N,s-=1,u=[],c=[]}else d.delete(n),b(n,i,f),T(e,n.prev,n.next),T(e,n,a===null?e.effect.first:a.next),T(e,a,n),a=n;continue}for(u=[],c=[];i!==null&&i!==n;)(d??=new Set).add(i),c.push(i),i=i.next;if(i===null)continue}(n.f&m)===0&&u.push(n),a=n,i=n.next}if(e.outrogroups!==null){for(const v of e.outrogroups)v.pending.size===0&&(L($(v.done)),e.outrogroups?.delete(v));e.outrogroups.size===0&&(e.outrogroups=null)}if(i!==null||d!==void 0){var h=[];if(d!==void 0)for(n of d)(n.f&z)===0&&h.push(n);for(;i!==null;)(i.f&z)===0&&i!==e.fallback&&h.push(i),i=i.next;var w=h.length;if(w>0){var M=(t&J)!==0&&l===0?f:null;if(o){for(s=0;s<w;s+=1)h[s].nodes?.a?.measure();for(s=0;s<w;s+=1)h[s].nodes?.a?.fix()}_e(e,h,M)}}o&&pe(()=>{if(_!==void 0)for(n of _)n.nodes?.a?.apply()})}function me(e,r,f,t,g,o,l,p){var i=(l&oe)!==0?(l&te)===0?se(f,!1,!1):B(f):null,d=(l&ue)!==0?B(g):null;return{v:i,i:d,e:F(()=>(o(r,i??f,d??g,p),()=>{e.delete(t)}))}}function b(e,r,f){if(e.nodes)for(var t=e.nodes.start,g=e.nodes.end,o=r&&(r.f&m)===0?r.nodes.start:f;t!==null;){var l=de(t);if(o.before(t),t===g)return;t=l}}function T(e,r,f){r===null?e.effect.first=f:r.next=f,f===null?e.effect.last=r:f.prev=r}const K=he([]);function Ce(e,r="info",f=3e3){const t=Math.random().toString(36).substr(2,9);console.log(`[toasts.addToast][Action] Adding toast context={{'id': '${t}', 'type': '${r}', 'message': '${e}'}}`),K.update(g=>[...g,{id:t,message:e,type:r}]),setTimeout(()=>Te(t),f)}function Te(e){console.log(`[toasts.removeToast][Action] Removing toast context={{'id': '${e}'}}`),K.update(r=>r.filter(f=>f.id!==e))}export{Ce as a,we as e,Se as i,K as t};
|
||||
@@ -1 +0,0 @@
|
||||
import{h as i}from"./CQO205-B.js";function n(s,l,r){var t=s==null?"":""+s;return t===""?null:t}function u(s,l,r,t,f,c){var a=s.__className;if(i||a!==r||a===void 0){var e=n(r);(!i||e!==s.getAttribute("class"))&&(e==null?s.removeAttribute("class"):s.className=e),s.__className=r}return c}export{u as s};
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
import{l as o,a as r}from"../chunks/CHnJS4Dz.js";export{o as load_css,r as start};
|
||||
@@ -1,5 +0,0 @@
|
||||
import{f as u,a as c,s as b,c as g,b as x}from"../chunks/CWb4Vnhz.js";import{i as _}from"../chunks/CCsGeFPC.js";import{h as $,z as y,p as w,t as h,a as S,s as d,c as v,r as l,j as p,at as j}from"../chunks/CQO205-B.js";import{s as m}from"../chunks/DKg_yD9X.js";import{p as T}from"../chunks/BEiADdeo.js";import{e as z,t as O}from"../chunks/CsANhQOh.js";function k(r,a,s,o,n){$&&y();var e=a.$$slots?.[s],t=!1;e===!0&&(e=a.children,t=!0),e===void 0||e(r,t?()=>o:o)}const A=!1,D=!1,Q=Object.freeze(Object.defineProperty({__proto__:null,prerender:D,ssr:A},Symbol.toStringTag,{value:"Module"}));var F=u('<header class="bg-white shadow-md p-4 flex justify-between items-center"><a href="/" class="text-3xl font-bold text-gray-800 focus:outline-none">Superset Tools</a> <nav class="space-x-4"><a href="/">Dashboard</a> <a href="/settings">Settings</a></nav></header>');function M(r,a){w(a,!1);const s=()=>g(T,"$page",o),[o,n]=b();_();var e=F(),t=d(v(e),2),i=v(t),f=d(i,2);l(t),l(e),h(()=>{m(i,1,`text-gray-600 hover:text-blue-600 font-medium ${s().url.pathname==="/"?"text-blue-600 border-b-2 border-blue-600":""}`),m(f,1,`text-gray-600 hover:text-blue-600 font-medium ${s().url.pathname==="/settings"?"text-blue-600 border-b-2 border-blue-600":""}`)}),c(r,e),S(),n()}var N=u('<footer class="bg-white border-t p-4 mt-8 text-center text-gray-500 text-sm">© 2025 Superset Tools. All rights reserved.</footer>');function P(r){var a=N();c(r,a)}var q=u("<div> </div>"),B=u('<div class="fixed bottom-0 right-0 p-4 space-y-2"></div>');function C(r){const a=()=>g(O,"$toasts",s),[s,o]=b();var n=B();z(n,5,a,e=>e.id,(e,t)=>{var i=q(),f=v(i,!0);l(i),h(()=>{m(i,1,`p-4 rounded-md shadow-lg text-white
|
||||
${p(t).type==="info"&&"bg-blue-500"}
|
||||
${p(t).type==="success"&&"bg-green-500"}
|
||||
${p(t).type==="error"&&"bg-red-500"}
|
||||
`),x(f,p(t).message)}),c(e,i)}),l(n),c(r,n),o()}var E=u('<!> <main class="bg-gray-50 min-h-screen flex flex-col"><!> <div class="p-4 flex-grow"><!></div> <!></main>',1);function R(r,a){var s=E(),o=j(s);C(o);var n=d(o,2),e=v(n);M(e,{});var t=d(e,2),i=v(t);k(i,a,"default",{}),l(t);var f=d(t,2);P(f),l(n),c(r,s)}export{R as component,Q as universal};
|
||||
@@ -1 +0,0 @@
|
||||
import{f,a as g,s as u,b as p,c as b}from"../chunks/CWb4Vnhz.js";import{i as d}from"../chunks/CCsGeFPC.js";import{p as h,t as v,a as _,c as e,r as s,s as $,n as y}from"../chunks/CQO205-B.js";import{p as k}from"../chunks/BEiADdeo.js";var w=f('<div class="container mx-auto p-4 text-center mt-20"><h1 class="text-6xl font-bold text-gray-800 mb-4"> </h1> <p class="text-2xl text-gray-600 mb-8"> </p> <a href="/" class="bg-blue-500 text-white px-6 py-3 rounded-lg hover:bg-blue-600 transition-colors">Back to Dashboard</a></div>');function q(n,i){h(i,!1);const r=()=>b(k,"$page",c),[c,l]=u();d();var t=w(),a=e(t),m=e(a,!0);s(a);var o=$(a,2),x=e(o,!0);s(o),y(2),s(t),v(()=>{p(m,r().status),p(x,r().error?.message||"Page not found")}),g(n,t),_(),l()}export{q as component};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
{"version":"1766259433446"}
|
||||
{"version":"1766262590857"}
|
||||
@@ -1 +1 @@
|
||||
export const env={}
|
||||
export const env={"PUBLIC_WS_URL":"ws://localhost:8000"}
|
||||
@@ -129,6 +129,9 @@
|
||||
"_index2.js",
|
||||
"_stores.js",
|
||||
"_toasts.js"
|
||||
],
|
||||
"css": [
|
||||
"_app/immutable/assets/_layout.RZHRvmcL.css"
|
||||
]
|
||||
},
|
||||
"src/routes/+layout.ts": {
|
||||
|
||||
@@ -950,7 +950,7 @@ const options = {
|
||||
<div class="error">
|
||||
<span class="status">` + status + '</span>\n <div class="message">\n <h1>' + message + "</h1>\n </div>\n </div>\n </body>\n</html>\n"
|
||||
},
|
||||
version_hash: "1uq6ubj"
|
||||
version_hash: "1ootf77"
|
||||
};
|
||||
async function get_hooks() {
|
||||
let handle;
|
||||
|
||||
@@ -10,7 +10,7 @@ return {
|
||||
assets: new Set([]),
|
||||
mimeTypes: {},
|
||||
_: {
|
||||
client: {start:"_app/immutable/entry/start.CiUb2lZD.js",app:"_app/immutable/entry/app.B-xBk5-0.js",imports:["_app/immutable/entry/start.CiUb2lZD.js","_app/immutable/chunks/CHnJS4Dz.js","_app/immutable/chunks/CQO205-B.js","_app/immutable/chunks/CqZim_6h.js","_app/immutable/entry/app.B-xBk5-0.js","_app/immutable/chunks/CQO205-B.js","_app/immutable/chunks/CWb4Vnhz.js","_app/immutable/chunks/CqZim_6h.js","_app/immutable/chunks/C98uKxzC.js"],stylesheets:[],fonts:[],uses_env_dynamic_public:false},
|
||||
client: {start:"_app/immutable/entry/start.BHAeOrfR.js",app:"_app/immutable/entry/app.BXnpILpp.js",imports:["_app/immutable/entry/start.BHAeOrfR.js","_app/immutable/chunks/D0iaTcAo.js","_app/immutable/chunks/BtL0wB3H.js","_app/immutable/chunks/BxZpmA7Z.js","_app/immutable/entry/app.BXnpILpp.js","_app/immutable/chunks/BtL0wB3H.js","_app/immutable/chunks/cv2LK44M.js","_app/immutable/chunks/BxZpmA7Z.js","_app/immutable/chunks/vVxDbqKK.js"],stylesheets:[],fonts:[],uses_env_dynamic_public:false},
|
||||
nodes: [
|
||||
__memo(() => import('./nodes/0.js')),
|
||||
__memo(() => import('./nodes/1.js')),
|
||||
|
||||
@@ -10,7 +10,7 @@ return {
|
||||
assets: new Set([]),
|
||||
mimeTypes: {},
|
||||
_: {
|
||||
client: {start:"_app/immutable/entry/start.CiUb2lZD.js",app:"_app/immutable/entry/app.B-xBk5-0.js",imports:["_app/immutable/entry/start.CiUb2lZD.js","_app/immutable/chunks/CHnJS4Dz.js","_app/immutable/chunks/CQO205-B.js","_app/immutable/chunks/CqZim_6h.js","_app/immutable/entry/app.B-xBk5-0.js","_app/immutable/chunks/CQO205-B.js","_app/immutable/chunks/CWb4Vnhz.js","_app/immutable/chunks/CqZim_6h.js","_app/immutable/chunks/C98uKxzC.js"],stylesheets:[],fonts:[],uses_env_dynamic_public:false},
|
||||
client: {start:"_app/immutable/entry/start.BHAeOrfR.js",app:"_app/immutable/entry/app.BXnpILpp.js",imports:["_app/immutable/entry/start.BHAeOrfR.js","_app/immutable/chunks/D0iaTcAo.js","_app/immutable/chunks/BtL0wB3H.js","_app/immutable/chunks/BxZpmA7Z.js","_app/immutable/entry/app.BXnpILpp.js","_app/immutable/chunks/BtL0wB3H.js","_app/immutable/chunks/cv2LK44M.js","_app/immutable/chunks/BxZpmA7Z.js","_app/immutable/chunks/vVxDbqKK.js"],stylesheets:[],fonts:[],uses_env_dynamic_public:false},
|
||||
nodes: [
|
||||
__memo(() => import('./nodes/0.js')),
|
||||
__memo(() => import('./nodes/1.js')),
|
||||
|
||||
@@ -8,6 +8,6 @@ export const universal = {
|
||||
"prerender": false
|
||||
};
|
||||
export const universal_id = "src/routes/+layout.ts";
|
||||
export const imports = ["_app/immutable/nodes/0.Cd4CVt-Z.js","_app/immutable/chunks/CWb4Vnhz.js","_app/immutable/chunks/CQO205-B.js","_app/immutable/chunks/CCsGeFPC.js","_app/immutable/chunks/DKg_yD9X.js","_app/immutable/chunks/BEiADdeo.js","_app/immutable/chunks/CHnJS4Dz.js","_app/immutable/chunks/CqZim_6h.js","_app/immutable/chunks/CsANhQOh.js"];
|
||||
export const stylesheets = [];
|
||||
export const imports = ["_app/immutable/nodes/0.DZdF_zz-.js","_app/immutable/chunks/cv2LK44M.js","_app/immutable/chunks/BtL0wB3H.js","_app/immutable/chunks/CRLlKr96.js","_app/immutable/chunks/xdjHc-A2.js","_app/immutable/chunks/DXE57cnx.js","_app/immutable/chunks/D0iaTcAo.js","_app/immutable/chunks/BxZpmA7Z.js","_app/immutable/chunks/Dbod7Wv8.js"];
|
||||
export const stylesheets = ["_app/immutable/assets/0.RZHRvmcL.css"];
|
||||
export const fonts = [];
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
export const index = 1;
|
||||
let component_cache;
|
||||
export const component = async () => component_cache ??= (await import('../entries/pages/_error.svelte.js')).default;
|
||||
export const imports = ["_app/immutable/nodes/1.CppBCq8O.js","_app/immutable/chunks/CWb4Vnhz.js","_app/immutable/chunks/CQO205-B.js","_app/immutable/chunks/CCsGeFPC.js","_app/immutable/chunks/BEiADdeo.js","_app/immutable/chunks/CHnJS4Dz.js","_app/immutable/chunks/CqZim_6h.js"];
|
||||
export const imports = ["_app/immutable/nodes/1.Bh-fCbID.js","_app/immutable/chunks/cv2LK44M.js","_app/immutable/chunks/BtL0wB3H.js","_app/immutable/chunks/CRLlKr96.js","_app/immutable/chunks/DXE57cnx.js","_app/immutable/chunks/D0iaTcAo.js","_app/immutable/chunks/BxZpmA7Z.js"];
|
||||
export const stylesheets = [];
|
||||
export const fonts = [];
|
||||
|
||||
@@ -9,6 +9,6 @@ export const universal = {
|
||||
"load": null
|
||||
};
|
||||
export const universal_id = "src/routes/+page.ts";
|
||||
export const imports = ["_app/immutable/nodes/2.DbjHrap6.js","_app/immutable/chunks/BGnnHgKo.js","_app/immutable/chunks/CQO205-B.js","_app/immutable/chunks/CsANhQOh.js","_app/immutable/chunks/CWb4Vnhz.js","_app/immutable/chunks/CCsGeFPC.js","_app/immutable/chunks/C98uKxzC.js","_app/immutable/chunks/CqZim_6h.js","_app/immutable/chunks/DKg_yD9X.js"];
|
||||
export const imports = ["_app/immutable/nodes/2.BmiXdPHI.js","_app/immutable/chunks/DyPeVqDG.js","_app/immutable/chunks/BtL0wB3H.js","_app/immutable/chunks/Dbod7Wv8.js","_app/immutable/chunks/cv2LK44M.js","_app/immutable/chunks/CRLlKr96.js","_app/immutable/chunks/vVxDbqKK.js","_app/immutable/chunks/BxZpmA7Z.js","_app/immutable/chunks/xdjHc-A2.js"];
|
||||
export const stylesheets = [];
|
||||
export const fonts = [];
|
||||
|
||||
@@ -9,6 +9,6 @@ export const universal = {
|
||||
"load": null
|
||||
};
|
||||
export const universal_id = "src/routes/settings/+page.ts";
|
||||
export const imports = ["_app/immutable/nodes/3.BgpIj6zk.js","_app/immutable/chunks/BGnnHgKo.js","_app/immutable/chunks/CQO205-B.js","_app/immutable/chunks/CsANhQOh.js","_app/immutable/chunks/CWb4Vnhz.js","_app/immutable/chunks/CCsGeFPC.js","_app/immutable/chunks/C98uKxzC.js"];
|
||||
export const imports = ["_app/immutable/nodes/3.guWMyWpk.js","_app/immutable/chunks/DyPeVqDG.js","_app/immutable/chunks/BtL0wB3H.js","_app/immutable/chunks/Dbod7Wv8.js","_app/immutable/chunks/cv2LK44M.js","_app/immutable/chunks/CRLlKr96.js","_app/immutable/chunks/vVxDbqKK.js"];
|
||||
export const stylesheets = [];
|
||||
export const fonts = [];
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
import { onMount, onDestroy } from 'svelte';
|
||||
import { get } from 'svelte/store';
|
||||
import { selectedTask, taskLogs } from '../lib/stores.js';
|
||||
import { getWsUrl } from '../lib/api.js';
|
||||
// [/SECTION]
|
||||
|
||||
let ws;
|
||||
@@ -26,8 +27,7 @@
|
||||
if (task) {
|
||||
console.log(`[TaskRunner][Entry] Connecting to logs for task: ${task.id}`);
|
||||
taskLogs.set([]); // Clear previous logs
|
||||
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
const wsUrl = `${protocol}//${window.location.host}/ws/logs/${task.id}`;
|
||||
const wsUrl = getWsUrl(task.id);
|
||||
ws = new WebSocket(wsUrl);
|
||||
|
||||
ws.onopen = () => {
|
||||
|
||||
@@ -4,9 +4,20 @@
|
||||
// @LAYER: Infra-API
|
||||
|
||||
import { addToast } from './toasts.js';
|
||||
import { PUBLIC_WS_URL } from '$env/static/public';
|
||||
|
||||
const API_BASE_URL = '/api';
|
||||
|
||||
/**
|
||||
* Returns the WebSocket URL for a specific task, with fallback logic.
|
||||
* @param {string} taskId
|
||||
* @returns {string}
|
||||
*/
|
||||
export const getWsUrl = (taskId) => {
|
||||
const baseUrl = PUBLIC_WS_URL || `ws://${window.location.hostname}:8000`;
|
||||
return `${baseUrl}/ws/logs/${taskId}`;
|
||||
};
|
||||
|
||||
// [DEF:fetchApi:Function]
|
||||
// @PURPOSE: Generic GET request wrapper.
|
||||
// @PARAM: endpoint (string) - API endpoint.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script>
|
||||
import '../app.css';
|
||||
import Navbar from '../components/Navbar.svelte';
|
||||
import Footer from '../components/Footer.svelte';
|
||||
import Toast from '../components/Toast.svelte';
|
||||
|
||||
@@ -7,8 +7,7 @@ export default defineConfig({
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://localhost:8000',
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/api/, '')
|
||||
changeOrigin: true
|
||||
},
|
||||
'/ws': {
|
||||
target: 'ws://localhost:8000',
|
||||
|
||||
34
specs/001-fix-ui-ws-validation/checklists/requirements.md
Normal file
34
specs/001-fix-ui-ws-validation/checklists/requirements.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# Specification Quality Checklist: Fix UI Styling, WebSocket Port Mismatch, and URL Validation
|
||||
|
||||
**Purpose**: Validate specification completeness and quality before proceeding to planning
|
||||
**Created**: 2025-12-20
|
||||
**Feature**: [specs/001-fix-ui-ws-validation/spec.md](../spec.md)
|
||||
|
||||
## Content Quality
|
||||
|
||||
- [x] No implementation details (languages, frameworks, APIs)
|
||||
- [x] Focused on user value and business needs
|
||||
- [x] Written for non-technical stakeholders
|
||||
- [x] All mandatory sections completed
|
||||
|
||||
## Requirement Completeness
|
||||
|
||||
- [x] No [NEEDS CLARIFICATION] markers remain
|
||||
- [x] Requirements are testable and unambiguous
|
||||
- [x] Success criteria are measurable
|
||||
- [x] Success criteria are technology-agnostic (no implementation details)
|
||||
- [x] All acceptance scenarios are defined
|
||||
- [x] Edge cases are identified
|
||||
- [x] Scope is clearly bounded
|
||||
- [x] Dependencies and assumptions identified
|
||||
|
||||
## Feature Readiness
|
||||
|
||||
- [x] All functional requirements have clear acceptance criteria
|
||||
- [x] User scenarios cover primary flows
|
||||
- [x] Feature meets measurable outcomes defined in Success Criteria
|
||||
- [x] No implementation details leak into specification
|
||||
|
||||
## Notes
|
||||
|
||||
- Initial validation passed. The specification has been refined to be technology-agnostic while addressing the specific issues reported (styling, real-time communication, and URL validation).
|
||||
31
specs/001-fix-ui-ws-validation/data-model.md
Normal file
31
specs/001-fix-ui-ws-validation/data-model.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# Data Model: Fix UI Styling, WebSocket Port Mismatch, and URL Validation
|
||||
|
||||
## Entities
|
||||
|
||||
### ServiceConnection
|
||||
Represents the configuration for an external service.
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
|-------|------|-------------|------------|
|
||||
| `base_url` | `AnyHttpUrl` | The base URL of the service. | Normalized to include `/api/v1` if missing. |
|
||||
| `name` | `string` | Friendly name for the connection. | Required. |
|
||||
| `status` | `string` | Connection status (connected, failed, etc.). | Read-only. |
|
||||
|
||||
### TaskLogMessage
|
||||
The structure of messages sent over the WebSocket for real-time logs.
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `task_id` | `string` | Unique identifier for the task. |
|
||||
| `message` | `string` | The log message text. |
|
||||
| `timestamp` | `datetime` | When the log was generated. |
|
||||
| `level` | `string` | Log level (INFO, ERROR, etc.). |
|
||||
|
||||
## State Transitions
|
||||
|
||||
### Connection Validation
|
||||
1. User inputs `base_url`.
|
||||
2. System validates URL format.
|
||||
3. System checks for `/api/v1` suffix.
|
||||
4. If missing, system appends `/api/v1`.
|
||||
5. System attempts connection and updates `status`.
|
||||
74
specs/001-fix-ui-ws-validation/plan.md
Normal file
74
specs/001-fix-ui-ws-validation/plan.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# Implementation Plan: Fix UI Styling, WebSocket Port Mismatch, and URL Validation
|
||||
|
||||
**Branch**: `001-fix-ui-ws-validation` | **Date**: 2025-12-20 | **Spec**: [specs/001-fix-ui-ws-validation/spec.md](specs/001-fix-ui-ws-validation/spec.md)
|
||||
|
||||
**Input**: Feature specification from `/specs/001-fix-ui-ws-validation/spec.md`
|
||||
|
||||
## Summary
|
||||
|
||||
This feature addresses three critical issues: unstyled UI due to missing Tailwind CSS imports, broken real-time logs caused by WebSocket port mismatches in development, and strict URL validation that prevents successful connections to external services. The technical approach involves importing Tailwind in the root layout, using environment variables for WebSocket URLs with a fallback, and relaxing URL validation to automatically append version suffixes.
|
||||
|
||||
## Technical Context
|
||||
|
||||
**Language/Version**: Python 3.9+, Node.js 18+
|
||||
**Primary Dependencies**: FastAPI, SvelteKit, Tailwind CSS, Pydantic
|
||||
**Storage**: N/A (Configuration based)
|
||||
**Testing**: pytest
|
||||
**Target Platform**: Linux server
|
||||
**Project Type**: Web application (frontend + backend)
|
||||
**Performance Goals**: Real-time updates within 500ms
|
||||
**Constraints**: SPA-First Architecture (No Node.js in production)
|
||||
**Scale/Scope**: Targeted fixes for UI, real-time communication, and validation logic.
|
||||
|
||||
## Constitution Check
|
||||
|
||||
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
|
||||
|
||||
| Principle | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| I. SPA-First Architecture | PASS | SvelteKit SPA will be built and served by FastAPI. Post-design: Confirmed. |
|
||||
| II. API-Driven Communication | PASS | Real-time logs via WebSockets; configuration via REST. Post-design: Confirmed. |
|
||||
| III. Modern Stack Consistency | PASS | Uses SvelteKit, FastAPI, and Tailwind CSS. Post-design: Confirmed. |
|
||||
| IV. Semantic Protocol Adherence | PASS | Implementation will use anchors and contracts as per `semantic_protocol.md`. Post-design: Confirmed. |
|
||||
|
||||
## Project Structure
|
||||
|
||||
### Documentation (this feature)
|
||||
|
||||
```text
|
||||
specs/001-fix-ui-ws-validation/
|
||||
├── plan.md # This file
|
||||
├── research.md # Phase 0 output
|
||||
├── data-model.md # Phase 1 output
|
||||
├── quickstart.md # Phase 1 output
|
||||
├── contracts/ # Phase 1 output
|
||||
└── tasks.md # Phase 2 output
|
||||
```
|
||||
|
||||
### Source Code (repository root)
|
||||
|
||||
```text
|
||||
backend/
|
||||
├── src/
|
||||
│ ├── models/ # URL validation logic in superset_tool/models.py (or equivalent)
|
||||
│ ├── services/
|
||||
│ └── api/ # WebSocket and REST endpoints
|
||||
└── tests/
|
||||
|
||||
frontend/
|
||||
├── src/
|
||||
│ ├── components/
|
||||
│ ├── pages/
|
||||
│ └── routes/ # +layout.svelte for global styling
|
||||
└── tests/
|
||||
```
|
||||
|
||||
**Structure Decision**: Web application structure (Option 2) is used as both frontend and backend components are modified.
|
||||
|
||||
## Complexity Tracking
|
||||
|
||||
> **Fill ONLY if Constitution Check has violations that must be justified**
|
||||
|
||||
| Violation | Why Needed | Simpler Alternative Rejected Because |
|
||||
|-----------|------------|-------------------------------------|
|
||||
| None | N/A | N/A |
|
||||
53
specs/001-fix-ui-ws-validation/quickstart.md
Normal file
53
specs/001-fix-ui-ws-validation/quickstart.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# Quickstart: Fix UI Styling, WebSocket Port Mismatch, and URL Validation
|
||||
|
||||
## Development Setup
|
||||
|
||||
1. **Frontend Styling**:
|
||||
- Ensure Tailwind CSS is initialized: `cd frontend && npm install`
|
||||
- Verify `frontend/src/app.css` contains:
|
||||
```css
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
```
|
||||
- Import in `frontend/src/routes/+layout.svelte`:
|
||||
```svelte
|
||||
<script>
|
||||
import '../app.css';
|
||||
</script>
|
||||
```
|
||||
|
||||
2. **WebSocket Configuration**:
|
||||
- Create/Update `.env` in `frontend/`:
|
||||
```env
|
||||
PUBLIC_WS_URL=ws://localhost:8000
|
||||
```
|
||||
- Use in Svelte components:
|
||||
```javascript
|
||||
import { PUBLIC_WS_URL } from '$env/static/public';
|
||||
const wsUrl = PUBLIC_WS_URL || `ws://${window.location.hostname}:8000`;
|
||||
```
|
||||
|
||||
3. **Backend URL Validation**:
|
||||
- Update `superset_tool/models.py` (or relevant model file):
|
||||
```python
|
||||
from pydantic import validator
|
||||
|
||||
class ServiceConnection(BaseModel):
|
||||
base_url: str
|
||||
|
||||
@validator('base_url')
|
||||
def normalize_url(cls, v):
|
||||
if not v.endswith('/api/v1'):
|
||||
return f"{v.rstrip('/')}/api/v1"
|
||||
return v
|
||||
```
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Run backend: `cd backend && uvicorn src.app:app --reload`
|
||||
2. Run frontend: `cd frontend && npm run dev`
|
||||
3. Open browser and verify:
|
||||
- UI is styled (Tailwind classes working).
|
||||
- Logs appear in real-time (WebSocket connected).
|
||||
- External service connection accepts base URLs.
|
||||
41
specs/001-fix-ui-ws-validation/research.md
Normal file
41
specs/001-fix-ui-ws-validation/research.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# Research: Fix UI Styling, WebSocket Port Mismatch, and URL Validation
|
||||
|
||||
## WebSocket Port Mismatch Resolution
|
||||
|
||||
### Decision
|
||||
Use SvelteKit's `$env/static/public` for `PUBLIC_WS_URL` with a client-side fallback logic.
|
||||
|
||||
### Rationale
|
||||
SvelteKit allows exposing environment variables to the frontend. By using a public environment variable, we can explicitly set the WebSocket URL in different environments (dev vs. prod).
|
||||
|
||||
### Alternatives Considered
|
||||
- **Hardcoding**: Rejected as it breaks across different environments.
|
||||
- **Relative URLs**: WebSockets (`ws://` or `wss://`) cannot be purely relative in all browser contexts without logic to determine the host and port.
|
||||
|
||||
---
|
||||
|
||||
## URL Validation Relaxation
|
||||
|
||||
### Decision
|
||||
Modify the Pydantic model to use a `validator` (or `field_validator` in Pydantic v2) that checks for the `/api/v1` suffix and appends it if missing, while still ensuring the base URL is valid.
|
||||
|
||||
### Rationale
|
||||
This provides a seamless user experience where they can provide just the base URL, and the system handles the API versioning internally.
|
||||
|
||||
### Alternatives Considered
|
||||
- **Strict Validation with Error Message**: Rejected as it causes user frustration (as noted in the spec).
|
||||
- **Manual Suffixing in Service Clients**: Rejected as it's better to have a normalized URL in the data model.
|
||||
|
||||
---
|
||||
|
||||
## Global Styling (Tailwind CSS)
|
||||
|
||||
### Decision
|
||||
Import the global CSS file (which includes `@tailwind` directives) in `src/routes/+layout.svelte`.
|
||||
|
||||
### Rationale
|
||||
This is the standard SvelteKit pattern for ensuring styles are applied globally across all routes.
|
||||
|
||||
### Alternatives Considered
|
||||
- **Importing in each page**: Rejected as it's redundant and hard to maintain.
|
||||
- **Importing in `app.html`**: Possible, but importing in `+layout.svelte` allows for better integration with Svelte's build pipeline.
|
||||
94
specs/001-fix-ui-ws-validation/spec.md
Normal file
94
specs/001-fix-ui-ws-validation/spec.md
Normal file
@@ -0,0 +1,94 @@
|
||||
# Feature Specification: Fix UI Styling, WebSocket Port Mismatch, and URL Validation
|
||||
|
||||
**Feature Branch**: `001-fix-ui-ws-validation`
|
||||
**Created**: 2025-12-20
|
||||
**Status**: Draft
|
||||
**Input**: User description: "UI Styling: Tailwind CSS is not imported in the root layout, causing the unstyled appearance. WebSocket Mismatch: Port mismatch in dev mode is breaking real-time logs. Validation Error: Strict URL validation in superset_tool/models.py requires /api/v1, which caused the connection failure reported in your feedback."
|
||||
|
||||
## User Scenarios & Testing *(mandatory)*
|
||||
|
||||
### User Story 1 - Consistent UI Styling (Priority: P1)
|
||||
|
||||
As a user, I want the application to have a professional and styled appearance so that I can easily navigate and use the interface.
|
||||
|
||||
**Why this priority**: Unstyled UI makes the application look broken and difficult to use, impacting user trust and usability.
|
||||
|
||||
**Independent Test**: Can be fully tested by opening the application in a browser and verifying that consistent styling is applied globally across all routes.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** the application is running, **When** I navigate to the home page or settings page, **Then** I should see professional styling applied (e.g., correct fonts, colors, and layout).
|
||||
2. **Given** a new component is added, **When** it uses standard styling classes, **Then** those classes should be rendered correctly without additional imports.
|
||||
|
||||
---
|
||||
|
||||
### User Story 2 - Real-time Log Monitoring (Priority: P1)
|
||||
|
||||
As a developer or operator, I want to see real-time logs for running tasks so that I can monitor progress and debug issues effectively.
|
||||
|
||||
**Why this priority**: Real-time feedback is essential for long-running tasks like migrations or backups; without it, users are left wondering if the process is stuck.
|
||||
|
||||
**Independent Test**: Can be tested by starting a task and verifying that logs appear in the UI in real-time without requiring a page refresh.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** a task is running, **When** I view the task details page, **Then** I should see live log updates streamed via real-time communication.
|
||||
2. **Given** the application is running in development mode, **When** a real-time connection is initiated, **Then** it should correctly target the backend service port.
|
||||
|
||||
---
|
||||
|
||||
### User Story 3 - Flexible External Service Connection (Priority: P2)
|
||||
|
||||
As an administrator, I want to connect to external services using their base URL so that I don't have to worry about specific API version paths during configuration.
|
||||
|
||||
**Why this priority**: Strict validation currently prevents successful connection to valid service instances if the user doesn't provide a very specific suffix, leading to configuration frustration.
|
||||
|
||||
**Independent Test**: Can be tested by configuring a service connection with a standard base URL and verifying it connects successfully.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** a valid service base URL, **When** I save the connection settings, **Then** the system should validate and accept the URL even if it doesn't explicitly end in a specific API version suffix.
|
||||
2. **Given** a service URL that already includes an API version suffix, **When** I save the settings, **Then** the system should not duplicate the suffix or fail validation.
|
||||
|
||||
---
|
||||
|
||||
### Edge Cases
|
||||
|
||||
- **Connection Disconnection**: How does the system handle a real-time connection drop during a long-running task? (Assumption: It should attempt to reconnect or show a "Connection Lost" message).
|
||||
- **Invalid URL Formats**: How does the system handle URLs that are completely malformed? (Assumption: Standard URL validation should still apply).
|
||||
- **Styling Build Failures**: What happens if the styling assets fail to generate? (Assumption: The app should still be functional but may look unstyled; build logs should indicate the failure).
|
||||
|
||||
## Requirements *(mandatory)*
|
||||
|
||||
### Functional Requirements
|
||||
|
||||
- **FR-001**: System MUST ensure global styling (Tailwind CSS) is imported in `src/routes/+layout.svelte` to ensure consistent appearance.
|
||||
- **FR-002**: System MUST use an environment variable (e.g., `PUBLIC_WS_URL`) with a fallback to the backend port (8000) to determine the WebSocket connection URL.
|
||||
- **FR-003**: System MUST relax URL validation for external services to allow base URLs and automatically append `/api/v1` if the version suffix is missing.
|
||||
- **FR-004**: System MUST provide visual feedback (toast notification and status indicator in log view) when a real-time connection fails to establish.
|
||||
- **FR-005**: System MUST ensure that service clients correctly handle API versioning internally by using the normalized URL.
|
||||
|
||||
### Key Entities *(include if feature involves data)*
|
||||
|
||||
- **Service Connection**: Represents the configuration for connecting to an external service.
|
||||
- Attributes: Base URL, Credentials (if applicable), Connection Status.
|
||||
- **Task Log Stream**: Represents the real-time data flow of logs from the backend to the frontend.
|
||||
- Attributes: Task ID, Log Message, Timestamp.
|
||||
|
||||
## Success Criteria *(mandatory)*
|
||||
|
||||
### Measurable Outcomes
|
||||
|
||||
- **SC-001**: 100% of pages render with consistent, professional styling as verified by visual inspection.
|
||||
- **SC-002**: Real-time communication success rate is 100% in the development environment when both frontend and backend are running.
|
||||
- **SC-003**: Users can successfully configure and save external service connections using only the base domain/IP in 100% of valid cases.
|
||||
- **SC-004**: Real-time updates appear in the UI within 500ms of being generated on the backend.
|
||||
|
||||
## Clarifications
|
||||
|
||||
### Session 2025-12-20
|
||||
- Q: WebSocket Reconnection Strategy → A: Automatic reconnection with exponential backoff (Option A).
|
||||
- Q: URL Validation Strictness → A: Automatically append `/api/v1` if missing (Option A).
|
||||
- Q: Global Styling Implementation → A: Import in `src/routes/+layout.svelte` (Option A).
|
||||
- Q: WebSocket Port Configuration → A: Use environment variable with fallback (Option A).
|
||||
- Q: Visual Feedback for Connection Failure → A: Toast notification + Status indicator (Option A).
|
||||
139
specs/001-fix-ui-ws-validation/tasks.md
Normal file
139
specs/001-fix-ui-ws-validation/tasks.md
Normal file
@@ -0,0 +1,139 @@
|
||||
# Tasks: Fix UI Styling, WebSocket Port Mismatch, and URL Validation
|
||||
|
||||
**Input**: Design documents from `/specs/001-fix-ui-ws-validation/`
|
||||
**Prerequisites**: plan.md (required), spec.md (required for user stories), research.md, data-model.md, contracts/
|
||||
|
||||
**Organization**: Tasks are grouped by user story to enable independent implementation and testing of each story.
|
||||
|
||||
## Format: `[ID] [P?] [Story] Description`
|
||||
|
||||
- **[P]**: Can run in parallel (different files, no dependencies)
|
||||
- **[Story]**: Which user story this task belongs to (e.g., US1, US2, US3)
|
||||
- Include exact file paths in descriptions
|
||||
|
||||
## Phase 1: Setup (Shared Infrastructure)
|
||||
|
||||
**Purpose**: Project initialization and basic structure
|
||||
|
||||
- [x] T001 Verify project structure and install dependencies in `backend/` and `frontend/`
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Foundational (Blocking Prerequisites)
|
||||
|
||||
**Purpose**: Core infrastructure that MUST be complete before ANY user story can be implemented
|
||||
|
||||
**⚠️ CRITICAL**: No user story work can begin until this phase is complete
|
||||
|
||||
- [x] T002 [P] Configure `PUBLIC_WS_URL` in `frontend/.env`
|
||||
|
||||
**Checkpoint**: Foundation ready - user story implementation can now begin in parallel
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: User Story 1 - Consistent UI Styling (Priority: P1) 🎯 MVP
|
||||
|
||||
**Goal**: Apply Tailwind CSS globally via the root layout to ensure consistent appearance.
|
||||
|
||||
**Independent Test**: Open the application in a browser and verify that Tailwind styling is applied to all elements (e.g., Navbar, Footer, Buttons).
|
||||
|
||||
### Implementation for User Story 1
|
||||
|
||||
- [x] T003 [P] [US1] Verify Tailwind directives in `frontend/src/app.css`
|
||||
- [x] T004 [US1] Import `../app.css` in `frontend/src/routes/+layout.svelte`
|
||||
|
||||
**Checkpoint**: At this point, User Story 1 should be fully functional and testable independently
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: User Story 2 - Real-time Log Monitoring (Priority: P1)
|
||||
|
||||
**Goal**: Resolve WebSocket port mismatch using environment variables and fallback logic for real-time logs.
|
||||
|
||||
**Independent Test**: Start a task (e.g., a mock migration) and verify that logs appear in the `TaskRunner` component in real-time.
|
||||
|
||||
### Implementation for User Story 2
|
||||
|
||||
- [x] T005 [P] [US2] Implement WebSocket URL fallback logic in `frontend/src/lib/api.js`
|
||||
- [x] T006 [US2] Update `frontend/src/components/TaskRunner.svelte` to use the dynamic WebSocket URL
|
||||
|
||||
**Checkpoint**: At this point, User Stories 1 AND 2 should both work independently
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: User Story 3 - Flexible External Service Connection (Priority: P2)
|
||||
|
||||
**Goal**: Automatically append `/api/v1` to service base URLs if missing to simplify configuration.
|
||||
|
||||
**Independent Test**: Create a new service connection with `http://localhost:8080` and verify it is saved as `http://localhost:8080/api/v1`.
|
||||
|
||||
### Implementation for User Story 3
|
||||
|
||||
- [x] T007 [P] [US3] Relax `base_url` validation and add normalization in `superset_tool/models.py`
|
||||
- [x] T008 [US3] Add unit tests for `SupersetConfig` URL normalization in `backend/tests/test_models.py`
|
||||
|
||||
**Checkpoint**: All user stories should now be independently functional
|
||||
|
||||
---
|
||||
|
||||
## Phase 6: Polish & Cross-Cutting Concerns
|
||||
|
||||
**Purpose**: Improvements that affect multiple user stories
|
||||
|
||||
- [x] T009 [P] Update `docs/settings.md` with new URL validation behavior
|
||||
- [ ] T010 Run full verification suite per `quickstart.md`
|
||||
|
||||
---
|
||||
|
||||
## Dependencies & Execution Order
|
||||
|
||||
### Phase Dependencies
|
||||
|
||||
- **Setup (Phase 1)**: No dependencies - can start immediately
|
||||
- **Foundational (Phase 2)**: Depends on Setup completion - BLOCKS all user stories
|
||||
- **User Stories (Phase 3+)**: All depend on Foundational phase completion
|
||||
- User stories can then proceed in parallel (if staffed)
|
||||
- Or sequentially in priority order (P1 → P2 → P3)
|
||||
- **Polish (Final Phase)**: Depends on all desired user stories being complete
|
||||
|
||||
### User Story Dependencies
|
||||
|
||||
- **User Story 1 (P1)**: Can start after Foundational (Phase 2) - No dependencies on other stories
|
||||
- **User Story 2 (P2)**: Can start after Foundational (Phase 2) - May integrate with US1 but should be independently testable
|
||||
- **User Story 3 (P3)**: Can start after Foundational (Phase 2) - May integrate with US1/US2 but should be independently testable
|
||||
|
||||
### Parallel Opportunities
|
||||
|
||||
- All Setup tasks marked [P] can run in parallel
|
||||
- All Foundational tasks marked [P] can run in parallel (within Phase 2)
|
||||
- Once Foundational phase completes, all user stories can start in parallel
|
||||
- Models within a story marked [P] can run in parallel
|
||||
|
||||
---
|
||||
|
||||
## Parallel Example: User Story 1
|
||||
|
||||
```bash
|
||||
# Launch all models for User Story 1 together:
|
||||
Task: "Verify Tailwind directives in frontend/src/app.css"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Strategy
|
||||
|
||||
### MVP First (User Story 1 Only)
|
||||
|
||||
1. Complete Phase 1: Setup
|
||||
2. Complete Phase 2: Foundational (CRITICAL - blocks all stories)
|
||||
3. Complete Phase 3: User Story 1
|
||||
4. **STOP and VALIDATE**: Test User Story 1 independently
|
||||
5. Deploy/demo if ready
|
||||
|
||||
### Incremental Delivery
|
||||
|
||||
1. Complete Setup + Foundational → Foundation ready
|
||||
2. Add User Story 1 → Test independently → Deploy/Demo (MVP!)
|
||||
3. Add User Story 2 → Test independently → Deploy/Demo
|
||||
4. Add User Story 3 → Test independently → Deploy/Demo
|
||||
5. Each story adds value without breaking previous stories
|
||||
@@ -66,6 +66,8 @@ As a user, I want a consistent look and feel across all pages with a shared navi
|
||||
- **FR-005**: System MUST handle client-side navigation between routes without full page refreshes.
|
||||
- **FR-006**: System MUST integrate with the existing backend API for data retrieval.
|
||||
- **FR-007**: System MUST support data submission via existing API endpoints using standard asynchronous requests.
|
||||
- **FR-008**: System MUST support WebSocket proxying for real-time task logs (required by `TaskRunner.svelte`).
|
||||
- **FR-009**: System MUST support data submission for Settings updates and Plugin actions (e.g., triggering backups).
|
||||
|
||||
### Key Entities *(include if feature involves data)*
|
||||
|
||||
|
||||
@@ -23,9 +23,9 @@
|
||||
|
||||
**Purpose**: Project initialization and basic structure
|
||||
|
||||
- [ ] T001 Initialize SvelteKit in `frontend/` directory (replacing current setup)
|
||||
- [ ] T002 Install `@sveltejs/adapter-static` in `frontend/package.json`
|
||||
- [ ] T003 [P] Configure `frontend/svelte.config.js` for static adapter and SPA fallback
|
||||
- [x] T001 Initialize SvelteKit in `frontend/` directory (replacing current setup)
|
||||
- [x] T002 Install `@sveltejs/adapter-static` in `frontend/package.json`
|
||||
- [x] T003 [P] Configure `frontend/svelte.config.js` for static adapter and SPA fallback
|
||||
|
||||
---
|
||||
|
||||
@@ -35,10 +35,11 @@
|
||||
|
||||
**⚠️ CRITICAL**: No user story work can begin until this phase is complete
|
||||
|
||||
- [ ] T004 Create `frontend/src/routes/+layout.ts` to disable SSR and prerendering (`ssr = false`, `prerender = false`)
|
||||
- [ ] T005 Implement catch-all route in `backend/src/app.py` to serve `index.html` for SPA routing
|
||||
- [ ] T006 [P] Update `backend/src/app.py` to mount `frontend/build` directory using `StaticFiles`
|
||||
- [ ] T007 [P] Update `frontend/src/lib/api.js` to ensure compatibility with SvelteKit environment
|
||||
- [x] T004 Create `frontend/src/routes/+layout.ts` to disable SSR and prerendering (`ssr = false`, `prerender = false`)
|
||||
- [x] T005 Implement catch-all route in `backend/src/app.py` to serve `index.html` for SPA routing
|
||||
- [x] T006 [P] Update `backend/src/app.py` to mount `frontend/build` directory using `StaticFiles`
|
||||
- [x] T007 [P] Update `frontend/src/lib/api.js` to ensure compatibility with SvelteKit environment
|
||||
- [x] T022 [FR-008] Configure WebSocket proxying in `backend/src/app.py` and `frontend/vite.config.js`
|
||||
|
||||
**Checkpoint**: Foundation ready - user story implementation can now begin in parallel
|
||||
|
||||
@@ -52,9 +53,11 @@
|
||||
|
||||
### Implementation for User Story 1
|
||||
|
||||
- [ ] T008 [P] [US1] Create Dashboard route in `frontend/src/routes/+page.svelte` (migrating from `App.svelte`/`Dashboard.svelte`)
|
||||
- [ ] T009 [P] [US1] Create Settings route in `frontend/src/routes/settings/+page.svelte` (migrating from `Settings.svelte`)
|
||||
- [ ] T010 [US1] Implement navigation links between Dashboard and Settings in `frontend/src/routes/+page.svelte` and `frontend/src/routes/settings/+page.svelte`
|
||||
- [x] T008 [P] [US1] Create Dashboard route in `frontend/src/routes/+page.svelte` (migrating from `App.svelte`/`Dashboard.svelte`)
|
||||
- [x] T009 [P] [US1] Create Settings route in `frontend/src/routes/settings/+page.svelte` (migrating from `Settings.svelte`)
|
||||
- [x] T010 [US1] Implement navigation links between Dashboard and Settings in `frontend/src/routes/+page.svelte` and `frontend/src/routes/settings/+page.svelte`
|
||||
- [x] T023 [US1] Implement "Save Settings" form submission in `frontend/src/routes/settings/+page.svelte`
|
||||
- [x] T024 [US1] Implement plugin action triggers (e.g., "Run Backup") in `frontend/src/routes/+page.svelte`
|
||||
|
||||
**Checkpoint**: At this point, User Story 1 should be fully functional and testable independently.
|
||||
|
||||
@@ -68,10 +71,10 @@
|
||||
|
||||
### Implementation for User Story 2
|
||||
|
||||
- [ ] T011 [P] [US2] Implement `load` function for Dashboard in `frontend/src/routes/+page.ts` to fetch plugins from `/api/plugins/`
|
||||
- [ ] T012 [P] [US2] Implement `load` function for Settings in `frontend/src/routes/settings/+page.ts` to fetch config and environments from `/api/settings/`
|
||||
- [ ] T013 [US2] Update `frontend/src/routes/+page.svelte` to use data from `load` function via `export let data;`
|
||||
- [ ] T014 [US2] Update `frontend/src/routes/settings/+page.svelte` to use data from `load` function via `export let data;`
|
||||
- [x] T011 [P] [US2] Implement `load` function for Dashboard in `frontend/src/routes/+page.ts` to fetch plugins from `/api/plugins/`
|
||||
- [x] T012 [P] [US2] Implement `load` function for Settings in `frontend/src/routes/settings/+page.ts` to fetch config and environments from `/api/settings/`
|
||||
- [x] T013 [US2] Update `frontend/src/routes/+page.svelte` to use data from `load` function via `export let data;`
|
||||
- [x] T014 [US2] Update `frontend/src/routes/settings/+page.svelte` to use data from `load` function via `export let data;`
|
||||
|
||||
**Checkpoint**: At this point, User Stories 1 AND 2 should both work independently.
|
||||
|
||||
@@ -85,9 +88,9 @@
|
||||
|
||||
### Implementation for User Story 3
|
||||
|
||||
- [ ] T015 [US3] Create shared layout in `frontend/src/routes/+layout.svelte` with `<slot />`
|
||||
- [ ] T016 [P] [US3] Move navigation bar component to `frontend/src/components/Navbar.svelte` and include in `+layout.svelte`
|
||||
- [ ] T017 [P] [US3] Create footer component in `frontend/src/components/Footer.svelte` and include in `+layout.svelte`
|
||||
- [x] T015 [US3] Create shared layout in `frontend/src/routes/+layout.svelte` with `<slot />`
|
||||
- [x] T016 [P] [US3] Move navigation bar component to `frontend/src/components/Navbar.svelte` and include in `+layout.svelte`
|
||||
- [x] T017 [P] [US3] Create footer component in `frontend/src/components/Footer.svelte` and include in `+layout.svelte`
|
||||
|
||||
**Checkpoint**: All user stories should now be independently functional.
|
||||
|
||||
@@ -97,10 +100,12 @@
|
||||
|
||||
**Purpose**: Improvements that affect multiple user stories
|
||||
|
||||
- [ ] T018 [P] Implement custom 404 error page in `frontend/src/routes/+error.svelte`
|
||||
- [ ] T019 Add graceful error handling for API failures in `load` functions (T011, T012)
|
||||
- [ ] T020 [P] Update `frontend/README.md` with new SvelteKit-based development and build instructions
|
||||
- [ ] T021 Run `specs/004-integrate-svelte-kit/quickstart.md` validation
|
||||
- [x] T018 [P] Implement custom 404 error page in `frontend/src/routes/+error.svelte`
|
||||
- [x] T019 Add graceful error handling for API failures in `load` functions (T011, T012)
|
||||
- [x] T020 [P] Update `frontend/README.md` with new SvelteKit-based development and build instructions
|
||||
- [x] T021 Run `specs/004-integrate-svelte-kit/quickstart.md` validation
|
||||
- [x] T025 [FR-008] Update `TaskRunner.svelte` to use SvelteKit-compatible WebSocket connection logic
|
||||
- [x] T026 [SC-001] Perform performance benchmarking to verify < 200ms transition time
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -39,19 +39,22 @@ class SupersetConfig(BaseModel):
|
||||
return v
|
||||
# [/DEF:SupersetConfig.validate_auth]
|
||||
|
||||
# [DEF:SupersetConfig.check_base_url_format:Function]
|
||||
# @PURPOSE: Проверяет, что `base_url` соответствует формату URL и содержит `/api/v1`.
|
||||
# [DEF:SupersetConfig.normalize_base_url:Function]
|
||||
# @PURPOSE: Нормализует `base_url`, добавляя `/api/v1`, если он отсутствует.
|
||||
# @PRE: `v` должна быть строкой.
|
||||
# @POST: Возвращает очищенный `v`, если формат корректен.
|
||||
# @POST: Возвращает нормализованный `v`.
|
||||
# @THROW: ValueError - Если формат URL невалиден.
|
||||
# @PARAM: v (str) - Значение поля base_url.
|
||||
@validator('base_url')
|
||||
def check_base_url_format(cls, v: str) -> str:
|
||||
def normalize_base_url(cls, v: str) -> str:
|
||||
v = v.strip()
|
||||
if not re.fullmatch(r'https?://.+/api/v1/?(?:.*)?', v):
|
||||
raise ValueError(f"Invalid URL format: {v}. Must include '/api/v1'.")
|
||||
if not v.startswith(('http://', 'https://')):
|
||||
raise ValueError(f"Invalid URL scheme: {v}. Must start with http:// or https://")
|
||||
|
||||
if '/api/v1' not in v:
|
||||
v = f"{v.rstrip('/')}/api/v1"
|
||||
return v
|
||||
# [/DEF:SupersetConfig.check_base_url_format]
|
||||
# [/DEF:SupersetConfig.normalize_base_url]
|
||||
|
||||
class Config:
|
||||
arbitrary_types_allowed = True
|
||||
|
||||
Reference in New Issue
Block a user