Commit Graph

30 Commits

Author SHA1 Message Date
REDCODE
220581579b refactor(auth): remove SendWithRefreshAsync, rely solely on JwtTokenRefresher
- Remove SendWithRefreshAsync method from AuthServiceBase (now dead code)
- Replace all 15 SendWithRefreshAsync call sites in 7 services with
  direct Client.XxxAsync calls
- Remove unused using System.Net from AuthServiceBase
- Update AGENTS.md to document JwtTokenRefresher as the sole token
  refresh mechanism
2026-07-10 01:12:31 +02:00
REDCODE
254e98b37b refactor(auth): extract AuthServiceBase, replace retry with re-auth on 401
- Move AuthServiceBase from ServiceBase.cs into its own file
- Replace FetchLoggedUserAsync retry loop with re-authentication on
  failure, then single recursive retry
- Fix override keyword order in App.razor OnInitializedAsync
- Clean up MainLayout.razor inject (unqualified type name)
2026-07-10 01:01:17 +02:00
b807e4b636 feat: infinite scroll for albums on cosplayer detail page 2026-07-09 17:53:42 +02:00
a38f627ca4 feat(dto): add RemovePerson flag to AlbumUpdateDto for explicit person unlinking 2026-07-09 15:56:20 +02:00
bac1ad0afc fix(build): remove stray semicolon in onclick lambda 2026-07-09 15:16:01 +02:00
e1d7822b7c refactor(api): fold unassigned album filter into search endpoint via query param 2026-07-09 15:09:09 +02:00
cc611828b9 feat(ui): unassigned album selector in PersonForm, Add Albums button in CosplayerDetail 2026-07-09 15:07:19 +02:00
9aead51481 feat(ui): add New Cosplayer button on list page with create endpoint and form 2026-07-09 15:00:52 +02:00
7521f15b63 fix(auth): force logout on any refresh failure, redirect to login 2026-07-09 12:45:37 +02:00
51a3d0d19b feat(client): add PersonService client methods and PersonForm component 2026-07-09 12:11:43 +02:00
23ef7e3ded fix(auth): detect banned/deleted user on page load via InitializeAsync
- LoginService.InitializeAsync: after fetching LoggedUser, check IsBanned
  and DeletedAt. If banned/disabled, set ForceLogoutReason, call Logout
  to clear state, and return false
- App.razor: after InitializeAsync completes, check ForceLogoutReason
  and navigate to /login if set. This handles the timing issue where
  MainLayout isnt created yet when the event fires on page load

Refs #45
2026-07-08 19:15:49 +02:00
a84d93882d fix(auth): show ban/disabled message on login and force-logout on refresh
- AuthController: fix HTTP 100 Continue → 403 Forbidden for banned/disabled
  users in both Login and RefreshToken endpoints
- LoginService.Login: return error message from AuthResultDto on failure
  instead of a generic message
- LoginService.Reauthenticate: parse AuthResultDto on refresh failure;
  if "User is banned" or "User is disabled", trigger force logout via
  new ForceLogout event, setting ForceLogoutReason property
- MainLayout: subscribe to ForceLogout event, redirect to /login
- Login.razor: display specific error messages (ban reason, force-logout
  reason) instead of generic "Login failed"

Refs #45
2026-07-08 19:10:53 +02:00
a599648d87 feat(admin): add user management page for admins
- Add AdminUsers.razor page at /Users with user listing, ban/unban toggle,
  access level change, delete, and create user functionality
- Extend UserService with GetAllUsersAsync, GetUserByIdAsync,
  UpdateUserAsync, DeleteUserAsync, CreateUserAsync methods
- Add Users link to NavMenu dropdown for admin users
- Fix ban/unban bug in UserController where BannedAt was only updated
  if the user had been previously banned

