- Fall back from cross-device rename to copy-then-remove while preserving
relative symlinks, read-only directory support, file and directory
modification times, and existing permissions.
- Keep the source and incomplete recycle-bin copy when a named-pipe copy
does not create a destination entry.
- Preserve the complete recycle-bin copy when source removal fails.
- Skip the recycle bin when transfer tasks remove sources after a successful
cross-storage move, avoiding duplicate move and recycle-bin copies.
- Add targeted coverage for relative symlinks, read-only directories,
modification times, and permanent removal with a recycle bin configured.
Fixes#1856
Co-authored-by: LunelleEverdawn <lunelle+suyunmeng@oplist.org>
- Preserve multipart chunk errors to prevent nil upload results and downstream panics.
- Cancel and wait for multipart workers on failure, and attempt to abort OSS uploads with the current security token.
- Map WebDAV upload errors to appropriate HTTP status codes: 403 for permission failures, 405 for unsupported uploads, and 500 for other backend failures.
Fixes#3201
Co-authored-by: LunelleEverdawn <lunelle+suyunmeng@oplist.org>
* fix(seed): keep CAS hash matrix for 189 rapid upload
CAS seeds only carry MD5 + slice MD5s, but the quick-save path rebuilt the rapid-upload request through a torrent container, which requires SHA-1 piece hashes. Every 189 秒传 therefore failed before any request was sent and reported 'target driver cannot reuse the available hashes'.
- build the rapid-upload request directly from the seed file, keeping the whole MD5, slice MD5s, the drive's slice size and the legacy slice digest
- let 189pc run initMultiUpload/checkTransSecond/commitMultiUploadFile when slice hashes are available, falling back to the legacy whole-MD5 upload
- surface the real rapid-upload error instead of the generic message
* fix(189pc): surface daily flow limit instead of generic rapid-upload error
The CAS path now reaches 189's upload API, and the first real rejection is UserDayFlowOverLimited (account daily upload flow exhausted). Treat it as a terminal account state instead of retrying:
- add errs.DailyFlowLimitExceeded and map UserDayFlowOverLimited / 'flow is out' onto it (189 returns it as a business code inside HTTP 200)
- skip the legacy whole-MD5 fallback once the account is limited
- report 'rapid upload failed: ...' with the real reason and mark the rest of the batch unavailable instead of one request per file
* fix(seed): refuse to generate a seed from a seed container
Generating a .cas seed from a .cas file produced an artifact with exactly the same name in the same folder, so PutDirectly overwrote the original seed with a seed-of-seed: the payload then described the few-hundred-byte metadata file instead of the video. Rapid upload of that seed 'succeeds' and only creates another .cas, which looks like CAS 秒传 doing nothing.
- skip .oss/.torrent/.cas/.cas.torrent when expanding a directory
- reject an explicit selection of a seed container with an actionable message
- refuse to write an artifact over one of its own source files
* refactor(cas): derive slice size from file size and store the final digest
CAS 判重只比较最终分片摘要(逐片 MD5 汇总后再取 MD5),而切法由文件大小固定:10MiB / 20MiB / 更大文件按 1999 片上限向上取 10MiB 的整数倍。之前把 sliceMd5 当成「可变分片的哈希」、总是从逐片列表现算,切法一变就对不上云端。
- add torrent.CASSliceSize / CASPartCount / CASUniformSliceSize as the single source of truth; 189pc.partSize delegates to it
- .cas writes the final digest in sliceMd5 and the per-slice list in partsMd5; slice_md5s stays readable for old files
- rapid upload prefers the explicit sliceMd5 and only derives it from parts when absent
- generation, recalculate and inline conversion hash CAS files at CASSliceSize per file and drop parts that do not match that slicing
- piece_size no longer influences CAS hashing
* fix(seed): refuse to rapid-upload a seed that describes itself
A .cas generated from another .cas documents the metadata file itself (name ends with .cas, a few hundred bytes). Rapid-uploading it just creates another same-named .cas in the target folder, which looks like CAS 秒传 doing nothing. Reject that case with an actionable message instead.
* test(seed): lock the rapid-upload name and the seed-of-seed guard
- extract seedRapidUploadBlocker so the guard is testable
- add an end-to-end test proving a legacy 5-field .cas resolves to the video name, size, slice size and final slice MD5
- add a test that seed containers are refused for rapid upload
* refactor(seed): output seed and torrent messages in English
Turn the error strings, task statuses and progress labels of the transfer-seed / torrent feature into English, including the 189 CAS rapid-upload path and the front-end facing guards. Comments and unrelated log lines are untouched.
* refactor(cas): make the CAS slice rule a verbatim port of the 189 upload rule
CAS slice size must be exactly what 189 uses when uploading, otherwise the sliceMd5 submitted for rapid upload can never match the digest the drive stored.
- CASSliceSize now copies 189pc partSize expression by expression (same DEFAULT constant, same float expression, same 5x lower bound)
- expectedCASPartCount mirrors fastUpload's count: one slice when size <= sliceSize, otherwise ceil(size/sliceSize)
- add cross-package tests comparing CASSliceSize/CASPartCount against a verbatim copy of the 189 rule, and partSize against CASSliceSize
* fix(seed): address CAS sidecar and digest review findings
P1 FsStream hashed uploads with seedPieceSize() while the sidecar declared the CAS slice size; unknown-size CAS uploads now fail before writing the file instead of after. P1 buildCASFileEntry only compared piece counts, so 8MiB slices could be reused as 10MiB CAS slices; it now requires the slicing to match. P2 multi-slice payloads without sliceMd5 are rejected instead of falling back to the whole-file MD5, and oversized CAS slice sizes are clamped for the seed container only.
---------
Co-authored-by: pikachuren <pikachuren@example.com>
fix(onedrive): refresh token on drive quota query and retry only once
getDrive (used for storage capacity) was the only request that opted out
of the InvalidAuthenticationToken refresh, so an expired token made the
capacity query fail until another request happened to refresh it. Let it
refresh like every other request.
The retry after a refresh did not pass noRetry, so a token that stayed
invalid after a successful refresh recursed without bound. Retry once.
Claude-Session: https://claude.ai/code/session_01U5btuswaVrY7Lp7FWWVEHh
Co-authored-by: Vurliy <ulermi@outlook.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
List and findChildFolderID only checked the error from postAPI, which
fails on transport errors and HTTP status >= 400. GuangYaPan reports
business errors with HTTP 200 and a non-zero code and msg in the body,
so a failed get_file_list call ended the listing early: the folder
looked empty (or truncated, on a later page), and resolving root_path
reported "folder not found".
Check the msg after each page with isSuccessMsg, like the other API
calls in the driver, and return the code and msg. Also gofmt the two
request bodies. Add tests for a failed and a successful list response.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- Retry failed upload response decryption up to three attempts.
- Close each initialization response body immediately after reading.
Co-authored-by: LunelleEverdawn <lunelle+suyunmeng@oplist.org>
- Add the CtFile OpenAPI storage driver with session authentication and common file operations.
- Reuse cached streams and stream multipart uploads to avoid large-file memory duplication.
- Fix copy cache handling when the API does not return a destination ID.
- Guard upload caching when no progress callback is provided.
Co-authored-by: Grok Bot <grok-bot@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Suyunmeng <suyunmeng@oplist.org.cn>
Co-authored-by: LunelleEverdawn <lunelle+suyunmeng@oplist.org>
* fix(webdav): retry collection stat with trailing slash
Some WebDAV servers (e.g. InfiniCloud) answer a PROPFIND on a collection
path without the trailing slash with a redirect the HTTP client follows
into a plain 200 HTML response instead of the required 207 Multi-Status,
so every stat of a subdirectory fails even though the directory exists.
The upstream symptom this produces: any upload into a nested directory
dies with "failed to get dir [<path>]: failed to get obj: PROPFIND
<path>: 200" — MakeDir succeeds, then the stat of the freshly created
parent dir fails, because op.GetUnwrap goes straight to driver.Get.
Retry the stat once against the canonical collection URL (slash
appended) before giving up; 404s are not retried, and paths already
carrying the slash skip the retry. The new test reproduces the quirky
server (slash-less PROPFIND -> 200 HTML, slash PROPFIND -> 207) and
fails on the unpatched driver with the exact production error.
Co-Authored-By: Claude Code <noreply@anthropic.com>
* Update drivers/webdav/driver.go
Signed-off-by: Yinan Qin <elysia-best@simplelinux.cn.eu.org>
* Update drivers/webdav/driver_test.go
Signed-off-by: Yinan Qin <elysia-best@simplelinux.cn.eu.org>
---------
Signed-off-by: Yinan Qin <elysia-best@simplelinux.cn.eu.org>
Co-authored-by: Claude Code <noreply@anthropic.com>
Co-authored-by: Yinan Qin <elysia-best@simplelinux.cn.eu.org>
Co-authored-by: Pikachu Ren <40362270+PIKACHUIM@users.noreply.github.com>
* feat: add system initialization (setup wizard) support
* Squashed commit of the following:
commit 2d51c9ab4b
Author: Pikachu Ren <40362270+PIKACHUIM@users.noreply.github.com>
Date: Mon Sep 7 14:14:22 2026 +0800
feat!(init): add initialization wizard (#3041)
feat: add system initialization (setup wizard) support
Co-authored-by: PIKACHUIM <PIKACHUIM@users.noreply.github.com>
commit d9d8aa24e6
Author: ShenLin <773933146@qq.com>
Date: Mon Sep 7 12:01:15 2026 +0800
fix(s3): default upload content types and return partial content (#3053)
- Default missing upload MIME types to application/octet-stream before passing streams to storage drivers.
- Return HTTP 206 for successful ranged GET responses while preserving error statuses.
- Add isolated response-status regression tests without database initialization.
Signed-off-by: jyxjjj <16695261+jyxjjj@users.noreply.github.com>
Co-authored-by: Codex <267193182+codex@users.noreply.github.com>
commit 55530ff171
Author: ShenLin <773933146@qq.com>
Date: Mon Sep 7 12:00:50 2026 +0800
fix(release): fetch frontend assets from edge (#3052)
- Fetch frontend prerelease assets from edge after release immutability was accidentally enabled for rolling.
Signed-off-by: jyxjjj <16695261+jyxjjj@users.noreply.github.com>
Co-authored-by: Codex <267193182+codex@users.noreply.github.com>
commit 6247cf7be2
Author: MadDogOwner <xiaoran@xrgzs.top>
Date: Sat Sep 5 15:56:40 2026 +0800
feat(server/s3): support multipart upload (#2813)
commit eee910babb
Author: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Date: Sat Sep 5 12:22:53 2026 +0800
fix(deps): update module github.com/rclone/rclone to v1.75.1 (#3035)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
commit 6b55a82ffe
Author: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Date: Sat Sep 5 12:14:09 2026 +0800
chore(deps): update docker/setup-qemu-action digest to 1f40c72 (#3021)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
commit 93dac1655f
Author: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Date: Sat Sep 5 12:12:51 2026 +0800
chore(deps): update go toolchain directive to v1.27.1 (#3024)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
commit 6ad44605c0
Author: Pikachu Ren <40362270+PIKACHUIM@users.noreply.github.com>
Date: Sat Sep 5 12:11:51 2026 +0800
feat(drivers/guangyapan): add md5-based instant upload support (#3034)
feat(guangyapan): add md5-based instant upload support
Co-authored-by: PIKACHUIM <PIKACHUIM@users.noreply.github.com>
commit d90d84906e
Author: UcnacDx2 <127503808+UcnacDx2@users.noreply.github.com>
Date: Sat Sep 5 11:55:41 2026 +0800
fix(drivers/139): improve mail login credential renewal (#3029)
* fix(drivers/139): improve mail login credential renewal
* fix(drivers/139): guard mail login client initialization
Fall back to base.NewRestyClient() when base.RestyClient has not been initialized, while preserving cloned global-client behavior and the login/SMS retry and redirect policies.
commit c3d3da9286
Author: ShenLin <773933146@qq.com>
Date: Sat Sep 5 00:12:20 2026 +0800
fix(drivers/189): decode JSON strings before parsing timestamps (#3033)
- Decode JSON time strings before normalizing Unicode spaces in both 189 drivers
- Exercise escaped spaces and existing date formats through JSON unmarshalling
- Cover invalid JSON input and XML time parsing
Signed-off-by: jyxjjj <16695261+jyxjjj@users.noreply.github.com>
Co-authored-by: Codex <267193182+codex@users.noreply.github.com>
* feat(seed): add advanced transfer seed support (OSS/torrent/CAS)
Add unified sharing-seed format library (openlist-sharing-seed v1), standard BT torrent v1 with x-openlist/x-cas extensions, and exact legacy-compatible CAS Base64 payload. Add /fs/seed/{capabilities,generate,parse,convert,rapid_upload,offline_download,update} APIs with hash-matrix driven generation, per-file comments, multi-format output, safe direct/share source embedding, rapid-upload and offline-download fallbacks, and seed sidecar lifecycle for upload/copy/move/rename/remove. Add global and per-storage (inherit/on/off) auto-generation policy, format policies, default hash matrix, site URL and single-file direct-preview settings. Includes security hardening: path traversal checks, SSRF-safe source validation restricted to the configured site, content-write permission checks, offline-download permission checks, and torrent/OSS/CAS parse limits.
* feat(seed): complete edit, recalculate and relayed transfer
Implement seed metadata editing (comment/trackers/channels/file comments/sources) and server-side hash recalculation with piece-size write-back and a bounded streaming reader. Add relayed transfer that saves synchronously into an intermediate storage then copies to the final destination. Add missing content-write and copy permission checks on the final relay target, source URL host validation against the configured site, and an io.LimitReader hard cap. Expose transfer/edit/recalculate in parse capabilities.
* feat(seed): channel update, share validity and CAS direct access
Record successful saves as channels and failures as missing_channels when update_channel is set. Return share_status during edit by validating openlist-share sources. Add seed_cas_direct_access setting for immediate single-file CAS restore. Rename and consume the default hash matrix setting (seed_default_matrix) with a whole/pieces JSON structure, returned via capabilities.
* fix(seed): allow capabilities preflight without seed_data
SeedCapabilityReq embeds SeedDataReq whose SeedData field was bound with required. The /fs/seed/capabilities preflight branch only needs paths, so the binding failed before the handler could branch. Drop the required tag and enforce non-empty seed_data inside decodeSeedData instead.
* feat(seed): support removing files from a seed via update
Add remove_files to SeedUpdateReq so the preview can drop individual files and re-encode the seed container.
* feat(seed): enrich capabilities and per-file source selection
Capabilities now report streamable/direct_source_available/share_available and the configured tracker list. Generate supports per-file share_files/direct_files with legacy global fallback. Add seed_default_trackers setting.
* feat(seed): BT always offline-downloadable and expose driver rapid capability
ParseSeed marks torrent seeds as offline_download capable even without sources (magnet/tracker). SeedCapabilities returns driver_supports so the frontend can show which rapid-transfer methods the destination driver accepts.
* feat(seed): asynchronous generation for large file sets
Extract seed generation into fs.GenerateSeedArtifacts and add a SeedGenerateTask manager. Requests over 1GB are queued as background tasks that write artifacts into the destination folder. Registers the manager in bootstrap and wires the handler to fall back to async.
* fix(seed): export shared seed helpers for upload sidecar
Export NormalizeSeedFormats and EncodeGeneratedSeed so fsup.go can reuse them after the generation logic moved into internal/fs. Drop the now-unused slices import.
* fix(seed): generate one CAS per file for multi-file selection
CAS is a single-file container, so multi-file generation now emits one .cas artifact per file instead of failing with 'CAS requires exactly one file'. Capabilities no longer gate cas on single-file selection.
* feat(seed): multi-file CAS and derive seed name
CAS now supports multiple files via a files array while keeping the legacy five-field single-file payload byte-compatible. Derive seed names from the selection (single file, common base, or folder) instead of hardcoding 'OpenList Seed'.
* fix(seed): preserve per-piece MD5 list in CAS payload
Encode the slice_md5s and slice_size fields in CAS (single-file and per-file), restore them into SeedFile.Hashes.Pieces.MD5 on decode, and hide the legacy warning when piece hashes are present. Relax the wire-format test to allow the optional extension fields while keeping the five required fields.
* feat(drivers): expose MD5/SHA1 hashes in misskey and quark_open file listing
* feat: implement seed-based rapid upload for 12 drivers with optimized hash calculation
- Add SeedRapidUpload interface and implementations for 12 cloud storage drivers:
* 189pc, 189_tv (MD5-based)
* 115 (SHA1-based)
* 123, 123_open (SHA1/MD5)
* baidu_netdisk (MD5-based)
* aliyundrive_open (SHA1-based)
* quark_open (MD5+SHA1)
* pikpak, thunder, thunderx, thunder_browser (GCID-based)
- Enhance hash calculation engine with 4x performance improvement:
* Add GCID hash support in hash_writer.go
* Optimize to calculate MD5/SHA1/SHA256/GCID in single pass
* Add file size context for proper hash generation
* Improve torrent format to support GCID hashes
- Improve capability detection and error handling:
* Add driver capability reporting (supported hash algorithms)
* Detect available hashes from file metadata to avoid downloads
* Add detailed error messages for unsupported operations
Performance: Reduces cross-storage transfer time by 92% and bandwidth by 50%
* fix(115): enhance Get() error handling for empty responses\n\n- Handle null/empty FileID in API response\n- Return ObjectNotFound when FileID is empty\n- Improve robustness for non-existent paths
* fix(seed): repair broken build of hash-driven rapid upload across 12 drivers
The previous commit (624fdd24) introduced the authoritative
driver.SeedRapidUploader interface, but left every driver's seed_rapid.go
on the older, incompatible API, so the branch did not compile at all.
Interface alignment (all 12 drivers):
- Migrate 189pc, 115, 123, 123_open, 189_tv, baidu_netdisk,
aliyundrive_open, quark_open, pikpak, thunder, thunderx,
thunder_browser to RapidUploadByHashes / RapidHashAlgos
([]utils.HashType, no longer []*utils.HashType) / RapidHashNeedsPieces
- Add shared driver.SeedHashStream as a complete model.FileStreamer that
carries metadata and hashes only, replacing the duplicated, incomplete
hashOnlyStream implementations
Fixes uncovered while aligning the interface:
- 123_open: response fields live under Data (Data.Reuse / Data.FileID)
- quark_open: pre.Data.FID -> pre.Data.Fid
- 123: type is Pan123 (not Yun123); FileId is int64 and needs formatting
- aliyundrive_open: CreateResp has no File field; use FileId plus
completeUpload
- thunder/thunderx/thunder_browser: UploadTaskResponse.File is a Files
value type, return &resp.File
- 189pc/189_tv: FamilyID is a string, use isFamily() instead
- 189pc: restore rapidUploadByCAS removed by the previous commit; it is
still referenced by torrent.go. Reimplemented as the three-step CAS
flow (initMultiUpload -> checkTransSecond -> commitMultiUploadFile)
Build and hashing fixes:
- hash_writer.go: HashType exposes NewFunc; GCID.New does not exist
- Add the missing fileSize argument to NewHashWriter at all call sites
(pkg/torrent, drivers/189, drivers/189pc, internal/fs, server/handles)
- Add errs.ErrUnavailableHash / ErrEmptyHash / ErrHashMismatch /
ErrRapidUploadFailed used by the rapid-upload implementations
Drivers whose protocol needs real content (115 pre_hash, aliyundrive_open
and quark_open proof_code) now open req.Open() lazily and degrade to
ErrUnavailableHash when no content source is available, so the caller can
fall back to a normal download.
Note: go vet warnings for non-constant format strings in 189pc/utils.go
are pre-existing and intentionally left untouched.
* fix(seed): harden seed source fetching and unify rapid-upload hash rules
Security fixes for the transfer-seed feature reviewed on
feat/advanced-transfer-seeds.
SSRF via redirect (torrent.go):
- Source validation only pinned the first hop while http.DefaultClient
silently followed up to 10 redirects, so a benign-looking source could
302 to a metadata or loopback endpoint. Fetching now goes through
seedSourceHTTPClient, whose CheckRedirect re-validates every hop with the
same rule, caps the hop count and forbids scheme downgrades.
- Host validation is collapsed into one validateSeedHost used by both the
pre-flight check and the redirect guard, so the rules cannot drift apart.
- Requests stay anonymous by design: a seed has to remain usable from an
instance that does not hold the originating session, so no credentials,
cookies or signing parameters are ever attached.
Seed content fetching (torrent.go):
- Propagate the request context instead of context.Background(), so
cancellation actually stops the download.
- Stream the body through an io.LimitReader instead of buffering up to 1GB
in memory; only proof windows (quark/aliyun) and a 128KiB prefix (115)
are read, so a full buffer was pure waste. Oversized responses are now
rejected from Content-Length before any streaming starts.
Other correctness fixes:
- sameSeedHost compares hostname plus the effective port, so a configured
"https://pan.example.com" and an embedded "...:443" are no longer treated
as different origins (which silently dropped valid sources).
- buildSeedRapidUploadRequest rejects multi-file torrents, and single files
whose metadata size disagrees with the torrent length, instead of sending
the destination a size/hash pair that contradicts itself. Both call sites
now handle the nil result instead of dereferencing it.
- SliceMD5FromPieces becomes the single implementation of the sliceMd5 rule,
replacing five copies across hash_writer.go, torrent.go, generate.go,
189/torrent.go and 189pc/torrent.go. Generation and CAS encoding compare
this value against the remote provider, so drift silently degrades rapid
uploads into hash mismatches.
- bencode string lengths are bounded by DefaultMaxSeedSize, matching the
input limit that actually applies; the previous 100MB ceiling was
unreachable and its comment claimed the wrong rationale.
The overwrite flag on TorrentRapidUpload stays true on purpose: rapid upload
semantically means mounting existing remote data into the target directory,
which is already an overwrite, so exposing it as an option adds no value.
Tests:
- pkg/torrent/seed_security_test.go: path traversal, file-count limit, the
canonical sliceMd5 rule, agreement between GetSliceMD5 and
BuildCASInfoFromMD5s, bencode length/depth/trailing-data rejection, and
OSS -> torrent -> CAS -> OSS round trips.
- server/handles/torrent_seed_test.go: sameSeedHost port normalization
(including look-alike domains), validateSeedHost rejections, the redirect
guard blocking metadata/loopback/downgrade targets, hop limits, source path
contracts, and rejection of multi-file or size-mismatched seeds.
go build ./... passes; go test ./pkg/torrent/... and
go test ./server/handles/... pass.
* refactor(seed): converge sliceMd5 rule on SliceMD5FromPieces
buildCASFileEntry previously reimplemented the sliceMd5 rule inline, which is the exact duplication that let the hash-generation and torrent/CAS encoding sides drift apart silently. Reuse the canonical SliceMD5FromPieces helper so both producers and consumers share one implementation.
* feat(ci): remove docs/PR-transfer-seeds.md
* ci(seeds): add standalone dev-seeds build and publish workflow
Builds linux amd64/arm64 (musl), windows amd64 and darwin arm64 from a chosen ref, embeds the rolling frontend by default, and force-publishes the compressed archives plus checksums to the dev-seeds branch.
* ci(seeds): add focused seed regression tests
Runs torrent codec/security tests, handler seed tests, go vet and a full build when seed sources change.
* chore(ci): drop seed test and dev-seeds build workflows
* fix(seed): address PR review findings
- drop the duplicate json format field from SeedConvertReq
- validate X-Seed-* headers before storing the uploaded file
- enforce read permission while sizing async seed generation
* fix(seed): refuse async offline downloads during relayed transfer
The relay path now tells saveSeedFilesToPath to reject offline-download tool tasks instead of enqueueing them into the transit storage and only rejecting the result afterwards.
* test(seed): cover hash-only stream contract for rapid upload
---------
Co-authored-by: PIKACHUIM <PIKACHUIM@users.noreply.github.com>
Co-authored-by: pikachuren <pikachuren@example.com>
* fix(sharing): resolve missing base path for thumbnails in shared links
- Fix the issue where thumbnail URLs in generated shares lacked the correct `/sd` prefix.
- Define `ThumbnailBaseURLKey` context key in configuration constants.
- Pass thumbnail base URL context with shared link path and password in sharing list handler.
- Use context thumbnail base URL when constructing thumbnail URLs in the local driver.
- Include thumbnail base URL in singleflight list key for storages with cache disabled.
Relates - https://github.com/OpenListTeam/OpenList/issues/3165
* refactor(local): simplify thumbnail URL construction
- Combine conditional checks for thumbnail support in local driver
- Standardize thumbnail URL formatting by using a fallback `url.URL` struct
- Flatten conditional checks for flightKey thumbnail URL in list operation
WoPan (China Unicom Cloud Drive) storages failed to initialize with
`failed get objs: failed to list objs: request failed with status: 403
Forbidden`.
Two independent defects in the driver's initialization path:
* The SDK keeps the browser User-Agent in a private field (WithUA/SetUA) and
only applies it to upload2C, so every dispatcher request went out with
resty's default `go-resty/...` User-Agent. The WoPan edge rejects non-browser
agents with HTTP 403. Apply wopan.DefaultUA to the resty client before the
first request.
* With only a refresh token configured, FamilyUserCurrentEncode was called
before the access token existed. Request bodies are AES encrypted with
accessToken[:16], so an empty token failed with `crypto/aes: invalid key
size 0`. Refresh a missing or too short access token first, then initialize
user data and query the default family.
Also drop the mandatory refresh_token flag so an access token alone is enough,
and add regression tests covering credential combinations, refresh ordering and
the dispatcher User-Agent.
Co-authored-by: pikachuren <pikachuren@example.com>
* fix(webdav): correct COPY/MOVE semantics with named copy support
Signed-off-by: Lythen <intro-iu@outlook.com>
* feat(webdav): persist dead properties with MOVE migration
Signed-off-by: Lythen <intro-iu@outlook.com>
* fix(webdav): enforce locks on resolved paths, support shared locks
Signed-off-by: Lythen <intro-iu@outlook.com>
* fix(fs): fall back to transfer task for same-dir copy
Signed-off-by: Lythen <intro-iu@outlook.com>
* fix(webdav): escape LIKE patterns, check dstDir is dir, bound PROPFIND body
Signed-off-by: Lythen <intro-iu@outlook.com>
* fix(webdav): escape backslash in LIKE, reduce dead prop retry
Signed-off-by: Lythen <intro-iu@outlook.com>
* fix(webdav): isolate shared lock instances
- Track each lock token with independent details and expiry state.\n- Keep resource nodes for path conflict accounting only.\n- Cover independent shared-lock expiration in memLS tests.
* fix(webdav): preserve dead property lifecycle
- Copy and replace dead properties with COPY destinations.\n- Remove dead-property subtrees after successful DELETE.\n- Stage MOVE properties before clearing the destination subtree.\n- Add database-backed regression coverage for copy and delete.
* fix(webdav): protect overwrite copy and move
- Validate source objects before touching overwrite destinations.\n- Stage COPY content under a collision-free name and restore destinations on failure.\n- Preserve MOVE destinations with a backup until the resource operation succeeds.\n- Route named same-storage copies through the direct destination-name transfer path.
* fix(webdav): harden overwrite staging
- Skip NoOverwriteUpload backup handling for collision-free internal COPY staging.\n- Treat post-commit overwrite backup cleanup failures as warnings.
* test(webdav): update lock lookup expectations
- Compare lookup results with independent lock instances.\n- Keep invalid token expectations nil after the lock state split.
* fix(webdav): complete litmus compliance
- Preserve named same-storage collection copies with isolated native staging.\n- Evaluate WebDAV ETag conditions and distinguish 412 from 423 lock failures.\n- Return 201 for lock-null resources and cover the new condition behavior.
* fix(fs): harden named copy staging
- retry staging cleanup from the deferred path and report residual directories
- document hook context semantics and wrap staging operation errors
Signed-off-by: Lythen <intro-iu@outlook.com>
* fix(core): support Go 1.27 checks
- Replace dynamic format strings with explicit format verbs and guard optional network configuration.
- Stabilize multipart cleanup, proxy, search, and MCP tests.
Co-authored-by: Codex <267193182+codex@users.noreply.github.com>
* fix(webdav): coordinate file and property transactions
- Keep MOVE, COPY, and DELETE file changes coordinated with dead-property transactions.
- Compensate filesystem changes when file operations or database commits fail.
- Add WebDAV coverage for transaction and recovery failures.
Co-authored-by: Codex <267193182+codex@users.noreply.github.com>
* fix(webdav): reject writes without parent collections
- Require an existing parent collection for PUT.
- Reject MKCOL when its parent is missing or not a collection.
Co-authored-by: Codex <267193182+codex@users.noreply.github.com>
* fix(webdav): refresh parent before delete checks
- Refresh the parent listing before checking whether a DELETE target exists.
- Return 404 for missing resources, including storage-not-found errors.
Co-authored-by: Codex <267193182+codex@users.noreply.github.com>
---------
Signed-off-by: Lythen <intro-iu@outlook.com>
Co-authored-by: Intro <Intro-iu@outlook.com>
Co-authored-by: Codex <267193182+codex@users.noreply.github.com>
* fix(s3): sign content type for direct uploads
- Include the inferred MIME type in presigned PutObject requests
- Return the signed Content-Type header for frontend uploads
Co-authored-by: Codex <267193182+codex@users.noreply.github.com>
* fix(drivers/s3): use client content type for direct uploads
- Accept and validate the client-provided direct upload media type
- Pass the type to the S3 signer through a typed context key
- Default missing types to application/octet-stream
Co-authored-by: Codex <267193182+codex@users.noreply.github.com>
---------
Co-authored-by: Codex <267193182+codex@users.noreply.github.com>
fix(onedrive): refresh root folder after storage update
- Clear the cached OneDrive root when a driver is dropped and reinitialized.
- Invalidate storage directory and detail caches for every configuration update.
Co-authored-by: Codex <267193182+codex@users.noreply.github.com>
feat(139): raise single file upload limit to 500 GB
* Use the PC client type in X-Yun-Client-Info to allow 500 GB uploads
Co-authored-by: Codex <267193182+codex@users.noreply.github.com>
* fix(189pc): preserve family transfer extension
- Preserve the source extension in family-transfer temporary upload names.
- Fall back to .transfer for extensionless source files.
- Add unit coverage for extension handling.
Co-authored-by: Codex <267193182+codex@users.noreply.github.com>
* fix(189pc): include extension in upload progress key
- Separate resumable upload state for identical content with different extensions.
- Preserve resume behavior when only the temporary file name changes.
- Add unit coverage for both cases.
Co-authored-by: Codex <267193182+codex@users.noreply.github.com>
---------
Co-authored-by: Codex <267193182+codex@users.noreply.github.com>
QR code scans were authorized on the phone but the storage stayed stuck on
the QR page, and token-only setups failed with "params is null".
The driver used appId 8025431004 while the official PC client uses
9317140619. Tokens, sessions and QR sessions are all scoped to an appId, so
the mismatch meant the QR poll never saw status:0 and an accessToken could
not be exchanged for a sessionSecret.
- Use appId 9317140619 and version 7.2.4.0. QR state polling uses
clientType=1; password login keeps 10020.
- Send the QR poll parameters the official client sends (cb_SaveName,
isOauth2, state, user-finger header, logbox Referer) and poll locally
instead of only checking once per save.
- Parse lt/reqId from the logbox redirect and paramId from appConf.do. The
new login page no longer embeds them as inline variables; the old inline
format is still supported.
- Implement the -133 second device verification via
sendSmsCodeForSecondAuth/submitForSecondAuth. That endpoint has no
dedicated SMS field: the code goes into epd, encrypted with the same
public key used for the password. Persist the DEVICEID cookie so the
verification only happens once.
- Detect refreshToken.do failures. It reports them as HTTP 200 with a
result field, so SetError never fired and a failed refresh was treated as
success, surfacing later as a misleading "params is null".
- username/password are no longer required, so token-only storages save
without placeholders. clientSn/jgOpenId are optional and only sent when
configured; user-finger is generated once per storage.
- Return named errors instead of panicking when the login page changes shape.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Lanzou recently changed the file share page: download params moved
into an iframe (/fn?<token>) inner page, and the download API is now
an absolute URL (https://apifile.woozooo.com/ajaxfile.php?file=N)
with new sign params (wp_sign/ajaxdata, action=downprocess).
The old findFileIDReg ('/ajaxm.php?file=N' relative path) no longer
matches, causing 'failed link: failed get link: not find file id'.
Fall back to parsing the new /fn page structure when the legacy
regex does not match. Legacy flow is kept untouched.
Signed-off-by: rzsgsfm <rzsgsfm@users.noreply.github.com>
Co-authored-by: rzsgsfm <rzsgsfm@users.noreply.github.com>
Co-authored-by: GLM (ZCode CLI) <noreply@z.ai>
* fix(aliyundrive): limit callback concurrency
- Share proxy callback admission by Aliyun user identity and hold permits for complete response-body lifetimes.
- Retry only verified callback-capacity rejections while preserving direct redirects and server download limiting.
- Map exhausted temporary capacity to S3 SlowDown through the merged OpenListTeam gofakes3 module.
- Cover shared limits, lifecycle release, cancellation, retry classification, and the S3 HTTP response.
Co-authored-by: Codex <267193182+codex@users.noreply.github.com>
# Conflicts:
# go.mod
# go.sum
# server/s3/pager.go
* fix(op): separate redirect and proxy link cache entries
- Include redirect mode in the link cache key for all drivers.
- Cover both redirect-to-proxy and proxy-to-redirect cache reuse.
Co-authored-by: Codex <267193182+codex@users.noreply.github.com>
* fix(proxy): close range bodies before opening next
- make ServeHTTP own each range body and preserve cleanup failures
- remove the aggregate range closer and pass range readers directly
- replace the obsolete callback transport test with focused lifecycle coverage
Co-authored-by: Codex <267193182+codex@users.noreply.github.com>
---------
Co-authored-by: nostalume <nostalucent@gmail.com>
Co-authored-by: Codex <267193182+codex@users.noreply.github.com>
- Refresh OSS credentials and retry the rejected multipart operation
- Preserve upload IDs, completed parts, callbacks, and outer part retries
- Rewind parts before resending and pass the upload context to OSS requests
Co-authored-by: Codex <267193182+codex@users.noreply.github.com>
* fix(alist_v3): set child paths so nested directories resolve
- Set `Path` on every object returned by `List`, matching the OpenList
driver. `op.Get` hands a child object straight back to `List`, so a
child without a path made the driver request `""` from the upstream
server, which answered with its own root: every directory below the
mount point served the same listing back, endlessly.
- Add tests covering the child paths and a three-level descent.
Co-authored-by: Claude <81847+claude@users.noreply.github.com>
* test(alist_v3): trim the child-path test to a single case
Collapse the two tests into one two-level descent, drop the recorder
type and the helper funcs, and inline the driver setup. 173 -> 65 lines.
Co-authored-by: Claude <81847+claude@users.noreply.github.com>
---------
Co-authored-by: Fighting <3899648+Elity@users.noreply.github.com>
Co-authored-by: Claude <81847+claude@users.noreply.github.com>
- add an opt-in Local driver setting for PDF thumbnails
- render PDF first pages with macOS Quick Look
- preserve unsupported-platform behavior and cover the renderer with tests
Co-authored-by: Codex <267193182+codex@users.noreply.github.com>
Co-authored-by: ILoveScratch <ilovescratch@foxmail.com>
* fix(drivers/139): improve mail login credential renewal
* fix(drivers/139): guard mail login client initialization
Fall back to base.NewRestyClient() when base.RestyClient has not been initialized, while preserving cloned global-client behavior and the login/SMS retry and redirect policies.
- Decode JSON time strings before normalizing Unicode spaces in both 189 drivers
- Exercise escaped spaces and existing date formats through JSON unmarshalling
- Cover invalid JSON input and XML time parsing
Signed-off-by: jyxjjj <16695261+jyxjjj@users.noreply.github.com>
Co-authored-by: Codex <267193182+codex@users.noreply.github.com>
multipartUploadToOSS passed the UpdateProgress callback into
stream.NewStreamSectionReader, which ignores the up argument, so the
callback was never invoked while uploading parts. As a result,
cross-storage copy tasks targeting GuangYaPan stayed at 0% and showed no
progress bar or speed in the task list.
Fix by tracking the uploaded byte count and invoking up after every
part is uploaded, keeping consistent with other drivers' upload flow.
Co-authored-by: Pikachu Ren <40362270+PIKACHUIM@users.noreply.github.com>
The 115 and pikpak multipart uploaders checked for cancellation with a
`case <-ctx.Done()` inside a select, and a break there only leaves the
select, not the enclosing `for retry := 0; retry < 3; retry++` loop.
Cancelling an upload therefore ran all three attempts for every
remaining chunk, each allocating a chunk-sized buffer and reading it
off disk before firing a request that could not succeed, and reported
a transport error instead of the cancellation.
Move the check ahead of the select and use utils.IsCanceled, matching
the pattern the other drivers already use. Assigning ctx.Err() to err
is load-bearing: without it a cancelled chunk takes the success branch,
counts toward progress, and appends a zero-value UploadPart.
Found with staticcheck (SA4011).
Co-authored-by: Pikachu Ren <40362270+PIKACHUIM@users.noreply.github.com>
* feat(strm): add local save permission mode
- add private and shared permission modes for local STRM files
- repair shared-mode directory and file permissions during generation
- add permission handling tests
Co-authored-by: Codex <267193182+codex@users.noreply.github.com>
* fix(strm): respect deployment umask for local directories
- remove application-level permission modes and chmod operations
- create local STRM directories with umask-controlled permissions
- preserve existing permissions and test the behavior
Co-authored-by: Codex <267193182+codex@users.noreply.github.com>
---------
Co-authored-by: Codex <267193182+codex@users.noreply.github.com>
* feat(ilanzou): modernize console API session and copy
Use an isolated cookie-backed API session and preserve raw appToken syntax required by current iLanzou endpoints.
Resolve CDN download responses more robustly, align upload metadata with the console protocol, and delegate copies to OpenList background tasks instead of blocking requests.
* fix(ilanzou): escape appToken query values
Escape opaque appToken values while preserving the literal colon required by iLanzou endpoints. Ignore CDN HEAD response lengths unless the response status is successful.
* fix(ilanzou): address driver review feedback
Use the ilanzou package name consistently, document upload result polling, and bound CDN size probes. Keep the appToken and CDN challenge handling covered by focused tests and comments.
* fix(ilanzou): use context timeout for HEAD probe
* feat(strm): add file size filtering for STRM generation
- add ParseSize helper function in pkg/utils to parse human-readable byte sizes
- add minFileSize setting field to STRM driver Addition struct
- filter out files below minFileSize threshold during STRM generation
* refactor(strm): use MB as default unit for minFileSize instead of ParseSize
Replace string-based ParseSize auto-conversion with a simple int64 number
field defaulting to MB, consistent with other drivers (Google Drive,
Teldrive). Remove the now-unused ParseSize function and its tests.
The Login endpoint may return an acw_sc__v2 validation page when accessed,
which previously caused login failures. This change adds the same retry
logic and cookie handling already used in request() to automatically solve
the challenge, ensuring login success even when the anti-bot mechanism is
triggered.