Compare commits

..

3 Commits

Author SHA1 Message Date
ex-hentai 0857478516 feat(thunder): allow setting space (#1219)
allows access to files on remote devices via Thunder's tunneling service.
2025-11-03 10:53:38 +08:00
Seven 66d9809057 feat(strm): strm local file (#1127)
* feat(strm): strm local file

* feat: 代码优化

* feat: 访问被strm挂载路径时也更新

* fix: 路径最后带/判断缺失

* fix: 路径最后带/判断缺失

* refactor

* refactor

* fix: close seekable-stream in `generateStrm`

* refactor: lazy create local file

* 优化路径判断

---------

Co-authored-by: KirCute <kircute@foxmail.com>
2025-11-03 10:48:15 +08:00
MoYan db8a7e8caf feat(123): allow modification of the platform header (#1542)
* feat(drivers/123): Allow modification of the platform field

* feat(drivers/123): Set login platfrom as web

* fix(drivers/123): update platform field help value
2025-11-03 10:02:52 +08:00
37 changed files with 266 additions and 3052 deletions
-1
View File
@@ -17,7 +17,6 @@ func Init() {
bootstrap.Log() bootstrap.Log()
bootstrap.InitDB() bootstrap.InitDB()
data.InitData() data.InitData()
bootstrap.InitPlugins()
bootstrap.InitStreamLimit() bootstrap.InitStreamLimit()
bootstrap.InitIndex() bootstrap.InitIndex()
bootstrap.InitUpgradePatch() bootstrap.InitUpgradePatch()
+3 -1
View File
@@ -41,7 +41,9 @@ func (d *Pan123) GetAddition() driver.Additional {
} }
func (d *Pan123) Init(ctx context.Context) error { func (d *Pan123) Init(ctx context.Context) error {
_, err := d.Request(UserInfo, http.MethodGet, nil, nil) _, err := d.Request(UserInfo, http.MethodGet, func(req *resty.Request) {
req.SetHeader("platform", "web")
}, nil)
return err return err
} }
+1
View File
@@ -13,6 +13,7 @@ type Addition struct {
//OrderDirection string `json:"order_direction" type:"select" options:"asc,desc" default:"asc"` //OrderDirection string `json:"order_direction" type:"select" options:"asc,desc" default:"asc"`
AccessToken string AccessToken string
UploadThread int `json:"UploadThread" type:"number" default:"3" help:"the threads of upload"` UploadThread int `json:"UploadThread" type:"number" default:"3" help:"the threads of upload"`
Platform string `json:"platform" type:"string" default:"web" help:"the platform header value, sent with API requests"`
} }
var config = driver.Config{ var config = driver.Config{
+1 -1
View File
@@ -203,7 +203,7 @@ do:
"referer": "https://www.123pan.com/", "referer": "https://www.123pan.com/",
"authorization": "Bearer " + d.AccessToken, "authorization": "Bearer " + d.AccessToken,
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) openlist-client", "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) openlist-client",
"platform": "web", "platform": d.Platform,
"app-version": "3", "app-version": "3",
//"user-agent": base.UserAgent, //"user-agent": base.UserAgent,
}) })
+12
View File
@@ -15,6 +15,7 @@ import (
"github.com/OpenListTeam/OpenList/v4/internal/stream" "github.com/OpenListTeam/OpenList/v4/internal/stream"
"github.com/OpenListTeam/OpenList/v4/pkg/utils" "github.com/OpenListTeam/OpenList/v4/pkg/utils"
"github.com/OpenListTeam/OpenList/v4/server/common" "github.com/OpenListTeam/OpenList/v4/server/common"
log "github.com/sirupsen/logrus"
) )
type Strm struct { type Strm struct {
@@ -40,6 +41,9 @@ func (d *Strm) Init(ctx context.Context) error {
if d.Paths == "" { if d.Paths == "" {
return errors.New("paths is required") return errors.New("paths is required")
} }
if d.SaveStrmToLocal && len(d.SaveStrmLocalPath) <= 0 {
return errors.New("SaveStrmLocalPath is required")
}
d.pathMap = make(map[string][]string) d.pathMap = make(map[string][]string)
for _, path := range strings.Split(d.Paths, "\n") { for _, path := range strings.Split(d.Paths, "\n") {
path = strings.TrimSpace(path) path = strings.TrimSpace(path)
@@ -48,6 +52,11 @@ func (d *Strm) Init(ctx context.Context) error {
} }
k, v := getPair(path) k, v := getPair(path)
d.pathMap[k] = append(d.pathMap[k], v) d.pathMap[k] = append(d.pathMap[k], v)
err := InsertStrm(utils.FixAndCleanPath(strings.TrimSpace(path)), d)
if err != nil {
log.Errorf("insert strmTrie error: %v", err)
continue
}
} }
if len(d.pathMap) == 1 { if len(d.pathMap) == 1 {
for k := range d.pathMap { for k := range d.pathMap {
@@ -87,6 +96,9 @@ func (d *Strm) Drop(ctx context.Context) error {
d.pathMap = nil d.pathMap = nil
d.downloadSuffix = nil d.downloadSuffix = nil
d.supportSuffix = nil d.supportSuffix = nil
for _, path := range strings.Split(d.Paths, "\n") {
RemoveStrm(utils.FixAndCleanPath(strings.TrimSpace(path)), d)
}
return nil return nil
} }
+164
View File
@@ -0,0 +1,164 @@
package strm
import (
"context"
"errors"
"io"
"os"
stdpath "path"
"strings"
"github.com/OpenListTeam/OpenList/v4/internal/model"
"github.com/OpenListTeam/OpenList/v4/internal/op"
"github.com/OpenListTeam/OpenList/v4/internal/stream"
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
log "github.com/sirupsen/logrus"
"github.com/tchap/go-patricia/v2/patricia"
)
var strmTrie = patricia.NewTrie()
func UpdateLocalStrm(ctx context.Context, path string, objs []model.Obj) {
path = utils.FixAndCleanPath(path)
updateLocal := func(driver *Strm, basePath string, objs []model.Obj) {
relParent := strings.TrimPrefix(basePath, driver.MountPath)
localParentPath := stdpath.Join(driver.SaveStrmLocalPath, relParent)
for _, obj := range objs {
localPath := stdpath.Join(localParentPath, obj.GetName())
generateStrm(ctx, driver, obj, localPath)
}
deleteExtraFiles(localParentPath, objs)
}
_ = strmTrie.VisitPrefixes(patricia.Prefix(path), func(needPathPrefix patricia.Prefix, item patricia.Item) error {
strmDrivers := item.([]*Strm)
needPath := string(needPathPrefix)
restPath := strings.TrimPrefix(path, needPath)
if len(restPath) > 0 && restPath[0] != '/' {
return nil
}
for _, strmDriver := range strmDrivers {
strmObjs, _ := utils.SliceConvert(objs, func(obj model.Obj) (model.Obj, error) {
ret := strmDriver.convert2strmObj(ctx, path, obj)
return &ret, nil
})
updateLocal(strmDriver, stdpath.Join(stdpath.Base(needPath), restPath), strmObjs)
}
return nil
})
}
func InsertStrm(dstPath string, d *Strm) error {
prefix := patricia.Prefix(strings.TrimRight(dstPath, "/"))
existing := strmTrie.Get(prefix)
if existing == nil {
if !strmTrie.Insert(prefix, []*Strm{d}) {
return errors.New("failed to insert strm")
}
return nil
}
if lst, ok := existing.([]*Strm); ok {
strmTrie.Set(prefix, append(lst, d))
} else {
return errors.New("invalid trie item type")
}
return nil
}
func RemoveStrm(dstPath string, d *Strm) {
prefix := patricia.Prefix(strings.TrimRight(dstPath, "/"))
existing := strmTrie.Get(prefix)
if existing == nil {
return
}
lst, ok := existing.([]*Strm)
if !ok {
return
}
if len(lst) == 1 && lst[0] == d {
strmTrie.Delete(prefix)
return
}
for i, di := range lst {
if di == d {
newList := append(lst[:i], lst[i+1:]...)
strmTrie.Set(prefix, newList)
return
}
}
}
func generateStrm(ctx context.Context, driver *Strm, obj model.Obj, localPath string) {
link, err := driver.Link(ctx, obj, model.LinkArgs{})
if err != nil {
log.Warnf("failed to generate strm of obj %s: failed to link: %v", localPath, err)
return
}
seekableStream, err := stream.NewSeekableStream(&stream.FileStream{
Obj: obj,
Ctx: ctx,
}, link)
if err != nil {
_ = link.Close()
log.Warnf("failed to generate strm of obj %s: failed to get seekable stream: %v", localPath, err)
return
}
defer seekableStream.Close()
file, err := utils.CreateNestedFile(localPath)
if err != nil {
log.Warnf("failed to generate strm of obj %s: failed to create local file: %v", localPath, err)
return
}
defer file.Close()
if _, err := io.Copy(file, seekableStream); err != nil {
log.Warnf("failed to generate strm of obj %s: copy failed: %v", localPath, err)
}
}
func deleteExtraFiles(localPath string, objs []model.Obj) {
localFiles, err := getLocalFiles(localPath)
if err != nil {
log.Errorf("Failed to read local files from %s: %v", localPath, err)
return
}
objsSet := make(map[string]struct{})
for _, obj := range objs {
if obj.IsDir() {
continue
}
objsSet[stdpath.Join(localPath, obj.GetName())] = struct{}{}
}
for _, localFile := range localFiles {
if _, exists := objsSet[localFile]; !exists {
err := os.Remove(localFile)
if err != nil {
log.Errorf("Failed to delete file: %s, error: %v\n", localFile, err)
} else {
log.Infof("Deleted file %s", localFile)
}
}
}
}
func getLocalFiles(localPath string) ([]string, error) {
var files []string
entries, err := os.ReadDir(localPath)
if err != nil {
return nil, err
}
for _, entry := range entries {
if !entry.IsDir() {
files = append(files, stdpath.Join(localPath, entry.Name()))
}
}
return files, nil
}
func init() {
op.RegisterObjsUpdateHook(UpdateLocalStrm)
}
+3 -1
View File
@@ -11,7 +11,9 @@ type Addition struct {
FilterFileTypes string `json:"filterFileTypes" type:"text" default:"strm" required:"false" help:"Supports suffix name of strm file"` FilterFileTypes string `json:"filterFileTypes" type:"text" default:"strm" required:"false" help:"Supports suffix name of strm file"`
DownloadFileTypes string `json:"downloadFileTypes" type:"text" default:"ass" required:"false" help:"Files need to download with strm (usally subtitles)"` DownloadFileTypes string `json:"downloadFileTypes" type:"text" default:"ass" required:"false" help:"Files need to download with strm (usally subtitles)"`
EncodePath bool `json:"encodePath" default:"true" required:"true" help:"encode the path in the strm file"` EncodePath bool `json:"encodePath" default:"true" required:"true" help:"encode the path in the strm file"`
LocalModel bool `json:"localModel" default:"false" help:"enable local mode"` WithoutUrl bool `json:"withoutUrl" default:"false" help:"strm file content without URL prefix"`
SaveStrmToLocal bool `json:"SaveStrmToLocal" default:"false" help:"save strm file locally"`
SaveStrmLocalPath string `json:"SaveStrmLocalPath" type:"text" help:"save strm file local path"`
} }
var config = driver.Config{ var config = driver.Config{
+23 -19
View File
@@ -61,6 +61,23 @@ func (d *Strm) list(ctx context.Context, dst, sub string, args *fs.ListArgs) ([]
var validObjs []model.Obj var validObjs []model.Obj
for _, obj := range objs { for _, obj := range objs {
objRes := d.convert2strmObj(ctx, reqPath, obj)
thumb, ok := model.GetThumb(obj)
if !ok {
validObjs = append(validObjs, &objRes)
continue
}
validObjs = append(validObjs, &model.ObjThumb{
Object: objRes,
Thumbnail: model.Thumbnail{
Thumbnail: thumb,
},
})
}
return validObjs, nil
}
func (d *Strm) convert2strmObj(ctx context.Context, reqPath string, obj model.Obj) model.Object {
id, name, path := "", obj.GetName(), "" id, name, path := "", obj.GetName(), ""
size := int64(0) size := int64(0)
if !obj.IsDir() { if !obj.IsDir() {
@@ -73,10 +90,10 @@ func (d *Strm) list(ctx context.Context, dst, sub string, args *fs.ListArgs) ([]
} else if _, ok := d.downloadSuffix[ext]; ok { } else if _, ok := d.downloadSuffix[ext]; ok {
size = obj.GetSize() size = obj.GetSize()
} else { } else {
continue
} }
} }
objRes := model.Object{ return model.Object{
ID: id, ID: id,
Path: path, Path: path,
Name: name, Name: name,
@@ -84,21 +101,6 @@ func (d *Strm) list(ctx context.Context, dst, sub string, args *fs.ListArgs) ([]
Modified: obj.ModTime(), Modified: obj.ModTime(),
IsFolder: obj.IsDir(), IsFolder: obj.IsDir(),
} }
thumb, ok := model.GetThumb(obj)
if !ok {
validObjs = append(validObjs, &objRes)
continue
}
validObjs = append(validObjs, &model.ObjThumb{
Object: objRes,
Thumbnail: model.Thumbnail{
Thumbnail: thumb,
},
})
}
return validObjs, nil
} }
func (d *Strm) getLink(ctx context.Context, path string) string { func (d *Strm) getLink(ctx context.Context, path string) string {
@@ -110,7 +112,7 @@ func (d *Strm) getLink(ctx context.Context, path string) string {
signPath := sign.Sign(path) signPath := sign.Sign(path)
finalPath = fmt.Sprintf("%s?sign=%s", finalPath, signPath) finalPath = fmt.Sprintf("%s?sign=%s", finalPath, signPath)
} }
if d.LocalModel { if d.WithoutUrl {
return finalPath return finalPath
} }
apiUrl := d.SiteUrl apiUrl := d.SiteUrl
@@ -119,7 +121,9 @@ func (d *Strm) getLink(ctx context.Context, path string) string {
} else { } else {
apiUrl = common.GetApiUrl(ctx) apiUrl = common.GetApiUrl(ctx)
} }
if !strings.HasPrefix(finalPath, "/") {
finalPath = "/" + finalPath
}
return fmt.Sprintf("%s/d%s", return fmt.Sprintf("%s/d%s",
apiUrl, apiUrl,
finalPath) finalPath)
+28 -3
View File
@@ -4,6 +4,7 @@ import (
"context" "context"
"fmt" "fmt"
"net/http" "net/http"
"net/url"
"strconv" "strconv"
"strings" "strings"
@@ -68,6 +69,7 @@ func (x *Thunder) Init(ctx context.Context) (err error) {
PackageName: "com.xunlei.downloadprovider", PackageName: "com.xunlei.downloadprovider",
UserAgent: "ANDROID-com.xunlei.downloadprovider/8.31.0.9726 netWorkType/5G appid/40 deviceName/Xiaomi_M2004j7ac deviceModel/M2004J7AC OSVersion/12 protocolVersion/301 platformVersion/10 sdkVersion/512000 Oauth2Client/0.9 (Linux 4_14_186-perf-gddfs8vbb238b) (JAVA 0)", UserAgent: "ANDROID-com.xunlei.downloadprovider/8.31.0.9726 netWorkType/5G appid/40 deviceName/Xiaomi_M2004j7ac deviceModel/M2004J7AC OSVersion/12 protocolVersion/301 platformVersion/10 sdkVersion/512000 Oauth2Client/0.9 (Linux 4_14_186-perf-gddfs8vbb238b) (JAVA 0)",
DownloadUserAgent: "Dalvik/2.1.0 (Linux; U; Android 12; M2004J7AC Build/SP1A.210812.016)", DownloadUserAgent: "Dalvik/2.1.0 (Linux; U; Android 12; M2004J7AC Build/SP1A.210812.016)",
Space: x.Space,
refreshCTokenCk: func(token string) { refreshCTokenCk: func(token string) {
x.CaptchaToken = token x.CaptchaToken = token
op.MustSaveDriverStorage(x) op.MustSaveDriverStorage(x)
@@ -167,6 +169,7 @@ func (x *ThunderExpert) Init(ctx context.Context) (err error) {
UserAgent: x.UserAgent, UserAgent: x.UserAgent,
DownloadUserAgent: x.DownloadUserAgent, DownloadUserAgent: x.DownloadUserAgent,
UseVideoUrl: x.UseVideoUrl, UseVideoUrl: x.UseVideoUrl,
Space: x.Space,
refreshCTokenCk: func(token string) { refreshCTokenCk: func(token string) {
x.CaptchaToken = token x.CaptchaToken = token
@@ -281,7 +284,7 @@ func (xc *XunLeiCommon) Link(ctx context.Context, file model.Obj, args model.Lin
_, err := xc.Request(FILE_API_URL+"/{fileID}", http.MethodGet, func(r *resty.Request) { _, err := xc.Request(FILE_API_URL+"/{fileID}", http.MethodGet, func(r *resty.Request) {
r.SetContext(ctx) r.SetContext(ctx)
r.SetPathParam("fileID", file.GetID()) r.SetPathParam("fileID", file.GetID())
//r.SetQueryParam("space", "") r.SetQueryParam("space", xc.Space)
}, &lFile) }, &lFile)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -322,6 +325,7 @@ func (xc *XunLeiCommon) MakeDir(ctx context.Context, parentDir model.Obj, dirNam
"kind": FOLDER, "kind": FOLDER,
"name": dirName, "name": dirName,
"parent_id": parentDir.GetID(), "parent_id": parentDir.GetID(),
"space": xc.Space,
}) })
}, nil) }, nil)
return err return err
@@ -333,6 +337,7 @@ func (xc *XunLeiCommon) Move(ctx context.Context, srcObj, dstDir model.Obj) erro
r.SetBody(&base.Json{ r.SetBody(&base.Json{
"to": base.Json{"parent_id": dstDir.GetID()}, "to": base.Json{"parent_id": dstDir.GetID()},
"ids": []string{srcObj.GetID()}, "ids": []string{srcObj.GetID()},
"space": xc.Space,
}) })
}, nil) }, nil)
return err return err
@@ -342,7 +347,10 @@ func (xc *XunLeiCommon) Rename(ctx context.Context, srcObj model.Obj, newName st
_, err := xc.Request(FILE_API_URL+"/{fileID}", http.MethodPatch, func(r *resty.Request) { _, err := xc.Request(FILE_API_URL+"/{fileID}", http.MethodPatch, func(r *resty.Request) {
r.SetContext(ctx) r.SetContext(ctx)
r.SetPathParam("fileID", srcObj.GetID()) r.SetPathParam("fileID", srcObj.GetID())
r.SetBody(&base.Json{"name": newName}) r.SetBody(&base.Json{
"name": newName,
"space": xc.Space,
})
}, nil) }, nil)
return err return err
} }
@@ -353,6 +361,7 @@ func (xc *XunLeiCommon) Copy(ctx context.Context, srcObj, dstDir model.Obj) erro
r.SetBody(&base.Json{ r.SetBody(&base.Json{
"to": base.Json{"parent_id": dstDir.GetID()}, "to": base.Json{"parent_id": dstDir.GetID()},
"ids": []string{srcObj.GetID()}, "ids": []string{srcObj.GetID()},
"space": xc.Space,
}) })
}, nil) }, nil)
return err return err
@@ -362,6 +371,7 @@ func (xc *XunLeiCommon) Remove(ctx context.Context, obj model.Obj) error {
_, err := xc.Request(FILE_API_URL+"/{fileID}/trash", http.MethodPatch, func(r *resty.Request) { _, err := xc.Request(FILE_API_URL+"/{fileID}/trash", http.MethodPatch, func(r *resty.Request) {
r.SetContext(ctx) r.SetContext(ctx)
r.SetPathParam("fileID", obj.GetID()) r.SetPathParam("fileID", obj.GetID())
r.SetQueryParam("space", xc.Space)
r.SetBody("{}") r.SetBody("{}")
}, nil) }, nil)
return err return err
@@ -387,6 +397,7 @@ func (xc *XunLeiCommon) Put(ctx context.Context, dstDir model.Obj, file model.Fi
"size": file.GetSize(), "size": file.GetSize(),
"hash": gcid, "hash": gcid,
"upload_type": UPLOAD_TYPE_RESUMABLE, "upload_type": UPLOAD_TYPE_RESUMABLE,
"space": xc.Space,
}) })
}, &resp) }, &resp)
if err != nil { if err != nil {
@@ -430,7 +441,7 @@ func (xc *XunLeiCommon) getFiles(ctx context.Context, folderId string) ([]model.
_, err := xc.Request(FILE_API_URL, http.MethodGet, func(r *resty.Request) { _, err := xc.Request(FILE_API_URL, http.MethodGet, func(r *resty.Request) {
r.SetContext(ctx) r.SetContext(ctx)
r.SetQueryParams(map[string]string{ r.SetQueryParams(map[string]string{
"space": "", "space": xc.Space,
"__type": "drive", "__type": "drive",
"refresh": "true", "refresh": "true",
"__sync": "true", "__sync": "true",
@@ -440,6 +451,17 @@ func (xc *XunLeiCommon) getFiles(ctx context.Context, folderId string) ([]model.
"limit": "100", "limit": "100",
"filters": `{"phase":{"eq":"PHASE_TYPE_COMPLETE"},"trashed":{"eq":false}}`, "filters": `{"phase":{"eq":"PHASE_TYPE_COMPLETE"},"trashed":{"eq":false}}`,
}) })
// 获取硬盘挂载目录等
if xc.Space != "" {
r.SetQueryParamsFromValues(url.Values{
"with": []string{
"withCategoryDiskMountPath",
"withCategoryDriveCachePath",
"withCategoryHistoryDownloadPath",
"withReadOnlyFS",
},
})
}
}, &fileList) }, &fileList)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -576,6 +598,7 @@ func (xc *XunLeiCommon) OfflineDownload(ctx context.Context, fileUrl string, par
"name": fileName, "name": fileName,
"parent_id": parentDir.GetID(), "parent_id": parentDir.GetID(),
"upload_type": UPLOAD_TYPE_URL, "upload_type": UPLOAD_TYPE_URL,
"space": xc.Space,
"url": base.Json{ "url": base.Json{
"url": fileUrl, "url": fileUrl,
}, },
@@ -602,6 +625,7 @@ func (xc *XunLeiCommon) OfflineList(ctx context.Context, nextPageToken string) (
"type": "offline", "type": "offline",
"limit": "10000", "limit": "10000",
"page_token": nextPageToken, "page_token": nextPageToken,
"space": xc.Space,
}) })
}, &resp) }, &resp)
@@ -618,6 +642,7 @@ func (xc *XunLeiCommon) DeleteOfflineTasks(ctx context.Context, taskIDs []string
SetQueryParams(map[string]string{ SetQueryParams(map[string]string{
"task_ids": strings.Join(taskIDs, ","), "task_ids": strings.Join(taskIDs, ","),
"delete_files": strconv.FormatBool(deleteFiles), "delete_files": strconv.FormatBool(deleteFiles),
"space": xc.Space,
}) })
}, nil) }, nil)
if err != nil { if err != nil {
+4 -1
View File
@@ -46,6 +46,8 @@ type ExpertAddition struct {
//优先使用视频链接代替下载链接 //优先使用视频链接代替下载链接
UseVideoUrl bool `json:"use_video_url"` UseVideoUrl bool `json:"use_video_url"`
Space string `json:"space" default:"" help:"device id for remote device"`
} }
// 登录特征,用于判断是否重新登录 // 登录特征,用于判断是否重新登录
@@ -80,6 +82,8 @@ type Addition struct {
CreditKey string `json:"credit_key" help:"credit key,used for login"` CreditKey string `json:"credit_key" help:"credit key,used for login"`
// 登录设备ID // 登录设备ID
DeviceID string `json:"device_id" default:""` DeviceID string `json:"device_id" default:""`
Space string `json:"space" default:"" help:"device id for remote device"`
} }
// 登录特征,用于判断是否重新登录 // 登录特征,用于判断是否重新登录
@@ -90,7 +94,6 @@ func (i *Addition) GetIdentity() string {
var config = driver.Config{ var config = driver.Config{
Name: "Thunder", Name: "Thunder",
LocalSort: true, LocalSort: true,
OnlyProxy: true,
} }
var configExpert = driver.Config{ var configExpert = driver.Config{
+1
View File
@@ -68,6 +68,7 @@ type Common struct {
UserAgent string UserAgent string
DownloadUserAgent string DownloadUserAgent string
UseVideoUrl bool UseVideoUrl bool
Space string
// 验证码token刷新成功回调 // 验证码token刷新成功回调
refreshCTokenCk func(token string) refreshCTokenCk func(token string)
+1 -5
View File
@@ -9,7 +9,6 @@ require (
github.com/OpenListTeam/sftpd-openlist v1.0.1 github.com/OpenListTeam/sftpd-openlist v1.0.1
github.com/OpenListTeam/tache v0.2.0 github.com/OpenListTeam/tache v0.2.0
github.com/OpenListTeam/times v0.1.0 github.com/OpenListTeam/times v0.1.0
github.com/OpenListTeam/wazero-wasip2 v0.0.0-20251015145605-cd3a2c9131d9
github.com/OpenListTeam/wopan-sdk-go v0.1.5 github.com/OpenListTeam/wopan-sdk-go v0.1.5
github.com/ProtonMail/go-crypto v1.3.0 github.com/ProtonMail/go-crypto v1.3.0
github.com/SheltonZhu/115driver v1.1.1 github.com/SheltonZhu/115driver v1.1.1
@@ -64,7 +63,6 @@ require (
github.com/stretchr/testify v1.10.0 github.com/stretchr/testify v1.10.0
github.com/t3rm1n4l/go-mega v0.0.0-20241213151442-a19cff0ec7b5 github.com/t3rm1n4l/go-mega v0.0.0-20241213151442-a19cff0ec7b5
github.com/tchap/go-patricia/v2 v2.3.3 github.com/tchap/go-patricia/v2 v2.3.3
github.com/tetratelabs/wazero v1.9.0
github.com/u2takey/ffmpeg-go v0.5.0 github.com/u2takey/ffmpeg-go v0.5.0
github.com/upyun/go-sdk/v3 v3.0.4 github.com/upyun/go-sdk/v3 v3.0.4
github.com/winfsp/cgofuse v1.6.0 github.com/winfsp/cgofuse v1.6.0
@@ -194,7 +192,7 @@ require (
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
github.com/bytedance/sonic v1.13.3 // indirect github.com/bytedance/sonic v1.13.3 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/coreos/go-semver v0.3.1 github.com/coreos/go-semver v0.3.1 // indirect
github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 // indirect github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect
@@ -305,5 +303,3 @@ replace github.com/ProtonMail/go-proton-api => github.com/henrybear327/go-proton
replace github.com/cronokirby/saferith => github.com/Da3zKi7/saferith v0.33.0-fixed replace github.com/cronokirby/saferith => github.com/Da3zKi7/saferith v0.33.0-fixed
// replace github.com/OpenListTeam/115-sdk-go => ../../OpenListTeam/115-sdk-go // replace github.com/OpenListTeam/115-sdk-go => ../../OpenListTeam/115-sdk-go
replace google.golang.org/genproto => google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822
-4
View File
@@ -53,8 +53,6 @@ github.com/OpenListTeam/tache v0.2.0 h1:Q4MjuyECn0CZCf1ZF91JaVaZTaps1mOTAm8bFj8s
github.com/OpenListTeam/tache v0.2.0/go.mod h1:qmnZ/VpY2DUlmjg3UoDeNFy/LRqrw0biN3hYEEGc/+A= github.com/OpenListTeam/tache v0.2.0/go.mod h1:qmnZ/VpY2DUlmjg3UoDeNFy/LRqrw0biN3hYEEGc/+A=
github.com/OpenListTeam/times v0.1.0 h1:qknxw+qj5CYKgXAwydA102UEpPcpU8TYNGRmwRyPYpg= github.com/OpenListTeam/times v0.1.0 h1:qknxw+qj5CYKgXAwydA102UEpPcpU8TYNGRmwRyPYpg=
github.com/OpenListTeam/times v0.1.0/go.mod h1:Jx7qen5NCYzKk2w14YuvU48YYMcPa1P9a+EJePC15Pc= github.com/OpenListTeam/times v0.1.0/go.mod h1:Jx7qen5NCYzKk2w14YuvU48YYMcPa1P9a+EJePC15Pc=
github.com/OpenListTeam/wazero-wasip2 v0.0.0-20251015145605-cd3a2c9131d9 h1:yddTD9Fxh6bLMLmG0hSR7Eh6XkoK0RMlE4N1e6/+Iy8=
github.com/OpenListTeam/wazero-wasip2 v0.0.0-20251015145605-cd3a2c9131d9/go.mod h1:+BpydPG2cUQHYFwH3/lVmvXyMl/zxHW+XM+XTSzqu2Q=
github.com/OpenListTeam/wopan-sdk-go v0.1.5 h1:iKKcVzIqBgtGDbn0QbdWrCazSGxXFmYFyrnFBG+U8dI= github.com/OpenListTeam/wopan-sdk-go v0.1.5 h1:iKKcVzIqBgtGDbn0QbdWrCazSGxXFmYFyrnFBG+U8dI=
github.com/OpenListTeam/wopan-sdk-go v0.1.5/go.mod h1:otynv0CgSNUClPpUgZ44qCZGcMRe0dc83Pkk65xAunI= github.com/OpenListTeam/wopan-sdk-go v0.1.5/go.mod h1:otynv0CgSNUClPpUgZ44qCZGcMRe0dc83Pkk65xAunI=
github.com/ProtonMail/bcrypt v0.0.0-20210511135022-227b4adcab57/go.mod h1:HecWFHognK8GfRDGnFQbW/LiV7A3MX3gZVs45vk5h8I= github.com/ProtonMail/bcrypt v0.0.0-20210511135022-227b4adcab57/go.mod h1:HecWFHognK8GfRDGnFQbW/LiV7A3MX3gZVs45vk5h8I=
@@ -690,8 +688,6 @@ github.com/taruti/bytepool v0.0.0-20160310082835-5e3a9ea56543 h1:6Y51mutOvRGRx6K
github.com/taruti/bytepool v0.0.0-20160310082835-5e3a9ea56543/go.mod h1:jpwqYA8KUVEvSUJHkCXsnBRJCSKP1BMa81QZ6kvRpow= github.com/taruti/bytepool v0.0.0-20160310082835-5e3a9ea56543/go.mod h1:jpwqYA8KUVEvSUJHkCXsnBRJCSKP1BMa81QZ6kvRpow=
github.com/tchap/go-patricia/v2 v2.3.3 h1:xfNEsODumaEcCcY3gI0hYPZ/PcpVv5ju6RMAhgwZDDc= github.com/tchap/go-patricia/v2 v2.3.3 h1:xfNEsODumaEcCcY3gI0hYPZ/PcpVv5ju6RMAhgwZDDc=
github.com/tchap/go-patricia/v2 v2.3.3/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= github.com/tchap/go-patricia/v2 v2.3.3/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k=
github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I=
github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM=
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4= github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4= github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4=
github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso= github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso=
-24
View File
@@ -1,24 +0,0 @@
//go:build !unix && !windows
package alloc // import "github.com/ncruces/go-sqlite3/internal/alloc"
import "github.com/tetratelabs/wazero/experimental"
func NewMemory(cap, max uint64) experimental.LinearMemory {
return &sliceMemory{make([]byte, 0, cap)}
}
type sliceMemory struct {
buf []byte
}
func (b *sliceMemory) Free() {}
func (b *sliceMemory) Reallocate(size uint64) []byte {
if cap := uint64(cap(b.buf)); size > cap {
b.buf = append(b.buf[:cap], make([]byte, size-cap)...)
} else {
b.buf = b.buf[:size]
}
return b.buf
}
-14
View File
@@ -1,14 +0,0 @@
package alloc_test // import "github.com/ncruces/go-sqlite3/internal/alloc"
import (
"math"
"testing"
"github.com/OpenListTeam/OpenList/v4/internal/alloc"
)
func TestVirtual(t *testing.T) {
defer func() { _ = recover() }()
alloc.NewMemory(math.MaxInt+2, math.MaxInt+2)
t.Error("want panic")
}
-75
View File
@@ -1,75 +0,0 @@
//go:build unix
package alloc // import "github.com/ncruces/go-sqlite3/internal/alloc"
import (
"math"
"github.com/tetratelabs/wazero/experimental"
"golang.org/x/sys/unix"
)
func NewMemory(cap, max uint64) experimental.LinearMemory {
// Round up to the page size.
rnd := uint64(unix.Getpagesize() - 1)
res := (max + rnd) &^ rnd
if res > math.MaxInt {
// This ensures int(res) overflows to a negative value,
// and unix.Mmap returns EINVAL.
res = math.MaxUint64
}
com := res
prot := unix.PROT_READ | unix.PROT_WRITE
if cap < max { // Commit memory only if cap=max.
com = 0
prot = unix.PROT_NONE
}
// Reserve res bytes of address space, to ensure we won't need to move it.
// A protected, private, anonymous mapping should not commit memory.
b, err := unix.Mmap(-1, 0, int(res), prot, unix.MAP_PRIVATE|unix.MAP_ANON)
if err != nil {
panic(err)
}
return &mmappedMemory{buf: b[:com]}
}
// The slice covers the entire mmapped memory:
// - len(buf) is the already committed memory,
// - cap(buf) is the reserved address space.
type mmappedMemory struct {
buf []byte
}
func (m *mmappedMemory) Reallocate(size uint64) []byte {
com := uint64(len(m.buf))
res := uint64(cap(m.buf))
if com < size && size <= res {
// Grow geometrically, round up to the page size.
rnd := uint64(unix.Getpagesize() - 1)
new := com + com>>3
new = min(max(size, new), res)
new = (new + rnd) &^ rnd
// Commit additional memory up to new bytes.
err := unix.Mprotect(m.buf[com:new], unix.PROT_READ|unix.PROT_WRITE)
if err != nil {
return nil
}
m.buf = m.buf[:new] // Update committed memory.
}
// Limit returned capacity because bytes beyond
// len(m.buf) have not yet been committed.
return m.buf[:size:len(m.buf)]
}
func (m *mmappedMemory) Free() {
err := unix.Munmap(m.buf[:cap(m.buf)])
if err != nil {
panic(err)
}
m.buf = nil
}
-76
View File
@@ -1,76 +0,0 @@
package alloc // import "github.com/ncruces/go-sqlite3/internal/alloc"
import (
"math"
"unsafe"
"github.com/tetratelabs/wazero/experimental"
"golang.org/x/sys/windows"
)
func NewMemory(cap, max uint64) experimental.LinearMemory {
// Round up to the page size.
rnd := uint64(windows.Getpagesize() - 1)
res := (max + rnd) &^ rnd
if res > math.MaxInt {
// This ensures uintptr(res) overflows to a large value,
// and windows.VirtualAlloc returns an error.
res = math.MaxUint64
}
com := res
kind := windows.MEM_COMMIT
if cap < max { // Commit memory only if cap=max.
com = 0
kind = windows.MEM_RESERVE
}
// Reserve res bytes of address space, to ensure we won't need to move it.
r, err := windows.VirtualAlloc(0, uintptr(res), uint32(kind), windows.PAGE_READWRITE)
if err != nil {
panic(err)
}
buf := unsafe.Slice((*byte)(unsafe.Pointer(r)), int(max))
mem := virtualMemory{addr: r, buf: buf[:com:res]}
return &mem
}
// The slice covers the entire mmapped memory:
// - len(buf) is the already committed memory,
// - cap(buf) is the reserved address space.
type virtualMemory struct {
buf []byte
addr uintptr
}
func (m *virtualMemory) Reallocate(size uint64) []byte {
com := uint64(len(m.buf))
res := uint64(cap(m.buf))
if com < size && size <= res {
// Grow geometrically, round up to the page size.
rnd := uint64(windows.Getpagesize() - 1)
new := com + com>>3
new = min(max(size, new), res)
new = (new + rnd) &^ rnd
// Commit additional memory up to new bytes.
_, err := windows.VirtualAlloc(m.addr, uintptr(new), windows.MEM_COMMIT, windows.PAGE_READWRITE)
if err != nil {
return nil
}
m.buf = m.buf[:new] // Update committed memory.
}
// Limit returned capacity because bytes beyond
// len(m.buf) have not yet been committed.
return m.buf[:size:len(m.buf)]
}
func (m *virtualMemory) Free() {
err := windows.VirtualFree(m.addr, 0, windows.MEM_RELEASE)
if err != nil {
panic(err)
}
m.addr = 0
}
-23
View File
@@ -1,23 +0,0 @@
// internal/bootstrap/plugin.go
package bootstrap
import (
"context"
"fmt"
"github.com/OpenListTeam/OpenList/v4/cmd/flags"
"github.com/OpenListTeam/OpenList/v4/internal/plugin"
)
// InitPlugins 初始化插件管理器
func InitPlugins() {
// 2. 创建并初始化 Manager
// "data" 目录应从配置中获取
manager, err := plugin.NewManager(context.Background(), flags.DataDir)
if err != nil {
// 在启动时,如果插件系统失败,应该 panic
panic(fmt.Sprintf("Failed to initialize plugin manager: %v", err))
}
plugin.PluginManager = manager
}
+1 -1
View File
@@ -12,7 +12,7 @@ var db *gorm.DB
func Init(d *gorm.DB) { func Init(d *gorm.DB) {
db = d db = d
err := AutoMigrate(new(model.Storage), new(model.User), new(model.Meta), new(model.SettingItem), new(model.SearchNode), new(model.TaskItem), new(model.SSHPublicKey), new(model.SharingDB), new(model.Plugin)) err := AutoMigrate(new(model.Storage), new(model.User), new(model.Meta), new(model.SettingItem), new(model.SearchNode), new(model.TaskItem), new(model.SSHPublicKey), new(model.SharingDB))
if err != nil { if err != nil {
log.Fatalf("failed migrate database: %s", err.Error()) log.Fatalf("failed migrate database: %s", err.Error())
} }
-47
View File
@@ -1,47 +0,0 @@
package db
import (
"context"
"github.com/OpenListTeam/OpenList/v4/internal/model"
"gorm.io/gorm"
)
// CreatePlugin 在数据库中插入一条新的插件记录
// 如果记录已存在,则会更新它 (Upsert)
func CreatePlugin(ctx context.Context, plugin *model.Plugin) error {
return db.WithContext(ctx).Save(plugin).Error
}
// GetPluginByID 从数据库中根据 ID 查询单个插件
func GetPluginByID(ctx context.Context, id string) (*model.Plugin, error) {
var plugin model.Plugin
err := db.WithContext(ctx).First(&plugin, "id = ?", id).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil // 返回 nil, nil 表示未找到
}
return nil, err
}
return &plugin, nil
}
// GetAllPlugins 从数据库中获取所有已安装的插件
func GetAllPlugins(ctx context.Context) ([]*model.Plugin, error) {
var plugins []*model.Plugin
err := db.WithContext(ctx).Find(&plugins).Error
return plugins, err
}
// DeletePluginByID 从数据库中根据 ID 删除一个插件
func DeletePluginByID(ctx context.Context, id string) error {
return db.WithContext(ctx).Delete(&model.Plugin{}, "id = ?", id).Error
}
// UpdatePluginStatus 更新指定插件的状态和消息
func UpdatePluginStatus(ctx context.Context, pluginID string, status model.PluginStatus, message string) error {
return db.WithContext(ctx).Model(&model.Plugin{}).Where("id = ?", pluginID).Updates(map[string]interface{}{
"status": status,
"message": message,
}).Error
}

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