Commit graph

838 commits

Author SHA1 Message Date
Ömer Duran
4b33d3bd1a
PWA: Add maskable icons, lang/dir, and install screenshots #5696 2026-06-30 19:53:52 +02:00
Michael Mayer
a2e8592dd5 Auth: Localize login/session/OIDC error responses via messageId #5699 #5682 2026-06-27 11:34:54 +00:00
Cathie Integra
3f096a592b Links: Use canonical trailing-slash form for website URLs 2026-06-24 17:20:30 +02:00
Michael Mayer
c5658a053d API: Align i18n.Response Go field names with JSON keys #5682
Rename the Response struct fields to match their json tags (Err to Error, Msg to Message) and the Error() method to ErrorString() to avoid a field/method clash; update callers and tests. JSON wire format is unchanged.
2026-06-23 12:42:57 +00:00
Michael Mayer
fd8448d6de UX: Name message fields messageId/messageParams in i18n payloads #5682
Rename the i18n notification/error envelope fields from id/params to messageId/messageParams across the WS notify Data and the i18n.Response body, so they are unambiguous next to session, log, and correlation ids. Updates the frontend consumers (notify.vue, api.js), tests, swagger, and the event/locales READMEs.
2026-06-23 12:22:30 +00:00
Michael Mayer
f5373ccec6 UX: Render backend error responses in the current UI locale #5682
The i18n.Response envelope now carries the untranslated source id and params; the frontend (api.js) renders them via Tp in the user's UI locale, with the server-rendered error/message as the instance-locale fallback. Common not-found/conflict/bad-request handlers were switched from hardcoded literals and raw err.Error() to Abort/i18n.Message helpers, so they benefit automatically.
2026-06-23 12:06:49 +00:00
Michael Mayer
49c6535c81 Storage: Fix duf path lookup to resolve the containing mount #5683
FindByPath now resolves a path's containing filesystem by longest-prefix match against the raw mount list, instead of the df-style display filter that hides overlay/pseudo roots. This fixes PathInfo/Free/StorageLow for paths like "/" whose mount is hidden (e.g. a container overlay root), which previously resolved to an arbitrary masked tmpfs.
2026-06-23 11:18:13 +00:00
Michael Mayer
9ecdb0ae26 UX: Render backend notifications in the current UI locale #5682
Backend *Msg helpers now publish the untranslated message id and params alongside the rendered fallback; the frontend translates the id and applies a positional-placeholder shim so notifications render in each user's UI language.
2026-06-23 09:06:46 +00:00
Michael Mayer
74a95fc748 ACL: Generate CLI role & auth-provider help from role tables #5667
Add pkg/txt.JoinOr plus acl.RolesCliUsageString and UserRoleUsageFor so the users/clients --role and --auth help derive from the role tables instead of hand-maintained literals, filtering the app/uploader aliases and visitor. Each edition lists its own roles; federatable contexts (LDAP, OIDC group-role, cluster grants) use ClusterInstanceRoles.
2026-06-16 11:57:44 +02:00
Michael Mayer
c45ee6e54f Auth: Revoke derived sessions & gate app passwords by login state #5647
- Add scoped session revocation (RevokeSessions / RevokeDerivedSessions)
  shared by the user-update API and the CLI: a privilege-level change now
  revokes logins and app-password-derived sessions while keeping the app
  passwords themselves, so configured devices keep working.
- Identify app-password-derived sessions by auth_method "session".
- Deny app passwords on the REST API when the account cannot log in
  (DenyLogIn); WebDAV access stays governed by CanUseWebDAV.
- Reduce the WebDAV auth cache expiration to one minute.
- Add IsSystemOrInvalid; allow deleting the initial admin account and skip
  re-initializing a deactivated or deleted admin in InitAccount.
- Move session-revocation scopes to pkg/authn and extend tests.
2026-06-15 23:41:01 +00:00
Michael Mayer
bc327016ad Metadata: Add Camera Make and Model updates via CLI & API #5663 #5656
Mirror the lens make/model editing surface for cameras: entity UpdateMakeModel/SaveForm, form validation, query, search, the GET/PUT /api/v1/cameras endpoints, and the cameras CLI command, plus a cameras ACL resource and scope.

