Refactor code structure for improved readability and maintainability
This commit is contained in:
parent
b47641feaa
commit
4f488bae45
78 changed files with 3309 additions and 1570 deletions
78
UpgradePlan2.md
Normal file
78
UpgradePlan2.md
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
|
||||
|
||||
<div class="page"> <nav class="sidebar"><NavMenu /></nav> <main class="main">@Body</main> </div> ``` Add a top bar (`<header class="topbar">`) with the app title "Twitch Archive" and a hamburger toggle `<button>` that toggles a `sidebarCollapsed` bool field to add/remove a CSS class.
|
||||
|
||||
Create (or extend) dotnet/src/TwitchArchive.Web/wwwroot/css/app.css:
|
||||
.page: display:flex; height:100vh
|
||||
.sidebar: width:220px; flex-shrink:0; background:#1e1e2e; color:#cdd6f4; overflow-y:auto
|
||||
.main: flex:1; overflow-y:auto; padding:1.5rem
|
||||
.nav-link: display:block; padding:0.6rem 1rem; color:#cdd6f4; text-decoration:none
|
||||
.nav-link.active: background:#313244; border-left:3px solid #89b4fa
|
||||
Media query @media(max-width:768px): .sidebar.collapsed { display:none }, .topbar { display:flex }, else .topbar { display:none }
|
||||
Step G — Full Blazor UI pages (Plan Step 8)
|
||||
Goal: implement the missing pages referenced by the NavMenu.
|
||||
|
||||
Steps
|
||||
|
||||
Dashboard.razor (/) — replace dotnet/src/TwitchArchive.Web/Pages/Index.razor. Display a CSS grid of streamer cards, each showing: username (link to /streamer/{username}), live/offline <span> badge, current RecoveryState text from WorkerManager.GetState(username), last session start from ISessionRepository, Start/Stop buttons. Poll every 10 s via PeriodicTimer in OnInitializedAsync, disposed in IAsyncDisposable.DisposeAsync.
|
||||
|
||||
StreamerDetail.razor (/streamer/{username}) — new file. Live status badge, pipeline step bar (Record → Process → Upload using CSS flex row), <ProcessConsole Streamer="@Username" />. Route parameter [Parameter] public string Username.
|
||||
|
||||
GlobalConfig.razor (/config/global) — new file. <EditForm> bound to a GlobalConfig loaded via IConfigurationService.LoadGlobal(). On valid submit: await ConfigService.SaveGlobal(model), show a dismissible success alert.
|
||||
|
||||
StreamerConfig.razor (/config/{username}) — new file. Per-field nullable override: each field has an <InputCheckbox> "Override" toggle; when unchecked the <InputText> is disabled and shows the global default as placeholder. Save calls SaveStreamer. Delete button removes the config file and navigates to /.
|
||||
|
||||
AddStreamer.razor (/config/new) — new file. Two fields: Username (required, lowercase) and Enabled checkbox. On submit: await ConfigService.SaveStreamer(new StreamerConfig { Username, Enabled }), then Nav.NavigateTo($"/config/{model.Username}").
|
||||
|
||||
AppSettings.razor (/settings) — new file. Tool-path fields bound to AppSettings. Change-password section: current password (validated against BCrypt hash) + new + confirm fields. On save: update appsettings.json and call IAuthService to refresh the cached hash.
|
||||
|
||||
Step H — Authentication (Plan Step 8)
|
||||
Goal: single-password BCrypt cookie auth protecting all pages except /login.
|
||||
|
||||
Steps
|
||||
|
||||
Add <PackageReference Include="BCrypt.Net-Next" Version="4.*" /> to TwitchArchive.Web.csproj.
|
||||
Create dotnet/src/TwitchArchive.Web/Services/IAuthService.cs + AuthService.cs — ValidatePassword(string plain) → bool using BCrypt.Net.BCrypt.Verify against AppSettings.PasswordHash. If hash is empty (first-run), any password is accepted.
|
||||
Create dotnet/src/TwitchArchive.Web/Pages/Login.razor (/login) — password <InputText type="password"> in an <EditForm>. Posts to /auth/login minimal-API endpoint via form navigation.
|
||||
Add minimal API endpoint POST /auth/login in Program.cs — reads password from form body, calls IAuthService.ValidatePassword, calls HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, ...), redirects to /. On failure, redirects to /login?error=1.
|
||||
In Program.cs add builder.Services.AddAuthentication(...).AddCookie(opt => opt.LoginPath = "/login"), app.UseAuthentication(), app.UseAuthorization().
|
||||
Update App.razor — replace <RouteView> with <AuthorizeRouteView>, wrap in <CascadingAuthenticationState>. Add @attribute [Authorize] to all pages except Login.razor.
|
||||
Step I — Docker (Plan Step 10)
|
||||
Steps
|
||||
|
||||
Create dotnet/Dockerfile:
|
||||
|
||||
Build stage (sdk:10.0): dotnet publish src/TwitchArchive.Web -c Release -o /app/publish
|
||||
Runtime stage (aspnet:10.0): apt-get install -y ffmpeg python3-pip rclone, pip3 install streamlink, download TwitchDownloaderCLI linux-x64 binary to /app/bin/ and chmod +x.
|
||||
EXPOSE 8080, ENV ASPNETCORE_URLS=http://+:8080, ENTRYPOINT ["dotnet","TwitchArchive.Web.dll"]
|
||||
Create dotnet/docker-compose.yml:
|
||||
|
||||
Create dotnet/src/TwitchArchive.Core/Config/ToolPathResolver.cs — static helper using RuntimeInformation.IsOSPlatform(OSPlatform.Windows) to resolve default binary paths; used by AppSettings property defaults and RecorderService/ProcessorService.
|
||||
|
||||
Step J — Extended unit tests (Plan Step 11)
|
||||
Steps
|
||||
|
||||
ConfigurationServiceTests.cs — load/save/merge in Path.GetTempPath() temp dir; assert roundtrip and merge precedence.
|
||||
TwitchApiClientTests.cs — mock HttpMessageHandler; token caching, GQL stream-status (live + offline), GetLatestVodAsync, network error → null.
|
||||
FileManagerServiceTests.cs — InitializeDirectories, GetPaths, GetUniquePath collision suffix on temp dirs.
|
||||
RecorderServiceTests.cs — mock IProcessRunner capturing ProcessRunOptions.Arguments; assert --hls-live-restart and resolved quality.
|
||||
UploadServiceTests.cs — mock IProcessRunner; assert rclone arguments contain copy --files-from and correct dest; assert temp list file is cleaned up.
|
||||
EffectiveConfigTests.cs — all merge-precedence cases: null streamer field → global default; non-null streamer field → override wins.
|
||||
SessionRepositoryTests.cs — EF in-memory; AddSessionAsync, GetRecentSessionsAsync, UpdateSessionAsync status change.
|
||||
Verification
|
||||
Manual checks:
|
||||
|
||||
All NavMenu links resolve without 404; active link is highlighted
|
||||
Login page blocks unauthenticated access; correct password grants access; wrong password shows error
|
||||
Dashboard renders streamer cards; Start/Stop toggles update Recovery State badge
|
||||
Global Config saves to global.json; reload confirms persistence
|
||||
Add Streamer creates config/streamers/{name}.json and redirects to per-streamer config page
|
||||
Sessions page shows rows after a recording completes with expandable job list
|
||||
Live Monitor shows real-time ProcessConsole output via SignalR
|
||||
Decisions
|
||||
NavMenu uses Blazor's built-in NavLink with pure CSS sidebar — no Bootstrap dependency to keep the bundle small
|
||||
Config service reads/writes the same config directory as the Python side — both runtimes can share config files without conversion
|
||||
EnsureCreated → Migrate() — migrations support future schema changes without data loss
|
||||
BCrypt single-password auth over full Identity — this is a single-user self-hosted tool; Identity adds unnecessary overhead
|
||||
IRecorderService extracted from StreamWorker — the recording path becomes independently testable without running the full state machine
|
||||
Or simply paste the content above into a new file UpgradePlan-Part2.md at the workspace root. Once terminal/file-edit tools are re-enabled, I can write it directly.
|
||||
Loading…
Add table
Add a link
Reference in a new issue