feat: Implement container logs functionality in InstancesViewModel

- Added properties for managing container logs, including log entries, service filters, and auto-refresh options.
- Introduced commands for refreshing logs, toggling auto-refresh, and closing the logs panel.
- Implemented log fetching logic with error handling and status messages.
- Integrated log display in the InstancesView with a dedicated logs panel.

feat: Enhance navigation to Instances page with auto-selection

- Added method to navigate to the Instances page and auto-select an instance based on abbreviation.

feat: Update SettingsViewModel to load and save Bitwarden configuration

- Integrated Bitwarden configuration loading from IOptions and saving to appsettings.json.
- Added properties for Bitwarden instance project ID and connection status.
- Updated UI to reflect Bitwarden settings and connection status.

feat: Add advanced options for instance creation

- Introduced a new expander in CreateInstanceView for advanced options, including purging stale volumes.

feat: Improve InstanceDetailsWindow with pending setup banner

- Added a banner to indicate pending setup for Xibo OAuth credentials, with editable fields for client ID and secret.

fix: Update appsettings.json to include Bitwarden configuration structure

- Added Bitwarden section to appsettings.json for storing configuration values.

chore: Update Docker Compose template with health checks

- Added health check configuration for web service in template.yml to ensure service availability.

refactor: Drop AppSettings table from database

- Removed AppSettings table and related migration files as part of database cleanup.

feat: Create ServiceLogEntry DTO for log management

- Added ServiceLogEntry class to represent individual log entries from Docker services.
This commit is contained in:
Matt Batchelder
2026-02-25 17:39:17 -05:00
parent a1c987ff21
commit 90eb649940
35 changed files with 1807 additions and 621 deletions

View File

@@ -31,6 +31,22 @@
<ScrollViewer>
<StackPanel Spacing="16">
<!-- ═══ Pending Setup Banner ═══ -->
<Border IsVisible="{Binding IsPendingSetup}"
Background="#1F2A1A" BorderBrush="#4ADE80" BorderThickness="1"
CornerRadius="8" Padding="14,10">
<StackPanel Orientation="Horizontal" Spacing="10">
<TextBlock Text="⚙" FontSize="18" VerticalAlignment="Center" Foreground="#4ADE80" />
<StackPanel>
<TextBlock Text="Pending Setup" FontSize="14" FontWeight="SemiBold"
Foreground="#4ADE80" />
<TextBlock FontSize="12" Foreground="{StaticResource TextSecondaryBrush}"
TextWrapping="Wrap"
Text="Enter your Xibo OAuth credentials below to complete instance initialisation." />
</StackPanel>
</StackPanel>
</Border>
<!-- ═══ OTS Admin Account ═══ -->
<Border Classes="card">
<StackPanel Spacing="8">
@@ -116,27 +132,57 @@
FontSize="12" Foreground="{StaticResource TextMutedBrush}" Margin="0,0,0,6"
TextWrapping="Wrap" />
<TextBlock Text="Client ID" FontSize="12" Foreground="{StaticResource TextSecondaryBrush}" />
<Grid ColumnDefinitions="*,Auto">
<TextBox Grid.Column="0" Text="{Binding OAuthClientId}" IsReadOnly="True"
FontFamily="Consolas,monospace" />
<Button Grid.Column="1" Content="Copy" Margin="4,0,0,0"
Command="{Binding CopyOAuthClientIdCommand}" />
</Grid>
<!-- ── Pending: editable credential input ── -->
<StackPanel Spacing="8" IsVisible="{Binding IsPendingSetup}">
<TextBlock FontSize="12" Foreground="{StaticResource TextSecondaryBrush}"
TextWrapping="Wrap"
Text="Log into the Xibo CMS as xibo_admin (password: password), go to Administration → Applications, create a client_credentials app, then paste the credentials here." />
<TextBlock Text="Client Secret" FontSize="12" Foreground="{StaticResource TextSecondaryBrush}" Margin="0,4,0,0" />
<Grid ColumnDefinitions="*,Auto,Auto,Auto">
<TextBox Grid.Column="0" Text="{Binding OAuthSecretDisplay}" IsReadOnly="True"
FontFamily="Consolas,monospace" />
<Button Grid.Column="1" Content="Show" Margin="4,0,0,0"
IsVisible="{Binding !OAuthSecretVisible}"
Command="{Binding ToggleOAuthSecretVisibilityCommand}" />
<Button Grid.Column="2" Content="Hide" Margin="4,0,0,0"
IsVisible="{Binding OAuthSecretVisible}"
Command="{Binding ToggleOAuthSecretVisibilityCommand}" />
<Button Grid.Column="3" Content="Copy" Margin="4,0,0,0"
Command="{Binding CopyOAuthSecretCommand}" />
</Grid>
<TextBlock Text="Client ID" FontSize="12"
Foreground="{StaticResource TextSecondaryBrush}" Margin="0,4,0,0" />
<TextBox Text="{Binding InitClientId}" Watermark="OAuth2 Client ID" />
<TextBlock Text="Client Secret" FontSize="12"
Foreground="{StaticResource TextSecondaryBrush}" />
<TextBox Text="{Binding InitClientSecret}" PasswordChar="●"
Watermark="(paste from Xibo Applications page)" />
<Button Content="Initialize Instance"
Command="{Binding InitializeCommand}"
IsEnabled="{Binding !IsBusy}"
Classes="accent"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Center"
Padding="14,10" FontSize="14"
Margin="0,8,0,0" />
</StackPanel>
<!-- ── Initialized: read-only display ── -->
<StackPanel Spacing="8" IsVisible="{Binding !IsPendingSetup}">
<TextBlock Text="Client ID" FontSize="12"
Foreground="{StaticResource TextSecondaryBrush}" />
<Grid ColumnDefinitions="*,Auto">
<TextBox Grid.Column="0" Text="{Binding OAuthClientId}" IsReadOnly="True"
FontFamily="Consolas,monospace" />
<Button Grid.Column="1" Content="Copy" Margin="4,0,0,0"
Command="{Binding CopyOAuthClientIdCommand}" />
</Grid>
<TextBlock Text="Client Secret" FontSize="12"
Foreground="{StaticResource TextSecondaryBrush}" Margin="0,4,0,0" />
<Grid ColumnDefinitions="*,Auto,Auto,Auto">
<TextBox Grid.Column="0" Text="{Binding OAuthSecretDisplay}" IsReadOnly="True"
FontFamily="Consolas,monospace" />
<Button Grid.Column="1" Content="Show" Margin="4,0,0,0"
IsVisible="{Binding !OAuthSecretVisible}"
Command="{Binding ToggleOAuthSecretVisibilityCommand}" />
<Button Grid.Column="2" Content="Hide" Margin="4,0,0,0"
IsVisible="{Binding OAuthSecretVisible}"
Command="{Binding ToggleOAuthSecretVisibilityCommand}" />
<Button Grid.Column="3" Content="Copy" Margin="4,0,0,0"
Command="{Binding CopyOAuthSecretCommand}" />
</Grid>
</StackPanel>
</StackPanel>
</Border>