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
4b2ddf6d18
63 changed files with 1733 additions and 1274 deletions
20
.editorconfig
Normal file
20
.editorconfig
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
# Top-most EditorConfig file, https://editorconfig.org
|
||||
root = true
|
||||
|
||||
# Use Unix-style newlines with a newline ending every file.
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = false
|
||||
|
||||
# Set rules by file type.
|
||||
[*.json]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.{js,yaml,yml,md,txt}]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
|
|
@ -6,16 +6,9 @@ build/
|
|||
dist/
|
||||
bin/
|
||||
tests/upload-files/
|
||||
.tmp
|
||||
.gocache
|
||||
.var
|
||||
*.html
|
||||
*.md
|
||||
.*
|
||||
.idea
|
||||
.codex
|
||||
.local
|
||||
.config
|
||||
.github
|
||||
.tmp
|
||||
.local
|
||||
.cache
|
||||
.gocache
|
||||
.var
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
# PhotoPrism® — Repository Guidelines
|
||||
|
||||
**Last Updated:** November 25, 2025
|
||||
**Last Updated:** December 8, 2025
|
||||
|
||||
## Purpose
|
||||
|
||||
This file tells automated coding agents (and humans) where to find the single sources of truth for building, testing, and contributing to PhotoPrism.
|
||||
Learn more: https://agents.md/
|
||||
This file tells automated coding agents (and humans) where to find the single sources of truth for building, testing, and contributing to this repository. Visit https://agents.md/ to learn more.
|
||||
|
||||
## Sources of Truth
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
# Ubuntu 25.10 (Questing Quokka)
|
||||
FROM photoprism/develop:251121-questing
|
||||
FROM photoprism/develop:251211-questing
|
||||
|
||||
# Harden npm usage by default (applies to npm ci / install in dev container)
|
||||
ENV NPM_CONFIG_IGNORE_SCRIPTS=true
|
||||
|
|
|
|||
118
NOTICE
118
NOTICE
|
|
@ -9,7 +9,7 @@ The following 3rd-party software packages may be used by or distributed with
|
|||
PhotoPrism. Any information relevant to third-party vendors listed below are
|
||||
collected using common, reasonable means.
|
||||
|
||||
Date generated: 2025-11-29
|
||||
Date generated: 2025-12-12
|
||||
|
||||
================================================================================
|
||||
|
||||
|
|
@ -405,6 +405,34 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|||
SOFTWARE.
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Package: github.com/clipperhouse/displaywidth
|
||||
Version: v0.6.0
|
||||
License: MIT (https://github.com/clipperhouse/displaywidth/blob/v0.6.0/LICENSE)
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 Matt Sherman
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Package: github.com/clipperhouse/stringish
|
||||
|
|
@ -1122,8 +1150,8 @@ SOFTWARE.
|
|||
--------------------------------------------------------------------------------
|
||||
|
||||
Package: github.com/gin-contrib/gzip
|
||||
Version: v1.2.3
|
||||
License: MIT (https://github.com/gin-contrib/gzip/blob/v1.2.3/LICENSE)
|
||||
Version: v1.2.5
|
||||
License: MIT (https://github.com/gin-contrib/gzip/blob/v1.2.5/LICENSE)
|
||||
|
||||
MIT License
|
||||
|
||||
|
|
@ -2443,8 +2471,8 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
|
|||
--------------------------------------------------------------------------------
|
||||
|
||||
Package: github.com/golang/geo
|
||||
Version: v0.0.0-20251125140653-09e2dd3603dd
|
||||
License: Apache-2.0 (https://github.com/golang/geo/blob/09e2dd3603dd/LICENSE)
|
||||
Version: v0.0.0-20251209161508-25c597310d4b
|
||||
License: Apache-2.0 (https://github.com/golang/geo/blob/25c597310d4b/LICENSE)
|
||||
|
||||
|
||||
Apache License
|
||||
|
|
@ -4530,8 +4558,8 @@ SOFTWARE.
|
|||
--------------------------------------------------------------------------------
|
||||
|
||||
Package: github.com/olekukonko/ll
|
||||
Version: v0.1.2
|
||||
License: MIT (https://github.com/olekukonko/ll/blob/v0.1.2/LICENSE)
|
||||
Version: v0.1.3
|
||||
License: MIT (https://github.com/olekukonko/ll/blob/v0.1.3/LICENSE)
|
||||
|
||||
MIT License
|
||||
|
||||
|
|
@ -4558,8 +4586,8 @@ SOFTWARE.
|
|||
--------------------------------------------------------------------------------
|
||||
|
||||
Package: github.com/olekukonko/tablewriter
|
||||
Version: v1.1.0
|
||||
License: MIT (https://github.com/olekukonko/tablewriter/blob/v1.1.0/LICENSE.md)
|
||||
Version: v1.1.2
|
||||
License: MIT (https://github.com/olekukonko/tablewriter/blob/v1.1.2/LICENSE.md)
|
||||
|
||||
Copyright (C) 2014 by Oleku Konko
|
||||
|
||||
|
|
@ -5782,34 +5810,6 @@ SOFTWARE.
|
|||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Package: github.com/rivo/uniseg
|
||||
Version: v0.4.7
|
||||
License: MIT (https://github.com/rivo/uniseg/blob/v0.4.7/LICENSE.txt)
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Oliver Kuederle
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Package: github.com/robfig/cron/v3
|
||||
Version: v3.0.1
|
||||
License: MIT (https://github.com/robfig/cron/blob/v3.0.1/LICENSE)
|
||||
|
|
@ -6376,8 +6376,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||
--------------------------------------------------------------------------------
|
||||
|
||||
Package: github.com/yalue/onnxruntime_go
|
||||
Version: v1.22.0
|
||||
License: MIT (https://github.com/yalue/onnxruntime_go/blob/v1.22.0/LICENSE)
|
||||
Version: v1.24.0
|
||||
License: MIT (https://github.com/yalue/onnxruntime_go/blob/v1.24.0/LICENSE)
|
||||
|
||||
Copyright (c) 2023 Nathan Otterness
|
||||
|
||||
|
|
@ -6610,8 +6610,8 @@ License: Apache-2.0 (https://github.com/zitadel/logging/blob/v0.6.2/LICENSE)
|
|||
--------------------------------------------------------------------------------
|
||||
|
||||
Package: github.com/zitadel/oidc/v3
|
||||
Version: v3.45.0
|
||||
License: Apache-2.0 (https://github.com/zitadel/oidc/blob/v3.45.0/LICENSE)
|
||||
Version: v3.45.1
|
||||
License: Apache-2.0 (https://github.com/zitadel/oidc/blob/v3.45.1/LICENSE)
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
|
|
@ -8188,8 +8188,8 @@ License: Apache-2.0 (https://github.com/go4org/go4/blob/214862532bf5/LICENSE)
|
|||
--------------------------------------------------------------------------------
|
||||
|
||||
Package: golang.org/x/crypto
|
||||
Version: v0.45.0
|
||||
License: BSD-3-Clause (https://cs.opensource.google/go/x/crypto/+/v0.45.0:LICENSE)
|
||||
Version: v0.46.0
|
||||
License: BSD-3-Clause (https://cs.opensource.google/go/x/crypto/+/v0.46.0:LICENSE)
|
||||
|
||||
Copyright 2009 The Go Authors.
|
||||
|
||||
|
|
@ -8222,8 +8222,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
--------------------------------------------------------------------------------
|
||||
|
||||
Package: golang.org/x/image
|
||||
Version: v0.33.0
|
||||
License: BSD-3-Clause (https://cs.opensource.google/go/x/image/+/v0.33.0:LICENSE)
|
||||
Version: v0.34.0
|
||||
License: BSD-3-Clause (https://cs.opensource.google/go/x/image/+/v0.34.0:LICENSE)
|
||||
|
||||
Copyright 2009 The Go Authors.
|
||||
|
||||
|
|
@ -8256,8 +8256,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
--------------------------------------------------------------------------------
|
||||
|
||||
Package: golang.org/x/mod/semver
|
||||
Version: v0.30.0
|
||||
License: BSD-3-Clause (https://cs.opensource.google/go/x/mod/+/v0.30.0:LICENSE)
|
||||
Version: v0.31.0
|
||||
License: BSD-3-Clause (https://cs.opensource.google/go/x/mod/+/v0.31.0:LICENSE)
|
||||
|
||||
Copyright 2009 The Go Authors.
|
||||
|
||||
|
|
@ -8290,8 +8290,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
--------------------------------------------------------------------------------
|
||||
|
||||
Package: golang.org/x/net
|
||||
Version: v0.47.0
|
||||
License: BSD-3-Clause (https://cs.opensource.google/go/x/net/+/v0.47.0:LICENSE)
|
||||
Version: v0.48.0
|
||||
License: BSD-3-Clause (https://cs.opensource.google/go/x/net/+/v0.48.0:LICENSE)
|
||||
|
||||
Copyright 2009 The Go Authors.
|
||||
|
||||
|
|
@ -8324,8 +8324,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
--------------------------------------------------------------------------------
|
||||
|
||||
Package: golang.org/x/oauth2
|
||||
Version: v0.32.0
|
||||
License: BSD-3-Clause (https://cs.opensource.google/go/x/oauth2/+/v0.32.0:LICENSE)
|
||||
Version: v0.33.0
|
||||
License: BSD-3-Clause (https://cs.opensource.google/go/x/oauth2/+/v0.33.0:LICENSE)
|
||||
|
||||
Copyright 2009 The Go Authors.
|
||||
|
||||
|
|
@ -8358,8 +8358,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
--------------------------------------------------------------------------------
|
||||
|
||||
Package: golang.org/x/sync/errgroup
|
||||
Version: v0.18.0
|
||||
License: BSD-3-Clause (https://cs.opensource.google/go/x/sync/+/v0.18.0:LICENSE)
|
||||
Version: v0.19.0
|
||||
License: BSD-3-Clause (https://cs.opensource.google/go/x/sync/+/v0.19.0:LICENSE)
|
||||
|
||||
Copyright 2009 The Go Authors.
|
||||
|
||||
|
|
@ -8392,8 +8392,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
--------------------------------------------------------------------------------
|
||||
|
||||
Package: golang.org/x/sys
|
||||
Version: v0.38.0
|
||||
License: BSD-3-Clause (https://cs.opensource.google/go/x/sys/+/v0.38.0:LICENSE)
|
||||
Version: v0.39.0
|
||||
License: BSD-3-Clause (https://cs.opensource.google/go/x/sys/+/v0.39.0:LICENSE)
|
||||
|
||||
Copyright 2009 The Go Authors.
|
||||
|
||||
|
|
@ -8426,8 +8426,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
--------------------------------------------------------------------------------
|
||||
|
||||
Package: golang.org/x/text
|
||||
Version: v0.31.0
|
||||
License: BSD-3-Clause (https://cs.opensource.google/go/x/text/+/v0.31.0:LICENSE)
|
||||
Version: v0.32.0
|
||||
License: BSD-3-Clause (https://cs.opensource.google/go/x/text/+/v0.32.0:LICENSE)
|
||||
|
||||
Copyright 2009 The Go Authors.
|
||||
|
||||
|
|
@ -8523,8 +8523,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
--------------------------------------------------------------------------------
|
||||
|
||||
Package: google.golang.org/protobuf
|
||||
Version: v1.36.10
|
||||
License: BSD-3-Clause (https://github.com/protocolbuffers/protobuf-go/blob/v1.36.10/LICENSE)
|
||||
Version: v1.36.11
|
||||
License: BSD-3-Clause (https://github.com/protocolbuffers/protobuf-go/blob/v1.36.11/LICENSE)
|
||||
|
||||
Copyright (c) 2018 The Go Authors. All rights reserved.
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ You are [welcome to contact us](https://www.photoprism.app/contact) for change r
|
|||
|
||||
[**Patrick Kvaksrud**](https://github.com/Kvaksrud) (October 2023)
|
||||
|
||||
**Alexander Burke** (November 2023)
|
||||
|
||||
[**Lars Kusch**](https://github.com/LarsK1) (December 2023)
|
||||
|
||||
**Joseph Hobbs** (November 2024)
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ services:
|
|||
PHOTOPRISM_THUMB_UNCACHED: "true" # enables on-demand thumbnail rendering (high memory and cpu usage)
|
||||
## Run/install on first startup (options: update tensorflow https intel gpu davfs yt-dlp):
|
||||
PHOTOPRISM_INIT: "https tensorflow-gpu"
|
||||
## Computer Vision API (https://docs.photoprism.app/getting-started/config-options/#computer-vision):
|
||||
## Computer Vision (https://docs.photoprism.app/getting-started/config-options/#computer-vision):
|
||||
PHOTOPRISM_VISION_API: "true" # server: enables service API endpoints under /api/v1/vision (requires access token)
|
||||
PHOTOPRISM_VISION_URI: "" # client: service URI, e.g. http://hostname/api/v1/vision (leave blank to disable)
|
||||
PHOTOPRISM_VISION_KEY: "" # client: service access token (for authentication)
|
||||
|
|
|
|||
|
|
@ -136,10 +136,12 @@ services:
|
|||
# PHOTOPRISM_FFMPEG_BITRATE: "64" # video bitrate limit in Mbps (default: 60)
|
||||
## Run/install on first startup (options: update tensorflow https intel gpu davfs yt-dlp):
|
||||
PHOTOPRISM_INIT: "https postgresql"
|
||||
## Computer Vision API (https://docs.photoprism.app/getting-started/config-options/#computer-vision):
|
||||
## Computer Vision (https://docs.photoprism.app/getting-started/config-options/#computer-vision):
|
||||
PHOTOPRISM_VISION_API: "true" # server: enables service API endpoints under /api/v1/vision (requires access token)
|
||||
PHOTOPRISM_VISION_URI: "" # client: service URI, e.g. http://hostname/api/v1/vision (leave blank to disable)
|
||||
PHOTOPRISM_VISION_KEY: "" # client: service access token (for authentication)
|
||||
OLLAMA_BASE_URL: "http://ollama:11434" # use "https://ollama.com" for Ollama Cloud
|
||||
OLLAMA_API_KEY: "" # API key required to access Ollama (optional)
|
||||
## External dependencies and tools:
|
||||
TF_CPP_MIN_LOG_LEVEL: 1
|
||||
GOCACHE: "/go/src/github.com/photoprism/photoprism/.local/gocache"
|
||||
|
|
@ -357,7 +359,7 @@ services:
|
|||
|
||||
## Dummy WebDAV Server
|
||||
dummy-webdav:
|
||||
image: photoprism/dummy-webdav:251124
|
||||
image: photoprism/dummy-webdav:251210
|
||||
stop_grace_period: 10s
|
||||
environment:
|
||||
WEBDAV_USERNAME: admin
|
||||
|
|
@ -374,7 +376,7 @@ services:
|
|||
|
||||
## Dummy OIDC Identity Provider
|
||||
dummy-oidc:
|
||||
image: photoprism/dummy-oidc:251124
|
||||
image: photoprism/dummy-oidc:251210
|
||||
stop_grace_period: 5s
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
|
|
|
|||
|
|
@ -5,4 +5,4 @@ FROM golang:1.22-alpine
|
|||
|
||||
RUN go install github.com/skibish/ddns@latest
|
||||
|
||||
CMD ["ddns", "-conf-file", "/config/ddns.yml"]
|
||||
CMD ["ddns", "-conf-file", "/config/ddns.yml"]
|
||||
|
|
|
|||
|
|
@ -79,4 +79,4 @@ WORKDIR /photoprism
|
|||
EXPOSE 2342 2442 2443
|
||||
|
||||
# Keep container running.
|
||||
CMD ["tail", "-f", "/dev/null"]
|
||||
CMD ["tail", "-f", "/dev/null"]
|
||||
|
|
|
|||
|
|
@ -99,4 +99,4 @@ WORKDIR /photoprism
|
|||
EXPOSE 2342 2443
|
||||
|
||||
# keep container running
|
||||
CMD ["tail", "-f", "/dev/null"]
|
||||
CMD ["tail", "-f", "/dev/null"]
|
||||
|
|
|
|||
|
|
@ -1,26 +1,23 @@
|
|||
module caos-test-op
|
||||
|
||||
go 1.17
|
||||
go 1.24.0
|
||||
|
||||
require (
|
||||
github.com/caos/oidc v0.15.10
|
||||
github.com/gorilla/mux v1.8.1
|
||||
github.com/zitadel/oidc v1.13.5
|
||||
gopkg.in/square/go-jose.v2 v2.6.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/caos/logging v0.3.1 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/gorilla/handlers v1.5.2 // indirect
|
||||
github.com/gorilla/schema v1.2.1 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/gorilla/schema v1.4.1 // indirect
|
||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||
github.com/rs/cors v1.11.1 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
golang.org/x/crypto v0.18.0 // indirect
|
||||
golang.org/x/net v0.20.0 // indirect
|
||||
golang.org/x/oauth2 v0.16.0 // indirect
|
||||
golang.org/x/sys v0.16.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/protobuf v1.32.0 // indirect
|
||||
github.com/stretchr/testify v1.11.1 // indirect
|
||||
github.com/zitadel/logging v0.6.2 // indirect
|
||||
golang.org/x/crypto v0.46.0 // indirect
|
||||
golang.org/x/oauth2 v0.34.0 // indirect
|
||||
golang.org/x/sys v0.39.0 // indirect
|
||||
golang.org/x/text v0.32.0 // indirect
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,476 +1,46 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
||||
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
||||
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/caos/logging v0.0.2 h1:ebg5C/HN0ludYR+WkvnFjwSExF4wvyiWPyWGcKMYsoo=
|
||||
github.com/caos/logging v0.0.2/go.mod h1:9LKiDE2ChuGv6CHYif/kiugrfEXu9AwDiFWSreX7Wp0=
|
||||
github.com/caos/logging v0.3.1 h1:892AMeHs09D0e3ZcGB+QDRsZ5+2xtPAsAhOy8eKfztc=
|
||||
github.com/caos/logging v0.3.1/go.mod h1:B8QNS0WDmR2Keac52Fw+XN4ZJkzLDGrcRIPB2Ux4uRo=
|
||||
github.com/caos/oidc v0.15.10 h1:dSzkIvsZR2PSZgvBFFkLJt8A/MujsyLac1yNvBShXuw=
|
||||
github.com/caos/oidc v0.15.10/go.mod h1:4l0PPwdc6BbrdCFhNrRTUddsG292uHGa7gE2DSEIqoU=
|
||||
github.com/caos/oidc v1.3.0 h1:6I4S5XPjFQZx/GZVZaRvemFWYTlO27UvZILrWYL+P60=
|
||||
github.com/caos/oidc v1.3.0/go.mod h1:/EWr+09pcXQbSpCgGna6D+d726NtFAR4KRdG9D0x3OE=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
|
||||
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
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=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-github/v31 v31.0.0/go.mod h1:NQPZol8/1sMoWYGN2yaALIBytu17gAWfhbweiEed3pM=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
|
||||
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
|
||||
github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE=
|
||||
github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w=
|
||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/gorilla/schema v1.2.0 h1:YufUaxZYCKGFuAq3c96BOhjgd5nmXiOY9NGzF247Tsc=
|
||||
github.com/gorilla/schema v1.2.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
|
||||
github.com/gorilla/schema v1.2.1 h1:tjDxcmdb+siIqkTNoV+qRH2mjYdr2hHe5MKXbp61ziM=
|
||||
github.com/gorilla/schema v1.2.1/go.mod h1:Dg5SSm5PV60mhF2NFaTV1xuYYj8tV8NOPRo4FggUMnM=
|
||||
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
|
||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||
github.com/gorilla/schema v1.4.1 h1:jUg5hUjCSDZpNGLuXQOgIWGdlgrIdYvgQ0wZtdK1M3E=
|
||||
github.com/gorilla/schema v1.4.1/go.mod h1:Dg5SSm5PV60mhF2NFaTV1xuYYj8tV8NOPRo4FggUMnM=
|
||||
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
|
||||
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA=
|
||||
github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201217014255-9d1352758620 h1:3wPMTskHO3+O6jqTEXyFcsnuxMQOqYSaHsDxcbUXpqA=
|
||||
golang.org/x/crypto v0.0.0-20201217014255-9d1352758620/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
|
||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
|
||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43 h1:ld7aEMNHoBnnDAX15v1T6z31v8HwR2A9FYOuAhWqkwc=
|
||||
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ=
|
||||
golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191206220618-eeba5f6aabab/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220207234003-57398862261d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
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/zitadel/logging v0.6.2 h1:MW2kDDR0ieQynPZ0KIZPrh9ote2WkxfBif5QoARDQcU=
|
||||
github.com/zitadel/logging v0.6.2/go.mod h1:z6VWLWUkJpnNVDSLzrPSQSQyttysKZ6bCRongw0ROK4=
|
||||
github.com/zitadel/oidc v1.13.5 h1:7jhh68NGZitLqwLiVU9Dtwa4IraJPFF1vS+4UupO93U=
|
||||
github.com/zitadel/oidc v1.13.5/go.mod h1:rHs1DhU3Sv3tnI6bQRVlFa3u0lCwtR7S21WHY+yXgPA=
|
||||
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
||||
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
|
||||
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
|
||||
golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
|
||||
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
|
||||
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
||||
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
|
||||
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
|
||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
|||
104
docker/dummy/oidc/app/mock/client.go
Normal file
104
docker/dummy/oidc/app/mock/client.go
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
package mock
|
||||
|
||||
// revive:disable
|
||||
// Dummy storage implementation for the test OIDC provider; lint strictness is relaxed intentionally.
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/oidc/pkg/oidc"
|
||||
"github.com/zitadel/oidc/pkg/op"
|
||||
)
|
||||
|
||||
// ConfClient represents a fixed client configuration used by the dummy provider.
|
||||
type ConfClient struct {
|
||||
applicationType op.ApplicationType
|
||||
authMethod oidc.AuthMethod
|
||||
responseTypes []oidc.ResponseType
|
||||
grantTypes []oidc.GrantType
|
||||
ID string
|
||||
accessTokenType op.AccessTokenType
|
||||
devMode bool
|
||||
}
|
||||
|
||||
func (c *ConfClient) GetID() string {
|
||||
return c.ID
|
||||
}
|
||||
|
||||
func (c *ConfClient) RedirectURIs() []string {
|
||||
return []string{
|
||||
"https://registered.com/callback",
|
||||
"http://localhost:9999/callback",
|
||||
"http://localhost:5556/auth/callback",
|
||||
"custom://callback",
|
||||
"https://localhost:8443/test/a/instructions-example/callback",
|
||||
"https://op.certification.openid.net:62064/authz_cb",
|
||||
"https://op.certification.openid.net:62064/authz_post",
|
||||
"http://localhost:2342/api/v1/oidc/redirect",
|
||||
"https://app.localssl.dev/api/v1/oidc/redirect",
|
||||
}
|
||||
}
|
||||
func (c *ConfClient) PostLogoutRedirectURIs() []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func (c *ConfClient) LoginURL(id string) string {
|
||||
// return "authorize/callback?id=" + id
|
||||
return "login?id=" + id
|
||||
}
|
||||
|
||||
func (c *ConfClient) ApplicationType() op.ApplicationType {
|
||||
return c.applicationType
|
||||
}
|
||||
|
||||
func (c *ConfClient) AuthMethod() oidc.AuthMethod {
|
||||
return c.authMethod
|
||||
}
|
||||
|
||||
func (c *ConfClient) IDTokenLifetime() time.Duration {
|
||||
return 60 * time.Minute
|
||||
}
|
||||
|
||||
func (c *ConfClient) AccessTokenType() op.AccessTokenType {
|
||||
return c.accessTokenType
|
||||
}
|
||||
|
||||
func (c *ConfClient) ResponseTypes() []oidc.ResponseType {
|
||||
return c.responseTypes
|
||||
}
|
||||
|
||||
func (c *ConfClient) GrantTypes() []oidc.GrantType {
|
||||
return c.grantTypes
|
||||
}
|
||||
|
||||
func (c *ConfClient) DevMode() bool {
|
||||
return c.devMode
|
||||
}
|
||||
|
||||
func (c *ConfClient) AllowedScopes() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ConfClient) RestrictAdditionalIdTokenScopes() func(scopes []string) []string {
|
||||
return func(scopes []string) []string {
|
||||
return scopes
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ConfClient) RestrictAdditionalAccessTokenScopes() func(scopes []string) []string {
|
||||
return func(scopes []string) []string {
|
||||
return scopes
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ConfClient) IsScopeAllowed(scope string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *ConfClient) IDTokenUserinfoClaimsAssertion() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *ConfClient) ClockSkew() time.Duration {
|
||||
return 0
|
||||
}
|
||||
|
|
@ -1,5 +1,9 @@
|
|||
// Package mock provides an in-memory OIDC storage used by the dummy provider in development.
|
||||
package mock
|
||||
|
||||
// revive:disable
|
||||
// Dummy storage implementation for the test OIDC provider; lint strictness is relaxed intentionally.
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
|
|
@ -10,23 +14,36 @@ import (
|
|||
|
||||
"gopkg.in/square/go-jose.v2"
|
||||
|
||||
"github.com/caos/oidc/pkg/oidc"
|
||||
"github.com/caos/oidc/pkg/op"
|
||||
"github.com/zitadel/oidc/pkg/oidc"
|
||||
"github.com/zitadel/oidc/pkg/op"
|
||||
)
|
||||
|
||||
var (
|
||||
a = &AuthRequest{}
|
||||
t bool
|
||||
c string
|
||||
state string
|
||||
)
|
||||
|
||||
// AuthStorage keeps ephemeral keys and auth state for the dummy provider.
|
||||
type AuthStorage struct {
|
||||
key *rsa.PrivateKey
|
||||
kid string
|
||||
}
|
||||
|
||||
// NewAuthStorage constructs the dummy storage with a fresh RSA key.
|
||||
func NewAuthStorage() op.Storage {
|
||||
key, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
b := make([]byte, 16)
|
||||
rand.Read(b)
|
||||
|
||||
if _, err = rand.Read(b); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return &AuthStorage{
|
||||
key: key,
|
||||
|
|
@ -34,9 +51,11 @@ func NewAuthStorage() op.Storage {
|
|||
}
|
||||
}
|
||||
|
||||
// AuthRequest is a lightweight auth request implementation for tests.
|
||||
type AuthRequest struct {
|
||||
ID string
|
||||
ResponseType oidc.ResponseType
|
||||
ResponseMode oidc.ResponseMode
|
||||
RedirectURI string
|
||||
Nonce string
|
||||
ClientID string
|
||||
|
|
@ -86,6 +105,13 @@ func (a *AuthRequest) GetResponseType() oidc.ResponseType {
|
|||
return a.ResponseType
|
||||
}
|
||||
|
||||
func (a *AuthRequest) GetResponseMode() oidc.ResponseMode {
|
||||
if a.ResponseMode != "" {
|
||||
return a.ResponseMode
|
||||
}
|
||||
return oidc.ResponseModeQuery
|
||||
}
|
||||
|
||||
func (a *AuthRequest) GetScopes() []string {
|
||||
return []string{
|
||||
"openid",
|
||||
|
|
@ -108,20 +134,13 @@ func (a *AuthRequest) Done() bool {
|
|||
return true
|
||||
}
|
||||
|
||||
var (
|
||||
a = &AuthRequest{}
|
||||
t bool
|
||||
c string
|
||||
state string
|
||||
)
|
||||
|
||||
func (s *AuthStorage) Health(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateAuthRequest stores the incoming request in memory and returns a stub AuthRequest.
|
||||
func (s *AuthStorage) CreateAuthRequest(_ context.Context, authReq *oidc.AuthRequest, userId string) (op.AuthRequest, error) {
|
||||
fmt.Println("Userid: ", userId)
|
||||
fmt.Println("CreateAuthRequest ID: ", authReq.ID)
|
||||
fmt.Println("CreateAuthRequest CodeChallenge: ", authReq.CodeChallenge)
|
||||
fmt.Println("CreateAuthRequest CodeChallengeMethod: ", authReq.CodeChallengeMethod)
|
||||
fmt.Println("CreateAuthRequest State: ", authReq.State)
|
||||
|
|
@ -132,7 +151,14 @@ func (s *AuthStorage) CreateAuthRequest(_ context.Context, authReq *oidc.AuthReq
|
|||
fmt.Println("CreateAuthRequest Display: ", authReq.Display)
|
||||
fmt.Println("CreateAuthRequest LoginHint: ", authReq.LoginHint)
|
||||
fmt.Println("CreateAuthRequest IDTokenHint: ", authReq.IDTokenHint)
|
||||
a = &AuthRequest{ID: "authReqUserAgentId", ClientID: authReq.ClientID, ResponseType: authReq.ResponseType, Nonce: authReq.Nonce, RedirectURI: authReq.RedirectURI}
|
||||
a = &AuthRequest{
|
||||
ID: "authReqUserAgentId",
|
||||
ClientID: authReq.ClientID,
|
||||
ResponseType: authReq.ResponseType,
|
||||
ResponseMode: authReq.ResponseMode,
|
||||
Nonce: authReq.Nonce,
|
||||
RedirectURI: authReq.RedirectURI,
|
||||
}
|
||||
if authReq.CodeChallenge != "" {
|
||||
a.CodeChallenge = &oidc.CodeChallenge{
|
||||
Challenge: authReq.CodeChallenge,
|
||||
|
|
@ -143,12 +169,14 @@ func (s *AuthStorage) CreateAuthRequest(_ context.Context, authReq *oidc.AuthReq
|
|||
t = false
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func (s *AuthStorage) AuthRequestByCode(_ context.Context, code string) (op.AuthRequest, error) {
|
||||
if code != c {
|
||||
return nil, errors.New("invalid code")
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func (s *AuthStorage) SaveAuthCode(_ context.Context, id, code string) error {
|
||||
if a.ID != id {
|
||||
return errors.New("SaveAuthCode: not found")
|
||||
|
|
@ -156,10 +184,12 @@ func (s *AuthStorage) SaveAuthCode(_ context.Context, id, code string) error {
|
|||
c = code
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *AuthStorage) DeleteAuthRequest(context.Context, string) error {
|
||||
t = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *AuthStorage) AuthRequestByID(_ context.Context, id string) (op.AuthRequest, error) {
|
||||
fmt.Println("AuthRequestByID: ", id)
|
||||
if id != "authReqUserAgentId:usertoken" || t {
|
||||
|
|
@ -167,12 +197,15 @@ func (s *AuthStorage) AuthRequestByID(_ context.Context, id string) (op.AuthRequ
|
|||
}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func (s *AuthStorage) CreateAccessToken(ctx context.Context, request op.TokenRequest) (string, time.Time, error) {
|
||||
return "loginId", time.Now().UTC().Add(5 * time.Minute), nil
|
||||
}
|
||||
|
||||
func (s *AuthStorage) CreateAccessAndRefreshTokens(ctx context.Context, request op.TokenRequest, currentRefreshToken string) (accessTokenID string, newRefreshToken string, expiration time.Time, err error) {
|
||||
return "loginId", "refreshToken", time.Now().UTC().Add(5 * time.Minute), nil
|
||||
}
|
||||
|
||||
func (s *AuthStorage) TokenRequestByRefreshToken(ctx context.Context, refreshToken string) (op.RefreshTokenRequest, error) {
|
||||
if refreshToken != c {
|
||||
return nil, errors.New("invalid token")
|
||||
|
|
@ -183,13 +216,21 @@ func (s *AuthStorage) TokenRequestByRefreshToken(ctx context.Context, refreshTok
|
|||
func (s *AuthStorage) TerminateSession(_ context.Context, userID, clientID string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// RevokeToken is a no-op for the dummy implementation.
|
||||
func (s *AuthStorage) RevokeToken(_ context.Context, tokenOrTokenID string, userID string, clientID string) *oidc.Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *AuthStorage) GetSigningKey(_ context.Context, keyCh chan<- jose.SigningKey) {
|
||||
//keyCh <- jose.SigningKey{Algorithm: jose.RS256, Key: s.key}
|
||||
// keyCh <- jose.SigningKey{Algorithm: jose.RS256, Key: s.key}
|
||||
keyCh <- jose.SigningKey{Algorithm: jose.RS256, Key: &jose.JSONWebKey{Key: s.key, KeyID: s.kid}}
|
||||
}
|
||||
|
||||
func (s *AuthStorage) GetKey(_ context.Context) (*rsa.PrivateKey, error) {
|
||||
return s.key, nil
|
||||
}
|
||||
|
||||
func (s *AuthStorage) GetKeySet(_ context.Context) (*jose.JSONWebKeySet, error) {
|
||||
pubkey := s.key.Public()
|
||||
|
||||
|
|
@ -214,6 +255,7 @@ func (s *AuthStorage) GetKeySet(_ context.Context) (*jose.JSONWebKeySet, error)
|
|||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *AuthStorage) GetKeyByIDAndUserID(_ context.Context, _, _ string) (*jose.JSONWebKey, error) {
|
||||
pubkey := s.key.Public()
|
||||
|
||||
|
|
@ -229,6 +271,7 @@ func (s *AuthStorage) GetClientByClientID(_ context.Context, id string) (op.Clie
|
|||
if id == "none" {
|
||||
return nil, errors.New("GetClientByClientID: not found")
|
||||
}
|
||||
|
||||
var appType op.ApplicationType
|
||||
var authMethod oidc.AuthMethod
|
||||
var accessTokenType op.AccessTokenType
|
||||
|
|
@ -236,22 +279,25 @@ func (s *AuthStorage) GetClientByClientID(_ context.Context, id string) (op.Clie
|
|||
var grantTypes = []oidc.GrantType{
|
||||
oidc.GrantTypeCode,
|
||||
}
|
||||
if id == "web" {
|
||||
|
||||
switch id {
|
||||
case "web":
|
||||
appType = op.ApplicationTypeWeb
|
||||
authMethod = oidc.AuthMethodBasic
|
||||
accessTokenType = op.AccessTokenTypeBearer
|
||||
responseTypes = []oidc.ResponseType{oidc.ResponseTypeCode}
|
||||
} else if id == "native" {
|
||||
case "native":
|
||||
appType = op.ApplicationTypeNative
|
||||
authMethod = oidc.AuthMethodBasic
|
||||
accessTokenType = op.AccessTokenTypeBearer
|
||||
responseTypes = []oidc.ResponseType{oidc.ResponseTypeCode, oidc.ResponseTypeIDToken, oidc.ResponseTypeIDTokenOnly}
|
||||
} else {
|
||||
default:
|
||||
appType = op.ApplicationTypeUserAgent
|
||||
authMethod = oidc.AuthMethodNone
|
||||
accessTokenType = op.AccessTokenTypeJWT
|
||||
responseTypes = []oidc.ResponseType{oidc.ResponseTypeIDToken, oidc.ResponseTypeIDTokenOnly}
|
||||
}
|
||||
|
||||
return &ConfClient{ID: id, applicationType: appType, authMethod: authMethod, accessTokenType: accessTokenType, responseTypes: responseTypes, grantTypes: grantTypes, devMode: true}, nil
|
||||
}
|
||||
|
||||
|
|
@ -262,9 +308,10 @@ func (s *AuthStorage) AuthorizeClientIDSecret(_ context.Context, id string, _ st
|
|||
func (s *AuthStorage) SetUserinfoFromToken(ctx context.Context, userinfo oidc.UserInfoSetter, _, _, _ string) error {
|
||||
return s.SetUserinfoFromScopes(ctx, userinfo, "", "", []string{})
|
||||
}
|
||||
|
||||
func (s *AuthStorage) SetUserinfoFromScopes(ctx context.Context, userinfo oidc.UserInfoSetter, _, _ string, _ []string) error {
|
||||
userinfo.SetSubject(a.GetSubject())
|
||||
//userinfo.SetAddress(oidc.NewUserInfoAddress("Test 789\nPostfach 2", "", "", "", "", ""))
|
||||
// userinfo.SetAddress(oidc.NewUserInfoAddress("Test 789\nPostfach 2", "", "", "", "", ""))
|
||||
userinfo.SetEmail("test@example.com", true)
|
||||
userinfo.SetPhone("0791234567", true)
|
||||
userinfo.SetName("Test")
|
||||
|
|
@ -273,6 +320,7 @@ func (s *AuthStorage) SetUserinfoFromScopes(ctx context.Context, userinfo oidc.U
|
|||
userinfo.SetPreferredUsername("prefname")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *AuthStorage) GetPrivateClaimsFromScopes(_ context.Context, _, _ string, _ []string) (map[string]interface{}, error) {
|
||||
return map[string]interface{}{"private_claim": "test"}, nil
|
||||
}
|
||||
|
|
@ -281,98 +329,12 @@ func (s *AuthStorage) SetIntrospectionFromToken(ctx context.Context, introspect
|
|||
if err := s.SetUserinfoFromScopes(ctx, introspect, "", "", []string{}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
introspect.SetClientID(a.ClientID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *AuthStorage) ValidateJWTProfileScopes(ctx context.Context, userID string, scope []string) ([]string, error) {
|
||||
return scope, nil
|
||||
}
|
||||
|
||||
type ConfClient struct {
|
||||
applicationType op.ApplicationType
|
||||
authMethod oidc.AuthMethod
|
||||
responseTypes []oidc.ResponseType
|
||||
grantTypes []oidc.GrantType
|
||||
ID string
|
||||
accessTokenType op.AccessTokenType
|
||||
devMode bool
|
||||
}
|
||||
|
||||
func (c *ConfClient) GetID() string {
|
||||
return c.ID
|
||||
}
|
||||
func (c *ConfClient) RedirectURIs() []string {
|
||||
return []string{
|
||||
"https://registered.com/callback",
|
||||
"http://localhost:9999/callback",
|
||||
"http://localhost:5556/auth/callback",
|
||||
"custom://callback",
|
||||
"https://localhost:8443/test/a/instructions-example/callback",
|
||||
"https://op.certification.openid.net:62064/authz_cb",
|
||||
"https://op.certification.openid.net:62064/authz_post",
|
||||
"http://localhost:2342/api/v1/oidc/redirect",
|
||||
"https://app.localssl.dev/api/v1/oidc/redirect",
|
||||
}
|
||||
}
|
||||
func (c *ConfClient) PostLogoutRedirectURIs() []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func (c *ConfClient) LoginURL(id string) string {
|
||||
//return "authorize/callback?id=" + id
|
||||
return "login?id=" + id
|
||||
}
|
||||
|
||||
func (c *ConfClient) ApplicationType() op.ApplicationType {
|
||||
return c.applicationType
|
||||
}
|
||||
|
||||
func (c *ConfClient) AuthMethod() oidc.AuthMethod {
|
||||
return c.authMethod
|
||||
}
|
||||
|
||||
func (c *ConfClient) IDTokenLifetime() time.Duration {
|
||||
return 60 * time.Minute
|
||||
}
|
||||
func (c *ConfClient) AccessTokenType() op.AccessTokenType {
|
||||
return c.accessTokenType
|
||||
}
|
||||
func (c *ConfClient) ResponseTypes() []oidc.ResponseType {
|
||||
return c.responseTypes
|
||||
}
|
||||
func (c *ConfClient) GrantTypes() []oidc.GrantType {
|
||||
return c.grantTypes
|
||||
}
|
||||
|
||||
func (c *ConfClient) DevMode() bool {
|
||||
return c.devMode
|
||||
}
|
||||
|
||||
func (c *ConfClient) AllowedScopes() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ConfClient) RestrictAdditionalIdTokenScopes() func(scopes []string) []string {
|
||||
return func(scopes []string) []string {
|
||||
return scopes
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ConfClient) RestrictAdditionalAccessTokenScopes() func(scopes []string) []string {
|
||||
return func(scopes []string) []string {
|
||||
return scopes
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ConfClient) IsScopeAllowed(scope string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *ConfClient) IDTokenUserinfoClaimsAssertion() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *ConfClient) ClockSkew() time.Duration {
|
||||
return 0
|
||||
}
|
||||
|
|
|
|||
27
docker/dummy/oidc/app/mock/storage_test.go
Normal file
27
docker/dummy/oidc/app/mock/storage_test.go
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
package mock
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/zitadel/oidc/pkg/oidc"
|
||||
)
|
||||
|
||||
func TestAuthRequestResponseModeDefault(t *testing.T) {
|
||||
req := &AuthRequest{}
|
||||
if got := req.GetResponseMode(); got != oidc.ResponseModeQuery {
|
||||
t.Fatalf("expected default response mode %q, got %q", oidc.ResponseModeQuery, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRevokeTokenNoError(t *testing.T) {
|
||||
s := &AuthStorage{}
|
||||
if err := s.RevokeToken(
|
||||
context.TODO(),
|
||||
"token",
|
||||
"user",
|
||||
"client",
|
||||
); err != nil {
|
||||
t.Fatalf("expected nil error from RevokeToken, got %v", err)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +1,33 @@
|
|||
// Command dummy-oidc starts a minimal OIDC provider used by docker-compose for local development.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"log"
|
||||
"net/http"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/zitadel/oidc/pkg/op"
|
||||
|
||||
"caos-test-op/mock"
|
||||
"github.com/caos/oidc/pkg/op"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx := context.Background()
|
||||
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||
defer stop()
|
||||
|
||||
b := make([]byte, 32)
|
||||
rand.Read(b)
|
||||
_, _ = rand.Read(b)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
log.Printf("failed to seed crypto key: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
port := "9998"
|
||||
config := &op.Config{
|
||||
|
|
@ -29,26 +39,39 @@ func main() {
|
|||
|
||||
handler, err := op.NewOpenIDProvider(ctx, config, storage)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
log.Printf("failed to create OIDC provider: %v", err)
|
||||
return
|
||||
}
|
||||
router := handler.HttpHandler().(*mux.Router)
|
||||
router.Methods("GET").Path("/login").HandlerFunc(HandleLogin)
|
||||
server := &http.Server{
|
||||
Addr: ":" + port,
|
||||
Handler: router,
|
||||
Addr: ":" + port,
|
||||
Handler: router,
|
||||
ReadHeaderTimeout: 5 * time.Second,
|
||||
WriteTimeout: 10 * time.Second,
|
||||
IdleTimeout: 30 * time.Second,
|
||||
}
|
||||
err = server.ListenAndServe()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
_ = server.Shutdown(context.Background())
|
||||
}()
|
||||
|
||||
if err = server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
log.Printf("OIDC server stopped with error: %v", err)
|
||||
}
|
||||
<-ctx.Done()
|
||||
}
|
||||
|
||||
func HandleLogin(w http.ResponseWriter, r *http.Request) {
|
||||
r.ParseForm()
|
||||
// HandleLogin mocks a login page and immediately redirects with a user token.
|
||||
if err := r.ParseForm(); err != nil {
|
||||
http.Error(w, "invalid login request", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
requestId := r.Form.Get("id")
|
||||
// simulate user login and retrieve a token that indicates a successfully logged-in user
|
||||
usertoken := requestId + ":usertoken"
|
||||
userToken := requestId + ":usertoken"
|
||||
|
||||
http.Redirect(w, r, "/authorize/callback?id="+usertoken, http.StatusFound)
|
||||
http.Redirect(w, r, "/authorize/callback?id="+userToken, http.StatusFound)
|
||||
}
|
||||
|
|
|
|||
36
docker/dummy/oidc/app/server_test.go
Normal file
36
docker/dummy/oidc/app/server_test.go
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHandleLoginRedirects(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodGet, "/login?id=abc123", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
HandleLogin(w, req)
|
||||
|
||||
resp := w.Result()
|
||||
if resp.StatusCode != http.StatusFound {
|
||||
t.Fatalf("expected status %d, got %d", http.StatusFound, resp.StatusCode)
|
||||
}
|
||||
location := resp.Header.Get("Location")
|
||||
if location != "/authorize/callback?id=abc123:usertoken" {
|
||||
t.Fatalf("unexpected redirect location: %s", location)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleLoginBadRequest(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodPost, "/login", strings.NewReader("%zz"))
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
HandleLogin(w, req)
|
||||
|
||||
if w.Result().StatusCode != http.StatusBadRequest {
|
||||
t.Fatalf("expected bad request, got %d", w.Result().StatusCode)
|
||||
}
|
||||
}
|
||||
|
|
@ -16,4 +16,4 @@ EXPOSE 80
|
|||
COPY /docker/dummy/webdav/config.yml /webdav/config.yml
|
||||
COPY /docker/dummy/webdav/files /webdav/files
|
||||
|
||||
CMD webdav -c /webdav/config.yml
|
||||
CMD ["webdav", "-c", "/webdav/config.yml"]
|
||||
|
|
|
|||
|
|
@ -23,4 +23,4 @@ VOLUME "/go"
|
|||
EXPOSE 8888
|
||||
|
||||
ENTRYPOINT ["/usr/bin/tini", "--"]
|
||||
CMD ["/goproxy", "-listen", "0.0.0.0:8888"]
|
||||
CMD ["/goproxy", "-listen", "0.0.0.0:8888"]
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ ENV PHOTOPRISM_ARCH=$TARGETARCH \
|
|||
TF_VERSION=1.15.2 \
|
||||
TF_CPP_MIN_LOG_LEVEL=4 \
|
||||
MALLOC_ARENA_MAX=2 \
|
||||
PROG="photoprism" \
|
||||
PROG="photoprism"
|
||||
PHOTOPRISM_ASSETS_PATH="/opt/photoprism/assets" \
|
||||
PHOTOPRISM_IMPORT_PATH="/photoprism/import" \
|
||||
PHOTOPRISM_ORIGINALS_PATH="/photoprism/originals" \
|
||||
|
|
|
|||
|
|
@ -3,16 +3,9 @@ node_modules/
|
|||
tests/screenshots/
|
||||
tests/acceptance/screenshots/
|
||||
tests/upload-files/
|
||||
*.html
|
||||
*.md
|
||||
.*
|
||||
.idea
|
||||
.codex
|
||||
.local
|
||||
.config
|
||||
.github
|
||||
.tmp
|
||||
.local
|
||||
.cache
|
||||
.gocache
|
||||
.var
|
||||
*.html
|
||||
*.md
|
||||
.*
|
||||
428
frontend/package-lock.json
generated
428
frontend/package-lock.json
generated
|
|
@ -20,21 +20,21 @@
|
|||
"@mdi/font": "^7.4.47",
|
||||
"@testing-library/jest-dom": "^6.9.1",
|
||||
"@testing-library/react": "^16.3.0",
|
||||
"@vitejs/plugin-react": "^5.1.1",
|
||||
"@vitejs/plugin-vue": "^6.0.2",
|
||||
"@vitejs/plugin-react": "^5.1.2",
|
||||
"@vitejs/plugin-vue": "^6.0.3",
|
||||
"@vitest/browser": "^3.2.4",
|
||||
"@vitest/coverage-v8": "^3.2.4",
|
||||
"@vitest/ui": "^3.2.4",
|
||||
"@vue/compiler-sfc": "^3.5.18",
|
||||
"@vue/language-server": "^3.1.5",
|
||||
"@vue/language-server": "^3.1.8",
|
||||
"@vue/test-utils": "^2.4.6",
|
||||
"@vvo/tzdb": "^6.197.0",
|
||||
"@vvo/tzdb": "^6.198.0",
|
||||
"axios": "^1.13.2",
|
||||
"axios-mock-adapter": "^2.1.0",
|
||||
"babel-loader": "^10.0.0",
|
||||
"babel-plugin-istanbul": "^7.0.1",
|
||||
"babel-plugin-polyfill-corejs3": "^0.13.0",
|
||||
"browserslist": "^4.28.0",
|
||||
"browserslist": "^4.28.1",
|
||||
"cheerio": "1.0.0-rc.12",
|
||||
"core-js": "^3.47.0",
|
||||
"cross-env": "^7.0.3",
|
||||
|
|
@ -60,7 +60,7 @@
|
|||
"i": "^0.3.7",
|
||||
"jsdom": "^26.1.0",
|
||||
"luxon": "^3.7.2",
|
||||
"maplibre-gl": "^5.13.0",
|
||||
"maplibre-gl": "^5.14.0",
|
||||
"memoize-one": "^6.0.0",
|
||||
"mini-css-extract-plugin": "^2.9.4",
|
||||
"minimist": "^1.2.8",
|
||||
|
|
@ -71,15 +71,15 @@
|
|||
"postcss": "^8.5.6",
|
||||
"postcss-import": "^16.1.1",
|
||||
"postcss-loader": "^8.2.0",
|
||||
"postcss-preset-env": "^10.4.0",
|
||||
"postcss-preset-env": "^10.5.0",
|
||||
"postcss-reporter": "^7.1.0",
|
||||
"postcss-url": "^10.1.3",
|
||||
"prettier": "^3.7.2",
|
||||
"prettier": "^3.7.4",
|
||||
"pubsub-js": "^1.9.5",
|
||||
"regenerator-runtime": "^0.14.1",
|
||||
"resolve-url-loader": "^5.0.0",
|
||||
"sanitize-html": "^2.17.0",
|
||||
"sass": "^1.94.2",
|
||||
"sass": "^1.96.0",
|
||||
"sass-loader": "^16.0.6",
|
||||
"sockette": "^2.0.6",
|
||||
"style-loader": "^4.0.0",
|
||||
|
|
@ -94,11 +94,11 @@
|
|||
"vue-loader": "^17.4.2",
|
||||
"vue-loader-plugin": "^1.3.0",
|
||||
"vue-luxon": "^0.10.0",
|
||||
"vue-router": "^4.6.3",
|
||||
"vue-router": "^4.6.4",
|
||||
"vue-sanitize-directive": "^0.2.1",
|
||||
"vue-style-loader": "^4.1.3",
|
||||
"vue3-gettext": "^2.4.0",
|
||||
"vuetify": "^3.11.0",
|
||||
"vuetify": "^3.11.3",
|
||||
"webpack": "^5.103.0",
|
||||
"webpack-bundle-analyzer": "^4.10.2",
|
||||
"webpack-cli": "^6.0.1",
|
||||
|
|
@ -2571,6 +2571,28 @@
|
|||
"postcss": "^8.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@csstools/postcss-position-area-property": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/postcss-position-area-property/-/postcss-position-area-property-1.0.0.tgz",
|
||||
"integrity": "sha512-fUP6KR8qV2NuUZV3Cw8itx0Ep90aRjAZxAEzC3vrl6yjFv+pFsQbR18UuQctEKmA72K9O27CoYiKEgXxkqjg8Q==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/csstools"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/csstools"
|
||||
}
|
||||
],
|
||||
"license": "MIT-0",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"postcss": "^8.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@csstools/postcss-progressive-custom-properties": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-4.2.1.tgz",
|
||||
|
|
@ -2731,6 +2753,32 @@
|
|||
"postcss": "^8.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@csstools/postcss-system-ui-font-family": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/postcss-system-ui-font-family/-/postcss-system-ui-font-family-1.0.0.tgz",
|
||||
"integrity": "sha512-s3xdBvfWYfoPSBsikDXbuorcMG1nN1M6GdU0qBsGfcmNR0A/qhloQZpTxjA3Xsyrk1VJvwb2pOfiOT3at/DuIQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/csstools"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/csstools"
|
||||
}
|
||||
],
|
||||
"license": "MIT-0",
|
||||
"dependencies": {
|
||||
"@csstools/css-parser-algorithms": "^3.0.5",
|
||||
"@csstools/css-tokenizer": "^3.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"postcss": "^8.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@csstools/postcss-text-decoration-shorthand": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-4.0.3.tgz",
|
||||
|
|
@ -3892,9 +3940,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@maplibre/maplibre-gl-style-spec": {
|
||||
"version": "24.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-24.3.1.tgz",
|
||||
"integrity": "sha512-TUM5JD40H2mgtVXl5IwWz03BuQabw8oZQLJTmPpJA0YTYF+B+oZppy5lNMO6bMvHzB+/5mxqW9VLG3wFdeqtOw==",
|
||||
"version": "24.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-24.4.0.tgz",
|
||||
"integrity": "sha512-VVuNV2Yf0+yQoth4qbdIPE0qKS6nIG5Atki9BVHZ7R7+0lZyxqxwrh0XVNA5YkuKuytFg/1i3VMyJQnp2EtOqw==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@mapbox/jsonlint-lines-primitives": "~2.0.2",
|
||||
|
|
@ -3912,9 +3960,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@maplibre/mlt": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@maplibre/mlt/-/mlt-1.1.0.tgz",
|
||||
"integrity": "sha512-anR8WxKIgZUJQLlZtID0v06wd9Q//9K/6lLLU3dOzmeO/xLEzAwmEqP24jEnEUBcnZGkM4vidz9H6Q4guNAAlw==",
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@maplibre/mlt/-/mlt-1.1.2.tgz",
|
||||
"integrity": "sha512-SQKdJ909VGROkA6ovJgtHNs9YXV4YXUPS+VaZ50I2Mt951SLlUm2Cv34x5Xwc1HiFlsd3h2Yrs5cn7xzqBmENw==",
|
||||
"license": "(MIT OR Apache-2.0)",
|
||||
"dependencies": {
|
||||
"@mapbox/point-geometry": "^1.1.0"
|
||||
|
|
@ -4279,9 +4327,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@rolldown/pluginutils": {
|
||||
"version": "1.0.0-beta.47",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.47.tgz",
|
||||
"integrity": "sha512-8QagwMH3kNCuzD8EWL8R2YPW5e4OrHNSAHRFDdmFqEwEaD/KcNKjVoumo+gP2vW5eKB2UPbM6vTYiGZX0ixLnw==",
|
||||
"version": "1.0.0-beta.53",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.53.tgz",
|
||||
"integrity": "sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@rollup/plugin-node-resolve": {
|
||||
|
|
@ -4918,9 +4966,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "24.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz",
|
||||
"integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
|
||||
"version": "25.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.1.tgz",
|
||||
"integrity": "sha512-czWPzKIAXucn9PtsttxmumiQ9N0ok9FrBwgRWrwmVLlp86BrMExzvXRLFYRJ+Ex3g6yqj+KuaxfX1JTgV2lpfg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~7.16.0"
|
||||
|
|
@ -4975,15 +5023,15 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@vitejs/plugin-react": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.1.tgz",
|
||||
"integrity": "sha512-WQfkSw0QbQ5aJ2CHYw23ZGkqnRwqKHD/KYsMeTkZzPT4Jcf0DcBxBtwMJxnu6E7oxw5+JC6ZAiePgh28uJ1HBA==",
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.2.tgz",
|
||||
"integrity": "sha512-EcA07pHJouywpzsoTUqNh5NwGayl2PPVEJKUSinGGSxFGYn+shYbqMGBg6FXDqgXum9Ou/ecb+411ssw8HImJQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.28.5",
|
||||
"@babel/plugin-transform-react-jsx-self": "^7.27.1",
|
||||
"@babel/plugin-transform-react-jsx-source": "^7.27.1",
|
||||
"@rolldown/pluginutils": "1.0.0-beta.47",
|
||||
"@rolldown/pluginutils": "1.0.0-beta.53",
|
||||
"@types/babel__core": "^7.20.5",
|
||||
"react-refresh": "^0.18.0"
|
||||
},
|
||||
|
|
@ -4995,27 +5043,21 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@vitejs/plugin-vue": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.2.tgz",
|
||||
"integrity": "sha512-iHmwV3QcVGGvSC1BG5bZ4z6iwa1SOpAPWmnjOErd4Ske+lZua5K9TtAVdx0gMBClJ28DViCbSmZitjWZsWO3LA==",
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.3.tgz",
|
||||
"integrity": "sha512-TlGPkLFLVOY3T7fZrwdvKpjprR3s4fxRln0ORDo1VQ7HHyxJwTlrjKU3kpVWTlaAjIEuCTokmjkZnr8Tpc925w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@rolldown/pluginutils": "1.0.0-beta.50"
|
||||
"@rolldown/pluginutils": "1.0.0-beta.53"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0",
|
||||
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0",
|
||||
"vue": "^3.2.25"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitejs/plugin-vue/node_modules/@rolldown/pluginutils": {
|
||||
"version": "1.0.0-beta.50",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.50.tgz",
|
||||
"integrity": "sha512-5e76wQiQVeL1ICOZVUg4LSOVYg9jyhGCin+icYozhsUzM+fHE7kddi1bdiE0jwVqTfkjba3jUFbEkoC9WkdvyA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@vitest/browser": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/browser/-/browser-3.2.4.tgz",
|
||||
|
|
@ -5214,23 +5256,23 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@volar/language-core": {
|
||||
"version": "2.4.23",
|
||||
"resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.23.tgz",
|
||||
"integrity": "sha512-hEEd5ET/oSmBC6pi1j6NaNYRWoAiDhINbT8rmwtINugR39loROSlufGdYMF9TaKGfz+ViGs1Idi3mAhnuPcoGQ==",
|
||||
"version": "2.4.26",
|
||||
"resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.26.tgz",
|
||||
"integrity": "sha512-hH0SMitMxnB43OZpyF1IFPS9bgb2I3bpCh76m2WEK7BE0A0EzpYsRp0CCH2xNKshr7kacU5TQBLYn4zj7CG60A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@volar/source-map": "2.4.23"
|
||||
"@volar/source-map": "2.4.26"
|
||||
}
|
||||
},
|
||||
"node_modules/@volar/language-server": {
|
||||
"version": "2.4.23",
|
||||
"resolved": "https://registry.npmjs.org/@volar/language-server/-/language-server-2.4.23.tgz",
|
||||
"integrity": "sha512-k0iO+tybMGMMyrNdWOxgFkP0XJTdbH0w+WZlM54RzJU3WZSjHEupwL30klpM7ep4FO6qyQa03h+VcGHD4Q8gEg==",
|
||||
"version": "2.4.26",
|
||||
"resolved": "https://registry.npmjs.org/@volar/language-server/-/language-server-2.4.26.tgz",
|
||||
"integrity": "sha512-Xsyu+VDgM8TyVkQfBz2aIViSEOgH2un0gIJlp0M8rssDDLCqr4ssQzwHOyPf7sT7UIjrlAMnJvRkC/u0mmgtYw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@volar/language-core": "2.4.23",
|
||||
"@volar/language-service": "2.4.23",
|
||||
"@volar/typescript": "2.4.23",
|
||||
"@volar/language-core": "2.4.26",
|
||||
"@volar/language-service": "2.4.26",
|
||||
"@volar/typescript": "2.4.26",
|
||||
"path-browserify": "^1.0.1",
|
||||
"request-light": "^0.7.0",
|
||||
"vscode-languageserver": "^9.0.1",
|
||||
|
|
@ -5240,30 +5282,30 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@volar/language-service": {
|
||||
"version": "2.4.23",
|
||||
"resolved": "https://registry.npmjs.org/@volar/language-service/-/language-service-2.4.23.tgz",
|
||||
"integrity": "sha512-h5mU9DZ/6u3LCB9xomJtorNG6awBNnk9VuCioGsp6UtFiM8amvS5FcsaC3dabdL9zO0z+Gq9vIEMb/5u9K6jGQ==",
|
||||
"version": "2.4.26",
|
||||
"resolved": "https://registry.npmjs.org/@volar/language-service/-/language-service-2.4.26.tgz",
|
||||
"integrity": "sha512-ZBPRR1ytXttSV5X4VPvEQR/glxs+7/4IOJIBCOW3/EJk4z77R4mF2y4wM3fNgOXXZT5h16j3sC5w+LGNkz2VlA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@volar/language-core": "2.4.23",
|
||||
"@volar/language-core": "2.4.26",
|
||||
"vscode-languageserver-protocol": "^3.17.5",
|
||||
"vscode-languageserver-textdocument": "^1.0.11",
|
||||
"vscode-uri": "^3.0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/@volar/source-map": {
|
||||
"version": "2.4.23",
|
||||
"resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.23.tgz",
|
||||
"integrity": "sha512-Z1Uc8IB57Lm6k7q6KIDu/p+JWtf3xsXJqAX/5r18hYOTpJyBn0KXUR8oTJ4WFYOcDzWC9n3IflGgHowx6U6z9Q==",
|
||||
"version": "2.4.26",
|
||||
"resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.26.tgz",
|
||||
"integrity": "sha512-JJw0Tt/kSFsIRmgTQF4JSt81AUSI1aEye5Zl65EeZ8H35JHnTvFGmpDOBn5iOxd48fyGE+ZvZBp5FcgAy/1Qhw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@volar/typescript": {
|
||||
"version": "2.4.23",
|
||||
"resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.23.tgz",
|
||||
"integrity": "sha512-lAB5zJghWxVPqfcStmAP1ZqQacMpe90UrP5RJ3arDyrhy4aCUQqmxPPLB2PWDKugvylmO41ljK7vZ+t6INMTag==",
|
||||
"version": "2.4.26",
|
||||
"resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.26.tgz",
|
||||
"integrity": "sha512-N87ecLD48Sp6zV9zID/5yuS1+5foj0DfuYGdQ6KHj/IbKvyKv1zNX6VCmnKYwtmHadEO6mFc2EKISiu3RDPAvA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@volar/language-core": "2.4.23",
|
||||
"@volar/language-core": "2.4.26",
|
||||
"path-browserify": "^1.0.1",
|
||||
"vscode-uri": "^3.0.8"
|
||||
}
|
||||
|
|
@ -5356,12 +5398,12 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@vue/language-core": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-3.1.5.tgz",
|
||||
"integrity": "sha512-FMcqyzWN+sYBeqRMWPGT2QY0mUasZMVIuHvmb5NT3eeqPrbHBYtCP8JWEUCDCgM+Zr62uuWY/qoeBrPrzfa78w==",
|
||||
"version": "3.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-3.1.8.tgz",
|
||||
"integrity": "sha512-PfwAW7BLopqaJbneChNL6cUOTL3GL+0l8paYP5shhgY5toBNidWnMXWM+qDwL7MC9+zDtzCF2enT8r6VPu64iw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@volar/language-core": "2.4.23",
|
||||
"@volar/language-core": "2.4.26",
|
||||
"@vue/compiler-dom": "^3.5.0",
|
||||
"@vue/shared": "^3.5.0",
|
||||
"alien-signals": "^3.0.0",
|
||||
|
|
@ -5379,15 +5421,15 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@vue/language-server": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@vue/language-server/-/language-server-3.1.5.tgz",
|
||||
"integrity": "sha512-JhcikpL5hPbFKlhM1Cijj99cmPx1LNffzpMZIZc9AXiDhpXACHVasN8wX2o76m3LlJZhpK97PLNOjLK8eD4Swg==",
|
||||
"version": "3.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@vue/language-server/-/language-server-3.1.8.tgz",
|
||||
"integrity": "sha512-Tw7Dw4qaDfW0s01gAdFIxX8ZcllrLMI1XXZRx3QI9iIcpJVJGevW6+TYhg/xvpT2NKGwkImzM9UoXZwWlnNTEg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@volar/language-server": "2.4.23",
|
||||
"@vue/language-core": "3.1.5",
|
||||
"@vue/language-service": "3.1.5",
|
||||
"@vue/typescript-plugin": "3.1.5",
|
||||
"@volar/language-server": "2.4.26",
|
||||
"@vue/language-core": "3.1.8",
|
||||
"@vue/language-service": "3.1.8",
|
||||
"@vue/typescript-plugin": "3.1.8",
|
||||
"vscode-uri": "^3.0.8"
|
||||
},
|
||||
"bin": {
|
||||
|
|
@ -5398,22 +5440,22 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@vue/language-service": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@vue/language-service/-/language-service-3.1.5.tgz",
|
||||
"integrity": "sha512-tSbHGh+Kl8r6crkfQMj80NDlL7X0bAGSwXBw0VfE1SYcROaoog55wKim/hpZCSTbN+SkZ9gY6FQ6/y0KJN4xEA==",
|
||||
"version": "3.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@vue/language-service/-/language-service-3.1.8.tgz",
|
||||
"integrity": "sha512-/dcZn/hSOlv4njHqvti+lH/txyC2BPs60Ih96Z2RsJ4iCu4NBCe9wYxqLhdtYSBZaDHxDcI2O7cI4fREO7brIw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@volar/language-service": "2.4.23",
|
||||
"@vue/language-core": "3.1.5",
|
||||
"@volar/language-service": "2.4.26",
|
||||
"@vue/language-core": "3.1.8",
|
||||
"@vue/shared": "^3.5.0",
|
||||
"path-browserify": "^1.0.1",
|
||||
"volar-service-css": "0.0.67",
|
||||
"volar-service-emmet": "0.0.67",
|
||||
"volar-service-html": "0.0.67",
|
||||
"volar-service-json": "0.0.67",
|
||||
"volar-service-pug": "0.0.67",
|
||||
"volar-service-pug-beautify": "0.0.67",
|
||||
"volar-service-typescript": "0.0.67",
|
||||
"volar-service-css": "0.0.68",
|
||||
"volar-service-emmet": "0.0.68",
|
||||
"volar-service-html": "0.0.68",
|
||||
"volar-service-json": "0.0.68",
|
||||
"volar-service-pug": "0.0.68",
|
||||
"volar-service-pug-beautify": "0.0.68",
|
||||
"volar-service-typescript": "0.0.68",
|
||||
"vscode-html-languageservice": "^5.2.0",
|
||||
"vscode-uri": "^3.0.8"
|
||||
}
|
||||
|
|
@ -5479,13 +5521,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@vue/typescript-plugin": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@vue/typescript-plugin/-/typescript-plugin-3.1.5.tgz",
|
||||
"integrity": "sha512-jZU02lOiq74nX/PlxqJpLzX3EH52uAkInM7w6yAIs8hXAzs+UmWyeE78Gig8/5NM/t/qK9dgRsv1jQWveEK9JQ==",
|
||||
"version": "3.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@vue/typescript-plugin/-/typescript-plugin-3.1.8.tgz",
|
||||
"integrity": "sha512-pGXOtxzILWJm0YVTiG9JTzge9a3bX6462Aif1FVYPyy+B/CbBluEDSse7r52Vs5tceaf+rjfaE/q4B9aGNNUzw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@volar/typescript": "2.4.23",
|
||||
"@vue/language-core": "3.1.5",
|
||||
"@volar/typescript": "2.4.26",
|
||||
"@vue/language-core": "3.1.8",
|
||||
"@vue/shared": "^3.5.0",
|
||||
"path-browserify": "^1.0.1"
|
||||
}
|
||||
|
|
@ -5504,9 +5546,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@vvo/tzdb": {
|
||||
"version": "6.197.0",
|
||||
"resolved": "https://registry.npmjs.org/@vvo/tzdb/-/tzdb-6.197.0.tgz",
|
||||
"integrity": "sha512-SB2jDs9lnYvYaGbzj/KMJrq7YSRshbtL4z1pN5evvzMlKWpAnsEtSVcXd5p5mY5NGwc+/dm0iv4wZxlg9vtr5w==",
|
||||
"version": "6.198.0",
|
||||
"resolved": "https://registry.npmjs.org/@vvo/tzdb/-/tzdb-6.198.0.tgz",
|
||||
"integrity": "sha512-bNRWBhWYl0edVgyX6AYbhoCM2tk2lXJjGCyO2VDc2xn6Dw8dLd7WGj2DDXkVOkmOIQTNjEAcxrEpIzz5pWVwFg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@webassemblyjs/ast": {
|
||||
|
|
@ -6299,9 +6341,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/baseline-browser-mapping": {
|
||||
"version": "2.8.32",
|
||||
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.32.tgz",
|
||||
"integrity": "sha512-OPz5aBThlyLFgxyhdwf/s2+8ab3OvT7AdTNvKHBwpXomIYeXqpUUuT8LrdtxZSsWJ4R4CU1un4XGh5Ez3nlTpw==",
|
||||
"version": "2.9.6",
|
||||
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.6.tgz",
|
||||
"integrity": "sha512-v9BVVpOTLB59C9E7aSnmIF8h7qRsFpx+A2nugVMTszEOMcfjlZMsXRm4LF23I3Z9AJxc8ANpIvzbzONoX9VJlg==",
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"baseline-browser-mapping": "dist/cli.js"
|
||||
|
|
@ -6358,9 +6400,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/browserslist": {
|
||||
"version": "4.28.0",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.0.tgz",
|
||||
"integrity": "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==",
|
||||
"version": "4.28.1",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
|
||||
"integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
|
|
@ -6377,11 +6419,11 @@
|
|||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.8.25",
|
||||
"caniuse-lite": "^1.0.30001754",
|
||||
"electron-to-chromium": "^1.5.249",
|
||||
"baseline-browser-mapping": "^2.9.0",
|
||||
"caniuse-lite": "^1.0.30001759",
|
||||
"electron-to-chromium": "^1.5.263",
|
||||
"node-releases": "^2.0.27",
|
||||
"update-browserslist-db": "^1.1.4"
|
||||
"update-browserslist-db": "^1.2.0"
|
||||
},
|
||||
"bin": {
|
||||
"browserslist": "cli.js"
|
||||
|
|
@ -6491,9 +6533,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001757",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001757.tgz",
|
||||
"integrity": "sha512-r0nnL/I28Zi/yjk1el6ilj27tKcdjLsNqAOZr0yVjWPrSQyHgKI2INaEWw21bAQSv2LXRt1XuCS/GomNpWOxsQ==",
|
||||
"version": "1.0.30001760",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001760.tgz",
|
||||
"integrity": "sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
|
|
@ -7075,9 +7117,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cssdb": {
|
||||
"version": "8.4.2",
|
||||
"resolved": "https://registry.npmjs.org/cssdb/-/cssdb-8.4.2.tgz",
|
||||
"integrity": "sha512-PzjkRkRUS+IHDJohtxkIczlxPPZqRo0nXplsYXOMBRPjcVRjj1W4DfvRgshUYTVuUigU7ptVYkFJQ7abUB0nyg==",
|
||||
"version": "8.5.2",
|
||||
"resolved": "https://registry.npmjs.org/cssdb/-/cssdb-8.5.2.tgz",
|
||||
"integrity": "sha512-Pmoj9RmD8RIoIzA2EQWO4D4RMeDts0tgAH0VXdlNdxjuBGI3a9wMOIcUwaPNmD4r2qtIa06gqkIf7sECl+cBCg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
|
|
@ -7611,9 +7653,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.262",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.262.tgz",
|
||||
"integrity": "sha512-NlAsMteRHek05jRUxUR0a5jpjYq9ykk6+kO0yRaMi5moe7u0fVIOeQ3Y30A8dIiWFBNUoQGi1ljb1i5VtS9WQQ==",
|
||||
"version": "1.5.267",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz",
|
||||
"integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/emmet": {
|
||||
|
|
@ -7648,9 +7690,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/enhanced-resolve": {
|
||||
"version": "5.18.3",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz",
|
||||
"integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==",
|
||||
"version": "5.18.4",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.4.tgz",
|
||||
"integrity": "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.2.4",
|
||||
|
|
@ -8479,9 +8521,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/expect-type": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz",
|
||||
"integrity": "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==",
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz",
|
||||
"integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
|
|
@ -10803,9 +10845,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/maplibre-gl": {
|
||||
"version": "5.13.0",
|
||||
"resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-5.13.0.tgz",
|
||||
"integrity": "sha512-UsIVP34rZdM4TjrjhwBAhbC3HT7AzFx9p/draiAPlLr8/THozZF6WmJnZ9ck4q94uO55z7P7zoGCh+AZVoagsQ==",
|
||||
"version": "5.14.0",
|
||||
"resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-5.14.0.tgz",
|
||||
"integrity": "sha512-O2ok6N/bQ9NA9nJ22r/PRQQYkUe9JwfDMjBPkQ+8OwsVH4TpA5skIAM2wc0k+rni5lVbAVONVyBvgi1rF2vEPA==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@mapbox/geojson-rewind": "^0.5.2",
|
||||
|
|
@ -10816,8 +10858,8 @@
|
|||
"@mapbox/vector-tile": "^2.0.4",
|
||||
"@mapbox/whoots-js": "^3.1.0",
|
||||
"@maplibre/maplibre-gl-style-spec": "^24.3.1",
|
||||
"@maplibre/mlt": "^1.1.0",
|
||||
"@maplibre/vt-pbf": "^4.0.3",
|
||||
"@maplibre/mlt": "^1.1.2",
|
||||
"@maplibre/vt-pbf": "^4.1.0",
|
||||
"@types/geojson": "^7946.0.16",
|
||||
"@types/geojson-vt": "3.2.5",
|
||||
"@types/supercluster": "^7.1.3",
|
||||
|
|
@ -11188,9 +11230,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/nwsapi": {
|
||||
"version": "2.2.22",
|
||||
"resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.22.tgz",
|
||||
"integrity": "sha512-ujSMe1OWVn55euT1ihwCI1ZcAaAU3nxUiDwfDQldc51ZXaB9m2AyOn6/jh1BLe2t/G8xd6uKG1UBF2aZJeg2SQ==",
|
||||
"version": "2.2.23",
|
||||
"resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.23.tgz",
|
||||
"integrity": "sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/object-assign": {
|
||||
|
|
@ -12782,9 +12824,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/postcss-preset-env": {
|
||||
"version": "10.4.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-10.4.0.tgz",
|
||||
"integrity": "sha512-2kqpOthQ6JhxqQq1FSAAZGe9COQv75Aw8WbsOvQVNJ2nSevc9Yx/IKZGuZ7XJ+iOTtVon7LfO7ELRzg8AZ+sdw==",
|
||||
"version": "10.5.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-10.5.0.tgz",
|
||||
"integrity": "sha512-xgxFQPAPxeWmsgy8cR7GM1PGAL/smA5E9qU7K//D4vucS01es3M0fDujhDJn3kY8Ip7/vVYcecbe1yY+vBo3qQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
|
|
@ -12824,21 +12866,23 @@
|
|||
"@csstools/postcss-nested-calc": "^4.0.0",
|
||||
"@csstools/postcss-normalize-display-values": "^4.0.0",
|
||||
"@csstools/postcss-oklab-function": "^4.0.12",
|
||||
"@csstools/postcss-position-area-property": "^1.0.0",
|
||||
"@csstools/postcss-progressive-custom-properties": "^4.2.1",
|
||||
"@csstools/postcss-random-function": "^2.0.1",
|
||||
"@csstools/postcss-relative-color-syntax": "^3.0.12",
|
||||
"@csstools/postcss-scope-pseudo-class": "^4.0.1",
|
||||
"@csstools/postcss-sign-functions": "^1.1.4",
|
||||
"@csstools/postcss-stepped-value-functions": "^4.0.9",
|
||||
"@csstools/postcss-system-ui-font-family": "^1.0.0",
|
||||
"@csstools/postcss-text-decoration-shorthand": "^4.0.3",
|
||||
"@csstools/postcss-trigonometric-functions": "^4.0.9",
|
||||
"@csstools/postcss-unset-value": "^4.0.0",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"browserslist": "^4.26.0",
|
||||
"autoprefixer": "^10.4.22",
|
||||
"browserslist": "^4.28.0",
|
||||
"css-blank-pseudo": "^7.0.1",
|
||||
"css-has-pseudo": "^7.0.3",
|
||||
"css-prefers-color-scheme": "^10.0.0",
|
||||
"cssdb": "^8.4.2",
|
||||
"cssdb": "^8.5.2",
|
||||
"postcss-attribute-case-insensitive": "^7.0.1",
|
||||
"postcss-clamp": "^4.1.0",
|
||||
"postcss-color-functional-notation": "^7.0.12",
|
||||
|
|
@ -13099,9 +13143,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.2.tgz",
|
||||
"integrity": "sha512-n3HV2J6QhItCXndGa3oMWvWFAgN1ibnS7R9mt6iokScBOC0Ul9/iZORmU2IWUMcyAQaMPjTlY3uT34TqocUxMA==",
|
||||
"version": "3.7.4",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz",
|
||||
"integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"prettier": "bin/prettier.cjs"
|
||||
|
|
@ -13239,9 +13283,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/react": {
|
||||
"version": "19.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
|
||||
"integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
|
||||
"version": "19.2.3",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
|
||||
"integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
|
|
@ -13249,16 +13293,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/react-dom": {
|
||||
"version": "19.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz",
|
||||
"integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==",
|
||||
"version": "19.2.3",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz",
|
||||
"integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"scheduler": "^0.27.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^19.2.0"
|
||||
"react": "^19.2.3"
|
||||
}
|
||||
},
|
||||
"node_modules/react-is": {
|
||||
|
|
@ -13715,9 +13759,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/sass": {
|
||||
"version": "1.94.2",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.94.2.tgz",
|
||||
"integrity": "sha512-N+7WK20/wOr7CzA2snJcUSSNTCzeCGUTFY3OgeQP3mZ1aj9NMQ0mSTXwlrnd89j33zzQJGqIN52GIOmYrfq46A==",
|
||||
"version": "1.96.0",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.96.0.tgz",
|
||||
"integrity": "sha512-8u4xqqUeugGNCYwr9ARNtQKTOj4KmYiJAVKXf2CTIivTCR51j96htbMKWDru8H5SaQWpyVgTfOF8Ylyf5pun1Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chokidar": "^4.0.0",
|
||||
|
|
@ -14656,9 +14700,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/terser-webpack-plugin": {
|
||||
"version": "5.3.14",
|
||||
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz",
|
||||
"integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==",
|
||||
"version": "5.3.16",
|
||||
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.16.tgz",
|
||||
"integrity": "sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/trace-mapping": "^0.3.25",
|
||||
|
|
@ -15205,9 +15249,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/update-browserslist-db": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz",
|
||||
"integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==",
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.2.tgz",
|
||||
"integrity": "sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
|
|
@ -15308,9 +15352,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "7.2.4",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.2.4.tgz",
|
||||
"integrity": "sha512-NL8jTlbo0Tn4dUEXEsUg8KeyG/Lkmc4Fnzb8JXN/Ykm9G4HNImjtABMJgkQoVjOBN/j2WAwDTRytdqJbZsah7w==",
|
||||
"version": "7.2.7",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.2.7.tgz",
|
||||
"integrity": "sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.0",
|
||||
|
|
@ -15495,9 +15539,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/volar-service-css": {
|
||||
"version": "0.0.67",
|
||||
"resolved": "https://registry.npmjs.org/volar-service-css/-/volar-service-css-0.0.67.tgz",
|
||||
"integrity": "sha512-zV7C6enn9T9tuvQ6iSUyYEs34iPXR69Pf9YYWpbFYPWzVs22w96BtE8p04XYXbmjU6unt5oFt+iLL77bMB5fhA==",
|
||||
"version": "0.0.68",
|
||||
"resolved": "https://registry.npmjs.org/volar-service-css/-/volar-service-css-0.0.68.tgz",
|
||||
"integrity": "sha512-lJSMh6f3QzZ1tdLOZOzovLX0xzAadPhx8EKwraDLPxBndLCYfoTvnNuiFFV8FARrpAlW5C0WkH+TstPaCxr00Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"vscode-css-languageservice": "^6.3.0",
|
||||
|
|
@ -15514,9 +15558,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/volar-service-emmet": {
|
||||
"version": "0.0.67",
|
||||
"resolved": "https://registry.npmjs.org/volar-service-emmet/-/volar-service-emmet-0.0.67.tgz",
|
||||
"integrity": "sha512-UDBL5x7KptmuJZNCCXMlCndMhFult/tj+9jXq3FH1ZGS1E4M/1U5hC06pg1c6e4kn+vnR6bqmvX0vIhL4f98+A==",
|
||||
"version": "0.0.68",
|
||||
"resolved": "https://registry.npmjs.org/volar-service-emmet/-/volar-service-emmet-0.0.68.tgz",
|
||||
"integrity": "sha512-nHvixrRQ83EzkQ4G/jFxu9Y4eSsXS/X2cltEPDM+K9qZmIv+Ey1w0tg1+6caSe8TU5Hgw4oSTwNMf/6cQb3LzQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@emmetio/css-parser": "^0.4.1",
|
||||
|
|
@ -15534,9 +15578,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/volar-service-html": {
|
||||
"version": "0.0.67",
|
||||
"resolved": "https://registry.npmjs.org/volar-service-html/-/volar-service-html-0.0.67.tgz",
|
||||
"integrity": "sha512-ljREMF79JbcjNvObiv69HK2HCl5UT7WTD10zi6CRFUHMbPfiF2UZ42HGLsEGSzaHGZz6H4IFjSS/qfENRLUviQ==",
|
||||
"version": "0.0.68",
|
||||
"resolved": "https://registry.npmjs.org/volar-service-html/-/volar-service-html-0.0.68.tgz",
|
||||
"integrity": "sha512-fru9gsLJxy33xAltXOh4TEdi312HP80hpuKhpYQD4O5hDnkNPEBdcQkpB+gcX0oK0VxRv1UOzcGQEUzWCVHLfA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"vscode-html-languageservice": "^5.3.0",
|
||||
|
|
@ -15553,9 +15597,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/volar-service-json": {
|
||||
"version": "0.0.67",
|
||||
"resolved": "https://registry.npmjs.org/volar-service-json/-/volar-service-json-0.0.67.tgz",
|
||||
"integrity": "sha512-P252euHvOabARHnwH74ssyd9s7LuqtoIBSX18Ybx2Dc1yoTD4+tzCPjC9tKxKrKspbNrTuDaiE9rr9pSRnIlGg==",
|
||||
"version": "0.0.68",
|
||||
"resolved": "https://registry.npmjs.org/volar-service-json/-/volar-service-json-0.0.68.tgz",
|
||||
"integrity": "sha512-2d73Khlffwa0o6B6nA4vCyvqqTBuik1aj1i+EPC8a2UdmjMBAvHhGUcUiF3EscGhenv42UxL7f3tWbsITBNGZg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"vscode-json-languageservice": "^5.4.0",
|
||||
|
|
@ -15571,24 +15615,24 @@
|
|||
}
|
||||
},
|
||||
"node_modules/volar-service-pug": {
|
||||
"version": "0.0.67",
|
||||
"resolved": "https://registry.npmjs.org/volar-service-pug/-/volar-service-pug-0.0.67.tgz",
|
||||
"integrity": "sha512-XDrjmvg+MnVZ6xAx2S0zpJ1WWFWvkUZ2PPyeDo3Q+LTdbHmwnHA0ixLZWmbAhHCoZLQ77465/iLVQcectqikKA==",
|
||||
"version": "0.0.68",
|
||||
"resolved": "https://registry.npmjs.org/volar-service-pug/-/volar-service-pug-0.0.68.tgz",
|
||||
"integrity": "sha512-/nZ8fYKDDrV/fqlBoMACWaghFfaIFZMH+qO9YiRsdGbekAvSZqjQAKl1DSJwWqAkabA68Md51mRIdzYzqleipQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@volar/language-service": "~2.4.0",
|
||||
"muggle-string": "^0.4.1",
|
||||
"pug-lexer": "^5.0.1",
|
||||
"pug-parser": "^6.0.0",
|
||||
"volar-service-html": "0.0.67",
|
||||
"volar-service-html": "0.0.68",
|
||||
"vscode-html-languageservice": "^5.3.0",
|
||||
"vscode-languageserver-textdocument": "^1.0.11"
|
||||
}
|
||||
},
|
||||
"node_modules/volar-service-pug-beautify": {
|
||||
"version": "0.0.67",
|
||||
"resolved": "https://registry.npmjs.org/volar-service-pug-beautify/-/volar-service-pug-beautify-0.0.67.tgz",
|
||||
"integrity": "sha512-eRuW79REwqeSww7HeubSGkQ7xYM/cdCdvYS7iJx7p6sakMa/9rdJkXkB+j4ZMx/7r0+AtDpZfV3TiRBIOs2pLA==",
|
||||
"version": "0.0.68",
|
||||
"resolved": "https://registry.npmjs.org/volar-service-pug-beautify/-/volar-service-pug-beautify-0.0.68.tgz",
|
||||
"integrity": "sha512-CUKfD2l9aPam7jeZT0bQtAOfSWm6XmOmTXOfYDMILuK6+5uYiMKbPW2y+OdK2FnHjcAC53GnHpUrIbfxojRoFQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@johnsoncodehk/pug-beautify": "^0.2.2"
|
||||
|
|
@ -15603,9 +15647,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/volar-service-typescript": {
|
||||
"version": "0.0.67",
|
||||
"resolved": "https://registry.npmjs.org/volar-service-typescript/-/volar-service-typescript-0.0.67.tgz",
|
||||
"integrity": "sha512-rfQBy36Rm1PU9vLWHk8BYJ4r2j/CI024vocJcH4Nb6K2RTc2Irmw6UOVY5DdGiPRV5r+e10wLMK5njj/EcL8sA==",
|
||||
"version": "0.0.68",
|
||||
"resolved": "https://registry.npmjs.org/volar-service-typescript/-/volar-service-typescript-0.0.68.tgz",
|
||||
"integrity": "sha512-z7B/7CnJ0+TWWFp/gh2r5/QwMObHNDiQiv4C9pTBNI2Wxuwymd4bjEORzrJ/hJ5Yd5+OzeYK+nFCKevoGEEeKw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"path-browserify": "^1.0.1",
|
||||
|
|
@ -15637,9 +15681,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/vscode-css-languageservice": {
|
||||
"version": "6.3.8",
|
||||
"resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-6.3.8.tgz",
|
||||
"integrity": "sha512-dBk/9ullEjIMbfSYAohGpDOisOVU1x2MQHOeU12ohGJQI7+r0PCimBwaa/pWpxl/vH4f7ibrBfxIZY3anGmHKQ==",
|
||||
"version": "6.3.9",
|
||||
"resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-6.3.9.tgz",
|
||||
"integrity": "sha512-1tLWfp+TDM5ZuVWht3jmaY5y7O6aZmpeXLoHl5bv1QtRsRKt4xYGRMmdJa5Pqx/FTkgRbsna9R+Gn2xE+evVuA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vscode/l10n": "^0.0.18",
|
||||
|
|
@ -15649,9 +15693,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/vscode-html-languageservice": {
|
||||
"version": "5.6.0",
|
||||
"resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-5.6.0.tgz",
|
||||
"integrity": "sha512-FIVz83oGw2tBkOr8gQPeiREInnineCKGCz3ZD1Pi6opOuX3nSRkc4y4zLLWsuop+6ttYX//XZCI6SLzGhRzLmA==",
|
||||
"version": "5.6.1",
|
||||
"resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-5.6.1.tgz",
|
||||
"integrity": "sha512-5Mrqy5CLfFZUgkyhNZLA1Ye5g12Cb/v6VM7SxUzZUaRKWMDz4md+y26PrfRTSU0/eQAl3XpO9m2og+GGtDMuaA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vscode/l10n": "^0.0.18",
|
||||
|
|
@ -15661,9 +15705,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/vscode-json-languageservice": {
|
||||
"version": "5.6.3",
|
||||
"resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-5.6.3.tgz",
|
||||
"integrity": "sha512-UDF7sJF5t7mzUzXL6dsClkvnHS4xnDL/gOMKGQiizRHmswlk/xSPGZxEvAtszWQF0ImNcJ0j9l+rHuefGzit1w==",
|
||||
"version": "5.6.4",
|
||||
"resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-5.6.4.tgz",
|
||||
"integrity": "sha512-i0MhkFmnQAbYr+PiE6Th067qa3rwvvAErCEUo0ql+ghFXHvxbwG3kLbwMaIUrrbCLUDEeULiLgROJjtuyYoIsA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vscode/l10n": "^0.0.18",
|
||||
|
|
@ -15863,9 +15907,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/vue-router": {
|
||||
"version": "4.6.3",
|
||||
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.3.tgz",
|
||||
"integrity": "sha512-ARBedLm9YlbvQomnmq91Os7ck6efydTSpRP3nuOKCvgJOHNrhRoJDSKtee8kcL1Vf7nz6U+PMBL+hTvR3bTVQg==",
|
||||
"version": "4.6.4",
|
||||
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.4.tgz",
|
||||
"integrity": "sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/devtools-api": "^6.6.4"
|
||||
|
|
@ -15997,9 +16041,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/vuetify": {
|
||||
"version": "3.11.0",
|
||||
"resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.11.0.tgz",
|
||||
"integrity": "sha512-ITGeT3uaTIwI2SdyTvtE45tY6FlS2oWklfLU47s2K0ZHnu1it35p9lz8oE15Id8ThtKyQojQGobMkN+korheEw==",
|
||||
"version": "3.11.3",
|
||||
"resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.11.3.tgz",
|
||||
"integrity": "sha512-sZe/2f143cbqtJupkynOGOHxgn+YjRrXIj0atZlBECbOr4nZPCmSdukPSbudb0wU3fQpUjlaTGx0m4vIBPQqGQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
|
|
@ -16863,10 +16907,10 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/workbox-build/node_modules/lru-cache": {
|
||||
"version": "11.2.2",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz",
|
||||
"integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==",
|
||||
"license": "ISC",
|
||||
"version": "11.2.4",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz",
|
||||
"integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,21 +44,21 @@
|
|||
"@mdi/font": "^7.4.47",
|
||||
"@testing-library/jest-dom": "^6.9.1",
|
||||
"@testing-library/react": "^16.3.0",
|
||||
"@vitejs/plugin-react": "^5.1.1",
|
||||
"@vitejs/plugin-vue": "^6.0.2",
|
||||
"@vitejs/plugin-react": "^5.1.2",
|
||||
"@vitejs/plugin-vue": "^6.0.3",
|
||||
"@vitest/browser": "^3.2.4",
|
||||
"@vitest/coverage-v8": "^3.2.4",
|
||||
"@vitest/ui": "^3.2.4",
|
||||
"@vue/compiler-sfc": "^3.5.18",
|
||||
"@vue/language-server": "^3.1.5",
|
||||
"@vue/language-server": "^3.1.8",
|
||||
"@vue/test-utils": "^2.4.6",
|
||||
"@vvo/tzdb": "^6.197.0",
|
||||
"@vvo/tzdb": "^6.198.0",
|
||||
"axios": "^1.13.2",
|
||||
"axios-mock-adapter": "^2.1.0",
|
||||
"babel-loader": "^10.0.0",
|
||||
"babel-plugin-istanbul": "^7.0.1",
|
||||
"babel-plugin-polyfill-corejs3": "^0.13.0",
|
||||
"browserslist": "^4.28.0",
|
||||
"browserslist": "^4.28.1",
|
||||
"cheerio": "1.0.0-rc.12",
|
||||
"core-js": "^3.47.0",
|
||||
"cross-env": "^7.0.3",
|
||||
|
|
@ -84,7 +84,7 @@
|
|||
"i": "^0.3.7",
|
||||
"jsdom": "^26.1.0",
|
||||
"luxon": "^3.7.2",
|
||||
"maplibre-gl": "^5.13.0",
|
||||
"maplibre-gl": "^5.14.0",
|
||||
"memoize-one": "^6.0.0",
|
||||
"mini-css-extract-plugin": "^2.9.4",
|
||||
"minimist": "^1.2.8",
|
||||
|
|
@ -95,15 +95,15 @@
|
|||
"postcss": "^8.5.6",
|
||||
"postcss-import": "^16.1.1",
|
||||
"postcss-loader": "^8.2.0",
|
||||
"postcss-preset-env": "^10.4.0",
|
||||
"postcss-preset-env": "^10.5.0",
|
||||
"postcss-reporter": "^7.1.0",
|
||||
"postcss-url": "^10.1.3",
|
||||
"prettier": "^3.7.2",
|
||||
"prettier": "^3.7.4",
|
||||
"pubsub-js": "^1.9.5",
|
||||
"regenerator-runtime": "^0.14.1",
|
||||
"resolve-url-loader": "^5.0.0",
|
||||
"sanitize-html": "^2.17.0",
|
||||
"sass": "^1.94.2",
|
||||
"sass": "^1.96.0",
|
||||
"sass-loader": "^16.0.6",
|
||||
"sockette": "^2.0.6",
|
||||
"style-loader": "^4.0.0",
|
||||
|
|
@ -118,11 +118,11 @@
|
|||
"vue-loader": "^17.4.2",
|
||||
"vue-loader-plugin": "^1.3.0",
|
||||
"vue-luxon": "^0.10.0",
|
||||
"vue-router": "^4.6.3",
|
||||
"vue-router": "^4.6.4",
|
||||
"vue-sanitize-directive": "^0.2.1",
|
||||
"vue-style-loader": "^4.1.3",
|
||||
"vue3-gettext": "^2.4.0",
|
||||
"vuetify": "^3.11.0",
|
||||
"vuetify": "^3.11.3",
|
||||
"webpack": "^5.103.0",
|
||||
"webpack-bundle-analyzer": "^4.10.2",
|
||||
"webpack-cli": "^6.0.1",
|
||||
|
|
|
|||
35
go.mod
35
go.mod
|
|
@ -12,9 +12,9 @@ require (
|
|||
github.com/dsoprea/go-tiff-image-structure/v2 v2.0.0-20221003165014-8ecc4f52edca
|
||||
github.com/dustin/go-humanize v1.0.1
|
||||
github.com/esimov/pigo v1.4.6
|
||||
github.com/gin-contrib/gzip v1.2.3
|
||||
github.com/gin-contrib/gzip v1.2.5
|
||||
github.com/gin-gonic/gin v1.11.0
|
||||
github.com/golang/geo v0.0.0-20251125140653-09e2dd3603dd
|
||||
github.com/golang/geo v0.0.0-20251209161508-25c597310d4b
|
||||
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
|
||||
|
|
@ -38,18 +38,18 @@ require (
|
|||
github.com/tidwall/gjson v1.18.0
|
||||
github.com/ulule/deepcopier v0.0.0-20200430083143-45decc6639b6
|
||||
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
|
||||
golang.org/x/crypto v0.45.0
|
||||
golang.org/x/net v0.47.0
|
||||
golang.org/x/crypto v0.46.0
|
||||
golang.org/x/net v0.48.0
|
||||
gonum.org/v1/gonum v0.16.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/go-xmlfmt/xmlfmt v1.1.3 // indirect
|
||||
golang.org/x/image v0.33.0
|
||||
golang.org/x/image v0.34.0
|
||||
)
|
||||
|
||||
require github.com/olekukonko/tablewriter v1.1.0
|
||||
require github.com/olekukonko/tablewriter v1.1.2
|
||||
|
||||
require github.com/google/uuid v1.6.0
|
||||
|
||||
|
|
@ -58,7 +58,7 @@ require github.com/chzyer/readline v1.5.1 // indirect
|
|||
require github.com/gabriel-vasile/mimetype v1.4.11
|
||||
|
||||
require (
|
||||
golang.org/x/sync v0.18.0
|
||||
golang.org/x/sync v0.19.0
|
||||
golang.org/x/time v0.14.0
|
||||
)
|
||||
|
||||
|
|
@ -71,7 +71,7 @@ require (
|
|||
|
||||
require github.com/dustinkirkland/golang-petname v0.0.0-20240428194347-eebcea082ee0
|
||||
|
||||
require golang.org/x/text v0.31.0
|
||||
require golang.org/x/text v0.32.0
|
||||
|
||||
require (
|
||||
github.com/IGLOU-EU/go-wildcard v1.0.3
|
||||
|
|
@ -87,11 +87,11 @@ require (
|
|||
github.com/ugjka/go-tz/v2 v2.2.6
|
||||
github.com/urfave/cli/v2 v2.27.7
|
||||
github.com/wamuir/graft v0.10.0
|
||||
github.com/yalue/onnxruntime_go v1.22.0
|
||||
github.com/zitadel/oidc/v3 v3.45.0
|
||||
golang.org/x/mod v0.30.0
|
||||
golang.org/x/sys v0.38.0
|
||||
google.golang.org/protobuf v1.36.10
|
||||
github.com/yalue/onnxruntime_go v1.24.0
|
||||
github.com/zitadel/oidc/v3 v3.45.1
|
||||
golang.org/x/mod v0.31.0
|
||||
golang.org/x/sys v0.39.0
|
||||
google.golang.org/protobuf v1.36.11
|
||||
gorm.io/driver/mysql v1.5.7
|
||||
gorm.io/driver/postgres v1.5.9
|
||||
gorm.io/driver/sqlite v1.5.6
|
||||
|
|
@ -106,6 +106,7 @@ require (
|
|||
github.com/bytedance/gopkg v0.1.3 // indirect
|
||||
github.com/bytedance/sonic/loader v0.3.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/clipperhouse/displaywidth v0.6.0 // indirect
|
||||
github.com/clipperhouse/stringish v0.1.1 // indirect
|
||||
github.com/clipperhouse/uax29/v2 v2.3.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.6 // indirect
|
||||
|
|
@ -160,12 +161,11 @@ require (
|
|||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect
|
||||
github.com/olekukonko/errors v1.1.0 // indirect
|
||||
github.com/olekukonko/ll v0.1.2 // indirect
|
||||
github.com/olekukonko/ll v0.1.3 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/procfs v0.17.0 // indirect
|
||||
github.com/quic-go/qpack v0.5.1 // indirect
|
||||
github.com/quic-go/quic-go v0.55.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/swaggo/swag v1.16.6 // indirect
|
||||
github.com/tidwall/match v1.2.0 // indirect
|
||||
|
|
@ -183,10 +183,9 @@ require (
|
|||
go.opentelemetry.io/otel v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.38.0 // indirect
|
||||
go.uber.org/mock v0.6.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||
golang.org/x/oauth2 v0.32.0 // indirect
|
||||
golang.org/x/tools v0.38.0 // indirect
|
||||
golang.org/x/oauth2 v0.33.0 // indirect
|
||||
golang.org/x/tools v0.39.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
|
|
|
|||
68
go.sum
68
go.sum
|
|
@ -51,6 +51,8 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn
|
|||
github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
|
||||
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/clipperhouse/displaywidth v0.6.0 h1:k32vueaksef9WIKCNcoqRNyKbyvkvkysNYnAWz2fN4s=
|
||||
github.com/clipperhouse/displaywidth v0.6.0/go.mod h1:R+kHuzaYWFkTm7xoMmK1lFydbci4X2CicfbGstSGg0o=
|
||||
github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
|
||||
github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
|
||||
github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4=
|
||||
|
|
@ -119,8 +121,8 @@ github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/
|
|||
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||
github.com/gabriel-vasile/mimetype v1.4.11 h1:AQvxbp830wPhHTqc1u7nzoLT+ZFxGY7emj5DR5DYFik=
|
||||
github.com/gabriel-vasile/mimetype v1.4.11/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/gzip v1.2.5 h1:fIZs0S+l17pIu1P5XRJOo/YNqfIuPCrZZ3TWB7pjckI=
|
||||
github.com/gin-contrib/gzip v1.2.5/go.mod h1:aomRgR7ftdZV3uWY0gW/m8rChfxau0n8YVvwlOHONzw=
|
||||
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
|
||||
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
||||
github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
|
||||
|
|
@ -198,8 +200,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-20251125140653-09e2dd3603dd h1:aL71U44NmpsvVa8k5Zgtc57+t9VRIyahGt8l/OsSTIM=
|
||||
github.com/golang/geo v0.0.0-20251125140653-09e2dd3603dd/go.mod h1:Mymr9kRGDc64JPr03TSZmuIBODZ3KyswLzm1xL0HFA8=
|
||||
github.com/golang/geo v0.0.0-20251209161508-25c597310d4b h1:6y9D6yfaR5FyqoNoV2S+XJyhzeMUlkdIeUX1Ssj0FJQ=
|
||||
github.com/golang/geo v0.0.0-20251209161508-25c597310d4b/go.mod h1:Mymr9kRGDc64JPr03TSZmuIBODZ3KyswLzm1xL0HFA8=
|
||||
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=
|
||||
|
|
@ -329,10 +331,10 @@ github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj
|
|||
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0=
|
||||
github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM=
|
||||
github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
|
||||
github.com/olekukonko/ll v0.1.2 h1:lkg/k/9mlsy0SxO5aC+WEpbdT5K83ddnNhAepz7TQc0=
|
||||
github.com/olekukonko/ll v0.1.2/go.mod h1:b52bVQRRPObe+yyBl0TxNfhesL0nedD4Cht0/zx55Ew=
|
||||
github.com/olekukonko/tablewriter v1.1.0 h1:N0LHrshF4T39KvI96fn6GT8HEjXRXYNDrDjKFDB7RIY=
|
||||
github.com/olekukonko/tablewriter v1.1.0/go.mod h1:5c+EBPeSqvXnLLgkm9isDdzR3wjfBkHR9Nhfp3NWrzo=
|
||||
github.com/olekukonko/ll v0.1.3 h1:sV2jrhQGq5B3W0nENUISCR6azIPf7UBUpVq0x/y70Fg=
|
||||
github.com/olekukonko/ll v0.1.3/go.mod h1:b52bVQRRPObe+yyBl0TxNfhesL0nedD4Cht0/zx55Ew=
|
||||
github.com/olekukonko/tablewriter v1.1.2 h1:L2kI1Y5tZBct/O/TyZK1zIE9GlBj/TVs+AY5tZDCDSc=
|
||||
github.com/olekukonko/tablewriter v1.1.2/go.mod h1:z7SYPugVqGVavWoA2sGsFIoOVNmEHxUAAMrhXONtfkg=
|
||||
github.com/orcaman/writerseeker v0.0.0-20200621085525-1d3f536ff85e h1:s2RNOM/IGdY0Y6qfTeUKhDawdHDpK9RGBdx80qN4Ttw=
|
||||
github.com/orcaman/writerseeker v0.0.0-20200621085525-1d3f536ff85e/go.mod h1:nBdnFKj15wFbf94Rwfq4m30eAcyY9V/IyKAGQFtqkW0=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||
|
|
@ -360,8 +362,6 @@ github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
|||
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||
github.com/quic-go/quic-go v0.55.0 h1:zccPQIqYCXDt5NmcEabyYvOnomjs8Tlwl7tISjJh9Mk=
|
||||
github.com/quic-go/quic-go v0.55.0/go.mod h1:DR51ilwU1uE164KuWXhinFcKWGlEjzys2l8zUl5Ss1U=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
|
|
@ -421,13 +421,13 @@ github.com/wamuir/graft v0.10.0 h1:HSpBUvm7O+jwsRIuDQlw80xW4xMXRFkOiVLtWaZCU2s=
|
|||
github.com/wamuir/graft v0.10.0/go.mod h1:k6NJX3fCM/xzh5NtHky9USdgHTcz2vAvHp4c23I6UK4=
|
||||
github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 h1:FnBeRrxr7OU4VvAzt5X7s6266i6cSVkkFPS0TuXWbIg=
|
||||
github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
|
||||
github.com/yalue/onnxruntime_go v1.22.0 h1:SzqOfFRRrLRRAFR5VoSxABjTiQSAi8Y4ETYKrMFK1jk=
|
||||
github.com/yalue/onnxruntime_go v1.22.0/go.mod h1:b4X26A8pekNb1ACJ58wAXgNKeUCGEAQ9dmACut9Sm/4=
|
||||
github.com/yalue/onnxruntime_go v1.24.0 h1:IdgJLxxyotlsUTmL1UnHZgBzXJGgY51LZ4vQ5rZeOXU=
|
||||
github.com/yalue/onnxruntime_go v1.24.0/go.mod h1:b4X26A8pekNb1ACJ58wAXgNKeUCGEAQ9dmACut9Sm/4=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/zitadel/logging v0.6.2 h1:MW2kDDR0ieQynPZ0KIZPrh9ote2WkxfBif5QoARDQcU=
|
||||
github.com/zitadel/logging v0.6.2/go.mod h1:z6VWLWUkJpnNVDSLzrPSQSQyttysKZ6bCRongw0ROK4=
|
||||
github.com/zitadel/oidc/v3 v3.45.0 h1:SaVJ2kdcJi/zdEWWlAns+81VxmfdYX4E+2mWFVIH7Ec=
|
||||
github.com/zitadel/oidc/v3 v3.45.0/go.mod h1:UeK0iVOoqfMuDVgSfv56BqTz8YQC2M+tGRIXZ7Ii3VY=
|
||||
github.com/zitadel/oidc/v3 v3.45.1 h1:x7J8NywTUtLR9T5uu2dufae3gJrl6VVpIfvGZy+kzJg=
|
||||
github.com/zitadel/oidc/v3 v3.45.1/go.mod h1:oFArtAPTXEA4ajkIe/JfBjv7hhlD0kr///UqaO3Uzd0=
|
||||
github.com/zitadel/schema v1.3.1 h1:QT3kwiRIRXXLVAs6gCK/u044WmUVh6IlbLXUsn6yRQU=
|
||||
github.com/zitadel/schema v1.3.1/go.mod h1:071u7D2LQacy1HAN+YnMd/mx1qVE2isb0Mjeqg46xnU=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
|
|
@ -462,8 +462,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
|
|||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
||||
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
|
|
@ -478,8 +478,8 @@ golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+o
|
|||
golang.org/x/image v0.0.0-20200927104501-e162460cd6b5/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY=
|
||||
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
|
||||
golang.org/x/image v0.33.0 h1:LXRZRnv1+zGd5XBUVRFmYEphyyKJjQjCRiOuAP3sZfQ=
|
||||
golang.org/x/image v0.33.0/go.mod h1:DD3OsTYT9chzuzTQt+zMcOlBHgfoKQb1gry8p76Y1sc=
|
||||
golang.org/x/image v0.34.0 h1:33gCkyw9hmwbZJeZkct8XyR11yH889EQt/QH4VmXMn8=
|
||||
golang.org/x/image v0.34.0/go.mod h1:2RNFBZRB+vnwwFil8GkMdRvrJOFd1AzdZI6vOY+eJVU=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
|
|
@ -500,8 +500,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
|||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
|
||||
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
|
||||
golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
|
||||
golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
|
@ -530,15 +530,15 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
|||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
||||
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY=
|
||||
golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo=
|
||||
golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
|
@ -550,8 +550,8 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
|
@ -585,8 +585,8 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
golang.org/x/term v0.0.0-20191110171634-ad39bd3f0407/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
|
|
@ -608,8 +608,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
|||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
||||
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
||||
|
|
@ -642,8 +642,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
|
|||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
|
||||
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
|
||||
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
|
||||
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
|
@ -682,8 +682,8 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac
|
|||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
## PhotoPrism — Vision Package
|
||||
|
||||
**Last Updated:** November 25, 2025
|
||||
**Last Updated:** December 10, 2025
|
||||
|
||||
### Overview
|
||||
|
||||
`internal/ai/vision` provides the shared model registry, request builders, and parsers that power PhotoPrism’s caption, label, face, NSFW, and future generate workflows. It reads `vision.yml`, normalizes models, and dispatches calls to one of three engines:
|
||||
|
||||
- **TensorFlow (built‑in)** — default Nasnet / NSFW / Facenet models, no remote service required.
|
||||
- **Ollama** — local or proxied multimodal LLMs. See [`ollama/README.md`](ollama/README.md) for tuning and schema details.
|
||||
- **Ollama** — local or proxied multimodal LLMs. See [`ollama/README.md`](ollama/README.md) for tuning and schema details. The engine defaults to `${OLLAMA_BASE_URL:-http://ollama:11434}/api/generate`, trimming any trailing slash on the base URL; set `OLLAMA_BASE_URL=https://ollama.com` to opt into cloud defaults.
|
||||
- **OpenAI** — cloud Responses API. See [`openai/README.md`](openai/README.md) for prompts, schema variants, and header requirements.
|
||||
|
||||
### Configuration
|
||||
|
|
@ -51,33 +51,62 @@ The `vision.yml` file is usually kept in the `storage/config` directory (overrid
|
|||
|
||||
#### Model Options
|
||||
|
||||
| Option | Default | Description |
|
||||
|-------------------|-----------------------------------------------------------------------------------------|------------------------------------------------------------------------------------|
|
||||
| `Temperature` | engine default (`0.1` for Ollama; unset for OpenAI) | Controls randomness; clamped to `[0,2]`. `gpt-5*` OpenAI models are forced to `0`. |
|
||||
| `TopP` | engine default (`0.9` for some Ollama label defaults; unset for OpenAI) | Nucleus sampling parameter. |
|
||||
| `MaxOutputTokens` | engine default (OpenAI caption 512, labels 1024; Ollama label default 256) | Upper bound on generated tokens; adapters raise low values to defaults. |
|
||||
| `ForceJson` | engine-specific (`true` for OpenAI labels; `false` for Ollama labels; captions `false`) | Forces structured output when enabled. |
|
||||
| `SchemaVersion` | derived from schema name | Override when coordinating schema migrations. |
|
||||
| `Stop` | engine default | Array of stop sequences (e.g., `["\\n\\n"]`). |
|
||||
| `NumThread` | runtime auto | Caps CPU threads for local engines. |
|
||||
| `NumCtx` | engine default | Context window length (tokens). |
|
||||
The model `Options` adjust model parameters such as temperature, top-p, and schema constraints when using [Ollama](ollama/README.md) or [OpenAI](openai/README.md). Rows are ordered exactly as defined in `vision/model_options.go`.
|
||||
|
||||
| Option | Engines | Default | Description |
|
||||
|--------------------|------------------|----------------------|-----------------------------------------------------------------------------------------|
|
||||
| `Temperature` | Ollama, OpenAI | engine default | Controls randomness with a value between `0.01` and `2.0`; not used for OpenAI's GPT-5. |
|
||||
| `TopK` | Ollama | engine default | Limits sampling to the top K tokens to reduce rare or noisy outputs. |
|
||||
| `TopP` | Ollama, OpenAI | engine default | Nucleus sampling; keeps the smallest token set whose cumulative probability ≥ `p`. |
|
||||
| `MinP` | Ollama | engine default | Drops tokens whose probability mass is below `p`, trimming the long tail. |
|
||||
| `TypicalP` | Ollama | engine default | Keeps tokens with typicality under the threshold; combine with TopP/MinP for flow. |
|
||||
| `TfsZ` | Ollama | engine default | Tail free sampling parameter; lower values reduce repetition. |
|
||||
| `Seed` | Ollama | random per run | Fix for reproducible outputs; unset for more variety between runs. |
|
||||
| `NumKeep` | Ollama | engine default | How many tokens to keep from the prompt before sampling starts. |
|
||||
| `RepeatLastN` | Ollama | engine default | Number of recent tokens considered for repetition penalties. |
|
||||
| `RepeatPenalty` | Ollama | engine default | Multiplier >1 discourages repeating the same tokens or phrases. |
|
||||
| `PresencePenalty` | OpenAI | engine default | Increases the likelihood of introducing new tokens by penalizing existing ones. |
|
||||
| `FrequencyPenalty` | OpenAI | engine default | Penalizes tokens in proportion to their frequency so far. |
|
||||
| `PenalizeNewline` | Ollama | engine default | Whether to apply repetition penalties to newline tokens. |
|
||||
| `Stop` | Ollama, OpenAI | engine default | Array of stop sequences (e.g., `["\\n\\n"]`). |
|
||||
| `Mirostat` | Ollama | engine default | Enables Mirostat sampling (`0` off, `1/2` modes). |
|
||||
| `MirostatTau` | Ollama | engine default | Controls surprise target for Mirostat sampling. |
|
||||
| `MirostatEta` | Ollama | engine default | Learning rate for Mirostat adaptation. |
|
||||
| `NumPredict` | Ollama | engine default | Ollama-specific max output tokens; synonymous intent with `MaxOutputTokens`. |
|
||||
| `MaxOutputTokens` | Ollama, OpenAI | engine default | Upper bound on generated tokens; adapters raise low values to defaults. |
|
||||
| `ForceJson` | Ollama, OpenAI | engine default | Forces structured output when enabled. |
|
||||
| `SchemaVersion` | Ollama, OpenAI | derived from schema | Override when coordinating schema migrations. |
|
||||
| `CombineOutputs` | OpenAI | engine default | Controls whether multi-output models combine results automatically. |
|
||||
| `Detail` | OpenAI | engine default | Controls OpenAI vision detail level (`low`, `high`, `auto`). |
|
||||
| `NumCtx` | Ollama, OpenAI | engine default | Context window length (tokens). |
|
||||
| `NumThread` | Ollama | runtime auto | Caps CPU threads for local engines. |
|
||||
| `NumBatch` | Ollama | engine default | Batch size for prompt processing. |
|
||||
| `NumGpu` | Ollama | engine default | Number of GPUs to distribute work across. |
|
||||
| `MainGpu` | Ollama | engine default | Primary GPU index when multiple GPUs are present. |
|
||||
| `LowVram` | Ollama | engine default | Enable VRAM-saving mode; may reduce performance. |
|
||||
| `VocabOnly` | Ollama | engine default | Load vocabulary only for quick metadata inspection. |
|
||||
| `UseMmap` | Ollama | engine default | Memory map model weights instead of fully loading them. |
|
||||
| `UseMlock` | Ollama | engine default | Lock model weights in RAM to reduce paging. |
|
||||
| `Numa` | Ollama | engine default | Enable NUMA-aware allocations when available. |
|
||||
|
||||
#### Model Service
|
||||
|
||||
Used for Ollama/OpenAI (and any future HTTP engines). All credentials and identifiers support `${ENV_VAR}` expansion.
|
||||
Configures the endpoint URL, method, format, and authentication for [Ollama](ollama/README.md), [OpenAI](openai/README.md), and other engines that perform remote HTTP requests:
|
||||
|
||||
| Field | Default | Notes |
|
||||
|------------------------------------|------------------------------------------|------------------------------------------------------|
|
||||
| `Uri` | required for remote | Endpoint base. Empty keeps model local (TensorFlow). |
|
||||
| `Method` | `POST` | Override verb if provider needs it. |
|
||||
| `Key` | `""` | Bearer token; prefer env expansion. |
|
||||
| `Username` / `Password` | `""` | Injected as basic auth when URI lacks userinfo. |
|
||||
| `Model` | `""` | Endpoint-specific override; wins over model/name. |
|
||||
| `Org` / `Project` | `""` | OpenAI headers (org/proj IDs) |
|
||||
| `RequestFormat` / `ResponseFormat` | set by engine alias | Explicit values win over alias defaults. |
|
||||
| `FileScheme` | set by engine alias (`data` or `base64`) | Controls image transport. |
|
||||
| `Disabled` | `false` | Disable the endpoint without removing the model. |
|
||||
| Field | Default | Notes |
|
||||
|------------------------------------|------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `Uri` | required for remote | Endpoint base. Empty keeps model local (TensorFlow). Ollama alias fills `${OLLAMA_BASE_URL}/api/generate`, defaulting to `http://ollama:11434`. |
|
||||
| `Method` | `POST` | Override verb if provider needs it. |
|
||||
| `Key` | `""` | Bearer token; prefer env expansion (OpenAI: `OPENAI_API_KEY`, Ollama: `OLLAMA_API_KEY`). |
|
||||
| `Username` / `Password` | `""` | Injected as basic auth when URI lacks userinfo. |
|
||||
| `Model` | `""` | Endpoint-specific override; wins over model/name. |
|
||||
| `Org` / `Project` | `""` | OpenAI headers (org/proj IDs) |
|
||||
| `RequestFormat` / `ResponseFormat` | set by engine alias | Explicit values win over alias defaults. |
|
||||
| `FileScheme` | set by engine alias (`data` or `base64`) | Controls image transport. |
|
||||
| `Disabled` | `false` | Disable the endpoint without removing the model. |
|
||||
|
||||
> **Authentication:** All credentials and identifiers support `${ENV_VAR}` expansion. `Service.Key` sets `Authorization: Bearer <token>`; `Username`/`Password` injects HTTP basic authentication into the service URI when it is not already present. When `Service.Key` is empty, PhotoPrism defaults to `OPENAI_API_KEY` (OpenAI engine) or `OLLAMA_API_KEY` (Ollama engine), also honoring their `_FILE` counterparts.
|
||||
|
||||
### Field Behavior & Precedence
|
||||
|
||||
- Model identifier resolution order: `Service.Model` → `Model` → `Name`. `Model.GetModel()` returns `(id, name, version)` where Ollama receives `name:version` and other engines receive `name` plus a separate `Version`.
|
||||
|
|
@ -113,7 +142,7 @@ Models:
|
|||
Engine: ollama
|
||||
Run: newly-indexed
|
||||
Service:
|
||||
Uri: http://ollama:11434/api/generate
|
||||
Uri: ${OLLAMA_BASE_URL}/api/generate
|
||||
```
|
||||
|
||||
More Ollama guidance: [`internal/ai/vision/ollama/README.md`](ollama/README.md).
|
||||
|
|
|
|||
|
|
@ -32,43 +32,6 @@ const (
|
|||
logDataTruncatedSuffix = "... (truncated)"
|
||||
)
|
||||
|
||||
// ApiRequestOptions represents additional model parameters listed in the documentation.
|
||||
type ApiRequestOptions struct {
|
||||
NumKeep int `yaml:"NumKeep,omitempty" json:"num_keep,omitempty"`
|
||||
Seed int `yaml:"Seed,omitempty" json:"seed,omitempty"`
|
||||
NumPredict int `yaml:"NumPredict,omitempty" json:"num_predict,omitempty"`
|
||||
TopK int `yaml:"TopK,omitempty" json:"top_k,omitempty"`
|
||||
TopP float64 `yaml:"TopP,omitempty" json:"top_p,omitempty"`
|
||||
MinP float64 `yaml:"MinP,omitempty" json:"min_p,omitempty"`
|
||||
TfsZ float64 `yaml:"TfsZ,omitempty" json:"tfs_z,omitempty"`
|
||||
TypicalP float64 `yaml:"TypicalP,omitempty" json:"typical_p,omitempty"`
|
||||
RepeatLastN int `yaml:"RepeatLastN,omitempty" json:"repeat_last_n,omitempty"`
|
||||
Temperature float64 `yaml:"Temperature,omitempty" json:"temperature,omitempty"`
|
||||
RepeatPenalty float64 `yaml:"RepeatPenalty,omitempty" json:"repeat_penalty,omitempty"`
|
||||
PresencePenalty float64 `yaml:"PresencePenalty,omitempty" json:"presence_penalty,omitempty"`
|
||||
FrequencyPenalty float64 `yaml:"FrequencyPenalty,omitempty" json:"frequency_penalty,omitempty"`
|
||||
Mirostat int `yaml:"Mirostat,omitempty" json:"mirostat,omitempty"`
|
||||
MirostatTau float64 `yaml:"MirostatTau,omitempty" json:"mirostat_tau,omitempty"`
|
||||
MirostatEta float64 `yaml:"MirostatEta,omitempty" json:"mirostat_eta,omitempty"`
|
||||
PenalizeNewline bool `yaml:"PenalizeNewline,omitempty" json:"penalize_newline,omitempty"`
|
||||
Stop []string `yaml:"Stop,omitempty" json:"stop,omitempty"`
|
||||
Numa bool `yaml:"Numa,omitempty" json:"numa,omitempty"`
|
||||
NumCtx int `yaml:"NumCtx,omitempty" json:"num_ctx,omitempty"`
|
||||
NumBatch int `yaml:"NumBatch,omitempty" json:"num_batch,omitempty"`
|
||||
NumGpu int `yaml:"NumGpu,omitempty" json:"num_gpu,omitempty"`
|
||||
MainGpu int `yaml:"MainGpu,omitempty" json:"main_gpu,omitempty"`
|
||||
LowVram bool `yaml:"LowVram,omitempty" json:"low_vram,omitempty"`
|
||||
VocabOnly bool `yaml:"VocabOnly,omitempty" json:"vocab_only,omitempty"`
|
||||
UseMmap bool `yaml:"UseMmap,omitempty" json:"use_mmap,omitempty"`
|
||||
UseMlock bool `yaml:"UseMlock,omitempty" json:"use_mlock,omitempty"`
|
||||
NumThread int `yaml:"NumThread,omitempty" json:"num_thread,omitempty"`
|
||||
MaxOutputTokens int `yaml:"MaxOutputTokens,omitempty" json:"max_output_tokens,omitempty"`
|
||||
Detail string `yaml:"Detail,omitempty" json:"detail,omitempty"`
|
||||
ForceJson bool `yaml:"ForceJson,omitempty" json:"force_json,omitempty"`
|
||||
SchemaVersion string `yaml:"SchemaVersion,omitempty" json:"schema_version,omitempty"`
|
||||
CombineOutputs string `yaml:"CombineOutputs,omitempty" json:"combine_outputs,omitempty"`
|
||||
}
|
||||
|
||||
// ApiRequestContext represents a context parameter returned from a previous request.
|
||||
type ApiRequestContext = []int
|
||||
|
||||
|
|
@ -84,7 +47,7 @@ type ApiRequest struct {
|
|||
Url string `form:"url" yaml:"Url,omitempty" json:"url,omitempty"`
|
||||
Org string `form:"org" yaml:"Org,omitempty" json:"org,omitempty"`
|
||||
Project string `form:"project" yaml:"Project,omitempty" json:"project,omitempty"`
|
||||
Options *ApiRequestOptions `form:"options" yaml:"Options,omitempty" json:"options,omitempty"`
|
||||
Options *ModelOptions `form:"options" yaml:"Options,omitempty" json:"options,omitempty"`
|
||||
Context *ApiRequestContext `form:"context" yaml:"Context,omitempty" json:"context,omitempty"`
|
||||
Stream bool `form:"stream" yaml:"Stream,omitempty" json:"stream"`
|
||||
Images Files `form:"images" yaml:"Images,omitempty" json:"images,omitempty"`
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import (
|
|||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/ai/vision/openai"
|
||||
"github.com/photoprism/photoprism/pkg/http/scheme"
|
||||
)
|
||||
|
||||
|
|
@ -36,7 +35,7 @@ type EngineDefaults interface {
|
|||
SystemPrompt(model *Model) string
|
||||
UserPrompt(model *Model) string
|
||||
SchemaTemplate(model *Model) string
|
||||
Options(model *Model) *ApiRequestOptions
|
||||
Options(model *Model) *ModelOptions
|
||||
}
|
||||
|
||||
// Engine groups the callbacks required to integrate a third-party vision service.
|
||||
|
|
@ -61,14 +60,6 @@ func init() {
|
|||
FileScheme: scheme.Data,
|
||||
DefaultResolution: DefaultResolution,
|
||||
})
|
||||
|
||||
RegisterEngineAlias(openai.EngineName, EngineInfo{
|
||||
Uri: "https://api.openai.com/v1/responses",
|
||||
RequestFormat: ApiFormatOpenAI,
|
||||
ResponseFormat: ApiFormatOpenAI,
|
||||
FileScheme: scheme.Data,
|
||||
DefaultResolution: openai.DefaultResolution,
|
||||
})
|
||||
}
|
||||
|
||||
// RegisterEngine adds/overrides an engine implementation for a specific API format.
|
||||
|
|
@ -84,7 +75,9 @@ type EngineInfo struct {
|
|||
RequestFormat ApiFormat
|
||||
ResponseFormat ApiFormat
|
||||
FileScheme string
|
||||
DefaultModel string
|
||||
DefaultResolution int
|
||||
DefaultKey string // Optional placeholder key (e.g., ${OPENAI_API_KEY}); applied only when Service.Key is empty.
|
||||
}
|
||||
|
||||
// RegisterEngineAlias maps a logical engine name (e.g., "ollama") to a
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package vision
|
|||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/ai/vision/ollama"
|
||||
|
|
@ -23,16 +24,38 @@ func init() {
|
|||
Defaults: ollamaDefaults{},
|
||||
})
|
||||
|
||||
registerOllamaEngineDefaults()
|
||||
}
|
||||
|
||||
// registerOllamaEngineDefaults selects the default Ollama endpoint based on the
|
||||
// configured base URL and registers the engine alias accordingly. When
|
||||
// OLLAMA_BASE_URL points at the cloud host we only switch the default model to
|
||||
// the cloud preset; the actual base URL continues to come from
|
||||
// OLLAMA_BASE_URL (or falls back to the local compose default) so we don't
|
||||
// accidentally talk to the hosted service without an explicit endpoint.
|
||||
func registerOllamaEngineDefaults() {
|
||||
ensureEnv()
|
||||
|
||||
defaultModel := ollama.DefaultModel
|
||||
|
||||
// Use different default model for the Ollama cloud service.
|
||||
if baseUrl := os.Getenv(ollama.BaseUrlEnv); baseUrl == ollama.CloudBaseUrl {
|
||||
defaultModel = ollama.CloudModel
|
||||
}
|
||||
|
||||
// Register the human-friendly engine name so configuration can simply use
|
||||
// `Engine: "ollama"` and inherit adapter defaults.
|
||||
RegisterEngineAlias(ollama.EngineName, EngineInfo{
|
||||
Uri: ollama.DefaultUri,
|
||||
RequestFormat: ApiFormatOllama,
|
||||
ResponseFormat: ApiFormatOllama,
|
||||
FileScheme: scheme.Base64,
|
||||
DefaultModel: defaultModel,
|
||||
DefaultResolution: ollama.DefaultResolution,
|
||||
DefaultKey: ollama.APIKeyPlaceholder,
|
||||
})
|
||||
|
||||
CaptionModel.Engine = ollama.EngineName
|
||||
// Keep the default caption model config aligned with the defaults.
|
||||
CaptionModel.ApplyEngineDefaults()
|
||||
}
|
||||
|
||||
|
|
@ -78,20 +101,20 @@ func (ollamaDefaults) SchemaTemplate(model *Model) string {
|
|||
}
|
||||
|
||||
// Options returns the Ollama service request options.
|
||||
func (ollamaDefaults) Options(model *Model) *ApiRequestOptions {
|
||||
func (ollamaDefaults) Options(model *Model) *ModelOptions {
|
||||
if model == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch model.Type {
|
||||
case ModelTypeLabels:
|
||||
return &ApiRequestOptions{
|
||||
return &ModelOptions{
|
||||
Temperature: DefaultTemperature,
|
||||
TopP: 0.9,
|
||||
Stop: []string{"\n\n"},
|
||||
}
|
||||
case ModelTypeCaption:
|
||||
return &ApiRequestOptions{
|
||||
return &ModelOptions{
|
||||
Temperature: DefaultTemperature,
|
||||
}
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -3,11 +3,136 @@ package vision
|
|||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/ai/vision/ollama"
|
||||
"github.com/photoprism/photoprism/pkg/http/scheme"
|
||||
)
|
||||
|
||||
func TestRegisterOllamaEngineDefaults(t *testing.T) {
|
||||
original := os.Getenv(ollama.APIKeyEnv)
|
||||
originalCaptionModel := CaptionModel.Clone()
|
||||
testCaptionModel := CaptionModel.Clone()
|
||||
testCaptionModel.Model = ""
|
||||
testCaptionModel.Service.Uri = ""
|
||||
cloudToken := "moo9yaiS4ShoKiojiathie2vuejiec2X.Mahl7ewaej4ebi7afq8f_vwe" //nolint:gosec
|
||||
|
||||
t.Cleanup(func() {
|
||||
if original == "" {
|
||||
_ = os.Unsetenv(ollama.APIKeyEnv)
|
||||
} else {
|
||||
_ = os.Setenv(ollama.APIKeyEnv, original)
|
||||
}
|
||||
CaptionModel = originalCaptionModel
|
||||
registerOllamaEngineDefaults()
|
||||
})
|
||||
|
||||
t.Run("SelfHosted", func(t *testing.T) {
|
||||
ensureEnvOnce = sync.Once{}
|
||||
CaptionModel = testCaptionModel.Clone()
|
||||
_ = os.Unsetenv(ollama.APIKeyEnv)
|
||||
|
||||
registerOllamaEngineDefaults()
|
||||
|
||||
info, ok := EngineInfoFor(ollama.EngineName)
|
||||
if !ok {
|
||||
t.Fatalf("expected engine info for %s", ollama.EngineName)
|
||||
}
|
||||
|
||||
if info.Uri != ollama.DefaultUri {
|
||||
t.Fatalf("expected default uri %s, got %s", ollama.DefaultUri, info.Uri)
|
||||
}
|
||||
|
||||
if info.DefaultModel != ollama.DefaultModel {
|
||||
t.Fatalf("expected default model %s, got %s", ollama.DefaultModel, info.DefaultModel)
|
||||
}
|
||||
|
||||
if CaptionModel.Model != ollama.DefaultModel {
|
||||
t.Fatalf("expected caption model %s, got %s", ollama.DefaultModel, CaptionModel.Model)
|
||||
}
|
||||
|
||||
if CaptionModel.Service.Uri != ollama.DefaultUri {
|
||||
t.Fatalf("expected caption model uri %s, got %s", ollama.DefaultUri, CaptionModel.Service.Uri)
|
||||
}
|
||||
})
|
||||
t.Run("Cloud", func(t *testing.T) {
|
||||
ensureEnvOnce = sync.Once{}
|
||||
CaptionModel = testCaptionModel.Clone()
|
||||
t.Setenv(ollama.BaseUrlEnv, ollama.CloudBaseUrl+"/")
|
||||
|
||||
registerOllamaEngineDefaults()
|
||||
|
||||
info, ok := EngineInfoFor(ollama.EngineName)
|
||||
if !ok {
|
||||
t.Fatalf("expected engine info for %s", ollama.EngineName)
|
||||
}
|
||||
|
||||
if info.Uri != ollama.DefaultUri {
|
||||
t.Fatalf("expected default uri %s, got %s", ollama.DefaultUri, info.Uri)
|
||||
}
|
||||
|
||||
if info.DefaultModel != ollama.CloudModel {
|
||||
t.Fatalf("expected cloud model %s, got %s", ollama.CloudModel, info.DefaultModel)
|
||||
}
|
||||
|
||||
if CaptionModel.Model != ollama.CloudModel {
|
||||
t.Fatalf("expected caption model %s, got %s", ollama.CloudModel, CaptionModel.Model)
|
||||
}
|
||||
|
||||
if CaptionModel.Service.Uri != ollama.DefaultUri {
|
||||
t.Fatalf("expected caption model uri %s, got %s", ollama.DefaultUri, CaptionModel.Service.Uri)
|
||||
}
|
||||
})
|
||||
t.Run("ApiKeyAloneKeepsLocalDefaults", func(t *testing.T) {
|
||||
ensureEnvOnce = sync.Once{}
|
||||
CaptionModel = testCaptionModel.Clone()
|
||||
t.Setenv(ollama.APIKeyEnv, cloudToken)
|
||||
|
||||
registerOllamaEngineDefaults()
|
||||
|
||||
info, ok := EngineInfoFor(ollama.EngineName)
|
||||
if !ok {
|
||||
t.Fatalf("expected engine info for %s", ollama.EngineName)
|
||||
}
|
||||
|
||||
if info.DefaultModel != ollama.DefaultModel {
|
||||
t.Fatalf("expected default model %s, got %s", ollama.DefaultModel, info.DefaultModel)
|
||||
}
|
||||
})
|
||||
t.Run("NewModels", func(t *testing.T) {
|
||||
ensureEnvOnce = sync.Once{}
|
||||
CaptionModel = testCaptionModel.Clone()
|
||||
|
||||
t.Setenv(ollama.BaseUrlEnv, ollama.CloudBaseUrl)
|
||||
registerOllamaEngineDefaults()
|
||||
|
||||
model := &Model{Type: ModelTypeCaption, Engine: ollama.EngineName}
|
||||
model.ApplyEngineDefaults()
|
||||
|
||||
if model.Model != ollama.CloudModel {
|
||||
t.Fatalf("expected model %s, got %s", ollama.CloudModel, model.Model)
|
||||
}
|
||||
|
||||
if model.Service.Uri != ollama.DefaultUri {
|
||||
t.Fatalf("expected service uri %s, got %s", ollama.DefaultUri, model.Service.Uri)
|
||||
}
|
||||
|
||||
if model.Service.RequestFormat != ApiFormatOllama || model.Service.ResponseFormat != ApiFormatOllama {
|
||||
t.Fatalf("expected request/response format %s, got %s/%s", ApiFormatOllama, model.Service.RequestFormat, model.Service.ResponseFormat)
|
||||
}
|
||||
|
||||
if model.Service.FileScheme != scheme.Base64 {
|
||||
t.Fatalf("expected file scheme %s, got %s", scheme.Base64, model.Service.FileScheme)
|
||||
}
|
||||
|
||||
if model.Resolution != ollama.DefaultResolution {
|
||||
t.Fatalf("expected resolution %d, got %d", ollama.DefaultResolution, model.Resolution)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestOllamaDefaultConfidenceApplied(t *testing.T) {
|
||||
req := &ApiRequest{Format: FormatJSON}
|
||||
payload := ollama.Response{
|
||||
|
|
|
|||
|
|
@ -28,6 +28,16 @@ func init() {
|
|||
Parser: openaiParser{},
|
||||
Defaults: openaiDefaults{},
|
||||
})
|
||||
|
||||
RegisterEngineAlias(openai.EngineName, EngineInfo{
|
||||
Uri: "https://api.openai.com/v1/responses",
|
||||
RequestFormat: ApiFormatOpenAI,
|
||||
ResponseFormat: ApiFormatOpenAI,
|
||||
FileScheme: scheme.Data,
|
||||
DefaultModel: openai.DefaultModel,
|
||||
DefaultResolution: openai.DefaultResolution,
|
||||
DefaultKey: openai.APIKeyPlaceholder,
|
||||
})
|
||||
}
|
||||
|
||||
// SystemPrompt returns the default OpenAI system prompt for the specified model type.
|
||||
|
|
@ -80,19 +90,19 @@ func (openaiDefaults) SchemaTemplate(model *Model) string {
|
|||
}
|
||||
|
||||
// Options returns default OpenAI request options for the model.
|
||||
func (openaiDefaults) Options(model *Model) *ApiRequestOptions {
|
||||
func (openaiDefaults) Options(model *Model) *ModelOptions {
|
||||
if model == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch model.Type {
|
||||
case ModelTypeCaption:
|
||||
return &ApiRequestOptions{
|
||||
return &ModelOptions{
|
||||
Detail: openai.DefaultDetail,
|
||||
MaxOutputTokens: openai.CaptionMaxTokens,
|
||||
}
|
||||
case ModelTypeLabels:
|
||||
return &ApiRequestOptions{
|
||||
return &ModelOptions{
|
||||
Detail: openai.DefaultDetail,
|
||||
MaxOutputTokens: openai.LabelsMaxTokens,
|
||||
ForceJson: true,
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ func TestOpenAIBuilderBuildCaptionDisablesForceJSON(t *testing.T) {
|
|||
Type: ModelTypeCaption,
|
||||
Name: openai.DefaultModel,
|
||||
Engine: openai.EngineName,
|
||||
Options: &ApiRequestOptions{ForceJson: true},
|
||||
Options: &ModelOptions{ForceJson: true},
|
||||
}
|
||||
model.ApplyEngineDefaults()
|
||||
|
||||
|
|
@ -59,7 +59,7 @@ func TestApiRequestJSONForOpenAI(t *testing.T) {
|
|||
Prompt: "describe the scene",
|
||||
Images: []string{"data:image/jpeg;base64,AA=="},
|
||||
ResponseFormat: ApiFormatOpenAI,
|
||||
Options: &ApiRequestOptions{
|
||||
Options: &ModelOptions{
|
||||
Detail: openai.DefaultDetail,
|
||||
MaxOutputTokens: 128,
|
||||
Temperature: 0.2,
|
||||
|
|
@ -111,7 +111,7 @@ func TestApiRequestJSONForOpenAIDefaultSchemaName(t *testing.T) {
|
|||
Model: "gpt-5-mini",
|
||||
Images: []string{"data:image/jpeg;base64,AA=="},
|
||||
ResponseFormat: ApiFormatOpenAI,
|
||||
Options: &ApiRequestOptions{
|
||||
Options: &ModelOptions{
|
||||
Detail: openai.DefaultDetail,
|
||||
MaxOutputTokens: 64,
|
||||
ForceJson: true,
|
||||
|
|
@ -254,7 +254,7 @@ func TestPerformApiRequestOpenAISuccess(t *testing.T) {
|
|||
Model: "gpt-5-mini",
|
||||
Images: []string{"data:image/jpeg;base64,AA=="},
|
||||
ResponseFormat: ApiFormatOpenAI,
|
||||
Options: &ApiRequestOptions{
|
||||
Options: &ModelOptions{
|
||||
Detail: openai.DefaultDetail,
|
||||
},
|
||||
Schema: json.RawMessage(`{"type":"object"}`),
|
||||
|
|
@ -299,7 +299,7 @@ func TestPerformApiRequestOpenAITextFallback(t *testing.T) {
|
|||
Model: "gpt-5-mini",
|
||||
Images: []string{"data:image/jpeg;base64,AA=="},
|
||||
ResponseFormat: ApiFormatOpenAI,
|
||||
Options: &ApiRequestOptions{
|
||||
Options: &ModelOptions{
|
||||
Detail: openai.DefaultDetail,
|
||||
},
|
||||
Schema: nil,
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ type Model struct {
|
|||
SchemaFile string `yaml:"SchemaFile,omitempty" json:"schemaFile,omitempty"`
|
||||
Resolution int `yaml:"Resolution,omitempty" json:"resolution,omitempty"`
|
||||
TensorFlow *tensorflow.ModelInfo `yaml:"TensorFlow,omitempty" json:"tensorflow,omitempty"`
|
||||
Options *ApiRequestOptions `yaml:"Options,omitempty" json:"options,omitempty"`
|
||||
Options *ModelOptions `yaml:"Options,omitempty" json:"options,omitempty"`
|
||||
Service Service `yaml:"Service,omitempty" json:"service,omitempty"`
|
||||
Path string `yaml:"Path,omitempty" json:"-"`
|
||||
Disabled bool `yaml:"Disabled,omitempty" json:"disabled,omitempty"`
|
||||
|
|
@ -133,8 +133,6 @@ func (m *Model) IsDefault() bool {
|
|||
return m.Name == NsfwModel.Name
|
||||
case ModelTypeFace:
|
||||
return m.Name == FacenetModel.Name
|
||||
case ModelTypeCaption:
|
||||
return m.Name == CaptionModel.Name
|
||||
}
|
||||
|
||||
return false
|
||||
|
|
@ -334,12 +332,12 @@ func (m *Model) GetSource() string {
|
|||
|
||||
// GetOptions returns the API request options, applying engine defaults on
|
||||
// demand. Nil receivers return nil.
|
||||
func (m *Model) GetOptions() *ApiRequestOptions {
|
||||
func (m *Model) GetOptions() *ModelOptions {
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var engineDefaults *ApiRequestOptions
|
||||
var engineDefaults *ModelOptions
|
||||
if defaults := m.engineDefaults(); defaults != nil {
|
||||
engineDefaults = cloneOptions(defaults.Options(m))
|
||||
}
|
||||
|
|
@ -348,7 +346,7 @@ func (m *Model) GetOptions() *ApiRequestOptions {
|
|||
switch m.Type {
|
||||
case ModelTypeLabels, ModelTypeCaption, ModelTypeGenerate:
|
||||
if engineDefaults == nil {
|
||||
engineDefaults = &ApiRequestOptions{}
|
||||
engineDefaults = &ModelOptions{}
|
||||
}
|
||||
normalizeOptions(engineDefaults)
|
||||
m.Options = engineDefaults
|
||||
|
|
@ -364,7 +362,7 @@ func (m *Model) GetOptions() *ApiRequestOptions {
|
|||
return m.Options
|
||||
}
|
||||
|
||||
func mergeOptionDefaults(target, defaults *ApiRequestOptions) {
|
||||
func mergeOptionDefaults(target, defaults *ModelOptions) {
|
||||
if target == nil || defaults == nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -402,7 +400,7 @@ func mergeOptionDefaults(target, defaults *ApiRequestOptions) {
|
|||
}
|
||||
}
|
||||
|
||||
func normalizeOptions(opts *ApiRequestOptions) {
|
||||
func normalizeOptions(opts *ModelOptions) {
|
||||
if opts == nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -412,7 +410,7 @@ func normalizeOptions(opts *ApiRequestOptions) {
|
|||
}
|
||||
}
|
||||
|
||||
func cloneOptions(opts *ApiRequestOptions) *ApiRequestOptions {
|
||||
func cloneOptions(opts *ModelOptions) *ModelOptions {
|
||||
if opts == nil {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -467,34 +465,39 @@ func (m *Model) ApplyEngineDefaults() {
|
|||
}
|
||||
|
||||
engine := strings.TrimSpace(strings.ToLower(m.Engine))
|
||||
|
||||
if engine == "" {
|
||||
return
|
||||
}
|
||||
|
||||
if info, ok := EngineInfoFor(engine); ok {
|
||||
if m.Service.Uri == "" {
|
||||
if strings.TrimSpace(m.Model) == "" && strings.TrimSpace(m.Name) == "" {
|
||||
m.Model = info.DefaultModel
|
||||
}
|
||||
|
||||
if strings.TrimSpace(m.Service.Uri) == "" {
|
||||
m.Service.Uri = info.Uri
|
||||
}
|
||||
|
||||
if m.Service.RequestFormat == "" {
|
||||
if strings.TrimSpace(m.Service.RequestFormat) == "" {
|
||||
m.Service.RequestFormat = info.RequestFormat
|
||||
}
|
||||
|
||||
if m.Service.ResponseFormat == "" {
|
||||
if strings.TrimSpace(m.Service.ResponseFormat) == "" {
|
||||
m.Service.ResponseFormat = info.ResponseFormat
|
||||
}
|
||||
|
||||
if info.FileScheme != "" && m.Service.FileScheme == "" {
|
||||
if strings.TrimSpace(m.Service.FileScheme) == "" && info.FileScheme != "" {
|
||||
m.Service.FileScheme = info.FileScheme
|
||||
}
|
||||
|
||||
if info.DefaultResolution > 0 && m.Resolution <= 0 {
|
||||
if m.Resolution <= 0 && info.DefaultResolution > 0 {
|
||||
m.Resolution = info.DefaultResolution
|
||||
}
|
||||
}
|
||||
|
||||
if engine == openai.EngineName && strings.TrimSpace(m.Service.Key) == "" {
|
||||
m.Service.Key = "${OPENAI_API_KEY}"
|
||||
if strings.TrimSpace(m.Service.Key) == "" && info.DefaultKey != "" {
|
||||
m.Service.Key = info.DefaultKey
|
||||
}
|
||||
}
|
||||
|
||||
m.Engine = engine
|
||||
|
|
|
|||
39
internal/ai/vision/model_options.go
Normal file
39
internal/ai/vision/model_options.go
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
package vision
|
||||
|
||||
// ModelOptions represents additional model parameters listed in the documentation.
|
||||
// Comments note which engines currently honor each field.
|
||||
type ModelOptions struct {
|
||||
Temperature float64 `yaml:"Temperature,omitempty" json:"temperature,omitempty"` // Ollama, OpenAI
|
||||
TopK int `yaml:"TopK,omitempty" json:"top_k,omitempty"` // Ollama
|
||||
TopP float64 `yaml:"TopP,omitempty" json:"top_p,omitempty"` // Ollama, OpenAI
|
||||
MinP float64 `yaml:"MinP,omitempty" json:"min_p,omitempty"` // Ollama
|
||||
TypicalP float64 `yaml:"TypicalP,omitempty" json:"typical_p,omitempty"` // Ollama
|
||||
TfsZ float64 `yaml:"TfsZ,omitempty" json:"tfs_z,omitempty"` // Ollama
|
||||
Seed int `yaml:"Seed,omitempty" json:"seed,omitempty"` // Ollama
|
||||
NumKeep int `yaml:"NumKeep,omitempty" json:"num_keep,omitempty"` // Ollama
|
||||
RepeatLastN int `yaml:"RepeatLastN,omitempty" json:"repeat_last_n,omitempty"` // Ollama
|
||||
RepeatPenalty float64 `yaml:"RepeatPenalty,omitempty" json:"repeat_penalty,omitempty"` // Ollama
|
||||
PresencePenalty float64 `yaml:"PresencePenalty,omitempty" json:"presence_penalty,omitempty"` // OpenAI
|
||||
FrequencyPenalty float64 `yaml:"FrequencyPenalty,omitempty" json:"frequency_penalty,omitempty"` // OpenAI
|
||||
PenalizeNewline bool `yaml:"PenalizeNewline,omitempty" json:"penalize_newline,omitempty"` // Ollama
|
||||
Stop []string `yaml:"Stop,omitempty" json:"stop,omitempty"` // Ollama, OpenAI
|
||||
Mirostat int `yaml:"Mirostat,omitempty" json:"mirostat,omitempty"` // Ollama
|
||||
MirostatTau float64 `yaml:"MirostatTau,omitempty" json:"mirostat_tau,omitempty"` // Ollama
|
||||
MirostatEta float64 `yaml:"MirostatEta,omitempty" json:"mirostat_eta,omitempty"` // Ollama
|
||||
NumPredict int `yaml:"NumPredict,omitempty" json:"num_predict,omitempty"` // Ollama
|
||||
MaxOutputTokens int `yaml:"MaxOutputTokens,omitempty" json:"max_output_tokens,omitempty"` // Ollama, OpenAI
|
||||
ForceJson bool `yaml:"ForceJson,omitempty" json:"force_json,omitempty"` // Ollama, OpenAI
|
||||
SchemaVersion string `yaml:"SchemaVersion,omitempty" json:"schema_version,omitempty"` // Ollama, OpenAI
|
||||
CombineOutputs string `yaml:"CombineOutputs,omitempty" json:"combine_outputs,omitempty"` // OpenAI
|
||||
Detail string `yaml:"Detail,omitempty" json:"detail,omitempty"` // OpenAI
|
||||
NumCtx int `yaml:"NumCtx,omitempty" json:"num_ctx,omitempty"` // Ollama, OpenAI
|
||||
NumThread int `yaml:"NumThread,omitempty" json:"num_thread,omitempty"` // Ollama
|
||||
NumBatch int `yaml:"NumBatch,omitempty" json:"num_batch,omitempty"` // Ollama
|
||||
NumGpu int `yaml:"NumGpu,omitempty" json:"num_gpu,omitempty"` // Ollama
|
||||
MainGpu int `yaml:"MainGpu,omitempty" json:"main_gpu,omitempty"` // Ollama
|
||||
LowVram bool `yaml:"LowVram,omitempty" json:"low_vram,omitempty"` // Ollama
|
||||
VocabOnly bool `yaml:"VocabOnly,omitempty" json:"vocab_only,omitempty"` // Ollama
|
||||
UseMmap bool `yaml:"UseMmap,omitempty" json:"use_mmap,omitempty"` // Ollama
|
||||
UseMlock bool `yaml:"UseMlock,omitempty" json:"use_mlock,omitempty"` // Ollama
|
||||
Numa bool `yaml:"Numa,omitempty" json:"numa,omitempty"` // Ollama
|
||||
}
|
||||
|
|
@ -158,7 +158,7 @@ func TestModelGetOptionsRespectsCustomValues(t *testing.T) {
|
|||
model := &Model{
|
||||
Type: ModelTypeLabels,
|
||||
Engine: ollama.EngineName,
|
||||
Options: &ApiRequestOptions{
|
||||
Options: &ModelOptions{
|
||||
Temperature: 5,
|
||||
TopP: 0.95,
|
||||
Stop: []string{"CUSTOM"},
|
||||
|
|
@ -183,7 +183,7 @@ func TestModelGetOptionsFillsMissingFields(t *testing.T) {
|
|||
model := &Model{
|
||||
Type: ModelTypeLabels,
|
||||
Engine: ollama.EngineName,
|
||||
Options: &ApiRequestOptions{},
|
||||
Options: &ModelOptions{},
|
||||
}
|
||||
|
||||
model.ApplyEngineDefaults()
|
||||
|
|
@ -226,6 +226,20 @@ func TestModelApplyEngineDefaultsSetsServiceDefaults(t *testing.T) {
|
|||
assert.Equal(t, ApiFormatOpenAI, model.Service.RequestFormat)
|
||||
assert.Equal(t, ApiFormatOpenAI, model.Service.ResponseFormat)
|
||||
assert.Equal(t, scheme.Data, model.Service.FileScheme)
|
||||
assert.Equal(t, openai.APIKeyPlaceholder, model.Service.Key)
|
||||
})
|
||||
t.Run("OllamaEngineDefaults", func(t *testing.T) {
|
||||
model := &Model{
|
||||
Type: ModelTypeLabels,
|
||||
Engine: ollama.EngineName,
|
||||
}
|
||||
|
||||
model.ApplyEngineDefaults()
|
||||
|
||||
assert.Equal(t, ApiFormatOllama, model.Service.RequestFormat)
|
||||
assert.Equal(t, ApiFormatOllama, model.Service.ResponseFormat)
|
||||
assert.Equal(t, scheme.Base64, model.Service.FileScheme)
|
||||
assert.Equal(t, ollama.APIKeyPlaceholder, model.Service.Key)
|
||||
})
|
||||
t.Run("PreserveExistingService", func(t *testing.T) {
|
||||
model := &Model{
|
||||
|
|
@ -235,6 +249,7 @@ func TestModelApplyEngineDefaultsSetsServiceDefaults(t *testing.T) {
|
|||
Uri: "https://custom.example",
|
||||
FileScheme: scheme.Base64,
|
||||
RequestFormat: ApiFormatOpenAI,
|
||||
Key: "custom-key",
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -242,6 +257,7 @@ func TestModelApplyEngineDefaultsSetsServiceDefaults(t *testing.T) {
|
|||
|
||||
assert.Equal(t, "https://custom.example", model.Service.Uri)
|
||||
assert.Equal(t, scheme.Base64, model.Service.FileScheme)
|
||||
assert.Equal(t, "custom-key", model.Service.Key)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -295,6 +311,38 @@ func TestModelEndpointKeyOpenAIFallbacks(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestModelEndpointKeyOllamaFallbacks(t *testing.T) {
|
||||
t.Run("EnvFile", func(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
path := filepath.Join(dir, "ollama.key")
|
||||
if err := os.WriteFile(path, []byte("ollama-from-file\n"), 0o600); err != nil {
|
||||
t.Fatalf("write key file: %v", err)
|
||||
}
|
||||
|
||||
ensureEnvOnce = sync.Once{}
|
||||
|
||||
t.Setenv("OLLAMA_API_KEY", "")
|
||||
t.Setenv("OLLAMA_API_KEY_FILE", path)
|
||||
|
||||
model := &Model{Type: ModelTypeCaption, Engine: ollama.EngineName}
|
||||
model.ApplyEngineDefaults()
|
||||
|
||||
if got := model.EndpointKey(); got != "ollama-from-file" {
|
||||
t.Fatalf("expected file key, got %q", got)
|
||||
}
|
||||
})
|
||||
t.Run("EnvVariable", func(t *testing.T) {
|
||||
t.Setenv("OLLAMA_API_KEY", "ollama-env")
|
||||
|
||||
model := &Model{Type: ModelTypeCaption, Engine: ollama.EngineName}
|
||||
model.ApplyEngineDefaults()
|
||||
|
||||
if got := model.EndpointKey(); got != "ollama-env" {
|
||||
t.Fatalf("expected env key, got %q", got)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestModelGetSource(t *testing.T) {
|
||||
t.Run("NilModel", func(t *testing.T) {
|
||||
var model *Model
|
||||
|
|
@ -347,7 +395,7 @@ func TestModelApplyService(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestModel_IsDefault(t *testing.T) {
|
||||
nasnetCopy := *NasnetModel //nolint:govet // copy for test inspection only
|
||||
nasnetCopy := NasnetModel.Clone() //nolint:govet // copy for test inspection only
|
||||
nasnetCopy.Default = false
|
||||
|
||||
cases := []struct {
|
||||
|
|
@ -362,7 +410,7 @@ func TestModel_IsDefault(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "NasnetCopy",
|
||||
model: &nasnetCopy,
|
||||
model: nasnetCopy,
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -88,14 +88,9 @@ var (
|
|||
},
|
||||
}
|
||||
CaptionModel = &Model{
|
||||
Type: ModelTypeCaption,
|
||||
Name: ollama.CaptionModel,
|
||||
Version: VersionLatest,
|
||||
Engine: ollama.EngineName,
|
||||
Resolution: 720, // Original aspect ratio, with a max size of 720 x 720 pixels.
|
||||
Service: Service{
|
||||
Uri: "http://ollama:11434/api/generate",
|
||||
},
|
||||
Type: ModelTypeCaption,
|
||||
Engine: ollama.EngineName,
|
||||
Run: RunManual,
|
||||
}
|
||||
DefaultModels = Models{
|
||||
NasnetModel,
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
## PhotoPrism — Ollama Engine Integration
|
||||
|
||||
**Last Updated:** November 14, 2025
|
||||
**Last Updated:** December 10, 2025
|
||||
|
||||
### Overview
|
||||
|
||||
This package provides PhotoPrism’s native adapter for Ollama-compatible multimodal models. It lets Caption, Labels, and future Generate workflows call locally hosted models without changing worker logic, reusing the shared API client (`internal/ai/vision/api_client.go`) and result types (`LabelResult`, `CaptionResult`). Requests stay inside your infrastructure, rely on base64 thumbnails, and honor the same ACL, timeout, and logging hooks as the default TensorFlow engines.
|
||||
This package provides PhotoPrism’s native adapter for Ollama-compatible multimodal models. It lets Caption, Labels, and future Generate workflows call locally hosted models without changing worker logic, reusing the shared API client (`internal/ai/vision/api_client.go`) and result types (`LabelResult`, `CaptionResult`). Requests stay inside your infrastructure, rely on base64 thumbnails, and honor the same ACL, timeout, and logging hooks as the default TensorFlow engines. The adapter resolves `${OLLAMA_BASE_URL}/api/generate`, trimming trailing slashes and defaulting to `http://ollama:11434`; set `OLLAMA_BASE_URL=https://ollama.com` to opt into cloud defaults.
|
||||
|
||||
#### Context & Constraints
|
||||
|
||||
- Engine defaults live in `internal/ai/vision/ollama` and are applied whenever a model sets `Engine: ollama`. Aliases map to `ApiFormatOllama`, `scheme.Base64`, and a default 720 px thumbnail.
|
||||
- Engine defaults live in `internal/ai/vision/ollama` and are applied whenever a model sets `Engine: ollama`. Aliases map to `ApiFormatOllama`, `scheme.Base64`, and a default 720 px thumbnail. Cloud defaults are only selected when `OLLAMA_BASE_URL` equals `https://ollama.com`.
|
||||
- Responses may arrive as newline-delimited JSON chunks. `decodeOllamaResponse` keeps the most recent chunk, while `parseOllamaLabels` replays plain JSON strings found in `response`.
|
||||
- Structured JSON is optional for captions but enforced for labels when `Format: json` (default for label models targeting the Ollama engine).
|
||||
- The adapter never overwrites TensorFlow defaults. If an Ollama call fails, downstream code still has Nasnet, NSFW, and Face models available.
|
||||
|
|
@ -72,6 +72,8 @@ This package provides PhotoPrism’s native adapter for Ollama-compatible multim
|
|||
- `PHOTOPRISM_VISION_LABEL_SCHEMA_FILE` — Absolute path to a JSON snippet that overrides the default label schema (applies to every Ollama label model).
|
||||
- `PHOTOPRISM_VISION_YAML` — Custom `vision.yml` path. Keep it synced in Git if you automate deployments.
|
||||
- `OLLAMA_HOST`, `OLLAMA_MODELS`, `OLLAMA_MAX_QUEUE`, `OLLAMA_NUM_PARALLEL`, etc. — Provided in `compose*.yaml` to tune the Ollama daemon. Adjust `OLLAMA_KEEP_ALIVE` if you want models to stay loaded between worker batches.
|
||||
- `OLLAMA_API_KEY` / `OLLAMA_API_KEY_FILE` — Default bearer token picked up when `Service.Key` is empty; useful for hosted Ollama services (e.g., Ollama Cloud).
|
||||
- `OLLAMA_BASE_URL` — Base URL for the Ollama API; defaults to `http://ollama:11434`, trailing slashes are trimmed. Set to `https://ollama.com` to enable cloud defaults.
|
||||
- `PHOTOPRISM_LOG_LEVEL=trace` — Enables verbose request/response previews (truncated to avoid leaking images). Use temporarily when debugging parsing issues.
|
||||
|
||||
#### `vision.yml` Example
|
||||
|
|
@ -89,7 +91,7 @@ Models:
|
|||
Stop: ["\n\n"]
|
||||
ForceJson: true
|
||||
Service:
|
||||
Uri: http://ollama:11434/api/generate
|
||||
Uri: ${OLLAMA_BASE_URL}/api/generate
|
||||
RequestFormat: ollama
|
||||
ResponseFormat: ollama
|
||||
FileScheme: base64
|
||||
|
|
@ -101,7 +103,7 @@ Models:
|
|||
Options:
|
||||
Temperature: 0.2
|
||||
Service:
|
||||
Uri: http://ollama:11434/api/generate
|
||||
Uri: ${OLLAMA_BASE_URL}/api/generate
|
||||
```
|
||||
|
||||
Guidelines:
|
||||
|
|
|
|||
|
|
@ -5,4 +5,40 @@ const (
|
|||
EngineName = "ollama"
|
||||
// ApiFormat identifies Ollama-compatible request and response payloads.
|
||||
ApiFormat = "ollama"
|
||||
// APIKeyEnv defines the environment variable used for Ollama API tokens.
|
||||
APIKeyEnv = "OLLAMA_API_KEY" //nolint:gosec // environment variable name, not a secret
|
||||
// APIKeyFileEnv defines the file-based fallback environment variable for Ollama API tokens.
|
||||
APIKeyFileEnv = "OLLAMA_API_KEY_FILE" //nolint:gosec // environment variable name, not a secret
|
||||
// APIKeyPlaceholder is the `${VAR}` form injected when no explicit key is provided.
|
||||
APIKeyPlaceholder = "${" + APIKeyEnv + "}"
|
||||
// BaseUrlEnv defines the environment variable used for the Ollama base URL e.g. "https://ollama.com" or "http://ollama:11434".
|
||||
BaseUrlEnv = "OLLAMA_BASE_URL" //nolint:gosec // environment variable name, not a secret
|
||||
// BaseUrlPlaceholder is the `${VAR}` form injected when no explicit URL is provided.
|
||||
BaseUrlPlaceholder = "${" + BaseUrlEnv + "}"
|
||||
// DefaultBaseUrl is the local Ollama endpoint used when the environment variable is unset.
|
||||
DefaultBaseUrl = "http://ollama:11434"
|
||||
// CloudBaseUrl is the base URL for the Ollama Cloud service.
|
||||
CloudBaseUrl = "https://ollama.com"
|
||||
// DefaultUri is the default service URI for self-hosted Ollama instances.
|
||||
DefaultUri = BaseUrlPlaceholder + "/api/generate"
|
||||
// DefaultModel names the default caption model bundled with our adapter defaults.
|
||||
DefaultModel = "gemma3:latest"
|
||||
// CloudModel names the default caption for the Ollama cloud service, see https://ollama.com/cloud.
|
||||
CloudModel = "qwen3-vl:235b-instruct"
|
||||
// CaptionPrompt instructs Ollama caption models to emit a single, active-voice sentence.
|
||||
CaptionPrompt = "Create a caption with exactly one sentence in the active voice that describes the main visual content. Begin with the main subject and clear action. Avoid text formatting, meta-language, and filler words."
|
||||
// LabelConfidenceDefault is used when the model omits the confidence field.
|
||||
LabelConfidenceDefault = 0.5
|
||||
// LabelSystem defines the system prompt shared by Ollama label models. It aims to ensure that single-word nouns are returned.
|
||||
LabelSystem = "You are a PhotoPrism vision model. Output concise JSON that matches the schema. Each label name MUST be a single-word noun in its canonical singular form. Avoid spaces, punctuation, emoji, or descriptive phrases."
|
||||
// LabelSystemSimple defines a simple system prompt for Ollama label models that does not strictly require names to be single-word nouns.
|
||||
LabelSystemSimple = "You are a PhotoPrism vision model. Output concise JSON that matches the schema."
|
||||
// LabelPromptDefault defines a simple user prompt for Ollama label models.
|
||||
LabelPromptDefault = "Analyze the image and return label objects with name, confidence (0-1), and topicality (0-1)."
|
||||
// LabelPromptStrict asks the model to return scored labels for the provided image. It aims to ensure that single-word nouns are returned.
|
||||
LabelPromptStrict = "Analyze the image and return label objects with name (single-word noun), confidence (0-1), and topicality (0-1). Respond with JSON exactly like {\"labels\":[{\"name\":\"sunset\",\"confidence\":0.72,\"topicality\":0.64}]} and adjust the values for this image."
|
||||
// LabelPromptNSFW asks the model to return scored labels for the provided image that includes a NSFW flag and score. It aims to ensure that single-word nouns are returned.
|
||||
LabelPromptNSFW = "Analyze the image and return label objects with name (single-word noun), confidence (0-1), topicality (0-1), nsfw (true when the label describes sensitive or adult content), and nsfw_confidence (0-1). Respond with JSON exactly like {\"labels\":[{\"name\":\"sunset\",\"confidence\":0.72,\"topicality\":0.64,\"nsfw\":false,\"nsfw_confidence\":0.02}]} and adjust the values for this image."
|
||||
// DefaultResolution is the default thumbnail size submitted to Ollama models.
|
||||
DefaultResolution = 720
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,22 +0,0 @@
|
|||
package ollama
|
||||
|
||||
const (
|
||||
// CaptionPrompt instructs Ollama caption models to emit a single, active-voice sentence.
|
||||
CaptionPrompt = "Create a caption with exactly one sentence in the active voice that describes the main visual content. Begin with the main subject and clear action. Avoid text formatting, meta-language, and filler words."
|
||||
// CaptionModel names the default caption model bundled with our adapter defaults.
|
||||
CaptionModel = "gemma3"
|
||||
// LabelConfidenceDefault is used when the model omits the confidence field.
|
||||
LabelConfidenceDefault = 0.5
|
||||
// LabelSystem defines the system prompt shared by Ollama label models. It aims to ensure that single-word nouns are returned.
|
||||
LabelSystem = "You are a PhotoPrism vision model. Output concise JSON that matches the schema. Each label name MUST be a single-word noun in its canonical singular form. Avoid spaces, punctuation, emoji, or descriptive phrases."
|
||||
// LabelSystemSimple defines a simple system prompt for Ollama label models that does not strictly require names to be single-word nouns.
|
||||
LabelSystemSimple = "You are a PhotoPrism vision model. Output concise JSON that matches the schema."
|
||||
// LabelPromptDefault defines a simple user prompt for Ollama label models.
|
||||
LabelPromptDefault = "Analyze the image and return label objects with name, confidence (0-1), and topicality (0-1)."
|
||||
// LabelPromptStrict asks the model to return scored labels for the provided image. It aims to ensure that single-word nouns are returned.
|
||||
LabelPromptStrict = "Analyze the image and return label objects with name (single-word noun), confidence (0-1), and topicality (0-1). Respond with JSON exactly like {\"labels\":[{\"name\":\"sunset\",\"confidence\":0.72,\"topicality\":0.64}]} and adjust the values for this image."
|
||||
// LabelPromptNSFW asks the model to return scored labels for the provided image that includes a NSFW flag and score. It aims to ensure that single-word nouns are returned.
|
||||
LabelPromptNSFW = "Analyze the image and return label objects with name (single-word noun), confidence (0-1), topicality (0-1), nsfw (true when the label describes sensitive or adult content), and nsfw_confidence (0-1). Respond with JSON exactly like {\"labels\":[{\"name\":\"sunset\",\"confidence\":0.72,\"topicality\":0.64,\"nsfw\":false,\"nsfw_confidence\":0.02}]} and adjust the values for this image."
|
||||
// DefaultResolution is the default thumbnail size submitted to Ollama models.
|
||||
DefaultResolution = 720
|
||||
)
|
||||
|
|
@ -5,4 +5,36 @@ const (
|
|||
EngineName = "openai"
|
||||
// ApiFormat identifies OpenAI-compatible request and response payloads.
|
||||
ApiFormat = "openai"
|
||||
// APIKeyEnv defines the environment variable used for OpenAI API tokens.
|
||||
APIKeyEnv = "OPENAI_API_KEY" //nolint:gosec // environment variable name, not a secret
|
||||
// APIKeyFileEnv defines the file-based fallback environment variable for OpenAI API tokens.
|
||||
APIKeyFileEnv = "OPENAI_API_KEY_FILE" //nolint:gosec // environment variable name, not a secret
|
||||
// APIKeyPlaceholder is the `${VAR}` form injected when no explicit key is provided.
|
||||
APIKeyPlaceholder = "${" + APIKeyEnv + "}"
|
||||
// DefaultModel is the model used by default when accessing the OpenAI API.
|
||||
DefaultModel = "gpt-5-mini"
|
||||
// DefaultResolution is the default thumbnail size submitted to the OpenAI.
|
||||
DefaultResolution = 720
|
||||
// CaptionSystem defines the default system prompt for caption models.
|
||||
CaptionSystem = "You are a PhotoPrism vision model. Return concise, user-friendly captions that describe the main subjects accurately."
|
||||
// CaptionPrompt instructs caption models to respond with a single sentence.
|
||||
CaptionPrompt = "Provide exactly one sentence describing the key subject and action in the image. Avoid filler words and technical jargon."
|
||||
// LabelSystem defines the system prompt for label generation.
|
||||
LabelSystem = "You are a PhotoPrism vision model. Emit JSON that matches the provided schema and keep label names short, singular nouns."
|
||||
// LabelPromptDefault requests general-purpose labels.
|
||||
LabelPromptDefault = "Analyze the image and return label objects with name, confidence (0-1), and topicality (0-1)."
|
||||
// LabelPromptNSFW requests labels including NSFW metadata when required.
|
||||
LabelPromptNSFW = "Analyze the image and return label objects with name, confidence (0-1), topicality (0-1), nsfw (true when sensitive), and nsfw_confidence (0-1)."
|
||||
// DefaultDetail specifies the preferred thumbnail detail level for Requests API calls.
|
||||
DefaultDetail = "low"
|
||||
// CaptionMaxTokens suggests the output budget for caption responses.
|
||||
CaptionMaxTokens = 512
|
||||
// LabelsMaxTokens suggests the output budget for label responses.
|
||||
LabelsMaxTokens = 1024
|
||||
// DefaultTemperature configures deterministic replies.
|
||||
DefaultTemperature = 0.1
|
||||
// DefaultTopP limits nucleus sampling.
|
||||
DefaultTopP = 0.9
|
||||
// DefaultSchemaVersion is used when callers do not specify an explicit schema version.
|
||||
DefaultSchemaVersion = "v1"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,33 +0,0 @@
|
|||
package openai
|
||||
|
||||
const (
|
||||
// CaptionSystem defines the default system prompt for caption models.
|
||||
CaptionSystem = "You are a PhotoPrism vision model. Return concise, user-friendly captions that describe the main subjects accurately."
|
||||
// CaptionPrompt instructs caption models to respond with a single sentence.
|
||||
CaptionPrompt = "Provide exactly one sentence describing the key subject and action in the image. Avoid filler words and technical jargon."
|
||||
// LabelSystem defines the system prompt for label generation.
|
||||
LabelSystem = "You are a PhotoPrism vision model. Emit JSON that matches the provided schema and keep label names short, singular nouns."
|
||||
// LabelPromptDefault requests general-purpose labels.
|
||||
LabelPromptDefault = "Analyze the image and return label objects with name, confidence (0-1), and topicality (0-1)."
|
||||
// LabelPromptNSFW requests labels including NSFW metadata when required.
|
||||
LabelPromptNSFW = "Analyze the image and return label objects with name, confidence (0-1), topicality (0-1), nsfw (true when sensitive), and nsfw_confidence (0-1)."
|
||||
// DefaultDetail specifies the preferred thumbnail detail level for Requests API calls.
|
||||
DefaultDetail = "low"
|
||||
// CaptionMaxTokens suggests the output budget for caption responses.
|
||||
CaptionMaxTokens = 512
|
||||
// LabelsMaxTokens suggests the output budget for label responses.
|
||||
LabelsMaxTokens = 1024
|
||||
// DefaultTemperature configures deterministic replies.
|
||||
DefaultTemperature = 0.1
|
||||
// DefaultTopP limits nucleus sampling.
|
||||
DefaultTopP = 0.9
|
||||
// DefaultSchemaVersion is used when callers do not specify an explicit schema version.
|
||||
DefaultSchemaVersion = "v1"
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultModel is the model used by default when accessing the OpenAI API.
|
||||
DefaultModel = "gpt-5-mini"
|
||||
// DefaultResolution is the default thumbnail size submitted to the OpenAI.
|
||||
DefaultResolution = 720
|
||||
)
|
||||
|
|
@ -31,14 +31,18 @@ func (m *Service) Endpoint() (uri, method string) {
|
|||
return "", ""
|
||||
}
|
||||
|
||||
ensureEnv()
|
||||
|
||||
if uri = strings.TrimSpace(os.ExpandEnv(m.Uri)); strings.Contains(uri, "${") {
|
||||
uri = ""
|
||||
}
|
||||
|
||||
if m.Method != "" {
|
||||
method = m.Method
|
||||
} else {
|
||||
method = ServiceMethod
|
||||
}
|
||||
|
||||
uri = strings.TrimSpace(m.Uri)
|
||||
|
||||
if username, password := m.BasicAuth(); username != "" || password != "" {
|
||||
if parsed, err := url.Parse(uri); err == nil {
|
||||
if parsed.User == nil {
|
||||
|
|
|
|||
|
|
@ -33,10 +33,26 @@ func TestServiceEndpoint(t *testing.T) {
|
|||
wantURI: "https://keep:me@vision.example.com",
|
||||
wantMethod: ServiceMethod,
|
||||
},
|
||||
{
|
||||
name: "ExpandsBaseUrlEnv",
|
||||
svc: Service{Uri: "${OLLAMA_BASE_URL}/api/generate"},
|
||||
wantURI: "http://custom:11434/api/generate",
|
||||
wantMethod: ServiceMethod,
|
||||
},
|
||||
{
|
||||
name: "FallbacksWhenEnvMissing",
|
||||
svc: Service{Uri: "${OLLAMA_BASE_URL}/api/generate"},
|
||||
wantURI: "http://ollama:11434/api/generate",
|
||||
wantMethod: ServiceMethod,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.name == "ExpandsBaseUrlEnv" {
|
||||
t.Setenv("OLLAMA_BASE_URL", "http://custom:11434")
|
||||
}
|
||||
|
||||
uri, method := tt.svc.Endpoint()
|
||||
if uri != tt.wantURI {
|
||||
t.Fatalf("uri: got %q want %q", uri, tt.wantURI)
|
||||
|
|
|
|||
7
internal/ai/vision/testdata/vision.yml
vendored
7
internal/ai/vision/testdata/vision.yml
vendored
|
|
@ -65,12 +65,13 @@ Models:
|
|||
Name: embeddings
|
||||
Outputs: 512
|
||||
- Type: caption
|
||||
Name: gemma3
|
||||
Version: latest
|
||||
Model: gemma3:latest
|
||||
Engine: ollama
|
||||
Run: manual
|
||||
Resolution: 720
|
||||
Service:
|
||||
Uri: http://ollama:11434/api/generate
|
||||
Uri: ${OLLAMA_BASE_URL}/api/generate
|
||||
Key: ${OLLAMA_API_KEY}
|
||||
FileScheme: base64
|
||||
RequestFormat: ollama
|
||||
ResponseFormat: ollama
|
||||
|
|
|
|||
|
|
@ -25,35 +25,7 @@ Additional information can be found in our Developer Guide:
|
|||
package vision
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
)
|
||||
|
||||
var log = event.Log
|
||||
|
||||
var ensureEnvOnce sync.Once
|
||||
|
||||
// ensureEnv loads environment-backed credentials once so adapters can look up
|
||||
// OPENAI_API_KEY even when operators rely on OPENAI_API_KEY_FILE. Future engine
|
||||
// integrations can reuse this hook to normalise additional secrets.
|
||||
func ensureEnv() {
|
||||
ensureEnvOnce.Do(func() {
|
||||
if os.Getenv("OPENAI_API_KEY") != "" {
|
||||
return
|
||||
}
|
||||
|
||||
if path := strings.TrimSpace(os.Getenv("OPENAI_API_KEY_FILE")); fs.FileExistsNotEmpty(path) {
|
||||
// #nosec G304 path provided via env
|
||||
if data, err := os.ReadFile(path); err == nil {
|
||||
if key := clean.Auth(string(data)); key != "" {
|
||||
_ = os.Setenv("OPENAI_API_KEY", key)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
61
internal/ai/vision/vision_env.go
Normal file
61
internal/ai/vision/vision_env.go
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
package vision
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/ai/vision/ollama"
|
||||
"github.com/photoprism/photoprism/internal/ai/vision/openai"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
)
|
||||
|
||||
var ensureEnvOnce sync.Once
|
||||
|
||||
// ensureEnv loads environment-backed credentials once so adapters can look up
|
||||
// OPENAI_API_KEY / OLLAMA_API_KEY even when operators rely on *_FILE fallbacks.
|
||||
// Future engine integrations can reuse this hook to normalize additional
|
||||
// secrets.
|
||||
func ensureEnv() {
|
||||
ensureEnvOnce.Do(func() {
|
||||
loadEnvKeyFromFile(openai.APIKeyEnv, openai.APIKeyFileEnv)
|
||||
loadEnvKeyFromFile(ollama.APIKeyEnv, ollama.APIKeyFileEnv)
|
||||
|
||||
// Init the Ollama base URL by trimming trailing slashes or using the default.
|
||||
initEnvUrl(ollama.BaseUrlEnv, ollama.DefaultBaseUrl)
|
||||
})
|
||||
}
|
||||
|
||||
// initEnvUrl ensures that the variable contains no trailing
|
||||
// slashes and sets a default value if it is missing.
|
||||
func initEnvUrl(envName, defaultUrl string) {
|
||||
if base := strings.TrimSpace(os.Getenv(envName)); base != "" {
|
||||
if normalized := strings.TrimRight(base, "/"); normalized != base {
|
||||
_ = os.Setenv(envName, normalized)
|
||||
}
|
||||
} else if defaultUrl != "" {
|
||||
_ = os.Setenv(envName, defaultUrl)
|
||||
}
|
||||
}
|
||||
|
||||
// loadEnvKeyFromFile populates envVar from fileVar when the environment value
|
||||
// is empty and the referenced file exists and is non-empty.
|
||||
func loadEnvKeyFromFile(envVar, fileVar string) {
|
||||
if os.Getenv(envVar) != "" {
|
||||
return
|
||||
}
|
||||
|
||||
filePath := strings.TrimSpace(os.Getenv(fileVar))
|
||||
|
||||
if !fs.FileExistsNotEmpty(filePath) {
|
||||
return
|
||||
}
|
||||
|
||||
// #nosec G304 path provided via env
|
||||
if data, err := os.ReadFile(filePath); err == nil {
|
||||
if key := clean.Auth(string(data)); key != "" {
|
||||
_ = os.Setenv(envVar, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
63
internal/ai/vision/vision_env_test.go
Normal file
63
internal/ai/vision/vision_env_test.go
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
package vision
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestInitEnvUrl(t *testing.T) {
|
||||
const envName = "TEST_OLLAMA_BASE_URL"
|
||||
|
||||
// Case: trims trailing slash.
|
||||
t.Setenv(envName, "http://example.com/")
|
||||
initEnvUrl(envName, "")
|
||||
if got := os.Getenv(envName); got != "http://example.com" {
|
||||
t.Fatalf("trim: expected http://example.com, got %s", got)
|
||||
}
|
||||
|
||||
// Case: sets default when unset.
|
||||
t.Setenv(envName, "")
|
||||
initEnvUrl(envName, "http://default.local")
|
||||
if got := os.Getenv(envName); got != "http://default.local" {
|
||||
t.Fatalf("default: expected http://default.local, got %s", got)
|
||||
}
|
||||
|
||||
// Case: leaves already-normalized value untouched.
|
||||
t.Setenv(envName, "http://kept.local")
|
||||
initEnvUrl(envName, "http://ignored.local")
|
||||
if got := os.Getenv(envName); got != "http://kept.local" {
|
||||
t.Fatalf("preserve: expected http://kept.local, got %s", got)
|
||||
}
|
||||
}
|
||||
|
||||
// TestLoadEnvKeyFromFile verifies that loadEnvKeyFromFile reads API keys from
|
||||
// *_FILE variables when the primary env var is empty.
|
||||
func TestLoadEnvKeyFromFile(t *testing.T) {
|
||||
t.Run("ReadsFileWhenUnset", func(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
path := filepath.Join(dir, "key.txt")
|
||||
if err := os.WriteFile(path, []byte("file-secret\n"), 0o600); err != nil {
|
||||
t.Fatalf("write key file: %v", err)
|
||||
}
|
||||
|
||||
t.Setenv("TEST_KEY", "")
|
||||
t.Setenv("TEST_KEY_FILE", path)
|
||||
|
||||
loadEnvKeyFromFile("TEST_KEY", "TEST_KEY_FILE")
|
||||
|
||||
if got := os.Getenv("TEST_KEY"); got != "file-secret" {
|
||||
t.Fatalf("expected file-secret, got %q", got)
|
||||
}
|
||||
})
|
||||
t.Run("EnvWinsOverFile", func(t *testing.T) {
|
||||
t.Setenv("TEST_KEY", "keep-env")
|
||||
t.Setenv("TEST_KEY_FILE", "/nonexistent")
|
||||
|
||||
loadEnvKeyFromFile("TEST_KEY", "TEST_KEY_FILE")
|
||||
|
||||
if got := os.Getenv("TEST_KEY"); got != "keep-env" {
|
||||
t.Fatalf("expected keep-env, got %q", got)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -4489,11 +4489,11 @@
|
|||
"timestamppb.Timestamp": {
|
||||
"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.",
|
||||
"description": "Non-negative fractions of a second at nanosecond resolution. This field is\nthe nanosecond portion of the duration, not an alternative to seconds.\nNegative second values with fractions must still have non-negative nanos\nvalues that count forward in time. Must be between 0 and 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.",
|
||||
"description": "Represents seconds of UTC time since Unix epoch 1970-01-01T00:00:00Z. Must\nbe between -315576000000 and 315576000000 inclusive (which corresponds to\n0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z).",
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
|
|
@ -4540,7 +4540,7 @@
|
|||
"type": "string"
|
||||
},
|
||||
"options": {
|
||||
"$ref": "#/definitions/vision.ApiRequestOptions"
|
||||
"$ref": "#/definitions/vision.ModelOptions"
|
||||
},
|
||||
"org": {
|
||||
"type": "string"
|
||||
|
|
@ -4575,113 +4575,6 @@
|
|||
},
|
||||
"type": "object"
|
||||
},
|
||||
"vision.ApiRequestOptions": {
|
||||
"properties": {
|
||||
"combine_outputs": {
|
||||
"type": "string"
|
||||
},
|
||||
"detail": {
|
||||
"type": "string"
|
||||
},
|
||||
"force_json": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"frequency_penalty": {
|
||||
"type": "number"
|
||||
},
|
||||
"low_vram": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"main_gpu": {
|
||||
"type": "integer"
|
||||
},
|
||||
"max_output_tokens": {
|
||||
"type": "integer"
|
||||
},
|
||||
"min_p": {
|
||||
"type": "number"
|
||||
},
|
||||
"mirostat": {
|
||||
"type": "integer"
|
||||
},
|
||||
"mirostat_eta": {
|
||||
"type": "number"
|
||||
},
|
||||
"mirostat_tau": {
|
||||
"type": "number"
|
||||
},
|
||||
"num_batch": {
|
||||
"type": "integer"
|
||||
},
|
||||
"num_ctx": {
|
||||
"type": "integer"
|
||||
},
|
||||
"num_gpu": {
|
||||
"type": "integer"
|
||||
},
|
||||
"num_keep": {
|
||||
"type": "integer"
|
||||
},
|
||||
"num_predict": {
|
||||
"type": "integer"
|
||||
},
|
||||
"num_thread": {
|
||||
"type": "integer"
|
||||
},
|
||||
"numa": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"penalize_newline": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"presence_penalty": {
|
||||
"type": "number"
|
||||
},
|
||||
"repeat_last_n": {
|
||||
"type": "integer"
|
||||
},
|
||||
"repeat_penalty": {
|
||||
"type": "number"
|
||||
},
|
||||
"schema_version": {
|
||||
"type": "string"
|
||||
},
|
||||
"seed": {
|
||||
"type": "integer"
|
||||
},
|
||||
"stop": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"temperature": {
|
||||
"type": "number"
|
||||
},
|
||||
"tfs_z": {
|
||||
"type": "number"
|
||||
},
|
||||
"top_k": {
|
||||
"type": "integer"
|
||||
},
|
||||
"top_p": {
|
||||
"type": "number"
|
||||
},
|
||||
"typical_p": {
|
||||
"type": "number"
|
||||
},
|
||||
"use_mlock": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"use_mmap": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"vocab_only": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"vision.ApiResponse": {
|
||||
"properties": {
|
||||
"code": {
|
||||
|
|
@ -4810,7 +4703,7 @@
|
|||
"type": "string"
|
||||
},
|
||||
"options": {
|
||||
"$ref": "#/definitions/vision.ApiRequestOptions"
|
||||
"$ref": "#/definitions/vision.ModelOptions"
|
||||
},
|
||||
"prompt": {
|
||||
"type": "string"
|
||||
|
|
@ -4855,6 +4748,146 @@
|
|||
"EngineLocal"
|
||||
]
|
||||
},
|
||||
"vision.ModelOptions": {
|
||||
"properties": {
|
||||
"combine_outputs": {
|
||||
"description": "OpenAI",
|
||||
"type": "string"
|
||||
},
|
||||
"detail": {
|
||||
"description": "OpenAI",
|
||||
"type": "string"
|
||||
},
|
||||
"force_json": {
|
||||
"description": "Ollama, OpenAI",
|
||||
"type": "boolean"
|
||||
},
|
||||
"frequency_penalty": {
|
||||
"description": "OpenAI",
|
||||
"type": "number"
|
||||
},
|
||||
"low_vram": {
|
||||
"description": "Ollama",
|
||||
"type": "boolean"
|
||||
},
|
||||
"main_gpu": {
|
||||
"description": "Ollama",
|
||||
"type": "integer"
|
||||
},
|
||||
"max_output_tokens": {
|
||||
"description": "Ollama, OpenAI",
|
||||
"type": "integer"
|
||||
},
|
||||
"min_p": {
|
||||
"description": "Ollama",
|
||||
"type": "number"
|
||||
},
|
||||
"mirostat": {
|
||||
"description": "Ollama",
|
||||
"type": "integer"
|
||||
},
|
||||
"mirostat_eta": {
|
||||
"description": "Ollama",
|
||||
"type": "number"
|
||||
},
|
||||
"mirostat_tau": {
|
||||
"description": "Ollama",
|
||||
"type": "number"
|
||||
},
|
||||
"num_batch": {
|
||||
"description": "Ollama",
|
||||
"type": "integer"
|
||||
},
|
||||
"num_ctx": {
|
||||
"description": "Ollama, OpenAI",
|
||||
"type": "integer"
|
||||
},
|
||||
"num_gpu": {
|
||||
"description": "Ollama",
|
||||
"type": "integer"
|
||||
},
|
||||
"num_keep": {
|
||||
"description": "Ollama",
|
||||
"type": "integer"
|
||||
},
|
||||
"num_predict": {
|
||||
"description": "Ollama",
|
||||
"type": "integer"
|
||||
},
|
||||
"num_thread": {
|
||||
"description": "Ollama",
|
||||
"type": "integer"
|
||||
},
|
||||
"numa": {
|
||||
"description": "Ollama",
|
||||
"type": "boolean"
|
||||
},
|
||||
"penalize_newline": {
|
||||
"description": "Ollama",
|
||||
"type": "boolean"
|
||||
},
|
||||
"presence_penalty": {
|
||||
"description": "OpenAI",
|
||||
"type": "number"
|
||||
},
|
||||
"repeat_last_n": {
|
||||
"description": "Ollama",
|
||||
"type": "integer"
|
||||
},
|
||||
"repeat_penalty": {
|
||||
"description": "Ollama",
|
||||
"type": "number"
|
||||
},
|
||||
"schema_version": {
|
||||
"description": "Ollama, OpenAI",
|
||||
"type": "string"
|
||||
},
|
||||
"seed": {
|
||||
"description": "Ollama",
|
||||
"type": "integer"
|
||||
},
|
||||
"stop": {
|
||||
"description": "Ollama, OpenAI",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"temperature": {
|
||||
"description": "Ollama, OpenAI",
|
||||
"type": "number"
|
||||
},
|
||||
"tfs_z": {
|
||||
"description": "Ollama",
|
||||
"type": "number"
|
||||
},
|
||||
"top_k": {
|
||||
"description": "Ollama",
|
||||
"type": "integer"
|
||||
},
|
||||
"top_p": {
|
||||
"description": "Ollama, OpenAI",
|
||||
"type": "number"
|
||||
},
|
||||
"typical_p": {
|
||||
"description": "Ollama",
|
||||
"type": "number"
|
||||
},
|
||||
"use_mlock": {
|
||||
"description": "Ollama",
|
||||
"type": "boolean"
|
||||
},
|
||||
"use_mmap": {
|
||||
"description": "Ollama",
|
||||
"type": "boolean"
|
||||
},
|
||||
"vocab_only": {
|
||||
"description": "Ollama",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"vision.ModelType": {
|
||||
"enum": [
|
||||
"labels",
|
||||
|
|
|
|||
|
|
@ -717,7 +717,7 @@ func (c *Config) IndexSchedule() string {
|
|||
}
|
||||
|
||||
// WakeupInterval returns the duration between background worker runs
|
||||
// required for face recognition and index maintenance(1-86400s).
|
||||
// required for face recognition and index maintenance (1-86400s).
|
||||
func (c *Config) WakeupInterval() time.Duration {
|
||||
if c.options.WakeupInterval <= 0 {
|
||||
if c.Unsafe() {
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ const DefaultAutoIndexDelay = 300 // 5 Minutes
|
|||
const DefaultAutoImportDelay = -1 // Disabled
|
||||
|
||||
// MinWakeupInterval is the minimum allowed interval for the background worker.
|
||||
const MinWakeupInterval = time.Minute // 1 Minute
|
||||
const MinWakeupInterval = time.Second // 1 Second
|
||||
// MaxWakeupInterval is the maximum allowed interval for the background worker.
|
||||
const MaxWakeupInterval = time.Hour * 24 // 1 Day
|
||||
// DefaultWakeupIntervalSeconds is the default worker interval in seconds.
|
||||
|
|
|
|||
|
|
@ -353,7 +353,7 @@ func TestConfig_WakeupInterval(t *testing.T) {
|
|||
|
||||
c.options.WakeupInterval = 45
|
||||
|
||||
assert.Equal(t, "1m0s", c.WakeupInterval().String())
|
||||
assert.Equal(t, "45s", c.WakeupInterval().String())
|
||||
|
||||
c.options.WakeupInterval = 0
|
||||
|
||||
|
|
|
|||
118
internal/server/gzip.go
Normal file
118
internal/server/gzip.go
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
)
|
||||
|
||||
// gzipExcludedExtensions contains file extensions that should never be gzip-compressed.
|
||||
// These formats are already compressed or typically served as large binary payloads.
|
||||
var gzipExcludedExtensions = map[string]struct{}{
|
||||
".png": {},
|
||||
".gif": {},
|
||||
".jpeg": {},
|
||||
".jpg": {},
|
||||
".webp": {},
|
||||
".mp3": {},
|
||||
".mp4": {},
|
||||
".zip": {},
|
||||
".gz": {},
|
||||
}
|
||||
|
||||
// NewGzipShouldCompressFn returns a high-performance gzip decision function for PhotoPrism.
|
||||
// It mirrors the legacy exclusion rules (extensions and path prefixes) and adds targeted
|
||||
// route exclusions for binary/streaming endpoints that must not be compressed.
|
||||
func NewGzipShouldCompressFn(conf *config.Config) func(c *gin.Context) bool {
|
||||
if conf == nil {
|
||||
return func(*gin.Context) bool { return false }
|
||||
}
|
||||
|
||||
apiBase := conf.BaseUri(config.ApiUri)
|
||||
|
||||
// Raw path fallbacks for dynamic exclusions in case FullPath is unavailable.
|
||||
sharePrefix := conf.BaseUri("/s/")
|
||||
photoDlPrefix := apiBase + "/photos/"
|
||||
clusterThemePath := apiBase + "/cluster/theme"
|
||||
|
||||
// FullPath patterns (exact match) for dynamic routes that should bypass gzip.
|
||||
excludedFullPaths := map[string]struct{}{
|
||||
apiBase + "/photos/:uid/dl": {},
|
||||
apiBase + "/cluster/theme": {},
|
||||
conf.BaseUri("/s/:token/:shared/preview"): {},
|
||||
}
|
||||
|
||||
// Path prefixes that should bypass gzip (prefix match on raw URL path).
|
||||
excludedPrefixes := []string{
|
||||
// Health endpoints are small and frequently polled; gzip would add overhead.
|
||||
conf.BaseUri("/livez"),
|
||||
conf.BaseUri("/health"),
|
||||
conf.BaseUri("/readyz"),
|
||||
conf.BaseUri(config.ApiUri + "/t"),
|
||||
conf.BaseUri(config.ApiUri + "/folders/t"),
|
||||
conf.BaseUri(config.ApiUri + "/dl"),
|
||||
conf.BaseUri(config.ApiUri + "/zip"),
|
||||
conf.BaseUri(config.ApiUri + "/albums"),
|
||||
conf.BaseUri(config.ApiUri + "/labels"),
|
||||
conf.BaseUri(config.ApiUri + "/videos"),
|
||||
}
|
||||
|
||||
return func(c *gin.Context) bool {
|
||||
if c == nil || c.Request == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Only compress when the client explicitly accepts gzip and the connection is not upgraded.
|
||||
if !strings.Contains(strings.ToLower(c.GetHeader("Accept-Encoding")), "gzip") {
|
||||
return false
|
||||
}
|
||||
if strings.Contains(strings.ToLower(c.GetHeader("Connection")), "upgrade") {
|
||||
return false
|
||||
}
|
||||
|
||||
path := c.Request.URL.Path
|
||||
if path == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
// Exclude known already-compressed/binary extensions.
|
||||
if ext := strings.ToLower(filepath.Ext(path)); ext != "" {
|
||||
if _, ok := gzipExcludedExtensions[ext]; ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Exclude configured prefix groups.
|
||||
for _, prefix := range excludedPrefixes {
|
||||
if prefix != "" && strings.HasPrefix(path, prefix) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Exclude matched route patterns for dynamic endpoints.
|
||||
if full := c.FullPath(); full != "" {
|
||||
if _, ok := excludedFullPaths[full]; ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback exclusions using raw path checks for robustness.
|
||||
// Note: Keep the prefix guard here (not just HasSuffix), as the frontend SPA
|
||||
// wildcard route may include paths ending in "/preview" (HTML) that should
|
||||
// remain compressible (e.g., "/library/.../preview").
|
||||
if path == clusterThemePath {
|
||||
return false
|
||||
}
|
||||
if strings.HasPrefix(path, photoDlPrefix) && strings.HasSuffix(path, "/dl") {
|
||||
return false
|
||||
}
|
||||
if strings.HasPrefix(path, sharePrefix) && strings.HasSuffix(path, "/preview") {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
158
internal/server/gzip_test.go
Normal file
158
internal/server/gzip_test.go
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
stdgzip "compress/gzip"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-contrib/gzip"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
)
|
||||
|
||||
func TestGzipMiddleware(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
// Enable gzip for this test router.
|
||||
conf := config.TestConfig()
|
||||
conf.Options().HttpCompression = "gzip"
|
||||
|
||||
r := gin.New()
|
||||
r.Use(gzip.Gzip(
|
||||
gzip.DefaultCompression,
|
||||
gzip.WithCustomShouldCompressFn(NewGzipShouldCompressFn(conf)),
|
||||
))
|
||||
|
||||
r.GET("/ok", func(c *gin.Context) {
|
||||
c.String(http.StatusOK, "hello world")
|
||||
})
|
||||
|
||||
excludedPath := conf.BaseUri(config.ApiUri + "/dl/test")
|
||||
r.GET(excludedPath, func(c *gin.Context) {
|
||||
c.String(http.StatusOK, "download")
|
||||
})
|
||||
|
||||
livezPath := conf.BaseUri("/livez")
|
||||
r.GET(livezPath, func(c *gin.Context) {
|
||||
c.String(http.StatusOK, "ok")
|
||||
})
|
||||
|
||||
healthzPath := conf.BaseUri("/healthz")
|
||||
r.GET(healthzPath, func(c *gin.Context) {
|
||||
c.String(http.StatusOK, "ok")
|
||||
})
|
||||
|
||||
readyzPath := conf.BaseUri("/readyz")
|
||||
r.GET(readyzPath, func(c *gin.Context) {
|
||||
c.String(http.StatusOK, "ok")
|
||||
})
|
||||
|
||||
imagePath := "/file.jpg"
|
||||
r.GET(imagePath, func(c *gin.Context) {
|
||||
c.String(http.StatusOK, "image")
|
||||
})
|
||||
|
||||
photoDlRoute := conf.BaseUri(config.ApiUri + "/photos/:uid/dl")
|
||||
r.GET(photoDlRoute, func(c *gin.Context) {
|
||||
c.String(http.StatusOK, "photo")
|
||||
})
|
||||
|
||||
clusterThemeRoute := conf.BaseUri(config.ApiUri + "/cluster/theme")
|
||||
r.GET(clusterThemeRoute, func(c *gin.Context) {
|
||||
c.String(http.StatusOK, "theme")
|
||||
})
|
||||
|
||||
sharePreviewRoute := conf.BaseUri("/s/:token/:shared/preview")
|
||||
r.GET(sharePreviewRoute, func(c *gin.Context) {
|
||||
c.String(http.StatusOK, "preview")
|
||||
})
|
||||
|
||||
doRequest := func(path string, acceptGzip bool) *httptest.ResponseRecorder {
|
||||
w := httptest.NewRecorder()
|
||||
req := httptest.NewRequest("GET", path, nil)
|
||||
if acceptGzip {
|
||||
req.Header.Set("Accept-Encoding", "gzip")
|
||||
}
|
||||
r.ServeHTTP(w, req)
|
||||
return w
|
||||
}
|
||||
|
||||
t.Run("CompressesSuccessfulResponse", func(t *testing.T) {
|
||||
w := doRequest("/ok", true)
|
||||
|
||||
require.Equal(t, http.StatusOK, w.Code)
|
||||
assert.Equal(t, "gzip", w.Header().Get("Content-Encoding"))
|
||||
|
||||
zr, err := stdgzip.NewReader(bytes.NewReader(w.Body.Bytes()))
|
||||
require.NoError(t, err)
|
||||
defer zr.Close()
|
||||
|
||||
b, err := io.ReadAll(zr)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "hello world", string(b))
|
||||
})
|
||||
t.Run("DoesNotCompressExcludedPrefixes", func(t *testing.T) {
|
||||
w := doRequest(excludedPath, true)
|
||||
|
||||
require.Equal(t, http.StatusOK, w.Code)
|
||||
assert.Empty(t, w.Header().Get("Content-Encoding"))
|
||||
assert.Equal(t, "download", w.Body.String())
|
||||
})
|
||||
t.Run("DoesNotCompressExcludedExtensions", func(t *testing.T) {
|
||||
w := doRequest(imagePath, true)
|
||||
|
||||
require.Equal(t, http.StatusOK, w.Code)
|
||||
assert.Empty(t, w.Header().Get("Content-Encoding"))
|
||||
assert.Equal(t, "image", w.Body.String())
|
||||
})
|
||||
t.Run("DoesNotCompressHealthEndpoints", func(t *testing.T) {
|
||||
for _, path := range []string{livezPath, healthzPath, readyzPath} {
|
||||
w := doRequest(path, true)
|
||||
|
||||
require.Equal(t, http.StatusOK, w.Code, path)
|
||||
assert.Empty(t, w.Header().Get("Content-Encoding"), path)
|
||||
assert.Equal(t, "ok", w.Body.String(), path)
|
||||
}
|
||||
})
|
||||
t.Run("DoesNotCompressPhotoOriginalDownload", func(t *testing.T) {
|
||||
w := doRequest(conf.BaseUri(config.ApiUri+"/photos/abc/dl"), true)
|
||||
|
||||
require.Equal(t, http.StatusOK, w.Code)
|
||||
assert.Empty(t, w.Header().Get("Content-Encoding"))
|
||||
assert.Equal(t, "photo", w.Body.String())
|
||||
})
|
||||
t.Run("DoesNotCompressClusterThemeDownload", func(t *testing.T) {
|
||||
w := doRequest(clusterThemeRoute, true)
|
||||
|
||||
require.Equal(t, http.StatusOK, w.Code)
|
||||
assert.Empty(t, w.Header().Get("Content-Encoding"))
|
||||
assert.Equal(t, "theme", w.Body.String())
|
||||
})
|
||||
t.Run("DoesNotCompressSharePreview", func(t *testing.T) {
|
||||
w := doRequest(conf.BaseUri("/s/tok/shared/preview"), true)
|
||||
|
||||
require.Equal(t, http.StatusOK, w.Code)
|
||||
assert.Empty(t, w.Header().Get("Content-Encoding"))
|
||||
assert.Equal(t, "preview", w.Body.String())
|
||||
})
|
||||
t.Run("DoesNotCompressWithoutAcceptEncoding", func(t *testing.T) {
|
||||
w := doRequest("/ok", false)
|
||||
|
||||
require.Equal(t, http.StatusOK, w.Code)
|
||||
assert.Empty(t, w.Header().Get("Content-Encoding"))
|
||||
assert.Equal(t, "hello world", w.Body.String())
|
||||
})
|
||||
t.Run("DoesNotCompressNotFound", func(t *testing.T) {
|
||||
w := doRequest("/missing", true)
|
||||
|
||||
require.Equal(t, http.StatusNotFound, w.Code)
|
||||
assert.Empty(t, w.Header().Get("Content-Encoding"))
|
||||
assert.Contains(t, w.Body.String(), "404")
|
||||
})
|
||||
}
|
||||
|
|
@ -78,21 +78,10 @@ func Start(ctx context.Context, conf *config.Config) {
|
|||
case "br", "brotli":
|
||||
log.Infof("server: brotli compression is currently not supported")
|
||||
case "gzip":
|
||||
// Use a custom compression predicate for fast, targeted exclusions.
|
||||
router.Use(gzip.Gzip(
|
||||
gzip.DefaultCompression,
|
||||
gzip.WithExcludedExtensions([]string{
|
||||
".png", ".gif", ".jpeg", ".jpg", ".webp", ".mp3", ".mp4", ".zip", ".gz",
|
||||
}),
|
||||
gzip.WithExcludedPaths([]string{
|
||||
conf.BaseUri("/health"),
|
||||
conf.BaseUri(config.ApiUri + "/t"),
|
||||
conf.BaseUri(config.ApiUri + "/folders/t"),
|
||||
conf.BaseUri(config.ApiUri + "/dl"),
|
||||
conf.BaseUri(config.ApiUri + "/zip"),
|
||||
conf.BaseUri(config.ApiUri + "/albums"),
|
||||
conf.BaseUri(config.ApiUri + "/labels"),
|
||||
conf.BaseUri(config.ApiUri + "/videos"),
|
||||
}),
|
||||
gzip.WithCustomShouldCompressFn(NewGzipShouldCompressFn(conf)),
|
||||
))
|
||||
log.Infof("server: enabled gzip compression")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ echo "Building PhotoPrismPi SD card image..."
|
|||
DESTDIR=$(realpath "${1:-./setup/nas/raspberry-pi}")
|
||||
|
||||
# Ubuntu Server version and download URL:
|
||||
UBUNTU_VERSION="${2:-24.04.2}"
|
||||
UBUNTU_VERSION="${2:-24.04.3}"
|
||||
UBUNTU_URL="https://cdimage.ubuntu.com/releases/${UBUNTU_VERSION}/release/ubuntu-${UBUNTU_VERSION}-preinstalled-server-arm64+raspi.img.xz"
|
||||
|
||||
# SD card image file name and path:
|
||||
|
|
|
|||
|
|
@ -101,10 +101,12 @@ services:
|
|||
PHOTOPRISM_DATABASE_PASSWORD: "insecure" # MariaDB database password, must be the same as MARIADB_PASSWORD
|
||||
## Run/install on first startup (https://docs.photoprism.app/getting-started/config-options/#docker-image):
|
||||
PHOTOPRISM_INIT: "https yt-dlp" # options: update https tensorflow tensorflow-gpu intel gpu davfs yt-dlp
|
||||
## Computer Vision API (https://docs.photoprism.app/getting-started/config-options/#computer-vision):
|
||||
## Computer Vision (https://docs.photoprism.app/getting-started/config-options/#computer-vision):
|
||||
PHOTOPRISM_VISION_API: "false" # server: enables service API endpoints under /api/v1/vision (requires access token)
|
||||
PHOTOPRISM_VISION_URI: "" # client: service URI, e.g. http://hostname/api/v1/vision (leave blank to disable)
|
||||
PHOTOPRISM_VISION_KEY: "" # client: service access token (for authentication)
|
||||
OLLAMA_BASE_URL: "http://ollama:11434" # use "https://ollama.com" for Ollama Cloud
|
||||
OLLAMA_API_KEY: "" # API key required to access Ollama (optional)
|
||||
## Video Transcoding (https://docs.photoprism.app/getting-started/advanced/transcoding/):
|
||||
# PHOTOPRISM_FFMPEG_ENCODER: "software" # H.264/AVC encoder (software, intel, nvidia, apple, raspberry, or vaapi)
|
||||
# PHOTOPRISM_FFMPEG_SIZE: "1920" # video size limit in pixels (720-7680) (default: 3840)
|
||||
|
|
|
|||
|
|
@ -91,10 +91,12 @@ services:
|
|||
PHOTOPRISM_DATABASE_PASSWORD: "insecure" # MariaDB database password, must be the same as MARIADB_PASSWORD
|
||||
## Run/install on first startup (https://docs.photoprism.app/getting-started/config-options/#docker-image):
|
||||
PHOTOPRISM_INIT: "https tensorflow" # options: update https tensorflow tensorflow-gpu intel gpu davfs yt-dlp
|
||||
## Computer Vision API (https://docs.photoprism.app/getting-started/config-options/#computer-vision):
|
||||
## Computer Vision (https://docs.photoprism.app/getting-started/config-options/#computer-vision):
|
||||
PHOTOPRISM_VISION_API: "false" # server: enables service API endpoints under /api/v1/vision (requires access token)
|
||||
PHOTOPRISM_VISION_URI: "" # client: service URI, e.g. http://hostname/api/v1/vision (leave blank to disable)
|
||||
PHOTOPRISM_VISION_KEY: "" # client: service access token (for authentication)
|
||||
OLLAMA_BASE_URL: "http://ollama:11434" # use "https://ollama.com" for Ollama Cloud
|
||||
OLLAMA_API_KEY: "" # API key required to access Ollama (optional)
|
||||
## Video Transcoding (https://docs.photoprism.app/getting-started/advanced/transcoding/):
|
||||
# PHOTOPRISM_FFMPEG_ENCODER: "software" # H.264/AVC encoder (software, intel, nvidia, apple, raspberry, or vaapi)
|
||||
# PHOTOPRISM_FFMPEG_SIZE: "1920" # video size limit in pixels (720-7680) (default: 3840)
|
||||
|
|
|
|||
|
|
@ -84,6 +84,12 @@ services:
|
|||
PHOTOPRISM_SITE_AUTHOR: "" # meta site author
|
||||
## Run/install on first startup, see https://github.com/photoprism/photoprism/blob/develop/scripts/dist/Makefile:
|
||||
PHOTOPRISM_INIT: "https tensorflow" # common options: update https tensorflow tensorflow-gpu intel gpu davfs yt-dlp
|
||||
## Computer Vision (https://docs.photoprism.app/getting-started/config-options/#computer-vision):
|
||||
PHOTOPRISM_VISION_API: "false" # server: enables service API endpoints under /api/v1/vision (requires access token)
|
||||
PHOTOPRISM_VISION_URI: "" # client: service URI, e.g. http://hostname/api/v1/vision (leave blank to disable)
|
||||
PHOTOPRISM_VISION_KEY: "" # client: service access token (for authentication)
|
||||
OLLAMA_BASE_URL: "http://ollama:11434" # use "https://ollama.com" for Ollama Cloud
|
||||
OLLAMA_API_KEY: "" # API key required to access Ollama (optional)
|
||||
## Video Transcoding (https://docs.photoprism.app/getting-started/advanced/transcoding/):
|
||||
# PHOTOPRISM_FFMPEG_ENCODER: "software" # H.264/AVC encoder (software, intel, nvidia, apple, raspberry, or vaapi)
|
||||
# PHOTOPRISM_FFMPEG_SIZE: "1920" # video size limit in pixels (720-7680) (default: 3840)
|
||||
|
|
|
|||
|
|
@ -95,10 +95,12 @@ services:
|
|||
PHOTOPRISM_DATABASE_PASSWORD: "insecure" # MariaDB database password, must be the same as MARIADB_PASSWORD
|
||||
## Run/install on first startup, see https://github.com/photoprism/photoprism/blob/develop/scripts/dist/Makefile:
|
||||
PHOTOPRISM_INIT: "https tensorflow-gpu yt-dlp" # common options: update https tensorflow tensorflow-gpu intel gpu davfs yt-dlp
|
||||
## Computer Vision API (https://docs.photoprism.app/getting-started/config-options/#computer-vision):
|
||||
## Computer Vision (https://docs.photoprism.app/getting-started/config-options/#computer-vision):
|
||||
PHOTOPRISM_VISION_API: "false" # server: enables service API endpoints under /api/v1/vision (requires access token)
|
||||
PHOTOPRISM_VISION_URI: "" # client: service URI, e.g. http://hostname/api/v1/vision (leave blank to disable)
|
||||
PHOTOPRISM_VISION_KEY: "" # client: service access token (for authentication)
|
||||
OLLAMA_BASE_URL: "http://ollama:11434" # use "https://ollama.com" for Ollama Cloud
|
||||
OLLAMA_API_KEY: "" # API key required to access Ollama (optional)
|
||||
## Video Transcoding (https://docs.photoprism.app/getting-started/advanced/transcoding/):
|
||||
PHOTOPRISM_FFMPEG_ENCODER: "nvidia" # H.264/AVC encoder (software, intel, nvidia, apple, raspberry, or vaapi)
|
||||
PHOTOPRISM_FFMPEG_SIZE: "1920" # video size limit in pixels (720-7680) (default: 3840)
|
||||
|
|
|
|||
|
|
@ -88,6 +88,12 @@ services:
|
|||
PHOTOPRISM_SITE_AUTHOR: "" # meta site author
|
||||
## Run/install on first startup, see https://github.com/photoprism/photoprism/blob/develop/scripts/dist/Makefile:
|
||||
PHOTOPRISM_INIT: "https tensorflow" # common options: update https tensorflow tensorflow-gpu intel gpu davfs yt-dlp
|
||||
## Computer Vision (https://docs.photoprism.app/getting-started/config-options/#computer-vision):
|
||||
PHOTOPRISM_VISION_API: "false" # server: enables service API endpoints under /api/v1/vision (requires access token)
|
||||
PHOTOPRISM_VISION_URI: "" # client: service URI, e.g. http://hostname/api/v1/vision (leave blank to disable)
|
||||
PHOTOPRISM_VISION_KEY: "" # client: service access token (for authentication)
|
||||
OLLAMA_BASE_URL: "http://ollama:11434" # use "https://ollama.com" for Ollama Cloud
|
||||
OLLAMA_API_KEY: "" # API key required to access Ollama (optional)
|
||||
## Video Transcoding (https://docs.photoprism.app/getting-started/advanced/transcoding/):
|
||||
# PHOTOPRISM_FFMPEG_ENCODER: "software" # H.264/AVC encoder (software, intel, nvidia, apple, raspberry, or vaapi)
|
||||
# PHOTOPRISM_FFMPEG_SIZE: "1920" # video size limit in pixels (720-7680) (default: 3840)
|
||||
|
|
|
|||
|
|
@ -81,6 +81,12 @@ services:
|
|||
PHOTOPRISM_SITE_AUTHOR: "" # meta site author
|
||||
## Run/install on first startup, see https://github.com/photoprism/photoprism/blob/develop/scripts/dist/Makefile:
|
||||
PHOTOPRISM_INIT: "https tensorflow" # common options: update https tensorflow tensorflow-gpu intel gpu davfs yt-dlp
|
||||
## Computer Vision (https://docs.photoprism.app/getting-started/config-options/#computer-vision):
|
||||
PHOTOPRISM_VISION_API: "false" # server: enables service API endpoints under /api/v1/vision (requires access token)
|
||||
PHOTOPRISM_VISION_URI: "" # client: service URI, e.g. http://hostname/api/v1/vision (leave blank to disable)
|
||||
PHOTOPRISM_VISION_KEY: "" # client: service access token (for authentication)
|
||||
OLLAMA_BASE_URL: "http://ollama:11434" # use "https://ollama.com" for Ollama Cloud
|
||||
OLLAMA_API_KEY: "" # API key required to access Ollama (optional)
|
||||
## Video Transcoding (https://docs.photoprism.app/getting-started/advanced/transcoding/):
|
||||
# PHOTOPRISM_FFMPEG_ENCODER: "software" # H.264/AVC encoder (software, intel, nvidia, apple, raspberry, or vaapi)
|
||||
# PHOTOPRISM_FFMPEG_SIZE: "1920" # video size limit in pixels (720-7680) (default: 3840)
|
||||
|
|
|
|||
|
|
@ -89,6 +89,12 @@ services:
|
|||
PHOTOPRISM_SITE_CAPTION: "AI-Powered Photos App"
|
||||
PHOTOPRISM_SITE_DESCRIPTION: "" # meta site description
|
||||
PHOTOPRISM_SITE_AUTHOR: "" # meta site author
|
||||
## Computer Vision (https://docs.photoprism.app/getting-started/config-options/#computer-vision):
|
||||
PHOTOPRISM_VISION_API: "false" # server: enables service API endpoints under /api/v1/vision (requires access token)
|
||||
PHOTOPRISM_VISION_URI: "" # client: service URI, e.g. http://hostname/api/v1/vision (leave blank to disable)
|
||||
PHOTOPRISM_VISION_KEY: "" # client: service access token (for authentication)
|
||||
OLLAMA_BASE_URL: "http://ollama:11434" # use "https://ollama.com" for Ollama Cloud
|
||||
OLLAMA_API_KEY: "" # API key required to access Ollama (optional)
|
||||
## Video Transcoding (https://docs.photoprism.app/getting-started/advanced/transcoding/):
|
||||
# PHOTOPRISM_FFMPEG_ENCODER: "software" # H.264/AVC encoder (software, intel, nvidia, apple, raspberry, or vaapi)
|
||||
# PHOTOPRISM_FFMPEG_SIZE: "1920" # video size limit in pixels (720-7680) (default: 3840)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue