feat: add default share duration setting for one-click shares

This commit is contained in:
Yeicor 2023-11-04 19:07:25 +01:00
parent a4cb813ddf
commit 846f72ebbc
14 changed files with 177 additions and 93 deletions

View File

@ -7,6 +7,7 @@ import (
"net/http"
"os"
"os/exec"
"strconv"
"strings"
"github.com/filebrowser/filebrowser/v2/errors"
@ -156,16 +157,18 @@ func (a *HookAuth) SaveUser() (*users.User, error) {
// create user with the provided credentials
d := &users.User{
Username: a.Cred.Username,
Password: pass,
Scope: a.Settings.Defaults.Scope,
Locale: a.Settings.Defaults.Locale,
ViewMode: a.Settings.Defaults.ViewMode,
SingleClick: a.Settings.Defaults.SingleClick,
Sorting: a.Settings.Defaults.Sorting,
Perm: a.Settings.Defaults.Perm,
Commands: a.Settings.Defaults.Commands,
HideDotfiles: a.Settings.Defaults.HideDotfiles,
Username: a.Cred.Username,
Password: pass,
Scope: a.Settings.Defaults.Scope,
Locale: a.Settings.Defaults.Locale,
DefaultShareDurationTime: a.Settings.Defaults.DefaultShareDurationTime,
DefaultShareDurationUnit: a.Settings.Defaults.DefaultShareDurationUnit,
ViewMode: a.Settings.Defaults.ViewMode,
SingleClick: a.Settings.Defaults.SingleClick,
Sorting: a.Settings.Defaults.Sorting,
Perm: a.Settings.Defaults.Perm,
Commands: a.Settings.Defaults.Commands,
HideDotfiles: a.Settings.Defaults.HideDotfiles,
}
u = a.GetUser(d)
@ -216,22 +219,26 @@ func (a *HookAuth) GetUser(d *users.User) *users.User {
Share: isAdmin || a.Fields.GetBoolean("user.perm.share", d.Perm.Share),
Download: isAdmin || a.Fields.GetBoolean("user.perm.download", d.Perm.Download),
}
defaultShareDurationTime, _ := strconv.Atoi(
a.Fields.GetString("user.defaultShareDurationTime", strconv.Itoa(d.DefaultShareDurationTime)))
user := users.User{
ID: d.ID,
Username: d.Username,
Password: d.Password,
Scope: a.Fields.GetString("user.scope", d.Scope),
Locale: a.Fields.GetString("user.locale", d.Locale),
ViewMode: users.ViewMode(a.Fields.GetString("user.viewMode", string(d.ViewMode))),
SingleClick: a.Fields.GetBoolean("user.singleClick", d.SingleClick),
ID: d.ID,
Username: d.Username,
Password: d.Password,
Scope: a.Fields.GetString("user.scope", d.Scope),
Locale: a.Fields.GetString("user.locale", d.Locale),
DefaultShareDurationTime: defaultShareDurationTime,
DefaultShareDurationUnit: a.Fields.GetString("user.defaultShareDurationUnit", d.DefaultShareDurationUnit),
LockPassword: true,
ViewMode: users.ViewMode(a.Fields.GetString("user.viewMode", string(d.ViewMode))),
SingleClick: a.Fields.GetBoolean("user.singleClick", d.SingleClick),
Perm: perms,
Commands: a.Fields.GetArray("user.commands", d.Commands),
Sorting: files.Sorting{
Asc: a.Fields.GetBoolean("user.sorting.asc", d.Sorting.Asc),
By: a.Fields.GetString("user.sorting.by", d.Sorting.By),
},
Commands: a.Fields.GetArray("user.commands", d.Commands),
HideDotfiles: a.Fields.GetBoolean("user.hideDotfiles", d.HideDotfiles),
Perm: perms,
LockPassword: true,
}
return &user

View File

@ -318,9 +318,11 @@ func quickSetup(flags *pflag.FlagSet, d pythonData) {
CreateUserDir: false,
UserHomeBasePath: settings.DefaultUsersHomeBasePath,
Defaults: settings.UserDefaults{
Scope: ".",
Locale: "en",
SingleClick: false,
Scope: ".",
Locale: "en",
DefaultShareDurationTime: -1,
DefaultShareDurationUnit: "hours",
SingleClick: false,
Perm: users.Permissions{
Admin: false,
Execute: true,

View File

@ -27,14 +27,16 @@ var usersCmd = &cobra.Command{
func printUsers(usrs []*users.User) {
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) //nolint:gomnd
fmt.Fprintln(w, "ID\tUsername\tScope\tLocale\tV. Mode\tS.Click\tAdmin\tExecute\tCreate\tRename\tModify\tDelete\tShare\tDownload\tPwd Lock")
fmt.Fprintln(w, "ID\tUsername\tScope\tLocale\tDefShare\tV. Mode\tS.Click\tAdmin\tExecute\tCreate\tRename\tModify\tDelete\tShare\tDownload\tPwd Lock")
for _, u := range usrs {
fmt.Fprintf(w, "%d\t%s\t%s\t%s\t%s\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t\n",
fmt.Fprintf(w, "%d\t%s\t%s\t%s\t%d%s\t%s\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t\n",
u.ID,
u.Username,
u.Scope,
u.Locale,
u.DefaultShareDurationTime,
u.DefaultShareDurationUnit,
u.ViewMode,
u.SingleClick,
u.Perm.Admin,
@ -75,6 +77,8 @@ func addUserFlags(flags *pflag.FlagSet) {
flags.StringSlice("commands", nil, "a list of the commands a user can execute")
flags.String("scope", ".", "scope for users")
flags.String("locale", "en", "locale for users")
flags.Int("defaultShareDurationTime", -1, "default share duration time for users (-1 disables it and 0 makes it permanent)")
flags.String("defaultShareDurationUnit", "hours", "default share duration unit for users")
flags.String("viewMode", string(users.ListViewMode), "view mode for users")
flags.Bool("singleClick", false, "use single clicks only")
}
@ -95,6 +99,10 @@ func getUserDefaults(flags *pflag.FlagSet, defaults *settings.UserDefaults, all
defaults.Scope = mustGetString(flags, flag.Name)
case "locale":
defaults.Locale = mustGetString(flags, flag.Name)
case "defaultShareDurationTime":
defaults.DefaultShareDurationTime = mustGetInt(flags, flag.Name)
case "defaultShareDurationUnit":
defaults.DefaultShareDurationUnit = mustGetString(flags, flag.Name)
case "viewMode":
defaults.ViewMode = getViewMode(flags)
case "singleClick":

View File

@ -41,17 +41,21 @@ options you want to change.`,
checkErr(err)
defaults := settings.UserDefaults{
Scope: user.Scope,
Locale: user.Locale,
ViewMode: user.ViewMode,
SingleClick: user.SingleClick,
Perm: user.Perm,
Sorting: user.Sorting,
Commands: user.Commands,
Scope: user.Scope,
Locale: user.Locale,
DefaultShareDurationTime: user.DefaultShareDurationTime,
DefaultShareDurationUnit: user.DefaultShareDurationUnit,
ViewMode: user.ViewMode,
SingleClick: user.SingleClick,
Perm: user.Perm,
Sorting: user.Sorting,
Commands: user.Commands,
}
getUserDefaults(flags, &defaults, false)
user.Scope = defaults.Scope
user.Locale = defaults.Locale
user.DefaultShareDurationTime = defaults.DefaultShareDurationTime
user.DefaultShareDurationUnit = defaults.DefaultShareDurationUnit
user.ViewMode = defaults.ViewMode
user.SingleClick = defaults.SingleClick
user.Perm = defaults.Perm

View File

@ -43,6 +43,12 @@ func mustGetUint(flags *pflag.FlagSet, flag string) uint {
return b
}
func mustGetInt(flags *pflag.FlagSet, flag string) int {
b, err := flags.GetInt(flag)
checkErr(err)
return b
}
func generateKey() []byte {
k, err := settings.GenerateKey()
checkErr(err)

View File

@ -24,6 +24,7 @@
</td>
<td class="small">
<button
ref="copyShare"
class="action copy-clipboard"
:data-clipboard-text="buildLink(link)"
:aria-label="$t('buttons.copyToClipboard')"
@ -34,6 +35,7 @@
</td>
<td class="small" v-if="hasDownloadLink()">
<button
ref="copyDownload"
class="action copy-clipboard"
:data-clipboard-text="buildDownloadLink(link)"
:aria-label="$t('buttons.copyDownloadLinkToClipboard')"
@ -144,7 +146,7 @@ export default {
};
},
computed: {
...mapState(["req", "selected", "selectedCount"]),
...mapState(["req", "selected", "selectedCount", "user"]),
...mapGetters(["isListing"]),
url() {
if (!this.isListing) {
@ -165,8 +167,23 @@ export default {
this.links = links;
this.sort();
if (this.links.length == 0) {
if (this.links.length === 0) {
this.listing = false;
// If enabled and this is the first share, automate the creation of the link and copy it to the clipboard.
if (this.user.defaultShareDurationTime !== -1) {
this.time = this.user.defaultShareDurationTime.toString();
this.unit = this.user.defaultShareDurationUnit;
await this.submit();
this.$nextTick(() => {
console.log(this.$refs)
if (this.$refs.hasOwnProperty("copyDownload") && this.$refs.copyDownload.length > 0) {
this.$refs.copyDownload[0].click();
} else if (this.$refs.hasOwnProperty("copyShare") && this.$refs.copyShare.length > 0) {
this.$refs.copyShare[0].click();
}
});
}
}
} catch (e) {
this.$showError(e);
@ -183,7 +200,7 @@ export default {
},
methods: {
submit: async function () {
let isPermanent = !this.time || this.time == 0;
let isPermanent = !this.time || this.time === "0";
try {
let res = null;

View File

@ -298,15 +298,15 @@ body.rtl .card .card-title>*:first-child {
opacity: 1;
}
.card#share .input-group {
.input-group {
display: flex;
}
.card#share .input-group * {
.input-group * {
border: none;
}
.card#share .input-group input {
.input-group input {
flex: 1;
}

View File

@ -199,6 +199,8 @@
"createUserHomeDirectory": "Create user home directory",
"customStylesheet": "Custom Stylesheet",
"defaultUserDescription": "These are the default settings for new users.",
"defaultShareDuration": "Default share duration",
"defaultShareDurationExplanation": "The default share duration to automatically copy direct download links to the clipboard when creating a share. Set to 0 for permanent shares and -1 to disable the feature and choose a duration manually.",
"disableExternalLinks": "Disable external links (except documentation)",
"disableUsedDiskPercentage": "Disable used disk percentage graph",
"documentation": "documentation",

View File

@ -24,6 +24,23 @@
class="input input--block"
:locale.sync="locale"
></languages>
<h3>{{ $t("settings.defaultShareDuration") }}</h3>
<p>{{ $t("settings.defaultShareDurationExplanation") }}</p>
<div class="input-group input">
<input
v-focus
type="number"
max="2147483647"
min="-1"
v-model.trim="defaultShareDurationTime"
/>
<select class="right" v-model="defaultShareDurationUnit" :aria-label="$t('time.unit')">
<option value="seconds">{{ $t("time.seconds") }}</option>
<option value="minutes">{{ $t("time.minutes") }}</option>
<option value="hours">{{ $t("time.hours") }}</option>
<option value="days">{{ $t("time.days") }}</option>
</select>
</div>
</div>
<div class="card-action">
@ -90,6 +107,8 @@ export default {
singleClick: false,
dateFormat: false,
locale: "",
defaultShareDurationTime: -1,
defaultShareDurationUnit: "hours",
};
},
computed: {
@ -114,6 +133,8 @@ export default {
this.hideDotfiles = this.user.hideDotfiles;
this.singleClick = this.user.singleClick;
this.dateFormat = this.user.dateFormat;
this.defaultShareDurationTime = this.user.defaultShareDurationTime;
this.defaultShareDurationUnit = this.user.defaultShareDurationUnit;
},
methods: {
...mapMutations(["updateUser", "setLoading"]),
@ -143,6 +164,8 @@ export default {
hideDotfiles: this.hideDotfiles,
singleClick: this.singleClick,
dateFormat: this.dateFormat,
defaultShareDurationTime: parseInt(this.defaultShareDurationTime),
defaultShareDurationUnit: this.defaultShareDurationUnit,
};
const shouldReload =
rtlLanguages.includes(data.locale) !==
@ -152,6 +175,8 @@ export default {
"hideDotfiles",
"singleClick",
"dateFormat",
"defaultShareDurationTime",
"defaultShareDurationUnit",
]);
this.updateUser(data);
if (shouldReload) {

View File

@ -20,15 +20,17 @@ const (
)
type userInfo struct {
ID uint `json:"id"`
Locale string `json:"locale"`
ViewMode users.ViewMode `json:"viewMode"`
SingleClick bool `json:"singleClick"`
Perm users.Permissions `json:"perm"`
Commands []string `json:"commands"`
LockPassword bool `json:"lockPassword"`
HideDotfiles bool `json:"hideDotfiles"`
DateFormat bool `json:"dateFormat"`
ID uint `json:"id"`
Locale string `json:"locale"`
DefaultShareDurationTime int `json:"defaultShareDurationTime"`
DefaultShareDurationUnit string `json:"defaultShareDurationUnit"`
ViewMode users.ViewMode `json:"viewMode"`
SingleClick bool `json:"singleClick"`
Perm users.Permissions `json:"perm"`
Commands []string `json:"commands"`
LockPassword bool `json:"lockPassword"`
HideDotfiles bool `json:"hideDotfiles"`
DateFormat bool `json:"dateFormat"`
}
type authToken struct {
@ -184,15 +186,17 @@ func renewHandler(tokenExpireTime time.Duration) handleFunc {
func printToken(w http.ResponseWriter, _ *http.Request, d *data, user *users.User, tokenExpirationTime time.Duration) (int, error) {
claims := &authToken{
User: userInfo{
ID: user.ID,
Locale: user.Locale,
ViewMode: user.ViewMode,
SingleClick: user.SingleClick,
Perm: user.Perm,
LockPassword: user.LockPassword,
Commands: user.Commands,
HideDotfiles: user.HideDotfiles,
DateFormat: user.DateFormat,
ID: user.ID,
Locale: user.Locale,
DefaultShareDurationTime: user.DefaultShareDurationTime,
DefaultShareDurationUnit: user.DefaultShareDurationUnit,
ViewMode: user.ViewMode,
SingleClick: user.SingleClick,
Perm: user.Perm,
LockPassword: user.LockPassword,
Commands: user.Commands,
HideDotfiles: user.HideDotfiles,
DateFormat: user.DateFormat,
},
RegisteredClaims: jwt.RegisteredClaims{
IssuedAt: jwt.NewNumericDate(time.Now()),

View File

@ -8,21 +8,25 @@ import (
// UserDefaults is a type that holds the default values
// for some fields on User.
type UserDefaults struct {
Scope string `json:"scope"`
Locale string `json:"locale"`
ViewMode users.ViewMode `json:"viewMode"`
SingleClick bool `json:"singleClick"`
Sorting files.Sorting `json:"sorting"`
Perm users.Permissions `json:"perm"`
Commands []string `json:"commands"`
HideDotfiles bool `json:"hideDotfiles"`
DateFormat bool `json:"dateFormat"`
Scope string `json:"scope"`
Locale string `json:"locale"`
DefaultShareDurationTime int `json:"defaultShareDurationTime"`
DefaultShareDurationUnit string `json:"defaultShareDurationUnit"`
ViewMode users.ViewMode `json:"viewMode"`
SingleClick bool `json:"singleClick"`
Sorting files.Sorting `json:"sorting"`
Perm users.Permissions `json:"perm"`
Commands []string `json:"commands"`
HideDotfiles bool `json:"hideDotfiles"`
DateFormat bool `json:"dateFormat"`
}
// Apply applies the default options to a user.
func (d *UserDefaults) Apply(u *users.User) {
u.Scope = d.Scope
u.Locale = d.Locale
u.DefaultShareDurationTime = d.DefaultShareDurationTime
u.DefaultShareDurationUnit = d.DefaultShareDurationUnit
u.ViewMode = d.ViewMode
u.SingleClick = d.SingleClick
u.Perm = d.Perm

View File

@ -121,10 +121,12 @@ func importConf(db *storm.DB, path string, sto *storage.Storage) error {
Key: key,
Signup: false,
Defaults: settings.UserDefaults{
Scope: cfg.Defaults.Scope,
Commands: cfg.Defaults.Commands,
ViewMode: users.ViewMode(cfg.Defaults.ViewMode),
Locale: cfg.Defaults.Locale,
Scope: cfg.Defaults.Scope,
Commands: cfg.Defaults.Commands,
ViewMode: users.ViewMode(cfg.Defaults.ViewMode),
Locale: cfg.Defaults.Locale,
DefaultShareDurationTime: -1,
DefaultShareDurationUnit: "hours",
Perm: users.Permissions{
Admin: false,
Execute: cfg.Defaults.AllowCommands,

View File

@ -3,7 +3,6 @@ package importer
import (
"encoding/json"
"fmt"
"github.com/asdine/storm/v3"
bolt "go.etcd.io/bbolt"
@ -56,14 +55,16 @@ func convertUsersToNew(old []*oldUser) ([]*users.User, error) {
for _, oldUser := range old {
user := &users.User{
Username: oldUser.Username,
Password: oldUser.Password,
Scope: oldUser.Scope,
Locale: oldUser.Locale,
LockPassword: oldUser.LockPassword,
ViewMode: users.ViewMode(oldUser.ViewMode),
Commands: oldUser.Commands,
Rules: []rules.Rule{},
Username: oldUser.Username,
Password: oldUser.Password,
Scope: oldUser.Scope,
Locale: oldUser.Locale,
DefaultShareDurationTime: -1,
DefaultShareDurationUnit: "hours",
LockPassword: oldUser.LockPassword,
ViewMode: users.ViewMode(oldUser.ViewMode),
Commands: oldUser.Commands,
Rules: []rules.Rule{},
Perm: users.Permissions{
Admin: oldUser.Admin,
Execute: oldUser.AllowCommands,

View File

@ -21,21 +21,23 @@ const (
// User describes a user.
type User struct {
ID uint `storm:"id,increment" json:"id"`
Username string `storm:"unique" json:"username"`
Password string `json:"password"`
Scope string `json:"scope"`
Locale string `json:"locale"`
LockPassword bool `json:"lockPassword"`
ViewMode ViewMode `json:"viewMode"`
SingleClick bool `json:"singleClick"`
Perm Permissions `json:"perm"`
Commands []string `json:"commands"`
Sorting files.Sorting `json:"sorting"`
Fs afero.Fs `json:"-" yaml:"-"`
Rules []rules.Rule `json:"rules"`
HideDotfiles bool `json:"hideDotfiles"`
DateFormat bool `json:"dateFormat"`
ID uint `storm:"id,increment" json:"id"`
Username string `storm:"unique" json:"username"`
Password string `json:"password"`
Scope string `json:"scope"`
Locale string `json:"locale"`
DefaultShareDurationTime int `json:"defaultShareDurationTime"`
DefaultShareDurationUnit string `json:"defaultShareDurationUnit"`
LockPassword bool `json:"lockPassword"`
ViewMode ViewMode `json:"viewMode"`
SingleClick bool `json:"singleClick"`
Perm Permissions `json:"perm"`
Commands []string `json:"commands"`
Sorting files.Sorting `json:"sorting"`
Fs afero.Fs `json:"-" yaml:"-"`
Rules []rules.Rule `json:"rules"`
HideDotfiles bool `json:"hideDotfiles"`
DateFormat bool `json:"dateFormat"`
}
// GetRules implements rules.Provider.