mirror of
https://github.com/photoprism/photoprism.git
synced 2026-01-23 02:24:24 +00:00
Merge remote-tracking branch 'origin/develop' into PostgreSQL
This commit is contained in:
commit
b86ddf0ec6
64 changed files with 1258 additions and 486 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -79,4 +79,5 @@ Thumbs.db
|
|||
.c9revisions
|
||||
.settings
|
||||
.swp
|
||||
AGENTS.md
|
||||
.vscode
|
||||
|
|
|
|||
6
Makefile
6
Makefile
|
|
@ -119,6 +119,8 @@ devtools: install-go dep-npm
|
|||
.SILENT: help;
|
||||
logs:
|
||||
$(DOCKER_COMPOSE) logs -f
|
||||
down:
|
||||
$(DOCKER_COMPOSE) --profile=all down --remove-orphans
|
||||
help:
|
||||
@echo "For build instructions, visit <https://docs.photoprism.app/developer-guide/>."
|
||||
docs: swag
|
||||
|
|
@ -797,6 +799,10 @@ docker-release-plucky:
|
|||
docker pull --platform=arm64 photoprism/develop:plucky
|
||||
docker pull --platform=arm64 photoprism/develop:plucky-slim
|
||||
scripts/docker/buildx-multi.sh photoprism linux/amd64,linux/arm64 ce /plucky
|
||||
start-traefik:
|
||||
$(DOCKER_COMPOSE) up -d --wait traefik
|
||||
stop-traefik:
|
||||
$(DOCKER_COMPOSE) down traefik
|
||||
start-local:
|
||||
$(DOCKER_COMPOSE) -f compose.local.yaml up -d --wait
|
||||
stop-local:
|
||||
|
|
|
|||
|
|
@ -58,6 +58,8 @@ services:
|
|||
PHOTOPRISM_DISABLE_TENSORFLOW: "false" # Don't use TensorFlow for image classification
|
||||
PHOTOPRISM_DETECT_NSFW: "false" # Flag photos as private that MAY be offensive (requires TensorFlow)
|
||||
PHOTOPRISM_UPLOAD_NSFW: "false" # Allows uploads that may be offensive
|
||||
PHOTOPRISM_UPLOAD_ALLOW: "" # restricts uploads to these file types (comma-separated list of EXTENSIONS; leave blank to allow all)
|
||||
PHOTOPRISM_UPLOAD_ARCHIVES: "true" # allows upload of zip archives (will be extracted before import)
|
||||
PHOTOPRISM_DARKTABLE_PRESETS: "false" # Enables Darktable presets and disables concurrent RAW conversion
|
||||
PHOTOPRISM_THUMB_FILTER: "lanczos" # Resample filter, best to worst: blackman, lanczos, cubic, linear
|
||||
PHOTOPRISM_THUMB_UNCACHED: "true" # Enables on-demand thumbnail rendering (high memory and cpu usage)
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ services:
|
|||
- "traefik:dummy-webdav.localssl.dev"
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.docker.network=photoprism"
|
||||
- "traefik.http.services.photoprism.loadbalancer.server.port=2342"
|
||||
- "traefik.http.services.photoprism.loadbalancer.server.scheme=http"
|
||||
- "traefik.http.routers.photoprism.entrypoints=websecure"
|
||||
|
|
@ -114,6 +115,8 @@ services:
|
|||
PHOTOPRISM_RAW_PRESETS: "false" # enables applying user presets when converting RAW images (reduces performance)
|
||||
PHOTOPRISM_DETECT_NSFW: "false" # automatically flags photos as private that MAY be offensive (requires TensorFlow)
|
||||
PHOTOPRISM_UPLOAD_NSFW: "false" # allows uploads that MAY be offensive (no effect without TensorFlow)
|
||||
PHOTOPRISM_UPLOAD_ALLOW: "" # restricts uploads to these file types (comma-separated list of EXTENSIONS; leave blank to allow all)
|
||||
PHOTOPRISM_UPLOAD_ARCHIVES: "true" # allows upload of zip archives (will be extracted before import)
|
||||
PHOTOPRISM_THUMB_LIBRARY: "auto" # image processing library to be used for generating thumbnails (auto, imaging, vips)
|
||||
PHOTOPRISM_THUMB_FILTER: "auto" # downscaling filter (imaging best to worst: blackman, lanczos, cubic, linear, nearest)
|
||||
PHOTOPRISM_THUMB_UNCACHED: "true" # enables on-demand thumbnail rendering (high memory and cpu usage)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ services:
|
|||
- "2344:2342" # HTTP port (host:container)
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.docker.network=photoprism"
|
||||
- "traefik.http.services.latest.loadbalancer.server.port=2342"
|
||||
- "traefik.http.routers.latest.entrypoints=websecure"
|
||||
- "traefik.http.routers.latest.rule=Host(`latest.localssl.dev`)"
|
||||
|
|
@ -50,6 +51,8 @@ services:
|
|||
PHOTOPRISM_DISABLE_TENSORFLOW: "false" # disables all features depending on TensorFlow
|
||||
PHOTOPRISM_DETECT_NSFW: "false" # automatically flags photos as private that MAY be offensive (requires TensorFlow)
|
||||
PHOTOPRISM_UPLOAD_NSFW: "false" # allows uploads that MAY be offensive (no effect without TensorFlow)
|
||||
PHOTOPRISM_UPLOAD_ALLOW: "" # restricts uploads to these file types (comma-separated list of EXTENSIONS; leave blank to allow all)
|
||||
PHOTOPRISM_UPLOAD_ARCHIVES: "true" # allows upload of zip archives (will be extracted before import)
|
||||
PHOTOPRISM_RAW_PRESETS: "false" # enables applying user presets when converting RAW images (reduces performance)
|
||||
PHOTOPRISM_THUMB_FILTER: "lanczos" # resample filter, best to worst: blackman, lanczos, cubic, linear
|
||||
PHOTOPRISM_THUMB_UNCACHED: "true" # enables on-demand thumbnail rendering (high memory and cpu usage)
|
||||
|
|
@ -63,7 +66,7 @@ services:
|
|||
- "./storage:/photoprism/storage"
|
||||
- "./storage/originals:/photoprism/originals"
|
||||
|
||||
## Join shared "photoprism-develop" network
|
||||
## Join shared "photoprism" network
|
||||
networks:
|
||||
default:
|
||||
name: photoprism
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ services:
|
|||
- "2345:2342" # HTTP port (host:container)
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.docker.network=photoprism"
|
||||
- "traefik.http.services.latest.loadbalancer.server.port=2342"
|
||||
- "traefik.http.routers.latest.entrypoints=websecure"
|
||||
- "traefik.http.routers.latest.rule=Host(`local.localssl.dev`)"
|
||||
|
|
@ -51,6 +52,8 @@ services:
|
|||
PHOTOPRISM_DISABLE_TENSORFLOW: "false" # disables all features depending on TensorFlow
|
||||
PHOTOPRISM_DETECT_NSFW: "false" # automatically flags photos as private that MAY be offensive (requires TensorFlow)
|
||||
PHOTOPRISM_UPLOAD_NSFW: "false" # allows uploads that MAY be offensive (no effect without TensorFlow)
|
||||
PHOTOPRISM_UPLOAD_ALLOW: "" # restricts uploads to these file types (comma-separated list of EXTENSIONS; leave blank to allow all)
|
||||
PHOTOPRISM_UPLOAD_ARCHIVES: "true" # allows upload of zip archives (will be extracted before import)
|
||||
PHOTOPRISM_RAW_PRESETS: "false" # enables applying user presets when converting RAW images (reduces performance)
|
||||
PHOTOPRISM_THUMB_FILTER: "lanczos" # resample filter, best to worst: blackman, lanczos, cubic, linear
|
||||
PHOTOPRISM_THUMB_UNCACHED: "true" # enables on-demand thumbnail rendering (high memory and cpu usage)
|
||||
|
|
@ -70,7 +73,7 @@ services:
|
|||
volumes:
|
||||
- "./storage:/photoprism/storage"
|
||||
|
||||
## Join shared "photoprism-develop" network
|
||||
## Join shared "photoprism" network
|
||||
networks:
|
||||
default:
|
||||
name: photoprism
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ services:
|
|||
MYSQL_PASSWORD: "photoprism"
|
||||
MYSQL_ROOT_PASSWORD: "photoprism"
|
||||
|
||||
## Join shared "photoprism-develop" network
|
||||
## Join shared "photoprism" network
|
||||
networks:
|
||||
default:
|
||||
name: photoprism
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ services:
|
|||
MYSQL_PASSWORD: "photoprism"
|
||||
MYSQL_ROOT_PASSWORD: "photoprism"
|
||||
|
||||
## Join shared "photoprism-develop" network
|
||||
## Join shared "photoprism" network
|
||||
networks:
|
||||
default:
|
||||
name: photoprism
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ services:
|
|||
- "traefik:dummy-webdav.localssl.dev"
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.docker.network=photoprism"
|
||||
- "traefik.http.services.photoprism.loadbalancer.server.port=2342"
|
||||
- "traefik.http.services.photoprism.loadbalancer.server.scheme=http"
|
||||
- "traefik.http.routers.photoprism.entrypoints=websecure"
|
||||
|
|
@ -117,6 +118,8 @@ services:
|
|||
PHOTOPRISM_RAW_PRESETS: "false" # enables applying user presets when converting RAW images (reduces performance)
|
||||
PHOTOPRISM_DETECT_NSFW: "false" # automatically flags photos as private that MAY be offensive (requires TensorFlow)
|
||||
PHOTOPRISM_UPLOAD_NSFW: "false" # allows uploads that MAY be offensive (no effect without TensorFlow)
|
||||
PHOTOPRISM_UPLOAD_ALLOW: "" # restricts uploads to these file types (comma-separated list of EXTENSIONS; leave blank to allow all)
|
||||
PHOTOPRISM_UPLOAD_ARCHIVES: "true" # allows upload of zip archives (will be extracted before import)
|
||||
PHOTOPRISM_THUMB_LIBRARY: "auto" # image processing library to be used for generating thumbnails (auto, imaging, vips)
|
||||
PHOTOPRISM_THUMB_FILTER: "auto" # downscaling filter (imaging best to worst: blackman, lanczos, cubic, linear, nearest)
|
||||
PHOTOPRISM_THUMB_UNCACHED: "true" # enables on-demand thumbnail rendering (high memory and cpu usage)
|
||||
|
|
|
|||
|
|
@ -122,6 +122,8 @@ services:
|
|||
PHOTOPRISM_RAW_PRESETS: "false" # enables applying user presets when converting RAW images (reduces performance)
|
||||
PHOTOPRISM_DETECT_NSFW: "false" # automatically flags photos as private that MAY be offensive (requires TensorFlow)
|
||||
PHOTOPRISM_UPLOAD_NSFW: "false" # allows uploads that MAY be offensive (no effect without TensorFlow)
|
||||
PHOTOPRISM_UPLOAD_ALLOW: "" # restricts uploads to these file types (comma-separated list of EXTENSIONS; leave blank to allow all)
|
||||
PHOTOPRISM_UPLOAD_ARCHIVES: "true" # allows upload of zip archives (will be extracted before import)
|
||||
PHOTOPRISM_THUMB_LIBRARY: "auto" # image processing library to be used for generating thumbnails (auto, imaging, vips)
|
||||
PHOTOPRISM_THUMB_FILTER: "auto" # downscaling filter (imaging best to worst: blackman, lanczos, cubic, linear, nearest)
|
||||
PHOTOPRISM_THUMB_UNCACHED: "true" # enables on-demand thumbnail rendering (high memory and cpu usage)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ services:
|
|||
- "2344:2342" # HTTP port (host:container)
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.docker.network=photoprism"
|
||||
- "traefik.http.services.preview.loadbalancer.server.port=2342"
|
||||
- "traefik.http.routers.preview.entrypoints=websecure"
|
||||
- "traefik.http.routers.preview.rule=Host(`preview.localssl.dev`)"
|
||||
|
|
@ -50,6 +51,8 @@ services:
|
|||
PHOTOPRISM_DISABLE_TENSORFLOW: "false" # disables all features depending on TensorFlow
|
||||
PHOTOPRISM_DETECT_NSFW: "false" # automatically flags photos as private that MAY be offensive (requires TensorFlow)
|
||||
PHOTOPRISM_UPLOAD_NSFW: "false" # allows uploads that MAY be offensive (no effect without TensorFlow)
|
||||
PHOTOPRISM_UPLOAD_ALLOW: "" # restricts uploads to these file types (comma-separated list of EXTENSIONS; leave blank to allow all)
|
||||
PHOTOPRISM_UPLOAD_ARCHIVES: "true" # allows upload of zip archives (will be extracted before import)
|
||||
PHOTOPRISM_RAW_PRESETS: "false" # enables applying user presets when converting RAW images (reduces performance)
|
||||
PHOTOPRISM_THUMB_FILTER: "lanczos" # resample filter, best to worst: blackman, lanczos, cubic, linear
|
||||
PHOTOPRISM_THUMB_UNCACHED: "true" # enables on-demand thumbnail rendering (high memory and cpu usage)
|
||||
|
|
@ -63,7 +66,7 @@ services:
|
|||
- "./storage:/photoprism/storage"
|
||||
- "./storage/originals:/photoprism/originals"
|
||||
|
||||
## Join shared "photoprism-develop" network
|
||||
## Join shared "photoprism" network
|
||||
networks:
|
||||
default:
|
||||
name: photoprism
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ services:
|
|||
- "traefik:dummy-webdav.localssl.dev"
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.docker.network=photoprism"
|
||||
- "traefik.http.services.photoprism.loadbalancer.server.port=2342"
|
||||
- "traefik.http.services.photoprism.loadbalancer.server.scheme=http"
|
||||
- "traefik.http.routers.photoprism.entrypoints=websecure"
|
||||
|
|
@ -122,6 +123,8 @@ services:
|
|||
PHOTOPRISM_RAW_PRESETS: "false" # enables applying user presets when converting RAW images (reduces performance)
|
||||
PHOTOPRISM_DETECT_NSFW: "false" # automatically flags photos as private that MAY be offensive (requires TensorFlow)
|
||||
PHOTOPRISM_UPLOAD_NSFW: "false" # allows uploads that MAY be offensive (no effect without TensorFlow)
|
||||
PHOTOPRISM_UPLOAD_ALLOW: "" # restricts uploads to these file types (comma-separated list of EXTENSIONS; leave blank to allow all)
|
||||
PHOTOPRISM_UPLOAD_ARCHIVES: "true" # allows upload of zip archives (will be extracted before import)
|
||||
PHOTOPRISM_THUMB_LIBRARY: "auto" # image processing library to be used for generating thumbnails (auto, imaging, vips)
|
||||
PHOTOPRISM_THUMB_FILTER: "auto" # downscaling filter (imaging best to worst: blackman, lanczos, cubic, linear, nearest)
|
||||
PHOTOPRISM_THUMB_UNCACHED: "true" # enables on-demand thumbnail rendering (high memory and cpu usage)
|
||||
|
|
|
|||
684
frontend/package-lock.json
generated
684
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -44,7 +44,7 @@
|
|||
"@mdi/font": "^7.4.47",
|
||||
"@testing-library/jest-dom": "^6.8.0",
|
||||
"@testing-library/react": "^16.3.0",
|
||||
"@vitejs/plugin-react": "^5.0.1",
|
||||
"@vitejs/plugin-react": "^5.0.2",
|
||||
"@vitejs/plugin-vue": "^6.0.1",
|
||||
"@vitest/browser": "^3.2.4",
|
||||
"@vitest/coverage-v8": "^3.2.4",
|
||||
|
|
@ -59,15 +59,15 @@
|
|||
"babel-plugin-istanbul": "^7.0.0",
|
||||
"babel-plugin-polyfill-corejs3": "^0.13.0",
|
||||
"browserslist": "^4.25.3",
|
||||
"chai": "^5.3.2",
|
||||
"chai": "^5.3.3",
|
||||
"cheerio": "1.0.0-rc.12",
|
||||
"chrome-finder": "^1.0.7",
|
||||
"core-js": "^3.45.1",
|
||||
"cross-env": "^10.0.0",
|
||||
"css-loader": "^7.1.2",
|
||||
"cssnano": "^7.1.0",
|
||||
"cssnano": "^7.1.1",
|
||||
"easygettext": "^2.17.0",
|
||||
"eslint": "^9.33.0",
|
||||
"eslint": "^9.34.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-formatter-pretty": "^6.0.1",
|
||||
"eslint-plugin-html": "^8.1.3",
|
||||
|
|
@ -82,7 +82,7 @@
|
|||
"file-saver": "^2.0.5",
|
||||
"floating-vue": "^5.2.2",
|
||||
"globals": "^16.3.0",
|
||||
"hls.js": "^1.6.10",
|
||||
"hls.js": "^1.6.11",
|
||||
"i": "^0.3.7",
|
||||
"jsdom": "^26.1.0",
|
||||
"karma": "^6.4.4",
|
||||
|
|
@ -93,7 +93,7 @@
|
|||
"karma-verbose-reporter": "^0.0.8",
|
||||
"karma-webpack": "^5.0.1",
|
||||
"luxon": "^3.7.1",
|
||||
"maplibre-gl": "^5.6.2",
|
||||
"maplibre-gl": "^5.7.0",
|
||||
"memoize-one": "^6.0.0",
|
||||
"mini-css-extract-plugin": "^2.9.4",
|
||||
"minimist": "^1.2.8",
|
||||
|
|
@ -105,7 +105,7 @@
|
|||
"postcss": "^8.5.6",
|
||||
"postcss-import": "^16.1.1",
|
||||
"postcss-loader": "^8.1.1",
|
||||
"postcss-preset-env": "^10.2.4",
|
||||
"postcss-preset-env": "^10.3.1",
|
||||
"postcss-reporter": "^7.1.0",
|
||||
"postcss-url": "^10.1.3",
|
||||
"prettier": "^3.6.2",
|
||||
|
|
@ -113,7 +113,7 @@
|
|||
"regenerator-runtime": "^0.14.1",
|
||||
"resolve-url-loader": "^5.0.0",
|
||||
"sanitize-html": "^2.17.0",
|
||||
"sass": "^1.90.0",
|
||||
"sass": "^1.91.0",
|
||||
"sass-loader": "^16.0.5",
|
||||
"server": "^1.0.42",
|
||||
"sockette": "^2.0.6",
|
||||
|
|
@ -133,7 +133,7 @@
|
|||
"vue-sanitize-directive": "^0.2.1",
|
||||
"vue-style-loader": "^4.1.3",
|
||||
"vue3-gettext": "^2.4.0",
|
||||
"vuetify": "^3.9.5",
|
||||
"vuetify": "^3.9.6",
|
||||
"webpack": "^5.101.3",
|
||||
"webpack-bundle-analyzer": "^4.10.2",
|
||||
"webpack-cli": "^6.0.1",
|
||||
|
|
|
|||
6
go.mod
6
go.mod
|
|
@ -14,7 +14,7 @@ require (
|
|||
github.com/esimov/pigo v1.4.6
|
||||
github.com/gin-contrib/gzip v1.2.3
|
||||
github.com/gin-gonic/gin v1.10.1
|
||||
github.com/golang/geo v0.0.0-20250821133510-ecfc33a939ac
|
||||
github.com/golang/geo v0.0.0-20250825151631-54d70cc7cb31
|
||||
github.com/google/open-location-code/go v0.0.0-20250620134813-83986da0156b
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/gosimple/slug v1.15.0
|
||||
|
|
@ -34,7 +34,7 @@ require (
|
|||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58
|
||||
github.com/sevlyar/go-daemon v0.1.6
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/tidwall/gjson v1.18.0
|
||||
github.com/ulule/deepcopier v0.0.0-20200430083143-45decc6639b6
|
||||
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
|
||||
|
|
@ -55,7 +55,7 @@ require github.com/google/uuid v1.6.0
|
|||
|
||||
require github.com/chzyer/readline v1.5.1 // indirect
|
||||
|
||||
require github.com/gabriel-vasile/mimetype v1.4.9
|
||||
require github.com/gabriel-vasile/mimetype v1.4.10
|
||||
|
||||
require (
|
||||
golang.org/x/sync v0.16.0
|
||||
|
|
|
|||
12
go.sum
12
go.sum
|
|
@ -113,8 +113,8 @@ github.com/esimov/pigo v1.4.6/go.mod h1:uqj9Y3+3IRYhFK071rxz1QYq0ePhA6+R9jrUZavi
|
|||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
|
||||
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
|
||||
github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||
github.com/gin-contrib/gzip v1.2.3 h1:dAhT722RuEG330ce2agAs75z7yB+NKvX/ZM1r8w0u2U=
|
||||
github.com/gin-contrib/gzip v1.2.3/go.mod h1:ad72i4Bzmaypk8M762gNXa2wkxxjbz0icRNnuLJ9a/c=
|
||||
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
|
||||
|
|
@ -172,8 +172,8 @@ github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGw
|
|||
github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
|
||||
github.com/golang/geo v0.0.0-20200319012246-673a6f80352d/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
|
||||
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
|
||||
github.com/golang/geo v0.0.0-20250821133510-ecfc33a939ac h1:gBtvd74wZT02fkOyoG651Sx2imv5lKdVvDwpUjKPFnw=
|
||||
github.com/golang/geo v0.0.0-20250821133510-ecfc33a939ac/go.mod h1:AN0OjM34c3PbjAsX+QNma1nYtJtRxl+s9MZNV7S+efw=
|
||||
github.com/golang/geo v0.0.0-20250825151631-54d70cc7cb31 h1:226lwSa0uZO7sd7QU88n43nDIqGoc+bOh0vSO3Q/byU=
|
||||
github.com/golang/geo v0.0.0-20250825151631-54d70cc7cb31/go.mod h1:AN0OjM34c3PbjAsX+QNma1nYtJtRxl+s9MZNV7S+efw=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
|
|
@ -360,8 +360,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/sunfish-shogi/bufseekio v0.0.0-20210207115823-a4185644b365/go.mod h1:dEzdXgvImkQ3WLI+0KQpmEx8T/C/ma9KeS3AfmU899I=
|
||||
github.com/sunfish-shogi/bufseekio v0.1.0 h1:zu38kFbv0KuuiwZQeuYeS02U9AM14j0pVA9xkHOCJ2A=
|
||||
github.com/sunfish-shogi/bufseekio v0.1.0/go.mod h1:dEzdXgvImkQ3WLI+0KQpmEx8T/C/ma9KeS3AfmU899I=
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ var (
|
|||
DownloadUrl = ""
|
||||
ServiceUri = ""
|
||||
ServiceKey = ""
|
||||
ServiceTimeout = 10 * time.Minute
|
||||
ServiceTimeout = 5 * time.Minute
|
||||
ServiceMethod = http.MethodPost
|
||||
ServiceFileScheme = scheme.Data
|
||||
ServiceRequestFormat = ApiFormatVision
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ func AlbumDownloadName(c *gin.Context) customize.DownloadName {
|
|||
//
|
||||
// @Summary streams the album contents as zip archive
|
||||
// @Id DownloadAlbum
|
||||
// @Tags Images, Albums
|
||||
// @Tags Albums, Download
|
||||
// @Produce application/zip
|
||||
// @Failure 403,404,500 {object} i18n.Response
|
||||
// @Success 200 {file} application/zip
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import (
|
|||
//
|
||||
// @Summary returns the request and response headers as JSON if debug mode is enabled
|
||||
// @Id Echo
|
||||
// @Tags Debug
|
||||
// @Success 200
|
||||
// @Router /api/v1/echo [get]
|
||||
func Echo(router *gin.RouterGroup) {
|
||||
|
|
|
|||
|
|
@ -11,9 +11,15 @@ import (
|
|||
"github.com/photoprism/photoprism/pkg/i18n"
|
||||
)
|
||||
|
||||
// SendFeedback sends a feedback message.
|
||||
// SendFeedback allows members to submit a feedback message to the PhotoPrism team.
|
||||
//
|
||||
// POST /api/v1/feedback
|
||||
// @Summary allows members to submit a feedback message to the PhotoPrism team
|
||||
// @Id SendFeedback
|
||||
// @Tags Admin
|
||||
// @Produce json
|
||||
// @Success 200 {object} form.Feedback
|
||||
// @Failure 400,401,403 {object} i18n.Response
|
||||
// @Router /api/v1/feedback [post]
|
||||
func SendFeedback(router *gin.RouterGroup) {
|
||||
router.POST("/feedback", func(c *gin.Context) {
|
||||
conf := get.Config()
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/collectors"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
"github.com/prometheus/common/expfmt"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/auth/acl"
|
||||
|
|
@ -17,9 +18,15 @@ import (
|
|||
"github.com/photoprism/photoprism/pkg/media/http/header"
|
||||
)
|
||||
|
||||
// GetMetrics provides a prometheus-compatible metrics endpoint for monitoring.
|
||||
// GetMetrics provides a Prometheus-compatible metrics stream for monitoring.
|
||||
//
|
||||
// GET /api/v1/metrics
|
||||
// @Summary a prometheus-compatible metrics endpoint for monitoring this instance
|
||||
// @Id GetMetrics
|
||||
// @Tags Metrics
|
||||
// @Produce event-stream
|
||||
// @Success 200 {object} []dto.MetricFamily
|
||||
// @Failure 401,403 {object} i18n.Response
|
||||
// @Router /api/v1/metrics [get]
|
||||
func GetMetrics(router *gin.RouterGroup) {
|
||||
router.GET("/metrics", func(c *gin.Context) {
|
||||
s := Auth(c, acl.ResourceMetrics, acl.AccessAll)
|
||||
|
|
@ -43,14 +50,18 @@ func GetMetrics(router *gin.RouterGroup) {
|
|||
registerCountMetrics(factory, counts)
|
||||
registerBuildInfoMetric(factory, conf.ClientPublic())
|
||||
|
||||
metrics, err := reg.Gather()
|
||||
var metrics []*dto.MetricFamily
|
||||
var err error
|
||||
|
||||
metrics, err = reg.Gather()
|
||||
|
||||
if err != nil {
|
||||
logErr("metrics", err)
|
||||
return false
|
||||
}
|
||||
|
||||
for _, metric := range metrics {
|
||||
if _, err := expfmt.MetricFamilyToText(w, metric); err != nil {
|
||||
if _, err = expfmt.MetricFamilyToText(w, metric); err != nil {
|
||||
logErr("metrics", err)
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,12 +6,14 @@ import (
|
|||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// Options returns an empty response to handle CORS preflight requests.
|
||||
// Options returns CORS headers with an empty response body.
|
||||
//
|
||||
// @Summary returns CORS headers with an empty response body
|
||||
// @Id Options
|
||||
// @Success 204
|
||||
// @Router /api/v1/{any} [options]
|
||||
// @Summary returns CORS headers with an empty response body
|
||||
// @Description A preflight request is automatically issued by a browser and in normal cases, front-end developers don't need to craft such requests themselves. It appears when request is qualified as "to be preflighted" and omitted for simple requests.
|
||||
// @Id Options
|
||||
// @Tags CORS
|
||||
// @Success 204
|
||||
// @Router /api/v1/{any} [options]
|
||||
func Options(router *gin.RouterGroup) {
|
||||
router.OPTIONS("/*any", func(c *gin.Context) {
|
||||
c.Status(http.StatusNoContent)
|
||||
|
|
|
|||
|
|
@ -6,16 +6,24 @@ import (
|
|||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/auth/acl"
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/photoprism/get"
|
||||
"github.com/photoprism/photoprism/internal/server/process"
|
||||
)
|
||||
|
||||
// StopServer initiates a server restart if the user is authorized.
|
||||
// StopServer allows authorized admins to restart the server.
|
||||
//
|
||||
// POST /api/v1/server/stop
|
||||
// @Summary allows authorized admins to restart the server
|
||||
// @Id StopServer
|
||||
// @Tags Admin
|
||||
// @Produce json
|
||||
// @Success 200 {object} config.Options
|
||||
// @Failure 401,403 {object} i18n.Response
|
||||
// @Router /api/v1/server/stop [post]
|
||||
func StopServer(router *gin.RouterGroup) {
|
||||
router.POST("/server/stop", func(c *gin.Context) {
|
||||
s := Auth(c, acl.ResourceConfig, acl.ActionManage)
|
||||
|
||||
conf := get.Config()
|
||||
|
||||
// Abort if permission is not granted.
|
||||
|
|
@ -24,11 +32,14 @@ func StopServer(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
var options *config.Options
|
||||
options = conf.Options()
|
||||
|
||||
// Trigger restart.
|
||||
//
|
||||
// Note that this requires an entrypoint script or other process to
|
||||
// spawns a new instance when the server exists with status code 1.
|
||||
c.JSON(http.StatusOK, conf.Options())
|
||||
c.JSON(http.StatusOK, options)
|
||||
process.Restart()
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ import (
|
|||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// GetStatus reports if the server is operational.
|
||||
// GetStatus responds with status code 200 if the server is operational.
|
||||
//
|
||||
// @Summary reports if the server is operational
|
||||
// @Summary responds with status code 200 if the server is operational
|
||||
// @Id GetStatus
|
||||
// @Tags Server
|
||||
// @Tags Debug
|
||||
// @Produce json
|
||||
// @Success 200 {object} gin.H
|
||||
// @Router /api/v1/status [get]
|
||||
|
|
|
|||
|
|
@ -402,8 +402,8 @@
|
|||
"application/zip"
|
||||
],
|
||||
"tags": [
|
||||
"Images",
|
||||
"Albums"
|
||||
"Albums",
|
||||
"Download"
|
||||
],
|
||||
"summary": "streams the album contents as zip archive",
|
||||
"operationId": "DownloadAlbum",
|
||||
|
|
@ -1716,6 +1716,9 @@
|
|||
},
|
||||
"/api/v1/echo": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Debug"
|
||||
],
|
||||
"summary": "returns the request and response headers as JSON if debug mode is enabled",
|
||||
"operationId": "Echo",
|
||||
"responses": {
|
||||
|
|
@ -2064,6 +2067,44 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/feedback": {
|
||||
"post": {
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Admin"
|
||||
],
|
||||
"summary": "allows members to submit a feedback message to the PhotoPrism team",
|
||||
"operationId": "SendFeedback",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/form.Feedback"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/i18n.Response"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/i18n.Response"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Forbidden",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/i18n.Response"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/files/{hash}": {
|
||||
"get": {
|
||||
"produces": [
|
||||
|
|
@ -2730,6 +2771,41 @@
|
|||
"responses": {}
|
||||
}
|
||||
},
|
||||
"/api/v1/metrics": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"text/event-stream"
|
||||
],
|
||||
"tags": [
|
||||
"Metrics"
|
||||
],
|
||||
"summary": "a prometheus-compatible metrics endpoint for monitoring this instance",
|
||||
"operationId": "GetMetrics",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/io_prometheus_client.MetricFamily"
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/i18n.Response"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Forbidden",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/i18n.Response"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/moments/time": {
|
||||
"get": {
|
||||
"produces": [
|
||||
|
|
@ -3982,6 +4058,38 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/server/stop": {
|
||||
"post": {
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Admin"
|
||||
],
|
||||
"summary": "allows authorized admins to restart the server",
|
||||
"operationId": "StopServer",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/config.Options"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/i18n.Response"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Forbidden",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/i18n.Response"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/services": {
|
||||
"get": {
|
||||
"produces": [
|
||||
|
|
@ -4521,9 +4629,9 @@
|
|||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Server"
|
||||
"Debug"
|
||||
],
|
||||
"summary": "reports if the server is operational",
|
||||
"summary": "responds with status code 200 if the server is operational",
|
||||
"operationId": "GetStatus",
|
||||
"responses": {
|
||||
"200": {
|
||||
|
|
@ -5275,14 +5383,95 @@
|
|||
},
|
||||
"/api/v1/zip": {
|
||||
"post": {
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Download"
|
||||
],
|
||||
"responses": {}
|
||||
"summary": "creates a zip file archive for download",
|
||||
"operationId": "ZipCreate",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "file"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/i18n.Response"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Forbidden",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/i18n.Response"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/i18n.Response"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/zip/{filename}": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/zip"
|
||||
],
|
||||
"tags": [
|
||||
"Download"
|
||||
],
|
||||
"summary": "returns a zip file archive after it has been created",
|
||||
"operationId": "ZipDownload",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "zip archive filename returned by the POST /api/v1/zip endpoint",
|
||||
"name": "filename",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "file"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Forbidden",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/i18n.Response"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/i18n.Response"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/i18n.Response"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/{any}": {
|
||||
"options": {
|
||||
"description": "A preflight request is automatically issued by a browser and in normal cases, front-end developers don't need to craft such requests themselves. It appears when request is qualified as \"to be preflighted\" and omitted for simple requests.",
|
||||
"tags": [
|
||||
"CORS"
|
||||
],
|
||||
"summary": "returns CORS headers with an empty response body",
|
||||
"operationId": "Options",
|
||||
"responses": {
|
||||
|
|
@ -7193,6 +7382,29 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"form.Feedback": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"Category": {
|
||||
"type": "string"
|
||||
},
|
||||
"Message": {
|
||||
"type": "string"
|
||||
},
|
||||
"UserAgent": {
|
||||
"type": "string"
|
||||
},
|
||||
"UserEmail": {
|
||||
"type": "string"
|
||||
},
|
||||
"UserLocales": {
|
||||
"type": "string"
|
||||
},
|
||||
"UserName": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"form.File": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
@ -7578,6 +7790,295 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"io_prometheus_client.Bucket": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"cumulative_count": {
|
||||
"description": "Cumulative in increasing order.",
|
||||
"type": "integer"
|
||||
},
|
||||
"cumulative_count_float": {
|
||||
"description": "Overrides cumulative_count if \u003e 0.",
|
||||
"type": "number"
|
||||
},
|
||||
"exemplar": {
|
||||
"$ref": "#/definitions/io_prometheus_client.Exemplar"
|
||||
},
|
||||
"upper_bound": {
|
||||
"description": "Inclusive.",
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
},
|
||||
"io_prometheus_client.BucketSpan": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"length": {
|
||||
"description": "Length of consecutive buckets.",
|
||||
"type": "integer"
|
||||
},
|
||||
"offset": {
|
||||
"description": "Gap to previous span, or starting point for 1st span (which can be negative).",
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"io_prometheus_client.Counter": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"created_timestamp": {
|
||||
"$ref": "#/definitions/timestamppb.Timestamp"
|
||||
},
|
||||
"exemplar": {
|
||||
"$ref": "#/definitions/io_prometheus_client.Exemplar"
|
||||
},
|
||||
"value": {
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
},
|
||||
"io_prometheus_client.Exemplar": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"label": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/io_prometheus_client.LabelPair"
|
||||
}
|
||||
},
|
||||
"timestamp": {
|
||||
"description": "OpenMetrics-style.",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/timestamppb.Timestamp"
|
||||
}
|
||||
]
|
||||
},
|
||||
"value": {
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
},
|
||||
"io_prometheus_client.Gauge": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"value": {
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
},
|
||||
"io_prometheus_client.Histogram": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"bucket": {
|
||||
"description": "Buckets for the conventional histogram.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/io_prometheus_client.Bucket"
|
||||
}
|
||||
},
|
||||
"created_timestamp": {
|
||||
"$ref": "#/definitions/timestamppb.Timestamp"
|
||||
},
|
||||
"exemplars": {
|
||||
"description": "Only used for native histograms. These exemplars MUST have a timestamp.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/io_prometheus_client.Exemplar"
|
||||
}
|
||||
},
|
||||
"negative_count": {
|
||||
"description": "Absolute count of each bucket.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"negative_delta": {
|
||||
"description": "Use either \"negative_delta\" or \"negative_count\", the former for\nregular histograms with integer counts, the latter for float\nhistograms.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"negative_span": {
|
||||
"description": "Negative buckets for the native histogram.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/io_prometheus_client.BucketSpan"
|
||||
}
|
||||
},
|
||||
"positive_count": {
|
||||
"description": "Absolute count of each bucket.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"positive_delta": {
|
||||
"description": "Use either \"positive_delta\" or \"positive_count\", the former for\nregular histograms with integer counts, the latter for float\nhistograms.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"positive_span": {
|
||||
"description": "Positive buckets for the native histogram.\nUse a no-op span (offset 0, length 0) for a native histogram without any\nobservations yet and with a zero_threshold of 0. Otherwise, it would be\nindistinguishable from a classic histogram.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/io_prometheus_client.BucketSpan"
|
||||
}
|
||||
},
|
||||
"sample_count": {
|
||||
"type": "integer"
|
||||
},
|
||||
"sample_count_float": {
|
||||
"description": "Overrides sample_count if \u003e 0.",
|
||||
"type": "number"
|
||||
},
|
||||
"sample_sum": {
|
||||
"type": "number"
|
||||
},
|
||||
"schema": {
|
||||
"description": "schema defines the bucket schema. Currently, valid numbers are -4 \u003c= n \u003c= 8.\nThey are all for base-2 bucket schemas, where 1 is a bucket boundary in each case, and\nthen each power of two is divided into 2^n logarithmic buckets.\nOr in other words, each bucket boundary is the previous boundary times 2^(2^-n).\nIn the future, more bucket schemas may be added using numbers \u003c -4 or \u003e 8.",
|
||||
"type": "integer"
|
||||
},
|
||||
"zero_count": {
|
||||
"description": "Count in zero bucket.",
|
||||
"type": "integer"
|
||||
},
|
||||
"zero_count_float": {
|
||||
"description": "Overrides sb_zero_count if \u003e 0.",
|
||||
"type": "number"
|
||||
},
|
||||
"zero_threshold": {
|
||||
"description": "Breadth of the zero bucket.",
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
},
|
||||
"io_prometheus_client.LabelPair": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"io_prometheus_client.Metric": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"counter": {
|
||||
"$ref": "#/definitions/io_prometheus_client.Counter"
|
||||
},
|
||||
"gauge": {
|
||||
"$ref": "#/definitions/io_prometheus_client.Gauge"
|
||||
},
|
||||
"histogram": {
|
||||
"$ref": "#/definitions/io_prometheus_client.Histogram"
|
||||
},
|
||||
"label": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/io_prometheus_client.LabelPair"
|
||||
}
|
||||
},
|
||||
"summary": {
|
||||
"$ref": "#/definitions/io_prometheus_client.Summary"
|
||||
},
|
||||
"timestamp_ms": {
|
||||
"type": "integer"
|
||||
},
|
||||
"untyped": {
|
||||
"$ref": "#/definitions/io_prometheus_client.Untyped"
|
||||
}
|
||||
}
|
||||
},
|
||||
"io_prometheus_client.MetricFamily": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"help": {
|
||||
"type": "string"
|
||||
},
|
||||
"metric": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/io_prometheus_client.Metric"
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"$ref": "#/definitions/io_prometheus_client.MetricType"
|
||||
},
|
||||
"unit": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"io_prometheus_client.MetricType": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"enum": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5
|
||||
],
|
||||
"x-enum-varnames": [
|
||||
"MetricType_COUNTER",
|
||||
"MetricType_GAUGE",
|
||||
"MetricType_SUMMARY",
|
||||
"MetricType_UNTYPED",
|
||||
"MetricType_HISTOGRAM",
|
||||
"MetricType_GAUGE_HISTOGRAM"
|
||||
]
|
||||
},
|
||||
"io_prometheus_client.Quantile": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"quantile": {
|
||||
"type": "number"
|
||||
},
|
||||
"value": {
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
},
|
||||
"io_prometheus_client.Summary": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"created_timestamp": {
|
||||
"$ref": "#/definitions/timestamppb.Timestamp"
|
||||
},
|
||||
"quantile": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/io_prometheus_client.Quantile"
|
||||
}
|
||||
},
|
||||
"sample_count": {
|
||||
"type": "integer"
|
||||
},
|
||||
"sample_sum": {
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
},
|
||||
"io_prometheus_client.Untyped": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"value": {
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
},
|
||||
"nsfw.Result": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
@ -8334,6 +8835,12 @@
|
|||
"resizeOperation": {
|
||||
"$ref": "#/definitions/tensorflow.ResizeOperation"
|
||||
},
|
||||
"shape": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/tensorflow.ShapeComponent"
|
||||
}
|
||||
},
|
||||
"width": {
|
||||
"type": "integer"
|
||||
}
|
||||
|
|
@ -8354,6 +8861,15 @@
|
|||
"Padding"
|
||||
]
|
||||
},
|
||||
"tensorflow.ShapeComponent": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Batch"
|
||||
],
|
||||
"x-enum-varnames": [
|
||||
"ShapeBatch"
|
||||
]
|
||||
},
|
||||
"time.Duration": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
|
|
@ -8366,12 +8882,16 @@
|
|||
1000000000,
|
||||
60000000000,
|
||||
3600000000000,
|
||||
-9223372036854775808,
|
||||
9223372036854775807,
|
||||
1,
|
||||
1000,
|
||||
1000000,
|
||||
1000000000,
|
||||
60000000000,
|
||||
3600000000000,
|
||||
-9223372036854775808,
|
||||
9223372036854775807,
|
||||
1,
|
||||
1000,
|
||||
1000000,
|
||||
|
|
@ -8388,12 +8908,16 @@
|
|||
"Second",
|
||||
"Minute",
|
||||
"Hour",
|
||||
"minDuration",
|
||||
"maxDuration",
|
||||
"Nanosecond",
|
||||
"Microsecond",
|
||||
"Millisecond",
|
||||
"Second",
|
||||
"Minute",
|
||||
"Hour",
|
||||
"minDuration",
|
||||
"maxDuration",
|
||||
"Nanosecond",
|
||||
"Microsecond",
|
||||
"Millisecond",
|
||||
|
|
@ -8402,6 +8926,19 @@
|
|||
"Hour"
|
||||
]
|
||||
},
|
||||
"timestamppb.Timestamp": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"nanos": {
|
||||
"description": "Non-negative fractions of a second at nanosecond resolution. Negative\nsecond values with fractions must still have non-negative nanos values\nthat count forward in time. Must be from 0 to 999,999,999\ninclusive.",
|
||||
"type": "integer"
|
||||
},
|
||||
"seconds": {
|
||||
"description": "Represents seconds of UTC time since Unix epoch\n1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to\n9999-12-31T23:59:59Z inclusive.",
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"vision.ApiFormat": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
|
|
|
|||
|
|
@ -25,8 +25,13 @@ import (
|
|||
|
||||
// ZipCreate creates a zip file archive for download.
|
||||
//
|
||||
// @Tags Download
|
||||
// @Router /api/v1/zip [post]
|
||||
// @Summary creates a zip file archive for download
|
||||
// @Id ZipCreate
|
||||
// @Tags Download
|
||||
// @Produce json
|
||||
// @Failure 400,403,404 {object} i18n.Response
|
||||
// @Success 200 {file} application/zip
|
||||
// @Router /api/v1/zip [post]
|
||||
func ZipCreate(router *gin.RouterGroup) {
|
||||
router.POST("/zip", func(c *gin.Context) {
|
||||
s := Auth(c, acl.ResourcePhotos, acl.ActionDownload)
|
||||
|
|
@ -148,9 +153,16 @@ func ZipCreate(router *gin.RouterGroup) {
|
|||
})
|
||||
}
|
||||
|
||||
// ZipDownload downloads a zip file archive.
|
||||
// ZipDownload returns a zip file archive after it has been created.
|
||||
//
|
||||
// GET /api/v1/zip/:filename
|
||||
// @Summary returns a zip file archive after it has been created
|
||||
// @Id ZipDownload
|
||||
// @Tags Download
|
||||
// @Produce application/zip
|
||||
// @Failure 403,404,500 {object} i18n.Response
|
||||
// @Success 200 {file} application/zip
|
||||
// @Param filename path string true "zip archive filename returned by the POST /api/v1/zip endpoint"
|
||||
// @Router /api/v1/zip/{filename} [get]
|
||||
func ZipDownload(router *gin.RouterGroup) {
|
||||
router.GET("/zip/:filename", func(c *gin.Context) {
|
||||
if InvalidDownloadToken(c) {
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ const (
|
|||
ResourceWebhooks Resource = "webhooks"
|
||||
ResourceMetrics Resource = "metrics"
|
||||
ResourceVision Resource = "vision"
|
||||
ResourcePortal Resource = "portal"
|
||||
ResourceFeedback Resource = "feedback"
|
||||
ResourceDefault Resource = "default"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -125,6 +125,14 @@ var (
|
|||
ActionView: true,
|
||||
ActionUpdateOwn: true,
|
||||
}
|
||||
GrantSearchDownloadUpdateOwn = Grant{
|
||||
AccessShared: true,
|
||||
AccessOwn: true,
|
||||
ActionSearch: true,
|
||||
ActionView: true,
|
||||
ActionDownload: true,
|
||||
ActionUpdateOwn: true,
|
||||
}
|
||||
GrantSubscribeOwn = Grant{
|
||||
AccessOwn: true,
|
||||
ActionSubscribe: true,
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ var ResourceNames = []Resource{
|
|||
ResourceWebhooks,
|
||||
ResourceMetrics,
|
||||
ResourceVision,
|
||||
ResourcePortal,
|
||||
ResourceFeedback,
|
||||
ResourceDefault,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,6 +108,10 @@ var Rules = ACL{
|
|||
RoleAdmin: GrantFullAccess,
|
||||
RoleClient: GrantUseOwn,
|
||||
},
|
||||
ResourcePortal: Roles{
|
||||
RoleAdmin: GrantFullAccess,
|
||||
RoleClient: GrantSearchDownloadUpdateOwn,
|
||||
},
|
||||
ResourceFeedback: Roles{
|
||||
RoleAdmin: GrantFullAccess,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -21,12 +21,12 @@ var AuthResetCommand = &cli.Command{
|
|||
&cli.BoolFlag{
|
||||
Name: "trace",
|
||||
Aliases: []string{"t"},
|
||||
Usage: "show trace logs for debugging",
|
||||
Usage: "shows trace logs for debugging",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "yes",
|
||||
Aliases: []string{"y"},
|
||||
Usage: "assume \"yes\" and run non-interactively",
|
||||
Usage: "runs the command non-interactively",
|
||||
},
|
||||
},
|
||||
Action: authResetAction,
|
||||
|
|
|
|||
|
|
@ -33,12 +33,12 @@ var backupFlags = []cli.Flag{
|
|||
&cli.BoolFlag{
|
||||
Name: "force",
|
||||
Aliases: []string{"f"},
|
||||
Usage: "replace the index database backup file, if it exists",
|
||||
Usage: "replaces the index database backup file, if it exists",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "albums",
|
||||
Aliases: []string{"a"},
|
||||
Usage: "create YAML files to back up album metadata (in the standard backup path if no other path is specified)",
|
||||
Usage: "creates YAML files to back up album metadata (in the standard backup path if no other path is specified)",
|
||||
},
|
||||
&cli.PathFlag{
|
||||
Name: "albums-path",
|
||||
|
|
@ -48,7 +48,7 @@ var backupFlags = []cli.Flag{
|
|||
&cli.BoolFlag{
|
||||
Name: "database",
|
||||
Aliases: []string{"index", "i"},
|
||||
Usage: "create index database backup (in the backup path with the date as filename if no filename is passed, or sent to stdout if - is passed as filename)",
|
||||
Usage: "creates an index database backup (in the backup path with the date as filename if no filename is passed, or sent to stdout if - is passed as filename)",
|
||||
},
|
||||
&cli.PathFlag{
|
||||
Name: "database-path",
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ var CleanUpCommand = &cli.Command{
|
|||
var cleanUpFlags = []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "dry",
|
||||
Usage: "dry run, don't actually remove anything",
|
||||
Usage: "performs a dry run that doesn't actually remove anything",
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ var ClientsRemoveCommand = &cli.Command{
|
|||
&cli.BoolFlag{
|
||||
Name: "force",
|
||||
Aliases: []string{"f"},
|
||||
Usage: "don't ask for confirmation",
|
||||
Usage: "skips asking for confirmation",
|
||||
},
|
||||
},
|
||||
Action: clientsRemoveAction,
|
||||
|
|
|
|||
|
|
@ -17,12 +17,12 @@ var ClientsResetCommand = &cli.Command{
|
|||
&cli.BoolFlag{
|
||||
Name: "trace",
|
||||
Aliases: []string{"t"},
|
||||
Usage: "show trace logs for debugging",
|
||||
Usage: "shows trace logs for debugging",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "yes",
|
||||
Aliases: []string{"y"},
|
||||
Usage: "assume \"yes\" and run non-interactively",
|
||||
Usage: "runs the command non-interactively",
|
||||
},
|
||||
},
|
||||
Action: clientsResetAction,
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ var FacesCommands = &cli.Command{
|
|||
&cli.BoolFlag{
|
||||
Name: "force",
|
||||
Aliases: []string{"f"},
|
||||
Usage: "remove all people and faces",
|
||||
Usage: "removes all people and faces",
|
||||
},
|
||||
},
|
||||
Action: facesResetAction,
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ var indexFlags = []cli.Flag{
|
|||
&cli.BoolFlag{
|
||||
Name: "cleanup",
|
||||
Aliases: []string{"c"},
|
||||
Usage: "remove orphan index entries and thumbnails",
|
||||
Usage: "removes orphan index entries and thumbnails",
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ var MigrationsRunCommand = &cli.Command{
|
|||
&cli.BoolFlag{
|
||||
Name: "trace",
|
||||
Aliases: []string{"t"},
|
||||
Usage: "show trace logs for debugging",
|
||||
Usage: "shows trace logs for debugging",
|
||||
},
|
||||
},
|
||||
Action: migrationsRunAction,
|
||||
|
|
|
|||
|
|
@ -27,12 +27,12 @@ var PasswdCommand = &cli.Command{
|
|||
&cli.BoolFlag{
|
||||
Name: "show",
|
||||
Aliases: []string{"s"},
|
||||
Usage: "show bcrypt hash of new password",
|
||||
Usage: "shows bcrypt hash of new password",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "remove",
|
||||
Aliases: []string{"rm"},
|
||||
Usage: "remove password to disable local authentication",
|
||||
Usage: "removes password to disable local authentication",
|
||||
},
|
||||
},
|
||||
Action: passwdAction,
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ var PlacesCommands = &cli.Command{
|
|||
&cli.BoolFlag{
|
||||
Name: "yes",
|
||||
Aliases: []string{"y"},
|
||||
Usage: "assume \"yes\" and run non-interactively",
|
||||
Usage: "runs the command non-interactively",
|
||||
},
|
||||
},
|
||||
Action: placesUpdateAction,
|
||||
|
|
|
|||
|
|
@ -26,11 +26,11 @@ var PurgeCommand = &cli.Command{
|
|||
var purgeFlags = []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "hard",
|
||||
Usage: "permanently remove from index",
|
||||
Usage: "permanently removes data from the index",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "dry",
|
||||
Usage: "dry run, don't actually remove anything",
|
||||
Usage: "performs a dry run that doesn't actually remove anything",
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,17 +25,17 @@ var ResetCommand = &cli.Command{
|
|||
&cli.BoolFlag{
|
||||
Name: "index",
|
||||
Aliases: []string{"i"},
|
||||
Usage: "reset index database only",
|
||||
Usage: "resets only the index database ",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "trace",
|
||||
Aliases: []string{"t"},
|
||||
Usage: "show trace logs for debugging",
|
||||
Usage: "shows trace logs for debugging",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "yes",
|
||||
Aliases: []string{"y"},
|
||||
Usage: "assume \"yes\" and run non-interactively",
|
||||
Usage: "runs the command non-interactively",
|
||||
},
|
||||
},
|
||||
Action: resetAction,
|
||||
|
|
|
|||
|
|
@ -15,10 +15,10 @@ const (
|
|||
UserRoleUsage = "user account `ROLE` (admin or guest)"
|
||||
UserAuthUsage = "authentication `PROVIDER` (default, local, oidc or none)"
|
||||
UserAuthIDUsage = "authentication `ID` e.g. Subject ID or Distinguished Name (DN)"
|
||||
UserAdminUsage = "make user super admin with full access"
|
||||
UserNoLoginUsage = "disable login on the web interface"
|
||||
UserWebDAVUsage = "allow to sync files via WebDAV"
|
||||
UserDisable2FA = "deactivate two-factor authentication"
|
||||
UserAdminUsage = "makes user super admin with full access"
|
||||
UserNoLoginUsage = "disables login on the web interface"
|
||||
UserWebDAVUsage = "allows to sync files via WebDAV"
|
||||
UserDisable2FA = "deactivates two-factor authentication"
|
||||
)
|
||||
|
||||
// UsersCommands configures the user management subcommands.
|
||||
|
|
@ -98,19 +98,19 @@ var UserTokensFlag = &cli.BoolFlag{
|
|||
var UsersLoginFlag = &cli.BoolFlag{
|
||||
Name: "login",
|
||||
Aliases: []string{"l"},
|
||||
Usage: "show date and time of last login",
|
||||
Usage: "shows date and time of last login",
|
||||
}
|
||||
|
||||
// UsersCreatedFlag is a CLI flag for showing the account creation timestamp in reports.
|
||||
var UsersCreatedFlag = &cli.BoolFlag{
|
||||
Name: "created",
|
||||
Aliases: []string{"a"},
|
||||
Usage: "show account creation timestamp",
|
||||
Usage: "shows account creation timestamp",
|
||||
}
|
||||
|
||||
// UsersDeletedFlag is a CLI flag for showing deleted user accounts in reports.
|
||||
var UsersDeletedFlag = &cli.BoolFlag{
|
||||
Name: "deleted",
|
||||
Aliases: []string{"r"},
|
||||
Usage: "show deleted user accounts",
|
||||
Usage: "shows deleted user accounts",
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ var UsersRemoveCommand = &cli.Command{
|
|||
&cli.BoolFlag{
|
||||
Name: "force",
|
||||
Aliases: []string{"f"},
|
||||
Usage: "don't ask for confirmation",
|
||||
Usage: "skips asking for confirmation",
|
||||
},
|
||||
},
|
||||
Action: usersRemoveAction,
|
||||
|
|
|
|||
|
|
@ -20,12 +20,12 @@ var UsersResetCommand = &cli.Command{
|
|||
&cli.BoolFlag{
|
||||
Name: "trace",
|
||||
Aliases: []string{"t"},
|
||||
Usage: "show trace logs for debugging",
|
||||
Usage: "shows trace logs for debugging",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "yes",
|
||||
Aliases: []string{"y"},
|
||||
Usage: "assume \"yes\" and run non-interactively",
|
||||
Usage: "runs the command non-interactively",
|
||||
},
|
||||
},
|
||||
Action: usersResetAction,
|
||||
|
|
|
|||
|
|
@ -30,14 +30,14 @@ func TestCliFlags_Replace(t *testing.T) {
|
|||
Name: "public",
|
||||
Aliases: []string{"p"},
|
||||
Hidden: true,
|
||||
Usage: "disable authentication, advanced settings, and WebDAV remote access",
|
||||
Usage: "disables authentication, advanced settings, and WebDAV remote access",
|
||||
EnvVars: EnvVars("PUBLIC"),
|
||||
}}
|
||||
|
||||
newPublicFlag := CliFlag{Flag: &cli.BoolFlag{
|
||||
Name: "public",
|
||||
Hidden: false,
|
||||
Usage: "disable authentication, advanced settings, and WebDAV remote access",
|
||||
Usage: "disables authentication, advanced settings, and WebDAV remote access",
|
||||
EnvVars: EnvVars("PUBLIC"),
|
||||
}}
|
||||
|
||||
|
|
@ -114,7 +114,7 @@ func TestCliFlags_Insert(t *testing.T) {
|
|||
Name: "public",
|
||||
Aliases: []string{"p"},
|
||||
Hidden: true,
|
||||
Usage: "disable authentication, advanced settings, and WebDAV remote access",
|
||||
Usage: "disables authentication, advanced settings, and WebDAV remote access",
|
||||
EnvVars: EnvVars("PUBLIC"),
|
||||
}}
|
||||
|
||||
|
|
@ -163,7 +163,7 @@ func TestCliFlags_InsertBefore(t *testing.T) {
|
|||
Name: "public",
|
||||
Aliases: []string{"p"},
|
||||
Hidden: true,
|
||||
Usage: "disable authentication, advanced settings, and WebDAV remote access",
|
||||
Usage: "disables authentication, advanced settings, and WebDAV remote access",
|
||||
EnvVars: EnvVars("PUBLIC"),
|
||||
}}
|
||||
|
||||
|
|
@ -214,7 +214,7 @@ func TestCliFlags_Prepend(t *testing.T) {
|
|||
Name: "public",
|
||||
Aliases: []string{"p"},
|
||||
Hidden: true,
|
||||
Usage: "disable authentication, advanced settings, and WebDAV remote access",
|
||||
Usage: "disables authentication, advanced settings, and WebDAV remote access",
|
||||
EnvVars: EnvVars("PUBLIC"),
|
||||
}}
|
||||
|
||||
|
|
|
|||
|
|
@ -291,6 +291,8 @@ func (c *Config) Propagate() {
|
|||
thumb.SizeOnDemand = c.ThumbSizeUncached()
|
||||
thumb.JpegQualityDefault = c.JpegQuality()
|
||||
thumb.CachePublic = c.HttpCachePublic()
|
||||
thumb.ExamplesPath = c.ExamplesPath()
|
||||
thumb.IccProfilesPath = c.IccProfilesPath()
|
||||
initThumbs()
|
||||
|
||||
// Configure video download package.
|
||||
|
|
|
|||
|
|
@ -291,3 +291,8 @@ func (c *Config) ImgPath() string {
|
|||
func (c *Config) ThemePath() string {
|
||||
return filepath.Join(c.ConfigPath(), "theme")
|
||||
}
|
||||
|
||||
// PortalPath returns the path to portal config files.
|
||||
func (c *Config) PortalPath() string {
|
||||
return filepath.Join(c.ConfigPath(), "portal")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -600,6 +600,16 @@ func (c *Config) CustomAssetsPath() string {
|
|||
return ""
|
||||
}
|
||||
|
||||
// ProfilesPath returns the path where processing profile files are stored.
|
||||
func (c *Config) ProfilesPath() string {
|
||||
return filepath.Join(c.AssetsPath(), "profiles")
|
||||
}
|
||||
|
||||
// IccProfilesPath returns the path where ICC color profile files are stored.
|
||||
func (c *Config) IccProfilesPath() string {
|
||||
return filepath.Join(c.AssetsPath(), "profiles/icc")
|
||||
}
|
||||
|
||||
// CustomStaticPath returns the custom static assets' path.
|
||||
func (c *Config) CustomStaticPath() string {
|
||||
if dir := c.CustomAssetsPath(); dir == "" {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/functions"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/rnd"
|
||||
)
|
||||
|
||||
|
|
@ -474,8 +475,10 @@ func TestConfig_ImportAllow(t *testing.T) {
|
|||
assert.Equal(t, "", c.ImportAllow().String())
|
||||
}
|
||||
|
||||
func TestConfig_AssetsPath2(t *testing.T) {
|
||||
func TestConfig_AssetsPath(t *testing.T) {
|
||||
c := NewConfig(CliTestContext())
|
||||
|
||||
assert.True(t, strings.HasSuffix(c.AssetsPath(), "/assets"))
|
||||
assert.Equal(t, "/go/src/github.com/photoprism/photoprism/assets", c.AssetsPath())
|
||||
c.options.AssetsPath = ""
|
||||
if s := c.AssetsPath(); s != "" && s != "/opt/photoprism/assets" {
|
||||
|
|
@ -483,6 +486,27 @@ func TestConfig_AssetsPath2(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestConfig_ProfilesPath(t *testing.T) {
|
||||
c := NewConfig(CliTestContext())
|
||||
|
||||
result := c.ProfilesPath()
|
||||
assert.True(t, strings.HasSuffix(result, "/assets/profiles"))
|
||||
assert.True(t, fs.PathExists(result))
|
||||
}
|
||||
|
||||
func TestConfig_IccProfilesPath(t *testing.T) {
|
||||
c := NewConfig(CliTestContext())
|
||||
|
||||
result := c.IccProfilesPath()
|
||||
assert.True(t, strings.HasSuffix(result, "/assets/profiles/icc"))
|
||||
}
|
||||
|
||||
func TestConfig_CustomAssetsPath(t *testing.T) {
|
||||
c := NewConfig(CliTestContext())
|
||||
|
||||
assert.Equal(t, "", c.CustomAssetsPath())
|
||||
}
|
||||
|
||||
func TestConfig_MariadbBin(t *testing.T) {
|
||||
c := NewConfig(CliTestContext())
|
||||
assert.Contains(t, c.MariadbBin(), "mariadb")
|
||||
|
|
|
|||
|
|
@ -201,18 +201,6 @@ func TestConfig_ThumbCachePath(t *testing.T) {
|
|||
assert.True(t, strings.HasSuffix(c.ThumbCachePath(), "storage/testdata/"+functions.PhotoPrismTestToFolderName()+"/cache/thumbnails"))
|
||||
}
|
||||
|
||||
func TestConfig_AssetsPath(t *testing.T) {
|
||||
c := NewConfig(CliTestContext())
|
||||
|
||||
assert.True(t, strings.HasSuffix(c.AssetsPath(), "/assets"))
|
||||
}
|
||||
|
||||
func TestConfig_CustomAssetsPath(t *testing.T) {
|
||||
c := NewConfig(CliTestContext())
|
||||
|
||||
assert.Equal(t, "", c.CustomAssetsPath())
|
||||
}
|
||||
|
||||
func TestConfig_AdminUser(t *testing.T) {
|
||||
c := NewConfig(CliTestContext())
|
||||
|
||||
|
|
@ -287,6 +275,13 @@ func TestConfig_ThemePath(t *testing.T) {
|
|||
assert.Equal(t, expected, path)
|
||||
}
|
||||
|
||||
func TestConfig_PortalPath(t *testing.T) {
|
||||
c := NewConfig(CliTestContext())
|
||||
|
||||
path := c.PortalPath()
|
||||
assert.Equal(t, "/go/src/github.com/photoprism/photoprism/storage/testdata/config/portal", path)
|
||||
}
|
||||
|
||||
func TestConfig_IndexWorkers(t *testing.T) {
|
||||
c := NewConfig(CliTestContext())
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ var Flags = CliFlags{
|
|||
Name: "public",
|
||||
Aliases: []string{"p"},
|
||||
Hidden: true,
|
||||
Usage: "disable authentication, advanced settings, and WebDAV remote access",
|
||||
Usage: "disables authentication, advanced settings, and WebDAV remote access",
|
||||
EnvVars: EnvVars("PUBLIC"),
|
||||
}}, {
|
||||
Flag: &cli.StringFlag{
|
||||
|
|
@ -95,12 +95,12 @@ var Flags = CliFlags{
|
|||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "oidc-redirect",
|
||||
Usage: "automatically redirect unauthenticated users to the configured identity provider",
|
||||
Usage: "automatically redirects unauthenticated users to the configured identity provider",
|
||||
EnvVars: EnvVars("OIDC_REDIRECT"),
|
||||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "oidc-register",
|
||||
Usage: "allow new users to create an account when they sign in with OpenID Connect",
|
||||
Usage: "allows new users to create an account when they sign in with OpenID Connect",
|
||||
EnvVars: EnvVars("OIDC_REGISTER"),
|
||||
}}, {
|
||||
Flag: &cli.StringFlag{
|
||||
|
|
@ -111,12 +111,12 @@ var Flags = CliFlags{
|
|||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "oidc-webdav",
|
||||
Usage: "allow new OpenID Connect users to use WebDAV when they have a role that allows it",
|
||||
Usage: "allows new OpenID Connect users to use WebDAV when they have a role that allows it",
|
||||
EnvVars: EnvVars("OIDC_WEBDAV"),
|
||||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "disable-oidc",
|
||||
Usage: "disable single sign-on via OpenID Connect, even if an identity provider has been configured",
|
||||
Usage: "disables single sign-on via OpenID Connect, even if an identity provider has been configured",
|
||||
EnvVars: EnvVars("DISABLE_OIDC"),
|
||||
}}, {
|
||||
Flag: &cli.Int64Flag{
|
||||
|
|
@ -146,34 +146,34 @@ var Flags = CliFlags{
|
|||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "prod",
|
||||
Usage: "disable debug mode and log startup warnings and errors only",
|
||||
Usage: "disables debug mode and only logs startup warnings and errors",
|
||||
EnvVars: EnvVars("PROD"),
|
||||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "debug",
|
||||
Usage: "enable debug mode for development and troubleshooting",
|
||||
Usage: "enables debug mode for development and troubleshooting",
|
||||
EnvVars: EnvVars("DEBUG"),
|
||||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "trace",
|
||||
Usage: "enable trace mode to display all debug and trace logs",
|
||||
Usage: "enables trace mode to display all debug and trace logs",
|
||||
EnvVars: EnvVars("TRACE"),
|
||||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "test",
|
||||
Hidden: true,
|
||||
Usage: "enable test mode",
|
||||
Usage: "enables test mode",
|
||||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "unsafe",
|
||||
Hidden: true,
|
||||
Usage: "disable safety checks",
|
||||
Usage: "disables safety checks",
|
||||
EnvVars: EnvVars("UNSAFE"),
|
||||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "demo",
|
||||
Hidden: true,
|
||||
Usage: "enable demo mode",
|
||||
Usage: "enables demo mode",
|
||||
EnvVars: EnvVars("DEMO"),
|
||||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
|
|
@ -198,7 +198,7 @@ var Flags = CliFlags{
|
|||
Flag: &cli.StringFlag{
|
||||
Name: "defaults-yaml",
|
||||
Aliases: []string{"y"},
|
||||
Usage: "load default config values from `FILENAME` if it exists, does not override CLI flags or environment variables",
|
||||
Usage: "loads default config values from `FILENAME` if it exists, does not override CLI flags or environment variables",
|
||||
Value: "/etc/photoprism/defaults.yml",
|
||||
EnvVars: EnvVars("DEFAULTS_YAML"),
|
||||
TakesFile: true,
|
||||
|
|
@ -252,23 +252,23 @@ var Flags = CliFlags{
|
|||
}}, {
|
||||
Flag: &cli.StringFlag{
|
||||
Name: "import-allow",
|
||||
Usage: "restrict imports to these file types (comma-separated list of `EXTENSIONS`; leave blank to allow all)",
|
||||
Usage: "restricts imports to these file types (comma-separated list of `EXTENSIONS`; leave blank to allow all)",
|
||||
EnvVars: EnvVars("IMPORT_ALLOW"),
|
||||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "upload-nsfw",
|
||||
Aliases: []string{"n"},
|
||||
Usage: "allow uploads that might be offensive (detecting unsafe content requires TensorFlow)",
|
||||
Usage: "allows uploads that might be offensive (detecting unsafe content requires TensorFlow)",
|
||||
EnvVars: EnvVars("UPLOAD_NSFW"),
|
||||
}}, {
|
||||
Flag: &cli.StringFlag{
|
||||
Name: "upload-allow",
|
||||
Usage: "restrict uploads to these file types (comma-separated list of `EXTENSIONS`; leave blank to allow all)",
|
||||
Usage: "restricts uploads to these file types (comma-separated list of `EXTENSIONS`; leave blank to allow all)",
|
||||
EnvVars: EnvVars("UPLOAD_ALLOW"),
|
||||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "upload-archives",
|
||||
Usage: "allow upload of zip archives (will be extracted before import)",
|
||||
Usage: "allows upload of zip archives (will be extracted before import)",
|
||||
EnvVars: EnvVars("UPLOAD_ARCHIVES"),
|
||||
}}, {
|
||||
Flag: &cli.IntFlag{
|
||||
|
|
@ -312,12 +312,12 @@ var Flags = CliFlags{
|
|||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "sidecar-yaml",
|
||||
Usage: "create YAML sidecar files to back up picture metadata",
|
||||
Usage: "creates YAML sidecar files to back up picture metadata",
|
||||
EnvVars: EnvVars("SIDECAR_YAML"),
|
||||
}, DocDefault: "true"}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "usage-info",
|
||||
Usage: "display usage information in the user interface",
|
||||
Usage: "displays storage usage information in the user interface",
|
||||
EnvVars: EnvVars("USAGE_INFO"),
|
||||
}}, {
|
||||
Flag: &cli.Uint64Flag{
|
||||
|
|
@ -346,12 +346,12 @@ var Flags = CliFlags{
|
|||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "backup-database",
|
||||
Usage: "create regular backups based on the configured schedule",
|
||||
Usage: "enables regular backups based on the configured schedule",
|
||||
EnvVars: EnvVars("BACKUP_DATABASE"),
|
||||
}, DocDefault: "true"}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "backup-albums",
|
||||
Usage: "create YAML files to back up album metadata",
|
||||
Usage: "enables the use of YAML files for backing up album metadata",
|
||||
EnvVars: EnvVars("BACKUP_ALBUMS"),
|
||||
}, DocDefault: "true"}, {
|
||||
Flag: &cli.IntFlag{
|
||||
|
|
@ -389,118 +389,118 @@ var Flags = CliFlags{
|
|||
Flag: &cli.BoolFlag{
|
||||
Name: "read-only",
|
||||
Aliases: []string{"r"},
|
||||
Usage: "disable features that require write permission for the originals folder",
|
||||
Usage: "disables features that require write permission for the originals folder",
|
||||
EnvVars: EnvVars("READONLY"),
|
||||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "experimental",
|
||||
Aliases: []string{"e"},
|
||||
Usage: "enable new features that may be incomplete or unstable",
|
||||
Usage: "enables new features that may be incomplete or unstable",
|
||||
EnvVars: EnvVars("EXPERIMENTAL"),
|
||||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "disable-frontend",
|
||||
Usage: "disable the web user interface so that only the service API endpoints are accessible",
|
||||
Usage: "disables the web user interface so that only the service API endpoints are accessible",
|
||||
EnvVars: EnvVars("DISABLE_FRONTEND"),
|
||||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "disable-settings",
|
||||
Usage: "disable the settings frontend and related API endpoints, e.g. in combination with public mode",
|
||||
Usage: "disables the settings frontend and related API endpoints, e.g. in combination with public mode",
|
||||
EnvVars: EnvVars("DISABLE_SETTINGS"),
|
||||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "disable-backups",
|
||||
Usage: "prevent database and album backups as well as YAML sidecar files from being created",
|
||||
Usage: "prevents database and album backups as well as YAML sidecar files from being created",
|
||||
EnvVars: EnvVars("DISABLE_BACKUPS"),
|
||||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "disable-restart",
|
||||
Usage: "prevent admins from restarting the server through the user interface",
|
||||
Usage: "prevents admins from restarting the server through the user interface",
|
||||
EnvVars: EnvVars("DISABLE_RESTART"),
|
||||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "disable-webdav",
|
||||
Usage: "prevent other apps from accessing PhotoPrism as a shared network drive",
|
||||
Usage: "prevents other apps from accessing PhotoPrism as a shared network drive",
|
||||
EnvVars: EnvVars("DISABLE_WEBDAV"),
|
||||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "disable-places",
|
||||
Usage: "disable interactive world maps and reverse geocoding",
|
||||
Usage: "disables interactive world maps and reverse geocoding",
|
||||
EnvVars: EnvVars("DISABLE_PLACES"),
|
||||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "disable-tensorflow",
|
||||
Usage: "disable features depending on TensorFlow, e.g. image classification and face recognition",
|
||||
Usage: "disables features depending on TensorFlow, e.g. image classification and face recognition",
|
||||
EnvVars: EnvVars("DISABLE_TENSORFLOW"),
|
||||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "disable-faces",
|
||||
Usage: "disable face detection and recognition (requires TensorFlow)",
|
||||
Usage: "disables face detection and recognition (requires TensorFlow)",
|
||||
EnvVars: EnvVars("DISABLE_FACES"),
|
||||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "disable-classification",
|
||||
Usage: "disable image classification (requires TensorFlow)",
|
||||
Usage: "disables image classification (requires TensorFlow)",
|
||||
EnvVars: EnvVars("DISABLE_CLASSIFICATION"),
|
||||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "disable-ffmpeg",
|
||||
Usage: "disable video transcoding and thumbnail extraction with FFmpeg",
|
||||
Usage: "disables video transcoding and thumbnail extraction with FFmpeg",
|
||||
EnvVars: EnvVars("DISABLE_FFMPEG"),
|
||||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "disable-exiftool",
|
||||
Usage: "disable metadata extraction with ExifTool (required for full Video, Live Photo, and XMP support)",
|
||||
Usage: "disables metadata extraction with ExifTool (required for full Video, Live Photo, and XMP support)",
|
||||
EnvVars: EnvVars("DISABLE_EXIFTOOL"),
|
||||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "disable-vips",
|
||||
Usage: "disable image processing and conversion with libvips",
|
||||
Usage: "disables image processing and conversion with libvips",
|
||||
EnvVars: EnvVars("DISABLE_VIPS"),
|
||||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "disable-sips",
|
||||
Usage: "disable file conversion using the sips command under macOS",
|
||||
Usage: "disables file conversion using the sips command under macOS",
|
||||
EnvVars: EnvVars("DISABLE_SIPS"),
|
||||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "disable-darktable",
|
||||
Usage: "disable conversion of RAW images with Darktable",
|
||||
Usage: "disables conversion of RAW images with Darktable",
|
||||
EnvVars: EnvVars("DISABLE_DARKTABLE"),
|
||||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "disable-rawtherapee",
|
||||
Usage: "disable conversion of RAW images with RawTherapee",
|
||||
Usage: "disables conversion of RAW images with RawTherapee",
|
||||
EnvVars: EnvVars("DISABLE_RAWTHERAPEE"),
|
||||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "disable-imagemagick",
|
||||
Usage: "disable conversion of image files with ImageMagick",
|
||||
Usage: "disables conversion of image files with ImageMagick",
|
||||
EnvVars: EnvVars("DISABLE_IMAGEMAGICK"),
|
||||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "disable-heifconvert",
|
||||
Usage: "disable conversion of HEIC images with libheif",
|
||||
Usage: "disables conversion of HEIC images with libheif",
|
||||
EnvVars: EnvVars("DISABLE_HEIFCONVERT"),
|
||||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "disable-jpegxl",
|
||||
Usage: "disable JPEG XL file format support",
|
||||
Usage: "disables JPEG XL file format support",
|
||||
EnvVars: EnvVars("DISABLE_JPEGXL"),
|
||||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "disable-raw",
|
||||
Usage: "disable indexing and conversion of RAW images",
|
||||
Usage: "disables indexing and conversion of RAW images",
|
||||
EnvVars: EnvVars("DISABLE_RAW"),
|
||||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "raw-presets",
|
||||
Usage: "enables applying user presets when converting RAW images (reduces performance)",
|
||||
Usage: "enables custom user presets when converting RAW images (reduces performance)",
|
||||
EnvVars: EnvVars("RAW_PRESETS"),
|
||||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "exif-bruteforce",
|
||||
Usage: "always perform a brute-force search if no Exif headers were found",
|
||||
Usage: "performs a brute-force search if no Exif headers were found",
|
||||
EnvVars: EnvVars("EXIF_BRUTEFORCE"),
|
||||
}}, {
|
||||
Flag: &cli.StringFlag{
|
||||
|
|
@ -630,7 +630,7 @@ var Flags = CliFlags{
|
|||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "cdn-video",
|
||||
Usage: "stream videos over the specified CDN",
|
||||
Usage: "streams videos over the specified CDN",
|
||||
EnvVars: EnvVars("CDN_VIDEO"),
|
||||
}}, {
|
||||
Flag: &cli.StringFlag{
|
||||
|
|
@ -651,6 +651,26 @@ var Flags = CliFlags{
|
|||
EnvVars: EnvVars("CORS_METHODS"),
|
||||
Value: header.DefaultAccessControlAllowMethods,
|
||||
}}, {
|
||||
Flag: &cli.StringFlag{
|
||||
Name: "portal-url",
|
||||
Usage: "PhotoPrism® Portal server `URL`",
|
||||
EnvVars: EnvVars("PORTAL_URL"),
|
||||
}}, {
|
||||
Flag: &cli.StringFlag{
|
||||
Name: "portal-client",
|
||||
Usage: "PhotoPrism® Portal client `ID`",
|
||||
EnvVars: EnvVars("PORTAL_CLIENT"),
|
||||
}}, {
|
||||
Flag: &cli.StringFlag{
|
||||
Name: "portal-secret",
|
||||
Usage: "PhotoPrism® Portal client `SECRET`",
|
||||
EnvVars: EnvVars("PORTAL_SECRET"),
|
||||
}}, {
|
||||
Flag: &cli.StringFlag{
|
||||
Name: "instance-secret",
|
||||
Usage: "unique `SECRET` for authenticating this instance",
|
||||
EnvVars: EnvVars("INSTANCE_SECRET"),
|
||||
}}, {
|
||||
Flag: &cli.StringFlag{
|
||||
Name: "https-proxy",
|
||||
Usage: "proxy server `URL` to be used for outgoing connections *optional*",
|
||||
|
|
@ -658,7 +678,7 @@ var Flags = CliFlags{
|
|||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "https-proxy-insecure",
|
||||
Usage: "ignore invalid HTTPS certificates when using a proxy",
|
||||
Usage: "ignores invalid HTTPS certificates when using a proxy",
|
||||
EnvVars: EnvVars("HTTPS_PROXY_INSECURE"),
|
||||
}}, {
|
||||
Flag: &cli.StringFlag{
|
||||
|
|
@ -693,12 +713,12 @@ var Flags = CliFlags{
|
|||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "disable-tls",
|
||||
Usage: "disable HTTPS/TLS even if the site URL starts with https:// and a certificate is available",
|
||||
Usage: "disables HTTPS/TLS even if the site URL starts with https:// and a certificate is available",
|
||||
EnvVars: EnvVars("DISABLE_TLS"),
|
||||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "default-tls",
|
||||
Usage: "default to a self-signed HTTPS/TLS certificate if no other certificate is available",
|
||||
Usage: "uses a self-signed HTTPS/TLS certificate if no other certificate is available",
|
||||
EnvVars: EnvVars("DEFAULT_TLS"),
|
||||
}}, {
|
||||
Flag: &cli.StringFlag{
|
||||
|
|
@ -731,7 +751,7 @@ var Flags = CliFlags{
|
|||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "http-cache-public",
|
||||
Usage: "allow static content to be cached by a CDN or caching proxy",
|
||||
Usage: "allows static content to be cached by a CDN or caching proxy",
|
||||
EnvVars: EnvVars("HTTP_CACHE_PUBLIC"),
|
||||
}}, {
|
||||
Flag: &cli.IntFlag{
|
||||
|
|
@ -995,7 +1015,7 @@ var Flags = CliFlags{
|
|||
Flag: &cli.BoolFlag{
|
||||
Name: "thumb-uncached",
|
||||
Aliases: []string{"u"},
|
||||
Usage: "generate missing thumbnails on demand (high memory and cpu usage)",
|
||||
Usage: "generates missing thumbnails on demand (high memory and cpu usage)",
|
||||
EnvVars: EnvVars("THUMB_UNCACHED"),
|
||||
}}, {
|
||||
Flag: &cli.StringFlag{
|
||||
|
|
@ -1026,24 +1046,24 @@ var Flags = CliFlags{
|
|||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "vision-api",
|
||||
Usage: "enable computer vision service API endpoints under /api/v1/vision (requires authorized access token)",
|
||||
Usage: "enables the computer vision API endpoints under /api/v1/vision (requires authorization)",
|
||||
EnvVars: EnvVars("VISION_API"),
|
||||
}}, {
|
||||
Flag: &cli.StringFlag{
|
||||
Name: "vision-uri",
|
||||
Usage: "remote computer vision service `URI`, e.g. https://example.com/api/v1/vision (leave blank to disable)",
|
||||
Usage: "vision service base `URI`, e.g. https://example.com/api/v1/vision (leave blank to disable)",
|
||||
Value: "",
|
||||
EnvVars: EnvVars("VISION_URI"),
|
||||
}}, {
|
||||
Flag: &cli.StringFlag{
|
||||
Name: "vision-key",
|
||||
Usage: "remote computer vision service access `TOKEN` *optional*",
|
||||
Usage: "vision service access `TOKEN` *optional*",
|
||||
Value: "",
|
||||
EnvVars: EnvVars("VISION_KEY"),
|
||||
}}, {
|
||||
Flag: &cli.BoolFlag{
|
||||
Name: "detect-nsfw",
|
||||
Usage: "flag newly added pictures as private if they might be offensive (requires TensorFlow)",
|
||||
Usage: "flags newly added pictures as private if they might be offensive (requires TensorFlow)",
|
||||
EnvVars: EnvVars("DETECT_NSFW"),
|
||||
}}, {
|
||||
Flag: &cli.IntFlag{
|
||||
|
|
|
|||
|
|
@ -140,6 +140,10 @@ type Options struct {
|
|||
CORSOrigin string `yaml:"CORSOrigin" json:"-" flag:"cors-origin"`
|
||||
CORSHeaders string `yaml:"CORSHeaders" json:"-" flag:"cors-headers"`
|
||||
CORSMethods string `yaml:"CORSMethods" json:"-" flag:"cors-methods"`
|
||||
PortalUrl string `yaml:"PortalUrl" json:"-" flag:"portal-url"`
|
||||
PortalClient string `yaml:"PortalClient" json:"-" flag:"portal-client"`
|
||||
PortalSecret string `yaml:"PortalSecret" json:"-" flag:"portal-secret"`
|
||||
InstanceSecret string `yaml:"InstanceSecret" json:"-" flag:"instance-secret"`
|
||||
HttpsProxy string `yaml:"HttpsProxy" json:"HttpsProxy" flag:"https-proxy"`
|
||||
HttpsProxyInsecure bool `yaml:"HttpsProxyInsecure" json:"HttpsProxyInsecure" flag:"https-proxy-insecure"`
|
||||
TrustedPlatform string `yaml:"TrustedPlatform" json:"-" flag:"trusted-platform"`
|
||||
|
|
|
|||
|
|
@ -168,6 +168,12 @@ func (c *Config) Report() (rows [][]string, cols []string) {
|
|||
{"cors-headers", c.CORSHeaders()},
|
||||
{"cors-methods", c.CORSMethods()},
|
||||
|
||||
// Portal Server.
|
||||
{"portal-url", fmt.Sprintf("%s", c.Options().PortalUrl)},
|
||||
{"portal-client", fmt.Sprintf("%s", c.Options().PortalClient)},
|
||||
{"portal-secret", fmt.Sprintf("%s", strings.Repeat("*", utf8.RuneCountInString(c.Options().PortalSecret)))},
|
||||
{"instance-secret", fmt.Sprintf("%s", strings.Repeat("*", utf8.RuneCountInString(c.Options().InstanceSecret)))},
|
||||
|
||||
// URIs.
|
||||
{"base-uri", c.BaseUri("/")},
|
||||
{"api-uri", c.ApiUri()},
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ var OptionsReportSections = []ReportSection{
|
|||
{Start: "PHOTOPRISM_READONLY", Title: "Feature Flags"},
|
||||
{Start: "PHOTOPRISM_DEFAULT_LOCALE", Title: "Customization"},
|
||||
{Start: "PHOTOPRISM_SITE_URL", Title: "Site Information"},
|
||||
{Start: "PHOTOPRISM_PORTAL_URL", Title: "Portal Server"},
|
||||
{Start: "PHOTOPRISM_HTTPS_PROXY", Title: "Proxy Server"},
|
||||
{Start: "PHOTOPRISM_DISABLE_TLS", Title: "Web Server"},
|
||||
{Start: "PHOTOPRISM_DATABASE_DRIVER", Title: "Database Connection"},
|
||||
|
|
@ -51,6 +52,7 @@ var YamlReportSections = []ReportSection{
|
|||
{Start: "ReadOnly", Title: "Feature Flags"},
|
||||
{Start: "DefaultLocale", Title: "Customization"},
|
||||
{Start: "SiteUrl", Title: "Site Information"},
|
||||
{Start: "PortalUrl", Title: "Portal Server"},
|
||||
{Start: "HttpsProxy", Title: "Proxy Server"},
|
||||
{Start: "DisableTLS", Title: "Web Server"},
|
||||
{Start: "DatabaseDriver", Title: "Database Connection"},
|
||||
|
|
|
|||
|
|
@ -751,7 +751,7 @@ func TestMediaFile_MimeType(t *testing.T) {
|
|||
|
||||
assert.True(t, fs.SameType(header.ContentTypeXml, f.BaseType()))
|
||||
assert.Equal(t, "text/xml", f.BaseType())
|
||||
assert.Equal(t, "text/xml; charset=utf-8", f.MimeType())
|
||||
assert.True(t, strings.EqualFold("text/xml; charset=utf-8", f.MimeType()))
|
||||
assert.True(t, f.HasMimeType("text/xml"))
|
||||
assert.False(t, f.IsMov())
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
package thumb
|
||||
|
||||
var (
|
||||
CachePublic = false
|
||||
)
|
||||
|
|
@ -12,9 +12,6 @@ const (
|
|||
ColorPreserve ColorSpace = "preserve"
|
||||
)
|
||||
|
||||
// Color sets the standard color profile for thumbnails.
|
||||
var Color = ColorAuto
|
||||
|
||||
// ParseColor returns a ColorSpace based on the config value string and image library.
|
||||
func ParseColor(name string, lib Lib) ColorSpace {
|
||||
if lib == LibVips {
|
||||
|
|
|
|||
18
internal/thumb/config.go
Normal file
18
internal/thumb/config.go
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package thumb
|
||||
|
||||
import (
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
)
|
||||
|
||||
// Package configuration variables.
|
||||
var (
|
||||
Library = LibImaging // Image processing library to be used.
|
||||
Color = ColorAuto // Color sets the standard color profile for thumbnails.
|
||||
Filter = ResampleLanczos // Filter specifies the default downscaling filter.
|
||||
SizeCached = SizeFit1920.Width // Pre-generated thumbnail size limit.
|
||||
SizeOnDemand = SizeFit5120.Width // On-demand thumbnail size limit.
|
||||
JpegQualityDefault = QualityMedium // JpegQualityDefault sets the compression level of newly created JPEGs.
|
||||
CachePublic = false // Specifies if static content may be cached by a CDN or caching proxy.
|
||||
ExamplesPath = fs.Abs("../../assets/examples")
|
||||
IccProfilesPath = fs.Abs("../../assets/profiles/icc")
|
||||
)
|
||||
22
internal/thumb/config_test.go
Normal file
22
internal/thumb/config_test.go
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
package thumb
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
)
|
||||
|
||||
func TestConfig(t *testing.T) {
|
||||
t.Run("ExamplesPath", func(t *testing.T) {
|
||||
t.Logf("examples-path: %s", ExamplesPath)
|
||||
assert.Equal(t, fs.Abs("../../assets/examples"), ExamplesPath)
|
||||
assert.True(t, fs.PathExists(ExamplesPath))
|
||||
})
|
||||
t.Run("IccProfilesPath", func(t *testing.T) {
|
||||
t.Logf("icc-profiles-path: %s", ExamplesPath)
|
||||
assert.Equal(t, fs.Abs("../../assets/profiles/icc"), IccProfilesPath)
|
||||
// assert.True(t, fs.PathExists(IccProfilesPath))
|
||||
})
|
||||
}
|
||||
|
|
@ -20,9 +20,6 @@ const (
|
|||
ResampleNearest ResampleFilter = "nearest"
|
||||
)
|
||||
|
||||
// Filter specifies the default downscaling filter.
|
||||
var Filter = ResampleLanczos
|
||||
|
||||
// String returns the downscaling filter name as string.
|
||||
func (a ResampleFilter) String() string {
|
||||
return string(a)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,3 @@ const (
|
|||
LibVips Lib = "vips"
|
||||
LibImaging Lib = "imaging"
|
||||
)
|
||||
|
||||
// Library specifies the image library to be used.
|
||||
var Library = LibImaging
|
||||
|
|
|
|||
|
|
@ -19,9 +19,6 @@ const (
|
|||
QualityMin Quality = 70
|
||||
)
|
||||
|
||||
// JpegQualityDefault sets the compression level of newly created JPEGs.
|
||||
var JpegQualityDefault = QualityMedium
|
||||
|
||||
// Quality represents a JPEG image quality.
|
||||
type Quality int
|
||||
|
||||
|
|
|
|||
|
|
@ -4,12 +4,6 @@ import (
|
|||
"slices"
|
||||
)
|
||||
|
||||
// Default thumbnail size limits (cached and uncached).
|
||||
var (
|
||||
SizeCached = SizeFit1920.Width
|
||||
SizeOnDemand = SizeFit5120.Width
|
||||
)
|
||||
|
||||
// MaxSize returns the max supported size in pixels.
|
||||
func MaxSize() int {
|
||||
if SizeCached > SizeOnDemand {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue