Compare commits

..

1 Commits

Author SHA1 Message Date
1a8307467b fix(deps): update module google.golang.org/appengine to v2 2025-08-13 17:32:32 +00:00
53 changed files with 245 additions and 2168 deletions

View File

@ -1,38 +0,0 @@
name: Sync to Gitee
on:
push:
branches:
- main
workflow_dispatch:
jobs:
sync:
runs-on: ubuntu-latest
name: Sync GitHub to Gitee
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup SSH
run: |
mkdir -p ~/.ssh
echo "${{ secrets.GITEE_SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan gitee.com >> ~/.ssh/known_hosts
- name: Create single commit and push
run: |
git config user.name "GitHub Actions"
git config user.email "actions@github.com"
# Create a new branch
git checkout --orphan new-main
git add .
git commit -m "Sync from GitHub: $(date)"
# Add Gitee remote and force push
git remote add gitee ${{ vars.GITEE_REPO_URL }}
git push --force gitee new-main:main

View File

@ -14,17 +14,19 @@ FROM openlistteam/openlist-base-image:${BASE_IMAGE_TAG}
LABEL MAINTAINER="OpenList" LABEL MAINTAINER="OpenList"
ARG INSTALL_FFMPEG=false ARG INSTALL_FFMPEG=false
ARG INSTALL_ARIA2=false ARG INSTALL_ARIA2=false
ARG USER=openlist
ARG UID=1001
ARG GID=1001
WORKDIR /opt/openlist/ WORKDIR /opt/openlist/
RUN addgroup -g 1001 openlist && \ COPY --chmod=755 --from=builder /app/bin/openlist ./
adduser -D -u 1001 -G openlist openlist && \ COPY --chmod=755 entrypoint.sh /entrypoint.sh
mkdir -p /opt/openlist/data RUN adduser -u ${UID} -g ${GID} -h /opt/openlist/data -D -s /bin/sh ${USER} \
&& chown -R ${UID}:${GID} /opt \
&& chown -R ${UID}:${GID} /entrypoint.sh
COPY --from=builder --chmod=755 --chown=1001:1001 /app/bin/openlist ./ USER ${USER}
COPY --chmod=755 --chown=1001:1001 entrypoint.sh /entrypoint.sh
USER openlist
RUN /entrypoint.sh version RUN /entrypoint.sh version
ENV UMASK=022 RUN_ARIA2=${INSTALL_ARIA2} ENV UMASK=022 RUN_ARIA2=${INSTALL_ARIA2}

View File

@ -4,17 +4,20 @@ LABEL MAINTAINER="OpenList"
ARG TARGETPLATFORM ARG TARGETPLATFORM
ARG INSTALL_FFMPEG=false ARG INSTALL_FFMPEG=false
ARG INSTALL_ARIA2=false ARG INSTALL_ARIA2=false
ARG USER=openlist
ARG UID=1001
ARG GID=1001
WORKDIR /opt/openlist/ WORKDIR /opt/openlist/
RUN addgroup -g 1001 openlist && \ COPY --chmod=755 /build/${TARGETPLATFORM}/openlist ./
adduser -D -u 1001 -G openlist openlist && \ COPY --chmod=755 entrypoint.sh /entrypoint.sh
mkdir -p /opt/openlist/data
COPY --chmod=755 --chown=1001:1001 /build/${TARGETPLATFORM}/openlist ./ RUN adduser -u ${UID} -g ${GID} -h /opt/openlist/data -D -s /bin/sh ${USER} \
COPY --chmod=755 --chown=1001:1001 entrypoint.sh /entrypoint.sh && chown -R ${UID}:${GID} /opt \
&& chown -R ${UID}:${GID} /entrypoint.sh
USER openlist USER ${USER}
RUN /entrypoint.sh version RUN /entrypoint.sh version
ENV UMASK=022 RUN_ARIA2=${INSTALL_ARIA2} ENV UMASK=022 RUN_ARIA2=${INSTALL_ARIA2}

View File

@ -6,9 +6,10 @@ services:
ports: ports:
- '5244:5244' - '5244:5244'
- '5245:5245' - '5245:5245'
user: '0:0'
environment: environment:
- PUID=0
- PGID=0
- UMASK=022 - UMASK=022
- TZ=Asia/Shanghai - TZ=UTC
container_name: openlist container_name: openlist
image: 'openlistteam/openlist:latest' image: 'openlistteam/openlist:latest'

View File