Closes #45
2026-07-08 18:47:01 +02:00
3c1d2ef14b feat: add AlbumService and PersonService to WASM client
- AlbumService: CRUD operations for albums via API
- PersonService: GetAllAsync for person dropdown
- Register both as Scoped in DI
2026-07-08 13:11:31 +02:00
89c4bfe13f feat: implement seed-based deterministic random ordering for home page
Replace the fixed GUID-based ordering (OrderBy(a => a.Id)) with a
client-generated seed that produces a truly different random order on
each page refresh. The seed is passed to the server and used to
deterministically shuffle all asset IDs via a hash function, ensuring
consistent pagination with no duplicates across pages within a session.

Also fix LoadPreviousPage showing a spinner when already at page 1 by
adding loadedMinPage <= 1 to the guard clause.
2026-07-08 12:10:37 +02:00
REDCODE
03f52cfb5b feat: add statistics page with aggregate asset/user/system stats via fast DB queries 2026-07-08 01:49:50 +02:00
604f0e33a0 refactor: make LoginService singleton, centralize auth state and persistence
- Register LoginService as singleton in WASM (was scoped)
- Add InitializeAsync() with retry logic for user profile fetch on boot
- LoginService now owns localStorage persistence (not components)
- Login() auto-persists auth and fetches LoggedUser atomically
- Logout() clears localStorage internally
- Reauthenticate() re-fetches LoggedUser if null after token refresh
- Simplify App.razor, Login.razor, NavMenu.razor by removing direct localStorage calls
2026-07-07 19:18:50 +02:00
07fcd05031 feat: masonry gallery on homepage with infinite scroll
- Enrich AssetPreviewDto with ResolutionWidth/Height, HasThumbnail, HasPreview
- Extend GET /api/asset with ?type=&random=true for gallery queries
- Add general GetAssets repository method (type, dates, random, pagination)
- Fix LactoseAuthService.GetUserData to handle unmapped JWT claim names
- Add request-level auth debug logging middleware
- New AssetService (MilkStream.Client) for gallery API calls
- Rewrite Home.razor: CSS column-count masonry, infinite scroll via
  IntersectionObserver JS interop, inline preview overlay with
  progressive loading (preview -> thumbnail fallback), arrow/Esc nav
- Home.razor.css: responsive 2-5 column masonry, overlay styles,
  content-visibility:auto for memory optimization
- Sliding window of 10 pages (300 tiles) with loading=lazy images
- Broken thumbnail fallback to Bootstrap bi-image icon
2026-07-07 15:44:03 +02:00
64a0fdf81d feat(folders): add regex pattern field with named groups, syntax highlighting, and sample path preview 2026-07-07 14:19:35 +02:00
d93036f754 fix: handle 401 transparently at AuthServiceBase level for all services
- JwtTokenRefresher: remove ForceLogout that was clearing auth on refresh
  failure and redirecting to login. Instead return a synthetic 401 so the
  service-layer handling can retry.
- AuthServiceBase: add LoginService property and SendWithRefreshAsync
  helper — wraps any HTTP call, catches 401, refreshes token via
  Reauthenticate(), updates DefaultRequestHeaders, retries once.
- JobsService: remove local SendWithRefreshAsync, use base class version.
- SettingsService, FoldersService: wrap GET calls with
  SendWithRefreshAsync.
- LoginService: fix Logout to also clear LoggedUser.
2026-07-06 19:45:42 +02:00
e33d87bb30 feat: unify settings pages into sidebar + detail panel with auto-save
Replace two separate settings pages with a single unified page featuring:
- Sidebar navigation with 6 categories (General, Scanning, Upload, Paths, Jobs, Folders)
- Auto-save with 500ms debounce and visual feedback per setting
- Inline folder management as a dedicated tab
- Removed duplicate BeginSave event subscription bug in SettingBox components

Breaking: SettingsService.BeginSave event and OnBeginSave() removed
2026-07-06 19:30:07 +02:00
eb9f0d14d3 docs: add XML documentation to all public APIs across all projects
Achieves 100% XML doc coverage on non-trivial types with CS1591
enforcement via Directory.Build.props.

