Compare commits

...

145 Commits

Author SHA1 Message Date
16e07ae016 fix(s3): set default root path (close #2834) 2022-12-30 14:53:01 +08:00
d1b9db38c7 feat(docker): add docker-compose file (close #2067) 2022-12-30 14:25:22 +08:00
395f0fc5f3 fix(docker): use root user as default 2022-12-30 14:21:39 +08:00
143e4cd077 fix: mysql FULLTEXT search (#2840) 2022-12-30 14:20:04 +08:00
f777a2fab4 fix: version doesn't update 2022-12-30 01:24:37 +08:00
dad3012ec3 fix(deps): update module github.com/aws/aws-sdk-go to v1.44.169 (#2816)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-29 21:22:50 +08:00
d45209edb2 fix: /entrypoint.sh permission denied 2022-12-29 17:16:30 +08:00
e89489453d fix: cache nil value for meta 2022-12-28 17:44:34 +08:00
ed6c8194a7 feat: add PUID, PGID, Umask settings to docker image (close #2525 pr #2818)
Co-authored-by: DDSRem <1448139087@qq.com>
2022-12-28 17:18:27 +08:00
83fe17c6ec feat: support github login (#2639)
* Support Github Login

* improve according to codefactor

* fix due to last updates

* optimization

Co-authored-by: Noah Hsu <i@nn.ci>
2022-12-27 22:11:22 +08:00
c00dcc8f39 fix(deps): update module github.com/gin-gonic/gin to v1.8.2 (#2785)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-25 18:20:24 +08:00
e118f4a3b9 feat: update index by req.Paths 2022-12-24 20:23:04 +08:00
5e28d0f96a fix(deps): update module github.com/aws/aws-sdk-go to v1.44.167 (#2781)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-24 16:14:20 +08:00
3af23f6792 feat: batch reload all storages (close #2762 pr #2775) 2022-12-21 19:21:18 +08:00
3a41b929c9 fix: pgsql search [skip ci] (close #2761 pr #2774) 2022-12-21 19:19:37 +08:00
105f22969c feat: support cancel for some drivers (close #2717) 2022-12-21 15:03:09 +08:00
e4a88a7c13 fix(deps): update module github.com/aws/aws-sdk-go to v1.44.164 (#2773)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-21 12:04:32 +08:00
b0255040c6 chore: fix typo 2022-12-20 20:07:19 +08:00
f1e842e12a feat: customize settings layout (close #2765) 2022-12-20 20:04:37 +08:00
d756cf3e9f fix(local): disable copying or moving to subfolders (close #2760) 2022-12-20 16:27:04 +08:00
146619134d feat: customize proxy ignore headers (close #2763 pr #2766)
* clean referer when use proxy

* feat: customize proxy ignore headers

Co-authored-by: Noah Hsu <i@nn.ci>
2022-12-20 16:08:32 +08:00
372030071e fix(deps): update module github.com/aws/aws-sdk-go to v1.44.163 [skip ci] (#2738)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-20 15:13:14 +08:00
62a06fa0f9 feat: optimize file operation interface (#2757)
* feat: optimize file operation interface

* chore: fix typo

Co-authored-by: Noah Hsu <i@nn.ci>
2022-12-20 15:02:40 +08:00
e2bcca2fbd feat: static files for embed viewers (#2739) 2022-12-19 13:34:06 +08:00
4568af9542 feat: better static file Cache-Control (#2751) 2022-12-19 13:32:00 +08:00
b50d486a63 fix: sub path check if subPath = / 2022-12-18 21:28:38 +08:00
0ae3fc608b feat: export all cmd (#2746) 2022-12-18 19:53:39 +08:00
6024e8d832 refactor: split the db package hook and cache to the op package (#2747)
* refactor:separate the setting method from the db package to the op package and add the cache

* refactor:separate the meta method from the db package to the op package

* fix:setting not load database data

* refactor:separate the user method from the db package to the op package

* refactor:remove user JoinPath error

* fix:op package user cache

* refactor:fs package list method

* fix:tile virtual paths (close #2743)

* Revert "refactor:remove user JoinPath error"

This reverts commit 4e20daaf9e700da047000d4fd4900abbe05c3848.

* clean path directly may lead to unknown behavior

* fix: The path of the meta passed in must be prefix of reqPath

* chore: rename all virtualPath to mountPath

* fix: `getStoragesByPath` and `GetStorageVirtualFilesByPath`

is_sub_path:

/a/b isn't subpath of /a/bc

* fix: don't save setting if hook error

Co-authored-by: Noah Hsu <i@nn.ci>
2022-12-18 19:51:20 +08:00
f38f4f401b fix(139): modify chunk size to avoid large file upload failure (close #2744 close #2682 pr #2745) 2022-12-18 17:48:09 +08:00
3b2ae85009 chore: only ignore root dirs (#2741) 2022-12-18 16:48:32 +08:00
faf4150d1e docs: fix badges on README.md and README_cn.md [skip ci] (#2749) 2022-12-18 16:48:03 +08:00
fb64f00640 refactor: obj name mapping and internal path processing (#2733)
* refactor:Prepare to remove the get interface

* feat:add obj Unwarp interface

* refactor:obj name mapping and program internal path processing

* chore: fix typo

* feat: unwrap get

* fix: no use op.Get to get parent id

* fix: set the path uniformly

Co-authored-by: Noah Hsu <i@nn.ci>
2022-12-17 19:49:05 +08:00
3d336b328a feat: add pikpak share driver (close #2728 pr #2731) 2022-12-16 19:10:19 +08:00
f9cf29e0b6 fix(deps): update module golang.org/x/crypto to v0.4.0 (#2638) [skip ci]
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-16 19:08:52 +08:00
cbd038f30f fix(deps): update module golang.org/x/net to v0.4.0 (#2608) [skip ci]
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-16 19:05:20 +08:00
2aeb75a779 fix(deps): update module github.com/blevesearch/bleve/v2 to v2.3.6 (#2727) [skip ci]
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-16 19:05:06 +08:00
2f8eaf6bea fix(deps): update module github.com/pquerna/otp to v1.4.0 (#2708) [skip ci]
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-16 18:15:59 +08:00
fb7a5dec1b fix(deps): update module golang.org/x/image to v0.2.0 (#2601) [skip ci]
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-16 18:15:19 +08:00
e61bac039a fix(deps): update module github.com/aws/aws-sdk-go to v1.44.161 (#2595) [skip ci]
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-16 18:14:56 +08:00
b3be9ef428 feat(search): use FULLTEXT index (close #2716 pr #2726) 2022-12-16 16:51:36 +08:00
5a6b600ace feat: show gorm log on debug/dev mode (#2720) 2022-12-15 17:48:52 +08:00
e58ca686e3 feat: cache static files (#2715) 2022-12-15 17:48:29 +08:00
6f4b1ba4b3 feat: log to stdout & file (#2709) 2022-12-14 13:19:08 +08:00
cdc45630ae fix: whereInParent when parent = "/" (#2706) 2022-12-14 10:37:09 +08:00
7947ff1ae4 feat: limit max connection count (#2701) 2022-12-14 10:33:58 +08:00
33bae52fa1 refactor: optimize driver initialization need to manually deserialize and assign values, and remove redundant driver registration parameters (#2691)
* refactor: optimize driver initialization need to manually deserialize and assign values, and remove redundant driver registration parameters

* fix typo

Co-authored-by: Noah Hsu <i@nn.ci>
2022-12-13 18:03:30 +08:00
3ee45c69a7 fix(baidu_netdisk): encode path for create (close #2690) 2022-12-13 17:57:41 +08:00
179d285564 feat: optimize database search (#2687)
* feat: remove index on `SearchNode.Name`

As we do not use s% on name column, index there does not work

* fix: init index after init data

Or on the first run, it will log 'init index error: readObjectStart: expect { or n, but found , error found in #0 byte of ...||..., bigger context ...||...'

* fix: match parent more precisely

It will match `/a/bc` if we search in `/a/b` originally.
But it is not backward compatible by adding a suffix `/`
to all the data in parent field
2022-12-12 20:20:01 +08:00
a2e8e96c71 feat: respond static file on loading storages (#2686) 2022-12-12 20:17:58 +08:00
5043815d48 fix(search): don't delete virtual folder while update indexes (close #2677) 2022-12-11 14:59:58 +08:00
1640f06e13 feat(search): multiple keywords split by space (#2669) 2022-12-10 19:28:34 +08:00
62ea93837c feat: alist v3 index permission (#2653)
* feat: alist v3 index permission

* fix allowIndexed check

Co-authored-by: Noah Hsu <i@nn.ci>
2022-12-10 19:03:09 +08:00
446f82888c fix(local): add sign to thumbnail (close #2536 close #2650) 2022-12-09 10:08:31 +08:00
6f1aeb47fd feat: index enhancement (close #2632 pr #2636)
* feat: index paths as setting

* feat: clear index (#2632)

* feat: check indexMQ more frequently
2022-12-09 10:02:13 +08:00
1f7c1b4f43 fix(cors): allow all methods (close #2640) 2022-12-08 11:35:21 +08:00
3fa0217c4b feat(alist-v3): support write (close #2626 pr #2635) 2022-12-07 19:02:28 +08:00
2dd30f2b77 feat(search): support with password 2022-12-07 10:45:02 +08:00
6e23c8b4c0 feat: partial update index (close #2593 close #2621 pr #2624) 2022-12-07 10:41:52 +08:00
72aa63adce fix: skip virtual driver on building index (close #2604 pr #2617) 2022-12-06 20:43:32 +08:00
e65e8be59e fix(search): missed base_path of user for parent (close #2611) 2022-12-06 17:28:39 +08:00
7aa4dfb240 feat: use natural sort in SortFiles (#2612) 2022-12-06 17:28:18 +08:00
bd324233a0 fix: can't paste image while report bug (#2597) [skip ci] 2022-12-06 09:19:49 +08:00
f1a9b68022 fix(index): update indexes in database 2022-12-05 20:23:37 +08:00
dda1da4576 fix(index): nil pointer call 2022-12-05 20:22:35 +08:00
5b7aa9c1cf feat: allow all cors headers (close #2571) 2022-12-05 20:05:20 +08:00
a28aaceaad chore(ci): only build on main branch 2022-12-05 19:52:02 +08:00
2bb200af87 fix(deps): update modules by renovate[bot]
fix(deps): update module github.com/sheltonzhu/115driver to v1.0.13 (#2413) [skip ci]

fix(deps): update module github.com/golang-jwt/jwt/v4 to v4.4.3 (#2526) [skip ci]

fix(deps): update module golang.org/x/image to v0.1.0 (#2587) [skip ci]

chore: go mod tidy
Co-Authored-By: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-05 19:50:49 +08:00
97f1efbb72 feat!: disable --force-bin-dir if --data is abs
related issues: #2580 #2542

after this commit, the `--force-bin-dir` would take no effect if `--data` is absolute path
2022-12-05 18:32:48 +08:00
bf8b6f4c2c feat: customize ignore paths of indexes 2022-12-05 16:45:11 +08:00
bd33c200dc feat: optimize index build 2022-12-05 16:07:36 +08:00
bc6baf1be0 fix(ci): sort lang json file 2022-12-05 14:40:46 +08:00
dc8d5106f9 feat: auto fix address in alist & smb storages (#2582) 2022-12-05 13:31:34 +08:00
8c0dfe2f3d feat: Search enhancement (#2562)
* feat: ignore AList storage on indexing

* fix: remove unused err in `walkFn`

* chore(ci): fix auto_lang trigger and run it

* feat: batch index

* feat: quit index & init index

* feat: set DocType for bleve data

* fix: build index cleanup check origin err
2022-12-05 13:28:39 +08:00
4e1be9bee6 fix: async init aria2 to optimize start duration 2022-12-04 00:00:40 +08:00
4c5285e094 chore(ci): format lang file (#2558) 2022-12-03 12:19:10 +08:00
0838feeb82 fix:introduce buffered response writer for webdav, fix status/error return failed. (#2544)
* fix: introduce buffered response writer for webdav, fix webdav status/error return failed.

* fix: bypass buffered writer for GET/HEAD/POST requests
2022-12-02 17:59:59 +08:00
ae791c8634 fix: hide check in canAccess (#2556)
修复 meta.Password 和 meta.Hide 都为空的情况下,会导致无权限访问
2022-12-02 17:44:29 +08:00
09f480318c fix: unify settings string (#2555) 2022-12-02 17:42:42 +08:00
4c5be5f07f feat: only show CanAccess search results (#2548)
* feat: only show `CanAccess` search results

* have done in frontend

Co-authored-by: Noah Hsu <i@nn.ci>
2022-12-02 10:09:39 +08:00
9c1ffdbb82 fix(aliyundrive): return error if got wrong http code (#2543) 2022-12-01 21:48:19 +08:00
18a63e34dd fix(task): memory alignment for curID (close #2541) 2022-12-01 13:16:31 +08:00
ff0bcfef8a feat: optional sign all files 2022-11-30 22:10:07 +08:00
4980b71ba3 fix: add hide check to canAccess (close #2532) 2022-11-30 22:01:33 +08:00
b5bf5f4325 fix: check if the req path is relative path (close #2531) 2022-11-30 21:38:00 +08:00
f9788ea7cf feat(webdav): delete privacy header and optimize 302 (#2534)
* fix: delete set-cookie from sharepoint webdav response header

* fix: avoid two redirects when using webdav

* fix: return the correct Content-Type instead of just `application/octet-stream`

* feat: webdav backend localOnly -> proxyOnly
2022-11-30 20:52:33 +08:00
83644dab85 fix: mapping filename in GetName
some missed filename mapping
2022-11-30 20:46:54 +08:00
d94cf72da2 fix(local): webp image decode while generate thumbnail (close #2484 pr #2520)
* Fix that webp thumb  in local storage won't load

* Simplify code

Co-authored-by: Noah Hsu <i@nn.ci>
2022-11-29 09:47:40 +08:00
e98561ceb1 fix: filename char mapping while build index 2022-11-28 21:08:11 +08:00
76f37373e0 fix: settings map read and write concurrently 2022-11-28 16:54:03 +08:00
61a06992c3 fix(aria2): directory missing (close #1856 pr #2504) 2022-11-28 14:05:28 +08:00
ddcba93eea feat: multiple search indexes (#2514)
* refactor: abstract search interface

* wip: ~

* fix cycle import

* objs update hook

* wip: ~

* Delete search/none

* auto update index while cache changed

* db searcher

TODO: bleve init issue

cannot open index, metadata missing

* fix size type

why float64??

* fix typo

* fix nil pointer using

* api adapt ui

* bleve: fix clear & change struct
2022-11-28 13:45:25 +08:00
bb969d8dc6 fix(aliyundrive_share): get share link download url directly (close #2472) 2022-11-24 18:50:04 +08:00
2383e851e2 fix: reset index before build new one (#2471) 2022-11-24 14:47:49 +08:00
330a767fd7 feat: build index & search with bleve (close #1740 pr #2386)
* feat: build index & search with bleve (#1740)

* delete unused struct

Co-authored-by: Noah Hsu <i@nn.ci>
2022-11-24 11:46:47 +08:00
2b902de6fd fix(build): switch to crazymax/xgo 2022-11-22 21:08:27 +08:00
85e1350af8 fix: check password while upload (close #2444) 2022-11-22 16:14:01 +08:00
c09800790b feat: custom filename char mapping
fixes #2447 #2446 #2440 #2409 #2006 #1979 #1507 #324 #691 #518 #430
2022-11-22 15:54:18 +08:00
25fd343069 chore(deps): update module gorm and aws-sdk
fix(deps): update module gorm.io/gorm to v1.24.2 (#2436)

fix(deps): update module github.com/aws/aws-sdk-go to v1.44.142 (#2407)

Co-Authored-By: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-11-21 17:37:03 +08:00
518487e3df fix(123): optimize error messages (#2415) 2022-11-19 21:48:03 +08:00
a02d9c8463 fix: check error type on file not found (#2383) 2022-11-18 01:30:37 +08:00
8beeba7c0c fix(google_drive): check token before return link (close #2392) 2022-11-17 09:08:31 +08:00
50fb49f0c3 fix(deps): update dependencies by renovate[bot] (#2344)
chore(deps): add renovate.json (#2344)

fix(deps): update module github.com/aws/aws-sdk-go to v1.44.137 (#2345)

chore(deps): update actions-cool/issues-helper action to v2.5.0 (#2346)

fix(deps): update module github.com/caarlos0/env/v6 to v6.10.1 (#2348)

fix(deps): update module github.com/gin-contrib/cors to v1.4.0 (#2349)

fix(deps): update module github.com/sirupsen/logrus to v1.9.0 (#2354) [skip ci]

fix(deps): update module gorm.io/driver/postgres to v1.4.5 (#2361)  [skip ci]

fix(deps): update module golang.org/x/crypto to v0.2.0 (#2357) [skip ci]

fix(deps): update module github.com/aws/aws-sdk-go to v1.44.138 (#2358) [skip ci]

fix(deps): update module gorm.io/gorm to v1.24.1 (#2366) [skip ci]

fix(deps): update module gorm.io/driver/mysql to v1.4.4 (#2360) [skip ci]

fix(deps): update module github.com/spf13/cobra to v1.6.1 (#2356) [skip ci]

chore(deps): update actions-cool/issues-helper action to v3 (#2367) [skip ci]

fix(deps): update module gorm.io/driver/sqlite to v1.4.3 (#2365) [skip ci]

chore(deps): update actions/checkout action to v3 (#2368) [skip ci]

chore(deps): update actions/setup-go action to v3 (#2374) [skip ci]

chore(deps): update actions/upload-artifact action to v3 (#2375) [skip ci]

chore(deps): update docker/build-push-action action to v3 (#2377) [skip ci]

chore(deps): update docker/login-action action to v2 (#2378) [skip ci]

chore(deps): update docker/metadata-action action to v4 (#2381) [skip ci]

chore(deps): update docker/setup-buildx-action action to v2 (#2382) [skip ci]

chore(deps): update docker/setup-qemu-action action to v2 (#2387) [skip ci]

fix(deps): update module github.com/aws/aws-sdk-go to v1.44.139 (#2394) [skip ci]

fix(deps): update module golang.org/x/crypto to v0.3.0 (#2395) [skip ci]

Co-Authored-By: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-11-17 08:49:15 +08:00
4dcaa24758 fix: cache is modified while sorting (close #2340) 2022-11-15 14:38:23 +08:00
3fbdf6f022 fix: resolve import cycle in alist v3 driver (close #2337 pr #2338) 2022-11-15 10:51:32 +08:00
aa9ba289bb fix(123): overwrite upload if file has no change (close #2324) 2022-11-14 17:58:49 +08:00
3b6d8987db chore: add id to resp of create storage 2022-11-13 20:17:10 +08:00
6e3df9f847 fix(google_drive): type of chunk_size (close #2303) 2022-11-12 18:46:38 +08:00
efe0e6af22 feat: silent start, stop and restart 2022-11-11 18:42:06 +08:00
00de9bf16d fix!: sign with the raw path instead of filename (#2258) 2022-11-11 16:24:25 +08:00
1743110a70 fix(123): incorrect order_by (close #2285) 2022-11-10 21:47:13 +08:00
0352a8e028 feat: add alist v2 driver (#2281) 2022-11-10 17:42:12 +08:00
c601bb794b feat(123): support mail login (close #2218 pr #2276) 2022-11-10 09:34:48 +08:00
42865486f1 fix(local): deal with relative symlink dir (#2274) 2022-11-09 18:15:42 +08:00
44f5cf40ef fix(115): update 115 driver lib to fix some bugs (#2275)
* fix duplicate cookies in client.List func
* rm useless cookie when init
2022-11-09 18:15:06 +08:00
c3ab378ac5 feat(google_drive): support shortcut (close #2268) 2022-11-09 16:19:33 +08:00
cdcbfb24c4 fix(local): directory handle (#2262)
* fix(local): check symlink dir

* fix(local): set size of dir to 0 (close #2264)
2022-11-09 11:20:09 +08:00
e05e2fd663 chore: change default timeout (close #2252) 2022-11-08 20:37:42 +08:00
6639cab1ae feat(google_drive): chunk upload (close #2241) 2022-11-07 20:58:52 +08:00
8241f0999a feat(google_drive): override upload (close #1880) 2022-11-07 20:35:35 +08:00
f3a5e3702d fix(189): force use CN time zone (close #2240) 2022-11-07 16:37:47 +08:00
46701a176d feat(aria2): mark aria2 seeding as complete (#2223)
Currently if using aria2 to download a torrent file, it does not
consider seeding + active as completed, so the torrent download task
only completes as aria2 stops seeding.

This commit uses seeder property of TaskInfo, and mark tasks with active
status and true seeder as complete.
2022-11-06 16:20:09 +08:00
26a29f20c3 fix: missed encode path while use down proxy (close #2208) 2022-11-06 14:46:47 +08:00
18cd45d257 fix: disable cache for 302 redirect (close #2216) 2022-11-05 15:54:51 +08:00
f0a533a77a feat(115): put UA as a variable (#2217)
In special cases, developers can pass in custom UA to solve the speed limit problem
Mainly for developers calling from outside
2022-11-05 15:50:57 +08:00
619a9aeb6c feat(115): add qrcode login (#2206) 2022-11-04 21:16:52 +08:00
7bfa5876ed fix(189pc): fix typo 2022-11-01 19:32:40 +08:00
f95ab6ee57 docs: add 115 to readme [skip ci] 2022-11-01 19:28:24 +08:00
e75f19e9c0 feat: add Referrer-Policy while redirect (pr #2160) 2022-11-01 19:19:08 +08:00
1c212f6c30 feat!: force to use the bin dir as the data dir (close #2108)
- move default log path to `data/log/log.log`
- replace `--conf` with `--data`
2022-11-01 19:16:23 +08:00
141419056d feat(115): add cloud 115 driver (#2164)
close #2112
close #1598
close #894
2022-11-01 15:31:31 +08:00
aabfe49cb9 docs: change contributors show [skip ci] 2022-10-30 15:26:31 +08:00
a3b631f9e9 fix(smb): remount smb before each operation (close #2123 pr #2140) 2022-10-30 15:05:07 +08:00
18165eb50d fix(123): get real url (#2135)
123 今天更新多加了一层跳转`https://web-pro.cjjd18.com/download/?params=base64encode(rawurl)`,导致ip如果不符则可能下载返回403,在服务器端处理获取rawurl
2022-10-27 17:02:35 +08:00
061c462f0b feat(mediatrack): get real url (#2132)
* feat:get real url for mediatrack

redirect token 一次有效,点击第二次就抛出了`400`错误。本次提交直接获取302后的真实链接返回前端

* add cache

cache 60 Second
2022-10-27 15:26:08 +08:00
5f79d665d9 feat: add alist v3 driver (close #1833 pr #2129)
* feat: add alist v3 driver (close #1833)

* chore: use generics

Co-authored-by: Noah Hsu <i@nn.ci>
2022-10-27 10:54:49 +08:00
f0cc0a76a9 chore: fix typos (#2122)
* refactor: fix typos

* Update help.go

Co-authored-by: Noah Hsu <i@nn.ci>
2022-10-26 14:05:56 +08:00
dd4674e486 feat: add smb driver (close #1746) (#2114)
* feat: add smb driver (close #1746)

* Update driver.go
2022-10-25 23:00:23 +08:00
0019959eec fix: delete cache if files is empty 2022-10-25 16:42:06 +08:00
3e9c38697d fix: use utils.Log during static file router init (#2100)
formerly the log is not in stderr
2022-10-24 23:33:57 +08:00
e3b7c41199 docs: update demo url & sponsor content [skip ci] 2022-10-24 22:48:36 +08:00
a2c808c8ce fix: incorrect root path of initial storage for dev mode 2022-10-23 16:26:14 +08:00
da7e17aa38 feat(local): add show hidden config (#2087) 2022-10-23 14:53:07 +08:00
02df3759df docs: fix typo [skip ci] 2022-10-20 14:29:28 +08:00
4fef500795 feat(user): set default password of init user from env (#2058)
add init user default password

Signed-off-by: ysicing <i@ysicing.me>

Signed-off-by: ysicing <i@ysicing.me>
2022-10-19 20:06:06 +08:00
07ece452b3 docs: fix docker link [skip ci] 2022-10-19 17:08:01 +08:00
233 changed files with 5972 additions and 2079 deletions

View File

@ -1,98 +0,0 @@
{
"files": [
"CONTRIBUTORS.md"
],
"imageSize": 100,
"commit": false,
"contributors": [
{
"login": "Xhofe",
"name": "Xhofe",
"avatar_url": "https://avatars.githubusercontent.com/u/36558727?v=4",
"profile": "http://nn.ci",
"contributions": [
"code",
"ideas",
"doc"
]
},
{
"login": "foxxorcat",
"name": "foxxorcat",
"avatar_url": "https://avatars.githubusercontent.com/u/95907542?v=4",
"profile": "https://github.com/foxxorcat",
"contributions": [
"code"
]
},
{
"login": "DaoChen6",
"name": "道辰",
"avatar_url": "https://avatars.githubusercontent.com/u/63903027?v=4",
"profile": "https://www.iflu.cf/",
"contributions": [
"doc"
]
},
{
"login": "vg-land",
"name": "vg-land",
"avatar_url": "https://avatars.githubusercontent.com/u/16739728?v=4",
"profile": "https://vg-land.github.io/",
"contributions": [
"code"
]
},
{
"login": "Clansty",
"name": "凌莞~(=^▽^=)",
"avatar_url": "https://avatars.githubusercontent.com/u/18461360?v=4",
"profile": "https://c5y.moe",
"contributions": [
"doc"
]
},
{
"login": "Windman1320",
"name": "Windman",
"avatar_url": "https://avatars.githubusercontent.com/u/9999486?v=4",
"profile": "https://github.com/Windman1320",
"contributions": [
"code"
]
},
{
"login": "ericarena",
"name": "ericarena",
"avatar_url": "https://avatars.githubusercontent.com/u/4518927?v=4",
"profile": "https://github.com/ericarena",
"contributions": [
"code"
]
},
{
"login": "WntFlm",
"name": "WntFlm",
"avatar_url": "https://avatars.githubusercontent.com/u/34620278?v=4",
"profile": "https://github.com/WntFlm",
"contributions": [
"code"
]
},
{
"login": "XZB-1248",
"name": "XZB-1248",
"avatar_url": "https://avatars.githubusercontent.com/u/28593573?v=4",
"profile": "https://github.com/XZB-1248",
"contributions": [
"code"
]
}
],
"contributorsPerLine": 7,
"projectName": "alist",
"projectOwner": "alist-org",
"repoType": "github",
"repoHost": "https://github.com",
"skipCi": true
}

View File

@ -54,4 +54,3 @@ body:
description: | description: |
Please copy and paste any relevant log output. Please copy and paste any relevant log output.
请复制粘贴错误日志,或者截图 请复制粘贴错误日志,或者截图
render: shell

View File

@ -7,6 +7,8 @@ on:
paths: paths:
- 'drivers/**' - 'drivers/**'
- 'internal/bootstrap/data/setting.go' - 'internal/bootstrap/data/setting.go'
- 'internal/conf/const.go'
- 'cmd/lang.go'
workflow_dispatch: workflow_dispatch:
jobs: jobs:
@ -19,12 +21,12 @@ jobs:
runs-on: ${{ matrix.platform }} runs-on: ${{ matrix.platform }}
steps: steps:
- name: Setup go - name: Setup go
uses: actions/setup-go@v2 uses: actions/setup-go@v3
with: with:
go-version: ${{ matrix.go-version }} go-version: ${{ matrix.go-version }}
- name: Checkout alist - name: Checkout alist
uses: actions/checkout@v2 uses: actions/checkout@v3
with: with:
path: alist path: alist
@ -42,6 +44,7 @@ jobs:
cd alist cd alist
go run ./main.go lang go run ./main.go lang
cd .. cd ..
- name: Copy lang file - name: Copy lang file
run: | run: |
cp -f ./alist/lang/*.json ./alist-web/src/lang/en/ 2>/dev/null || : cp -f ./alist/lang/*.json ./alist-web/src/lang/en/ 2>/dev/null || :
@ -61,4 +64,4 @@ jobs:
github_token: ${{ secrets.MY_TOKEN }} github_token: ${{ secrets.MY_TOKEN }}
branch: main branch: main
directory: alist-web directory: alist-web
repository: alist-org/alist-web repository: alist-org/alist-web

View File

@ -2,9 +2,9 @@ name: build
on: on:
push: push:
branches: [ '**' ] branches: [ 'main' ]
pull_request: pull_request:
branches: [ '**' ] branches: [ 'main' ]
jobs: jobs:
build: build:
@ -16,7 +16,7 @@ jobs:
runs-on: ${{ matrix.platform }} runs-on: ${{ matrix.platform }}
steps: steps:
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v2 uses: actions/setup-go@v3
with: with:
go-version: ${{ matrix.go-version }} go-version: ${{ matrix.go-version }}
@ -25,8 +25,8 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: | run: |
docker pull techknowlogick/xgo:latest docker pull crazymax/xgo:latest
go install src.techknowlogick.com/xgo@latest go install github.com/crazy-max/xgo@latest
sudo apt install upx sudo apt install upx
- name: Build - name: Build
@ -34,7 +34,7 @@ jobs:
bash build.sh dev bash build.sh dev
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
with: with:
name: alist name: alist
path: dist path: dist

View File

@ -10,27 +10,27 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v3
- name: Docker meta - name: Docker meta
id: meta id: meta
uses: docker/metadata-action@v3 uses: docker/metadata-action@v4
with: with:
images: xhofe/alist images: xhofe/alist
- name: Replace release with dev - name: Replace release with dev
run: | run: |
sed -i 's/release/dev/g' Dockerfile sed -i 's/release/dev/g' Dockerfile
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v1 uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1 uses: docker/setup-buildx-action@v2
- name: Login to DockerHub - name: Login to DockerHub
uses: docker/login-action@v1 uses: docker/login-action@v2
with: with:
username: xhofe username: xhofe
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push - name: Build and push
id: docker_build id: docker_build
uses: docker/build-push-action@v2 uses: docker/build-push-action@v3
with: with:
context: . context: .
push: true push: true

View File

@ -10,7 +10,7 @@ jobs:
if: github.event.label.name == 'duplicate' if: github.event.label.name == 'duplicate'
steps: steps:
- name: Create comment - name: Create comment
uses: actions-cool/issues-helper@v2 uses: actions-cool/issues-helper@v3
with: with:
actions: 'create-comment' actions: 'create-comment'
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -10,7 +10,7 @@ jobs:
if: github.event.label.name == 'invalid' if: github.event.label.name == 'invalid'
steps: steps:
- name: Create comment - name: Create comment
uses: actions-cool/issues-helper@v2 uses: actions-cool/issues-helper@v3
with: with:
actions: 'create-comment' actions: 'create-comment'
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -10,7 +10,7 @@ jobs:
if: github.event.label.name == 'question' if: github.event.label.name == 'question'
steps: steps:
- name: Create comment - name: Create comment
uses: actions-cool/issues-helper@v2.0.0 uses: actions-cool/issues-helper@v3.3.3
with: with:
actions: 'create-comment' actions: 'create-comment'
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -10,7 +10,7 @@ jobs:
if: github.event.label.name == 'wontfix' if: github.event.label.name == 'wontfix'
steps: steps:
- name: Create comment - name: Create comment
uses: actions-cool/issues-helper@v2 uses: actions-cool/issues-helper@v3
with: with:
actions: 'create-comment' actions: 'create-comment'
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0
- run: npx changelogithub # or changelogithub@0.12 if ensure the stable result - run: npx changelogithub # or changelogithub@0.12 if ensure the stable result
@ -27,19 +27,19 @@ jobs:
runs-on: ${{ matrix.platform }} runs-on: ${{ matrix.platform }}
steps: steps:
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v2 uses: actions/setup-go@v3
with: with:
go-version: ${{ matrix.go-version }} go-version: ${{ matrix.go-version }}
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Install dependencies - name: Install dependencies
run: | run: |
docker pull techknowlogick/xgo:latest docker pull crazymax/xgo:latest
go install src.techknowlogick.com/xgo@latest go install github.com/crazy-max/xgo@latest
sudo apt install upx sudo apt install upx
- name: Build - name: Build

View File

@ -11,29 +11,29 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v3
- name: Docker meta - name: Docker meta
id: meta id: meta
uses: docker/metadata-action@v3 uses: docker/metadata-action@v4
with: with:
images: xhofe/alist images: xhofe/alist
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v1 uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1 uses: docker/setup-buildx-action@v2
- name: Login to DockerHub - name: Login to DockerHub
uses: docker/login-action@v1 uses: docker/login-action@v2
with: with:
username: xhofe username: xhofe
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push - name: Build and push
id: docker_build id: docker_build
uses: docker/build-push-action@v2 uses: docker/build-push-action@v3
with: with:
context: . context: .
push: true push: true

14
.gitignore vendored
View File

@ -20,10 +20,12 @@ output/
# Dependency directories (remove the comment below to include it) # Dependency directories (remove the comment below to include it)
# vendor/ # vendor/
bin/* /bin/*
*.json *.json
data/ /build
log/ /data/
lang/ /log/
public/dist/* /lang/
!public/dist/README.md /daemon/
/public/dist/*
/!public/dist/README.md

View File

@ -1,33 +0,0 @@
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
[![All Contributors](https://img.shields.io/badge/all_contributors-9-orange.svg?style=flat-square)](#contributors-)
<!-- ALL-CONTRIBUTORS-BADGE:END -->
## Contributors ✨
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
<tr>
<td align="center"><a href="http://nn.ci"><img src="https://avatars.githubusercontent.com/u/36558727?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Xhofe</b></sub></a><br /><a href="https://github.com/alist-org/alist/commits?author=Xhofe" title="Code">💻</a> <a href="#ideas-Xhofe" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/alist-org/alist/commits?author=Xhofe" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/foxxorcat"><img src="https://avatars.githubusercontent.com/u/95907542?v=4?s=100" width="100px;" alt=""/><br /><sub><b>foxxorcat</b></sub></a><br /><a href="https://github.com/alist-org/alist/commits?author=foxxorcat" title="Code">💻</a></td>
<td align="center"><a href="https://www.iflu.cf/"><img src="https://avatars.githubusercontent.com/u/63903027?v=4?s=100" width="100px;" alt=""/><br /><sub><b>道辰</b></sub></a><br /><a href="https://github.com/alist-org/alist/commits?author=DaoChen6" title="Documentation">📖</a></td>
<td align="center"><a href="https://vg-land.github.io/"><img src="https://avatars.githubusercontent.com/u/16739728?v=4?s=100" width="100px;" alt=""/><br /><sub><b>vg-land</b></sub></a><br /><a href="https://github.com/alist-org/alist/commits?author=vg-land" title="Code">💻</a></td>
<td align="center"><a href="https://c5y.moe"><img src="https://avatars.githubusercontent.com/u/18461360?v=4?s=100" width="100px;" alt=""/><br /><sub><b>凌莞~(=^▽^=)</b></sub></a><br /><a href="https://github.com/alist-org/alist/commits?author=Clansty" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/Windman1320"><img src="https://avatars.githubusercontent.com/u/9999486?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Windman</b></sub></a><br /><a href="https://github.com/alist-org/alist/commits?author=Windman1320" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/ericarena"><img src="https://avatars.githubusercontent.com/u/4518927?v=4?s=100" width="100px;" alt=""/><br /><sub><b>ericarena</b></sub></a><br /><a href="https://github.com/alist-org/alist/commits?author=ericarena" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/WntFlm"><img src="https://avatars.githubusercontent.com/u/34620278?v=4?s=100" width="100px;" alt=""/><br /><sub><b>WntFlm</b></sub></a><br /><a href="https://github.com/alist-org/alist/commits?author=WntFlm" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/XZB-1248"><img src="https://avatars.githubusercontent.com/u/28593573?v=4?s=100" width="100px;" alt=""/><br /><sub><b>XZB-1248</b></sub></a><br /><a href="https://github.com/alist-org/alist/commits?author=XZB-1248" title="Code">💻</a></td>
</tr>
</table>
<!-- markdownlint-restore -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!

View File

@ -10,6 +10,9 @@ LABEL MAINTAINER="i@nn.ci"
VOLUME /opt/alist/data/ VOLUME /opt/alist/data/
WORKDIR /opt/alist/ WORKDIR /opt/alist/
COPY --from=builder /app/bin/alist ./ COPY --from=builder /app/bin/alist ./
RUN apk add ca-certificates COPY entrypoint.sh /entrypoint.sh
RUN apk add ca-certificates bash su-exec; \
chmod +x /entrypoint.sh
ENV PUID=0 PGID=0 UMASK=022
EXPOSE 5244 EXPOSE 5244
CMD [ "./alist", "server", "--no-prefix" ] ENTRYPOINT [ "/entrypoint.sh" ]

View File

@ -9,7 +9,7 @@
<img src="https://img.shields.io/github/license/Xhofe/alist" alt="License" /> <img src="https://img.shields.io/github/license/Xhofe/alist" alt="License" />
</a> </a>
<a href="https://github.com/Xhofe/alist/actions?query=workflow%3ABuild"> <a href="https://github.com/Xhofe/alist/actions?query=workflow%3ABuild">
<img src="https://img.shields.io/github/workflow/status/Xhofe/alist/build" alt="Build status" /> <img src="https://img.shields.io/github/actions/workflow/status/Xhofe/alist/build.yml?branch=main" alt="Build status" />
</a> </a>
<a href="https://github.com/Xhofe/alist/releases"> <a href="https://github.com/Xhofe/alist/releases">
<img src="https://img.shields.io/github/release/Xhofe/alist" alt="latest version" /> <img src="https://img.shields.io/github/release/Xhofe/alist" alt="latest version" />
@ -28,10 +28,10 @@
<a href="https://github.com/Xhofe/alist/releases"> <a href="https://github.com/Xhofe/alist/releases">
<img src="https://img.shields.io/github/downloads/Xhofe/alist/total?color=%239F7AEA&logo=github" alt="Downloads" /> <img src="https://img.shields.io/github/downloads/Xhofe/alist/total?color=%239F7AEA&logo=github" alt="Downloads" />
</a> </a>
<a href="https://github.com/Xhofe/alist/releases"> <a href="https://hub.docker.com/r/xhofe/alist">
<img src="https://img.shields.io/docker/pulls/xhofe/alist?color=%2348BB78&logo=docker&label=pulls" alt="Downloads" /> <img src="https://img.shields.io/docker/pulls/xhofe/alist?color=%2348BB78&logo=docker&label=pulls" alt="Downloads" />
</a> </a>
<a href="https://alist.nn.ci/zh/guide/sponsor.html"> <a href="https://alist.nn.ci/guide/sponsor.html">
<img src="https://img.shields.io/badge/%24-sponsor-F87171.svg" alt="sponsor" /> <img src="https://img.shields.io/badge/%24-sponsor-F87171.svg" alt="sponsor" />
</a> </a>
</div> </div>
@ -39,7 +39,7 @@
--- ---
English | [中文](./README_cn.md) | [Contributors](./CONTRIBUTORS.md) | [Contributing](./CONTRIBUTING.md) | [CODE_OF_CONDUCT](./CODE_OF_CONDUCT.md) English | [中文](./README_cn.md) | [Contributing](./CONTRIBUTING.md) | [CODE_OF_CONDUCT](./CODE_OF_CONDUCT.md)
## Features ## Features
@ -67,6 +67,8 @@ English | [中文](./README_cn.md) | [Contributors](./CONTRIBUTORS.md) | [Contri
- [x] [Google photo](https://photos.google.com/) - [x] [Google photo](https://photos.google.com/)
- [x] [Mega.nz](https://mega.nz) - [x] [Mega.nz](https://mega.nz)
- [x] [Baidu photo](https://photo.baidu.com/) - [x] [Baidu photo](https://photo.baidu.com/)
- [x] SMB
- [x] [115](https://115.com/)
- [x] Easy to deploy and out-of-the-box - [x] Easy to deploy and out-of-the-box
- [x] File preview (PDF, markdown, code, plain text, ...) - [x] File preview (PDF, markdown, code, plain text, ...)
- [x] Image preview in gallery mode - [x] Image preview in gallery mode
@ -91,17 +93,29 @@ English | [中文](./README_cn.md) | [Contributors](./CONTRIBUTORS.md) | [Contri
## Demo ## Demo
<https://pan.nn.ci> <https://al.nn.ci>
## Discussion ## Discussion
Please go to our [discussion forum](https://github.com/Xhofe/alist/discussions) for general questions, **issues are for bug reports and feature request only.** Please go to our [discussion forum](https://github.com/Xhofe/alist/discussions) for general questions, **issues are for bug reports and feature request only.**
## Special sponsors ## Sponsor
AList is an open-source software, if you happen to like this project and want me to keep going, please consider sponsoring me or providing a single donation! Thanks for all the love and support:
https://alist.nn.ci/guide/sponsor.html
### Special sponsors
- [找资源 - 阿里云盘资源搜索引擎](https://zhaoziyuan.la/) - [找资源 - 阿里云盘资源搜索引擎](https://zhaoziyuan.la/)
- [KinhDown 百度云盘不限速下载永久免费已稳定运行3年非常可靠!](https://kinhdown.com/?Type=Tutorials) - [KinhDown 百度云盘不限速下载永久免费已稳定运行3年非常可靠Q群 -> 786799372](https://kinhdown.com)
- [JetBrains: Essential tools for software developers and teams](https://www.jetbrains.com/) - [JetBrains: Essential tools for software developers and teams](https://www.jetbrains.com/)
## Contributors
Thanks goes to these wonderful people:
[![Contributors](http://contributors.nn.ci/api?repo=alist-org/alist&repo=alist-org/alist-web&repo=alist-org/docs)](https://github.com/alist-org/alist/graphs/contributors)
## License ## License
The `AList` is open-source software licensed under the AGPL-3.0 license. The `AList` is open-source software licensed under the AGPL-3.0 license.
@ -115,4 +129,4 @@ The `AList` is open-source software licensed under the AGPL-3.0 license.
--- ---
> [@Blog](https://nn.ci/) · [@GitHub](https://github.com/Xhofe) · [@TelegramGroup](https://t.me/alist_chat) · [@Discord](https://discord.gg/F4ymsH4xv2) > [@Blog](https://nn.ci/) · [@GitHub](https://github.com/Xhofe) · [@TelegramGroup](https://t.me/alist_chat) · [@Discord](https://discord.gg/F4ymsH4xv2)

View File

@ -9,7 +9,7 @@
<img src="https://img.shields.io/github/license/Xhofe/alist" alt="License" /> <img src="https://img.shields.io/github/license/Xhofe/alist" alt="License" />
</a> </a>
<a href="https://github.com/Xhofe/alist/actions?query=workflow%3ABuild"> <a href="https://github.com/Xhofe/alist/actions?query=workflow%3ABuild">
<img src="https://img.shields.io/github/workflow/status/Xhofe/alist/build" alt="Build status" /> <img src="https://img.shields.io/github/actions/workflow/status/Xhofe/alist/build.yml?branch=main" alt="Build status" />
</a> </a>
<a href="https://github.com/Xhofe/alist/releases"> <a href="https://github.com/Xhofe/alist/releases">
<img src="https://img.shields.io/github/release/Xhofe/alist" alt="latest version" /> <img src="https://img.shields.io/github/release/Xhofe/alist" alt="latest version" />
@ -28,7 +28,7 @@
<a href="https://github.com/Xhofe/alist/releases"> <a href="https://github.com/Xhofe/alist/releases">
<img src="https://img.shields.io/github/downloads/Xhofe/alist/total?color=%239F7AEA&logo=github" alt="Downloads" /> <img src="https://img.shields.io/github/downloads/Xhofe/alist/total?color=%239F7AEA&logo=github" alt="Downloads" />
</a> </a>
<a href="https://github.com/Xhofe/alist/releases"> <a href="https://hub.docker.com/r/xhofe/alist">
<img src="https://img.shields.io/docker/pulls/xhofe/alist?color=%2348BB78&logo=docker&label=pulls" alt="Downloads" /> <img src="https://img.shields.io/docker/pulls/xhofe/alist?color=%2348BB78&logo=docker&label=pulls" alt="Downloads" />
</a> </a>
<a href="https://alist.nn.ci/zh/guide/sponsor.html"> <a href="https://alist.nn.ci/zh/guide/sponsor.html">
@ -39,7 +39,7 @@
--- ---
[English](./README.md) | 中文 | [Contributors](./CONTRIBUTORS.md) | [Contributing](./CONTRIBUTING.md) | [CODE_OF_CONDUCT](./CODE_OF_CONDUCT.md) [English](./README.md) | 中文 | [Contributing](./CONTRIBUTING.md) | [CODE_OF_CONDUCT](./CODE_OF_CONDUCT.md)
## Features ## Features
@ -67,6 +67,8 @@
- [x] [谷歌相册](https://photos.google.com/) - [x] [谷歌相册](https://photos.google.com/)
- [x] [Mega.nz](https://mega.nz) - [x] [Mega.nz](https://mega.nz)
- [x] [一刻相册](https://photo.baidu.com/) - [x] [一刻相册](https://photo.baidu.com/)
- [x] SMB
- [x] [115](https://115.com/)
- [x] 部署方便,开箱即用 - [x] 部署方便,开箱即用
- [x] 文件预览PDF、markdown、代码、纯文本…… - [x] 文件预览PDF、markdown、代码、纯文本……
- [x] 画廊模式下的图像预览 - [x] 画廊模式下的图像预览
@ -91,18 +93,29 @@
## Demo ## Demo
<https://pan.nn.ci> <https://al.nn.ci>
## Discussion ## Discussion
一般问题请到[讨论论坛](https://github.com/Xhofe/alist/discussions) **issue仅针对错误报告和功能请求。** 一般问题请到[讨论论坛](https://github.com/Xhofe/alist/discussions) **issue仅针对错误报告和功能请求。**
## Special sponsors ## Sponsor
AList 是一个开源软件如果你碰巧喜欢这个项目并希望我继续下去请考虑赞助我或提供一个单一的捐款感谢所有的爱和支持https://alist.nn.ci/zh/guide/sponsor.html
### Special sponsors
- [找资源 - 阿里云盘资源搜索引擎](https://zhaoziyuan.la/) - [找资源 - 阿里云盘资源搜索引擎](https://zhaoziyuan.la/)
- [KinhDown 百度云盘不限速下载永久免费已稳定运行3年非常可靠!](https://kinhdown.com/?Type=Tutorials) - [KinhDown 百度云盘不限速下载永久免费已稳定运行3年非常可靠Q群 -> 786799372](https://kinhdown.com)
- [JetBrains: Essential tools for software developers and teams](https://www.jetbrains.com/) - [JetBrains: Essential tools for software developers and teams](https://www.jetbrains.com/)
## 许可 ## Contributors
Thanks goes to these wonderful people:
[![Contributors](http://contributors.nn.ci/api?repo=alist-org/alist&repo=alist-org/alist-web&repo=alist-org/docs)](https://github.com/alist-org/alist/graphs/contributors)
## 许可
`AList` 是在 AGPL-3.0 许可下许可的开源软件。 `AList` 是在 AGPL-3.0 许可下许可的开源软件。

View File

@ -4,19 +4,19 @@ Copyright © 2022 NAME HERE <EMAIL ADDRESS>
package cmd package cmd
import ( import (
"github.com/alist-org/alist/v3/internal/db" "github.com/alist-org/alist/v3/internal/op"
"github.com/alist-org/alist/v3/pkg/utils" "github.com/alist-org/alist/v3/pkg/utils"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
// passwordCmd represents the password command // PasswordCmd represents the password command
var passwordCmd = &cobra.Command{ var PasswordCmd = &cobra.Command{
Use: "admin", Use: "admin",
Aliases: []string{"password"}, Aliases: []string{"password"},
Short: "Show admin user's info", Short: "Show admin user's info",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
Init() Init()
admin, err := db.GetAdmin() admin, err := op.GetAdmin()
if err != nil { if err != nil {
utils.Log.Errorf("failed get admin user: %+v", err) utils.Log.Errorf("failed get admin user: %+v", err)
} else { } else {
@ -26,7 +26,7 @@ var passwordCmd = &cobra.Command{
} }
func init() { func init() {
rootCmd.AddCommand(passwordCmd) RootCmd.AddCommand(PasswordCmd)
// Here you will define your flags and configuration settings. // Here you will define your flags and configuration settings.

View File

@ -4,22 +4,22 @@ Copyright © 2022 NAME HERE <EMAIL ADDRESS>
package cmd package cmd
import ( import (
"github.com/alist-org/alist/v3/internal/db" "github.com/alist-org/alist/v3/internal/op"
"github.com/alist-org/alist/v3/pkg/utils" "github.com/alist-org/alist/v3/pkg/utils"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
// cancel2FACmd represents the delete2fa command // Cancel2FACmd represents the delete2fa command
var cancel2FACmd = &cobra.Command{ var Cancel2FACmd = &cobra.Command{
Use: "cancel2fa", Use: "cancel2fa",
Short: "Delete 2FA of admin user", Short: "Delete 2FA of admin user",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
Init() Init()
admin, err := db.GetAdmin() admin, err := op.GetAdmin()
if err != nil { if err != nil {
utils.Log.Errorf("failed to get admin user: %+v", err) utils.Log.Errorf("failed to get admin user: %+v", err)
} else { } else {
err := db.Cancel2FAByUser(admin) err := op.Cancel2FAByUser(admin)
if err != nil { if err != nil {
utils.Log.Errorf("failed to cancel 2FA: %+v", err) utils.Log.Errorf("failed to cancel 2FA: %+v", err)
} }
@ -28,7 +28,7 @@ var cancel2FACmd = &cobra.Command{
} }
func init() { func init() {
rootCmd.AddCommand(cancel2FACmd) RootCmd.AddCommand(Cancel2FACmd)
// Here you will define your flags and configuration settings. // Here you will define your flags and configuration settings.

View File

@ -1,8 +1,14 @@
package cmd package cmd
import ( import (
"os"
"path/filepath"
"strconv"
"github.com/alist-org/alist/v3/internal/bootstrap" "github.com/alist-org/alist/v3/internal/bootstrap"
"github.com/alist-org/alist/v3/internal/bootstrap/data" "github.com/alist-org/alist/v3/internal/bootstrap/data"
"github.com/alist-org/alist/v3/pkg/utils"
log "github.com/sirupsen/logrus"
) )
func Init() { func Init() {
@ -10,4 +16,29 @@ func Init() {
bootstrap.Log() bootstrap.Log()
bootstrap.InitDB() bootstrap.InitDB()
data.InitData() data.InitData()
bootstrap.InitIndex()
}
var pid = -1
var pidFile string
func initDaemon() {
ex, err := os.Executable()
if err != nil {
log.Fatal(err)
}
exPath := filepath.Dir(ex)
_ = os.MkdirAll(filepath.Join(exPath, "daemon"), 0700)
pidFile = filepath.Join(exPath, "daemon/pid")
if utils.Exists(pidFile) {
bytes, err := os.ReadFile(pidFile)
if err != nil {
log.Fatal("failed to read pid file", err)
}
id, err := strconv.Atoi(string(bytes))
if err != nil {
log.Fatal("failed to parse pid data", err)
}
pid = id
}
} }

View File

@ -1,8 +1,9 @@
package flags package flags
var ( var (
Config string // config file DataDir string
Debug bool Debug bool
NoPrefix bool NoPrefix bool
Dev bool Dev bool
ForceBinDir bool
) )

View File

@ -71,7 +71,7 @@ func writeFile(name string, data interface{}) {
} else { } else {
log.Infof("%s.json changed, update file", name) log.Infof("%s.json changed, update file", name)
//log.Infof("old: %+v\nnew:%+v", oldData, data) //log.Infof("old: %+v\nnew:%+v", oldData, data)
utils.WriteJsonToFile(fmt.Sprintf("lang/%s.json", name), data) utils.WriteJsonToFile(fmt.Sprintf("lang/%s.json", name), newData, true)
} }
} }
@ -123,8 +123,8 @@ func generateSettingsJson() {
//utils.WriteJsonToFile("lang/settings.json", settingsLang) //utils.WriteJsonToFile("lang/settings.json", settingsLang)
} }
// langCmd represents the lang command // LangCmd represents the lang command
var langCmd = &cobra.Command{ var LangCmd = &cobra.Command{
Use: "lang", Use: "lang",
Short: "Generate language json file", Short: "Generate language json file",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
@ -138,7 +138,7 @@ var langCmd = &cobra.Command{
} }
func init() { func init() {
rootCmd.AddCommand(langCmd) RootCmd.AddCommand(LangCmd)
// Here you will define your flags and configuration settings. // Here you will define your flags and configuration settings.

32
cmd/restart.go Normal file
View File

@ -0,0 +1,32 @@
/*
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
*/
package cmd
import (
"github.com/spf13/cobra"
)
// RestartCmd represents the restart command
var RestartCmd = &cobra.Command{
Use: "restart",
Short: "Restart alist server by daemon/pid file",
Run: func(cmd *cobra.Command, args []string) {
stop()
start()
},
}
func init() {
RootCmd.AddCommand(RestartCmd)
// Here you will define your flags and configuration settings.
// Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.:
// restartCmd.PersistentFlags().String("foo", "", "A help for foo")
// Cobra supports local flags which will only run when this command
// is called directly, e.g.:
// restartCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

View File

@ -8,7 +8,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
var rootCmd = &cobra.Command{ var RootCmd = &cobra.Command{
Use: "alist", Use: "alist",
Short: "A file list program that supports multiple storage.", Short: "A file list program that supports multiple storage.",
Long: `A file list program that supports multiple storage, Long: `A file list program that supports multiple storage,
@ -17,15 +17,16 @@ Complete documentation is available at https://alist.nn.ci/`,
} }
func Execute() { func Execute() {
if err := rootCmd.Execute(); err != nil { if err := RootCmd.Execute(); err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
os.Exit(1) os.Exit(1)
} }
} }
func init() { func init() {
rootCmd.PersistentFlags().StringVar(&flags.Config, "conf", "data/config.json", "config file") RootCmd.PersistentFlags().StringVar(&flags.DataDir, "data", "data", "config file")
rootCmd.PersistentFlags().BoolVar(&flags.Debug, "debug", false, "start with debug mode") RootCmd.PersistentFlags().BoolVar(&flags.Debug, "debug", false, "start with debug mode")
rootCmd.PersistentFlags().BoolVar(&flags.NoPrefix, "no-prefix", false, "disable env prefix") RootCmd.PersistentFlags().BoolVar(&flags.NoPrefix, "no-prefix", false, "disable env prefix")
rootCmd.PersistentFlags().BoolVar(&flags.Dev, "dev", false, "start with dev mode") RootCmd.PersistentFlags().BoolVar(&flags.Dev, "dev", false, "start with dev mode")
RootCmd.PersistentFlags().BoolVar(&flags.ForceBinDir, "force-bin-dir", false, "Force to use the directory where the binary file is located as data directory")
} }

View File

@ -20,8 +20,8 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
// serverCmd represents the server command // ServerCmd represents the server command
var serverCmd = &cobra.Command{ var ServerCmd = &cobra.Command{
Use: "server", Use: "server",
Short: "Start the server at the specified address", Short: "Start the server at the specified address",
Long: `Start the server at the specified address Long: `Start the server at the specified address
@ -76,7 +76,7 @@ the address is defined in config file`,
} }
func init() { func init() {
rootCmd.AddCommand(serverCmd) RootCmd.AddCommand(ServerCmd)
// Here you will define your flags and configuration settings. // Here you will define your flags and configuration settings.
@ -95,5 +95,5 @@ func OutAlistInit() {
cmd *cobra.Command cmd *cobra.Command
args []string args []string
) )
serverCmd.Run(cmd, args) ServerCmd.Run(cmd, args)
} }

71
cmd/start.go Normal file
View File

@ -0,0 +1,71 @@
/*
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
*/
package cmd
import (
"os"
"os/exec"
"path/filepath"
"strconv"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
// StartCmd represents the start command
var StartCmd = &cobra.Command{
Use: "start",
Short: "Silent start alist server with `--force-bin-dir`",
Run: func(cmd *cobra.Command, args []string) {
start()
},
}
func start() {
initDaemon()
if pid != -1 {
_, err := os.FindProcess(pid)
if err == nil {
log.Info("alist already started, pid ", pid)
return
}
}
args := os.Args
args[1] = "server"
args = append(args, "--force-bin-dir")
cmd := &exec.Cmd{
Path: args[0],
Args: args,
Env: os.Environ(),
}
stdout, err := os.OpenFile(filepath.Join(filepath.Dir(pidFile), "start.log"), os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
if err != nil {
log.Fatal(os.Getpid(), ": failed to open start log file:", err)
}
cmd.Stderr = stdout
cmd.Stdout = stdout
err = cmd.Start()
if err != nil {
log.Fatal("failed to start children process: ", err)
}
log.Infof("success start pid: %d", cmd.Process.Pid)
err = os.WriteFile(pidFile, []byte(strconv.Itoa(cmd.Process.Pid)), 0666)
if err != nil {
log.Warn("failed to record pid, you may not be able to stop the program with `./alist stop`")
}
}
func init() {
RootCmd.AddCommand(StartCmd)
// Here you will define your flags and configuration settings.
// Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.:
// startCmd.PersistentFlags().String("foo", "", "A help for foo")
// Cobra supports local flags which will only run when this command
// is called directly, e.g.:
// startCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

58
cmd/stop.go Normal file
View File

@ -0,0 +1,58 @@
/*
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
*/
package cmd
import (
"os"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
// StopCmd represents the stop command
var StopCmd = &cobra.Command{
Use: "stop",
Short: "Stop alist server by daemon/pid file",
Run: func(cmd *cobra.Command, args []string) {
stop()
},
}
func stop() {
initDaemon()
if pid == -1 {
log.Info("Seems not have been started. Try use `alist start` to start server.")
return
}
process, err := os.FindProcess(pid)
if err != nil {
log.Errorf("failed to find process by pid: %d, reason: %v", pid, process)
return
}
err = process.Kill()
if err != nil {
log.Errorf("failed to kill process %d: %v", pid, err)
} else {
log.Info("killed process: ", pid)
}
err = os.Remove(pidFile)
if err != nil {
log.Errorf("failed to remove pid file")
}
pid = -1
}
func init() {
RootCmd.AddCommand(StopCmd)
// Here you will define your flags and configuration settings.
// Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.:
// stopCmd.PersistentFlags().String("foo", "", "A help for foo")
// Cobra supports local flags which will only run when this command
// is called directly, e.g.:
// stopCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

View File

@ -1,6 +1,5 @@
/* /*
Copyright © 2022 NAME HERE <EMAIL ADDRESS> Copyright © 2022 NAME HERE <EMAIL ADDRESS>
*/ */
package cmd package cmd
@ -12,8 +11,8 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
// versionCmd represents the version command // VersionCmd represents the version command
var versionCmd = &cobra.Command{ var VersionCmd = &cobra.Command{
Use: "version", Use: "version",
Short: "Show current version of AList", Short: "Show current version of AList",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
@ -30,7 +29,7 @@ WebVersion: %s
} }
func init() { func init() {
rootCmd.AddCommand(versionCmd) RootCmd.AddCommand(VersionCmd)
// Here you will define your flags and configuration settings. // Here you will define your flags and configuration settings.

14
docker-compose.yml Normal file
View File

@ -0,0 +1,14 @@
version: '3.3'
services:
alist:
restart: always
volumes:
- '/etc/alist:/opt/alist/data'
ports:
- '5244:5244'
environment:
- PUID=0
- PGID=0
- UMASK=022
container_name: alist
image: 'xhofe/alist:latest'

93
drivers/115/driver.go Normal file
View File

@ -0,0 +1,93 @@
package _115
import (
"context"
"os"
driver115 "github.com/SheltonZhu/115driver/pkg/driver"
"github.com/alist-org/alist/v3/internal/driver"
"github.com/alist-org/alist/v3/internal/model"
"github.com/alist-org/alist/v3/pkg/utils"
"github.com/pkg/errors"
)
type Pan115 struct {
model.Storage
Addition
client *driver115.Pan115Client
}
func (d *Pan115) Config() driver.Config {
return config
}
func (d *Pan115) GetAddition() driver.Additional {
return &d.Addition
}
func (d *Pan115) Init(ctx context.Context) error {
return d.login()
}
func (d *Pan115) Drop(ctx context.Context) error {
return nil
}
func (d *Pan115) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
files, err := d.getFiles(dir.GetID())
if err != nil && !errors.Is(err, driver115.ErrNotExist) {
return nil, err
}
return utils.SliceConvert(files, func(src driver115.File) (model.Obj, error) {
return src, nil
})
}
func (d *Pan115) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
downloadInfo, err := d.client.Download(file.(driver115.File).PickCode)
if err != nil {
return nil, err
}
link := &model.Link{
URL: downloadInfo.Url.Url,
Header: downloadInfo.Header,
}
return link, nil
}
func (d *Pan115) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
if _, err := d.client.Mkdir(parentDir.GetID(), dirName); err != nil {
return err
}
return nil
}
func (d *Pan115) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
return d.client.Move(dstDir.GetID(), srcObj.GetID())
}
func (d *Pan115) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
return d.client.Rename(srcObj.GetID(), newName)
}
func (d *Pan115) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
return d.client.Copy(dstDir.GetID(), srcObj.GetID())
}
func (d *Pan115) Remove(ctx context.Context, obj model.Obj) error {
return d.client.Delete(obj.GetID())
}
func (d *Pan115) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
tempFile, err := utils.CreateTempFile(stream.GetReadCloser())
if err != nil {
return err
}
defer func() {
_ = tempFile.Close()
_ = os.Remove(tempFile.Name())
}()
return d.client.UploadFastOrByMultipart(dstDir.GetID(), stream.GetName(), stream.GetSize(), tempFile)
}
var _ driver.Driver = (*Pan115)(nil)

25
drivers/115/meta.go Normal file
View File

@ -0,0 +1,25 @@
package _115
import (
"github.com/alist-org/alist/v3/internal/driver"
"github.com/alist-org/alist/v3/internal/op"
)
type Addition struct {
Cookie string `json:"cookie"`
QRCodeToken string `json:"qrcode_token"`
driver.RootID
}
var config = driver.Config{
Name: "115 Cloud",
DefaultRoot: "0",
OnlyProxy: true,
OnlyLocal: true,
}
func init() {
op.RegisterDriver(func() driver.Driver {
return &Pan115{}
})
}

8
drivers/115/types.go Normal file
View File

@ -0,0 +1,8 @@
package _115
import (
"github.com/SheltonZhu/115driver/pkg/driver"
"github.com/alist-org/alist/v3/internal/model"
)
var _ model.Obj = (*driver.File)(nil)

49
drivers/115/util.go Normal file
View File

@ -0,0 +1,49 @@
package _115
import (
"fmt"
"github.com/SheltonZhu/115driver/pkg/driver"
"github.com/pkg/errors"
)
var UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36 115Browser/23.9.3.2 115disk/30.1.0"
func (d *Pan115) login() error {
var err error
opts := []driver.Option{
driver.UA(UserAgent),
}
d.client = driver.New(opts...)
cr := &driver.Credential{}
if d.Addition.QRCodeToken != "" {
s := &driver.QRCodeSession{
UID: d.Addition.QRCodeToken,
}
if cr, err = d.client.QRCodeLogin(s); err != nil {
return errors.Wrap(err, "failed to login by qrcode")
}
d.Addition.Cookie = fmt.Sprintf("UID=%s;CID=%s;SEID=%s", cr.UID, cr.CID, cr.SEID)
d.Addition.QRCodeToken = ""
} else if d.Addition.Cookie != "" {
if err = cr.FromCookie(d.Addition.Cookie); err != nil {
return errors.Wrap(err, "failed to login by cookies")
}
d.client.ImportCredential(cr)
} else {
return errors.New("missing cookie or qrcode account")
}
return d.client.LoginCheck()
}
func (d *Pan115) getFiles(fileId string) ([]driver.File, error) {
res := make([]driver.File, 0)
files, err := d.client.List(fileId)
if err != nil {
return nil, err
}
for _, file := range *files {
res = append(res, file)
}
return res, nil
}

View File

@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"context" "context"
"crypto/md5" "crypto/md5"
"encoding/base64"
"encoding/binary" "encoding/binary"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
@ -36,15 +37,10 @@ func (d *Pan123) Config() driver.Config {
} }
func (d *Pan123) GetAddition() driver.Additional { func (d *Pan123) GetAddition() driver.Additional {
return d.Addition return &d.Addition
} }
func (d *Pan123) Init(ctx context.Context, storage model.Storage) error { func (d *Pan123) Init(ctx context.Context) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
return d.login() return d.login()
} }
@ -62,14 +58,9 @@ func (d *Pan123) List(ctx context.Context, dir model.Obj, args model.ListArgs) (
}) })
} }
//func (d *Pan123) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *Pan123) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { func (d *Pan123) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
if f, ok := file.(File); ok { if f, ok := file.(File); ok {
var resp DownResp //var resp DownResp
var headers map[string]string var headers map[string]string
if !utils.IsLocalIPAddr(args.IP) { if !utils.IsLocalIPAddr(args.IP) {
headers = map[string]string{ headers = map[string]string{
@ -86,16 +77,25 @@ func (d *Pan123) Link(ctx context.Context, file model.Obj, args model.LinkArgs)
"size": f.Size, "size": f.Size,
"type": f.Type, "type": f.Type,
} }
_, err := d.request("https://www.123pan.com/api/file/download_info", http.MethodPost, func(req *resty.Request) { resp, err := d.request("https://www.123pan.com/api/file/download_info", http.MethodPost, func(req *resty.Request) {
req.SetBody(data).SetHeaders(headers) req.SetBody(data).SetHeaders(headers)
}, &resp) }, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
u, err := url.Parse(resp.Data.DownloadUrl) downloadUrl := utils.Json.Get(resp, "data", "DownloadUrl").ToString()
u, err := url.Parse(downloadUrl)
if err != nil { if err != nil {
return nil, err return nil, err
} }
nu := u.Query().Get("params")
if nu != "" {
du, _ := base64.StdEncoding.DecodeString(nu)
u, err = url.Parse(string(du))
if err != nil {
return nil, err
}
}
u_ := fmt.Sprintf("https://%s%s", u.Host, u.Path) u_ := fmt.Sprintf("https://%s%s", u.Host, u.Path)
res, err := base.NoRedirectClient.R().SetQueryParamsFromValues(u.Query()).Head(u_) res, err := base.NoRedirectClient.R().SetQueryParamsFromValues(u.Query()).Head(u_)
if err != nil { if err != nil {
@ -103,7 +103,7 @@ func (d *Pan123) Link(ctx context.Context, file model.Obj, args model.LinkArgs)
} }
log.Debug(res.String()) log.Debug(res.String())
link := model.Link{ link := model.Link{
URL: resp.Data.DownloadUrl, URL: downloadUrl,
} }
log.Debugln("res code: ", res.StatusCode()) log.Debugln("res code: ", res.StatusCode())
if res.StatusCode() == 302 { if res.StatusCode() == 302 {
@ -220,13 +220,13 @@ func (d *Pan123) Put(ctx context.Context, dstDir model.Obj, stream model.FileStr
"type": 0, "type": 0,
} }
var resp UploadResp var resp UploadResp
_, err := d.request("https://www.123pan.com/api/file/upload_request", http.MethodPost, func(req *resty.Request) { _, err := d.request("https://www.123pan.com/a/api/file/upload_request", http.MethodPost, func(req *resty.Request) {
req.SetBody(data) req.SetBody(data).SetContext(ctx)
}, &resp) }, &resp)
if err != nil { if err != nil {
return err return err
} }
if resp.Data.Key == "" { if resp.Data.Reuse || resp.Data.Key == "" {
return nil return nil
} }
cfg := &aws.Config{ cfg := &aws.Config{
@ -245,14 +245,14 @@ func (d *Pan123) Put(ctx context.Context, dstDir model.Obj, stream model.FileStr
Key: &resp.Data.Key, Key: &resp.Data.Key,
Body: uploadFile, Body: uploadFile,
} }
_, err = uploader.Upload(input) _, err = uploader.UploadWithContext(ctx, input)
if err != nil { if err != nil {
return err return err
} }
_, err = d.request("https://www.123pan.com/api/file/upload_complete", http.MethodPost, func(req *resty.Request) { _, err = d.request("https://www.123pan.com/api/file/upload_complete", http.MethodPost, func(req *resty.Request) {
req.SetBody(base.Json{ req.SetBody(base.Json{
"fileId": resp.Data.FileId, "fileId": resp.Data.FileId,
}) }).SetContext(ctx)
}, nil) }, nil)
return err return err
} }

View File

@ -8,7 +8,7 @@ import (
type Addition struct { type Addition struct {
Username string `json:"username" required:"true"` Username string `json:"username" required:"true"`
Password string `json:"password" required:"true"` Password string `json:"password" required:"true"`
OrderBy string `json:"order_by" type:"select" options:"name,fileId,updateAt,createAt" default:"name"` OrderBy string `json:"order_by" type:"select" options:"file_name,size,update_at" default:"file_name"`
OrderDirection string `json:"order_direction" type:"select" options:"asc,desc" default:"asc"` OrderDirection string `json:"order_direction" type:"select" options:"asc,desc" default:"asc"`
driver.RootID driver.RootID
// define other // define other
@ -21,10 +21,8 @@ var config = driver.Config{
DefaultRoot: "0", DefaultRoot: "0",
} }
func New() driver.Driver {
return &Pan123{}
}
func init() { func init() {
op.RegisterDriver(config, New) op.RegisterDriver(func() driver.Driver {
return &Pan123{}
})
} }

View File

@ -7,13 +7,13 @@ import (
"github.com/alist-org/alist/v3/internal/model" "github.com/alist-org/alist/v3/internal/model"
) )
type BaseResp struct { //type BaseResp struct {
Code int `json:"code"` // Code interface{} `json:"code"`
Message string `json:"message"` // Message string `json:"message"`
} //}
type TokenResp struct { type TokenResp struct {
BaseResp //BaseResp
Data struct { Data struct {
Token string `json:"token"` Token string `json:"token"`
} `json:"data"` } `json:"data"`
@ -62,22 +62,22 @@ var _ model.Obj = (*File)(nil)
//var _ model.Thumb = (*File)(nil) //var _ model.Thumb = (*File)(nil)
type Files struct { type Files struct {
BaseResp //BaseResp
Data struct { Data struct {
InfoList []File `json:"InfoList"` InfoList []File `json:"InfoList"`
Next string `json:"Next"` Next string `json:"Next"`
} `json:"data"` } `json:"data"`
} }
type DownResp struct { //type DownResp struct {
BaseResp // //BaseResp
Data struct { // Data struct {
DownloadUrl string `json:"DownloadUrl"` // DownloadUrl string `json:"DownloadUrl"`
} `json:"data"` // } `json:"data"`
} //}
type UploadResp struct { type UploadResp struct {
BaseResp //BaseResp
Data struct { Data struct {
AccessKeyId string `json:"AccessKeyId"` AccessKeyId string `json:"AccessKeyId"`
Bucket string `json:"Bucket"` Bucket string `json:"Bucket"`
@ -85,5 +85,6 @@ type UploadResp struct {
SecretAccessKey string `json:"SecretAccessKey"` SecretAccessKey string `json:"SecretAccessKey"`
SessionToken string `json:"SessionToken"` SessionToken string `json:"SessionToken"`
FileId int64 `json:"FileId"` FileId int64 `json:"FileId"`
Reuse bool `json:"Reuse"`
} `json:"data"` } `json:"data"`
} }

View File

@ -6,6 +6,7 @@ import (
"net/http" "net/http"
"github.com/alist-org/alist/v3/drivers/base" "github.com/alist-org/alist/v3/drivers/base"
"github.com/alist-org/alist/v3/pkg/utils"
"github.com/go-resty/resty/v2" "github.com/go-resty/resty/v2"
jsoniter "github.com/json-iterator/go" jsoniter "github.com/json-iterator/go"
) )
@ -13,19 +14,29 @@ import (
// do others that not defined in Driver interface // do others that not defined in Driver interface
func (d *Pan123) login() error { func (d *Pan123) login() error {
url := "https://www.123pan.com/api/user/sign_in" var body base.Json
var resp TokenResp url := "https://www.123pan.com/a/api/user/sign_in"
_, err := base.RestyClient.R(). if utils.IsEmailFormat(d.Username) {
SetResult(&resp). body = base.Json{
SetBody(base.Json{ "mail": d.Username,
"password": d.Password,
"type": 2,
}
} else {
body = base.Json{
"passport": d.Username, "passport": d.Username,
"password": d.Password, "password": d.Password,
}).Post(url) }
}
var resp TokenResp
res, err := base.RestyClient.R().
SetResult(&resp).
SetBody(body).Post(url)
if err != nil { if err != nil {
return err return err
} }
if resp.Code != 200 { if utils.Json.Get(res.Body(), "code").ToInt() != 200 {
err = fmt.Errorf(resp.Message) err = fmt.Errorf(utils.Json.Get(res.Body(), "message").ToString())
} else { } else {
d.AccessToken = resp.Data.Token d.AccessToken = resp.Data.Token
} }
@ -34,7 +45,12 @@ func (d *Pan123) login() error {
func (d *Pan123) request(url string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) { func (d *Pan123) request(url string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
req := base.RestyClient.R() req := base.RestyClient.R()
req.SetHeader("Authorization", "Bearer "+d.AccessToken) req.SetHeaders(map[string]string{
"origin": "https://www.123pan.com",
"authorization": "Bearer " + d.AccessToken,
"platform": "web",
"app-version": "1.2",
})
if callback != nil { if callback != nil {
callback(req) callback(req)
} }
@ -46,7 +62,7 @@ func (d *Pan123) request(url string, method string, callback base.ReqCallback, r
return nil, err return nil, err
} }
body := res.Body() body := res.Body()
code := jsoniter.Get(body, "code").ToInt() code := utils.Json.Get(body, "code").ToInt()
if code != 0 { if code != 0 {
if code == 401 { if code == 401 {
err := d.login() err := d.login()

View File

@ -27,16 +27,11 @@ func (d *Yun139) Config() driver.Config {
} }
func (d *Yun139) GetAddition() driver.Additional { func (d *Yun139) GetAddition() driver.Additional {
return d.Addition return &d.Addition
} }
func (d *Yun139) Init(ctx context.Context, storage model.Storage) error { func (d *Yun139) Init(ctx context.Context) error {
d.Storage = storage _, err := d.post("/orchestration/personalCloud/user/v1.0/qryUserExternInfo", base.Json{
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
_, err = d.post("/orchestration/personalCloud/user/v1.0/qryUserExternInfo", base.Json{
"qryUserExternInfoReq": base.Json{ "qryUserExternInfoReq": base.Json{
"commonAccountInfo": base.Json{ "commonAccountInfo": base.Json{
"account": d.Account, "account": d.Account,
@ -59,11 +54,6 @@ func (d *Yun139) List(ctx context.Context, dir model.Obj, args model.ListArgs) (
} }
} }
//func (d *Yun139) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *Yun139) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { func (d *Yun139) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
u, err := d.getLink(file.GetID()) u, err := d.getLink(file.GetID())
if err != nil { if err != nil {
@ -275,10 +265,13 @@ func (d *Yun139) Put(ctx context.Context, dstDir model.Obj, stream model.FileStr
if err != nil { if err != nil {
return err return err
} }
var Default int64 = 10485760 var Default int64 = 104857600
part := int(math.Ceil(float64(stream.GetSize()) / float64(Default))) part := int(math.Ceil(float64(stream.GetSize()) / float64(Default)))
var start int64 = 0 var start int64 = 0
for i := 0; i < part; i++ { for i := 0; i < part; i++ {
if utils.IsCanceled(ctx) {
return ctx.Err()
}
byteSize := stream.GetSize() - start byteSize := stream.GetSize() - start
if byteSize > Default { if byteSize > Default {
byteSize = Default byteSize = Default
@ -292,6 +285,7 @@ func (d *Yun139) Put(ctx context.Context, dstDir model.Obj, stream model.FileStr
if err != nil { if err != nil {
return err return err
} }
req = req.WithContext(ctx)
headers := map[string]string{ headers := map[string]string{
"Accept": "*/*", "Accept": "*/*",
"Content-Type": "text/plain;name=" + unicode(stream.GetName()), "Content-Type": "text/plain;name=" + unicode(stream.GetName()),

View File

@ -19,7 +19,7 @@ var config = driver.Config{
} }
func init() { func init() {
op.RegisterDriver(config, func() driver.Driver { op.RegisterDriver(func() driver.Driver {
return &Yun139{} return &Yun139{}
}) })
} }

View File

@ -26,15 +26,10 @@ func (d *Cloud189) Config() driver.Config {
} }
func (d *Cloud189) GetAddition() driver.Additional { func (d *Cloud189) GetAddition() driver.Additional {
return d.Addition return &d.Addition
} }
func (d *Cloud189) Init(ctx context.Context, storage model.Storage) error { func (d *Cloud189) Init(ctx context.Context) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
d.client = resty.New(). d.client = resty.New().
SetTimeout(base.DefaultTimeout). SetTimeout(base.DefaultTimeout).
SetRetryCount(3). SetRetryCount(3).
@ -51,11 +46,6 @@ func (d *Cloud189) List(ctx context.Context, dir model.Obj, args model.ListArgs)
return d.getFiles(dir.GetID()) return d.getFiles(dir.GetID())
} }
//func (d *Cloud189) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *Cloud189) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { func (d *Cloud189) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
var resp DownResp var resp DownResp
u := "https://cloud.189.cn/api/portal/getFileInfo.action" u := "https://cloud.189.cn/api/portal/getFileInfo.action"
@ -204,7 +194,7 @@ func (d *Cloud189) Remove(ctx context.Context, obj model.Obj) error {
} }
func (d *Cloud189) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error { func (d *Cloud189) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
return d.newUpload(dstDir, stream, up) return d.newUpload(ctx, dstDir, stream, up)
} }
var _ driver.Driver = (*Cloud189)(nil) var _ driver.Driver = (*Cloud189)(nil)

View File

@ -18,7 +18,7 @@ var config = driver.Config{
} }
func init() { func init() {
op.RegisterDriver(config, func() driver.Driver { op.RegisterDriver(func() driver.Driver {
return &Cloud189{} return &Cloud189{}
}) })
} }

View File

@ -2,6 +2,7 @@ package _189
import ( import (
"bytes" "bytes"
"context"
"crypto/md5" "crypto/md5"
"encoding/base64" "encoding/base64"
"encoding/hex" "encoding/hex"
@ -178,7 +179,6 @@ func (d *Cloud189) request(url string, method string, callback base.ReqCallback,
func (d *Cloud189) getFiles(fileId string) ([]model.Obj, error) { func (d *Cloud189) getFiles(fileId string) ([]model.Obj, error) {
res := make([]model.Obj, 0) res := make([]model.Obj, 0)
pageNum := 1 pageNum := 1
loc, _ := time.LoadLocation("Local")
for { for {
var resp Files var resp Files
_, err := d.request("https://cloud.189.cn/api/open/file/listFiles.action", http.MethodGet, func(req *resty.Request) { _, err := d.request("https://cloud.189.cn/api/open/file/listFiles.action", http.MethodGet, func(req *resty.Request) {
@ -200,7 +200,7 @@ func (d *Cloud189) getFiles(fileId string) ([]model.Obj, error) {
break break
} }
for _, folder := range resp.FileListAO.FolderList { for _, folder := range resp.FileListAO.FolderList {
lastOpTime, _ := time.ParseInLocation("2006-01-02 15:04:05", folder.LastOpTime, loc) lastOpTime := utils.MustParseCNTime(folder.LastOpTime)
res = append(res, &model.Object{ res = append(res, &model.Object{
ID: strconv.FormatInt(folder.Id, 10), ID: strconv.FormatInt(folder.Id, 10),
Name: folder.Name, Name: folder.Name,
@ -209,7 +209,7 @@ func (d *Cloud189) getFiles(fileId string) ([]model.Obj, error) {
}) })
} }
for _, file := range resp.FileListAO.FileList { for _, file := range resp.FileListAO.FileList {
lastOpTime, _ := time.ParseInLocation("2006-01-02 15:04:05", file.LastOpTime, loc) lastOpTime := utils.MustParseCNTime(file.LastOpTime)
res = append(res, &model.ObjThumb{ res = append(res, &model.ObjThumb{
Object: model.Object{ Object: model.Object{
ID: strconv.FormatInt(file.Id, 10), ID: strconv.FormatInt(file.Id, 10),
@ -307,7 +307,7 @@ func (d *Cloud189) uploadRequest(uri string, form map[string]string, resp interf
return data, nil return data, nil
} }
func (d *Cloud189) newUpload(dstDir model.Obj, file model.FileStreamer, up driver.UpdateProgress) error { func (d *Cloud189) newUpload(ctx context.Context, dstDir model.Obj, file model.FileStreamer, up driver.UpdateProgress) error {
sessionKey, err := d.getSessionKey() sessionKey, err := d.getSessionKey()
if err != nil { if err != nil {
return err return err
@ -336,6 +336,9 @@ func (d *Cloud189) newUpload(dstDir model.Obj, file model.FileStreamer, up drive
md5s := make([]string, 0) md5s := make([]string, 0)
md5Sum := md5.New() md5Sum := md5.New()
for i = 1; i <= count; i++ { for i = 1; i <= count; i++ {
if utils.IsCanceled(ctx) {
return ctx.Err()
}
byteSize = file.GetSize() - finish byteSize = file.GetSize() - finish
if DEFAULT < byteSize { if DEFAULT < byteSize {
byteSize = DEFAULT byteSize = DEFAULT
@ -365,12 +368,15 @@ func (d *Cloud189) newUpload(dstDir model.Obj, file model.FileStreamer, up drive
log.Debugf("uploadData: %+v", uploadData) log.Debugf("uploadData: %+v", uploadData)
requestURL := uploadData.RequestURL requestURL := uploadData.RequestURL
uploadHeaders := strings.Split(decodeURIComponent(uploadData.RequestHeader), "&") uploadHeaders := strings.Split(decodeURIComponent(uploadData.RequestHeader), "&")
req, _ := http.NewRequest(http.MethodPut, requestURL, bytes.NewReader(byteData)) req, err := http.NewRequest(http.MethodPut, requestURL, bytes.NewReader(byteData))
if err != nil {
return err
}
req = req.WithContext(ctx)
for _, v := range uploadHeaders { for _, v := range uploadHeaders {
i := strings.Index(v, "=") i := strings.Index(v, "=")
req.Header.Set(v[0:i], v[i+1:]) req.Header.Set(v[0:i], v[i+1:])
} }
r, err := base.HttpClient.Do(req) r, err := base.HttpClient.Do(req)
log.Debugf("%+v %+v", r, r.Request.Header) log.Debugf("%+v %+v", r, r.Request.Header)
r.Body.Close() r.Body.Close()

View File

@ -13,7 +13,7 @@ import (
"github.com/go-resty/resty/v2" "github.com/go-resty/resty/v2"
) )
type Yun189PC struct { type Cloud189PC struct {
model.Storage model.Storage
Addition Addition
@ -26,20 +26,15 @@ type Yun189PC struct {
tokenInfo *AppSessionResp tokenInfo *AppSessionResp
} }
func (y *Yun189PC) Config() driver.Config { func (y *Cloud189PC) Config() driver.Config {
return config return config
} }
func (y *Yun189PC) GetAddition() driver.Additional { func (y *Cloud189PC) GetAddition() driver.Additional {
return y.Addition return &y.Addition
} }
func (y *Yun189PC) Init(ctx context.Context, storage model.Storage) (err error) { func (y *Cloud189PC) Init(ctx context.Context) (err error) {
y.Storage = storage
if err = utils.Json.UnmarshalFromString(y.Storage.Addition, &y.Addition); err != nil {
return err
}
// 处理个人云和家庭云参数 // 处理个人云和家庭云参数
if y.isFamily() && y.RootFolderID == "-11" { if y.isFamily() && y.RootFolderID == "-11" {
y.RootFolderID = "" y.RootFolderID = ""
@ -78,15 +73,15 @@ func (y *Yun189PC) Init(ctx context.Context, storage model.Storage) (err error)
return return
} }
func (y *Yun189PC) Drop(ctx context.Context) error { func (y *Cloud189PC) Drop(ctx context.Context) error {
return nil return nil
} }
func (y *Yun189PC) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) { func (y *Cloud189PC) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
return y.getFiles(ctx, dir.GetID()) return y.getFiles(ctx, dir.GetID())
} }
func (y *Yun189PC) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { func (y *Cloud189PC) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
var downloadUrl struct { var downloadUrl struct {
URL string `json:"fileDownloadUrl"` URL string `json:"fileDownloadUrl"`
} }
@ -145,7 +140,7 @@ func (y *Yun189PC) Link(ctx context.Context, file model.Obj, args model.LinkArgs
return like, nil return like, nil
} }
func (y *Yun189PC) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error { func (y *Cloud189PC) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
fullUrl := API_URL fullUrl := API_URL
if y.isFamily() { if y.isFamily() {
fullUrl += "/family/file" fullUrl += "/family/file"
@ -172,7 +167,7 @@ func (y *Yun189PC) MakeDir(ctx context.Context, parentDir model.Obj, dirName str
return err return err
} }
func (y *Yun189PC) Move(ctx context.Context, srcObj, dstDir model.Obj) error { func (y *Cloud189PC) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
_, err := y.post(API_URL+"/batch/createBatchTask.action", func(req *resty.Request) { _, err := y.post(API_URL+"/batch/createBatchTask.action", func(req *resty.Request) {
req.SetContext(ctx) req.SetContext(ctx)
req.SetFormData(map[string]string{ req.SetFormData(map[string]string{
@ -196,7 +191,7 @@ func (y *Yun189PC) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
return err return err
} }
func (y *Yun189PC) Rename(ctx context.Context, srcObj model.Obj, newName string) error { func (y *Cloud189PC) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
queryParam := make(map[string]string) queryParam := make(map[string]string)
fullUrl := API_URL fullUrl := API_URL
method := http.MethodPost method := http.MethodPost
@ -221,7 +216,7 @@ func (y *Yun189PC) Rename(ctx context.Context, srcObj model.Obj, newName string)
return err return err
} }
func (y *Yun189PC) Copy(ctx context.Context, srcObj, dstDir model.Obj) error { func (y *Cloud189PC) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
_, err := y.post(API_URL+"/batch/createBatchTask.action", func(req *resty.Request) { _, err := y.post(API_URL+"/batch/createBatchTask.action", func(req *resty.Request) {
req.SetContext(ctx) req.SetContext(ctx)
req.SetFormData(map[string]string{ req.SetFormData(map[string]string{
@ -246,7 +241,7 @@ func (y *Yun189PC) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
return err return err
} }
func (y *Yun189PC) Remove(ctx context.Context, obj model.Obj) error { func (y *Cloud189PC) Remove(ctx context.Context, obj model.Obj) error {
_, err := y.post(API_URL+"/batch/createBatchTask.action", func(req *resty.Request) { _, err := y.post(API_URL+"/batch/createBatchTask.action", func(req *resty.Request) {
req.SetContext(ctx) req.SetContext(ctx)
req.SetFormData(map[string]string{ req.SetFormData(map[string]string{
@ -270,7 +265,7 @@ func (y *Yun189PC) Remove(ctx context.Context, obj model.Obj) error {
return err return err
} }
func (y *Yun189PC) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error { func (y *Cloud189PC) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
if y.RapidUpload { if y.RapidUpload {
return y.FastUpload(ctx, dstDir, stream, up) return y.FastUpload(ctx, dstDir, stream, up)
} }

View File

@ -15,7 +15,7 @@ type Addition struct {
Type string `json:"type" type:"select" options:"personal,family" default:"personal"` Type string `json:"type" type:"select" options:"personal,family" default:"personal"`
FamilyID string `json:"family_id"` FamilyID string `json:"family_id"`
RapidUpload bool `json:"rapid_upload"` RapidUpload bool `json:"rapid_upload"`
NonuseOrc bool `json:"nonuse_orc"` NoUseOcr bool `json:"no_use_ocr"`
} }
var config = driver.Config{ var config = driver.Config{
@ -24,7 +24,7 @@ var config = driver.Config{
} }
func init() { func init() {
op.RegisterDriver(config, func() driver.Driver { op.RegisterDriver(func() driver.Driver {
return &Yun189PC{} return &Cloud189PC{}
}) })
} }

View File

@ -47,7 +47,7 @@ const (
CHANNEL_ID = "web_cloud.189.cn" CHANNEL_ID = "web_cloud.189.cn"
) )
func (y *Yun189PC) request(url, method string, callback base.ReqCallback, params Params, resp interface{}) ([]byte, error) { func (y *Cloud189PC) request(url, method string, callback base.ReqCallback, params Params, resp interface{}) ([]byte, error) {
dateOfGmt := getHttpDateStr() dateOfGmt := getHttpDateStr()
sessionKey := y.tokenInfo.SessionKey sessionKey := y.tokenInfo.SessionKey
sessionSecret := y.tokenInfo.SessionSecret sessionSecret := y.tokenInfo.SessionSecret
@ -124,15 +124,15 @@ func (y *Yun189PC) request(url, method string, callback base.ReqCallback, params
} }
} }
func (y *Yun189PC) get(url string, callback base.ReqCallback, resp interface{}) ([]byte, error) { func (y *Cloud189PC) get(url string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
return y.request(url, http.MethodGet, callback, nil, resp) return y.request(url, http.MethodGet, callback, nil, resp)
} }
func (y *Yun189PC) post(url string, callback base.ReqCallback, resp interface{}) ([]byte, error) { func (y *Cloud189PC) post(url string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
return y.request(url, http.MethodPost, callback, nil, resp) return y.request(url, http.MethodPost, callback, nil, resp)
} }
func (y *Yun189PC) getFiles(ctx context.Context, fileId string) ([]model.Obj, error) { func (y *Cloud189PC) getFiles(ctx context.Context, fileId string) ([]model.Obj, error) {
fullUrl := API_URL fullUrl := API_URL
if y.isFamily() { if y.isFamily() {
fullUrl += "/family/file" fullUrl += "/family/file"
@ -184,9 +184,9 @@ func (y *Yun189PC) getFiles(ctx context.Context, fileId string) ([]model.Obj, er
return res, nil return res, nil
} }
func (y *Yun189PC) login() (err error) { func (y *Cloud189PC) login() (err error) {
// 初始化登陆所需参数 // 初始化登陆所需参数
if y.loginParam == nil || !y.NonuseOrc { if y.loginParam == nil || !y.NoUseOcr {
if err = y.initLoginParam(); err != nil { if err = y.initLoginParam(); err != nil {
// 验证码也通过错误返回 // 验证码也通过错误返回
return err return err
@ -198,7 +198,7 @@ func (y *Yun189PC) login() (err error) {
// 销毁登陆参数 // 销毁登陆参数
y.loginParam = nil y.loginParam = nil
// 遇到错误,重新加载登陆参数 // 遇到错误,重新加载登陆参数
if err != nil && y.NonuseOrc { if err != nil && y.NoUseOcr {
if err1 := y.initLoginParam(); err1 != nil { if err1 := y.initLoginParam(); err1 != nil {
err = fmt.Errorf("err1: %s \nerr2: %s", err, err1) err = fmt.Errorf("err1: %s \nerr2: %s", err, err1)
} }
@ -264,7 +264,7 @@ func (y *Yun189PC) login() (err error) {
/* 初始化登陆需要的参数 /* 初始化登陆需要的参数
* 如果遇到验证码返回错误 * 如果遇到验证码返回错误
*/ */
func (y *Yun189PC) initLoginParam() error { func (y *Cloud189PC) initLoginParam() error {
// 清除cookie // 清除cookie
jar, _ := cookiejar.New(nil) jar, _ := cookiejar.New(nil)
y.client.SetCookieJar(jar) y.client.SetCookieJar(jar)
@ -315,7 +315,7 @@ func (y *Yun189PC) initLoginParam() error {
return fmt.Errorf("failed to obtain verification code") return fmt.Errorf("failed to obtain verification code")
} }
if imgRes.Size() > 20 { if imgRes.Size() > 20 {
if setting.GetStr(conf.OcrApi) != "" && !y.NonuseOrc { if setting.GetStr(conf.OcrApi) != "" && !y.NoUseOcr {
vRes, err := base.RestyClient.R(). vRes, err := base.RestyClient.R().
SetMultipartField("image", "validateCode.png", "image/png", bytes.NewReader(imgRes.Body())). SetMultipartField("image", "validateCode.png", "image/png", bytes.NewReader(imgRes.Body())).
Post(setting.GetStr(conf.OcrApi)) Post(setting.GetStr(conf.OcrApi))
@ -335,7 +335,7 @@ func (y *Yun189PC) initLoginParam() error {
} }
// 刷新会话 // 刷新会话
func (y *Yun189PC) refreshSession() (err error) { func (y *Cloud189PC) refreshSession() (err error) {
var erron RespErr var erron RespErr
var userSessionResp UserSessionResp var userSessionResp UserSessionResp
_, err = y.client.R(). _, err = y.client.R().
@ -381,7 +381,7 @@ func (y *Yun189PC) refreshSession() (err error) {
} }
// 普通上传 // 普通上传
func (y *Yun189PC) CommonUpload(ctx context.Context, dstDir model.Obj, file model.FileStreamer, up driver.UpdateProgress) (err error) { func (y *Cloud189PC) CommonUpload(ctx context.Context, dstDir model.Obj, file model.FileStreamer, up driver.UpdateProgress) (err error) {
const DEFAULT int64 = 10485760 const DEFAULT int64 = 10485760
var count = int64(math.Ceil(float64(file.GetSize()) / float64(DEFAULT))) var count = int64(math.Ceil(float64(file.GetSize()) / float64(DEFAULT)))
@ -418,10 +418,8 @@ func (y *Yun189PC) CommonUpload(ctx context.Context, dstDir model.Obj, file mode
silceMd5Hexs := make([]string, 0, count) silceMd5Hexs := make([]string, 0, count)
byteData := bytes.NewBuffer(make([]byte, DEFAULT)) byteData := bytes.NewBuffer(make([]byte, DEFAULT))
for i := int64(1); i <= count; i++ { for i := int64(1); i <= count; i++ {
select { if utils.IsCanceled(ctx) {
case <-ctx.Done():
return ctx.Err() return ctx.Err()
default:
} }
// 读取块 // 读取块
@ -491,7 +489,7 @@ func (y *Yun189PC) CommonUpload(ctx context.Context, dstDir model.Obj, file mode
} }
// 快传 // 快传
func (y *Yun189PC) FastUpload(ctx context.Context, dstDir model.Obj, file model.FileStreamer, up driver.UpdateProgress) (err error) { func (y *Cloud189PC) FastUpload(ctx context.Context, dstDir model.Obj, file model.FileStreamer, up driver.UpdateProgress) (err error) {
// 需要获取完整文件md5,必须支持 io.Seek // 需要获取完整文件md5,必须支持 io.Seek
tempFile, err := utils.CreateTempFile(file.GetReadCloser()) tempFile, err := utils.CreateTempFile(file.GetReadCloser())
if err != nil { if err != nil {
@ -511,10 +509,8 @@ func (y *Yun189PC) FastUpload(ctx context.Context, dstDir model.Obj, file model.
silceMd5Hexs := make([]string, 0, count) silceMd5Hexs := make([]string, 0, count)
silceMd5Base64s := make([]string, 0, count) silceMd5Base64s := make([]string, 0, count)
for i := 1; i <= count; i++ { for i := 1; i <= count; i++ {
select { if utils.IsCanceled(ctx) {
case <-ctx.Done():
return ctx.Err() return ctx.Err()
default:
} }
silceMd5.Reset() silceMd5.Reset()
@ -616,11 +612,11 @@ func (y *Yun189PC) FastUpload(ctx context.Context, dstDir model.Obj, file model.
return err return err
} }
func (y *Yun189PC) isFamily() bool { func (y *Cloud189PC) isFamily() bool {
return y.Type == "family" return y.Type == "family"
} }
func (y *Yun189PC) isLogin() bool { func (y *Cloud189PC) isLogin() bool {
if y.tokenInfo == nil { if y.tokenInfo == nil {
return false return false
} }
@ -629,7 +625,7 @@ func (y *Yun189PC) isLogin() bool {
} }
// 获取家庭云所有用户信息 // 获取家庭云所有用户信息
func (y *Yun189PC) getFamilyInfoList() ([]FamilyInfoResp, error) { func (y *Cloud189PC) getFamilyInfoList() ([]FamilyInfoResp, error) {
var resp FamilyInfoListResp var resp FamilyInfoListResp
_, err := y.get(API_URL+"/family/manage/getFamilyList.action", nil, &resp) _, err := y.get(API_URL+"/family/manage/getFamilyList.action", nil, &resp)
if err != nil { if err != nil {
@ -639,7 +635,7 @@ func (y *Yun189PC) getFamilyInfoList() ([]FamilyInfoResp, error) {
} }
// 抽取家庭云ID // 抽取家庭云ID
func (y *Yun189PC) getFamilyID() (string, error) { func (y *Cloud189PC) getFamilyID() (string, error) {
infos, err := y.getFamilyInfoList() infos, err := y.getFamilyInfoList()
if err != nil { if err != nil {
return "", err return "", err

118
drivers/alist_v2/driver.go Normal file
View File

@ -0,0 +1,118 @@
package alist_v2
import (
"context"
"github.com/alist-org/alist/v3/drivers/base"
"github.com/alist-org/alist/v3/internal/driver"
"github.com/alist-org/alist/v3/internal/errs"
"github.com/alist-org/alist/v3/internal/model"
"github.com/alist-org/alist/v3/server/common"
)
type AListV2 struct {
model.Storage
Addition
}
func (d *AListV2) Config() driver.Config {
return config
}
func (d *AListV2) GetAddition() driver.Additional {
return &d.Addition
}
func (d *AListV2) Init(ctx context.Context) error {
if len(d.Addition.Address) > 0 && string(d.Addition.Address[len(d.Addition.Address)-1]) == "/" {
d.Addition.Address = d.Addition.Address[0 : len(d.Addition.Address)-1]
}
// TODO login / refresh token
//op.MustSaveDriverStorage(d)
return nil
}
func (d *AListV2) Drop(ctx context.Context) error {
return nil
}
func (d *AListV2) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
url := d.Address + "/api/public/path"
var resp common.Resp[PathResp]
_, err := base.RestyClient.R().
SetResult(&resp).
SetHeader("Authorization", d.AccessToken).
SetBody(PathReq{
PageNum: 0,
PageSize: 0,
Path: dir.GetPath(),
Password: d.Password,
}).Post(url)
if err != nil {
return nil, err
}
var files []model.Obj
for _, f := range resp.Data.Files {
file := model.ObjThumb{
Object: model.Object{
Name: f.Name,
Modified: *f.UpdatedAt,
Size: f.Size,
IsFolder: f.Type == 1,
},
Thumbnail: model.Thumbnail{Thumbnail: f.Thumbnail},
}
files = append(files, &file)
}
return files, nil
}
func (d *AListV2) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
url := d.Address + "/api/public/path"
var resp common.Resp[PathResp]
_, err := base.RestyClient.R().
SetResult(&resp).
SetHeader("Authorization", d.AccessToken).
SetBody(PathReq{
PageNum: 0,
PageSize: 0,
Path: file.GetPath(),
Password: d.Password,
}).Post(url)
if err != nil {
return nil, err
}
return &model.Link{
URL: resp.Data.Files[0].Url,
}, nil
}
func (d *AListV2) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
return errs.NotImplement
}
func (d *AListV2) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
return errs.NotImplement
}
func (d *AListV2) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
return errs.NotImplement
}
func (d *AListV2) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
return errs.NotImplement
}
func (d *AListV2) Remove(ctx context.Context, obj model.Obj) error {
return errs.NotImplement
}
func (d *AListV2) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
return errs.NotImplement
}
//func (d *AList) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
// return nil, errs.NotSupport
//}
var _ driver.Driver = (*AListV2)(nil)

26
drivers/alist_v2/meta.go Normal file
View File

@ -0,0 +1,26 @@
package alist_v2
import (
"github.com/alist-org/alist/v3/internal/driver"
"github.com/alist-org/alist/v3/internal/op"
)
type Addition struct {
driver.RootPath
Address string `json:"url" required:"true"`
Password string `json:"password"`
AccessToken string `json:"access_token"`
}
var config = driver.Config{
Name: "AList V2",
LocalSort: true,
NoUpload: true,
DefaultRoot: "/",
}
func init() {
op.RegisterDriver(func() driver.Driver {
return &AListV2{}
})
}

31
drivers/alist_v2/types.go Normal file
View File

@ -0,0 +1,31 @@
package alist_v2
import (
"time"
)
type File struct {
Id string `json:"-"`
Name string `json:"name"`
Size int64 `json:"size"`
Type int `json:"type"`
Driver string `json:"driver"`
UpdatedAt *time.Time `json:"updated_at"`
Thumbnail string `json:"thumbnail"`
Url string `json:"url"`
SizeStr string `json:"size_str"`
TimeStr string `json:"time_str"`
}
type PathResp struct {
Type string `json:"type"`
//Meta Meta `json:"meta"`
Files []File `json:"files"`
}
type PathReq struct {
PageNum int `json:"page_num"`
PageSize int `json:"page_size"`
Password string `json:"password"`
Path string `json:"path"`
}

1
drivers/alist_v2/util.go Normal file
View File

@ -0,0 +1 @@
package alist_v2

179
drivers/alist_v3/driver.go Normal file
View File

@ -0,0 +1,179 @@
package alist_v3
import (
"context"
"io"
"path"
"strconv"
"strings"
"github.com/alist-org/alist/v3/drivers/base"
"github.com/alist-org/alist/v3/internal/driver"
"github.com/alist-org/alist/v3/internal/model"
"github.com/alist-org/alist/v3/server/common"
)
type AListV3 struct {
model.Storage
Addition
}
func (d *AListV3) Config() driver.Config {
return config
}
func (d *AListV3) GetAddition() driver.Additional {
return &d.Addition
}
func (d *AListV3) Init(ctx context.Context) error {
d.Addition.Address = strings.TrimSuffix(d.Addition.Address, "/")
// TODO login / refresh token
//op.MustSaveDriverStorage(d)
return nil
}
func (d *AListV3) Drop(ctx context.Context) error {
return nil
}
func (d *AListV3) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
url := d.Address + "/api/fs/list"
var resp common.Resp[FsListResp]
_, err := base.RestyClient.R().
SetResult(&resp).
SetHeader("Authorization", d.AccessToken).
SetBody(ListReq{
PageReq: model.PageReq{
Page: 1,
PerPage: 0,
},
Path: dir.GetPath(),
Password: d.Password,
Refresh: false,
}).Post(url)
if err != nil {
return nil, err
}
var files []model.Obj
for _, f := range resp.Data.Content {
file := model.ObjThumb{
Object: model.Object{
Name: f.Name,
Modified: f.Modified,
Size: f.Size,
IsFolder: f.IsDir,
},
Thumbnail: model.Thumbnail{Thumbnail: f.Thumb},
}
files = append(files, &file)
}
return files, nil
}
func (d *AListV3) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
url := d.Address + "/api/fs/get"
var resp common.Resp[FsGetResp]
_, err := base.RestyClient.R().
SetResult(&resp).
SetHeader("Authorization", d.AccessToken).
SetBody(FsGetReq{
Path: file.GetPath(),
Password: d.Password,
}).Post(url)
if err != nil {
return nil, err
}
return &model.Link{
URL: resp.Data.RawURL,
}, nil
}
func (d *AListV3) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
url := d.Address + "/api/fs/mkdir"
var resp common.Resp[interface{}]
_, err := base.RestyClient.R().
SetResult(&resp).
SetHeader("Authorization", d.AccessToken).
SetBody(MkdirOrLinkReq{
Path: path.Join(parentDir.GetPath(), dirName),
}).Post(url)
return checkResp(resp, err)
}
func (d *AListV3) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
url := d.Address + "/api/fs/move"
var resp common.Resp[interface{}]
_, err := base.RestyClient.R().
SetResult(&resp).
SetHeader("Authorization", d.AccessToken).
SetBody(MoveCopyReq{
SrcDir: srcObj.GetPath(),
DstDir: dstDir.GetPath(),
Names: []string{srcObj.GetName()},
}).Post(url)
return checkResp(resp, err)
}
func (d *AListV3) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
url := d.Address + "/api/fs/rename"
var resp common.Resp[interface{}]
_, err := base.RestyClient.R().
SetResult(&resp).
SetHeader("Authorization", d.AccessToken).
SetBody(RenameReq{
Path: srcObj.GetPath(),
Name: newName,
}).Post(url)
return checkResp(resp, err)
}
func (d *AListV3) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
url := d.Address + "/api/fs/copy"
var resp common.Resp[interface{}]
_, err := base.RestyClient.R().
SetResult(&resp).
SetHeader("Authorization", d.AccessToken).
SetBody(MoveCopyReq{
SrcDir: srcObj.GetPath(),
DstDir: dstDir.GetPath(),
Names: []string{srcObj.GetName()},
}).Post(url)
return checkResp(resp, err)
}
func (d *AListV3) Remove(ctx context.Context, obj model.Obj) error {
url := d.Address + "/api/fs/remove"
var resp common.Resp[interface{}]
_, err := base.RestyClient.R().
SetResult(&resp).
SetHeader("Authorization", d.AccessToken).
SetBody(RemoveReq{
Dir: obj.GetPath(),
Names: []string{obj.GetName()},
}).Post(url)
return checkResp(resp, err)
}
func (d *AListV3) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
url := d.Address + "/api/fs/put"
var resp common.Resp[interface{}]
fileBytes, err := io.ReadAll(stream.GetReadCloser())
if err != nil {
return nil
}
_, err = base.RestyClient.R().SetContext(ctx).
SetResult(&resp).
SetHeader("Authorization", d.AccessToken).
SetHeader("File-Path", path.Join(dstDir.GetPath(), stream.GetName())).
SetHeader("Password", d.Password).
SetHeader("Content-Length", strconv.FormatInt(stream.GetSize(), 10)).
SetBody(fileBytes).Put(url)
return checkResp(resp, err)
}
//func (d *AList) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
// return nil, errs.NotSupport
//}
var _ driver.Driver = (*AListV3)(nil)

25
drivers/alist_v3/meta.go Normal file
View File

@ -0,0 +1,25 @@
package alist_v3
import (
"github.com/alist-org/alist/v3/internal/driver"
"github.com/alist-org/alist/v3/internal/op"
)
type Addition struct {
driver.RootPath
Address string `json:"url" required:"true"`
Password string `json:"password"`
AccessToken string `json:"access_token"`
}
var config = driver.Config{
Name: "AList V3",
LocalSort: true,
DefaultRoot: "/",
}
func init() {
op.RegisterDriver(func() driver.Driver {
return &AListV3{}
})
}

65
drivers/alist_v3/types.go Normal file
View File

@ -0,0 +1,65 @@
package alist_v3
import (
"time"
"github.com/alist-org/alist/v3/internal/model"
)
type ListReq struct {
model.PageReq
Path string `json:"path" form:"path"`
Password string `json:"password" form:"password"`
Refresh bool `json:"refresh"`
}
type ObjResp struct {
Name string `json:"name"`
Size int64 `json:"size"`
IsDir bool `json:"is_dir"`
Modified time.Time `json:"modified"`
Sign string `json:"sign"`
Thumb string `json:"thumb"`
Type int `json:"type"`
}
type FsListResp struct {
Content []ObjResp `json:"content"`
Total int64 `json:"total"`
Readme string `json:"readme"`
Write bool `json:"write"`
Provider string `json:"provider"`
}
type FsGetReq struct {
Path string `json:"path" form:"path"`
Password string `json:"password" form:"password"`
}
type FsGetResp struct {
ObjResp
RawURL string `json:"raw_url"`
Readme string `json:"readme"`
Provider string `json:"provider"`
Related []ObjResp `json:"related"`
}
type MkdirOrLinkReq struct {
Path string `json:"path" form:"path"`
}
type MoveCopyReq struct {
SrcDir string `json:"src_dir"`
DstDir string `json:"dst_dir"`
Names []string `json:"names"`
}
type RenameReq struct {
Path string `json:"path"`
Name string `json:"name"`
}
type RemoveReq struct {
Dir string `json:"dir"`
Names []string `json:"names"`
}

17
drivers/alist_v3/util.go Normal file
View File

@ -0,0 +1,17 @@
package alist_v3
import (
"errors"
"github.com/alist-org/alist/v3/server/common"
)
func checkResp(resp common.Resp[interface{}], err error) error {
if err != nil {
return err
}
if resp.Message == "success" {
return nil
}
return errors.New(resp.Message)
}

View File

@ -38,18 +38,13 @@ func (d *AliDrive) Config() driver.Config {
} }
func (d *AliDrive) GetAddition() driver.Additional { func (d *AliDrive) GetAddition() driver.Additional {
return d.Addition return &d.Addition
} }
func (d *AliDrive) Init(ctx context.Context, storage model.Storage) error { func (d *AliDrive) Init(ctx context.Context) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
// TODO login / refresh token // TODO login / refresh token
//op.MustSaveDriverStorage(d) //op.MustSaveDriverStorage(d)
err = d.refreshToken() err := d.refreshToken()
if err != nil { if err != nil {
return err return err
} }
@ -86,11 +81,6 @@ func (d *AliDrive) List(ctx context.Context, dir model.Obj, args model.ListArgs)
}) })
} }
//func (d *AliDrive) Get(ctx context.Context, path string) (model.Obj, error) {
// // TODO this is optional
// return nil, errs.NotImplement
//}
func (d *AliDrive) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { func (d *AliDrive) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
data := base.Json{ data := base.Json{
"drive_id": d.DriveId, "drive_id": d.DriveId,
@ -258,10 +248,14 @@ func (d *AliDrive) Put(ctx context.Context, dstDir model.Obj, stream model.FileS
} }
for i, partInfo := range resp.PartInfoList { for i, partInfo := range resp.PartInfoList {
if utils.IsCanceled(ctx) {
return ctx.Err()
}
req, err := http.NewRequest("PUT", partInfo.UploadUrl, io.LimitReader(file, DEFAULT)) req, err := http.NewRequest("PUT", partInfo.UploadUrl, io.LimitReader(file, DEFAULT))
if err != nil { if err != nil {
return err return err
} }
req = req.WithContext(ctx)
res, err := base.HttpClient.Do(req) res, err := base.HttpClient.Do(req)
if err != nil { if err != nil {
return err return err

View File

@ -18,10 +18,8 @@ var config = driver.Config{
DefaultRoot: "root", DefaultRoot: "root",
} }
func New() driver.Driver {
return &AliDrive{}
}
func init() { func init() {
op.RegisterDriver(config, New) op.RegisterDriver(func() driver.Driver {
return &AliDrive{}
})
} }

View File

@ -62,6 +62,8 @@ func (d *AliDrive) request(url, method string, callback base.ReqCallback, resp i
return d.request(url, method, callback, resp) return d.request(url, method, callback, resp)
} }
return nil, errors.New(e.Message), e return nil, errors.New(e.Message), e
} else if res.IsError() {
return nil, errors.New("bad status code " + res.Status()), e
} }
return res.Body(), nil, e return res.Body(), nil, e
} }

View File

@ -8,7 +8,6 @@ import (
"github.com/alist-org/alist/v3/drivers/base" "github.com/alist-org/alist/v3/drivers/base"
"github.com/alist-org/alist/v3/internal/driver" "github.com/alist-org/alist/v3/internal/driver"
"github.com/alist-org/alist/v3/internal/errs"
"github.com/alist-org/alist/v3/internal/model" "github.com/alist-org/alist/v3/internal/model"
"github.com/alist-org/alist/v3/pkg/cron" "github.com/alist-org/alist/v3/pkg/cron"
"github.com/alist-org/alist/v3/pkg/utils" "github.com/alist-org/alist/v3/pkg/utils"
@ -29,16 +28,11 @@ func (d *AliyundriveShare) Config() driver.Config {
} }
func (d *AliyundriveShare) GetAddition() driver.Additional { func (d *AliyundriveShare) GetAddition() driver.Additional {
return d.Addition return &d.Addition
} }
func (d *AliyundriveShare) Init(ctx context.Context, storage model.Storage) error { func (d *AliyundriveShare) Init(ctx context.Context) error {
d.Storage = storage err := d.refreshToken()
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
err = d.refreshToken()
if err != nil { if err != nil {
return err return err
} }
@ -73,69 +67,42 @@ func (d *AliyundriveShare) List(ctx context.Context, dir model.Obj, args model.L
}) })
} }
//func (d *AliyundriveShare) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *AliyundriveShare) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { func (d *AliyundriveShare) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
data := base.Json{ data := base.Json{
"drive_id": d.DriveId, "drive_id": d.DriveId,
"file_id": file.GetID(), "file_id": file.GetID(),
"expire_sec": 14400, // // Only ten minutes lifetime
"expire_sec": 600,
"share_id": d.ShareId,
} }
var resp ShareLinkResp
var e ErrorResp var e ErrorResp
res, err := base.RestyClient.R(). _, err := base.RestyClient.R().
SetError(&e).SetBody(data). SetError(&e).SetBody(data).SetResult(&resp).
SetHeader("content-type", "application/json"). SetHeader("content-type", "application/json").
SetHeader("Authorization", "Bearer\t"+d.AccessToken). SetHeader("Authorization", "Bearer\t"+d.AccessToken).
Post("https://api.aliyundrive.com/v2/file/get_download_url") SetHeader("x-share-token", d.ShareToken).
Post("https://api.aliyundrive.com/v2/file/get_share_link_download_url")
if err != nil { if err != nil {
return nil, err return nil, err
} }
var u string var u string
if e.Code != "" { if e.Code != "" {
if e.Code == "AccessTokenInvalid" { if e.Code == "AccessTokenInvalid" || e.Code == "ShareLinkTokenInvalid" {
err = d.refreshToken() if e.Code == "AccessTokenInvalid" {
err = d.refreshToken()
} else {
err = d.getShareToken()
}
if err != nil { if err != nil {
return nil, err return nil, err
} }
return d.Link(ctx, file, args) return d.Link(ctx, file, args)
} else if e.Code == "ForbiddenNoPermission.File" {
data = utils.MergeMap(data, base.Json{
// Only ten minutes valid
"expire_sec": 600,
"share_id": d.ShareId,
})
var resp ShareLinkResp
var e2 ErrorResp
_, err = base.RestyClient.R().
SetError(&e2).SetBody(data).SetResult(&resp).
SetHeader("content-type", "application/json").
SetHeader("Authorization", "Bearer\t"+d.AccessToken).
SetHeader("x-share-token", d.ShareToken).
Post("https://api.aliyundrive.com/v2/file/get_share_link_download_url")
if err != nil {
return nil, err
}
if e2.Code != "" {
if e2.Code == "AccessTokenInvalid" || e2.Code == "ShareLinkTokenInvalid" {
err = d.getShareToken()
if err != nil {
return nil, err
}
return d.Link(ctx, file, args)
} else {
return nil, errors.New(e2.Code + ":" + e2.Message)
}
} else {
u = resp.DownloadUrl
}
} else { } else {
return nil, errors.New(e.Code + ":" + e.Message) return nil, errors.New(e.Code + ": " + e.Message)
} }
} else { } else {
u = utils.Json.Get(res.Body(), "url").ToString() u = resp.DownloadUrl
} }
return &model.Link{ return &model.Link{
Header: http.Header{ Header: http.Header{
@ -145,34 +112,4 @@ func (d *AliyundriveShare) Link(ctx context.Context, file model.Obj, args model.
}, nil }, nil
} }
func (d *AliyundriveShare) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
// TODO create folder
return errs.NotSupport
}
func (d *AliyundriveShare) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
// TODO move obj
return errs.NotSupport
}
func (d *AliyundriveShare) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
// TODO rename obj
return errs.NotSupport
}
func (d *AliyundriveShare) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
// TODO copy obj
return errs.NotSupport
}
func (d *AliyundriveShare) Remove(ctx context.Context, obj model.Obj) error {
// TODO remove obj
return errs.NotSupport
}
func (d *AliyundriveShare) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
// TODO upload file
return errs.NotSupport
}
var _ driver.Driver = (*AliyundriveShare)(nil) var _ driver.Driver = (*AliyundriveShare)(nil)

View File

@ -23,7 +23,7 @@ var config = driver.Config{
} }
func init() { func init() {
op.RegisterDriver(config, func() driver.Driver { op.RegisterDriver(func() driver.Driver {
return &AliyundriveShare{} return &AliyundriveShare{}
}) })
} }

View File

@ -1,10 +1,13 @@
package drivers package drivers
import ( import (
_ "github.com/alist-org/alist/v3/drivers/115"
_ "github.com/alist-org/alist/v3/drivers/123" _ "github.com/alist-org/alist/v3/drivers/123"
_ "github.com/alist-org/alist/v3/drivers/139" _ "github.com/alist-org/alist/v3/drivers/139"
_ "github.com/alist-org/alist/v3/drivers/189" _ "github.com/alist-org/alist/v3/drivers/189"
_ "github.com/alist-org/alist/v3/drivers/189pc" _ "github.com/alist-org/alist/v3/drivers/189pc"
_ "github.com/alist-org/alist/v3/drivers/alist_v2"
_ "github.com/alist-org/alist/v3/drivers/alist_v3"
_ "github.com/alist-org/alist/v3/drivers/aliyundrive" _ "github.com/alist-org/alist/v3/drivers/aliyundrive"
_ "github.com/alist-org/alist/v3/drivers/aliyundrive_share" _ "github.com/alist-org/alist/v3/drivers/aliyundrive_share"
_ "github.com/alist-org/alist/v3/drivers/baidu_netdisk" _ "github.com/alist-org/alist/v3/drivers/baidu_netdisk"
@ -18,9 +21,11 @@ import (
_ "github.com/alist-org/alist/v3/drivers/mega" _ "github.com/alist-org/alist/v3/drivers/mega"
_ "github.com/alist-org/alist/v3/drivers/onedrive" _ "github.com/alist-org/alist/v3/drivers/onedrive"
_ "github.com/alist-org/alist/v3/drivers/pikpak" _ "github.com/alist-org/alist/v3/drivers/pikpak"
_ "github.com/alist-org/alist/v3/drivers/pikpak_share"
_ "github.com/alist-org/alist/v3/drivers/quark" _ "github.com/alist-org/alist/v3/drivers/quark"
_ "github.com/alist-org/alist/v3/drivers/s3" _ "github.com/alist-org/alist/v3/drivers/s3"
_ "github.com/alist-org/alist/v3/drivers/sftp" _ "github.com/alist-org/alist/v3/drivers/sftp"
_ "github.com/alist-org/alist/v3/drivers/smb"
_ "github.com/alist-org/alist/v3/drivers/teambition" _ "github.com/alist-org/alist/v3/drivers/teambition"
_ "github.com/alist-org/alist/v3/drivers/thunder" _ "github.com/alist-org/alist/v3/drivers/thunder"
_ "github.com/alist-org/alist/v3/drivers/uss" _ "github.com/alist-org/alist/v3/drivers/uss"

View File

@ -31,15 +31,10 @@ func (d *BaiduNetdisk) Config() driver.Config {
} }
func (d *BaiduNetdisk) GetAddition() driver.Additional { func (d *BaiduNetdisk) GetAddition() driver.Additional {
return d.Addition return &d.Addition
} }
func (d *BaiduNetdisk) Init(ctx context.Context, storage model.Storage) error { func (d *BaiduNetdisk) Init(ctx context.Context) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
return d.refreshToken() return d.refreshToken()
} }
@ -57,11 +52,6 @@ func (d *BaiduNetdisk) List(ctx context.Context, dir model.Obj, args model.ListA
}) })
} }
//func (d *BaiduNetdisk) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *BaiduNetdisk) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { func (d *BaiduNetdisk) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
if d.DownloadAPI == "crack" { if d.DownloadAPI == "crack" {
return d.linkCrack(file, args) return d.linkCrack(file, args)
@ -202,6 +192,9 @@ func (d *BaiduNetdisk) Put(ctx context.Context, dstDir model.Obj, stream model.F
} }
left = stream.GetSize() left = stream.GetSize()
for i, partseq := range precreateResp.BlockList { for i, partseq := range precreateResp.BlockList {
if utils.IsCanceled(ctx) {
return ctx.Err()
}
byteSize := Default byteSize := Default
var byteData []byte var byteData []byte
if left < Default { if left < Default {
@ -217,7 +210,11 @@ func (d *BaiduNetdisk) Put(ctx context.Context, dstDir model.Obj, stream model.F
} }
u := "https://d.pcs.baidu.com/rest/2.0/pcs/superfile2" u := "https://d.pcs.baidu.com/rest/2.0/pcs/superfile2"
params["partseq"] = strconv.Itoa(partseq) params["partseq"] = strconv.Itoa(partseq)
res, err := base.RestyClient.R().SetQueryParams(params).SetFileReader("file", stream.GetName(), bytes.NewReader(byteData)).Post(u) res, err := base.RestyClient.R().
SetContext(ctx).
SetQueryParams(params).
SetFileReader("file", stream.GetName(), bytes.NewReader(byteData)).
Post(u)
if err != nil { if err != nil {
return err return err
} }

View File

@ -20,10 +20,8 @@ var config = driver.Config{
DefaultRoot: "/", DefaultRoot: "/",
} }
func New() driver.Driver {
return &BaiduNetdisk{}
}
func init() { func init() {
op.RegisterDriver(config, New) op.RegisterDriver(func() driver.Driver {
return &BaiduNetdisk{}
})
} }

View File

@ -187,7 +187,7 @@ func (d *BaiduNetdisk) create(path string, size int64, isdir int, uploadid, bloc
params := map[string]string{ params := map[string]string{
"method": "create", "method": "create",
} }
data := fmt.Sprintf("path=%s&size=%d&isdir=%d", path, size, isdir) data := fmt.Sprintf("path=%s&size=%d&isdir=%d", encodeURIComponent(path), size, isdir)
if uploadid != "" { if uploadid != "" {
data += fmt.Sprintf("&uploadid=%s&block_list=%s", uploadid, block_list) data += fmt.Sprintf("&uploadid=%s&block_list=%s", uploadid, block_list)
} }

View File

@ -29,15 +29,10 @@ func (d *BaiduPhoto) Config() driver.Config {
} }
func (d *BaiduPhoto) GetAddition() driver.Additional { func (d *BaiduPhoto) GetAddition() driver.Additional {
return d.Addition return &d.Addition
} }
func (d *BaiduPhoto) Init(ctx context.Context, storage model.Storage) error { func (d *BaiduPhoto) Init(ctx context.Context) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
return d.refreshToken() return d.refreshToken()
} }
@ -245,6 +240,9 @@ func (d *BaiduPhoto) Put(ctx context.Context, dstDir model.Obj, stream model.Fil
} }
for i := 0; i < count; i++ { for i := 0; i < count; i++ {
if utils.IsCanceled(ctx) {
return ctx.Err()
}
uploadParams["partseq"] = fmt.Sprint(i) uploadParams["partseq"] = fmt.Sprint(i)
_, err = d.Post("https://c3.pcs.baidu.com/rest/2.0/pcs/superfile2", func(r *resty.Request) { _, err = d.Post("https://c3.pcs.baidu.com/rest/2.0/pcs/superfile2", func(r *resty.Request) {
r.SetContext(ctx) r.SetContext(ctx)

View File

@ -24,7 +24,7 @@ var config = driver.Config{
} }
func init() { func init() {
op.RegisterDriver(config, func() driver.Driver { op.RegisterDriver(func() driver.Driver {
return &BaiduPhoto{} return &BaiduPhoto{}
}) })
} }

View File

@ -100,7 +100,7 @@ type (
) )
func (a *Album) GetSize() int64 { return 0 } func (a *Album) GetSize() int64 { return 0 }
func (a *Album) GetName() string { return fmt.Sprint(a.Title) } func (a *Album) GetName() string { return a.Title }
func (a *Album) ModTime() time.Time { func (a *Album) ModTime() time.Time {
if a.parseTime == nil { if a.parseTime == nil {
a.parseTime = toTime(a.Mtime) a.parseTime = toTime(a.Mtime)

View File

@ -11,7 +11,7 @@ var NoRedirectClient *resty.Client
var RestyClient = NewRestyClient() var RestyClient = NewRestyClient()
var HttpClient = &http.Client{} var HttpClient = &http.Client{}
var UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36" var UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
var DefaultTimeout = time.Second * 10 var DefaultTimeout = time.Second * 30
func init() { func init() {
NoRedirectClient = resty.New().SetRedirectPolicy( NoRedirectClient = resty.New().SetRedirectPolicy(

View File

@ -7,7 +7,6 @@ import (
"github.com/alist-org/alist/v3/internal/driver" "github.com/alist-org/alist/v3/internal/driver"
"github.com/alist-org/alist/v3/internal/errs" "github.com/alist-org/alist/v3/internal/errs"
"github.com/alist-org/alist/v3/internal/model" "github.com/alist-org/alist/v3/internal/model"
"github.com/alist-org/alist/v3/pkg/utils"
"github.com/jlaffaye/ftp" "github.com/jlaffaye/ftp"
) )
@ -22,15 +21,10 @@ func (d *FTP) Config() driver.Config {
} }
func (d *FTP) GetAddition() driver.Additional { func (d *FTP) GetAddition() driver.Additional {
return d.Addition return &d.Addition
} }
func (d *FTP) Init(ctx context.Context, storage model.Storage) error { func (d *FTP) Init(ctx context.Context) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
return d.login() return d.login()
} }
@ -66,11 +60,6 @@ func (d *FTP) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]m
return res, nil return res, nil
} }
//func (d *FTP) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *FTP) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { func (d *FTP) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
if err := d.login(); err != nil { if err := d.login(); err != nil {
return nil, err return nil, err
@ -124,6 +113,7 @@ func (d *FTP) Put(ctx context.Context, dstDir model.Obj, stream model.FileStream
if err := d.login(); err != nil { if err := d.login(); err != nil {
return err return err
} }
// TODO: support cancel
return d.conn.Stor(stdpath.Join(dstDir.GetPath(), stream.GetName()), stream) return d.conn.Stor(stdpath.Join(dstDir.GetPath(), stream.GetName()), stream)
} }

View File

@ -19,10 +19,8 @@ var config = driver.Config{
DefaultRoot: "/", DefaultRoot: "/",
} }
func New() driver.Driver {
return &FTP{}
}
func init() { func init() {
op.RegisterDriver(config, New) op.RegisterDriver(func() driver.Driver {
return &FTP{}
})
} }

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"fmt" "fmt"
"net/http" "net/http"
"strconv"
"github.com/alist-org/alist/v3/drivers/base" "github.com/alist-org/alist/v3/drivers/base"
"github.com/alist-org/alist/v3/internal/driver" "github.com/alist-org/alist/v3/internal/driver"
@ -24,14 +25,12 @@ func (d *GoogleDrive) Config() driver.Config {
} }
func (d *GoogleDrive) GetAddition() driver.Additional { func (d *GoogleDrive) GetAddition() driver.Additional {
return d.Addition return &d.Addition
} }
func (d *GoogleDrive) Init(ctx context.Context, storage model.Storage) error { func (d *GoogleDrive) Init(ctx context.Context) error {
d.Storage = storage if d.ChunkSize == 0 {
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition) d.ChunkSize = 5
if err != nil {
return err
} }
return d.refreshToken() return d.refreshToken()
} }
@ -50,13 +49,12 @@ func (d *GoogleDrive) List(ctx context.Context, dir model.Obj, args model.ListAr
}) })
} }
//func (d *GoogleDrive) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *GoogleDrive) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { func (d *GoogleDrive) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
url := fmt.Sprintf("https://www.googleapis.com/drive/v3/files/%s?includeItemsFromAllDrives=true&supportsAllDrives=true", file.GetID()) url := fmt.Sprintf("https://www.googleapis.com/drive/v3/files/%s?includeItemsFromAllDrives=true&supportsAllDrives=true", file.GetID())
_, err := d.request(url, http.MethodGet, nil, nil)
if err != nil {
return nil, err
}
link := model.Link{ link := model.Link{
URL: url + "&alt=media", URL: url + "&alt=media",
Header: http.Header{ Header: http.Header{
@ -112,15 +110,36 @@ func (d *GoogleDrive) Remove(ctx context.Context, obj model.Obj) error {
} }
func (d *GoogleDrive) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error { func (d *GoogleDrive) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
data := base.Json{ obj := stream.GetOld()
"name": stream.GetName(), var (
"parents": []string{dstDir.GetID()}, e Error
url string
data base.Json
res *resty.Response
err error
)
if obj != nil {
url = fmt.Sprintf("https://www.googleapis.com/upload/drive/v3/files/%s?uploadType=resumable&supportsAllDrives=true", obj.GetID())
data = base.Json{}
} else {
data = base.Json{
"name": stream.GetName(),
"parents": []string{dstDir.GetID()},
}
url = "https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable&supportsAllDrives=true"
}
req := base.NoRedirectClient.R().
SetHeaders(map[string]string{
"Authorization": "Bearer " + d.AccessToken,
"X-Upload-Content-Type": stream.GetMimetype(),
"X-Upload-Content-Length": strconv.FormatInt(stream.GetSize(), 10),
}).
SetError(&e).SetBody(data).SetContext(ctx)
if obj != nil {
res, err = req.Patch(url)
} else {
res, err = req.Post(url)
} }
var e Error
url := "https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable&supportsAllDrives=true"
res, err := base.NoRedirectClient.R().SetHeader("Authorization", "Bearer "+d.AccessToken).
SetError(&e).SetBody(data).
Post(url)
if err != nil { if err != nil {
return err return err
} }
@ -135,9 +154,13 @@ func (d *GoogleDrive) Put(ctx context.Context, dstDir model.Obj, stream model.Fi
return fmt.Errorf("%s: %v", e.Error.Message, e.Error.Errors) return fmt.Errorf("%s: %v", e.Error.Message, e.Error.Errors)
} }
putUrl := res.Header().Get("location") putUrl := res.Header().Get("location")
_, err = d.request(putUrl, http.MethodPut, func(req *resty.Request) { if stream.GetSize() < d.ChunkSize*1024*1024 {
req.SetBody(stream.GetReadCloser()) _, err = d.request(putUrl, http.MethodPut, func(req *resty.Request) {
}, nil) req.SetHeader("Content-Length", strconv.FormatInt(stream.GetSize(), 10)).SetBody(stream.GetReadCloser())
}, nil)
} else {
err = d.chunkUpload(ctx, stream, putUrl)
}
return err return err
} }

View File

@ -12,6 +12,7 @@ type Addition struct {
OrderDirection string `json:"order_direction" type:"select" options:"asc,desc"` OrderDirection string `json:"order_direction" type:"select" options:"asc,desc"`
ClientID string `json:"client_id" required:"true" default:"202264815644.apps.googleusercontent.com"` ClientID string `json:"client_id" required:"true" default:"202264815644.apps.googleusercontent.com"`
ClientSecret string `json:"client_secret" required:"true" default:"X4Z3ca8xfWDb1Voo-F9a7ZxJ"` ClientSecret string `json:"client_secret" required:"true" default:"X4Z3ca8xfWDb1Voo-F9a7ZxJ"`
ChunkSize int64 `json:"chunk_size" type:"number" default:"5" help:"chunk size while uploading (unit: MB)"`
} }
var config = driver.Config{ var config = driver.Config{
@ -20,10 +21,8 @@ var config = driver.Config{
DefaultRoot: "root", DefaultRoot: "root",
} }
func New() driver.Driver {
return &GoogleDrive{}
}
func init() { func init() {
op.RegisterDriver(config, New) op.RegisterDriver(func() driver.Driver {
return &GoogleDrive{}
})
} }

View File

@ -5,6 +5,7 @@ import (
"time" "time"
"github.com/alist-org/alist/v3/internal/model" "github.com/alist-org/alist/v3/internal/model"
log "github.com/sirupsen/logrus"
) )
type TokenError struct { type TokenError struct {
@ -18,17 +19,22 @@ type Files struct {
} }
type File struct { type File struct {
Id string `json:"id"` Id string `json:"id"`
Name string `json:"name"` Name string `json:"name"`
MimeType string `json:"mimeType"` MimeType string `json:"mimeType"`
ModifiedTime time.Time `json:"modifiedTime"` ModifiedTime time.Time `json:"modifiedTime"`
Size string `json:"size"` Size string `json:"size"`
ThumbnailLink string `json:"thumbnailLink"` ThumbnailLink string `json:"thumbnailLink"`
ShortcutDetails struct {
TargetId string `json:"targetId"`
TargetMimeType string `json:"targetMimeType"`
} `json:"shortcutDetails"`
} }
func fileToObj(f File) *model.ObjThumb { func fileToObj(f File) *model.ObjThumb {
log.Debugf("google file: %+v", f)
size, _ := strconv.ParseInt(f.Size, 10, 64) size, _ := strconv.ParseInt(f.Size, 10, 64)
return &model.ObjThumb{ obj := &model.ObjThumb{
Object: model.Object{ Object: model.Object{
ID: f.Id, ID: f.Id,
Name: f.Name, Name: f.Name,
@ -38,6 +44,11 @@ func fileToObj(f File) *model.ObjThumb {
}, },
Thumbnail: model.Thumbnail{}, Thumbnail: model.Thumbnail{},
} }
if f.MimeType == "application/vnd.google-apps.shortcut" {
obj.ID = f.ShortcutDetails.TargetId
obj.IsFolder = f.ShortcutDetails.TargetMimeType == "application/vnd.google-apps.folder"
}
return obj
} }
type Error struct { type Error struct {

View File

@ -1,10 +1,15 @@
package google_drive package google_drive
import ( import (
"context"
"fmt" "fmt"
"io"
"net/http" "net/http"
"strconv"
"github.com/alist-org/alist/v3/drivers/base" "github.com/alist-org/alist/v3/drivers/base"
"github.com/alist-org/alist/v3/internal/model"
"github.com/alist-org/alist/v3/pkg/utils"
"github.com/go-resty/resty/v2" "github.com/go-resty/resty/v2"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
@ -77,7 +82,7 @@ func (d *GoogleDrive) getFiles(id string) ([]File, error) {
} }
query := map[string]string{ query := map[string]string{
"orderBy": orderBy, "orderBy": orderBy,
"fields": "files(id,name,mimeType,size,modifiedTime,thumbnailLink),nextPageToken", "fields": "files(id,name,mimeType,size,modifiedTime,thumbnailLink,shortcutDetails),nextPageToken",
"pageSize": "1000", "pageSize": "1000",
"q": fmt.Sprintf("'%s' in parents and trashed = false", id), "q": fmt.Sprintf("'%s' in parents and trashed = false", id),
//"includeItemsFromAllDrives": "true", //"includeItemsFromAllDrives": "true",
@ -95,3 +100,28 @@ func (d *GoogleDrive) getFiles(id string) ([]File, error) {
} }
return res, nil return res, nil
} }
func (d *GoogleDrive) chunkUpload(ctx context.Context, stream model.FileStreamer, url string) error {
var defaultChunkSize = d.ChunkSize * 1024 * 1024
var finish int64 = 0
for finish < stream.GetSize() {
if utils.IsCanceled(ctx) {
return ctx.Err()
}
chunkSize := stream.GetSize() - finish
if chunkSize > defaultChunkSize {
chunkSize = defaultChunkSize
}
_, err := d.request(url, http.MethodPut, func(req *resty.Request) {
req.SetHeaders(map[string]string{
"Content-Length": strconv.FormatInt(chunkSize, 10),
"Content-Range": fmt.Sprintf("bytes %d-%d/%d", finish, finish+chunkSize-1, stream.GetSize()),
}).SetBody(io.LimitReader(stream.GetReadCloser(), chunkSize)).SetContext(ctx)
}, nil)
if err != nil {
return err
}
finish += chunkSize
}
return nil
}

View File

@ -26,15 +26,10 @@ func (d *GooglePhoto) Config() driver.Config {
} }
func (d *GooglePhoto) GetAddition() driver.Additional { func (d *GooglePhoto) GetAddition() driver.Additional {
return d.Addition return &d.Addition
} }
func (d *GooglePhoto) Init(ctx context.Context, storage model.Storage) error { func (d *GooglePhoto) Init(ctx context.Context) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
return d.refreshToken() return d.refreshToken()
} }
@ -52,11 +47,6 @@ func (d *GooglePhoto) List(ctx context.Context, dir model.Obj, args model.ListAr
}) })
} }
//func (d *GooglePhoto) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *GooglePhoto) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { func (d *GooglePhoto) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
f, err := d.getMedia(file.GetID()) f, err := d.getMedia(file.GetID())
if err != nil { if err != nil {
@ -134,7 +124,7 @@ func (d *GooglePhoto) Put(ctx context.Context, dstDir model.Obj, stream model.Fi
} }
resp, err := d.request(postUrl, http.MethodPost, func(req *resty.Request) { resp, err := d.request(postUrl, http.MethodPost, func(req *resty.Request) {
req.SetBody(stream.GetReadCloser()) req.SetBody(stream.GetReadCloser()).SetContext(ctx)
}, nil, postHeaders) }, nil, postHeaders)
if err != nil { if err != nil {

View File

@ -21,10 +21,8 @@ var config = driver.Config{
LocalSort: true, LocalSort: true,
} }
func New() driver.Driver {
return &GooglePhoto{}
}
func init() { func init() {
op.RegisterDriver(config, New) op.RegisterDriver(func() driver.Driver {
return &GooglePhoto{}
})
} }

View File

@ -9,7 +9,6 @@ import (
"github.com/alist-org/alist/v3/internal/driver" "github.com/alist-org/alist/v3/internal/driver"
"github.com/alist-org/alist/v3/internal/errs" "github.com/alist-org/alist/v3/internal/errs"
"github.com/alist-org/alist/v3/internal/model" "github.com/alist-org/alist/v3/internal/model"
"github.com/alist-org/alist/v3/pkg/utils"
"github.com/go-resty/resty/v2" "github.com/go-resty/resty/v2"
) )
@ -25,15 +24,10 @@ func (d *LanZou) Config() driver.Config {
} }
func (d *LanZou) GetAddition() driver.Additional { func (d *LanZou) GetAddition() driver.Additional {
return d.Addition return &d.Addition
} }
func (d *LanZou) Init(ctx context.Context, storage model.Storage) error { func (d *LanZou) Init(ctx context.Context) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
if d.IsCookie() { if d.IsCookie() {
if d.RootFolderID == "" { if d.RootFolderID == "" {
d.RootFolderID = "-1" d.RootFolderID = "-1"
@ -163,7 +157,7 @@ func (d *LanZou) Put(ctx context.Context, dstDir model.Obj, stream model.FileStr
"id": "WU_FILE_0", "id": "WU_FILE_0",
"name": stream.GetName(), "name": stream.GetName(),
"folder_id": dstDir.GetID(), "folder_id": dstDir.GetID(),
}).SetFileReader("upload_file", stream.GetName(), stream) }).SetFileReader("upload_file", stream.GetName(), stream).SetContext(ctx)
}, nil, true) }, nil, true)
return err return err
} }

View File

@ -146,16 +146,16 @@ func IsNumber(str string) bool {
var findFromReg = regexp.MustCompile(`data : '(.+?)'`) // 查找from字符串 var findFromReg = regexp.MustCompile(`data : '(.+?)'`) // 查找from字符串
// 解析html中的from // 解析html中的form
func htmlFormToMap(html string) (map[string]string, error) { func htmlFormToMap(html string) (map[string]string, error) {
froms := findFromReg.FindStringSubmatch(html) forms := findFromReg.FindStringSubmatch(html)
if len(froms) != 2 { if len(forms) != 2 {
return nil, fmt.Errorf("not find file sgin") return nil, fmt.Errorf("not find file sgin")
} }
return fromToMap(froms[1]), nil return formToMap(forms[1]), nil
} }
func fromToMap(from string) map[string]string { func formToMap(from string) map[string]string {
var param = make(map[string]string) var param = make(map[string]string)
for _, kv := range strings.Split(from, "&") { for _, kv := range strings.Split(from, "&") {
kv := strings.SplitN(kv, "=", 2)[:2] kv := strings.SplitN(kv, "=", 2)[:2]

View File

@ -25,7 +25,7 @@ var config = driver.Config{
} }
func init() { func init() {
op.RegisterDriver(config, func() driver.Driver { op.RegisterDriver(func() driver.Driver {
return &LanZou{} return &LanZou{}
}) })
} }

View File

@ -16,11 +16,12 @@ import (
"github.com/alist-org/alist/v3/internal/conf" "github.com/alist-org/alist/v3/internal/conf"
"github.com/alist-org/alist/v3/internal/driver" "github.com/alist-org/alist/v3/internal/driver"
"github.com/alist-org/alist/v3/internal/errs"
"github.com/alist-org/alist/v3/internal/model" "github.com/alist-org/alist/v3/internal/model"
"github.com/alist-org/alist/v3/internal/sign"
"github.com/alist-org/alist/v3/pkg/utils" "github.com/alist-org/alist/v3/pkg/utils"
"github.com/alist-org/alist/v3/server/common" "github.com/alist-org/alist/v3/server/common"
"github.com/disintegration/imaging" "github.com/disintegration/imaging"
_ "golang.org/x/image/webp"
) )
type Local struct { type Local struct {
@ -32,24 +33,18 @@ func (d *Local) Config() driver.Config {
return config return config
} }
func (d *Local) Init(ctx context.Context, storage model.Storage) error { func (d *Local) Init(ctx context.Context) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
if !utils.Exists(d.GetRootPath()) { if !utils.Exists(d.GetRootPath()) {
err = fmt.Errorf("root folder %s not exists", d.GetRootPath()) return fmt.Errorf("root folder %s not exists", d.GetRootPath())
} else {
if !filepath.IsAbs(d.GetRootPath()) {
abs, err := filepath.Abs(d.GetRootPath())
if err != nil {
return err
}
d.SetRootPath(abs)
}
} }
return err if !filepath.IsAbs(d.GetRootPath()) {
abs, err := filepath.Abs(d.GetRootPath())
if err != nil {
return err
}
d.Addition.RootFolderPath = abs
}
return nil
} }
func (d *Local) Drop(ctx context.Context) error { func (d *Local) Drop(ctx context.Context) error {
@ -57,7 +52,7 @@ func (d *Local) Drop(ctx context.Context) error {
} }
func (d *Local) GetAddition() driver.Additional { func (d *Local) GetAddition() driver.Additional {
return d.Addition return &d.Addition
} }
func (d *Local) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) { func (d *Local) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
@ -68,21 +63,27 @@ func (d *Local) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([
} }
var files []model.Obj var files []model.Obj
for _, f := range rawFiles { for _, f := range rawFiles {
if strings.HasPrefix(f.Name(), ".") { if !d.ShowHidden && strings.HasPrefix(f.Name(), ".") {
continue continue
} }
thumb := "" thumb := ""
if d.Thumbnail && utils.GetFileType(f.Name()) == conf.IMAGE { if d.Thumbnail && utils.GetFileType(f.Name()) == conf.IMAGE {
thumb = common.GetApiUrl(nil) + stdpath.Join("/d", args.ReqPath, f.Name()) thumb = common.GetApiUrl(nil) + stdpath.Join("/d", args.ReqPath, f.Name())
thumb = utils.EncodePath(thumb, true) thumb = utils.EncodePath(thumb, true)
thumb += "?type=thumb" thumb += "?type=thumb&sign=" + sign.Sign(stdpath.Join(args.ReqPath, f.Name()))
}
isFolder := f.IsDir() || isSymlinkDir(f, fullPath)
var size int64
if !isFolder {
size = f.Size()
} }
file := model.ObjThumb{ file := model.ObjThumb{
Object: model.Object{ Object: model.Object{
Path: filepath.Join(dir.GetPath(), f.Name()),
Name: f.Name(), Name: f.Name(),
Modified: f.ModTime(), Modified: f.ModTime(),
Size: f.Size(), Size: size,
IsFolder: f.IsDir(), IsFolder: isFolder,
}, },
Thumbnail: model.Thumbnail{ Thumbnail: model.Thumbnail{
Thumbnail: thumb, Thumbnail: thumb,
@ -93,24 +94,6 @@ func (d *Local) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([
return files, nil return files, nil
} }
func (d *Local) Get(ctx context.Context, path string) (model.Obj, error) {
f, err := os.Stat(path)
if err != nil {
if strings.Contains(err.Error(), "cannot find the file") {
return nil, errs.ObjectNotFound
}
return nil, err
}
file := model.Object{
Path: path,
Name: f.Name(),
Modified: f.ModTime(),
Size: f.Size(),
IsFolder: f.IsDir(),
}
return &file, nil
}
func (d *Local) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { func (d *Local) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
fullPath := file.GetPath() fullPath := file.GetPath()
var link model.Link var link model.Link
@ -153,6 +136,9 @@ func (d *Local) MakeDir(ctx context.Context, parentDir model.Obj, dirName string
func (d *Local) Move(ctx context.Context, srcObj, dstDir model.Obj) error { func (d *Local) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
srcPath := srcObj.GetPath() srcPath := srcObj.GetPath()
dstPath := filepath.Join(dstDir.GetPath(), srcObj.GetName()) dstPath := filepath.Join(dstDir.GetPath(), srcObj.GetName())
if utils.IsSubPath(srcPath, dstPath) {
return fmt.Errorf("the destination folder is a subfolder of the source folder")
}
err := os.Rename(srcPath, dstPath) err := os.Rename(srcPath, dstPath)
if err != nil { if err != nil {
return err return err
@ -173,6 +159,9 @@ func (d *Local) Rename(ctx context.Context, srcObj model.Obj, newName string) er
func (d *Local) Copy(ctx context.Context, srcObj, dstDir model.Obj) error { func (d *Local) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
srcPath := srcObj.GetPath() srcPath := srcObj.GetPath()
dstPath := filepath.Join(dstDir.GetPath(), srcObj.GetName()) dstPath := filepath.Join(dstDir.GetPath(), srcObj.GetName())
if utils.IsSubPath(srcPath, dstPath) {
return fmt.Errorf("the destination folder is a subfolder of the source folder")
}
var err error var err error
if srcObj.IsDir() { if srcObj.IsDir() {
err = utils.CopyDir(srcPath, dstPath) err = utils.CopyDir(srcPath, dstPath)

View File

@ -7,7 +7,8 @@ import (
type Addition struct { type Addition struct {
driver.RootPath driver.RootPath
Thumbnail bool `json:"thumbnail" required:"true" help:"enable thumbnail"` Thumbnail bool `json:"thumbnail" required:"true" help:"enable thumbnail"`
ShowHidden bool `json:"show_hidden" default:"true" required:"false" help:"show hidden directories and files"`
} }
var config = driver.Config{ var config = driver.Config{
@ -18,10 +19,8 @@ var config = driver.Config{
DefaultRoot: "/", DefaultRoot: "/",
} }
func New() driver.Driver {
return &Local{}
}
func init() { func init() {
op.RegisterDriver(config, New) op.RegisterDriver(func() driver.Driver {
return &Local{}
})
} }

View File

@ -1 +1,25 @@
package local package local
import (
"io/fs"
"os"
"path/filepath"
)
func isSymlinkDir(f fs.FileInfo, path string) bool {
if f.Mode()&os.ModeSymlink == os.ModeSymlink {
dst, err := os.Readlink(filepath.Join(path, f.Name()))
if err != nil {
return false
}
if !filepath.IsAbs(dst) {
dst = filepath.Join(path, dst)
}
stat, err := os.Stat(dst)
if err != nil {
return false
}
return stat.IsDir()
}
return false
}

View File

@ -8,13 +8,12 @@ import (
"io" "io"
"net/http" "net/http"
"os" "os"
"path"
"strconv" "strconv"
"time"
"github.com/alist-org/alist/v3/drivers/base" "github.com/alist-org/alist/v3/drivers/base"
"github.com/alist-org/alist/v3/internal/driver" "github.com/alist-org/alist/v3/internal/driver"
"github.com/alist-org/alist/v3/internal/model" "github.com/alist-org/alist/v3/internal/model"
"github.com/alist-org/alist/v3/internal/op"
"github.com/alist-org/alist/v3/pkg/utils" "github.com/alist-org/alist/v3/pkg/utils"
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/credentials"
@ -35,16 +34,11 @@ func (d *MediaTrack) Config() driver.Config {
} }
func (d *MediaTrack) GetAddition() driver.Additional { func (d *MediaTrack) GetAddition() driver.Additional {
return d.Addition return &d.Addition
} }
func (d *MediaTrack) Init(ctx context.Context, storage model.Storage) error { func (d *MediaTrack) Init(ctx context.Context) error {
d.Storage = storage _, err := d.request("https://kayle.api.mediatrack.cn/users", http.MethodGet, nil, nil)
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
_, err = d.request("https://kayle.api.mediatrack.cn/users", http.MethodGet, nil, nil)
return err return err
} }
@ -63,7 +57,7 @@ func (d *MediaTrack) List(ctx context.Context, dir model.Obj, args model.ListArg
if f.File != nil && f.File.Cover != "" { if f.File != nil && f.File.Cover != "" {
thumb = "https://nano.mtres.cn/" + f.File.Cover thumb = "https://nano.mtres.cn/" + f.File.Cover
} }
return &model.ObjThumb{ return &Object{
Object: model.Object{ Object: model.Object{
ID: f.ID, ID: f.ID,
Name: f.Title, Name: f.Title,
@ -72,15 +66,11 @@ func (d *MediaTrack) List(ctx context.Context, dir model.Obj, args model.ListArg
Size: size, Size: size,
}, },
Thumbnail: model.Thumbnail{Thumbnail: thumb}, Thumbnail: model.Thumbnail{Thumbnail: thumb},
ParentID: dir.GetID(),
}, nil }, nil
}) })
} }
//func (d *MediaTrack) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *MediaTrack) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { func (d *MediaTrack) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
url := fmt.Sprintf("https://kayn.api.mediatrack.cn/v1/download_token/asset?asset_id=%s&source_type=project&password=&source_id=%s", url := fmt.Sprintf("https://kayn.api.mediatrack.cn/v1/download_token/asset?asset_id=%s&source_type=project&password=&source_id=%s",
file.GetID(), d.ProjectID) file.GetID(), d.ProjectID)
@ -91,7 +81,21 @@ func (d *MediaTrack) Link(ctx context.Context, file model.Obj, args model.LinkAr
} }
token := utils.Json.Get(body, "data", "token").ToString() token := utils.Json.Get(body, "data", "token").ToString()
url = "https://kayn.api.mediatrack.cn/v1/download/redirect?token=" + token url = "https://kayn.api.mediatrack.cn/v1/download/redirect?token=" + token
return &model.Link{URL: url}, nil res, err := base.NoRedirectClient.R().Get(url)
if err != nil {
return nil, err
}
log.Debug(res.String())
link := model.Link{
URL: url,
}
log.Debugln("res code: ", res.StatusCode())
if res.StatusCode() == 302 {
link.URL = res.Header().Get("location")
expired := time.Duration(60) * time.Second
link.Expiration = &expired
}
return &link, nil
} }
func (d *MediaTrack) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error { func (d *MediaTrack) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
@ -141,16 +145,18 @@ func (d *MediaTrack) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
} }
func (d *MediaTrack) Remove(ctx context.Context, obj model.Obj) error { func (d *MediaTrack) Remove(ctx context.Context, obj model.Obj) error {
dir, err := op.Get(ctx, d, path.Dir(obj.GetPath())) var parentID string
if err != nil { if o, ok := obj.(*Object); ok {
return err parentID = o.ParentID
} else {
return fmt.Errorf("obj is not local Object")
} }
data := base.Json{ data := base.Json{
"origin_id": dir.GetID(), "origin_id": parentID,
"ids": []string{obj.GetID()}, "ids": []string{obj.GetID()},
} }
url := "https://jayce.api.mediatrack.cn/v4/assets/batch/delete" url := "https://jayce.api.mediatrack.cn/v4/assets/batch/delete"
_, err = d.request(url, http.MethodDelete, func(req *resty.Request) { _, err := d.request(url, http.MethodDelete, func(req *resty.Request) {
req.SetBody(data) req.SetBody(data)
}, nil) }, nil)
return err return err
@ -189,7 +195,7 @@ func (d *MediaTrack) Put(ctx context.Context, dstDir model.Obj, stream model.Fil
Key: &resp.Data.Object, Key: &resp.Data.Object,
Body: tempFile, Body: tempFile,
} }
_, err = uploader.Upload(input) _, err = uploader.UploadWithContext(ctx, input)
if err != nil { if err != nil {
return err return err
} }

View File

@ -18,7 +18,7 @@ var config = driver.Config{
} }
func init() { func init() {
op.RegisterDriver(config, func() driver.Driver { op.RegisterDriver(func() driver.Driver {
return &MediaTrack{} return &MediaTrack{}
}) })
} }

View File

@ -1,6 +1,10 @@
package mediatrack package mediatrack
import "time" import (
"time"
"github.com/alist-org/alist/v3/internal/model"
)
type BaseResp struct { type BaseResp struct {
Status string `json:"status"` Status string `json:"status"`
@ -60,3 +64,9 @@ type UploadResp struct {
TraceID string `json:"trace_id"` TraceID string `json:"trace_id"`
RequestID string `json:"requestId"` RequestID string `json:"requestId"`
} }
type Object struct {
model.Object
model.Thumbnail
ParentID string
}

View File

@ -9,7 +9,6 @@ import (
"github.com/alist-org/alist/v3/internal/driver" "github.com/alist-org/alist/v3/internal/driver"
"github.com/alist-org/alist/v3/internal/errs" "github.com/alist-org/alist/v3/internal/errs"
"github.com/alist-org/alist/v3/internal/model" "github.com/alist-org/alist/v3/internal/model"
"github.com/alist-org/alist/v3/pkg/chanio"
"github.com/alist-org/alist/v3/pkg/utils" "github.com/alist-org/alist/v3/pkg/utils"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/t3rm1n4l/go-mega" "github.com/t3rm1n4l/go-mega"
@ -26,15 +25,10 @@ func (d *Mega) Config() driver.Config {
} }
func (d *Mega) GetAddition() driver.Additional { func (d *Mega) GetAddition() driver.Additional {
return d.Addition return &d.Addition
} }
func (d *Mega) Init(ctx context.Context, storage model.Storage) error { func (d *Mega) Init(ctx context.Context) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
d.c = mega.New() d.c = mega.New()
return d.c.Login(d.Email, d.Password) return d.c.Login(d.Email, d.Password)
} }
@ -62,13 +56,10 @@ func (d *Mega) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]
return nil, fmt.Errorf("unable to convert dir to mega node") return nil, fmt.Errorf("unable to convert dir to mega node")
} }
func (d *Mega) Get(ctx context.Context, path string) (model.Obj, error) { func (d *Mega) GetRoot(ctx context.Context) (model.Obj, error) {
if path == "/" { n := d.c.FS.GetRoot()
n := d.c.FS.GetRoot() log.Debugf("mega root: %+v", *n)
log.Debugf("mega root: %+v", *n) return &MegaNode{n}, nil
return &MegaNode{n}, nil
}
return nil, errs.NotSupport
} }
func (d *Mega) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { func (d *Mega) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
@ -85,17 +76,21 @@ func (d *Mega) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*
//u := down.GetResourceUrl() //u := down.GetResourceUrl()
//u = strings.Replace(u, "http", "https", 1) //u = strings.Replace(u, "http", "https", 1)
//return &model.Link{URL: u}, nil //return &model.Link{URL: u}, nil
c := chanio.New() r, w := io.Pipe()
go func() { go func() {
defer func() { defer func() {
_ = recover() _ = recover()
}() }()
log.Debugf("chunk size: %d", down.Chunks()) log.Debugf("chunk size: %d", down.Chunks())
var (
chunk []byte
err error
)
for id := 0; id < down.Chunks(); id++ { for id := 0; id < down.Chunks(); id++ {
chunk, err := down.DownloadChunk(id) chunk, err = down.DownloadChunk(id)
if err != nil { if err != nil {
log.Errorf("mega down: %+v", err) log.Errorf("mega down: %+v", err)
return break
} }
log.Debugf("id: %d,len: %d", id, len(chunk)) log.Debugf("id: %d,len: %d", id, len(chunk))
//_, _, err = down.ChunkLocation(id) //_, _, err = down.ChunkLocation(id)
@ -104,14 +99,16 @@ func (d *Mega) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*
// return // return
//} //}
//_, err = c.Write(chunk) //_, err = c.Write(chunk)
_, err = c.Write(chunk) if _, err = w.Write(chunk); err != nil {
break
}
} }
err := c.Close() err = w.CloseWithError(err)
if err != nil { if err != nil {
log.Errorf("mega down: %+v", err) log.Errorf("mega down: %+v", err)
} }
}() }()
return &model.Link{Data: c}, nil return &model.Link{Data: r}, nil
} }
return nil, fmt.Errorf("unable to convert dir to mega node") return nil, fmt.Errorf("unable to convert dir to mega node")
} }
@ -159,6 +156,9 @@ func (d *Mega) Put(ctx context.Context, dstDir model.Obj, stream model.FileStrea
} }
for id := 0; id < u.Chunks(); id++ { for id := 0; id < u.Chunks(); id++ {
if utils.IsCanceled(ctx) {
return ctx.Err()
}
_, chkSize, err := u.ChunkLocation(id) _, chkSize, err := u.ChunkLocation(id)
if err != nil { if err != nil {
return err return err

View File

@ -20,7 +20,7 @@ var config = driver.Config{
} }
func init() { func init() {
op.RegisterDriver(config, func() driver.Driver { op.RegisterDriver(func() driver.Driver {
return &Mega{} return &Mega{}
}) })
} }

View File

@ -2,14 +2,13 @@ package onedrive
import ( import (
"context" "context"
"fmt"
"net/http" "net/http"
stdpath "path"
"github.com/alist-org/alist/v3/drivers/base" "github.com/alist-org/alist/v3/drivers/base"
"github.com/alist-org/alist/v3/internal/driver" "github.com/alist-org/alist/v3/internal/driver"
"github.com/alist-org/alist/v3/internal/errs" "github.com/alist-org/alist/v3/internal/errs"
"github.com/alist-org/alist/v3/internal/model" "github.com/alist-org/alist/v3/internal/model"
"github.com/alist-org/alist/v3/internal/op"
"github.com/alist-org/alist/v3/pkg/utils" "github.com/alist-org/alist/v3/pkg/utils"
"github.com/go-resty/resty/v2" "github.com/go-resty/resty/v2"
) )
@ -25,15 +24,10 @@ func (d *Onedrive) Config() driver.Config {
} }
func (d *Onedrive) GetAddition() driver.Additional { func (d *Onedrive) GetAddition() driver.Additional {
return d.Addition return &d.Addition
} }
func (d *Onedrive) Init(ctx context.Context, storage model.Storage) error { func (d *Onedrive) Init(ctx context.Context) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
if d.ChunkSize < 1 { if d.ChunkSize < 1 {
d.ChunkSize = 5 d.ChunkSize = 5
} }
@ -50,7 +44,7 @@ func (d *Onedrive) List(ctx context.Context, dir model.Obj, args model.ListArgs)
return nil, err return nil, err
} }
return utils.SliceConvert(files, func(src File) (model.Obj, error) { return utils.SliceConvert(files, func(src File) (model.Obj, error) {
return fileToObj(src), nil return fileToObj(src, dir.GetID()), nil
}) })
} }
@ -95,18 +89,21 @@ func (d *Onedrive) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
} }
func (d *Onedrive) Rename(ctx context.Context, srcObj model.Obj, newName string) error { func (d *Onedrive) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
dstDir, err := op.Get(ctx, d, stdpath.Dir(srcObj.GetPath())) //dstDir, err := op.GetUnwrap(ctx, d, stdpath.Dir(srcObj.GetPath()))
if err != nil { var parentID string
return err if o, ok := srcObj.(*Object); ok {
parentID = o.ParentID
} else {
return fmt.Errorf("srcObj is not Object")
} }
data := base.Json{ data := base.Json{
"parentReference": base.Json{ "parentReference": base.Json{
"id": dstDir.GetID(), "id": parentID,
}, },
"name": newName, "name": newName,
} }
url := d.GetMetaUrl(false, srcObj.GetPath()) url := d.GetMetaUrl(false, srcObj.GetPath())
_, err = d.Request(url, http.MethodPatch, func(req *resty.Request) { _, err := d.Request(url, http.MethodPatch, func(req *resty.Request) {
req.SetBody(data) req.SetBody(data)
}, nil) }, nil)
return err return err
@ -140,7 +137,7 @@ func (d *Onedrive) Remove(ctx context.Context, obj model.Obj) error {
func (d *Onedrive) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error { func (d *Onedrive) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
var err error var err error
if stream.GetSize() <= 4*1024*1024 { if stream.GetSize() <= 4*1024*1024 {
err = d.upSmall(dstDir, stream) err = d.upSmall(ctx, dstDir, stream)
} else { } else {
err = d.upBig(ctx, dstDir, stream, up) err = d.upBig(ctx, dstDir, stream, up)
} }

View File

@ -23,10 +23,8 @@ var config = driver.Config{
DefaultRoot: "/", DefaultRoot: "/",
} }
func New() driver.Driver {
return &Onedrive{}
}
func init() { func init() {
op.RegisterDriver(config, New) op.RegisterDriver(func() driver.Driver {
return &Onedrive{}
})
} }

View File

@ -42,21 +42,29 @@ type File struct {
} `json:"parentReference"` } `json:"parentReference"`
} }
func fileToObj(f File) *model.ObjThumbURL { type Object struct {
model.ObjThumbURL
ParentID string
}
func fileToObj(f File, parentID string) *Object {
thumb := "" thumb := ""
if len(f.Thumbnails) > 0 { if len(f.Thumbnails) > 0 {
thumb = f.Thumbnails[0].Medium.Url thumb = f.Thumbnails[0].Medium.Url
} }
return &model.ObjThumbURL{ return &Object{
Object: model.Object{ ObjThumbURL: model.ObjThumbURL{
ID: f.Id, Object: model.Object{
Name: f.Name, ID: f.Id,
Size: f.Size, Name: f.Name,
Modified: f.LastModifiedDateTime, Size: f.Size,
IsFolder: f.File == nil, Modified: f.LastModifiedDateTime,
IsFolder: f.File == nil,
},
Thumbnail: model.Thumbnail{Thumbnail: thumb},
Url: model.Url{Url: f.Url},
}, },
Thumbnail: model.Thumbnail{Thumbnail: thumb}, ParentID: parentID,
Url: model.Url{Url: f.Url},
} }
} }

View File

@ -147,14 +147,14 @@ func (d *Onedrive) GetFile(path string) (*File, error) {
return &file, err return &file, err
} }
func (d *Onedrive) upSmall(dstDir model.Obj, stream model.FileStreamer) error { func (d *Onedrive) upSmall(ctx context.Context, dstDir model.Obj, stream model.FileStreamer) error {
url := d.GetMetaUrl(false, stdpath.Join(dstDir.GetPath(), stream.GetName())) + "/content" url := d.GetMetaUrl(false, stdpath.Join(dstDir.GetPath(), stream.GetName())) + "/content"
data, err := io.ReadAll(stream) data, err := io.ReadAll(stream)
if err != nil { if err != nil {
return err return err
} }
_, err = d.Request(url, http.MethodPut, func(req *resty.Request) { _, err = d.Request(url, http.MethodPut, func(req *resty.Request) {
req.SetBody(data) req.SetBody(data).SetContext(ctx)
}, nil) }, nil)
return err return err
} }
@ -185,6 +185,10 @@ func (d *Onedrive) upBig(ctx context.Context, dstDir model.Obj, stream model.Fil
return err return err
} }
req, err := http.NewRequest("PUT", uploadUrl, bytes.NewBuffer(byteData)) req, err := http.NewRequest("PUT", uploadUrl, bytes.NewBuffer(byteData))
if err != nil {
return err
}
req = req.WithContext(ctx)
req.Header.Set("Content-Length", strconv.Itoa(int(byteSize))) req.Header.Set("Content-Length", strconv.Itoa(int(byteSize)))
req.Header.Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", finish, finish+byteSize-1, stream.GetSize())) req.Header.Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", finish, finish+byteSize-1, stream.GetSize()))
finish += byteSize finish += byteSize

View File

@ -35,15 +35,10 @@ func (d *PikPak) Config() driver.Config {
} }
func (d *PikPak) GetAddition() driver.Additional { func (d *PikPak) GetAddition() driver.Additional {
return d.Addition return &d.Addition
} }
func (d *PikPak) Init(ctx context.Context, storage model.Storage) error { func (d *PikPak) Init(ctx context.Context) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
return d.login() return d.login()
} }
@ -194,7 +189,7 @@ func (d *PikPak) Put(ctx context.Context, dstDir model.Obj, stream model.FileStr
Key: &key, Key: &key,
Body: tempFile, Body: tempFile,
} }
_, err = uploader.Upload(input) _, err = uploader.UploadWithContext(ctx, input)
return err return err
} }

View File

@ -17,10 +17,8 @@ var config = driver.Config{
DefaultRoot: "", DefaultRoot: "",
} }
func New() driver.Driver {
return &PikPak{}
}
func init() { func init() {
op.RegisterDriver(config, New) op.RegisterDriver(func() driver.Driver {
return &PikPak{}
})
} }

View File

@ -0,0 +1,81 @@
package pikpak_share
import (
"context"
"net/http"
"github.com/alist-org/alist/v3/internal/driver"
"github.com/alist-org/alist/v3/internal/model"
"github.com/alist-org/alist/v3/pkg/utils"
"github.com/go-resty/resty/v2"
log "github.com/sirupsen/logrus"
)
type PikPakShare struct {
model.Storage
Addition
RefreshToken string
AccessToken string
PassCodeToken string
}
func (d *PikPakShare) Config() driver.Config {
return config
}
func (d *PikPakShare) GetAddition() driver.Additional {
return &d.Addition
}
func (d *PikPakShare) Init(ctx context.Context) error {
err := d.login()
if err != nil {
return err
}
if d.SharePwd != "" {
err = d.getSharePassToken()
if err != nil {
return err
}
}
return nil
}
func (d *PikPakShare) Drop(ctx context.Context) error {
return nil
}
func (d *PikPakShare) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
files, err := d.getFiles(dir.GetID())
if err != nil {
return nil, err
}
return utils.SliceConvert(files, func(src File) (model.Obj, error) {
return fileToObj(src), nil
})
}
func (d *PikPakShare) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
var resp ShareResp
query := map[string]string{
"share_id": d.ShareId,
"file_id": file.GetID(),
"pass_code_token": d.PassCodeToken,
}
_, err := d.request("https://api-drive.mypikpak.com/drive/v1/share/file_info", http.MethodGet, func(req *resty.Request) {
req.SetQueryParams(query)
}, &resp)
if err != nil {
return nil, err
}
link := model.Link{
URL: resp.FileInfo.WebContentLink,
}
if len(resp.FileInfo.Medias) > 0 && resp.FileInfo.Medias[0].Link.Url != "" {
log.Debugln("use media link")
link.URL = resp.FileInfo.Medias[0].Link.Url
}
return &link, nil
}
var _ driver.Driver = (*PikPakShare)(nil)

View File

@ -0,0 +1,27 @@
package pikpak_share
import (
"github.com/alist-org/alist/v3/internal/driver"
"github.com/alist-org/alist/v3/internal/op"
)
type Addition struct {
driver.RootID
Username string `json:"username" required:"true"`
Password string `json:"password" required:"true"`
ShareId string `json:"share_id" required:"true"`
SharePwd string `json:"share_pwd"`
}
var config = driver.Config{
Name: "PikPakShare",
LocalSort: true,
NoUpload: true,
DefaultRoot: "",
}
func init() {
op.RegisterDriver(func() driver.Driver {
return &PikPakShare{}
})
}

View File

@ -0,0 +1,80 @@
package pikpak_share
import (
"strconv"
"time"
"github.com/alist-org/alist/v3/internal/model"
)
type RespErr struct {
ErrorCode int `json:"error_code"`
Error string `json:"error"`
}
type ShareResp struct {
ShareStatus string `json:"share_status"`
ShareStatusText string `json:"share_status_text"`
FileInfo File `json:"file_info"`
Files []File `json:"files"`
NextPageToken string `json:"next_page_token"`
PassCodeToken string `json:"pass_code_token"`
}
type File struct {
Id string `json:"id"`
ShareId string `json:"share_id"`
Kind string `json:"kind"`
Name string `json:"name"`
ModifiedTime time.Time `json:"modified_time"`
Size string `json:"size"`
ThumbnailLink string `json:"thumbnail_link"`
WebContentLink string `json:"web_content_link"`
Medias []Media `json:"medias"`
}
func fileToObj(f File) *model.ObjThumb {
size, _ := strconv.ParseInt(f.Size, 10, 64)
return &model.ObjThumb{
Object: model.Object{
ID: f.Id,
Name: f.Name,
Size: size,
Modified: f.ModifiedTime,
IsFolder: f.Kind == "drive#folder",
},
Thumbnail: model.Thumbnail{
Thumbnail: f.ThumbnailLink,
},
}
}
type Media struct {
MediaId string `json:"media_id"`
MediaName string `json:"media_name"`
Video struct {
Height int `json:"height"`
Width int `json:"width"`
Duration int `json:"duration"`
BitRate int `json:"bit_rate"`
FrameRate int `json:"frame_rate"`
VideoCodec string `json:"video_codec"`
AudioCodec string `json:"audio_codec"`
VideoType string `json:"video_type"`
} `json:"video"`
Link struct {
Url string `json:"url"`
Token string `json:"token"`
Expire time.Time `json:"expire"`
} `json:"link"`
NeedMoreQuota bool `json:"need_more_quota"`
VipTypes []interface{} `json:"vip_types"`
RedirectLink string `json:"redirect_link"`
IconLink string `json:"icon_link"`
IsDefault bool `json:"is_default"`
Priority int `json:"priority"`
IsOrigin bool `json:"is_origin"`
ResolutionName string `json:"resolution_name"`
IsVisible bool `json:"is_visible"`
Category string `json:"category"`
}

View File

@ -0,0 +1,154 @@
package pikpak_share
import (
"errors"
"net/http"
"github.com/alist-org/alist/v3/drivers/base"
"github.com/alist-org/alist/v3/internal/op"
"github.com/go-resty/resty/v2"
jsoniter "github.com/json-iterator/go"
)
// do others that not defined in Driver interface
func (d *PikPakShare) login() error {
url := "https://user.mypikpak.com/v1/auth/signin"
var e RespErr
res, err := base.RestyClient.R().SetError(&e).SetBody(base.Json{
"captcha_token": "",
"client_id": "YNxT9w7GMdWvEOKa",
"client_secret": "dbw2OtmVEeuUvIptb1Coyg",
"username": d.Username,
"password": d.Password,
}).Post(url)
if err != nil {
return err
}
if e.ErrorCode != 0 {
return errors.New(e.Error)
}
data := res.Body()
d.RefreshToken = jsoniter.Get(data, "refresh_token").ToString()
d.AccessToken = jsoniter.Get(data, "access_token").ToString()
return nil
}
func (d *PikPakShare) refreshToken() error {
url := "https://user.mypikpak.com/v1/auth/token"
var e RespErr
res, err := base.RestyClient.R().SetError(&e).
SetHeader("user-agent", "").SetBody(base.Json{
"client_id": "YNxT9w7GMdWvEOKa",
"client_secret": "dbw2OtmVEeuUvIptb1Coyg",
"grant_type": "refresh_token",
"refresh_token": d.RefreshToken,
}).Post(url)
if err != nil {
d.Status = err.Error()
op.MustSaveDriverStorage(d)
return err
}
if e.ErrorCode != 0 {
if e.ErrorCode == 4126 {
// refresh_token invalid, re-login
return d.login()
}
d.Status = e.Error
op.MustSaveDriverStorage(d)
return errors.New(e.Error)
}
data := res.Body()
d.Status = "work"
d.RefreshToken = jsoniter.Get(data, "refresh_token").ToString()
d.AccessToken = jsoniter.Get(data, "access_token").ToString()
op.MustSaveDriverStorage(d)
return nil
}
func (d *PikPakShare) request(url string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
req := base.RestyClient.R()
req.SetHeader("Authorization", "Bearer "+d.AccessToken)
if callback != nil {
callback(req)
}
if resp != nil {
req.SetResult(resp)
}
var e RespErr
req.SetError(&e)
res, err := req.Execute(method, url)
if err != nil {
return nil, err
}
if e.ErrorCode != 0 {
if e.ErrorCode == 16 {
// login / refresh token
err = d.refreshToken()
if err != nil {
return nil, err
}
return d.request(url, method, callback, resp)
}
return nil, errors.New(e.Error)
}
return res.Body(), nil
}
func (d *PikPakShare) getSharePassToken() error {
query := map[string]string{
"share_id": d.ShareId,
"pass_code": d.SharePwd,
"thumbnail_size": "SIZE_LARGE",
"limit": "100",
}
var resp ShareResp
_, err := d.request("https://api-drive.mypikpak.com/drive/v1/share", http.MethodGet, func(req *resty.Request) {
req.SetQueryParams(query)
}, &resp)
if err != nil {
return err
}
d.PassCodeToken = resp.PassCodeToken
return nil
}
func (d *PikPakShare) getFiles(id string) ([]File, error) {
res := make([]File, 0)
pageToken := "first"
for pageToken != "" {
if pageToken == "first" {
pageToken = ""
}
query := map[string]string{
"parent_id": id,
"share_id": d.ShareId,
"thumbnail_size": "SIZE_LARGE",
"with_audit": "true",
"limit": "100",
"filters": `{"phase":{"eq":"PHASE_TYPE_COMPLETE"},"trashed":{"eq":false}}`,
"page_token": pageToken,
"pass_code_token": d.PassCodeToken,
}
var resp ShareResp
_, err := d.request("https://api-drive.mypikpak.com/drive/v1/share/detail", http.MethodGet, func(req *resty.Request) {
req.SetQueryParams(query)
}, &resp)
if err != nil {
return nil, err
}
if resp.ShareStatus != "OK" {
if resp.ShareStatus == "PASS_CODE_EMPTY" || resp.ShareStatus == "PASS_CODE_ERROR" {
err = d.getSharePassToken()
if err != nil {
return nil, err
}
return d.getFiles(id)
}
return nil, errors.New(resp.ShareStatusText)
}
pageToken = resp.NextPageToken
res = append(res, resp.Files...)
}
return res, nil
}

View File

@ -29,16 +29,11 @@ func (d *Quark) Config() driver.Config {
} }
func (d *Quark) GetAddition() driver.Additional { func (d *Quark) GetAddition() driver.Additional {
return d.Addition return &d.Addition
} }
func (d *Quark) Init(ctx context.Context, storage model.Storage) error { func (d *Quark) Init(ctx context.Context) error {
d.Storage = storage _, err := d.request("/config", http.MethodGet, nil, nil)
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
_, err = d.request("/config", http.MethodGet, nil, nil)
return err return err
} }
@ -56,11 +51,6 @@ func (d *Quark) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([
}) })
} }
//func (d *Quark) Get(ctx context.Context, path string) (model.Obj, error) {
// // TODO this is optional
// return nil, errs.NotImplement
//}
func (d *Quark) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { func (d *Quark) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
data := base.Json{ data := base.Json{
"fids": []string{file.GetID()}, "fids": []string{file.GetID()},
@ -189,6 +179,9 @@ func (d *Quark) Put(ctx context.Context, dstDir model.Obj, stream model.FileStre
partNumber := 1 partNumber := 1
sizeDivide100 := stream.GetSize() / 100 sizeDivide100 := stream.GetSize() / 100
for left > 0 { for left > 0 {
if utils.IsCanceled(ctx) {
return ctx.Err()
}
if left > int64(partSize) { if left > int64(partSize) {
bytes = defaultBytes bytes = defaultBytes
} else { } else {
@ -200,7 +193,7 @@ func (d *Quark) Put(ctx context.Context, dstDir model.Obj, stream model.FileStre
} }
left -= int64(partSize) left -= int64(partSize)
log.Debugf("left: %d", left) log.Debugf("left: %d", left)
m, err := d.upPart(pre, stream.GetMimetype(), partNumber, bytes) m, err := d.upPart(ctx, pre, stream.GetMimetype(), partNumber, bytes)
//m, err := driver.UpPart(pre, file.GetMIMEType(), partNumber, bytes, account, md5Str, sha1Str) //m, err := driver.UpPart(pre, file.GetMIMEType(), partNumber, bytes, account, md5Str, sha1Str)
if err != nil { if err != nil {
return err return err

View File

@ -18,10 +18,8 @@ var config = driver.Config{
DefaultRoot: "0", DefaultRoot: "0",
} }
func New() driver.Driver {
return &Quark{}
}
func init() { func init() {
op.RegisterDriver(config, New) op.RegisterDriver(func() driver.Driver {
return &Quark{}
})
} }

View File

@ -1,6 +1,7 @@
package quark package quark
import ( import (
"context"
"crypto/md5" "crypto/md5"
"encoding/base64" "encoding/base64"
"errors" "errors"
@ -118,7 +119,7 @@ func (d *Quark) upHash(md5, sha1, taskId string) (bool, error) {
return resp.Data.Finish, err return resp.Data.Finish, err
} }
func (d *Quark) upPart(pre UpPreResp, mineType string, partNumber int, bytes []byte) (string, error) { func (d *Quark) upPart(ctx context.Context, pre UpPreResp, mineType string, partNumber int, bytes []byte) (string, error) {
//func (driver Quark) UpPart(pre UpPreResp, mineType string, partNumber int, bytes []byte, account *model.Account, md5Str, sha1Str string) (string, error) { //func (driver Quark) UpPart(pre UpPreResp, mineType string, partNumber int, bytes []byte, account *model.Account, md5Str, sha1Str string) (string, error) {
timeStr := time.Now().UTC().Format(http.TimeFormat) timeStr := time.Now().UTC().Format(http.TimeFormat)
data := base.Json{ data := base.Json{
@ -135,7 +136,7 @@ x-oss-user-agent:aliyun-sdk-js/6.6.1 Chrome 98.0.4758.80 on Windows 10 64-bit
} }
var resp UpAuthResp var resp UpAuthResp
_, err := d.request("/file/upload/auth", http.MethodPost, func(req *resty.Request) { _, err := d.request("/file/upload/auth", http.MethodPost, func(req *resty.Request) {
req.SetBody(data) req.SetBody(data).SetContext(ctx)
}, &resp) }, &resp)
if err != nil { if err != nil {
return "", err return "", err
@ -150,7 +151,7 @@ x-oss-user-agent:aliyun-sdk-js/6.6.1 Chrome 98.0.4758.80 on Windows 10 64-bit
// } // }
//} //}
u := fmt.Sprintf("https://%s.%s/%s", pre.Data.Bucket, pre.Data.UploadUrl[7:], pre.Data.ObjKey) u := fmt.Sprintf("https://%s.%s/%s", pre.Data.Bucket, pre.Data.UploadUrl[7:], pre.Data.ObjKey)
res, err := base.RestyClient.R(). res, err := base.RestyClient.R().SetContext(ctx).
SetHeaders(map[string]string{ SetHeaders(map[string]string{
"Authorization": resp.Data.AuthKey, "Authorization": resp.Data.AuthKey,
"Content-Type": mineType, "Content-Type": mineType,

View File

@ -11,7 +11,6 @@ import (
"github.com/alist-org/alist/v3/internal/driver" "github.com/alist-org/alist/v3/internal/driver"
"github.com/alist-org/alist/v3/internal/model" "github.com/alist-org/alist/v3/internal/model"
"github.com/alist-org/alist/v3/pkg/utils"
"github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3" "github.com/aws/aws-sdk-go/service/s3"
"github.com/aws/aws-sdk-go/service/s3/s3manager" "github.com/aws/aws-sdk-go/service/s3/s3manager"
@ -31,19 +30,14 @@ func (d *S3) Config() driver.Config {
} }
func (d *S3) GetAddition() driver.Additional { func (d *S3) GetAddition() driver.Additional {
return d.Addition return &d.Addition
} }
func (d *S3) Init(ctx context.Context, storage model.Storage) error { func (d *S3) Init(ctx context.Context) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
if d.Region == "" { if d.Region == "" {
d.Region = "alist" d.Region = "alist"
} }
err = d.initSession() err := d.initSession()
if err != nil { if err != nil {
return err return err
} }
@ -63,11 +57,6 @@ func (d *S3) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]mo
return d.listV1(dir.GetPath()) return d.listV1(dir.GetPath())
} }
//func (d *S3) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *S3) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { func (d *S3) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
path := getKey(file.GetPath(), false) path := getKey(file.GetPath(), false)
disposition := fmt.Sprintf(`attachment;filename="%s"`, url.QueryEscape(stdpath.Base(path))) disposition := fmt.Sprintf(`attachment;filename="%s"`, url.QueryEscape(stdpath.Base(path)))
@ -145,7 +134,7 @@ func (d *S3) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreame
Key: &key, Key: &key,
Body: stream, Body: stream,
} }
_, err := uploader.Upload(input) _, err := uploader.UploadWithContext(ctx, input)
return err return err
} }

View File

@ -21,14 +21,13 @@ type Addition struct {
var config = driver.Config{ var config = driver.Config{
Name: "S3", Name: "S3",
DefaultRoot: "/",
LocalSort: true, LocalSort: true,
CheckStatus: true, CheckStatus: true,
} }
func New() driver.Driver {
return &S3{}
}
func init() { func init() {
op.RegisterDriver(config, New) op.RegisterDriver(func() driver.Driver {
return &S3{}
})
} }

View File

@ -23,15 +23,10 @@ func (d *SFTP) Config() driver.Config {
} }
func (d *SFTP) GetAddition() driver.Additional { func (d *SFTP) GetAddition() driver.Additional {
return d.Addition return &d.Addition
} }
func (d *SFTP) Init(ctx context.Context, storage model.Storage) error { func (d *SFTP) Init(ctx context.Context) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
return d.initClient() return d.initClient()
} }
@ -52,11 +47,6 @@ func (d *SFTP) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]
}) })
} }
//func (d *SFTP) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *SFTP) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { func (d *SFTP) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
remoteFile, err := d.client.Open(file.GetPath()) remoteFile, err := d.client.Open(file.GetPath())
if err != nil { if err != nil {

Some files were not shown because too many files have changed in this diff Show More