@ -69,45 +69,13 @@ func (d *Open123) List(ctx context.Context, dir model.Obj, args model.ListArgs)
func (d *Open123) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { func (d *Open123) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
fileId, _ := strconv.ParseInt(file.GetID(), 10, 64) fileId, _ := strconv.ParseInt(file.GetID(), 10, 64)
if d.DirectLink {
res, err := d.getDirectLink(fileId)
if err != nil {
return nil, err
}
if d.DirectLinkPrivateKey == "" {
duration := 365 * 24 * time.Hour // 缓存1年
return &model.Link{
URL: res.Data.URL,
Expiration: &duration,
}, nil
}
u, err := d.getUserInfo()
if err != nil {
return nil, err
}
duration := time.Duration(d.DirectLinkValidDuration) * time.Minute
newURL, err := d.SignURL(res.Data.URL, d.DirectLinkPrivateKey,
u.Data.UID, duration)
if err != nil {
return nil, err
}
return &model.Link{
URL: newURL,
Expiration: &duration,
}, nil
}
res, err := d.getDownloadInfo(fileId) res, err := d.getDownloadInfo(fileId)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &model.Link{URL: res.Data.DownloadUrl}, nil link := model.Link{URL: res.Data.DownloadUrl}
return &link, nil
} }
func (d *Open123) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error { func (d *Open123) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {

View File

@ -23,11 +23,6 @@ type Addition struct {
// 上传线程数 // 上传线程数
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"`
// 使用直链
DirectLink bool `json:"DirectLink" type:"boolean" default:"false" required:"false" help:"use direct link when download file"`
DirectLinkPrivateKey string `json:"DirectLinkPrivateKey" required:"false" help:"private key for direct link, if URL authentication is enabled"`
DirectLinkValidDuration int64 `json:"DirectLinkValidDuration" type:"number" default:"30" required:"false" help:"minutes, if URL authentication is enabled"`
driver.RootID driver.RootID
} }

View File

@ -127,7 +127,7 @@ type RefreshTokenResp struct {
type UserInfoResp struct { type UserInfoResp struct {
BaseResp BaseResp
Data struct { Data struct {
UID uint64 `json:"uid"` UID int64 `json:"uid"`
Username string `json:"username"` Username string `json:"username"`
DisplayName string `json:"displayName"` DisplayName string `json:"displayName"`
HeadImage string `json:"headImage"` HeadImage string `json:"headImage"`
@ -158,13 +158,6 @@ type DownloadInfoResp struct {
} `json:"data"` } `json:"data"`
} }
type DirectLinkResp struct {
BaseResp
Data struct {
URL string `json:"url"`
} `json:"data"`
}
// 创建文件V2返回 // 创建文件V2返回
type UploadCreateResp struct { type UploadCreateResp struct {
BaseResp BaseResp

View File

@ -70,8 +70,6 @@ func (d *Open123) Upload(ctx context.Context, file model.FileStreamer, createRes
var reader *stream.SectionReader var reader *stream.SectionReader
var rateLimitedRd io.Reader var rateLimitedRd io.Reader
sliceMD5 := "" sliceMD5 := ""
// 表单
b := bytes.NewBuffer(make([]byte, 0, 2048))
threadG.GoWithLifecycle(errgroup.Lifecycle{ threadG.GoWithLifecycle(errgroup.Lifecycle{
Before: func(ctx context.Context) error { Before: func(ctx context.Context) error {
if reader == nil { if reader == nil {
@ -86,6 +84,7 @@ func (d *Open123) Upload(ctx context.Context, file model.FileStreamer, createRes
if err != nil { if err != nil {
return err return err
} }
rateLimitedRd = driver.NewLimitedUploadStream(ctx, reader)
} }
return nil return nil
}, },
@ -93,8 +92,9 @@ func (d *Open123) Upload(ctx context.Context, file model.FileStreamer, createRes
// 重置分片reader位置因为HashReader、上一次失败已经读取到分片EOF // 重置分片reader位置因为HashReader、上一次失败已经读取到分片EOF
reader.Seek(0, io.SeekStart) reader.Seek(0, io.SeekStart)
b.Reset() // 创建表单数据
w := multipart.NewWriter(b) var b bytes.Buffer
w := multipart.NewWriter(&b)
// 添加表单字段 // 添加表单字段
err = w.WriteField("preuploadID", createResp.Data.PreuploadID) err = w.WriteField("preuploadID", createResp.Data.PreuploadID)
if err != nil { if err != nil {
@ -109,20 +109,21 @@ func (d *Open123) Upload(ctx context.Context, file model.FileStreamer, createRes
return err return err
} }
// 写入文件内容 // 写入文件内容
_, err = w.CreateFormFile("slice", fmt.Sprintf("%s.part%d", file.GetName(), partNumber)) fw, err := w.CreateFormFile("slice", fmt.Sprintf("%s.part%d", file.GetName(), partNumber))
if err != nil {
return err
}
_, err = utils.CopyWithBuffer(fw, rateLimitedRd)
if err != nil { if err != nil {
return err return err
} }
headSize := b.Len()
err = w.Close() err = w.Close()
if err != nil { if err != nil {
return err return err
} }
head := bytes.NewReader(b.Bytes()[:headSize])
tail := bytes.NewReader(b.Bytes()[headSize:])
rateLimitedRd = driver.NewLimitedUploadStream(ctx, io.MultiReader(head, reader, tail))
// 创建请求并设置header // 创建请求并设置header
req, err := http.NewRequestWithContext(ctx, http.MethodPost, uploadDomain+"/upload/v2/file/slice", rateLimitedRd) req, err := http.NewRequestWithContext(ctx, http.MethodPost, uploadDomain+"/upload/v2/file/slice", &b)
if err != nil { if err != nil {
return err return err
} }

View File

@ -1,20 +1,15 @@
package _123_open package _123_open
import ( import (
"crypto/md5"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt"
"net/http" "net/http"
"net/url"
"strconv" "strconv"
"strings"
"time" "time"
"github.com/OpenListTeam/OpenList/v4/drivers/base" "github.com/OpenListTeam/OpenList/v4/drivers/base"
"github.com/OpenListTeam/OpenList/v4/internal/op" "github.com/OpenListTeam/OpenList/v4/internal/op"
"github.com/go-resty/resty/v2" "github.com/go-resty/resty/v2"
"github.com/google/uuid"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
@ -25,8 +20,7 @@ var ( //不同情况下获取的AccessTokenQPS限制不同 如下模块化易于
RefreshToken = InitApiInfo(Api+"/api/v1/oauth2/access_token", 1) RefreshToken = InitApiInfo(Api+"/api/v1/oauth2/access_token", 1)
UserInfo = InitApiInfo(Api+"/api/v1/user/info", 1) UserInfo = InitApiInfo(Api+"/api/v1/user/info", 1)
FileList = InitApiInfo(Api+"/api/v2/file/list", 3) FileList = InitApiInfo(Api+"/api/v2/file/list", 3)
DownloadInfo = InitApiInfo(Api+"/api/v1/file/download_info", 5) DownloadInfo = InitApiInfo(Api+"/api/v1/file/download_info", 0)
DirectLink = InitApiInfo(Api+"/api/v1/direct-link/url", 5)
Mkdir = InitApiInfo(Api+"/upload/v1/file/mkdir", 2) Mkdir = InitApiInfo(Api+"/upload/v1/file/mkdir", 2)
Move = InitApiInfo(Api+"/api/v1/file/move", 1) Move = InitApiInfo(Api+"/api/v1/file/move", 1)
Rename = InitApiInfo(Api+"/api/v1/file/name", 1) Rename = InitApiInfo(Api+"/api/v1/file/name", 1)
@ -118,33 +112,6 @@ func (d *Open123) flushAccessToken() error {
return nil return nil
} }
func (d *Open123) SignURL(originURL, privateKey string, uid uint64, validDuration time.Duration) (newURL string, err error) {
// 生成Unix时间戳
ts := time.Now().Add(validDuration).Unix()
// 生成随机数建议使用UUID不能包含中划线-
rand := strings.ReplaceAll(uuid.New().String(), "-", "")
// 解析URL
objURL, err := url.Parse(originURL)
if err != nil {
return "", err
}
// 待签名字符串格式path-timestamp-rand-uid-privateKey
unsignedStr := fmt.Sprintf("%s-%d-%s-%d-%s", objURL.Path, ts, rand, uid, privateKey)
md5Hash := md5.Sum([]byte(unsignedStr))
// 生成鉴权参数格式timestamp-rand-uid-md5hash
authKey := fmt.Sprintf("%d-%s-%d-%x", ts, rand, uid, md5Hash)
// 添加鉴权参数到URL查询参数
v := objURL.Query()
v.Add("auth_key", authKey)
objURL.RawQuery = v.Encode()
return objURL.String(), nil
}
func (d *Open123) getUserInfo() (*UserInfoResp, error) { func (d *Open123) getUserInfo() (*UserInfoResp, error) {
var resp UserInfoResp var resp UserInfoResp
@ -192,21 +159,6 @@ func (d *Open123) getDownloadInfo(fileId int64) (*DownloadInfoResp, error) {
return &resp, nil return &resp, nil
} }
func (d *Open123) getDirectLink(fileId int64) (*DirectLinkResp, error) {
var resp DirectLinkResp
_, err := d.Request(DirectLink, http.MethodGet, func(req *resty.Request) {
req.SetQueryParams(map[string]string{
"fileId": strconv.FormatInt(fileId, 10),
})
}, &resp)
if err != nil {
return nil, err
}
return &resp, nil
}
func (d *Open123) mkdir(parentID int64, name string) error { func (d *Open123) mkdir(parentID int64, name string) error {
_, err := d.Request(Mkdir, http.MethodPost, func(req *resty.Request) { _, err := d.Request(Mkdir, http.MethodPost, func(req *resty.Request) {
req.SetBody(base.Json{ req.SetBody(base.Json{

View File

@ -48,7 +48,6 @@ import (
_ "github.com/OpenListTeam/OpenList/v4/drivers/onedrive_app" _ "github.com/OpenListTeam/OpenList/v4/drivers/onedrive_app"
_ "github.com/OpenListTeam/OpenList/v4/drivers/onedrive_sharelink" _ "github.com/OpenListTeam/OpenList/v4/drivers/onedrive_sharelink"
_ "github.com/OpenListTeam/OpenList/v4/drivers/openlist" _ "github.com/OpenListTeam/OpenList/v4/drivers/openlist"
_ "github.com/OpenListTeam/OpenList/v4/drivers/openlist_share"
_ "github.com/OpenListTeam/OpenList/v4/drivers/pikpak" _ "github.com/OpenListTeam/OpenList/v4/drivers/pikpak"
_ "github.com/OpenListTeam/OpenList/v4/drivers/pikpak_share" _ "github.com/OpenListTeam/OpenList/v4/drivers/pikpak_share"
_ "github.com/OpenListTeam/OpenList/v4/drivers/quark_open" _ "github.com/OpenListTeam/OpenList/v4/drivers/quark_open"

View File

@ -13,7 +13,7 @@ type Addition struct {
ClientSecret string `json:"client_secret" required:"false" help:"Keep it empty if you don't have one"` ClientSecret string `json:"client_secret" required:"false" help:"Keep it empty if you don't have one"`
AccessToken string AccessToken string
RefreshToken string `json:"refresh_token" required:"true"` RefreshToken string `json:"refresh_token" required:"true"`
RootNamespaceId string `json:"RootNamespaceId" required:"false"` RootNamespaceId string
} }
var config = driver.Config{ var config = driver.Config{

View File

@ -175,13 +175,6 @@ func (d *Dropbox) finishUploadSession(ctx context.Context, toPath string, offset
} }
req.Header.Set("Content-Type", "application/octet-stream") req.Header.Set("Content-Type", "application/octet-stream")
req.Header.Set("Authorization", "Bearer "+d.AccessToken) req.Header.Set("Authorization", "Bearer "+d.AccessToken)
if d.RootNamespaceId != "" {
apiPathRootJson, err := d.buildPathRootHeader()
if err != nil {
return err
}
req.Header.Set("Dropbox-API-Path-Root", apiPathRootJson)
}
uploadFinishArgs := UploadFinishArgs{ uploadFinishArgs := UploadFinishArgs{
Commit: struct { Commit: struct {
@ -226,13 +219,6 @@ func (d *Dropbox) startUploadSession(ctx context.Context) (string, error) {
} }
req.Header.Set("Content-Type", "application/octet-stream") req.Header.Set("Content-Type", "application/octet-stream")
req.Header.Set("Authorization", "Bearer "+d.AccessToken) req.Header.Set("Authorization", "Bearer "+d.AccessToken)
if d.RootNamespaceId != "" {
apiPathRootJson, err := d.buildPathRootHeader()
if err != nil {
return "", err
}
req.Header.Set("Dropbox-API-Path-Root", apiPathRootJson)
}
req.Header.Set("Dropbox-API-Arg", "{\"close\":false}") req.Header.Set("Dropbox-API-Arg", "{\"close\":false}")
res, err := base.HttpClient.Do(req) res, err := base.HttpClient.Do(req)
@ -247,11 +233,3 @@ func (d *Dropbox) startUploadSession(ctx context.Context) (string, error) {
_ = res.Body.Close() _ = res.Body.Close()
return sessionId, nil return sessionId, nil
} }
func (d *Dropbox) buildPathRootHeader() (string, error) {
return utils.Json.MarshalToString(map[string]interface{}{
".tag": "root",
"root": d.RootNamespaceId,
})
}

View File

@ -1,181 +0,0 @@
package openlist_share
import (
"context"
"fmt"
"net/http"
"net/url"
stdpath "path"
"strings"
"github.com/OpenListTeam/OpenList/v4/internal/driver"
"github.com/OpenListTeam/OpenList/v4/internal/errs"
"github.com/OpenListTeam/OpenList/v4/internal/model"
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
"github.com/OpenListTeam/OpenList/v4/server/common"
"github.com/go-resty/resty/v2"
)
type OpenListShare struct {
model.Storage
Addition
serverArchivePreview bool
}
func (d *OpenListShare) Config() driver.Config {
return config
}
func (d *OpenListShare) GetAddition() driver.Additional {
return &d.Addition
}
func (d *OpenListShare) Init(ctx context.Context) error {
d.Addition.Address = strings.TrimSuffix(d.Addition.Address, "/")
var settings common.Resp[map[string]string]
_, _, err := d.request("/public/settings", http.MethodGet, func(req *resty.Request) {
req.SetResult(&settings)
})
if err != nil {
return err
}
d.serverArchivePreview = settings.Data["share_archive_preview"] == "true"
return nil
}
func (d *OpenListShare) Drop(ctx context.Context) error {
return nil
}
func (d *OpenListShare) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
var resp common.Resp[FsListResp]
_, _, err := d.request("/fs/list", http.MethodPost, func(req *resty.Request) {
req.SetResult(&resp).SetBody(ListReq{
PageReq: model.PageReq{
Page: 1,
PerPage: 0,
},
Path: stdpath.Join(fmt.Sprintf("/@s/%s", d.ShareId), dir.GetPath()),
Password: d.Pwd,
Refresh: false,
})
})
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,
Ctime: f.Created,
Size: f.Size,
IsFolder: f.IsDir,
HashInfo: utils.FromString(f.HashInfo),
},
Thumbnail: model.Thumbnail{Thumbnail: f.Thumb},
}
files = append(files, &file)
}
return files, nil
}
func (d *OpenListShare) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
path := utils.FixAndCleanPath(stdpath.Join(d.ShareId, file.GetPath()))
u := fmt.Sprintf("%s/sd%s?pwd=%s", d.Address, path, d.Pwd)
return &model.Link{URL: u}, nil
}
func (d *OpenListShare) GetArchiveMeta(ctx context.Context, obj model.Obj, args model.ArchiveArgs) (model.ArchiveMeta, error) {
if !d.serverArchivePreview || !d.ForwardArchiveReq {
return nil, errs.NotImplement
}
var resp common.Resp[ArchiveMetaResp]
_, code, err := d.request("/fs/archive/meta", http.MethodPost, func(req *resty.Request) {
req.SetResult(&resp).SetBody(ArchiveMetaReq{
ArchivePass: args.Password,
Path: stdpath.Join(fmt.Sprintf("/@s/%s", d.ShareId), obj.GetPath()),
Password: d.Pwd,
Refresh: false,
})
})
if code == 202 {
return nil, errs.WrongArchivePassword
}
if err != nil {
return nil, err
}
var tree []model.ObjTree
if resp.Data.Content != nil {
tree = make([]model.ObjTree, 0, len(resp.Data.Content))
for _, content := range resp.Data.Content {
tree = append(tree, &content)
}
}
return &model.ArchiveMetaInfo{
Comment: resp.Data.Comment,
Encrypted: resp.Data.Encrypted,
Tree: tree,
}, nil
}
func (d *OpenListShare) ListArchive(ctx context.Context, obj model.Obj, args model.ArchiveInnerArgs) ([]model.Obj, error) {
if !d.serverArchivePreview || !d.ForwardArchiveReq {
return nil, errs.NotImplement
}
var resp common.Resp[ArchiveListResp]
_, code, err := d.request("/fs/archive/list", http.MethodPost, func(req *resty.Request) {
req.SetResult(&resp).SetBody(ArchiveListReq{
ArchiveMetaReq: ArchiveMetaReq{
ArchivePass: args.Password,
Path: stdpath.Join(fmt.Sprintf("/@s/%s", d.ShareId), obj.GetPath()),
Password: d.Pwd,
Refresh: false,
},
PageReq: model.PageReq{
Page: 1,
PerPage: 0,
},
InnerPath: args.InnerPath,
})
})
if code == 202 {
return nil, errs.WrongArchivePassword
}
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,
Ctime: f.Created,
Size: f.Size,
IsFolder: f.IsDir,
HashInfo: utils.FromString(f.HashInfo),
},
Thumbnail: model.Thumbnail{Thumbnail: f.Thumb},
}
files = append(files, &file)
}
return files, nil
}
func (d *OpenListShare) Extract(ctx context.Context, obj model.Obj, args model.ArchiveInnerArgs) (*model.Link, error) {
if !d.serverArchivePreview || !d.ForwardArchiveReq {
return nil, errs.NotSupport
}
path := utils.FixAndCleanPath(stdpath.Join(d.ShareId, obj.GetPath()))
u := fmt.Sprintf("%s/sad%s?pwd=%s&inner=%s&pass=%s",
d.Address,
path,
d.Pwd,
utils.EncodePath(args.InnerPath, true),
url.QueryEscape(args.Password))
return &model.Link{URL: u}, nil
}
var _ driver.Driver = (*OpenListShare)(nil)

View File

@ -1,27 +0,0 @@
package openlist_share
import (
"github.com/OpenListTeam/OpenList/v4/internal/driver"
"github.com/OpenListTeam/OpenList/v4/internal/op"
)
type Addition struct {
driver.RootPath
Address string `json:"url" required:"true"`
ShareId string `json:"sid" required:"true"`
Pwd string `json:"pwd"`
ForwardArchiveReq bool `json:"forward_archive_requests" default:"true"`
}
var config = driver.Config{
Name: "OpenListShare",
LocalSort: true,
NoUpload: true,
DefaultRoot: "/",
}
func init() {
op.RegisterDriver(func() driver.Driver {
return &OpenListShare{}
})
}

View File

@ -1,111 +0,0 @@
package openlist_share
import (
"time"
"github.com/OpenListTeam/OpenList/v4/internal/model"
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
)
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"`
Created time.Time `json:"created"`
Sign string `json:"sign"`
Thumb string `json:"thumb"`
Type int `json:"type"`
HashInfo string `json:"hashinfo"`
}
type FsListResp struct {
Content []ObjResp `json:"content"`
Total int64 `json:"total"`
Readme string `json:"readme"`
Write bool `json:"write"`
Provider string `json:"provider"`
}
type ArchiveMetaReq struct {
ArchivePass string `json:"archive_pass"`
Password string `json:"password"`
Path string `json:"path"`
Refresh bool `json:"refresh"`
}
type TreeResp struct {
ObjResp
Children []TreeResp `json:"children"`
hashCache *utils.HashInfo
}
func (t *TreeResp) GetSize() int64 {
return t.Size
}
func (t *TreeResp) GetName() string {
return t.Name
}
func (t *TreeResp) ModTime() time.Time {
return t.Modified
}
func (t *TreeResp) CreateTime() time.Time {
return t.Created
}
func (t *TreeResp) IsDir() bool {
return t.ObjResp.IsDir
}
func (t *TreeResp) GetHash() utils.HashInfo {
return utils.FromString(t.HashInfo)
}
func (t *TreeResp) GetID() string {
return ""
}
func (t *TreeResp) GetPath() string {
return ""
}
func (t *TreeResp) GetChildren() []model.ObjTree {
ret := make([]model.ObjTree, 0, len(t.Children))
for _, child := range t.Children {
ret = append(ret, &child)
}
return ret
}
func (t *TreeResp) Thumb() string {
return t.ObjResp.Thumb
}
type ArchiveMetaResp struct {
Comment string `json:"comment"`
Encrypted bool `json:"encrypted"`
Content []TreeResp `json:"content"`
RawURL string `json:"raw_url"`
Sign string `json:"sign"`
}
type ArchiveListReq struct {
model.PageReq
ArchiveMetaReq
InnerPath string `json:"inner_path"`
}
type ArchiveListResp struct {
Content []ObjResp `json:"content"`
Total int64 `json:"total"`
}

View File

@ -1,32 +0,0 @@
package openlist_share
import (
"fmt"
"github.com/OpenListTeam/OpenList/v4/drivers/base"
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
)
func (d *OpenListShare) request(api, method string, callback base.ReqCallback) ([]byte, int, error) {
url := d.Address + "/api" + api
req := base.RestyClient.R()
if callback != nil {
callback(req)
}
res, err := req.Execute(method, url)
if err != nil {
code := 0
if res != nil {
code = res.StatusCode()
}
return nil, code, err
}
if res.StatusCode() >= 400 {
return nil, res.StatusCode(), fmt.Errorf("request failed, status: %s", res.Status())
}
code := utils.Json.Get(res.Body(), "code").ToInt()
if code != 200 {
return nil, code, fmt.Errorf("request failed, code: %d, message: %s", code, utils.Json.Get(res.Body(), "message").ToString())
}
return res.Body(), 200, nil
}

View File

@ -132,7 +132,7 @@ func (d *Terabox) Remove(ctx context.Context, obj model.Obj) error {
func (d *Terabox) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error { func (d *Terabox) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
resp, err := base.RestyClient.R(). resp, err := base.RestyClient.R().
SetContext(ctx). SetContext(ctx).
Get("https://" + d.url_domain_prefix + "-data.terabox.com/rest/2.0/pcs/file?method=locateupload") Get("https://d.terabox.com/rest/2.0/pcs/file?method=locateupload")
if err != nil { if err != nil {
return err return err
} }

View File

@ -5,6 +5,11 @@ umask ${UMASK}
if [ "$1" = "version" ]; then if [ "$1" = "version" ]; then
./openlist version ./openlist version
else else
# Define the target directory path for openlist service
OPENLIST_DIR="/opt/service/start/openlist"
if [ ! -d "$OPENLIST_DIR" ]; then
cp -r /opt/service/stop/openlist "$OPENLIST_DIR" 2>/dev/null
fi
# Define the target directory path for aria2 service # Define the target directory path for aria2 service
ARIA2_DIR="/opt/service/start/aria2" ARIA2_DIR="/opt/service/start/aria2"
@ -14,12 +19,12 @@ else
mkdir -p "$ARIA2_DIR" mkdir -p "$ARIA2_DIR"
cp -r /opt/service/stop/aria2/* "$ARIA2_DIR" 2>/dev/null cp -r /opt/service/stop/aria2/* "$ARIA2_DIR" 2>/dev/null
fi fi
runsvdir /opt/service/start &
else else
# If aria2 should NOT run and target directory exists, remove it # If aria2 should NOT run and target directory exists, remove it
if [ -d "$ARIA2_DIR" ]; then if [ -d "$ARIA2_DIR" ]; then
rm -rf "$ARIA2_DIR" rm -rf "$ARIA2_DIR"
fi fi
fi fi
exec ./openlist server --no-prefix
exec runsvdir /opt/service/start
fi fi

3
go.mod
View File

@ -70,7 +70,8 @@ require (
golang.org/x/net v0.42.0 golang.org/x/net v0.42.0
golang.org/x/oauth2 v0.30.0 golang.org/x/oauth2 v0.30.0
golang.org/x/time v0.12.0 golang.org/x/time v0.12.0
google.golang.org/appengine v1.6.8 google.golang.org/appengine v1.6.7
google.golang.org/appengine/v2 v2.0.6
gopkg.in/ldap.v3 v3.1.0 gopkg.in/ldap.v3 v3.1.0
gorm.io/driver/mysql v1.5.7 gorm.io/driver/mysql v1.5.7
gorm.io/driver/postgres v1.5.9 gorm.io/driver/postgres v1.5.9

4
go.sum
View File

@ -892,8 +892,11 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
google.golang.org/appengine/v2 v2.0.6/go.mod h1:WoEXGoXNfa0mLvaH5sV3ZSGXwVmy8yf7Z1JKf3J3wLI=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
@ -920,6 +923,7 @@ google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc= google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d h1:TxyelI5cVkbREznMhfzycHdkp5cLA7DpE+GKjSslYhM= gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d h1:TxyelI5cVkbREznMhfzycHdkp5cLA7DpE+GKjSslYhM=

View File

@ -77,10 +77,6 @@ func InitConfig() {
log.Fatalf("update config struct error: %+v", err) log.Fatalf("update config struct error: %+v", err)
} }
} }
if !conf.Conf.Force {
confFromEnv()
}
if conf.Conf.MaxConcurrency > 0 { if conf.Conf.MaxConcurrency > 0 {
net.DefaultConcurrencyLimit = &net.ConcurrencyLimit{Limit: conf.Conf.MaxConcurrency} net.DefaultConcurrencyLimit = &net.ConcurrencyLimit{Limit: conf.Conf.MaxConcurrency}
} }
@ -96,31 +92,25 @@ func InitConfig() {
conf.MaxBufferLimit = conf.Conf.MaxBufferLimit * utils.MB conf.MaxBufferLimit = conf.Conf.MaxBufferLimit * utils.MB
} }
log.Infof("max buffer limit: %dMB", conf.MaxBufferLimit/utils.MB) log.Infof("max buffer limit: %dMB", conf.MaxBufferLimit/utils.MB)
if conf.Conf.MmapThreshold > 0 { if !conf.Conf.Force {
conf.MmapThreshold = conf.Conf.MmapThreshold * utils.MB confFromEnv()
} else {
conf.MmapThreshold = 0
} }
log.Infof("mmap threshold: %dMB", conf.Conf.MmapThreshold)
if len(conf.Conf.Log.Filter.Filters) == 0 { if len(conf.Conf.Log.Filter.Filters) == 0 {
conf.Conf.Log.Filter.Enable = false conf.Conf.Log.Filter.Enable = false
} }
// convert abs path // convert abs path
convertAbsPath := func(path *string) { convertAbsPath := func(path *string) {
if *path != "" && !filepath.IsAbs(*path) { if !filepath.IsAbs(*path) {
*path = filepath.Join(pwd, *path) *path = filepath.Join(pwd, *path)
} }
} }
convertAbsPath(&conf.Conf.Database.DBFile)
convertAbsPath(&conf.Conf.Scheme.CertFile)
convertAbsPath(&conf.Conf.Scheme.KeyFile)
convertAbsPath(&conf.Conf.Scheme.UnixFile)
convertAbsPath(&conf.Conf.Log.Name)
convertAbsPath(&conf.Conf.TempDir) convertAbsPath(&conf.Conf.TempDir)
convertAbsPath(&conf.Conf.BleveDir) convertAbsPath(&conf.Conf.BleveDir)
convertAbsPath(&conf.Conf.Log.Name)
convertAbsPath(&conf.Conf.Database.DBFile)
if conf.Conf.DistDir != "" {
convertAbsPath(&conf.Conf.DistDir) convertAbsPath(&conf.Conf.DistDir)
}
err := os.MkdirAll(conf.Conf.TempDir, 0o777) err := os.MkdirAll(conf.Conf.TempDir, 0o777)
if err != nil { if err != nil {
log.Fatalf("create temp dir error: %+v", err) log.Fatalf("create temp dir error: %+v", err)

View File

@ -111,7 +111,6 @@ func InitialSettings() []model.SettingItem {
{Key: conf.Favicon, Value: "https://res.oplist.org/logo/logo.svg", MigrationValue: "https://cdn.oplist.org/gh/OpenListTeam/Logo@main/logo.svg", Type: conf.TypeString, Group: model.STYLE}, {Key: conf.Favicon, Value: "https://res.oplist.org/logo/logo.svg", MigrationValue: "https://cdn.oplist.org/gh/OpenListTeam/Logo@main/logo.svg", Type: conf.TypeString, Group: model.STYLE},
{Key: conf.MainColor, Value: "#1890ff", Type: conf.TypeString, Group: model.STYLE}, {Key: conf.MainColor, Value: "#1890ff", Type: conf.TypeString, Group: model.STYLE},
{Key: "home_icon", Value: "🏠", Type: conf.TypeString, Group: model.STYLE}, {Key: "home_icon", Value: "🏠", Type: conf.TypeString, Group: model.STYLE},
{Key: "share_icon", Value: "🎁", Type: conf.TypeString, Group: model.STYLE},
{Key: "home_container", Value: "max_980px", Type: conf.TypeSelect, Options: "max_980px,hope_container", Group: model.STYLE}, {Key: "home_container", Value: "max_980px", Type: conf.TypeSelect, Options: "max_980px,hope_container", Group: model.STYLE},
{Key: "settings_layout", Value: "list", Type: conf.TypeSelect, Options: "list,responsive", Group: model.STYLE}, {Key: "settings_layout", Value: "list", Type: conf.TypeSelect, Options: "list,responsive", Group: model.STYLE},
// preview settings // preview settings
@ -164,10 +163,6 @@ func InitialSettings() []model.SettingItem {
{Key: conf.ForwardDirectLinkParams, Value: "false", Type: conf.TypeBool, Group: model.GLOBAL}, {Key: conf.ForwardDirectLinkParams, Value: "false", Type: conf.TypeBool, Group: model.GLOBAL},
{Key: conf.IgnoreDirectLinkParams, Value: "sign,openlist_ts", Type: conf.TypeString, Group: model.GLOBAL}, {Key: conf.IgnoreDirectLinkParams, Value: "sign,openlist_ts", Type: conf.TypeString, Group: model.GLOBAL},
{Key: conf.WebauthnLoginEnabled, Value: "false", Type: conf.TypeBool, Group: model.GLOBAL, Flag: model.PUBLIC}, {Key: conf.WebauthnLoginEnabled, Value: "false", Type: conf.TypeBool, Group: model.GLOBAL, Flag: model.PUBLIC},
{Key: conf.SharePreview, Value: "false", Type: conf.TypeBool, Group: model.GLOBAL, Flag: model.PUBLIC},
{Key: conf.ShareArchivePreview, Value: "false", Type: conf.TypeBool, Group: model.GLOBAL, Flag: model.PUBLIC},
{Key: conf.ShareForceProxy, Value: "true", Type: conf.TypeBool, Group: model.GLOBAL, Flag: model.PRIVATE},
{Key: conf.ShareSummaryContent, Value: "@{{creator}} shared {{#each files}}{{#if @first}}\"{{filename this}}\"{{/if}}{{#if @last}}{{#unless (eq @index 0)}} and {{@index}} more files{{/unless}}{{/if}}{{/each}} from {{site_title}}: {{base_url}}/@s/{{id}}{{#if pwd}} , the share code is {{pwd}}{{/if}}{{#if expires}}, please access before {{dateLocaleString expires}}.{{/if}}", Type: conf.TypeText, Group: model.GLOBAL, Flag: model.PUBLIC},
// single settings // single settings
{Key: conf.Token, Value: token, Type: conf.TypeString, Group: model.SINGLE, Flag: model.PRIVATE}, {Key: conf.Token, Value: token, Type: conf.TypeString, Group: model.SINGLE, Flag: model.PRIVATE},

View File

@ -33,8 +33,8 @@ func initUser() {
Role: model.ADMIN, Role: model.ADMIN,
BasePath: "/", BasePath: "/",
Authn: "[]", Authn: "[]",
// 0(can see hidden) - 8(webdav read) & 12(can read archives) - 14(can share) // 0(can see hidden) - 7(can remove) & 12(can read archives) - 13(can decompress archives)
Permission: 0x71FF, Permission: 0x31FF,
} }
if err := op.CreateUser(admin); err != nil { if err := op.CreateUser(admin); err != nil {
panic(err) panic(err)

View File

@ -120,7 +120,6 @@ type Config struct {
Log LogConfig `json:"log" envPrefix:"LOG_"` Log LogConfig `json:"log" envPrefix:"LOG_"`
DelayedStart int `json:"delayed_start" env:"DELAYED_START"` DelayedStart int `json:"delayed_start" env:"DELAYED_START"`
MaxBufferLimit int `json:"max_buffer_limitMB" env:"MAX_BUFFER_LIMIT_MB"` MaxBufferLimit int `json:"max_buffer_limitMB" env:"MAX_BUFFER_LIMIT_MB"`
MmapThreshold int `json:"mmap_thresholdMB" env:"MMAP_THRESHOLD_MB"`
MaxConnections int `json:"max_connections" env:"MAX_CONNECTIONS"` MaxConnections int `json:"max_connections" env:"MAX_CONNECTIONS"`
MaxConcurrency int `json:"max_concurrency" env:"MAX_CONCURRENCY"` MaxConcurrency int `json:"max_concurrency" env:"MAX_CONCURRENCY"`
TlsInsecureSkipVerify bool `json:"tls_insecure_skip_verify" env:"TLS_INSECURE_SKIP_VERIFY"` TlsInsecureSkipVerify bool `json:"tls_insecure_skip_verify" env:"TLS_INSECURE_SKIP_VERIFY"`
@ -177,7 +176,6 @@ func DefaultConfig(dataDir string) *Config {
}, },
}, },
MaxBufferLimit: -1, MaxBufferLimit: -1,
MmapThreshold: 4,
MaxConnections: 0, MaxConnections: 0,
MaxConcurrency: 64, MaxConcurrency: 64,
TlsInsecureSkipVerify: true, TlsInsecureSkipVerify: true,

View File

@ -33,7 +33,6 @@ const (
PreviewArchivesByDefault = "preview_archives_by_default" PreviewArchivesByDefault = "preview_archives_by_default"
ReadMeAutoRender = "readme_autorender" ReadMeAutoRender = "readme_autorender"
FilterReadMeScripts = "filter_readme_scripts" FilterReadMeScripts = "filter_readme_scripts"
// global // global
HideFiles = "hide_files" HideFiles = "hide_files"
CustomizeHead = "customize_head" CustomizeHead = "customize_head"
@ -46,10 +45,6 @@ const (
ForwardDirectLinkParams = "forward_direct_link_params" ForwardDirectLinkParams = "forward_direct_link_params"
IgnoreDirectLinkParams = "ignore_direct_link_params" IgnoreDirectLinkParams = "ignore_direct_link_params"
WebauthnLoginEnabled = "webauthn_login_enabled" WebauthnLoginEnabled = "webauthn_login_enabled"
SharePreview = "share_preview"
ShareArchivePreview = "share_archive_preview"
ShareForceProxy = "share_force_proxy"
ShareSummaryContent = "share_summary_content"
// index // index
SearchIndex = "search_index" SearchIndex = "search_index"
@ -172,5 +167,4 @@ const (
RequestHeaderKey RequestHeaderKey
UserAgentKey UserAgentKey
PathKey PathKey
SharingIDKey
) )

View File

@ -25,10 +25,7 @@ var PrivacyReg []*regexp.Regexp
var ( var (
// StoragesLoaded loaded success if empty // StoragesLoaded loaded success if empty
StoragesLoaded = false StoragesLoaded = false
// 单个Buffer最大限制
MaxBufferLimit = 16 * 1024 * 1024 MaxBufferLimit = 16 * 1024 * 1024
// 超过该阈值的Buffer将使用 mmap 分配,可主动释放内存
MmapThreshold = 4 * 1024 * 1024
) )
var ( var (
RawIndexHtml string RawIndexHtml string

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)) err := AutoMigrate(new(model.Storage), new(model.User), new(model.Meta), new(model.SettingItem), new(model.SearchNode), new(model.TaskItem), new(model.SSHPublicKey))
if err != nil { if err != nil {
log.Fatalf("failed migrate database: %s", err.Error()) log.Fatalf("failed migrate database: %s", err.Error())
} }

View File

@ -1,62 +0,0 @@
package db
import (
"github.com/OpenListTeam/OpenList/v4/internal/model"
"github.com/OpenListTeam/OpenList/v4/pkg/utils/random"
"github.com/pkg/errors"
)
func GetSharingById(id string) (*model.SharingDB, error) {
s := model.SharingDB{ID: id}
if err := db.Where(s).First(&s).Error; err != nil {
return nil, errors.Wrapf(err, "failed get sharing")
}
return &s, nil
}
func GetSharings(pageIndex, pageSize int) (sharings []model.SharingDB, count int64, err error) {
sharingDB := db.Model(&model.SharingDB{})
if err := sharingDB.Count(&count).Error; err != nil {
return nil, 0, errors.Wrapf(err, "failed get sharings count")
}
if err := sharingDB.Order(columnName("id")).Offset((pageIndex - 1) * pageSize).Limit(pageSize).Find(&sharings).Error; err != nil {
return nil, 0, errors.Wrapf(err, "failed get find sharings")
}
return sharings, count, nil
}
func GetSharingsByCreatorId(creator uint, pageIndex, pageSize int) (sharings []model.SharingDB, count int64, err error) {
sharingDB := db.Model(&model.SharingDB{})
cond := model.SharingDB{CreatorId: creator}
if err := sharingDB.Where(cond).Count(&count).Error; err != nil {
return nil, 0, errors.Wrapf(err, "failed get sharings count")
}
if err := sharingDB.Where(cond).Order(columnName("id")).Offset((pageIndex - 1) * pageSize).Limit(pageSize).Find(&sharings).Error; err != nil {
return nil, 0, errors.Wrapf(err, "failed get find sharings")
}
return sharings, count, nil
}
func CreateSharing(s *model.SharingDB) (string, error) {
id := random.String(8)
for len(id) < 12 {
old := model.SharingDB{
ID: id,
}
if err := db.Where(old).First(&old).Error; err != nil {
s.ID = id
return id, errors.WithStack(db.Create(s).Error)
}
id += random.String(1)
}
return "", errors.New("failed find valid id")
}
func UpdateSharing(s *model.SharingDB) error {
return errors.WithStack(db.Save(s).Error)
}
func DeleteSharingById(id string) error {
s := model.SharingDB{ID: id}
return errors.WithStack(db.Where(s).Delete(&s).Error)
}

View File

@ -23,10 +23,6 @@ var (
UnknownArchiveFormat = errors.New("unknown archive format") UnknownArchiveFormat = errors.New("unknown archive format")
WrongArchivePassword = errors.New("wrong archive password") WrongArchivePassword = errors.New("wrong archive password")
DriverExtractNotSupported = errors.New("driver extraction not supported") DriverExtractNotSupported = errors.New("driver extraction not supported")
WrongShareCode = errors.New("wrong share code")
InvalidSharing = errors.New("invalid sharing")
SharingNotFound = errors.New("sharing not found")
) )
// NewErr wrap constant error with an extra message // NewErr wrap constant error with an extra message

View File

@ -168,7 +168,7 @@ func GetStorage(path string, args *GetStoragesArgs) (driver.Driver, error) {
func Other(ctx context.Context, args model.FsOtherArgs) (interface{}, error) { func Other(ctx context.Context, args model.FsOtherArgs) (interface{}, error) {
res, err := other(ctx, args) res, err := other(ctx, args)
if err != nil { if err != nil {
log.Errorf("failed get other %s: %+v", args.Path, err) log.Errorf("failed remove %s: %+v", args.Path, err)
} }
return res, err return res, err
} }

View File

@ -77,26 +77,6 @@ type ArchiveDecompressArgs struct {
PutIntoNewDir bool PutIntoNewDir bool
} }
type SharingListArgs struct {
Refresh bool
Pwd string
}
type SharingArchiveMetaArgs struct {
ArchiveMetaArgs
Pwd string
}
type SharingArchiveListArgs struct {
ArchiveListArgs
Pwd string
}
type SharingLinkArgs struct {
Pwd string
LinkArgs
}
type RangeReaderIF interface { type RangeReaderIF interface {
RangeRead(ctx context.Context, httpRange http_range.Range) (io.ReadCloser, error) RangeRead(ctx context.Context, httpRange http_range.Range) (io.ReadCloser, error)
} }

View File

@ -1,47 +0,0 @@
package model
import "time"
type SharingDB struct {
ID string `json:"id" gorm:"type:char(12);primaryKey"`
FilesRaw string `json:"-" gorm:"type:text"`
Expires *time.Time `json:"expires"`
Pwd string `json:"pwd"`
Accessed int `json:"accessed"`
MaxAccessed int `json:"max_accessed"`
CreatorId uint `json:"-"`
Disabled bool `json:"disabled"`
Remark string `json:"remark"`
Readme string `json:"readme" gorm:"type:text"`
Header string `json:"header" gorm:"type:text"`
Sort
}
type Sharing struct {
*SharingDB
Files []string `json:"files"`
Creator *User `json:"-"`
}
func (s *Sharing) Valid() bool {
if s.Disabled {
return false
}
if s.MaxAccessed > 0 && s.Accessed >= s.MaxAccessed {
return false
}
if len(s.Files) == 0 {
return false
}
if !s.Creator.CanShare() {
return false
}
if s.Expires != nil && !s.Expires.IsZero() && s.Expires.Before(time.Now()) {
return false
}
return true
}
func (s *Sharing) Verify(pwd string) bool {
return s.Pwd == "" || s.Pwd == pwd
}

View File

@ -54,7 +54,6 @@ type User struct {
// 11: ftp/sftp write // 11: ftp/sftp write
// 12: can read archives // 12: can read archives
// 13: can decompress archives // 13: can decompress archives
// 14: can share
Permission int32 `json:"permission"` Permission int32 `json:"permission"`
OtpSecret string `json:"-"` OtpSecret string `json:"-"`
SsoID string `json:"sso_id"` // unique by sso platform SsoID string `json:"sso_id"` // unique by sso platform
@ -146,10 +145,6 @@ func (u *User) CanDecompress() bool {
return (u.Permission>>13)&1 == 1 return (u.Permission>>13)&1 == 1
} }
func (u *User) CanShare() bool {
return (u.Permission>>14)&1 == 1
}
func (u *User) JoinPath(reqPath string) (string, error) { func (u *User) JoinPath(reqPath string) (string, error) {
return utils.JoinBasePath(u.BasePath, reqPath) return utils.JoinBasePath(u.BasePath, reqPath)
} }

View File

@ -1,6 +1,7 @@
package net package net
import ( import (
"bytes"
"context" "context"
"errors" "errors"
"fmt" "fmt"
@ -14,7 +15,6 @@ import (
"github.com/OpenListTeam/OpenList/v4/internal/conf" "github.com/OpenListTeam/OpenList/v4/internal/conf"
"github.com/OpenListTeam/OpenList/v4/internal/model" "github.com/OpenListTeam/OpenList/v4/internal/model"
"github.com/OpenListTeam/OpenList/v4/pkg/utils" "github.com/OpenListTeam/OpenList/v4/pkg/utils"
"github.com/rclone/rclone/lib/mmap"
"github.com/OpenListTeam/OpenList/v4/pkg/http_range" "github.com/OpenListTeam/OpenList/v4/pkg/http_range"
"github.com/aws/aws-sdk-go/aws/awsutil" "github.com/aws/aws-sdk-go/aws/awsutil"
@ -255,10 +255,7 @@ func (d *downloader) sendChunkTask(newConcurrency bool) error {
finalSize += firstSize - minSize finalSize += firstSize - minSize
} }
} }
err := buf.Reset(int(finalSize)) buf.Reset(int(finalSize))
if err != nil {
return err
}
ch := chunk{ ch := chunk{
start: d.pos, start: d.pos,
size: finalSize, size: finalSize,
@ -648,13 +645,11 @@ func (mr MultiReadCloser) Close() error {
} }
type Buf struct { type Buf struct {
buffer *bytes.Buffer
size int //expected size size int //expected size
ctx context.Context ctx context.Context
offR int off int
offW int
rw sync.Mutex rw sync.Mutex
buf []byte
mmap bool
readSignal chan struct{} readSignal chan struct{}
readPending bool readPending bool
@ -663,62 +658,54 @@ type Buf struct {
// NewBuf is a buffer that can have 1 read & 1 write at the same time. // NewBuf is a buffer that can have 1 read & 1 write at the same time.
// when read is faster write, immediately feed data to read after written // when read is faster write, immediately feed data to read after written
func NewBuf(ctx context.Context, maxSize int) *Buf { func NewBuf(ctx context.Context, maxSize int) *Buf {
br := &Buf{ return &Buf{
ctx: ctx, ctx: ctx,
buffer: bytes.NewBuffer(make([]byte, 0, maxSize)),
size: maxSize, size: maxSize,
readSignal: make(chan struct{}, 1), readSignal: make(chan struct{}, 1),
} }
if conf.MmapThreshold > 0 && maxSize >= conf.MmapThreshold {
m, err := mmap.Alloc(maxSize)
if err == nil {
br.buf = m
br.mmap = true
return br
} }
} func (br *Buf) Reset(size int) {
br.buf = make([]byte, maxSize)
return br
}
func (br *Buf) Reset(size int) error {
br.rw.Lock() br.rw.Lock()
defer br.rw.Unlock() defer br.rw.Unlock()
if br.buf == nil { if br.buffer == nil {
return io.ErrClosedPipe return
}
if size > cap(br.buf) {
return fmt.Errorf("reset size %d exceeds max size %d", size, cap(br.buf))
} }
br.buffer.Reset()
br.size = size br.size = size
br.offR = 0 br.off = 0
br.offW = 0
return nil
} }
func (br *Buf) Read(p []byte) (int, error) { func (br *Buf) Read(p []byte) (n int, err error) {
if err := br.ctx.Err(); err != nil { if err := br.ctx.Err(); err != nil {
return 0, err return 0, err
} }
if len(p) == 0 { if len(p) == 0 {
return 0, nil return 0, nil
} }
if br.offR >= br.size { if br.off >= br.size {
return 0, io.EOF return 0, io.EOF
} }
for { for {
br.rw.Lock() br.rw.Lock()
if br.buf == nil { if br.buffer != nil {
br.rw.Unlock() n, err = br.buffer.Read(p)
return 0, io.ErrClosedPipe } else {
err = io.ErrClosedPipe
} }
if err != nil && err != io.EOF {
if br.offW < br.offR {
br.rw.Unlock() br.rw.Unlock()
return 0, io.ErrUnexpectedEOF return
}
if n > 0 {
br.off += n
br.rw.Unlock()
return n, nil
} }
if br.offW == br.offR {
br.readPending = true br.readPending = true
br.rw.Unlock() br.rw.Unlock()
// n==0, err==io.EOF
select { select {
case <-br.ctx.Done(): case <-br.ctx.Done():
return 0, br.ctx.Err() return 0, br.ctx.Err()
@ -729,34 +716,18 @@ func (br *Buf) Read(p []byte) (int, error) {
continue continue
} }
} }
n := copy(p, br.buf[br.offR:br.offW])
br.offR += n
br.rw.Unlock()
if n < len(p) && br.offR >= br.size {
return n, io.EOF
}
return n, nil
}
} }
func (br *Buf) Write(p []byte) (int, error) { func (br *Buf) Write(p []byte) (n int, err error) {
if err := br.ctx.Err(); err != nil { if err := br.ctx.Err(); err != nil {
return 0, err return 0, err
} }
if len(p) == 0 {
return 0, nil
}
br.rw.Lock() br.rw.Lock()
defer br.rw.Unlock() defer br.rw.Unlock()
if br.buf == nil { if br.buffer == nil {
return 0, io.ErrClosedPipe return 0, io.ErrClosedPipe
} }
if br.offW >= br.size { n, err = br.buffer.Write(p)
return 0, io.ErrShortWrite
}
n := copy(br.buf[br.offW:], p[:min(br.size-br.offW, len(p))])
br.offW += n
if br.readPending { if br.readPending {
br.readPending = false br.readPending = false
select { select {
@ -764,21 +735,12 @@ func (br *Buf) Write(p []byte) (int, error) {
default: default:
} }
} }
if n < len(p) { return
return n, io.ErrShortWrite
}
return n, nil
} }
func (br *Buf) Close() error { func (br *Buf) Close() {
br.rw.Lock() br.rw.Lock()
defer br.rw.Unlock() defer br.rw.Unlock()
var err error br.buffer = nil
if br.mmap {
err = mmap.Free(br.buf)
br.mmap = false
}
br.buf = nil
close(br.readSignal) close(br.readSignal)
return err
} }

View File

@ -1,139 +0,0 @@
package op
import (
"fmt"
stdpath "path"
"strings"
"github.com/OpenListTeam/OpenList/v4/internal/db"
"github.com/OpenListTeam/OpenList/v4/internal/model"
"github.com/OpenListTeam/OpenList/v4/pkg/singleflight"
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
"github.com/OpenListTeam/go-cache"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
)
func makeJoined(sdb []model.SharingDB) []model.Sharing {
creator := make(map[uint]*model.User)
return utils.MustSliceConvert(sdb, func(s model.SharingDB) model.Sharing {
var c *model.User
var ok bool
if c, ok = creator[s.CreatorId]; !ok {
var err error
if c, err = GetUserById(s.CreatorId); err != nil {
c = nil
} else {
creator[s.CreatorId] = c
}
}
var files []string
if err := utils.Json.UnmarshalFromString(s.FilesRaw, &files); err != nil {
files = make([]string, 0)
}
return model.Sharing{
SharingDB: &s,
Files: files,
Creator: c,
}
})
}
var sharingCache = cache.NewMemCache(cache.WithShards[*model.Sharing](8))
var sharingG singleflight.Group[*model.Sharing]
func GetSharingById(id string, refresh ...bool) (*model.Sharing, error) {
if !utils.IsBool(refresh...) {
if sharing, ok := sharingCache.Get(id); ok {
log.Debugf("use cache when get sharing %s", id)
return sharing, nil
}
}
sharing, err, _ := sharingG.Do(id, func() (*model.Sharing, error) {
s, err := db.GetSharingById(id)
if err != nil {
return nil, errors.WithMessagef(err, "failed get sharing [%s]", id)
}
creator, err := GetUserById(s.CreatorId)
if err != nil {
return nil, errors.WithMessagef(err, "failed get sharing creator [%s]", id)
}
var files []string
if err = utils.Json.UnmarshalFromString(s.FilesRaw, &files); err != nil {
files = make([]string, 0)
}
return &model.Sharing{
SharingDB: s,
Files: files,
Creator: creator,
}, nil
})
return sharing, err
}
func GetSharings(pageIndex, pageSize int) ([]model.Sharing, int64, error) {
s, cnt, err := db.GetSharings(pageIndex, pageSize)
if err != nil {
return nil, 0, errors.WithStack(err)
}
return makeJoined(s), cnt, nil
}
func GetSharingsByCreatorId(userId uint, pageIndex, pageSize int) ([]model.Sharing, int64, error) {
s, cnt, err := db.GetSharingsByCreatorId(userId, pageIndex, pageSize)
if err != nil {
return nil, 0, errors.WithStack(err)
}
return makeJoined(s), cnt, nil
}
func GetSharingUnwrapPath(sharing *model.Sharing, path string) (unwrapPath string, err error) {
if len(sharing.Files) == 0 {
return "", errors.New("cannot get actual path of an invalid sharing")
}
if len(sharing.Files) == 1 {
return stdpath.Join(sharing.Files[0], path), nil
}
path = utils.FixAndCleanPath(path)[1:]
if len(path) == 0 {
return "", errors.New("cannot get actual path of a sharing root path")
}
mapPath := ""
child, rest, _ := strings.Cut(path, "/")
for _, c := range sharing.Files {
if child == stdpath.Base(c) {
mapPath = c
break
}
}
if mapPath == "" {
return "", fmt.Errorf("failed find child [%s] of sharing [%s]", child, sharing.ID)
}
return stdpath.Join(mapPath, rest), nil
}
func CreateSharing(sharing *model.Sharing) (id string, err error) {
sharing.CreatorId = sharing.Creator.ID
sharing.FilesRaw, err = utils.Json.MarshalToString(utils.MustSliceConvert(sharing.Files, utils.FixAndCleanPath))
if err != nil {
return "", errors.WithStack(err)
}
return db.CreateSharing(sharing.SharingDB)
}
func UpdateSharing(sharing *model.Sharing, skipMarshal ...bool) (err error) {
if !utils.IsBool(skipMarshal...) {
sharing.CreatorId = sharing.Creator.ID
sharing.FilesRaw, err = utils.Json.MarshalToString(utils.MustSliceConvert(sharing.Files, utils.FixAndCleanPath))
if err != nil {
return errors.WithStack(err)
}
}
sharingCache.Del(sharing.ID)
return db.UpdateSharing(sharing.SharingDB)
}
func DeleteSharing(sid string) error {
sharingCache.Del(sid)
return db.DeleteSharingById(sid)
}

View File

@ -1,65 +0,0 @@
package sharing
import (
"context"
"github.com/OpenListTeam/OpenList/v4/internal/errs"
"github.com/OpenListTeam/OpenList/v4/internal/model"
"github.com/OpenListTeam/OpenList/v4/internal/op"
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
"github.com/pkg/errors"
)
func archiveMeta(ctx context.Context, sid, path string, args model.SharingArchiveMetaArgs) (*model.Sharing, *model.ArchiveMetaProvider, error) {
sharing, err := op.GetSharingById(sid, args.Refresh)
if err != nil {
return nil, nil, errors.WithStack(errs.SharingNotFound)
}
if !sharing.Valid() {
return sharing, nil, errors.WithStack(errs.InvalidSharing)
}
if !sharing.Verify(args.Pwd) {
return sharing, nil, errors.WithStack(errs.WrongShareCode)
}
path = utils.FixAndCleanPath(path)
if len(sharing.Files) == 1 || path != "/" {
unwrapPath, err := op.GetSharingUnwrapPath(sharing, path)
if err != nil {
return nil, nil, errors.WithMessage(err, "failed get sharing unwrap path")
}
storage, actualPath, err := op.GetStorageAndActualPath(unwrapPath)
if err != nil {
return nil, nil, errors.WithMessage(err, "failed get sharing file")
}
obj, err := op.GetArchiveMeta(ctx, storage, actualPath, args.ArchiveMetaArgs)
return sharing, obj, err
}
return nil, nil, errors.New("cannot get sharing root archive meta")
}
func archiveList(ctx context.Context, sid, path string, args model.SharingArchiveListArgs) (*model.Sharing, []model.Obj, error) {
sharing, err := op.GetSharingById(sid, args.Refresh)
if err != nil {
return nil, nil, errors.WithStack(errs.SharingNotFound)
}
if !sharing.Valid() {
return sharing, nil, errors.WithStack(errs.InvalidSharing)
}
if !sharing.Verify(args.Pwd) {
return sharing, nil, errors.WithStack(errs.WrongShareCode)
}
path = utils.FixAndCleanPath(path)
if len(sharing.Files) == 1 || path != "/" {
unwrapPath, err := op.GetSharingUnwrapPath(sharing, path)
if err != nil {
return nil, nil, errors.WithMessage(err, "failed get sharing unwrap path")
}
storage, actualPath, err := op.GetStorageAndActualPath(unwrapPath)
if err != nil {
return nil, nil, errors.WithMessage(err, "failed get sharing file")
}
obj, err := op.ListArchive(ctx, storage, actualPath, args.ArchiveListArgs)
return sharing, obj, err
}
return nil, nil, errors.New("cannot get sharing root archive list")
}

View File

@ -1,60 +0,0 @@
package sharing
import (
"context"
stdpath "path"
"time"
"github.com/OpenListTeam/OpenList/v4/internal/errs"
"github.com/OpenListTeam/OpenList/v4/internal/model"
"github.com/OpenListTeam/OpenList/v4/internal/op"
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
"github.com/pkg/errors"
)
func get(ctx context.Context, sid, path string, args model.SharingListArgs) (*model.Sharing, model.Obj, error) {
sharing, err := op.GetSharingById(sid, args.Refresh)
if err != nil {
return nil, nil, errors.WithStack(errs.SharingNotFound)
}
if !sharing.Valid() {
return sharing, nil, errors.WithStack(errs.InvalidSharing)
}
if !sharing.Verify(args.Pwd) {
return sharing, nil, errors.WithStack(errs.WrongShareCode)
}
path = utils.FixAndCleanPath(path)
if len(sharing.Files) == 1 || path != "/" {
unwrapPath, err := op.GetSharingUnwrapPath(sharing, path)
if err != nil {
return nil, nil, errors.WithMessage(err, "failed get sharing unwrap path")
}
if unwrapPath != "/" {
virtualFiles := op.GetStorageVirtualFilesByPath(stdpath.Dir(unwrapPath))
for _, f := range virtualFiles {
if f.GetName() == stdpath.Base(unwrapPath) {
return sharing, f, nil
}
}
} else {
return sharing, &model.Object{
Name: sid,
Size: 0,
Modified: time.Time{},
IsFolder: true,
}, nil
}
storage, actualPath, err := op.GetStorageAndActualPath(unwrapPath)
if err != nil {
return nil, nil, errors.WithMessage(err, "failed get sharing file")
}
obj, err := op.Get(ctx, storage, actualPath)
return sharing, obj, err
}
return sharing, &model.Object{
Name: sid,
Size: 0,
Modified: time.Time{},
IsFolder: true,
}, nil
}

View File

@ -1,46 +0,0 @@
package sharing
import (
"context"
"strings"
"github.com/OpenListTeam/OpenList/v4/internal/errs"
"github.com/OpenListTeam/OpenList/v4/internal/model"
"github.com/OpenListTeam/OpenList/v4/internal/op"
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
"github.com/OpenListTeam/OpenList/v4/server/common"
"github.com/pkg/errors"
)
func link(ctx context.Context, sid, path string, args *LinkArgs) (*model.Sharing, *model.Link, model.Obj, error) {
sharing, err := op.GetSharingById(sid, args.SharingListArgs.Refresh)
if err != nil {
return nil, nil, nil, errors.WithStack(errs.SharingNotFound)
}
if !sharing.Valid() {
return sharing, nil, nil, errors.WithStack(errs.InvalidSharing)
}
if !sharing.Verify(args.Pwd) {
return sharing, nil, nil, errors.WithStack(errs.WrongShareCode)
}
path = utils.FixAndCleanPath(path)
if len(sharing.Files) == 1 || path != "/" {
unwrapPath, err := op.GetSharingUnwrapPath(sharing, path)
if err != nil {
return nil, nil, nil, errors.WithMessage(err, "failed get sharing unwrap path")
}
storage, actualPath, err := op.GetStorageAndActualPath(unwrapPath)
if err != nil {
return nil, nil, nil, errors.WithMessage(err, "failed get sharing link")
}
l, obj, err := op.Link(ctx, storage, actualPath, args.LinkArgs)
if err != nil {
return nil, nil, nil, errors.WithMessage(err, "failed get sharing link")
}
if l.URL != "" && !strings.HasPrefix(l.URL, "http://") && !strings.HasPrefix(l.URL, "https://") {
l.URL = common.GetApiUrl(ctx) + l.URL
}
return sharing, l, obj, nil
}
return nil, nil, nil, errors.New("cannot get sharing root link")
}

View File

@ -1,83 +0,0 @@
package sharing
import (
"context"
stdpath "path"
"github.com/OpenListTeam/OpenList/v4/internal/errs"
"github.com/OpenListTeam/OpenList/v4/internal/model"
"github.com/OpenListTeam/OpenList/v4/internal/op"
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
"github.com/pkg/errors"
)
func list(ctx context.Context, sid, path string, args model.SharingListArgs) (*model.Sharing, []model.Obj, error) {
sharing, err := op.GetSharingById(sid, args.Refresh)
if err != nil {
return nil, nil, errors.WithStack(errs.SharingNotFound)
}
if !sharing.Valid() {
return sharing, nil, errors.WithStack(errs.InvalidSharing)
}
if !sharing.Verify(args.Pwd) {
return sharing, nil, errors.WithStack(errs.WrongShareCode)
}
path = utils.FixAndCleanPath(path)
if len(sharing.Files) == 1 || path != "/" {
unwrapPath, err := op.GetSharingUnwrapPath(sharing, path)
if err != nil {
return nil, nil, errors.WithMessage(err, "failed get sharing unwrap path")
}
virtualFiles := op.GetStorageVirtualFilesByPath(unwrapPath)
storage, actualPath, err := op.GetStorageAndActualPath(unwrapPath)
if err != nil && len(virtualFiles) == 0 {
return nil, nil, errors.WithMessage(err, "failed list sharing")
}
var objs []model.Obj
if storage != nil {
objs, err = op.List(ctx, storage, actualPath, model.ListArgs{
Refresh: args.Refresh,
ReqPath: stdpath.Join(sid, path),
})
if err != nil && len(virtualFiles) == 0 {
return nil, nil, errors.WithMessage(err, "failed list sharing")
}
}
om := model.NewObjMerge()
objs = om.Merge(objs, virtualFiles...)
model.SortFiles(objs, sharing.OrderBy, sharing.OrderDirection)
model.ExtractFolder(objs, sharing.ExtractFolder)
return sharing, objs, nil
}
objs := make([]model.Obj, 0, len(sharing.Files))
for _, f := range sharing.Files {
if f != "/" {
isVf := false
virtualFiles := op.GetStorageVirtualFilesByPath(stdpath.Dir(f))
for _, vf := range virtualFiles {
if vf.GetName() == stdpath.Base(f) {
objs = append(objs, vf)
isVf = true
break
}
}
if isVf {
continue
}
} else {
continue
}
storage, actualPath, err := op.GetStorageAndActualPath(f)
if err != nil {
continue
}
obj, err := op.Get(ctx, storage, actualPath)
if err != nil {
continue
}
objs = append(objs, obj)
}
model.SortFiles(objs, sharing.OrderBy, sharing.OrderDirection)
model.ExtractFolder(objs, sharing.ExtractFolder)
return sharing, objs, nil
}

View File

@ -1,58 +0,0 @@
package sharing
import (
"context"
"github.com/OpenListTeam/OpenList/v4/internal/model"
log "github.com/sirupsen/logrus"
)
func List(ctx context.Context, sid, path string, args model.SharingListArgs) (*model.Sharing, []model.Obj, error) {
sharing, res, err := list(ctx, sid, path, args)
if err != nil {
log.Errorf("failed list sharing %s/%s: %+v", sid, path, err)
return nil, nil, err
}
return sharing, res, nil
}
func Get(ctx context.Context, sid, path string, args model.SharingListArgs) (*model.Sharing, model.Obj, error) {
sharing, res, err := get(ctx, sid, path, args)
if err != nil {
log.Warnf("failed get sharing %s/%s: %s", sid, path, err)
return nil, nil, err
}
return sharing, res, nil
}
func ArchiveMeta(ctx context.Context, sid, path string, args model.SharingArchiveMetaArgs) (*model.Sharing, *model.ArchiveMetaProvider, error) {
sharing, res, err := archiveMeta(ctx, sid, path, args)
if err != nil {
log.Warnf("failed get sharing archive meta %s/%s: %s", sid, path, err)
return nil, nil, err
}
return sharing, res, nil
}
func ArchiveList(ctx context.Context, sid, path string, args model.SharingArchiveListArgs) (*model.Sharing, []model.Obj, error) {
sharing, res, err := archiveList(ctx, sid, path, args)
if err != nil {
log.Warnf("failed list sharing archive %s/%s: %s", sid, path, err)
return nil, nil, err
}
return sharing, res, nil
}
type LinkArgs struct {
model.SharingListArgs
model.LinkArgs
}
func Link(ctx context.Context, sid, path string, args *LinkArgs) (*model.Sharing, *model.Link, model.Obj, error) {
sharing, res, file, err := link(ctx, sid, path, args)
if err != nil {
log.Errorf("failed get sharing link %s/%s: %+v", sid, path, err)
return nil, nil, nil, err
}
return sharing, res, file, nil
}

View File

@ -15,7 +15,6 @@ import (
"github.com/OpenListTeam/OpenList/v4/pkg/buffer" "github.com/OpenListTeam/OpenList/v4/pkg/buffer"
"github.com/OpenListTeam/OpenList/v4/pkg/http_range" "github.com/OpenListTeam/OpenList/v4/pkg/http_range"
"github.com/OpenListTeam/OpenList/v4/pkg/utils" "github.com/OpenListTeam/OpenList/v4/pkg/utils"
"github.com/rclone/rclone/lib/mmap"
"go4.org/readerutil" "go4.org/readerutil"
) )
@ -61,12 +60,8 @@ func (f *FileStream) IsForceStreamUpload() bool {
} }
func (f *FileStream) Close() error { func (f *FileStream) Close() error {
if f.peekBuff != nil {
f.peekBuff.Reset()
f.peekBuff = nil
}
var err1, err2 error var err1, err2 error
err1 = f.Closers.Close() err1 = f.Closers.Close()
if errors.Is(err1, os.ErrClosed) { if errors.Is(err1, os.ErrClosed) {
err1 = nil err1 = nil
@ -79,6 +74,10 @@ func (f *FileStream) Close() error {
f.tmpFile = nil f.tmpFile = nil
} }
} }
if f.peekBuff != nil {
f.peekBuff.Reset()
f.peekBuff = nil
}
return errors.Join(err1, err2) return errors.Join(err1, err2)
} }
@ -195,19 +194,7 @@ func (f *FileStream) cache(maxCacheSize int64) (model.File, error) {
f.oriReader = f.Reader f.oriReader = f.Reader
} }
bufSize := maxCacheSize - int64(f.peekBuff.Len()) bufSize := maxCacheSize - int64(f.peekBuff.Len())
var buf []byte buf := make([]byte, bufSize)
if conf.MmapThreshold > 0 && bufSize >= int64(conf.MmapThreshold) {
m, err := mmap.Alloc(int(bufSize))
if err == nil {
f.Add(utils.CloseFunc(func() error {
return mmap.Free(m)
}))
buf = m
}
}
if buf == nil {
buf = make([]byte, bufSize)
}
n, err := io.ReadFull(f.oriReader, buf) n, err := io.ReadFull(f.oriReader, buf)
if bufSize != int64(n) { if bufSize != int64(n) {
return nil, fmt.Errorf("failed to read all data: (expect =%d, actual =%d) %w", bufSize, n, err) return nil, fmt.Errorf("failed to read all data: (expect =%d, actual =%d) %w", bufSize, n, err)

View File

@ -7,13 +7,11 @@ import (
"io" "io"
"testing" "testing"
"github.com/OpenListTeam/OpenList/v4/internal/conf"
"github.com/OpenListTeam/OpenList/v4/internal/model" "github.com/OpenListTeam/OpenList/v4/internal/model"
"github.com/OpenListTeam/OpenList/v4/pkg/http_range" "github.com/OpenListTeam/OpenList/v4/pkg/http_range"
) )
func TestFileStream_RangeRead(t *testing.T) { func TestFileStream_RangeRead(t *testing.T) {
conf.MaxBufferLimit = 16 * 1024 * 1024
type args struct { type args struct {
httpRange http_range.Range httpRange http_range.Range
} }
@ -73,7 +71,7 @@ func TestFileStream_RangeRead(t *testing.T) {
} }
}) })
} }
t.Run("after", func(t *testing.T) { t.Run("after check", func(t *testing.T) {
if f.GetFile() == nil { if f.GetFile() == nil {
t.Error("not cached") t.Error("not cached")
} }

View File

@ -8,14 +8,13 @@ import (
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"sync"
"github.com/OpenListTeam/OpenList/v4/internal/conf" "github.com/OpenListTeam/OpenList/v4/internal/conf"
"github.com/OpenListTeam/OpenList/v4/internal/model" "github.com/OpenListTeam/OpenList/v4/internal/model"
"github.com/OpenListTeam/OpenList/v4/internal/net" "github.com/OpenListTeam/OpenList/v4/internal/net"
"github.com/OpenListTeam/OpenList/v4/pkg/http_range" "github.com/OpenListTeam/OpenList/v4/pkg/http_range"
"github.com/OpenListTeam/OpenList/v4/pkg/pool"
"github.com/OpenListTeam/OpenList/v4/pkg/utils" "github.com/OpenListTeam/OpenList/v4/pkg/utils"
"github.com/rclone/rclone/lib/mmap"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
@ -154,49 +153,26 @@ func CacheFullAndHash(stream model.FileStreamer, up *model.UpdateProgress, hashT
type StreamSectionReader struct { type StreamSectionReader struct {
file model.FileStreamer file model.FileStreamer
off int64 off int64
bufPool *pool.Pool[[]byte] bufPool *sync.Pool
} }
func NewStreamSectionReader(file model.FileStreamer, maxBufferSize int, up *model.UpdateProgress) (*StreamSectionReader, error) { func NewStreamSectionReader(file model.FileStreamer, maxBufferSize int, up *model.UpdateProgress) (*StreamSectionReader, error) {
ss := &StreamSectionReader{file: file} ss := &StreamSectionReader{file: file}
if file.GetFile() != nil { if file.GetFile() == nil {
return ss, nil
}
maxBufferSize = min(maxBufferSize, int(file.GetSize())) maxBufferSize = min(maxBufferSize, int(file.GetSize()))
if maxBufferSize > conf.MaxBufferLimit { if maxBufferSize > conf.MaxBufferLimit {
_, err := file.CacheFullAndWriter(up, nil) _, err := file.CacheFullAndWriter(up, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return ss, nil
}
if conf.MmapThreshold > 0 && maxBufferSize >= conf.MmapThreshold {
ss.bufPool = &pool.Pool[[]byte]{
New: func() []byte {
buf, err := mmap.Alloc(maxBufferSize)
if err == nil {
file.Add(utils.CloseFunc(func() error {
return mmap.Free(buf)
}))
} else { } else {
buf = make([]byte, maxBufferSize) ss.bufPool = &sync.Pool{
} New: func() any {
return buf
},
}
} else {
ss.bufPool = &pool.Pool[[]byte]{
New: func() []byte {
return make([]byte, maxBufferSize) return make([]byte, maxBufferSize)
}, },
} }
} }
}
file.Add(utils.CloseFunc(func() error {
ss.bufPool.Reset()
return nil
}))
return ss, nil return ss, nil
} }
@ -208,7 +184,7 @@ func (ss *StreamSectionReader) GetSectionReader(off, length int64) (*SectionRead
if off != ss.off { if off != ss.off {
return nil, fmt.Errorf("stream not cached: request offset %d != current offset %d", off, ss.off) return nil, fmt.Errorf("stream not cached: request offset %d != current offset %d", off, ss.off)
} }
tempBuf := ss.bufPool.Get() tempBuf := ss.bufPool.Get().([]byte)
buf = tempBuf[:length] buf = tempBuf[:length]
n, err := io.ReadFull(ss.file, buf) n, err := io.ReadFull(ss.file, buf)
if int64(n) != length { if int64(n) != length {

View File

@ -1,37 +0,0 @@
package pool
import "sync"
type Pool[T any] struct {
New func() T
MaxCap int
cache []T
mu sync.Mutex
}
func (p *Pool[T]) Get() T {
p.mu.Lock()
defer p.mu.Unlock()
if len(p.cache) == 0 {
return p.New()
}
item := p.cache[len(p.cache)-1]
p.cache = p.cache[:len(p.cache)-1]
return item
}
func (p *Pool[T]) Put(item T) {
p.mu.Lock()
defer p.mu.Unlock()
if p.MaxCap == 0 || len(p.cache) < int(p.MaxCap) {
p.cache = append(p.cache, item)
}
}
func (p *Pool[T]) Reset() {
p.mu.Lock()
defer p.mu.Unlock()
clear(p.cache)
p.cache = nil
}

View File

@ -2,9 +2,6 @@ package common
import ( import (
"context" "context"
"fmt"
"html"
"net/http"
"strings" "strings"
"github.com/OpenListTeam/OpenList/v4/cmd/flags" "github.com/OpenListTeam/OpenList/v4/cmd/flags"
@ -41,41 +38,6 @@ func ErrorResp(c *gin.Context, err error, code int, l ...bool) {
//c.Abort() //c.Abort()
} }
// ErrorPage is used to return error page HTML.
// It also returns standard HTTP status code.
// @param l: if true, log error
func ErrorPage(c *gin.Context, err error, code int, l ...bool) {
if len(l) > 0 && l[0] {
if flags.Debug || flags.Dev {
log.Errorf("%+v", err)
} else {
log.Errorf("%v", err)
}
}
codes := fmt.Sprintf("%d %s", code, http.StatusText(code))
html := fmt.Sprintf(`<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="color-scheme" content="dark light" />
<meta name="robots" content="noindex" />
<title>%s</title>
</head>
<body>
<h1>%s</h1>
<hr>
<p>%s</p>
</body>
</html>`,
codes, codes, html.EscapeString(hidePrivacy(err.Error())))
c.Data(code, "text/html; charset=utf-8", []byte(html))
c.Abort()
}
func ErrorWithDataResp(c *gin.Context, err error, code int, data interface{}, l ...bool) { func ErrorWithDataResp(c *gin.Context, err error, code int, data interface{}, l ...bool) {
if len(l) > 0 && l[0] { if len(l) > 0 && l[0] {
if flags.Debug || flags.Dev { if flags.Debug || flags.Dev {

View File

@ -3,9 +3,9 @@ package handles
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io"
stdpath "path" stdpath "path"
"strings"
"github.com/OpenListTeam/OpenList/v4/internal/task"
"github.com/OpenListTeam/OpenList/v4/internal/archive/tool" "github.com/OpenListTeam/OpenList/v4/internal/archive/tool"
"github.com/OpenListTeam/OpenList/v4/internal/conf" "github.com/OpenListTeam/OpenList/v4/internal/conf"
@ -15,7 +15,6 @@ import (
"github.com/OpenListTeam/OpenList/v4/internal/op" "github.com/OpenListTeam/OpenList/v4/internal/op"
"github.com/OpenListTeam/OpenList/v4/internal/setting" "github.com/OpenListTeam/OpenList/v4/internal/setting"
"github.com/OpenListTeam/OpenList/v4/internal/sign" "github.com/OpenListTeam/OpenList/v4/internal/sign"
"github.com/OpenListTeam/OpenList/v4/internal/task"
"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"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@ -72,26 +71,13 @@ func toContentResp(objs []model.ObjTree) []ArchiveContentResp {
return ret return ret
} }
func FsArchiveMetaSplit(c *gin.Context) { func FsArchiveMeta(c *gin.Context) {
var req ArchiveMetaReq var req ArchiveMetaReq
if err := c.ShouldBind(&req); err != nil { if err := c.ShouldBind(&req); err != nil {
common.ErrorResp(c, err, 400) common.ErrorResp(c, err, 400)
return return
} }
if strings.HasPrefix(req.Path, "/@s") {
req.Path = strings.TrimPrefix(req.Path, "/@s")
SharingArchiveMeta(c, &req)
return
}
user := c.Request.Context().Value(conf.UserKey).(*model.User) user := c.Request.Context().Value(conf.UserKey).(*model.User)
if user.IsGuest() && user.Disabled {
common.ErrorStrResp(c, "Guest user is disabled, login please", 401)
return
}
FsArchiveMeta(c, &req, user)
}
func FsArchiveMeta(c *gin.Context, req *ArchiveMetaReq, user *model.User) {
if !user.CanReadArchives() { if !user.CanReadArchives() {
common.ErrorResp(c, errs.PermissionDenied, 403) common.ErrorResp(c, errs.PermissionDenied, 403)
return return
@ -156,27 +142,19 @@ type ArchiveListReq struct {
InnerPath string `json:"inner_path" form:"inner_path"` InnerPath string `json:"inner_path" form:"inner_path"`
} }
func FsArchiveListSplit(c *gin.Context) { type ArchiveListResp struct {
Content []ObjResp `json:"content"`
Total int64 `json:"total"`
}
func FsArchiveList(c *gin.Context) {
var req ArchiveListReq var req ArchiveListReq
if err := c.ShouldBind(&req); err != nil { if err := c.ShouldBind(&req); err != nil {
common.ErrorResp(c, err, 400) common.ErrorResp(c, err, 400)
return return
} }
req.Validate() req.Validate()
if strings.HasPrefix(req.Path, "/@s") {
req.Path = strings.TrimPrefix(req.Path, "/@s")
SharingArchiveList(c, &req)
return
}
user := c.Request.Context().Value(conf.UserKey).(*model.User) user := c.Request.Context().Value(conf.UserKey).(*model.User)
if user.IsGuest() && user.Disabled {
common.ErrorStrResp(c, "Guest user is disabled, login please", 401)
return
}
FsArchiveList(c, &req, user)
}
func FsArchiveList(c *gin.Context, req *ArchiveListReq, user *model.User) {
if !user.CanReadArchives() { if !user.CanReadArchives() {
common.ErrorResp(c, errs.PermissionDenied, 403) common.ErrorResp(c, errs.PermissionDenied, 403)
return return
@ -223,7 +201,7 @@ func FsArchiveList(c *gin.Context, req *ArchiveListReq, user *model.User) {
ret, _ := utils.SliceConvert(objs, func(src model.Obj) (ObjResp, error) { ret, _ := utils.SliceConvert(objs, func(src model.Obj) (ObjResp, error) {
return toObjsRespWithoutSignAndThumb(src), nil return toObjsRespWithoutSignAndThumb(src), nil
}) })
common.SuccessResp(c, common.PageResp{ common.SuccessResp(c, ArchiveListResp{
Content: ret, Content: ret,
Total: int64(total), Total: int64(total),
}) })
@ -320,7 +298,7 @@ func ArchiveDown(c *gin.Context) {
filename := stdpath.Base(innerPath) filename := stdpath.Base(innerPath)
storage, err := fs.GetStorage(archiveRawPath, &fs.GetStoragesArgs{}) storage, err := fs.GetStorage(archiveRawPath, &fs.GetStoragesArgs{})
if err != nil { if err != nil {
common.ErrorPage(c, err, 500) common.ErrorResp(c, err, 500)
return return
} }
if common.ShouldProxy(storage, filename) { if common.ShouldProxy(storage, filename) {
@ -340,7 +318,7 @@ func ArchiveDown(c *gin.Context) {
InnerPath: innerPath, InnerPath: innerPath,
}) })
if err != nil { if err != nil {
common.ErrorPage(c, err, 500) common.ErrorResp(c, err, 500)
return return
} }
redirect(c, link) redirect(c, link)
@ -354,7 +332,7 @@ func ArchiveProxy(c *gin.Context) {
filename := stdpath.Base(innerPath) filename := stdpath.Base(innerPath)
storage, err := fs.GetStorage(archiveRawPath, &fs.GetStoragesArgs{}) storage, err := fs.GetStorage(archiveRawPath, &fs.GetStoragesArgs{})
if err != nil { if err != nil {
common.ErrorPage(c, err, 500) common.ErrorResp(c, err, 500)
return return
} }
if canProxy(storage, filename) { if canProxy(storage, filename) {
@ -370,34 +348,16 @@ func ArchiveProxy(c *gin.Context) {
InnerPath: innerPath, InnerPath: innerPath,
}) })
if err != nil { if err != nil {
common.ErrorPage(c, err, 500) common.ErrorResp(c, err, 500)
return return
} }
proxy(c, link, file, storage.GetStorage().ProxyRange) proxy(c, link, file, storage.GetStorage().ProxyRange)
} else { } else {
common.ErrorPage(c, errors.New("proxy not allowed"), 403) common.ErrorStrResp(c, "proxy not allowed", 403)
return return
} }
} }
func proxyInternalExtract(c *gin.Context, rc io.ReadCloser, size int64, fileName string) {
defer func() {
if err := rc.Close(); err != nil {
log.Errorf("failed to close file streamer, %v", err)
}
}()
headers := map[string]string{
"Referrer-Policy": "no-referrer",
"Cache-Control": "max-age=0, no-cache, no-store, must-revalidate",
}
headers["Content-Disposition"] = utils.GenerateContentDisposition(fileName)
contentType := c.Request.Header.Get("Content-Type")
if contentType == "" {
contentType = utils.GetMimeType(fileName)
}
c.DataFromReader(200, size, contentType, rc, headers)
}
func ArchiveInternalExtract(c *gin.Context) { func ArchiveInternalExtract(c *gin.Context) {
archiveRawPath := c.Request.Context().Value(conf.PathKey).(string) archiveRawPath := c.Request.Context().Value(conf.PathKey).(string)
innerPath := utils.FixAndCleanPath(c.Query("inner")) innerPath := utils.FixAndCleanPath(c.Query("inner"))
@ -413,11 +373,25 @@ func ArchiveInternalExtract(c *gin.Context) {
InnerPath: innerPath, InnerPath: innerPath,
}) })
if err != nil { if err != nil {
common.ErrorPage(c, err, 500) common.ErrorResp(c, err, 500)
return return
} }
defer func() {
if err := rc.Close(); err != nil {
log.Errorf("failed to close file streamer, %v", err)
}
}()
headers := map[string]string{
"Referrer-Policy": "no-referrer",
"Cache-Control": "max-age=0, no-cache, no-store, must-revalidate",
}
fileName := stdpath.Base(innerPath) fileName := stdpath.Base(innerPath)
proxyInternalExtract(c, rc, size, fileName) headers["Content-Disposition"] = utils.GenerateContentDisposition(fileName)
contentType := c.Request.Header.Get("Content-Type")
if contentType == "" {
contentType = utils.GetMimeType(fileName)
}
c.DataFromReader(200, size, contentType, rc, headers)
} }
func ArchiveExtensions(c *gin.Context) { func ArchiveExtensions(c *gin.Context) {

View File

@ -26,7 +26,7 @@ func Down(c *gin.Context) {
filename := stdpath.Base(rawPath) filename := stdpath.Base(rawPath)
storage, err := fs.GetStorage(rawPath, &fs.GetStoragesArgs{}) storage, err := fs.GetStorage(rawPath, &fs.GetStoragesArgs{})
if err != nil { if err != nil {
common.ErrorPage(c, err, 500) common.ErrorResp(c, err, 500)
return return
} }
if common.ShouldProxy(storage, filename) { if common.ShouldProxy(storage, filename) {
@ -40,7 +40,7 @@ func Down(c *gin.Context) {
Redirect: true, Redirect: true,
}) })
if err != nil { if err != nil {
common.ErrorPage(c, err, 500) common.ErrorResp(c, err, 500)
return return
} }
redirect(c, link) redirect(c, link)
@ -52,7 +52,7 @@ func Proxy(c *gin.Context) {
filename := stdpath.Base(rawPath) filename := stdpath.Base(rawPath)
storage, err := fs.GetStorage(rawPath, &fs.GetStoragesArgs{}) storage, err := fs.GetStorage(rawPath, &fs.GetStoragesArgs{})
if err != nil { if err != nil {
common.ErrorPage(c, err, 500) common.ErrorResp(c, err, 500)
return return
} }
if canProxy(storage, filename) { if canProxy(storage, filename) {
@ -67,12 +67,12 @@ func Proxy(c *gin.Context) {
Type: c.Query("type"), Type: c.Query("type"),
}) })
if err != nil { if err != nil {
common.ErrorPage(c, err, 500) common.ErrorResp(c, err, 500)
return return
} }
proxy(c, link, file, storage.GetStorage().ProxyRange) proxy(c, link, file, storage.GetStorage().ProxyRange)
} else { } else {
common.ErrorPage(c, errors.New("proxy not allowed"), 403) common.ErrorStrResp(c, "proxy not allowed", 403)
return return
} }
} }
@ -89,7 +89,7 @@ func redirect(c *gin.Context, link *model.Link) {
} }
link.URL, err = utils.InjectQuery(link.URL, query) link.URL, err = utils.InjectQuery(link.URL, query)
if err != nil { if err != nil {
common.ErrorPage(c, err, 500) common.ErrorResp(c, err, 500)
return return
} }
} }
@ -106,7 +106,7 @@ func proxy(c *gin.Context, link *model.Link, file model.Obj, proxyRange bool) {
} }
link.URL, err = utils.InjectQuery(link.URL, query) link.URL, err = utils.InjectQuery(link.URL, query)
if err != nil { if err != nil {
common.ErrorPage(c, err, 500) common.ErrorResp(c, err, 500)
return return
} }
} }
@ -149,9 +149,9 @@ func proxy(c *gin.Context, link *model.Link, file model.Obj, proxyRange bool) {
log.Errorf("%s %s local proxy error: %+v", c.Request.Method, c.Request.URL.Path, err) log.Errorf("%s %s local proxy error: %+v", c.Request.Method, c.Request.URL.Path, err)
} else { } else {
if statusCode, ok := errors.Unwrap(err).(net.ErrorHttpStatusCode); ok { if statusCode, ok := errors.Unwrap(err).(net.ErrorHttpStatusCode); ok {
common.ErrorPage(c, err, int(statusCode), true) common.ErrorResp(c, err, int(statusCode), true)
} else { } else {
common.ErrorPage(c, err, 500, true) common.ErrorResp(c, err, 500, true)
} }
} }
} }

View File

@ -56,27 +56,14 @@ type FsListResp struct {
Provider string `json:"provider"` Provider string `json:"provider"`
} }
func FsListSplit(c *gin.Context) { func FsList(c *gin.Context) {
var req ListReq var req ListReq
if err := c.ShouldBind(&req); err != nil { if err := c.ShouldBind(&req); err != nil {
common.ErrorResp(c, err, 400) common.ErrorResp(c, err, 400)
return return
} }
req.Validate() req.Validate()
if strings.HasPrefix(req.Path, "/@s") {
req.Path = strings.TrimPrefix(req.Path, "/@s")
SharingList(c, &req)
return
}
user := c.Request.Context().Value(conf.UserKey).(*model.User) user := c.Request.Context().Value(conf.UserKey).(*model.User)
if user.IsGuest() && user.Disabled {
common.ErrorStrResp(c, "Guest user is disabled, login please", 401)
return
}
FsList(c, &req, user)
}
func FsList(c *gin.Context, req *ListReq, user *model.User) {
reqPath, err := user.JoinPath(req.Path) reqPath, err := user.JoinPath(req.Path)
if err != nil { if err != nil {
common.ErrorResp(c, err, 403) common.ErrorResp(c, err, 403)
@ -256,26 +243,13 @@ type FsGetResp struct {
Related []ObjResp `json:"related"` Related []ObjResp `json:"related"`
} }
func FsGetSplit(c *gin.Context) { func FsGet(c *gin.Context) {
var req FsGetReq var req FsGetReq
if err := c.ShouldBind(&req); err != nil { if err := c.ShouldBind(&req); err != nil {
common.ErrorResp(c, err, 400) common.ErrorResp(c, err, 400)
return return
} }
if strings.HasPrefix(req.Path, "/@s") {
req.Path = strings.TrimPrefix(req.Path, "/@s")
SharingGet(c, &req)
return
}
user := c.Request.Context().Value(conf.UserKey).(*model.User) user := c.Request.Context().Value(conf.UserKey).(*model.User)
if user.IsGuest() && user.Disabled {
common.ErrorStrResp(c, "Guest user is disabled, login please", 401)
return
}
FsGet(c, &req, user)
}
func FsGet(c *gin.Context, req *FsGetReq, user *model.User) {
reqPath, err := user.JoinPath(req.Path) reqPath, err := user.JoinPath(req.Path)
if err != nil { if err != nil {
common.ErrorResp(c, err, 403) common.ErrorResp(c, err, 403)

View File

@ -1,568 +0,0 @@
package handles
import (
"fmt"
stdpath "path"
"strings"
"time"
"github.com/OpenListTeam/OpenList/v4/internal/conf"
"github.com/OpenListTeam/OpenList/v4/internal/driver"
"github.com/OpenListTeam/OpenList/v4/internal/errs"
"github.com/OpenListTeam/OpenList/v4/internal/model"
"github.com/OpenListTeam/OpenList/v4/internal/op"
"github.com/OpenListTeam/OpenList/v4/internal/setting"
"github.com/OpenListTeam/OpenList/v4/internal/sharing"
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
"github.com/OpenListTeam/OpenList/v4/server/common"
"github.com/OpenListTeam/go-cache"
"github.com/gin-gonic/gin"
"github.com/pkg/errors"
)
func SharingGet(c *gin.Context, req *FsGetReq) {
sid, path, _ := strings.Cut(strings.TrimPrefix(req.Path, "/"), "/")
if sid == "" {
common.ErrorStrResp(c, "invalid share id", 400)
return
}
s, obj, err := sharing.Get(c.Request.Context(), sid, path, model.SharingListArgs{
Refresh: false,
Pwd: req.Password,
})
if dealError(c, err) {
return
}
_ = countAccess(c.ClientIP(), s)
fakePath := fmt.Sprintf("/%s/%s", sid, path)
url := ""
if !obj.IsDir() {
url = fmt.Sprintf("%s/sd%s", common.GetApiUrl(c), utils.EncodePath(fakePath, true))
if s.Pwd != "" {
url += "?pwd=" + s.Pwd
}
}
thumb, _ := model.GetThumb(obj)
common.SuccessResp(c, FsGetResp{
ObjResp: ObjResp{
Id: "",
Path: fakePath,
Name: obj.GetName(),
Size: obj.GetSize(),
IsDir: obj.IsDir(),
Modified: obj.ModTime(),
Created: obj.CreateTime(),
HashInfoStr: obj.GetHash().String(),
HashInfo: obj.GetHash().Export(),
Sign: "",
Type: utils.GetFileType(obj.GetName()),
Thumb: thumb,
},
RawURL: url,
Readme: s.Readme,
Header: s.Header,
Provider: "unknown",
Related: nil,
})
}
func SharingList(c *gin.Context, req *ListReq) {
sid, path, _ := strings.Cut(strings.TrimPrefix(req.Path, "/"), "/")
if sid == "" {
common.ErrorStrResp(c, "invalid share id", 400)
return
}
s, objs, err := sharing.List(c.Request.Context(), sid, path, model.SharingListArgs{
Refresh: req.Refresh,
Pwd: req.Password,
})
if dealError(c, err) {
return
}
_ = countAccess(c.ClientIP(), s)
fakePath := fmt.Sprintf("/%s/%s", sid, path)
total, objs := pagination(objs, &req.PageReq)
common.SuccessResp(c, FsListResp{
Content: utils.MustSliceConvert(objs, func(obj model.Obj) ObjResp {
thumb, _ := model.GetThumb(obj)
return ObjResp{
Id: "",
Path: stdpath.Join(fakePath, obj.GetName()),
Name: obj.GetName(),
Size: obj.GetSize(),
IsDir: obj.IsDir(),
Modified: obj.ModTime(),
Created: obj.CreateTime(),
HashInfoStr: obj.GetHash().String(),
HashInfo: obj.GetHash().Export(),
Sign: "",
Thumb: thumb,
Type: utils.GetObjType(obj.GetName(), obj.IsDir()),
}
}),
Total: int64(total),
Readme: s.Readme,
Header: s.Header,
Write: false,
Provider: "unknown",
})
}
func SharingArchiveMeta(c *gin.Context, req *ArchiveMetaReq) {
if !setting.GetBool(conf.ShareArchivePreview) {
common.ErrorStrResp(c, "sharing archives previewing is not allowed", 403)
return
}
sid, path, _ := strings.Cut(strings.TrimPrefix(req.Path, "/"), "/")
if sid == "" {
common.ErrorStrResp(c, "invalid share id", 400)
return
}
archiveArgs := model.ArchiveArgs{
LinkArgs: model.LinkArgs{
Header: c.Request.Header,
Type: c.Query("type"),
},
Password: req.ArchivePass,
}
s, ret, err := sharing.ArchiveMeta(c.Request.Context(), sid, path, model.SharingArchiveMetaArgs{
ArchiveMetaArgs: model.ArchiveMetaArgs{
ArchiveArgs: archiveArgs,
Refresh: req.Refresh,
},
Pwd: req.Password,
})
if dealError(c, err) {
return
}
_ = countAccess(c.ClientIP(), s)
fakePath := fmt.Sprintf("/%s/%s", sid, path)
url := fmt.Sprintf("%s/sad%s", common.GetApiUrl(c), utils.EncodePath(fakePath, true))
if s.Pwd != "" {
url += "?pwd=" + s.Pwd
}
common.SuccessResp(c, ArchiveMetaResp{
Comment: ret.GetComment(),
IsEncrypted: ret.IsEncrypted(),
Content: toContentResp(ret.GetTree()),
Sort: ret.Sort,
RawURL: url,
Sign: "",
})
}
func SharingArchiveList(c *gin.Context, req *ArchiveListReq) {
if !setting.GetBool(conf.ShareArchivePreview) {
common.ErrorStrResp(c, "sharing archives previewing is not allowed", 403)
return
}
sid, path, _ := strings.Cut(strings.TrimPrefix(req.Path, "/"), "/")
if sid == "" {
common.ErrorStrResp(c, "invalid share id", 400)
return
}
innerArgs := model.ArchiveInnerArgs{
ArchiveArgs: model.ArchiveArgs{
LinkArgs: model.LinkArgs{
Header: c.Request.Header,
Type: c.Query("type"),
},
Password: req.ArchivePass,
},
InnerPath: utils.FixAndCleanPath(req.InnerPath),
}
s, objs, err := sharing.ArchiveList(c.Request.Context(), sid, path, model.SharingArchiveListArgs{
ArchiveListArgs: model.ArchiveListArgs{
ArchiveInnerArgs: innerArgs,
Refresh: req.Refresh,
},
Pwd: req.Password,
})
if dealError(c, err) {
return
}
_ = countAccess(c.ClientIP(), s)
total, objs := pagination(objs, &req.PageReq)
ret, _ := utils.SliceConvert(objs, func(src model.Obj) (ObjResp, error) {
return toObjsRespWithoutSignAndThumb(src), nil
})
common.SuccessResp(c, common.PageResp{
Content: ret,
Total: int64(total),
})
}
func SharingDown(c *gin.Context) {
sid := c.Request.Context().Value(conf.SharingIDKey).(string)
path := c.Request.Context().Value(conf.PathKey).(string)
pwd := c.Query("pwd")
s, err := op.GetSharingById(sid)
if err == nil {
if !s.Valid() {
err = errs.InvalidSharing
} else if !s.Verify(pwd) {
err = errs.WrongShareCode
} else if len(s.Files) != 1 && path == "/" {
err = errors.New("cannot get sharing root link")
}
}
if dealErrorPage(c, err) {
return
}
unwrapPath, err := op.GetSharingUnwrapPath(s, path)
if err != nil {
common.ErrorPage(c, errors.New("failed get sharing unwrap path"), 500)
return
}
storage, actualPath, err := op.GetStorageAndActualPath(unwrapPath)
if dealErrorPage(c, err) {
return
}
if setting.GetBool(conf.ShareForceProxy) || common.ShouldProxy(storage, stdpath.Base(actualPath)) {
link, obj, err := op.Link(c.Request.Context(), storage, actualPath, model.LinkArgs{
Header: c.Request.Header,
Type: c.Query("type"),
})
if err != nil {
common.ErrorPage(c, errors.WithMessage(err, "failed get sharing link"), 500)
return
}
_ = countAccess(c.ClientIP(), s)
proxy(c, link, obj, storage.GetStorage().ProxyRange)
} else {
link, _, err := op.Link(c.Request.Context(), storage, actualPath, model.LinkArgs{
IP: c.ClientIP(),
Header: c.Request.Header,
Type: c.Query("type"),
Redirect: true,
})
if err != nil {
common.ErrorPage(c, errors.WithMessage(err, "failed get sharing link"), 500)
return
}
_ = countAccess(c.ClientIP(), s)
redirect(c, link)
}
}
func SharingArchiveExtract(c *gin.Context) {
if !setting.GetBool(conf.ShareArchivePreview) {
common.ErrorPage(c, errors.New("sharing archives previewing is not allowed"), 403)
return
}
sid := c.Request.Context().Value(conf.SharingIDKey).(string)
path := c.Request.Context().Value(conf.PathKey).(string)
pwd := c.Query("pwd")
innerPath := utils.FixAndCleanPath(c.Query("inner"))
archivePass := c.Query("pass")
s, err := op.GetSharingById(sid)
if err == nil {
if !s.Valid() {
err = errs.InvalidSharing
} else if !s.Verify(pwd) {
err = errs.WrongShareCode
} else if len(s.Files) != 1 && path == "/" {
err = errors.New("cannot extract sharing root")
}
}
if dealErrorPage(c, err) {
return
}
unwrapPath, err := op.GetSharingUnwrapPath(s, path)
if err != nil {
common.ErrorPage(c, errors.New("failed get sharing unwrap path"), 500)
return
}
storage, actualPath, err := op.GetStorageAndActualPath(unwrapPath)
if dealErrorPage(c, err) {
return
}
args := model.ArchiveInnerArgs{
ArchiveArgs: model.ArchiveArgs{
LinkArgs: model.LinkArgs{
Header: c.Request.Header,
Type: c.Query("type"),
},
Password: archivePass,
},
InnerPath: innerPath,
}
if _, ok := storage.(driver.ArchiveReader); ok {
if setting.GetBool(conf.ShareForceProxy) || common.ShouldProxy(storage, stdpath.Base(actualPath)) {
link, obj, err := op.DriverExtract(c.Request.Context(), storage, actualPath, args)
if dealErrorPage(c, err) {
return
}
proxy(c, link, obj, storage.GetStorage().ProxyRange)
} else {
args.Redirect = true
link, _, err := op.DriverExtract(c.Request.Context(), storage, actualPath, args)
if dealErrorPage(c, err) {
return
}
redirect(c, link)
}
} else {
rc, size, err := op.InternalExtract(c.Request.Context(), storage, actualPath, args)
if dealErrorPage(c, err) {
return
}
fileName := stdpath.Base(innerPath)
proxyInternalExtract(c, rc, size, fileName)
}
}
func dealError(c *gin.Context, err error) bool {
if err == nil {
return false
} else if errors.Is(err, errs.SharingNotFound) {
common.ErrorStrResp(c, "the share does not exist", 500)
} else if errors.Is(err, errs.InvalidSharing) {
common.ErrorStrResp(c, "the share has expired or is no longer valid", 500)
} else if errors.Is(err, errs.WrongShareCode) {
common.ErrorResp(c, err, 403)
} else if errors.Is(err, errs.WrongArchivePassword) {
common.ErrorResp(c, err, 202)
} else {
common.ErrorResp(c, err, 500)
}
return true
}
func dealErrorPage(c *gin.Context, err error) bool {
if err == nil {
return false
} else if errors.Is(err, errs.SharingNotFound) {
common.ErrorPage(c, errors.New("the share does not exist"), 500)
} else if errors.Is(err, errs.InvalidSharing) {
common.ErrorPage(c, errors.New("the share has expired or is no longer valid"), 500)
} else if errors.Is(err, errs.WrongShareCode) {
common.ErrorPage(c, err, 403)
} else if errors.Is(err, errs.WrongArchivePassword) {
common.ErrorPage(c, err, 202)
} else {
common.ErrorPage(c, err, 500)
}
return true
}
type SharingResp struct {
*model.Sharing
CreatorName string `json:"creator"`
CreatorRole int `json:"creator_role"`
}
func GetSharing(c *gin.Context) {
sid := c.Query("id")
user := c.Request.Context().Value(conf.UserKey).(*model.User)
s, err := op.GetSharingById(sid)
if err != nil || (!user.IsAdmin() && s.Creator.ID != user.ID) {
common.ErrorStrResp(c, "sharing not found", 404)
return
}
common.SuccessResp(c, SharingResp{
Sharing: s,
CreatorName: s.Creator.Username,
CreatorRole: s.Creator.Role,
})
}
func ListSharings(c *gin.Context) {
var req model.PageReq
if err := c.ShouldBind(&req); err != nil {
common.ErrorResp(c, err, 400)
return
}
req.Validate()
user := c.Request.Context().Value(conf.UserKey).(*model.User)
var sharings []model.Sharing
var total int64
var err error
if user.IsAdmin() {
sharings, total, err = op.GetSharings(req.Page, req.PerPage)
} else {
sharings, total, err = op.GetSharingsByCreatorId(user.ID, req.Page, req.PerPage)
}
if err != nil {
common.ErrorResp(c, err, 500, true)
return
}
common.SuccessResp(c, common.PageResp{
Content: utils.MustSliceConvert(sharings, func(s model.Sharing) SharingResp {
return SharingResp{
Sharing: &s,
CreatorName: s.Creator.Username,
CreatorRole: s.Creator.Role,
}
}),
Total: total,
})
}
type CreateSharingReq struct {
Files []string `json:"files"`
Expires *time.Time `json:"expires"`
Pwd string `json:"pwd"`
MaxAccessed int `json:"max_accessed"`
Disabled bool `json:"disabled"`
Remark string `json:"remark"`
Readme string `json:"readme"`
Header string `json:"header"`
model.Sort
}
type UpdateSharingReq struct {
ID string `json:"id"`
Accessed int `json:"accessed"`
CreateSharingReq
}
func UpdateSharing(c *gin.Context) {
var req UpdateSharingReq
if err := c.ShouldBind(&req); err != nil {
common.ErrorResp(c, err, 400)
return
}
if len(req.Files) == 0 || (len(req.Files) == 1 && req.Files[0] == "") {
common.ErrorStrResp(c, "must add at least 1 object", 400)
return
}
user := c.Request.Context().Value(conf.UserKey).(*model.User)
if !user.CanShare() {
common.ErrorStrResp(c, "permission denied", 403)
return
}
for i, s := range req.Files {
s = utils.FixAndCleanPath(s)
req.Files[i] = s
if !user.IsAdmin() && !strings.HasPrefix(s, user.BasePath) {
common.ErrorStrResp(c, fmt.Sprintf("permission denied to share path [%s]", s), 500)
return
}
}
s, err := op.GetSharingById(req.ID)
if err != nil || (!user.IsAdmin() && s.CreatorId != user.ID) {
common.ErrorStrResp(c, "sharing not found", 404)
return
}
s.Files = req.Files
s.Expires = req.Expires
s.Pwd = req.Pwd
s.Accessed = req.Accessed
s.MaxAccessed = req.MaxAccessed
s.Disabled = req.Disabled
s.Sort = req.Sort
s.Header = req.Header
s.Readme = req.Readme
s.Remark = req.Remark
if err = op.UpdateSharing(s); err != nil {
common.ErrorResp(c, err, 500)
} else {
common.SuccessResp(c, SharingResp{
Sharing: s,
CreatorName: s.Creator.Username,
CreatorRole: s.Creator.Role,
})
}
}
func CreateSharing(c *gin.Context) {
var req CreateSharingReq
var err error
if err = c.ShouldBind(&req); err != nil {
common.ErrorResp(c, err, 400)
return
}
if len(req.Files) == 0 || (len(req.Files) == 1 && req.Files[0] == "") {
common.ErrorStrResp(c, "must add at least 1 object", 400)
return
}
user := c.Request.Context().Value(conf.UserKey).(*model.User)
if !user.CanShare() {
common.ErrorStrResp(c, "permission denied", 403)
return
}
for i, s := range req.Files {
s = utils.FixAndCleanPath(s)
req.Files[i] = s
if !user.IsAdmin() && !strings.HasPrefix(s, user.BasePath) {
common.ErrorStrResp(c, fmt.Sprintf("permission denied to share path [%s]", s), 500)
return
}
}
s := &model.Sharing{
SharingDB: &model.SharingDB{
Expires: req.Expires,
Pwd: req.Pwd,
Accessed: 0,
MaxAccessed: req.MaxAccessed,
Disabled: req.Disabled,
Sort: req.Sort,
Remark: req.Remark,
Readme: req.Readme,
Header: req.Header,
},
Files: req.Files,
Creator: user,
}
var id string
if id, err = op.CreateSharing(s); err != nil {
common.ErrorResp(c, err, 500)
} else {
s.ID = id
common.SuccessResp(c, SharingResp{
Sharing: s,
CreatorName: s.Creator.Username,
CreatorRole: s.Creator.Role,
})
}
}
func DeleteSharing(c *gin.Context) {
sid := c.Query("id")
user := c.Request.Context().Value(conf.UserKey).(*model.User)
s, err := op.GetSharingById(sid)
if err != nil || (!user.IsAdmin() && s.CreatorId != user.ID) {
common.ErrorResp(c, err, 404)
return
}
if err = op.DeleteSharing(sid); err != nil {
common.ErrorResp(c, err, 500)
} else {
common.SuccessResp(c)
}
}
func SetEnableSharing(disable bool) func(ctx *gin.Context) {
return func(c *gin.Context) {
sid := c.Query("id")
user := c.Request.Context().Value(conf.UserKey).(*model.User)
s, err := op.GetSharingById(sid)
if err != nil || (!user.IsAdmin() && s.CreatorId != user.ID) {
common.ErrorStrResp(c, "sharing not found", 404)
return
}
s.Disabled = disable
if err = op.UpdateSharing(s, true); err != nil {
common.ErrorResp(c, err, 500)
} else {
common.SuccessResp(c)
}
}
}
var (
AccessCache = cache.NewMemCache[interface{}]()
AccessCountDelay = 30 * time.Minute
)
func countAccess(ip string, s *model.Sharing) error {
key := fmt.Sprintf("%s:%s", s.ID, ip)
_, ok := AccessCache.Get(key)
if !ok {
AccessCache.Set(key, struct{}{}, cache.WithEx[interface{}](AccessCountDelay))
s.Accessed += 1
return op.UpdateSharing(s, true)
}
return nil
}

View File

@ -14,8 +14,7 @@ import (
// Auth is a middleware that checks if the user is logged in. // Auth is a middleware that checks if the user is logged in.
// if token is empty, set user to guest // if token is empty, set user to guest
func Auth(allowDisabledGuest bool) func(c *gin.Context) { func Auth(c *gin.Context) {
return func(c *gin.Context) {
token := c.GetHeader("Authorization") token := c.GetHeader("Authorization")
if subtle.ConstantTimeCompare([]byte(token), []byte(setting.GetStr(conf.Token))) == 1 { if subtle.ConstantTimeCompare([]byte(token), []byte(setting.GetStr(conf.Token))) == 1 {
admin, err := op.GetAdmin() admin, err := op.GetAdmin()
@ -36,7 +35,7 @@ func Auth(allowDisabledGuest bool) func(c *gin.Context) {
c.Abort() c.Abort()
return return
} }
if !allowDisabledGuest && guest.Disabled { if guest.Disabled {
common.ErrorStrResp(c, "Guest user is disabled, login please", 401) common.ErrorStrResp(c, "Guest user is disabled, login please", 401)
c.Abort() c.Abort()
return return
@ -73,7 +72,6 @@ func Auth(allowDisabledGuest bool) func(c *gin.Context) {
log.Debugf("use login token: %+v", user) log.Debugf("use login token: %+v", user)
c.Next() c.Next()
} }
}
func Authn(c *gin.Context) { func Authn(c *gin.Context) {
token := c.GetHeader("Authorization") token := c.GetHeader("Authorization")

View File

@ -15,19 +15,14 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
) )
func PathParse(c *gin.Context) {
rawPath := parsePath(c.Param("path"))
common.GinWithValue(c, conf.PathKey, rawPath)
c.Next()
}
func Down(verifyFunc func(string, string) error) func(c *gin.Context) { func Down(verifyFunc func(string, string) error) func(c *gin.Context) {
return func(c *gin.Context) { return func(c *gin.Context) {
rawPath := c.Request.Context().Value(conf.PathKey).(string) rawPath := parsePath(c.Param("path"))
common.GinWithValue(c, conf.PathKey, rawPath)
meta, err := op.GetNearestMeta(rawPath) meta, err := op.GetNearestMeta(rawPath)
if err != nil { if err != nil {
if !errors.Is(errors.Cause(err), errs.MetaNotFound) { if !errors.Is(errors.Cause(err), errs.MetaNotFound) {
common.ErrorPage(c, err, 500, true) common.ErrorResp(c, err, 500, true)
return return
} }
} }
@ -37,7 +32,7 @@ func Down(verifyFunc func(string, string) error) func(c *gin.Context) {
s := c.Query("sign") s := c.Query("sign")
err = verifyFunc(rawPath, strings.TrimSuffix(s, "/")) err = verifyFunc(rawPath, strings.TrimSuffix(s, "/"))
if err != nil { if err != nil {
common.ErrorPage(c, err, 401) common.ErrorResp(c, err, 401)
c.Abort() c.Abort()
return return
} }

View File

@ -1,18 +0,0 @@
package middlewares
import (
"github.com/OpenListTeam/OpenList/v4/internal/conf"
"github.com/OpenListTeam/OpenList/v4/server/common"
"github.com/gin-gonic/gin"
)
func SharingIdParse(c *gin.Context) {
sid := c.Param("sid")
common.GinWithValue(c, conf.SharingIDKey, sid)
c.Next()
}
func EmptyPathParse(c *gin.Context) {
common.GinWithValue(c, conf.PathKey, "/")
c.Next()
}

View File

@ -44,29 +44,20 @@ func Init(e *gin.Engine) {
downloadLimiter := middlewares.DownloadRateLimiter(stream.ClientDownloadLimit) downloadLimiter := middlewares.DownloadRateLimiter(stream.ClientDownloadLimit)
signCheck := middlewares.Down(sign.Verify) signCheck := middlewares.Down(sign.Verify)
g.GET("/d/*path", middlewares.PathParse, signCheck, downloadLimiter, handles.Down) g.GET("/d/*path", signCheck, downloadLimiter, handles.Down)
g.GET("/p/*path", middlewares.PathParse, signCheck, downloadLimiter, handles.Proxy) g.GET("/p/*path", signCheck, downloadLimiter, handles.Proxy)
g.HEAD("/d/*path", middlewares.PathParse, signCheck, handles.Down) g.HEAD("/d/*path", signCheck, handles.Down)
g.HEAD("/p/*path", middlewares.PathParse, signCheck, handles.Proxy) g.HEAD("/p/*path", signCheck, handles.Proxy)
archiveSignCheck := middlewares.Down(sign.VerifyArchive) archiveSignCheck := middlewares.Down(sign.VerifyArchive)
g.GET("/ad/*path", middlewares.PathParse, archiveSignCheck, downloadLimiter, handles.ArchiveDown) g.GET("/ad/*path", archiveSignCheck, downloadLimiter, handles.ArchiveDown)
g.GET("/ap/*path", middlewares.PathParse, archiveSignCheck, downloadLimiter, handles.ArchiveProxy) g.GET("/ap/*path", archiveSignCheck, downloadLimiter, handles.ArchiveProxy)
g.GET("/ae/*path", middlewares.PathParse, archiveSignCheck, downloadLimiter, handles.ArchiveInternalExtract) g.GET("/ae/*path", archiveSignCheck, downloadLimiter, handles.ArchiveInternalExtract)
g.HEAD("/ad/*path", middlewares.PathParse, archiveSignCheck, handles.ArchiveDown) g.HEAD("/ad/*path", archiveSignCheck, handles.ArchiveDown)
g.HEAD("/ap/*path", middlewares.PathParse, archiveSignCheck, handles.ArchiveProxy) g.HEAD("/ap/*path", archiveSignCheck, handles.ArchiveProxy)
g.HEAD("/ae/*path", middlewares.PathParse, archiveSignCheck, handles.ArchiveInternalExtract) g.HEAD("/ae/*path", archiveSignCheck, handles.ArchiveInternalExtract)
g.GET("/sd/:sid", middlewares.EmptyPathParse, middlewares.SharingIdParse, downloadLimiter, handles.SharingDown)
g.GET("/sd/:sid/*path", middlewares.PathParse, middlewares.SharingIdParse, downloadLimiter, handles.SharingDown)
g.HEAD("/sd/:sid", middlewares.EmptyPathParse, middlewares.SharingIdParse, handles.SharingDown)
g.HEAD("/sd/:sid/*path", middlewares.PathParse, middlewares.SharingIdParse, handles.SharingDown)
g.GET("/sad/:sid", middlewares.EmptyPathParse, middlewares.SharingIdParse, downloadLimiter, handles.SharingArchiveExtract)
g.GET("/sad/:sid/*path", middlewares.PathParse, middlewares.SharingIdParse, downloadLimiter, handles.SharingArchiveExtract)
g.HEAD("/sad/:sid", middlewares.EmptyPathParse, middlewares.SharingIdParse, handles.SharingArchiveExtract)
g.HEAD("/sad/:sid/*path", middlewares.PathParse, middlewares.SharingIdParse, handles.SharingArchiveExtract)
api := g.Group("/api") api := g.Group("/api")
auth := api.Group("", middlewares.Auth(false)) auth := api.Group("", middlewares.Auth)
webauthn := api.Group("/authn", middlewares.Authn) webauthn := api.Group("/authn", middlewares.Authn)
api.POST("/auth/login", handles.Login) api.POST("/auth/login", handles.Login)
@ -102,9 +93,7 @@ func Init(e *gin.Engine) {
public.Any("/archive_extensions", handles.ArchiveExtensions) public.Any("/archive_extensions", handles.ArchiveExtensions)
_fs(auth.Group("/fs")) _fs(auth.Group("/fs"))
fsAndShare(api.Group("/fs", middlewares.Auth(true)))
_task(auth.Group("/task", middlewares.AuthNotGuest)) _task(auth.Group("/task", middlewares.AuthNotGuest))
_sharing(auth.Group("/share", middlewares.AuthNotGuest))
admin(auth.Group("/admin", middlewares.AuthAdmin)) admin(auth.Group("/admin", middlewares.AuthAdmin))
if flags.Debug || flags.Dev { if flags.Debug || flags.Dev {
debug(g.Group("/debug")) debug(g.Group("/debug"))
@ -180,16 +169,10 @@ func admin(g *gin.RouterGroup) {
index.GET("/progress", middlewares.SearchIndex, handles.GetProgress) index.GET("/progress", middlewares.SearchIndex, handles.GetProgress)
} }
func fsAndShare(g *gin.RouterGroup) {
g.Any("/list", handles.FsListSplit)
g.Any("/get", handles.FsGetSplit)
a := g.Group("/archive")
a.Any("/meta", handles.FsArchiveMetaSplit)
a.Any("/list", handles.FsArchiveListSplit)
}
func _fs(g *gin.RouterGroup) { func _fs(g *gin.RouterGroup) {
g.Any("/list", handles.FsList)
g.Any("/search", middlewares.SearchIndex, handles.Search) g.Any("/search", middlewares.SearchIndex, handles.Search)
g.Any("/get", handles.FsGet)
g.Any("/other", handles.FsOther) g.Any("/other", handles.FsOther)
g.Any("/dirs", handles.FsDirs) g.Any("/dirs", handles.FsDirs)
g.POST("/mkdir", handles.FsMkdir) g.POST("/mkdir", handles.FsMkdir)
@ -209,23 +192,16 @@ func _fs(g *gin.RouterGroup) {
// g.POST("/add_qbit", handles.AddQbittorrent) // g.POST("/add_qbit", handles.AddQbittorrent)
// g.POST("/add_transmission", handles.SetTransmission) // g.POST("/add_transmission", handles.SetTransmission)
g.POST("/add_offline_download", handles.AddOfflineDownload) g.POST("/add_offline_download", handles.AddOfflineDownload)
g.POST("/archive/decompress", handles.FsArchiveDecompress) a := g.Group("/archive")
a.Any("/meta", handles.FsArchiveMeta)
a.Any("/list", handles.FsArchiveList)
a.POST("/decompress", handles.FsArchiveDecompress)
} }
func _task(g *gin.RouterGroup) { func _task(g *gin.RouterGroup) {
handles.SetupTaskRoute(g) handles.SetupTaskRoute(g)
} }
func _sharing(g *gin.RouterGroup) {
g.Any("/list", handles.ListSharings)
g.GET("/get", handles.GetSharing)
g.POST("/create", handles.CreateSharing)
g.POST("/update", handles.UpdateSharing)
g.POST("/delete", handles.DeleteSharing)
g.POST("/enable", handles.SetEnableSharing(false))
g.POST("/disable", handles.SetEnableSharing(true))
}
func Cors(r *gin.Engine) { func Cors(r *gin.Engine) {
config := cors.DefaultConfig() config := cors.DefaultConfig()
// config.AllowAllOrigins = true // config.AllowAllOrigins = true