7b5bb66b37
feat: frontend-ready backend — pagination, auto-derivatives, list endpoints, bulk ops, OpenAPI
...
Pagination: count_by_owner + count_search on AssetRepository,
timeline/search return real total count (not page len).
Auto-derivatives: worker enqueues GenerateDerivative when
ExtractMetadata job completes, closing the upload→thumbnail gap.
List endpoints: GET /albums, GET /stacks with user scoping.
ListAlbumsHandler, ListStacksHandler, find_by_owner on AssetStackRepository.
Tag filtering: tag_name field on AssetFilters, JOIN asset_tags+tags
in postgres search/count queries.
Bulk operations: POST /assets/bulk-delete, POST /assets/bulk-tag.
Album update: PUT /albums/{id} with UpdateAlbumHandler (title, description).
OpenAPI: utoipa annotations on all 47 endpoints + all request/response
schemas registered. Scalar UI at /scalar covers full API.
2026-05-31 23:06:25 +02:00
bcaf49cc81
perf: scale fixes for 1M+ photo libraries
...
Indexes: share_targets.target_id, duplicate_groups.status,
GIN on stacks members + duplicate candidates JSONB,
composite (owner_user_id, created_at DESC) on assets.
N+1 elimination: batch metadata loading via find_by_assets(ids)
using WHERE asset_id = ANY($1), used in timeline + sidecar export.
Visibility: cache find_targets_for_user per request via OnceCell,
extract filter_visible helper to reduce duplication.
Streaming: FileStoragePort.open_file() returns (DataStream, u64),
LocalFileStorage uses ReaderStream instead of loading full file.
serve_file/serve_derivative use Body::from_stream().
Unbounded queries: sidecar full_export/import batched in 500-row
chunks instead of u32::MAX. find_unresolved paginated with
limit/offset. list_duplicates API accepts pagination params.
2026-05-31 22:40:25 +02:00
d879fd6437
fix: sync .env.example with actual config
...
Remove vars not read by code (DB_MAX/MIN_CONNECTIONS, STORAGE_BACKEND,
PRODUCTION, JWT_EXPIRY_HOURS). Add missing NATS_URL, RUST_LOG.
Fix PORT default 3000->8000, CORS origin to match.
2026-05-31 22:28:36 +02:00
168f2a6a27
docs: update Dockerfile and README
...
Dockerfile: add missing adapter crates (exif, sidecar, thumbnail).
README: update project structure, auth section, env vars table,
test count (206), docker usage, bounded context descriptions.
2026-05-31 22:27:55 +02:00
c6f82090d2
feat: auth hardening + codebase quality sweep
...
Refresh tokens: RefreshToken entity, PostgresRefreshTokenRepository,
login returns refresh token, POST /auth/refresh (rotation), POST /auth/logout,
JWT expiry 24h→1h, configurable via with_expiry().
Route protection: require_auth middleware on protected routes,
public routes split (register, login, refresh, sharing/access).
Authorization: caller_id added to ReadAssetFileQuery, ReadDerivativeQuery,
GetStackQuery, DeleteStackCommand with ownership checks. Admin-only gates
on processing, storage, sidecar, duplicates handlers.
Quality fixes: visibility filtering bypass in search(), unwrap panics in
date parsing, DRY auth header parsing, centralized parsers module,
email validation via email_address crate, value objects (Username, MimeType,
RelativePath), domain events (UserCreated, UserDeleted, AlbumCreated,
TagCreated, DuplicateDetected), postgres error mapping for constraint
violations, OptionExt::or_not_found helper, in_memory_repo! macro,
GetStackQuery moved to queries, album add_entry 200→201.
2026-05-31 22:26:02 +02:00
84fb410316
fe init
2026-05-31 21:32:28 +02:00
95916cedde
feat: directory scanner plugin — walk library paths, auto-register assets
...
- DirectoryScannerPlugin: recursive directory walk via FileStoragePort
- Computes SHA256 checksums, classifies media by extension
- Registers each file via RegisterAssetHandler (triggers AssetIngested → extract_metadata pipeline)
- Reads library_path_id from job payload, looks up volume + path
- Seeded plugin + scan_directory pipeline
- Trigger via POST /jobs with { job_type: "ScanDirectory", payload: { library_path_id: "..." } }
2026-05-31 21:18:23 +02:00
ef64e86439
feat: serve derivative files via GET /assets/{id}/derivatives/{profile}
...
- ReadDerivativeHandler queries DerivativeRepository + FileStoragePort
- Profile URL param: thumbnail, thumbnail_large, web_optimized, video_sd
- Immutable cache headers (derivatives don't change once generated)
- Wired into bootstrap catalog service builder
2026-05-31 21:10:58 +02:00
f85c0cb246
feat: real XMP sidecar adapter, replaces LogSidecarWriter stubs
...
- adapters-sidecar: XmpSidecarWriter using xmp_toolkit
- Writes StructuredData → XMP with EXIF/DC/XMP namespace routing
- Reads XMP back to StructuredData
- Wired into bootstrap + worker, deleted both LogSidecarWriter stubs
2026-05-31 21:05:46 +02:00
d379f3d3c8
refactor: code smell fixes — tests, events, naming
...
- Tests for ExecutePipelineHandler (happy path, fallback, disabled skip, failure retry, not found)
- Tests for ProcessNextJobHandler (empty queue, process, drain multiple)
- DerivativeGenerated domain event + event-payload mapping + event_store aggregate
- Renamed event-payload → adapters-event-payload, event-transport → adapters-event-transport
2026-05-31 21:00:50 +02:00
e11a1a828b
refactor: use workspace deps for all internal crates, no relative paths
2026-05-31 20:48:09 +02:00
35d5baf7be
feat: thumbnail generator plugin with configurable size/format
...
- ThumbnailGeneratorPort in domain (bytes + config → resized bytes)
- adapters-thumbnail: ImageThumbnailGenerator using image crate
- ThumbnailGeneratorPlugin reads width/height/format/profile from step config
- PostgresDerivativeRepository + 012_derivatives migration
- Seeded in extract_metadata pipeline as step 2 (300x300 webp)
- Standalone generate_derivative pipeline for on-demand use
2026-05-31 20:44:55 +02:00
45669ec848
feat: real EXIF extraction via adapters-exif crate
...
- MetadataExtractorPort in domain (bytes → StructuredData)
- adapters-exif: NomExifExtractor using nom-exif, handles EXIF + TrackInfo
- Worker's MetadataExtractorPlugin delegates to port, no longer knows nom-exif
- Filters noisy binary tags (U8Array, Undefined, Unknown)
2026-05-31 20:28:50 +02:00
d1c7243f5b
feat: seed default plugins/pipelines, auto-enqueue jobs on asset ingest
...
- Migration seeds metadata_extractor, sidecar_sync, no_op plugins
- Pipelines: extract_metadata → metadata_extractor, sync_sidecar → sidecar_sync
- Worker reacts to AssetIngested → enqueues ExtractMetadata job
- Worker reacts to SidecarSyncRequested → enqueues SyncSidecar job
- Closes the ingest-to-processing loop end-to-end
2026-05-31 20:12:42 +02:00
b5cda3afeb
feat: add VisibilityFilteredAssetRepository decorator for automatic access control on asset queries
2026-05-31 19:06:49 +02:00
0b2237860e
refactor: introduce IngestTransaction port to reduce IngestAssetHandler from 7 to 4 ports
2026-05-31 18:44:51 +02:00
aa09aec66b
feat: event store — persist domain events to Postgres event_log table via composite publisher
2026-05-31 18:36:10 +02:00
d022cb9068
feat: event-driven job dispatch via NATS subscription with 60s fallback sweep
2026-05-31 18:31:53 +02:00
5a4eb1e4f8
refactor: split bootstrap factory into per-context service builders
2026-05-31 18:28:57 +02:00
c16c9d4581
refactor: extract pg_repo macro and MapDomainError trait to reduce postgres adapter boilerplate
2026-05-31 18:24:16 +02:00
2fe0a4c245
dockerfile
2026-05-31 17:51:39 +02:00
838ed9a3f8
feat: wire NATS event publisher into bootstrap + worker
...
- Both binaries connect to NATS on startup, ensure JetStream stream
- EventPublisherAdapter<NatsTransport> replaces LogEventPublisher
- nats_url config with default nats://localhost:4222
- Deleted bootstrap's LogEventPublisher (no longer needed)
2026-05-31 11:53:51 +02:00
0e9911ebfc
feat: event infrastructure — payload, transport, NATS adapter
...
- EventPublisher now takes &DomainEvent (11 call sites + 3 impls updated)
- EventEnvelope + EventConsumer port in domain
- event-payload: serializable DomainEvent mirror with subject routing
- event-transport: generic Transport/MessageSource traits, publisher/consumer adapters
- adapters-nats: JetStream publish + durable pull consumer
2026-05-31 11:50:16 +02:00
dacfc3d453
feat: worker plugin system — domain ports, pipeline executor, built-in plugins
...
- PluginExecutor + PluginRegistry ports in domain
- ExecutePipelineCommand orchestrates job→pipeline→plugin steps
- ProcessNextJobCommand polls + executes next queued job
- InMemoryPluginRegistry, NoOp/MetadataExtractor/SidecarSync plugins
- Worker main rewritten with poll loop, factories module for DI
- Deleted template job/runner/jobs remnants
2026-05-31 11:35:05 +02:00
6c88ac344c
refactor: extract inline tests to separate files in auth + storage adapters
2026-05-31 11:16:18 +02:00
aff772f6d7
style: fmt postgres lib.rs
2026-05-31 11:12:18 +02:00
e082387f6e
refactor: group postgres adapters by bounded context
2026-05-31 11:11:46 +02:00
a6b86c23d8
feat: wire remaining handlers — tag, quota, register asset, sidecar, processing
...
14 new endpoints: POST tags, GET quota, POST register, 6 sidecar, 7 processing.
DTOs, AppState groups, LogSidecarWriter, full bootstrap wiring.
2026-05-31 11:04:22 +02:00
19be8c2adf
feat: add sidecar + processing migrations and postgres adapters
...
007_sidecar, 008_processing, 009_duplicate_groups migrations.
Tag, sidecar, job, batch, plugin, pipeline, duplicate repos.
2026-05-31 11:04:13 +02:00
3399e25441
feat: add sharing endpoints — share, link, revoke, public access
2026-05-31 10:50:28 +02:00
2d9dd2c2d0
refactor: clean up presentation layer — AppState grouping, multipart extractor, thin handlers
2026-05-31 06:14:19 +02:00
34b231a8f6
refactor: move business logic out of presentation — ReadAssetFile, checksum, auth checks, MetadataValue conversions
2026-05-31 06:10:07 +02:00
0f003a3bd6
feat: add file serving endpoint GET /assets/:id/file
2026-05-31 05:59:19 +02:00
3a18fd1d3f
fix: axum 0.8 route syntax, smoke test verified
2026-05-31 05:55:47 +02:00
9aba393fde
feat: vertical slice — migrations, postgres adapters, presentation handlers, bootstrap wiring
2026-05-31 05:52:42 +02:00
201eff717d
feat: add presentation layer + bootstrap wiring for vertical slice
2026-05-31 05:51:09 +02:00
8c1a0e4519
feat: add postgres migrations and repository adapters for vertical slice
2026-05-31 05:43:21 +02:00
4e2fc99065
docs: add proper README and MIT license
2026-05-31 05:32:54 +02:00
c2ebca0da0
style: cargo fmt --all
2026-05-31 05:31:42 +02:00
4b31a0f74b
app: add sidecar sync commands (export, detect, import, resolve, full export/import)
2026-05-31 05:29:03 +02:00
d1394ce7bb
app: add organization + sharing commands/queries
2026-05-31 05:17:51 +02:00
536bf3463a
app: add catalog commands/queries (RegisterAsset, UpdateMetadata, GetTimeline, GetAsset)
2026-05-31 05:13:47 +02:00
4549d746c3
app: add storage commands/queries + missing in-memory test repos
2026-05-31 05:11:02 +02:00
fa36bb8c0e
refactor: restructure application to CQRS, update api-types + presentation
...
- application: replace flat use_cases/ with identity/{commands,queries}/ and organization/commands/
- each use case now split into Command/Query struct + Handler struct
- api-types: add username to RegisterRequest/UserResponse, add CreateAlbumRequest/AlbumResponse
- presentation: update state, handlers, factory to use new handler types
- tests: restructured to match CQRS module layout, added get_profile tests
2026-05-31 05:00:34 +02:00
d62d8157a8
refactor: move template use cases into identity module, clean up application structure
2026-05-31 04:49:55 +02:00
de93373b43
refactor: restructure domain crate by bounded context
2026-05-31 04:44:48 +02:00
2b62d1ec81
fix: resolve clippy warnings in share_link, invite_code, asset_stack
2026-05-31 04:27:03 +02:00
51fa62fdef
app: add core use cases (RegisterUser, CreateAlbum) with TDD
2026-05-31 04:25:47 +02:00
294626db72
app: add in-memory test fakes for all critical ports
2026-05-31 04:25:40 +02:00
b67e595280
domain: add Processing entities and ports (Job, JobBatch, Plugin, Pipeline)
2026-05-31 03:35:41 +02:00