Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
924d742
feat(thumbnails): add imagor-compatible push-based thumbnail endpoint
butonic Sep 8, 2026
81d0a86
refactor(webdav): drive the thumbnail workflow via the push-based gen…
butonic Sep 8, 2026
2037084
refactor(thumbnails): remove the pull-based pipeline
butonic Sep 8, 2026
95fcc4c
fix(webdav): snap each thumbnail axis onto a ladder of edge lengths
dschmidt Oct 16, 2026
4c0768c
fix(webdav): name the thumbnail config fields after what they configure
dschmidt Oct 16, 2026
0f734ac
fix(webdav): type the upload part and answer the small things
dschmidt Oct 16, 2026
de467d7
feat(webdav): write a thumbnail in the type the source maps to
dschmidt Oct 16, 2026
07d6829
fix(webdav): hand an embedded image to the generator as it was found
dschmidt Oct 16, 2026
32f4ee0
feat(webdav): let a deployment add the types a preview is made of
dschmidt Oct 16, 2026
9879927
refactor(webdav): a converter returns the image bytes and their type
dschmidt Oct 16, 2026
2267b6d
fix(thumbnails): answer like imagor, and write what imagor writes
dschmidt Oct 16, 2026
7000230
docs: describe the push endpoint and the preview settings as they are
dschmidt Oct 16, 2026
918c2ba
fix(webdav): refuse what it cannot serve and pass the generator through
dschmidt Oct 16, 2026
99c7666
fix(webdav): make the legacy a flag mean something again
dschmidt Oct 16, 2026
6c9dc28
refactor: one gif resize, a request struct, half the routes
dschmidt Oct 16, 2026
9b4e173
test(thumbnails): compare the built-in generator against two real ima…
dschmidt Oct 16, 2026
eb6d102
test(thumbnails): put the push endpoint on ginkgo
dschmidt Oct 16, 2026
f4792a0
fix(webdav): keep the acceptance contract for previews
dschmidt Oct 17, 2026
85b7ccc
docs: fix what the thumbnail readmes get wrong
dschmidt Oct 17, 2026
3572410
fix: answer the review, and read imagor's path the way imagor does
dschmidt Oct 17, 2026
18c5d72
refactor(thumbnails): let the backend answer for the image it decodes
dschmidt Oct 17, 2026
67856fd
fix: the small things the review left over
dschmidt Oct 17, 2026
c5c5ec2
refactor(webdav): own the list of types a preview is announced for
dschmidt Oct 17, 2026
1da9fa8
fix: answer the third review round
dschmidt Oct 17, 2026
e981a06
refactor(thumbnails): one table for the output formats, one fit-in
dschmidt Oct 17, 2026
4da8971
docs: say what the code does, in the places that still described some…
dschmidt Oct 17, 2026
a6fcc8d
docs: the thumbnail settings are only checked with previews enabled
dschmidt Oct 17, 2026
999ad6f
fix: answer the review from @orgaralf
dschmidt Oct 18, 2026
c980083
docs: say less in the comments, and name what matchOperation returns
dschmidt Oct 18, 2026
48f9990
fix(webdav): ask the generator for the smart crop
dschmidt Oct 18, 2026
ee4465f
fix(webdav): stop refusing a type mapping on a guess about the generator
dschmidt Oct 19, 2026
89e2f42
refactor(webdav): ask ForType for options, not for an any map
dschmidt Oct 19, 2026
fa73366
fix(webdav): read the generator's status before the writer's error
dschmidt Oct 20, 2026
3ff5a34
fix(thumbnails): decode one frame of a gif asked for as a still
dschmidt Oct 20, 2026
260d6f0
fix(webdav): a missing s3 bucket is not a cache miss
dschmidt Oct 20, 2026
56d96d2
fix(webdav): say it when the thumbnail cache fails to answer
dschmidt Oct 20, 2026
ab29d48
fix(webdav): keep the deprecated cache root pointing at its files dir…
dschmidt Oct 20, 2026
4f79fdf
fix(webdav): hash the checksum that becomes the cache key
dschmidt Oct 20, 2026
41b7160
fix(webdav): the source type belongs in the cache key
dschmidt Oct 20, 2026
e871ebb
fix(thumbnails): read the image part without the multipart form
dschmidt Oct 20, 2026
97e8031
fix(thumbnails): take a slot once the image is in, not before
dschmidt Oct 20, 2026
89b5bdf
fix(thumbnails): cap the box a request may ask for at 2048 per axis
dschmidt Oct 20, 2026
9b9b5ab
Revert "fix(thumbnails): cap the box a request may ask for at 2048 pe…
dschmidt Oct 20, 2026
4e85772
fix(webdav): stop the resolution ladder at 2048
dschmidt Oct 20, 2026
0070f03
fix(thumbnails): give the thumbnail size its own ceiling
dschmidt Oct 20, 2026
951917e
test(thumbnails): hand every limit to imagor in the parity suite
dschmidt Oct 20, 2026
575a179
fix(thumbnails): decode one frame of a png asked for as a still
dschmidt Oct 20, 2026
c6ac874
refactor: name the thumbnail settings after the service, THUMBNAILS
dschmidt Oct 20, 2026
a5dd814
refactor: name the thumbnail yaml keys like their variables
dschmidt Oct 20, 2026
c490926
refactor: name the thumbnail config fields like their yaml keys
dschmidt Oct 20, 2026
46e845c
ci: give the pipeline's imagor the same limits as the suite
dschmidt Oct 20, 2026
16aadb4
Merge remote-tracking branch 'origin/main' into imagor
dschmidt Oct 20, 2026
9c327dc
docs: let the comments say what the code does, not what it used to do
dschmidt Oct 20, 2026
9f3e51b
docs(webdav): say what an existing deployment has to change
dschmidt Oct 20, 2026
ef5e9c2
docs: name who decides what instead of what a thumbnail is
dschmidt Oct 20, 2026
6d7dd34
fix(thumbnails): apply an embedded color profile in the pure Go build
dschmidt Oct 21, 2026
f7e8817
refactor(webdav,graph,frontend): put the thumbnail settings in a thum…
dschmidt Oct 21, 2026
83a0fac
fix(ci): quote the imagor body limit so the service starts
dschmidt Oct 21, 2026
8a80d75
refactor(webdav): keep ignoring the legacy a flag
dschmidt Oct 21, 2026
afdaace
docs(webdav): name every thumbnail variable that stopped doing anything
dschmidt Oct 21, 2026
8e508e5
refactor: name the settings that extend a default list additional_*
dschmidt Oct 23, 2026
964b4c0
Bump reva
aduffeck Oct 23, 2026
94756cb
Merge remote-tracking branch 'upstream/main' into imagor
aduffeck Oct 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .codacy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ exclude_paths:
- 'tests/acceptance/expected-failures-*.md'
# written by the search engine parity suite, table rows are as wide as they are
- 'services/search/pkg/parity/README.md'
# thumbnail docs: wide tables and prose that is not hard-wrapped
- 'services/thumbnails/README.md'
- 'services/webdav/README.md'
- 'tests/acceptance/bootstrap/**'
- 'tests/acceptance/TestHelpers/**'
- 'tests/acceptance/scripts/run.sh'
Expand Down
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,8 @@
"STORE_DEBUG_ADDR": "127.0.0.1:10464",
"STORE_GRPC_ADDR": "127.0.0.1:10460",
"THUMBNAILS_DEBUG_ADDR": "127.0.0.1:10194",
"THUMBNAILS_GRPC_ADDR": "127.0.0.1:10191",
"THUMBNAILS_HTTP_ADDR": "127.0.0.1:10190",
"WEBDAV_THUMBNAILS_GENERATOR_URL": "http://127.0.0.1:10190",
"USERLOG_HTTP_ADDR": "127.0.0.1:10211",
"USERLOG_DEBUG_ADDR": "127.0.0.1:10210",
"USERS_DEBUG_ADDR": "127.0.0.1:10145",
Expand Down
71 changes: 69 additions & 2 deletions .woodpecker.star
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ CHKO_DOCKER_PUSHRM = "chko/docker-pushrm:1"
CODACY_COVERAGE_REPORTER = "codacy/codacy-coverage-reporter:14.1.3"
COLLABORA_CODE = "collabora/code:24.04.5.1.1"
OPEN_SEARCH = "opensearchproject/opensearch:2"

# imagor for the thumbnail parity suite, keep equal to imagortest.Variants
IMAGOR = "shumc/imagor:1.9.11"
IMAGOR_MAGICK = "ghcr.io/cshum/imagor-magick:1.9.11"
INBUCKET_INBUCKET = "inbucket/inbucket"
MINIO_MC = "quay.io/opencloudeu/minio-mc-ci:1.0"
OC_CI_ALPINE = "owncloudci/alpine:latest"
Expand Down Expand Up @@ -786,18 +790,34 @@ def testOpencloud(ctx):
],
"environment": CI_HTTP_PROXY_ENV,
},
] + waitForOpenSearch() + [
] + waitForOpenSearch() + waitForImagor() + [
{
"name": "test",
"image": OC_CI_GOLANG,
"environment": {
"SEARCH_ENGINE_OPEN_SEARCH_CLIENT_ADDRESSES": "http://open-search:9200",
"THUMBNAILS_PARITY_IMAGOR_URL": "http://imagor:8000",
"THUMBNAILS_PARITY_IMAGOR_MAGICK_URL": "http://imagor-magick:8000",
},
"commands": [
"make test",
"mv coverage.out unit.coverage.out",
],
},
{
# the only run that covers the resize code the official docker
# images ship
# FIXME: run it with the libvips those images ship, see issue 3746
"name": "test-thumbnail-parity-vips",
"image": OC_CI_GOLANG,
"environment": {
"THUMBNAILS_PARITY_IMAGOR_URL": "http://imagor:8000",
"THUMBNAILS_PARITY_IMAGOR_MAGICK_URL": "http://imagor-magick:8000",
},
"commands": [
"go test -tags enable_vips ./services/thumbnails/pkg/parity/...",
],
},
{
"name": "codacy-coverage",
"image": CODACY_COVERAGE_REPORTER,
Expand All @@ -818,7 +838,7 @@ def testOpencloud(ctx):
pipeline = {
"name": "test-lint-unit",
"steps": steps,
"services": openSearchService(),
"services": openSearchService() + imagorServices(),
"when": [
event["base"],
event["cron"],
Expand Down Expand Up @@ -3424,6 +3444,53 @@ def waitForOpenSearch():
],
}]

def imagorServices():
environment = {
# the parity suite posts every image, and there is nothing to sign with
"IMAGOR_UNSAFE": True,
"UPLOAD_LOADER_ENABLE": True,
"HTTP_LOADER_DISABLE": True,
# the limits of the thumbnails service, so both sides refuse the same
# requests; keep in step with imagortest.Args, which configures imagor
# the same way when the suite starts it through testcontainers
"VIPS_MAX_WIDTH": 7680,
"VIPS_MAX_HEIGHT": 7680,
"VIPS_MAX_ANIMATION_FRAMES": 100,
# quoted: a number this large reaches the container as 5e+07, which imagor cannot parse
"UPLOAD_LOADER_MAX_ALLOWED_SIZE": "50000000",
"IMAGOR_PROCESS_CONCURRENCY": -1,
}

return [
{
"name": "imagor",
"image": IMAGOR,
"environment": environment,
},
{
"name": "imagor-magick",
"image": IMAGOR_MAGICK,
"environment": environment,
},
]

def waitForImagor():
return [{
"name": "wait-for-imagor",
"image": OC_CI_ALPINE,
"commands": [
"bash -c '" +
"for host in imagor imagor-magick; do\n" +
" until curl -sS -o /dev/null \"http://$host:8000/\"; do\n" +
" echo \"Waiting for http://$host:8000 to be healthy...\"\n" +
" sleep 2\n" +
" done\n" +
" echo \"http://$host:8000 healthy...\"\n" +
"done\n" +
"'",
],
}]

def logRequests():
return [{
"name": "api-test-failure-logs",
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ require (
github.com/leonelquinteros/gotext v1.7.3-0.20260422134830-b012b4ccae69
github.com/libregraph/idm v0.5.0
github.com/libregraph/lico v0.67.0
github.com/minio/minio-go/v7 v7.2.1
github.com/mna/pigeon v1.3.0
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
github.com/nats-io/nats-server/v2 v2.15.0
Expand All @@ -67,7 +68,7 @@ require (
github.com/open-policy-agent/opa v1.21.1
github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89
github.com/opencloud-eu/libre-graph-api-go v1.0.9-0.20261016073229-17ed3d5dd346
github.com/opencloud-eu/reva/v2 v2.51.1-0.20261021052306-7983cec1cd3a
github.com/opencloud-eu/reva/v2 v2.51.1-0.20261023103014-40a03d16bc92
github.com/opensearch-project/opensearch-go/v4 v4.7.3
github.com/orcaman/concurrent-map v1.0.0
github.com/pkg/errors v0.9.1
Expand Down Expand Up @@ -282,7 +283,6 @@ require (
github.com/minio/crc64nvme v1.1.1 // indirect
github.com/minio/highwayhash v1.0.4 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/minio/minio-go/v7 v7.2.1 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -761,8 +761,8 @@ github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89 h1:W1ms+l
github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89/go.mod h1:vigJkNss1N2QEceCuNw/ullDehncuJNFB6mEnzfq9UI=
github.com/opencloud-eu/libre-graph-api-go v1.0.9-0.20261016073229-17ed3d5dd346 h1:3ptbiFhSK4fzY/rC3idcYUveiwepMoOov1sBpc7Rg3k=
github.com/opencloud-eu/libre-graph-api-go v1.0.9-0.20261016073229-17ed3d5dd346/go.mod h1:lTM8JeGblNpoMySTW7Lui2+c5TTLI95mwxtdUIHHrhU=
github.com/opencloud-eu/reva/v2 v2.51.1-0.20261021052306-7983cec1cd3a h1:gChg4oBA5YYcpFrjKrlDXPjdOL5E6HknMSn8yj3vftc=
github.com/opencloud-eu/reva/v2 v2.51.1-0.20261021052306-7983cec1cd3a/go.mod h1:rAwuI3PX8n3RxBXHfpMtAKqfwxTIKhZol1Bi3Qj39p8=
github.com/opencloud-eu/reva/v2 v2.51.1-0.20261023103014-40a03d16bc92 h1:vGNerJ4b6uAmD8vBK9z1VYOYei+Xxs+K46GIFkQyZxM=
github.com/opencloud-eu/reva/v2 v2.51.1-0.20261023103014-40a03d16bc92/go.mod h1:rAwuI3PX8n3RxBXHfpMtAKqfwxTIKhZol1Bi3Qj39p8=
github.com/opencloud-eu/secure v0.0.0-20260312082735-b6f5cb2244e4 h1:l2oB/RctH+t8r7QBj5p8thfEHCM/jF35aAY3WQ3hADI=
github.com/opencloud-eu/secure v0.0.0-20260312082735-b6f5cb2244e4/go.mod h1:BmF5hyM6tXczk3MpQkFf1hpKSRqCyhqcbiQtiAF7+40=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
Expand Down
15 changes: 1 addition & 14 deletions opencloud/pkg/init/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func CreateConfig(insecure, forceOverwrite, diff bool, configPath, adminPassword
systemUserID, adminUserID, graphApplicationID, storageUsersMountID, serviceAccountID string
idmServicePassword, idpServicePassword, ocAdminServicePassword, revaServicePassword string
tokenManagerJwtSecret, collaborationWOPISecret, machineAuthAPIKey, systemUserAPIKey string
revaTransferSecret, thumbnailsTransferSecret, serviceAccountSecret, urlSigningSecret string
revaTransferSecret, serviceAccountSecret, urlSigningSecret string
adminPasswdwordGenerated bool
)

Expand All @@ -94,7 +94,6 @@ func CreateConfig(insecure, forceOverwrite, diff bool, configPath, adminPassword
machineAuthAPIKey = oldCfg.MachineAuthAPIKey
systemUserAPIKey = oldCfg.SystemUserAPIKey
revaTransferSecret = oldCfg.TransferSecret
thumbnailsTransferSecret = oldCfg.Thumbnails.Thumbnail.TransferSecret
serviceAccountSecret = oldCfg.Graph.ServiceAccount.ServiceAccountSecret
urlSigningSecret = oldCfg.URLSigningSecret
if urlSigningSecret == "" {
Expand Down Expand Up @@ -155,10 +154,6 @@ func CreateConfig(insecure, forceOverwrite, diff bool, configPath, adminPassword
if err != nil {
return fmt.Errorf("could not generate random secret for urlSigningSecret: %s", err)
}
thumbnailsTransferSecret, err = generators.GenerateRandomPassword(passwordLength)
if err != nil {
return fmt.Errorf("could not generate random password for thumbnailsTransferSecret: %s", err)
}
serviceAccountSecret, err = generators.GenerateRandomPassword(passwordLength)
if err != nil {
return fmt.Errorf("could not generate random secret for serviceAccountSecret: %s", err)
Expand Down Expand Up @@ -233,11 +228,6 @@ func CreateConfig(insecure, forceOverwrite, diff bool, configPath, adminPassword
},
ServiceAccount: serviceAccount,
},
Thumbnails: ThumbnailService{
Thumbnail: ThumbnailSettings{
TransferSecret: thumbnailsTransferSecret,
},
},
Gateway: Gateway{
StorageRegistry: StorageRegistry{
StorageUsersMountID: storageUsersMountID,
Expand Down Expand Up @@ -305,9 +295,6 @@ func CreateConfig(insecure, forceOverwrite, diff bool, configPath, adminPassword
},
ServiceAccount: serviceAccount,
}

cfg.Thumbnails.Thumbnail.WebdavAllowInsecure = true
cfg.Thumbnails.Thumbnail.Cs3AllowInsecure = true
}
yamlOutput, err := yaml.Marshal(cfg)
if err != nil {
Expand Down
13 changes: 0 additions & 13 deletions opencloud/pkg/init/structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ type OpenCloudConfig struct {
Groups UsersAndGroupsService `yaml:"groups"`
AuthGuest AuthGuest `yaml:"auth_guest"`
Ocm OcmService `yaml:"ocm"`
Thumbnails ThumbnailService `yaml:"thumbnails"`
Search Search `yaml:"search"`
Audit Audit `yaml:"audit"`
Settings SettingsService `yaml:"settings"`
Expand Down Expand Up @@ -226,18 +225,6 @@ type StorageUsers struct {
ServiceAccount ServiceAccount `yaml:"service_account"`
}

// ThumbnailSettings is the configuration for the thumbnail settings
type ThumbnailSettings struct {
TransferSecret string `yaml:"transfer_secret"`
WebdavAllowInsecure bool `yaml:"webdav_allow_insecure"`
Cs3AllowInsecure bool `yaml:"cs3_allow_insecure"`
}

// ThumbnailService is the configuration for the thumbnail service
type ThumbnailService struct {
Thumbnail ThumbnailSettings
}

// TokenManager is the configuration for the token manager
type TokenManager struct {
JWTSecret string `yaml:"jwt_secret"`
Expand Down
3 changes: 0 additions & 3 deletions protogen/.mockery.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,3 @@ packages:
github.com/opencloud-eu/opencloud/protogen/gen/opencloud/services/settings/v0:
interfaces:
ValueService: {}
github.com/opencloud-eu/opencloud/protogen/gen/opencloud/services/thumbnails/v0:
interfaces:
ThumbnailService: {}
Loading
Loading