472 Commits
Author SHA1 Message Date
REDCODE 191cba4946 fix(pr): implement BulkUpdate, BulkDelete, revert docker-compose, explain [FromQuery] 2026-07-09 16:22:51 +02:00
REDCODE a38f627ca4 feat(dto): add RemovePerson flag to AlbumUpdateDto for explicit person unlinking 2026-07-09 15:56:20 +02:00
REDCODE f31b753182 refactor(dto): move Unassigned filter into PagedSearchParametersDto 2026-07-09 15:54:02 +02:00
REDCODE 1377d02520 fix: prevent album title from being overwritten when only setting Person in update DTO 2026-07-09 15:19:03 +02:00
REDCODE fa434b180d fix(api): allow unlinking person from album by directly assigning null PersonOwnerId 2026-07-09 15:12:34 +02:00
REDCODE e1d7822b7c refactor(api): fold unassigned album filter into search endpoint via query param 2026-07-09 15:09:09 +02:00
REDCODE cc611828b9 feat(ui): unassigned album selector in PersonForm, Add Albums button in CosplayerDetail 2026-07-09 15:07:19 +02:00
REDCODE 9aead51481 feat(ui): add New Cosplayer button on list page with create endpoint and form 2026-07-09 15:00:52 +02:00
REDCODE 22b15c387b feat(ui): redesigned profile cropper with movable circle, dimmed exterior, square boundary, zoom controls; added ProfileCropZoom to model; switched avatars to background-image for zoom support 2026-07-09 13:15:19 +02:00
REDCODE d3ef5ff4c1 feat(backend): add ProfileCropX/Y to Person model for profile picture cropping 2026-07-09 13:01:01 +02:00
REDCODE 2dc0153b10 fix(backend): add [FromQuery] to person list endpoint to fix 415 error 2026-07-09 12:16:24 +02:00
REDCODE 7143af14f0 feat(client): add Cosplayers and CosplayerDetail pages, fix NavMenu, add modularity docs 2026-07-09 12:13:12 +02:00
REDCODE 20fd6a930b test(api): add person endpoint tests to WepApiTest.http 2026-07-09 12:09:40 +02:00
REDCODE 8f0af8d544 feat(backend): implement person detail, update, and delete endpoints 2026-07-09 12:03:33 +02:00
REDCODE 16161ebfb8 feat(backend): add PersonMapper with ToPersonPreviewDto and ToPersonDetailedDto 2026-07-09 12:02:36 +02:00
REDCODE ca224feafb feat(backend): eager-load ProfileAsset and Albums in PersonRepository 2026-07-09 12:01:46 +02:00
REDCODE 9c785423a9 feat(backend): add ProfileAssetId to Person model + migration 2026-07-09 12:00:56 +02:00
MrFastwind df14b8b227 fix (lactose): remove check from authentication endpoint
ref: #36
2026-07-08 23:04:51 +02:00
MrFastwind 15508013a3 Merge remote-tracking branch 'origin/develop' into develop
# Conflicts:
#	Lactose/Controllers/UserController.cs
2026-07-08 22:39:20 +02:00
MrFastwind cb47336230 fix(Lactose): Change to use UtcNow, even if alredy working without timeline 2026-07-08 22:38:05 +02:00
REDCODE 54cf5eff2b feat(stats): add percentage and stale counters to completeness cards
- Show percentage of total assets on missing metadata/thumbnail/preview/phash cards
- Add stale counts for thumbnails and previews (generated at wrong resolution vs current settings)
- Add h-100 to all completeness cards for consistent row heights
2026-07-08 19:45:09 +02:00
REDCODE 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
REDCODE 61cf886174 fix(user): hash password in Create endpoint instead of storing raw
- UserController.Create was assigning userCreateDto.Password directly without
  hashing via IPasswordHasher, unlike the Update method and AuthController
- Moved duplicate email/username checks before User object instantiation