Coverage by project:
- Butter: from 6.5% to 100% (DTOs, enums, MIME types)
- Lactose: from ~28% to 100% (controllers, services, repos, jobs, models)
- MilkStream.Client: from ~29% to 100% (all frontend services)

Uses <inheritdoc /> on repository implementations (interfaces
already documented) and full <summary>/<param>/<returns> tags
elsewhere. Enums include member-level docs.
2026-07-06 18:45:21 +02:00
0a9c3ba207 feat: optimize jobs API with pagination, DB persistence, and cleanup
- Split /api/jobs into three endpoints: active roots, paginated past,
  children fetched on expand — reduces response from ~1MB to ~5KB
- Persist completed jobs to PostgreSQL via JobRecord entity + migration
- Add configurable job retention cleanup (hourly timer, default 30 days)
- Fix PHashJob master not persisting (missing Complete() call)
- Add PastJobsResponse DTO to fix case-sensitive JsonElement deserialization
- Refactor JobTree/JobRow for lazy-loaded children via FetchChildren callback
- Handle 401 responses in JwtTokenRefresher with token refresh + retry
- Remove JobChildrenSummary component (replaced by inline parent summary)
2026-07-06 18:17:49 +02:00
b9a0e2f1b6 fix: handle 401 in JobsService directly with refresh+retry
The JwtTokenRefresher DelegatingHandler was not reliably intercepting
401 responses in Blazor WASM. Add a SendWithRefreshAsync helper to
JobsService that catches 401, calls Reauthenticate() to get a fresh
token, updates DefaultRequestHeaders, and retries once. This ensures
the Jobs page transparently recovers from token expiry without
showing an error.
2026-07-06 17:42:38 +02:00
f9dea23757 chore: remove redundant XML doc comments from ServiceBase 2026-07-06 17:34:18 +02:00
4128ea1b58 fix: redirect to login when token refresh fails in JwtTokenRefresher
- Inject NavigationManager and ILocalStorageService into JwtTokenRefresher
- Extract TryRefresh() helper to avoid repeating refresh logic
- Add ForceLogout() to LoginService that clears both AuthInfo and LoggedUser,
  firing AuthInfoChanged and LoggedUserChanged events
- When an auth refresh fails (both pre-emptive and reactive paths), call
  ForceLogout(), clear persisted auth from localStorage, and redirect to /
  with a full reload so the user sees the login page
2026-07-06 17:34:08 +02:00
0a48663d35 fix: notify auth info changed after token refresh and handle polling errors
- Fire AuthInfoChanged event after successful Reauthenticate() so all
  AuthServiceBase instances update DefaultRequestHeaders with the new
  token, preventing cascading failure on the next poll cycle
- Wrap RefreshJobs() poll body in try-catch so transient errors don't
  crash the component
2026-07-06 15:04:54 +02:00
copilot-swe-agent[bot]
a1d5e23817 Merge PR#2 (convert-frontend-to-wasm) into update-branch-to-be-compatible
Co-authored-by: SamueleLorefice <6562494+SamueleLorefice@users.noreply.github.com>
2026-04-22 21:17:46 +00:00
copilot-swe-agent[bot]
27259c7241 fix: address code review findings (blocking .Result, async void handlers, email logging)
Agent-Logs-Url: https://github.com/SamueleLorefice/MilkyShots/sessions/2a560c45-14b6-47a3-b348-22e4142217be

Co-authored-by: SamueleLorefice <6562494+SamueleLorefice@users.noreply.github.com>
2026-04-22 19:43:56 +00:00
copilot-swe-agent[bot]
7df482f9e9 feat: migrate MilkStream frontend from Blazor SSR to Blazor WASM
Agent-Logs-Url: https://github.com/SamueleLorefice/MilkyShots/sessions/2a560c45-14b6-47a3-b348-22e4142217be

Co-authored-by: SamueleLorefice <6562494+SamueleLorefice@users.noreply.github.com>
2026-04-22 19:39:07 +00:00