添加unzip按钮和后端相关实现,已支持权限相关逻辑

This commit is contained in:
unknown 2022-11-22 17:03:57 +08:00
parent 02db83c72e
commit 56c27c0090
16 changed files with 155 additions and 8 deletions

View File

@ -215,6 +215,7 @@ func (a *HookAuth) GetUser(d *users.User) *users.User {
Delete: isAdmin || a.Fields.GetBoolean("user.perm.delete", d.Perm.Delete), Delete: isAdmin || a.Fields.GetBoolean("user.perm.delete", d.Perm.Delete),
Share: isAdmin || a.Fields.GetBoolean("user.perm.share", d.Perm.Share), Share: isAdmin || a.Fields.GetBoolean("user.perm.share", d.Perm.Share),
Download: isAdmin || a.Fields.GetBoolean("user.perm.download", d.Perm.Download), Download: isAdmin || a.Fields.GetBoolean("user.perm.download", d.Perm.Download),
Unzip: isAdmin || a.Fields.GetBoolean("user.perm.unzip", d.Perm.Unzip),
} }
user := users.User{ user := users.User{
ID: d.ID, ID: d.ID,

View File

@ -329,6 +329,7 @@ func quickSetup(flags *pflag.FlagSet, d pythonData) {
Delete: true, Delete: true,
Share: true, Share: true,
Download: true, Download: true,
Unzip: true,
}, },
}, },
AuthMethod: "", AuthMethod: "",

View File

@ -119,21 +119,27 @@ export async function post(url, content = "", overwrite = false, onupload) {
}); });
} }
function moveCopy(items, copy = false, overwrite = false, rename = false) { function moveCopy(items, copy = false, overwrite = false, rename = false, unzip = false) {
let promises = []; let promises = [];
for (let item of items) { for (let item of items) {
const from = item.from; const from = item.from;
const to = encodeURIComponent(removePrefix(item.to)); const to = encodeURIComponent(removePrefix(item.to));
const url = `${from}?action=${ const url = `${from}?action=${
copy ? "copy" : "rename" unzip ? "unzip" : copy ? "copy" : "rename"
}&destination=${to}&override=${overwrite}&rename=${rename}`; }&destination=${to}&override=${overwrite}&rename=${rename}&unzip=${unzip}`;
console.log(url);
promises.push(resourceAction(url, "PATCH")); promises.push(resourceAction(url, "PATCH"));
} }
return Promise.all(promises); return Promise.all(promises);
} }
export function unzip(items) {
console.log("unzip fn")
return moveCopy(items, false, false,false, true);
}
export function move(items, overwrite = false, rename = false) { export function move(items, overwrite = false, rename = false) {
return moveCopy(items, false, overwrite, rename); return moveCopy(items, false, overwrite, rename);
} }

View File

