Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 4 additions & 176 deletions docs/reference/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -1859,28 +1859,6 @@
}
}
},
"/api/v1/events/health": {
"get": {
"tags": [
"sse"
],
"summary": "Sse Health",
"description": "Get SSE service health status.",
"operationId": "sse_health_api_v1_events_health_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SSEHealthResponse"
}
}
}
}
}
}
},
"/api/v1/events/executions/{execution_id}/events": {
"get": {
"tags": [
Expand Down Expand Up @@ -9068,14 +9046,7 @@
"title": "Exit Code"
},
"error_type": {
"anyOf": [
{
"$ref": "#/components/schemas/ExecutionErrorType"
},
{
"type": "null"
}
]
"$ref": "#/components/schemas/ExecutionErrorType"
},
"error_message": {
"type": "string",
Expand Down Expand Up @@ -9896,7 +9867,6 @@
"saga_commands",
"dead_letter_queue",
"dlq_events",
"event_bus_stream",
"websocket_events"
],
"title": "KafkaTopic",
Expand Down Expand Up @@ -13104,10 +13074,7 @@
"enum": [
"connected",
"subscribed",
"heartbeat",
"shutdown",
"status",
"error"
"status"
],
"title": "SSEControlEvent",
"description": "Control events for execution SSE streams (not from Kafka)."
Expand Down Expand Up @@ -13178,31 +13145,7 @@
}
],
"title": "Message",
"description": "Human-readable message (heartbeat, shutdown)"
},
"grace_period": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Grace Period",
"description": "Shutdown grace period in seconds"
},
"error": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Error",
"description": "Error message (error event)"
"description": "Human-readable message (subscribed event)"
},
"status": {
"anyOf": [
Expand Down Expand Up @@ -13294,71 +13237,6 @@
"title": "SSEExecutionEventData",
"description": "Typed model for SSE execution stream event payload.\n\nThis represents the JSON data sent inside each SSE message for execution streams.\nAll fields except event_type and execution_id are optional since different\nevent types carry different data."
},
"SSEHealthResponse": {
"properties": {
"status": {
"$ref": "#/components/schemas/SSEHealthStatus",
"description": "Health status: healthy or draining"
},
"kafka_enabled": {
"type": "boolean",
"title": "Kafka Enabled",
"description": "Whether Kafka features are enabled",
"default": true
},
"active_connections": {
"type": "integer",
"title": "Active Connections",
"description": "Total number of active SSE connections"
},
"active_executions": {
"type": "integer",
"title": "Active Executions",
"description": "Number of executions being monitored"
},
"active_consumers": {
"type": "integer",
"title": "Active Consumers",
"description": "Number of active Kafka consumers"
},
"max_connections_per_user": {
"type": "integer",
"title": "Max Connections Per User",
"description": "Maximum connections allowed per user"
},
"shutdown": {
"$ref": "#/components/schemas/ShutdownStatusResponse",
"description": "Shutdown status information"
},
"timestamp": {
"type": "string",
"format": "date-time",
"title": "Timestamp",
"description": "Health check timestamp"
}
},
"type": "object",
"required": [
"status",
"active_connections",
"active_executions",
"active_consumers",
"max_connections_per_user",
"shutdown",
"timestamp"
],
"title": "SSEHealthResponse",
"description": "Response model for SSE health check."
},
"SSEHealthStatus": {
"type": "string",
"enum": [
"healthy",
"draining"
],
"title": "SSEHealthStatus",
"description": "Health status for SSE service."
},
"SagaCancellationResponse": {
"properties": {
"success": {
Expand Down Expand Up @@ -14782,57 +14660,6 @@
"title": "SettingsHistoryResponse",
"description": "Response model for settings history (limited snapshot of recent changes)"
},
"ShutdownStatusResponse": {
"properties": {
"phase": {
"type": "string",
"title": "Phase",
"description": "Current shutdown phase"
},
"initiated": {
"type": "boolean",
"title": "Initiated",
"description": "Whether shutdown has been initiated"
},
"complete": {
"type": "boolean",
"title": "Complete",
"description": "Whether shutdown is complete"
},
"active_connections": {
"type": "integer",
"title": "Active Connections",
"description": "Number of active connections"
},
"draining_connections": {
"type": "integer",
"title": "Draining Connections",
"description": "Number of connections being drained"
},
"duration": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"title": "Duration",
"description": "Duration of shutdown in seconds"
}
},
"type": "object",
"required": [
"phase",
"initiated",
"complete",
"active_connections",
"draining_connections"
],
"title": "ShutdownStatusResponse",
"description": "Response model for shutdown status."
},
"SortOrder": {
"type": "string",
"enum": [
Expand Down Expand Up @@ -15984,6 +15811,7 @@
"title": "Reason"
}
},
"additionalProperties": true,
"type": "object",
"required": [
"event_id",
Expand Down
28 changes: 7 additions & 21 deletions frontend/src/App.svelte
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
<script lang="ts">
import { onMount, onDestroy } from 'svelte';
import { onMount } from 'svelte';
import { Router, goto } from "@mateothegreat/svelte5-router";
import { Toaster } from 'svelte-sonner';
import Header from "$components/Header.svelte";
import Footer from "$components/Footer.svelte";
import Spinner from "$components/Spinner.svelte";
import ErrorDisplay from "$components/ErrorDisplay.svelte";
import { theme } from '$stores/theme';
import { themeStore } from '$stores/theme.svelte';
Comment thread
HardMax71 marked this conversation as resolved.
import { initializeAuth, AuthInitializer } from '$lib/auth-init';
import { appError } from '$stores/errorStore';
import { isAuthenticated } from '$stores/auth';
import { get } from 'svelte/store';
import { appError } from '$stores/errorStore.svelte';
import { authStore } from '$stores/auth.svelte';

// Page components
import Home from "$routes/Home.svelte";
Expand All @@ -25,19 +24,6 @@
import AdminUsers from "$routes/admin/AdminUsers.svelte";
import AdminSettings from "$routes/admin/AdminSettings.svelte";

// Theme value derived from store with proper cleanup
let themeValue = $state('auto');
const unsubscribeTheme = theme.subscribe(value => { themeValue = value; });

// Global error state
let globalError = $state<{ error: Error | string; title?: string } | null>(null);
const unsubscribeError = appError.subscribe(value => { globalError = value; });

onDestroy(() => {
unsubscribeTheme();
unsubscribeError();
});

let authInitialized = $state(false);

// Initialize auth before rendering routes
Expand All @@ -55,7 +41,7 @@
// Auth hook for protected routes
const requireAuth = async () => {
await AuthInitializer.waitForInit();
if (!get(isAuthenticated)) {
if (!authStore.isAuthenticated) {
const currentPath = window.location.pathname + window.location.search;
if (currentPath !== '/login' && currentPath !== '/register') {
sessionStorage.setItem('redirectAfterLogin', currentPath);
Expand Down Expand Up @@ -85,8 +71,8 @@
];
</script>

{#if globalError}
<ErrorDisplay error={globalError.error} title={globalError.title} />
{#if appError.current}
<ErrorDisplay error={appError.current.error} title={appError.current.title} />
{:else}
<div class="flex flex-col min-h-screen bg-bg-default dark:bg-dark-bg-default pt-16">
<Header/>
Expand Down
32 changes: 16 additions & 16 deletions frontend/src/components/Header.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script lang="ts">
import { route, goto } from "@mateothegreat/svelte5-router";
import { isAuthenticated, username, userRole, logout as authLogout, userEmail } from "$stores/auth";
import { theme, toggleTheme } from "$stores/theme";
import { authStore } from "$stores/auth.svelte";
import { themeStore, toggleTheme } from "$stores/theme.svelte";
import { fade } from 'svelte/transition';
import { onMount, onDestroy } from 'svelte';
import NotificationCenter from '$components/NotificationCenter.svelte';
Expand All @@ -24,7 +24,7 @@
}

async function handleLogout() {
await authLogout();
await authStore.logout();
closeMenu();
goto('/login');
}
Expand Down Expand Up @@ -95,17 +95,17 @@

<div class="flex items-center space-x-3">
<button onclick={toggleTheme} title="Toggle theme" class="btn btn-ghost btn-icon text-fg-muted dark:text-dark-fg-muted hover:text-fg-default dark:hover:text-dark-fg-default">
{#if $theme === 'light'}
{#if themeStore.value === 'light'}
<Sun class="w-5 h-5" />
{:else if $theme === 'dark'}
{:else if themeStore.value === 'dark'}
<Moon class="w-5 h-5" />
{:else}
<MonitorCog class="w-5 h-5" />
{/if}
</button>

<div class="hidden lg:flex items-center space-x-3">
{#if $isAuthenticated}
{#if authStore.isAuthenticated}
<NotificationCenter />
<!-- User dropdown for all authenticated users -->
<div class="relative user-dropdown-container">
Expand All @@ -115,7 +115,7 @@
>
<div class="flex items-center space-x-2">
<User class="w-5 h-5" />
<span class="hidden xl:inline font-medium">{$username}</span>
<span class="hidden xl:inline font-medium">{authStore.username}</span>
<span class="transition-transform duration-200" class:rotate-180={showUserDropdown}>
<ChevronDown class="w-4 h-4" />
</span>
Expand All @@ -130,24 +130,24 @@
<div class="flex items-center gap-3">
<div class="w-7 h-7 rounded-full bg-primary/20 dark:bg-primary/30 flex items-center justify-center shrink-0">
<span class="text-xs font-semibold text-primary dark:text-primary-light">
{$username?.charAt(0).toUpperCase() ?? '?'}
{authStore.username?.charAt(0).toUpperCase() ?? '?'}
Comment thread
HardMax71 marked this conversation as resolved.
</span>
</div>
<div class="min-w-0">
<p class="text-sm font-medium text-fg-default dark:text-dark-fg-default truncate">
{$username}
{#if $userRole === 'admin'}
{authStore.username}
{#if authStore.userRole === 'admin'}
<span class="text-xs text-primary dark:text-primary-light font-medium ml-1">(Admin)</span>
{/if}
</p>
<p class="text-xs text-fg-muted dark:text-dark-fg-muted truncate">
{$userEmail || 'No email set'}
{authStore.userEmail || 'No email set'}
</p>
</div>
<div class="mx-1 sm:mx-2 h-6 w-px bg-border-default dark:bg-dark-border-default"></div>
</div>
<div class="mt-3 flex flex-col sm:flex-row gap-2">
{#if $userRole === 'admin'}
{#if authStore.userRole === 'admin'}
<button
onclick={() => {
showUserDropdown = false;
Expand Down Expand Up @@ -206,18 +206,18 @@
<div class="px-2 pt-2 pb-3 space-y-1 sm:px-3">

<div class="pt-3 mt-2 border-t border-border-default dark:border-dark-border-default">
{#if $isAuthenticated}
{#if authStore.isAuthenticated}
<div class="px-3 py-2">
<div class="text-sm font-medium text-fg-default dark:text-dark-fg-default">{$username}</div>
<div class="text-sm font-medium text-fg-default dark:text-dark-fg-default">{authStore.username}</div>
<div class="text-xs text-fg-muted dark:text-dark-fg-muted">
{#if $userRole === 'admin'}
{#if authStore.userRole === 'admin'}
Administrator
{:else}
Logged in
{/if}
</div>
</div>
{#if $userRole === 'admin'}
{#if authStore.userRole === 'admin'}
<a href="/admin/events" use:route onclick={closeMenu} class="block px-3 py-2 rounded-md text-base font-medium text-fg-default dark:text-dark-fg-default hover:bg-neutral-100 dark:hover:bg-neutral-700 flex items-center">
<Settings class="w-5 h-5 mr-2 -ml-1" /> Admin Panel
</a>
Expand Down
Loading
Loading