Refs #45
2026-07-08 18:53:46 +02:00
REDCODE 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
REDCODE e5fc1a017e chore: remove debug auth logging middleware from Program.cs 2026-07-08 18:08:48 +02:00
REDCODE 44394daebf fix(AlbumController): exclude deleted assets from search visibility for #11
Album search for User role used asset.IsPubliclyShared and
SharedWith checks without filtering soft-deleted assets. A deleted
public asset would cause its album to appear in search results.

Added asset.DeletedAt == null checks to both filter paths.

Refs: #11
2026-07-08 17:49:08 +02:00
REDCODE ac76aab168 fix(AssetController): correct search visibility filter for #11
The User-role asset search filter had two bugs:
1. DeletedAt != null was negated — shared non-deleted assets were
   excluded, while shared deleted assets passed the filter.
2. DeletedAt check was applied only to the shared leg — public
   deleted assets leaked through the filter.

Corrected to: not-deleted AND (public OR shared), matching the
logic used by GET /api/asset/{id}.

Refs: #11
2026-07-08 17:47:43 +02:00
REDCODE 4f56acb320 fix(controllers): tighten asset visibility and media access checks
- AlbumController GET {id}: exclude soft-deleted assets for User role
  (public/owned and shared paths) and Curator role (viewed all assets)
- AssetController GET: add explicit parentheses to DeletedAt operator
  grouping in search filter to match intent
- MediaController: deny Curators direct file access to deleted assets;
  extract CanAccessAssetDirectly helper method

Refs: #11
2026-07-08 17:44:03 +02:00
REDCODE 9b17b6b231 fix: faulty migration that could cause lactose crash on migration apply 2026-07-08 14:47:54 +02:00
REDCODE 4fe1fb0da2 feat: make album names clickable links in home tile hover and preview
- Add AlbumIds parallel list to AssetPreviewDto
- Populate AlbumIds in AssetsMapper
- Home tile hover: album names link to /albums/{id} with onclick:stopPropagation
- ImagePreview ChildContent: album names link to /albums/{id}
- CSS: tile-info-album and preview-info-album support <a> tags with hover underline
2026-07-08 13:48:17 +02:00
REDCODE 4c90e553af feat: auto-assign album cover from first alphabetically-sorted asset
- Add one-time migration SQL to set CoverAssetId for existing albums
- AlbumController.Create auto-sets cover from first asset if not specified
- CreateAlbumsJob assigns cover images after asset assignment phase
- Stats: add AlbumsMissingCover and CosplayersMissingCover counters
2026-07-08 13:20:11 +02:00
REDCODE b8f75f9529 refactor: extract ImagePreview shared component from Home and AlbumDetail
- Create ImagePreview.razor component with self-contained url generation, keyboard handling, and download
- Add Filename to AssetPreviewDto and AlbumAssetPreviewDto DTOs
- Update AssetsMapper and AlbumMapper to populate FileName
- Remove duplicate preview overlay HTML/CSS/code from Home.razor and AlbumDetail.razor
- Fix AssetDto.FileName hiding inherited member from AssetPreviewDto
2026-07-08 13:18:23 +02:00
REDCODE b58c8ebce8 fix: album controller Save calls, person list endpoint, media empty path guard
- Add missing Save() calls to AlbumController Update and Delete
- Wire CoverAssetId in AlbumController Create/Update/BulkUpdate
- Sort album assets by OriginalFilename in Get endpoint
- Implement PersonController.GetAll returning non-deleted people
- Guard MediaController GetThumb/GetPreview against empty paths (return 404)
2026-07-08 13:11:27 +02:00
REDCODE d602d2d34b feat: add CoverAssetId to albums with resolution data in full DTO
- Add CoverAssetId FK and CoverAsset navigation to Album model
- Add AlbumAssetPreviewDto with resolution/thumbnail info
- Extend AlbumPreviewDto/FullDto with PersonName, CoverAssetId, AssetCount
- Add CoverAssetId to AlbumCreateDto and AlbumUpdateDto
- Update AlbumMapper to populate new fields from eager-loaded relations
- Add EF migration for CoverAssetId column
2026-07-08 13:11:20 +02:00
REDCODE 949dec9bdc feat: add data completeness indicators to statistics page 2026-07-08 12:21:25 +02:00
REDCODE 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 7a0b72584c fix: propagate cancellation tokens through linked child job CTS
Replace manual childJobs.ForEach(j => j.Cancel()) with linked
CancellationTokenSources. Master jobs now call LinkParentToken(token)
on each child, so when the master's CTS fires, all children (running
and queued) receive cancellation automatically via their linked token.