@ -20,6 +20,7 @@ import ReplaceRename from "./ReplaceRename";
import Share from "./Share"; import Share from "./Share";
import Upload from "./Upload"; import Upload from "./Upload";
import ShareDelete from "./ShareDelete"; import ShareDelete from "./ShareDelete";
import Unzip from "./Unzip";
import { mapState } from "vuex"; import { mapState } from "vuex";
import buttons from "@/utils/buttons"; import buttons from "@/utils/buttons";
@ -40,6 +41,7 @@ export default {
ReplaceRename, ReplaceRename,
Upload, Upload,
ShareDelete, ShareDelete,
Unzip,
}, },
data: function () { data: function () {
return { return {
@ -100,6 +102,7 @@ export default {
"share", "share",
"upload", "upload",
"share-delete", "share-delete",
"unzip",
].indexOf(this.show) >= 0; ].indexOf(this.show) >= 0;
return (matched && this.show) || null; return (matched && this.show) || null;

View File

@ -0,0 +1,80 @@
<template>
<div class="card floating">
<div class="card-title">
<h2>{{ $t("prompts.unzip") }}</h2>
</div>
<div class="card-content">
<file-list @update:selected="(val) => (dest = val)"></file-list>
</div>
<div class="card-action">
<button
class="button button--flat button--grey"
@click="$store.commit('closeHovers')"
:aria-label="$t('buttons.cancel')"
:title="$t('buttons.cancel')"
>
{{ $t("buttons.cancel") }}
</button>
<button
class="button button--flat"
@click="unzip"
:disabled="$route.path === dest"
:aria-label="$t('buttons.unzip')"
:title="$t('buttons.unzip')"
>
{{ $t("buttons.unzip") }}
</button>
</div>
</div>
</template>
<script>
import { mapState } from "vuex";
import FileList from "./FileList";
import { files as api } from "@/api";
import buttons from "@/utils/buttons";
export default {
name: "unzip",
components: { FileList },
data: function () {
return {
current: window.location.pathname,
dest: null,
};
},
computed: mapState(["req", "selected"]),
methods: {
unzip: async function (event) {
event.preventDefault();
let items = [];
for (let item of this.selected) {
items.push({
from: this.req.items[item].url,
to: this.dest,
name: this.req.items[item].name,
});
}
let action = async () => {
buttons.loading("unzip");
await api
.unzip(items)
.then(() => {
buttons.success("unzip");
this.$router.push({ path: this.dest });
})
.catch((e) => {
buttons.done("unzip");
this.$showError(e);
});
};
action();
},
},
};
</script>

View File

@ -36,6 +36,10 @@
<input type="checkbox" :disabled="admin" v-model="perm.share" /> <input type="checkbox" :disabled="admin" v-model="perm.share" />
{{ $t("settings.perm.share") }} {{ $t("settings.perm.share") }}
</p> </p>
<p>
<input type="checkbox" :disabled="admin" v-model="perm.unzip" />
{{ $t("settings.perm.unzip") }}
</p>
</div> </div>
</template> </template>

View File

@ -1,5 +1,6 @@
{ {
"buttons": { "buttons": {
"unzip": "unzip",
"cancel": "Cancel", "cancel": "Cancel",
"close": "Close", "close": "Close",
"copy": "Copy", "copy": "Copy",
@ -120,6 +121,7 @@
}, },
"permanent": "Permanent", "permanent": "Permanent",
"prompts": { "prompts": {
"unzip": "Unzip",
"copy": "Copy", "copy": "Copy",
"copyMessage": "Choose the place to copy your files:", "copyMessage": "Choose the place to copy your files:",
"currentlyNavigating": "Currently navigating on:", "currentlyNavigating": "Currently navigating on:",
@ -214,7 +216,8 @@
"execute": "Execute commands", "execute": "Execute commands",
"modify": "Edit files", "modify": "Edit files",
"rename": "Rename or move files and directories", "rename": "Rename or move files and directories",
"share": "Share files" "share": "Share files",
"unzip": "unzip"
}, },
"permissions": "Permissions", "permissions": "Permissions",
"permissionsHelp": "You can set the user to be an administrator or choose the permissions individually. If you select \"Administrator\", all of the other options will be automatically checked. The management of users remains a privilege of an administrator.\n", "permissionsHelp": "You can set the user to be an administrator or choose the permissions individually. If you select \"Administrator\", all of the other options will be automatically checked. The management of users remains a privilege of an administrator.\n",

View File

@ -1,5 +1,6 @@
{ {
"buttons": { "buttons": {
"unzip": "解压",
"cancel": "取消", "cancel": "取消",
"close": "关闭", "close": "关闭",
"copy": "复制", "copy": "复制",
@ -120,6 +121,7 @@
}, },
"permanent": "永久", "permanent": "永久",
"prompts": { "prompts": {
"unzip":"解压文件",
"copy": "复制", "copy": "复制",
"copyMessage": "请选择欲复制至的目录:", "copyMessage": "请选择欲复制至的目录:",
"currentlyNavigating": "当前目录:", "currentlyNavigating": "当前目录:",
@ -210,7 +212,8 @@
"execute": "执行命令", "execute": "执行命令",
"modify": "编辑", "modify": "编辑",
"rename": "重命名或移动文件和文件夹", "rename": "重命名或移动文件和文件夹",
"share": "分享文件" "share": "分享文件",
"unzip": "解压文件"
}, },
"permissions": "权限", "permissions": "权限",
"permissionsHelp": "您可以将该用户设置为管理员或单独选择各项权限。如果您选择了“管理员”,则其他的选项会被自动选中,同时该用户可以管理其他用户。\n", "permissionsHelp": "您可以将该用户设置为管理员或单独选择各项权限。如果您选择了“管理员”,则其他的选项会被自动选中,同时该用户可以管理其他用户。\n",

View File

@ -1,5 +1,6 @@
{ {
"buttons": { "buttons": {
"unzip": "解压",
"cancel": "取消", "cancel": "取消",
"close": "關閉", "close": "關閉",
"copy": "複製", "copy": "複製",
@ -114,6 +115,7 @@
}, },
"permanent": "永久", "permanent": "永久",
"prompts": { "prompts": {
"unzip":"解壓檔案",
"copy": "複製", "copy": "複製",
"copyMessage": "請選擇欲複製至的目錄:", "copyMessage": "請選擇欲複製至的目錄:",
"currentlyNavigating": "目前目錄:", "currentlyNavigating": "目前目錄:",
@ -202,7 +204,8 @@
"execute": "執行命令", "execute": "執行命令",
"modify": "編輯檔案", "modify": "編輯檔案",
"rename": "重命名或移動檔案/資料夾", "rename": "重命名或移動檔案/資料夾",
"share": "分享檔案" "share": "分享檔案",
"unzip": "解壓檔案"
}, },
"permissions": "權限", "permissions": "權限",
"permissionsHelp": "您可以將該使用者設置為管理員,也可以單獨選擇各項權限。如果選擇了“管理員”,則其他的選項會被自動勾上,同時該使用者可以管理其他使用者。", "permissionsHelp": "您可以將該使用者設置為管理員,也可以單獨選擇各項權限。如果選擇了“管理員”,則其他的選項會被自動勾上,同時該使用者可以管理其他使用者。",

View File

@ -44,6 +44,13 @@
:label="$t('buttons.delete')" :label="$t('buttons.delete')"
show="delete" show="delete"
/> />
<action
v-if="headerButtons.unzip"
id="unzip-button"
icon="unarchive"
:label="$t('buttons.unzip')"
show="unzip"
/>
</template> </template>
<action <action
@ -112,6 +119,12 @@
:label="$t('buttons.delete')" :label="$t('buttons.delete')"
show="delete" show="delete"
/> />
<action
v-if="headerButtons.unzip"
icon="unarchive"
:label="$t('buttons.unzip')"
show="unzip"
/>
</div> </div>
<div v-if="loading"> <div v-if="loading">
@ -382,6 +395,7 @@ export default {
share: this.selectedCount === 1 && this.user.perm.share, share: this.selectedCount === 1 && this.user.perm.share,
move: this.selectedCount > 0 && this.user.perm.rename, move: this.selectedCount > 0 && this.user.perm.rename,
copy: this.selectedCount > 0 && this.user.perm.create, copy: this.selectedCount > 0 && this.user.perm.create,
unzip: this.selectedCount === 1 && this.isArchive(this.req.items[this.selected[0]].extension) && this.user.perm.unzip,
}; };
}, },
isMobile() { isMobile() {
@ -881,6 +895,10 @@ export default {
// Set the number of displayed items // Set the number of displayed items
this.showLimit = showQuantity > totalItems ? totalItems : showQuantity; this.showLimit = showQuantity > totalItems ? totalItems : showQuantity;
}, },
isArchive(ext) {
const zip_exts = [".zip",".bz2",".br",".tbz2",".gz",".xz",".rar",".tar"]; //
return zip_exts.indexOf(ext) > -1;
}
}, },
}; };
</script> </script>

3
go.mod
View File

@ -30,6 +30,8 @@ require (
gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v2 v2.4.0
) )
require github.com/pierrec/lz4 v2.6.1+incompatible // indirect
require ( require (
github.com/andybalholm/brotli v1.0.1 // indirect github.com/andybalholm/brotli v1.0.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
@ -46,6 +48,7 @@ require (
github.com/klauspost/compress v1.11.4 // indirect github.com/klauspost/compress v1.11.4 // indirect
github.com/klauspost/pgzip v1.2.5 // indirect github.com/klauspost/pgzip v1.2.5 // indirect
github.com/magiconair/properties v1.8.6 // indirect github.com/magiconair/properties v1.8.6 // indirect
github.com/mholt/archiver v3.1.1+incompatible
github.com/mitchellh/mapstructure v1.4.3 // indirect github.com/mitchellh/mapstructure v1.4.3 // indirect
github.com/nwaples/rardecode v1.1.0 // indirect github.com/nwaples/rardecode v1.1.0 // indirect
github.com/pelletier/go-toml v1.9.4 // indirect github.com/pelletier/go-toml v1.9.4 // indirect

4
go.sum
View File

@ -194,6 +194,8 @@ github.com/maruel/natural v1.0.0 h1:C1GqgYygkdnwD1H1psoEVsPazXyUqRooEvX/XyWFFDg=
github.com/maruel/natural v1.0.0/go.mod h1:eFVhYCcUOfZFxXoDZam8Ktya72wa79fNC3lc/leA0DQ= github.com/maruel/natural v1.0.0/go.mod h1:eFVhYCcUOfZFxXoDZam8Ktya72wa79fNC3lc/leA0DQ=
github.com/marusama/semaphore/v2 v2.5.0 h1:o/1QJD9DBYOWRnDhPwDVAXQn6mQYD0gZaS1Tpx6DJGM= github.com/marusama/semaphore/v2 v2.5.0 h1:o/1QJD9DBYOWRnDhPwDVAXQn6mQYD0gZaS1Tpx6DJGM=
github.com/marusama/semaphore/v2 v2.5.0/go.mod h1:z9nMiNUekt/LTpTUQdpp+4sJeYqUGpwMHfW0Z8V8fnQ= github.com/marusama/semaphore/v2 v2.5.0/go.mod h1:z9nMiNUekt/LTpTUQdpp+4sJeYqUGpwMHfW0Z8V8fnQ=
github.com/mholt/archiver v3.1.1+incompatible h1:1dCVxuqs0dJseYEhi5pl7MYPH9zDa1wBi7mF09cbNkU=
github.com/mholt/archiver v3.1.1+incompatible/go.mod h1:Dh2dOXnSdiLxRiPoVfIr/fI1TwETms9B8CTWfeh7ROU=
github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo= github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo=
github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4= github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
@ -206,6 +208,8 @@ github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhEC
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml/v2 v2.0.0 h1:P7Bq0SaI8nsexyay5UAyDo+ICWy5MQPgEZ5+l8JQTKo= github.com/pelletier/go-toml/v2 v2.0.0 h1:P7Bq0SaI8nsexyay5UAyDo+ICWy5MQPgEZ5+l8JQTKo=
github.com/pelletier/go-toml/v2 v2.0.0/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= github.com/pelletier/go-toml/v2 v2.0.0/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM=
github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pierrec/lz4/v4 v4.1.2 h1:qvY3YFXRQE/XB8MlLzJH7mSzBs74eA2gg52YTk6jUPM= github.com/pierrec/lz4/v4 v4.1.2 h1:qvY3YFXRQE/XB8MlLzJH7mSzBs74eA2gg52YTk6jUPM=
github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"fmt" "fmt"
"io" "io"
"log"
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
@ -17,6 +18,7 @@ import (
"github.com/filebrowser/filebrowser/v2/errors" "github.com/filebrowser/filebrowser/v2/errors"
"github.com/filebrowser/filebrowser/v2/files" "github.com/filebrowser/filebrowser/v2/files"
"github.com/filebrowser/filebrowser/v2/fileutils" "github.com/filebrowser/filebrowser/v2/fileutils"
"github.com/mholt/archiver"
) )
var resourceGetHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) { var resourceGetHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
@ -178,6 +180,7 @@ var resourcePutHandler = withUser(func(w http.ResponseWriter, r *http.Request, d
}) })
func resourcePatchHandler(fileCache FileCache) handleFunc { func resourcePatchHandler(fileCache FileCache) handleFunc {
log.SetFlags(log.Llongfile)
return withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) { return withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
src := r.URL.Path src := r.URL.Path
dst := r.URL.Query().Get("destination") dst := r.URL.Query().Get("destination")
@ -189,7 +192,7 @@ func resourcePatchHandler(fileCache FileCache) handleFunc {
if err != nil { if err != nil {
return errToStatus(err), err return errToStatus(err), err
} }
if dst == "/" || src == "/" { if dst == "/" || src == "/" || action != "unzip" {
return http.StatusForbidden, nil return http.StatusForbidden, nil
} }
@ -200,7 +203,8 @@ func resourcePatchHandler(fileCache FileCache) handleFunc {
override := r.URL.Query().Get("override") == "true" override := r.URL.Query().Get("override") == "true"
rename := r.URL.Query().Get("rename") == "true" rename := r.URL.Query().Get("rename") == "true"
if !override && !rename { unzip := r.URL.Query().Get("unzip") == "true"
if !override && !rename && !unzip {
if _, err = d.user.Fs.Stat(dst); err == nil { if _, err = d.user.Fs.Stat(dst); err == nil {
return http.StatusConflict, nil return http.StatusConflict, nil
} }
@ -327,6 +331,17 @@ func patchAction(ctx context.Context, action, src, dst string, d *data, fileCach
} }
return fileutils.MoveFile(d.user.Fs, src, dst) return fileutils.MoveFile(d.user.Fs, src, dst)
case "unzip":
if !d.user.Perm.Unzip {
return errors.ErrPermissionDenied
}
if strings.HasPrefix(src, "/") {
src = "." + src
}
if strings.HasPrefix(dst, "/") {
dst = "." + dst
}
return archiver.Unarchive(src, dst)
default: default:
return fmt.Errorf("unsupported action %s: %w", action, errors.ErrInvalidRequestParams) return fmt.Errorf("unsupported action %s: %w", action, errors.ErrInvalidRequestParams)
} }

View File

@ -134,6 +134,7 @@ func importConf(db *storm.DB, path string, sto *storage.Storage) error {
Delete: cfg.Defaults.AllowEdit, Delete: cfg.Defaults.AllowEdit,
Share: true, Share: true,
Download: true, Download: true,
Unzip: true,
}, },
}, },
} }

View File

@ -73,6 +73,7 @@ func convertUsersToNew(old []*oldUser) ([]*users.User, error) {
Delete: oldUser.AllowEdit, Delete: oldUser.AllowEdit,
Share: true, Share: true,
Download: true, Download: true,
Unzip: true,
}, },
} }

View File

@ -10,4 +10,5 @@ type Permissions struct {
Delete bool `json:"delete"` Delete bool `json:"delete"`
Share bool `json:"share"` Share bool `json:"share"`
Download bool `json:"download"` Download bool `json:"download"`
Unzip bool `json:"unzip"`
} }