添加unzip按钮和后端相关实现,已支持权限相关逻辑
This commit is contained in:
parent
02db83c72e
commit
56c27c0090
@ -215,6 +215,7 @@ func (a *HookAuth) GetUser(d *users.User) *users.User {
|
||||
Delete: isAdmin || a.Fields.GetBoolean("user.perm.delete", d.Perm.Delete),
|
||||
Share: isAdmin || a.Fields.GetBoolean("user.perm.share", d.Perm.Share),
|
||||
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{
|
||||
ID: d.ID,
|
||||
|
||||
@ -329,6 +329,7 @@ func quickSetup(flags *pflag.FlagSet, d pythonData) {
|
||||
Delete: true,
|
||||
Share: true,
|
||||
Download: true,
|
||||
Unzip: true,
|
||||
},
|
||||
},
|
||||
AuthMethod: "",
|
||||
|
||||
@ -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 = [];
|
||||
|
||||
for (let item of items) {
|
||||
const from = item.from;
|
||||
const to = encodeURIComponent(removePrefix(item.to));
|
||||
const url = `${from}?action=${
|
||||
copy ? "copy" : "rename"
|
||||
}&destination=${to}&override=${overwrite}&rename=${rename}`;
|
||||
unzip ? "unzip" : copy ? "copy" : "rename"
|
||||
}&destination=${to}&override=${overwrite}&rename=${rename}&unzip=${unzip}`;
|
||||
console.log(url);
|
||||
promises.push(resourceAction(url, "PATCH"));
|
||||
}
|
||||
|
||||
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) {
|
||||
return moveCopy(items, false, overwrite, rename);
|
||||
}
|
||||
|
||||
@ -20,6 +20,7 @@ import ReplaceRename from "./ReplaceRename";
|
||||
import Share from "./Share";
|
||||
import Upload from "./Upload";
|
||||
import ShareDelete from "./ShareDelete";
|
||||
import Unzip from "./Unzip";
|
||||
import { mapState } from "vuex";
|
||||
import buttons from "@/utils/buttons";
|
||||
|
||||
@ -40,6 +41,7 @@ export default {
|
||||
ReplaceRename,
|
||||
Upload,
|
||||
ShareDelete,
|
||||
Unzip,
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
@ -100,6 +102,7 @@ export default {
|
||||
"share",
|
||||
"upload",
|
||||
"share-delete",
|
||||
"unzip",
|
||||
].indexOf(this.show) >= 0;
|
||||
|
||||
return (matched && this.show) || null;
|
||||
|
||||
80
frontend/src/components/prompts/Unzip.vue
Normal file
80
frontend/src/components/prompts/Unzip.vue
Normal 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>
|
||||
@ -36,6 +36,10 @@
|
||||
<input type="checkbox" :disabled="admin" v-model="perm.share" />
|
||||
{{ $t("settings.perm.share") }}
|
||||
</p>
|
||||
<p>
|
||||
<input type="checkbox" :disabled="admin" v-model="perm.unzip" />
|
||||
{{ $t("settings.perm.unzip") }}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
{
|
||||
"buttons": {
|
||||
"unzip": "unzip",
|
||||
"cancel": "Cancel",
|
||||
"close": "Close",
|
||||
"copy": "Copy",
|
||||
@ -120,6 +121,7 @@
|
||||
},
|
||||
"permanent": "Permanent",
|
||||
"prompts": {
|
||||
"unzip": "Unzip",
|
||||
"copy": "Copy",
|
||||
"copyMessage": "Choose the place to copy your files:",
|
||||
"currentlyNavigating": "Currently navigating on:",
|
||||
@ -214,7 +216,8 @@
|
||||
"execute": "Execute commands",
|
||||
"modify": "Edit files",
|
||||
"rename": "Rename or move files and directories",
|
||||
"share": "Share files"
|
||||
"share": "Share files",
|
||||
"unzip": "unzip"
|
||||
},
|
||||
"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",
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
{
|
||||
"buttons": {
|
||||
"unzip": "解压",
|
||||
"cancel": "取消",
|
||||
"close": "关闭",
|
||||
"copy": "复制",
|
||||
@ -120,6 +121,7 @@
|
||||
},
|
||||
"permanent": "永久",
|
||||
"prompts": {
|
||||
"unzip":"解压文件",
|
||||
"copy": "复制",
|
||||
"copyMessage": "请选择欲复制至的目录:",
|
||||
"currentlyNavigating": "当前目录:",
|
||||
@ -210,7 +212,8 @@
|
||||
"execute": "执行命令",
|
||||
"modify": "编辑",
|
||||
"rename": "重命名或移动文件和文件夹",
|
||||
"share": "分享文件"
|
||||
"share": "分享文件",
|
||||
"unzip": "解压文件"
|
||||
},
|
||||
"permissions": "权限",
|
||||
"permissionsHelp": "您可以将该用户设置为管理员或单独选择各项权限。如果您选择了“管理员”,则其他的选项会被自动选中,同时该用户可以管理其他用户。\n",
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
{
|
||||
"buttons": {
|
||||
"unzip": "解压",
|
||||
"cancel": "取消",
|
||||
"close": "關閉",
|
||||
"copy": "複製",
|
||||
@ -114,6 +115,7 @@
|
||||
},
|
||||
"permanent": "永久",
|
||||
"prompts": {
|
||||
"unzip":"解壓檔案",
|
||||
"copy": "複製",
|
||||
"copyMessage": "請選擇欲複製至的目錄:",
|
||||
"currentlyNavigating": "目前目錄:",
|
||||
@ -202,7 +204,8 @@
|
||||
"execute": "執行命令",
|
||||
"modify": "編輯檔案",
|
||||
"rename": "重命名或移動檔案/資料夾",
|
||||
"share": "分享檔案"
|
||||
"share": "分享檔案",
|
||||
"unzip": "解壓檔案"
|
||||
},
|
||||
"permissions": "權限",
|
||||
"permissionsHelp": "您可以將該使用者設置為管理員,也可以單獨選擇各項權限。如果選擇了“管理員”,則其他的選項會被自動勾上,同時該使用者可以管理其他使用者。",
|
||||
|
||||
@ -44,6 +44,13 @@
|
||||
:label="$t('buttons.delete')"
|
||||
show="delete"
|
||||
/>
|
||||
<action
|
||||
v-if="headerButtons.unzip"
|
||||
id="unzip-button"
|
||||
icon="unarchive"
|
||||
:label="$t('buttons.unzip')"
|
||||
show="unzip"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<action
|
||||
@ -112,6 +119,12 @@
|
||||
:label="$t('buttons.delete')"
|
||||
show="delete"
|
||||
/>
|
||||
<action
|
||||
v-if="headerButtons.unzip"
|
||||
icon="unarchive"
|
||||
:label="$t('buttons.unzip')"
|
||||
show="unzip"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-if="loading">
|
||||
@ -382,6 +395,7 @@ export default {
|
||||
share: this.selectedCount === 1 && this.user.perm.share,
|
||||
move: this.selectedCount > 0 && this.user.perm.rename,
|
||||
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() {
|
||||
@ -881,6 +895,10 @@ export default {
|
||||
// Set the number of displayed items
|
||||
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>
|
||||
|
||||
3
go.mod
3
go.mod
@ -30,6 +30,8 @@ require (
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
require github.com/pierrec/lz4 v2.6.1+incompatible // indirect
|
||||
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.0.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/pgzip v1.2.5 // 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/nwaples/rardecode v1.1.0 // indirect
|
||||
github.com/pelletier/go-toml v1.9.4 // indirect
|
||||
|
||||
4
go.sum
4
go.sum
@ -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/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/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/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4=
|
||||
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/v2 v2.0.0 h1:P7Bq0SaI8nsexyay5UAyDo+ICWy5MQPgEZ5+l8JQTKo=
|
||||
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/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
|
||||
@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
@ -17,6 +18,7 @@ import (
|
||||
"github.com/filebrowser/filebrowser/v2/errors"
|
||||
"github.com/filebrowser/filebrowser/v2/files"
|
||||
"github.com/filebrowser/filebrowser/v2/fileutils"
|
||||
"github.com/mholt/archiver"
|
||||
)
|
||||
|
||||
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 {
|
||||
log.SetFlags(log.Llongfile)
|
||||
return withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||
src := r.URL.Path
|
||||
dst := r.URL.Query().Get("destination")
|
||||
@ -189,7 +192,7 @@ func resourcePatchHandler(fileCache FileCache) handleFunc {
|
||||
if err != nil {
|
||||
return errToStatus(err), err
|
||||
}
|
||||
if dst == "/" || src == "/" {
|
||||
if dst == "/" || src == "/" || action != "unzip" {
|
||||
return http.StatusForbidden, nil
|
||||
}
|
||||
|
||||
@ -200,7 +203,8 @@ func resourcePatchHandler(fileCache FileCache) handleFunc {
|
||||
|
||||
override := r.URL.Query().Get("override") == "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 {
|
||||
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)
|
||||
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:
|
||||
return fmt.Errorf("unsupported action %s: %w", action, errors.ErrInvalidRequestParams)
|
||||
}
|
||||
|
||||
@ -134,6 +134,7 @@ func importConf(db *storm.DB, path string, sto *storage.Storage) error {
|
||||
Delete: cfg.Defaults.AllowEdit,
|
||||
Share: true,
|
||||
Download: true,
|
||||
Unzip: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@ -73,6 +73,7 @@ func convertUsersToNew(old []*oldUser) ([]*users.User, error) {
|
||||
Delete: oldUser.AllowEdit,
|
||||
Share: true,
|
||||
Download: true,
|
||||
Unzip: true,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@ -10,4 +10,5 @@ type Permissions struct {
|
||||
Delete bool `json:"delete"`
|
||||
Share bool `json:"share"`
|
||||
Download bool `json:"download"`
|
||||
Unzip bool `json:"unzip"`
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user