Cancelling a master job now:
- Immediately signals all Running children via their linked CTS
- Ensures queued children, when started by JobManager, receive an
  already-cancelled token and bail without processing
- Eliminates the collection-modification-during-ForEach race condition

Affects: PHashJob, ThumbnailJob, PreviewJob, MetadataJob,
FileSystemCrawlJob.
2026-07-08 12:00:27 +02:00
REDCODE d4a8b272f2 feat: extend AssetPreviewDto with album and cosplayer names
- Add AlbumNames and CosplayerNames fields to AssetPreviewDto
- Include Albums and PersonOwner in AssetRepository.GetAssets() query
- Populate new fields in AssetsMapper.ToAssetPreviewDto()
2026-07-08 03:06:03 +02:00
REDCODE bb01f6526e fix: remove destructive People soft-delete in IntegrityCheckJob and drop duplicate hash stats 2026-07-08 02:58:00 +02:00
REDCODE 866d01c4e2 fix: rewrite TopTags query to avoid EF Core collection navigation translation error 2026-07-08 02:36:38 +02:00
REDCODE 2159eaec08 fix: populate LastLogin/BannedAt/UpdatedAt in UserInfoDto mapping
UsersMapper.ToGetUsersDto() was not copying LastLogin, BannedAt, or
UpdatedAt from the User entity to the UserInfoDto, causing the client
to always receive null for these fields. Added the missing mappings.

Also added null fallback ('Never') for LastLogin display on the User
page to prevent NullReferenceException when the value has not been set.
2026-07-08 02:32:12 +02:00
REDCODE bf3e5e619b fix: add JobBatchSize to DefaultSettings.json to prevent reset on restart 2026-07-08 02:25:18 +02:00
REDCODE 458cf70081 feat: add thousands separators to job status numbers 2026-07-08 02:03:40 +02:00
REDCODE 3d81b7a97e fix: skip existing AlbumAsset relationships in CreateAlbumsJob to prevent duplicate key violations on re-run 2026-07-08 01:50:39 +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
REDCODE 4b0a6df5dd fix: per-batch progress messages and counting bug in album/person jobs 2026-07-08 01:30:33 +02:00
REDCODE 744bc9e4d0 feat: improve logging and progress reporting for album/person jobs 2026-07-08 01:23:25 +02:00
REDCODE 76ba4fd74a configuration changes 2026-07-08 01:08:00 +02:00
REDCODE 132e3d2930 fix: make job cancellation work for all job states and types
Job.Cancel() now handles queued jobs (cts null) by setting Canceled status and firing events directly. ContinueWith checks t.IsCanceled to catch ThrowIfCancellationRequested patterns. cts is now disposed after job completion. CreatePersonsJob, CreateAlbumsJob, and IntegrityCheckJob now catch OperationCanceledException and set proper Canceled state. IntegrityCheckJob also gained cancellation checks between each step and within iteration loops.
2026-07-08 01:07:25 +02:00
REDCODE 6c9a608ce6 fix: propagate cancellation to sub-jobs in batch processing jobs
MetadataJob, ThumbnailJob, PreviewJob, and PHashJob previously only
canceled the master job's own token, leaving already-enqueued sub-jobs
running indefinitely. Added child-job tracking with proper cancellation
propagation at both master cancellation exit points, matching the
existing pattern in FileSystemCrawlJob.
2026-07-08 00:20:40 +02:00