Also tidy the lens surface for parity: self-validating SaveForm, empty make/model guard, X-Count search header, service-role grant, the empty-id/slug docs, and order cameras before lenses everywhere.
2026-06-15 09:25:44 +00:00
Keith Martin
c3eda657fd
Metadata: Add Lens Make and Model updates via CLI & API #5644 #5656
Adds the ability to override a lens's Make/Model (e.g. fixing Pentax lenses that ExifTool decodes as `4 38`) via a new photoprism lenses update CLI command and a `PUT /api/v1/lenses/:id endpoint`, plus a `GET /api/v1/lenses` search endpoint, a new lenses ACL resource, and an lenses ls list command.
2026-06-15 09:37:32 +02:00
Michael Mayer
9f9fce0976 Tests: Update stale portal paths after frontend URI change 2026-06-12 19:56:38 +00:00
Michael Mayer
487b0c0ec3 Http: Recognize additional reserved IPv4 ranges 2026-06-11 09:37:40 +00:00
Michael Mayer
71aeb26137 OIDC: Store email only when the token marks it verified 2026-06-10 00:04:26 +00:00
Michael Mayer
b88429f557 Cluster: Compact code comments in the OIDC follow-up changes 2026-06-09 10:20:17 +00:00
Michael Mayer
872af5d8fd Cluster: Add cluster OIDC, branded authorize errors, and sign-out
Implements three OIDC follow-ups from specs/portal/cluster-oidc.md:

- Zero-touch instance OIDC RP config: PHOTOPRISM_CLUSTER_OIDC derives the
  OIDC RP client_id/secret from the node client credentials and defaults
  the issuer to the instance's own origin (scheme.OriginURL), so a single
  boot reaches a working Portal login. Explicit oidc-client/secret/uri win.
- Human-friendly authorize errors: redirectable errors 302 back to the
  validated redirect_uri; non-redirectable ones content-negotiate a
  branded HTML page (oauth-error.gohtml) vs JSON via shared helpers in
  internal/api/oauth_error.go. The RP callback surfaces inbound ?error=.
- Cluster-wide Sign-Out (Tier 2): session.logoutEverywhere fans out a
  best-effort DELETE per peer session, clears their storage, then signs
  out locally; session-delete clears the OP cookie on any shared-domain
  OIDC-against-Portal node (OIDCSessionCookieClearPath).
2026-06-09 09:39:01 +00:00
Michael Mayer
51c4eed71e Auth: Serve Portal OIDC OP endpoints under /api/v1/oauth/* #4368 #4369
Move the Portal OIDC OP authorize/token/userinfo onto the shared CE
/api/v1/oauth/* routes (the reserved #4368/#4369 stubs) and drop the parallel
root /oauth/* set.

- Add overridable handler hooks (oauth_handlers.go) mirroring
  server.WebDAVHandler; OAuthToken delegates only the authorization_code grant
  (gated on a form content type) and keeps client_credentials/password/session
  unchanged; userinfo now serves GET and POST.
- Scope the OP session cookie to /api/v1/oauth (base-path aware) so the browser
  still sends it to the moved authorize endpoint instead of looping.
- Stop the proxy re-scoping instance->Portal authorize redirects
  (portalRootPathPrefixes -> /api/v1/oauth/).
- Point discovery at issuer + /api/v1/oauth/*; fix the JWKS URL base-path
  doubling for sub-path deployments (also in CE openid.go).
2026-06-03 15:28:05 +00:00
Michael Mayer
717b21c5ec Backend: Call clip.Bytes directly and drop the txt.ClipBytes facade #5638 2026-06-03 09:50:56 +00:00
Michael Mayer
deb3723fd6 Albums: Clip album_path to the column byte budget on write #5615 2026-06-03 07:53:16 +00:00
Michael Mayer
a7d098548e Video: Document codec detection and chunk scans in package docs #5617 2026-06-01 18:44:21 +00:00
Michael Mayer
23802d4fd9 Video: Validate ISO BMFF sample-entry framing in codec head scan #5617 2026-06-01 18:12:31 +00:00
Michael Mayer
b7d637d5dd Storage: Map out-of-space writes to the insufficient-storage error #5613 2026-05-30 22:23:14 +00:00
Michael Mayer
a57ca16bb3 Video: Name the matched exclude entry and apply it to transcode #5617
Add Formats.Match to report which exclude-list entry matched, and use it in the
remux, download, transcode, and index skip messages instead of always printing
the codec (which could be empty or differ from the entry the user configured).

Also apply the exclude list when building "videos transcode" plans so excluded
files are skipped during dry runs and real runs, and report exclusion clearly
when no preview image can be generated for an excluded video.
2026-05-30 21:50:55 +00:00
Michael Mayer
c691b8cbe0 Video: Probe video codecs from the file head in a single scan #5617
Combine the HEVC and MagicYUV sample-entry scans into one pass over the file
head, so the probe reads the head only once when go-mp4 does not report the
codec directly. Rename the scan limit to HeadScanLimit and group the
sample-entry chunk sets in brands.go.
2026-05-30 21:37:14 +00:00
Michael Mayer
9e5b94189e Video: Recognize MagicYUV in the probe and exclude the VFW wrapper #5617
Recognize MagicYUV sample entries in the built-in go-mp4 probe and consider the
probe's codec in addition to the metadata codec when applying the FFmpeg exclude
list, so a video can be matched even when one detector misses it. Rename the
shared sample-entry scan limit to CodecHeadScanLimit.

Also exclude the Matroska Video for Windows wrapper (v_ms/vfw/fourcc) by default,
since its actual codec is not exposed by metadata and the format is uncommon.
2026-05-30 21:29:12 +00:00
Michael Mayer
803ee6ce69 Video: Improve codec detection and exclude-list alias matching #5617
Read the video codec from the AVI VideoCodec metadata tag instead of falling
back to the container name, so codec-based entries on the FFmpeg exclude list
are applied during indexing.

Match exclude-list codecs by their canonical alias so the original name
reported by a metadata tool (e.g. m8ra), the human-readable name (magicyuv),
and the canonical FourCC (magy) are treated as equivalent. Add h264/h265
aliases and make magy the canonical MagicYUV codec name.
2026-05-30 21:02:19 +00:00
Michael Mayer
924c80f78c FS: Prefer relative over absolute paths in directory detection #5629 2026-05-30 16:10:46 +00:00
Michael Mayer
b04233f2f4 Backend: Use strings.SplitSeq for split-and-iterate loops #5599
Replace strings.Split with the lazy strings.SplitSeq iterator (Go 1.23+)
in loops that only range over the tokens, avoiding the intermediate
slice allocation. The yielded substrings are identical to Split, so
behavior is unchanged. Applies the suggestion from #5599 to its three
sites (CIDR parsing, WebDAV hidden-path, overlay path checks) and
extends it to two more split-and-iterate loops in the server compression
negotiator and MCP config-options builder for consistency. The slice
returning splitPath helper is intentionally left on strings.Split.
2026-05-30 09:49:47 +00:00
Michael Mayer
03129c9129 Vector: Reorganize package into topic-based files #4669
Split the catch-all values.go into one file per concept, each mirrored
by its test: distance.go, norm.go, stats.go, product.go, centroid.go,
plus the mean methods folded into mean.go and Copy/Dim/Sum into
vector.go. Remove values.go, values_test.go, and values_more_test.go so
functionality and tests live where developers expect them.

Hoist the two 512-dimensional face embeddings shared by the distance,
norm, and cosine tests into fixtures_test.go, removing the previous
triplication, and decompose the monolithic TestVector into per-concept
tests. Close pre-existing coverage gaps in the integer converters and
the GeometricMean/HarmonicMean method wrappers, bringing the package to
100% statement coverage. Pure code movement; no behavior change.
2026-05-30 09:44:03 +00:00
Michael Mayer
40b170ecb0 Vector: Add Normalize, Normalized, and Centroid helpers #4669
Prepare pkg/vector for a future refactoring of internal/ai/face without
touching the face package yet. Normalize scales a vector to unit length
(L2) in place and leaves a zero vector unchanged; Normalized returns a
normalized copy. Centroid returns the element-wise mean of a set of
vectors, ignoring entries whose length differs from the first and
averaging over the vectors actually included. Together with the existing
EuclideanDist, EuclideanNorm, and NewVector helpers these cover the math
currently hand-rolled in the face embeddings code. Fully covered by
tests (100% of the new functions).
2026-05-30 09:17:00 +00:00
Michael Mayer
a685b3f051 Vector: Add package doc comment and license header #4669
The root file of the vector package was missing the standard package
description and AGPL header required by the Go style rules. The vendored
alg subpackage keeps its own MIT license and is left unchanged.
2026-05-30 07:51:06 +00:00
Michael Mayer
e678231f34 Vector: Fix cosine distance, geometric mean, and copy semantics #4669
CosineDist now returns a true cosine distance (1 - similarity), so
identical vectors yield 0; the previous similarity behavior moves to a
new CosineSimilarity function/method. GeometricMean no longer treats a
zero element as an uninitialized accumulator: zeros now correctly yield
0 and negative inputs return NaN. NewVector copies float64 and Vector
inputs so the result is independent of the source slice, matching the
other converters. Norm uses absolute values so odd powers (L1) stay
well-defined for negative components, variance is simplified, and the
numeric error paths return NaN instead of the Epsilon sentinel. Add
focused tests for the mean, distance, norm, and copy helpers.
2026-05-30 07:51:06 +00:00
Michael Mayer
5511814dde Faces: Harden DBSCAN worker partitioning against zero step #5628
Extract the per-worker scan range size into partitionSize() and floor
it at 1 so the nearest() dispatch loop always advances and terminates,
even if the worker count exceeds the number of data points. The size
based numWorkers buckets keep points >= workers today, so this is a
defensive guard against future tuning. Add focused tests for the helper
and a high-worker end-to-end case.
2026-05-30 07:40:09 +00:00
Michael Mayer
418a018f6e Faces: Remove unused OPTICS clusterer from pkg/vector/alg #5628
The vendored OPTICS implementation was never invoked: face clustering
uses DBSCAN (alg.DBSCANWithProgress), and OPTICS() had no callers or
tests. Removing it also drops a latent no-op loop (PVS-Studio V8016)
in extract(). Drop the now-orphaned errZeroXi and update the rangeJob
comment and README to match. See discussion #5628.
2026-05-30 07:15:18 +00:00
Michael Mayer
3b38a4a2a5 Config: Improve low storage detection #5613 2026-05-29 01:45:04 +02:00
Michael Mayer
80ac70cc6a FFmpeg: Use atomic.Pointer for thread-safe Exclude list access #5617 2026-05-26 15:57:59 +00:00
Michael Mayer
d2e8c7afad Config: Add option to exclude video formats from FFmpeg #5617 2026-05-26 15:36:42 +00:00
Michael Mayer
bf9bbfb5fb Folders: Fix slug collision on long ASCII paths #5614 #5615 2026-05-26 02:34:48 +00:00
Michael Mayer
696c3cd4da Storage: Gate convert/backup/zip writes and centralize Err sentinels #5613 2026-05-26 01:35:06 +00:00
Michael Mayer
dd2c9e3e88 Storage: Refine low-storage logging and extract walk-error sentinels #5613 2026-05-26 00:32:35 +00:00
Michael Mayer
570a563748 Docs: Bump copyright year to 2026 in Go package headers 2026-05-25 14:39:38 +00:00
Michael Mayer
0f01057dba Index: Abort early when storage is critically low #5613 #5612 2026-05-25 14:27:21 +00:00
Michael Mayer
ef7a9db950 Cluster: Add scheme.ResolveAdvertiseURL helper for SiteUrl-aligned paths 2026-05-25 11:49:33 +00:00
Michael Mayer
32902d2c63 Cluster: Skip Location rewrite for Portal-root paths in proxy
The portal proxy was prefixing the per-instance path scope onto absolute
Location headers whose host matched the proxy, breaking instance-to-Portal
redirects like the Pro OIDC RP pointing at the Portal authorize endpoint.
Paths under /oauth/, /.well-known/, and /portal/ are now recognized as
Portal-root and left untouched.
2026-05-24 12:28:02 +00:00
Michael Mayer
8a28f605df Config: Refactor database driver parsing and detection #47 #4831 2026-05-24 12:24:10 +02:00
Michael Mayer
91d27f7e6e Docs: Update copyright year in Dockerfiles, scripts, and Go files
Signed-off-by: Michael Mayer <michael@photoprism.app>
2026-05-22 07:56:59 +02:00
Michael Mayer
12dbe82d36 Video: Tag HEVC remux output hvc1 and unify MP4 chunk scans #5593
Signed-off-by: Michael Mayer <michael@photoprism.app>
2026-05-20 17:57:40 +02:00
Michael Mayer
1b8ea51a2f Config: Normalize Site/CDN base URLs to strip default ports #5590
Signed-off-by: Michael Mayer <michael@photoprism.app>
2026-05-20 16:59:53 +02:00
Michael Mayer
2fc71540a9 Config: Strip default HTTP and HTTPS ports from the site URL #5590
Signed-off-by: Michael Mayer <michael@photoprism.app>
2026-05-19 01:05:23 +02:00