feat: add shell opt

License: MIT
Signed-off-by: Henrique Dias <hacdias@gmail.com>
This commit is contained in:
Henrique Dias 2019-01-02 19:06:35 +00:00
parent 06ccf4528c
commit 88effa0889
15 changed files with 127 additions and 149 deletions

View File

@ -38,17 +38,6 @@ func (c ConfigStore) SaveSettings(s *types.Settings) error {
return c.Save("settings", s)
}
// GetRunner is an helper method to get a runner object.
func (c ConfigStore) GetRunner() (*types.Runner, error) {
runner := &types.Runner{}
return runner, c.Get("runner", runner)
}
// SaveRunner is an helper method to set the runner object
func (c ConfigStore) SaveRunner(r *types.Runner) error {
return c.Save("runner", r)
}
// GetAuther is an helper method to get an auther object.
func (c ConfigStore) GetAuther(t types.AuthMethod) (types.Auther, error) {
if t == auth.MethodJSONAuth {

View File

@ -21,15 +21,15 @@ var cmdsAddCmd = &cobra.Command{
db := getDB()
defer db.Close()
st := getStore(db)
r, err := st.Config.GetRunner()
s, err := st.Config.GetSettings()
checkErr(err)
evt := mustGetString(cmd, "event")
command := mustGetString(cmd, "command")
r.Commands[evt] = append(r.Commands[evt], command)
err = st.Config.SaveRunner(r)
s.Commands[evt] = append(s.Commands[evt], command)
err = st.Config.SaveSettings(s)
checkErr(err)
printEvents(r.Commands)
printEvents(s.Commands)
},
}

View File

@ -18,16 +18,16 @@ var cmdsLsCmd = &cobra.Command{
db := getDB()
defer db.Close()
st := getStore(db)
r, err := st.Config.GetRunner()
s, err := st.Config.GetSettings()
checkErr(err)
evt := mustGetString(cmd, "event")
if evt == "" {
printEvents(r.Commands)
printEvents(s.Commands)
} else {
show := map[string][]string{}
show["before_"+evt] = r.Commands["before_"+evt]
show["after_"+evt] = r.Commands["after_"+evt]
show["before_"+evt] = s.Commands["before_"+evt]
show["after_"+evt] = s.Commands["after_"+evt]
printEvents(show)
}
},

View File

@ -21,16 +21,16 @@ var cmdsRmCmd = &cobra.Command{
db := getDB()
defer db.Close()
st := getStore(db)
r, err := st.Config.GetRunner()
s, err := st.Config.GetSettings()
checkErr(err)
evt := mustGetString(cmd, "event")
i, err := cmd.Flags().GetUint("index")
checkErr(err)
r.Commands[evt] = append(r.Commands[evt][:i], r.Commands[evt][i+1:]...)
err = st.Config.SaveRunner(r)
s.Commands[evt] = append(s.Commands[evt][:i], s.Commands[evt][i+1:]...)
err = st.Config.SaveSettings(s)
checkErr(err)
printEvents(r.Commands)
printEvents(s.Commands)
},
}

View File

@ -13,6 +13,8 @@ import (
"github.com/spf13/cobra"
)
func init() {
rootCmd.AddCommand(configCmd)
}

View File

@ -53,7 +53,7 @@ override the options.`,
checkErr(err)
defer db.Close()
saveConfig(db, settings, &types.Runner{}, auther)
saveConfig(db, settings, auther)
fmt.Printf(`
Congratulations! You've set up your database to use with File Browser.
@ -64,12 +64,10 @@ need to call the main command to boot up the server.
},
}
func saveConfig(db *storm.DB, s *types.Settings, r *types.Runner, a types.Auther) {
func saveConfig(db *storm.DB, s *types.Settings, a types.Auther) {
st := getStore(db)
err := st.Config.SaveSettings(s)
checkErr(err)
err = st.Config.SaveRunner(r)
checkErr(err)
err = st.Config.SaveAuther(a)
checkErr(err)
}

View File

@ -65,8 +65,6 @@ listening on loalhost on a random port. Use the flags to change it.`,
checkErr(err)
env.Auther, err = env.Store.Config.GetAuther(env.Settings.AuthMethod)
checkErr(err)
env.Runner, err = env.Store.Config.GetRunner()
checkErr(err)
startServer(cmd, env)
},
@ -133,7 +131,7 @@ func quickSetup(cmd *cobra.Command) {
checkErr(err)
defer db.Close()
saveConfig(db, settings, &types.Runner{}, &auth.JSONAuth{})
saveConfig(db, settings, &auth.JSONAuth{})
st := getStore(db)
err = st.Users.Save(user)

@ -1 +1 @@
Subproject commit eb33671e572daddf57a3bbdf47e3f09865e27837
Subproject commit 76ee2aa4d628d03e5d444a98a41b7d59bdba9b7e

View File

@ -27,7 +27,6 @@ type modifyRequest struct {
// Env contains the required info for FB to run.
type Env struct {
Auther types.Auther
Runner *types.Runner
Settings *types.Settings
Store *types.Store
mux sync.Mutex // settings mutex for Auther, Runner and Settings changes.

View File

@ -105,7 +105,7 @@ func (e *Env) resourceDeleteHandler(w http.ResponseWriter, r *http.Request) {
return
}
err := e.Runner.Run(func() error {
err := e.Settings.Run(func() error {
return user.Fs.RemoveAll(path)
}, "delete", path, "", user)
@ -156,7 +156,7 @@ func (e *Env) resourcePostPutHandler(w http.ResponseWriter, r *http.Request) {
}
}
err := e.Runner.Run(func() error {
err := e.Settings.Run(func() error {
file, err := user.Fs.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0775)
if err != nil {
return err
@ -222,7 +222,7 @@ func (e *Env) resourcePatchHandler(w http.ResponseWriter, r *http.Request) {
}
}
err = e.Runner.Run(func() error {
err = e.Settings.Run(func() error {
if action == "copy" {
return fileutils.Copy(user.Fs, src, dst)
}

View File

@ -13,6 +13,7 @@ type settingsData struct {
Defaults types.UserDefaults `json:"defaults"`
Rules []types.Rule `json:"rules"`
Branding types.Branding `json:"branding"`
Shell []string `json:"shell"`
Commands map[string][]string `json:"commands"`
}
@ -27,7 +28,8 @@ func (e *Env) settingsGetHandler(w http.ResponseWriter, r *http.Request) {
Defaults: e.Settings.Defaults,
Rules: e.Settings.Rules,
Branding: e.Settings.Branding,
Commands: e.Runner.Commands,
Shell: e.Settings.Shell,
Commands: e.Settings.Commands,
}
renderJSON(w, r, data)
@ -49,13 +51,6 @@ func (e *Env) settingsPutHandler(w http.ResponseWriter, r *http.Request) {
e.mux.Lock()
defer e.mux.Unlock()
runner := &types.Runner{Commands: req.Commands}
err = e.Store.Config.SaveRunner(runner)
if err != nil {
httpErr(w, r, http.StatusInternalServerError, err)
return
}
settings := &types.Settings{}
err = copier.Copy(settings, e.Settings)
if err != nil {
@ -67,6 +62,8 @@ func (e *Env) settingsPutHandler(w http.ResponseWriter, r *http.Request) {
settings.Defaults = req.Defaults
settings.Rules = req.Rules
settings.Branding = req.Branding
settings.Shell = req.Shell
settings.Commands = req.Commands
err = e.Store.Config.SaveSettings(settings)
if err != nil {
@ -74,6 +71,5 @@ func (e *Env) settingsPutHandler(w http.ResponseWriter, r *http.Request) {
return
}
e.Runner = runner
e.Settings = settings
}

View File

@ -38,7 +38,7 @@ func (e *Env) commandsHandler(w http.ResponseWriter, r *http.Request) {
}
defer conn.Close()
var command []string
var raw string
for {
_, msg, err := conn.ReadMessage()
@ -47,13 +47,13 @@ func (e *Env) commandsHandler(w http.ResponseWriter, r *http.Request) {
return
}
command = strings.Split(string(msg), " ")
if len(command) != 0 {
raw = strings.TrimSpace(string(msg))
if raw != "" {
break
}
}
if !user.CanExecute(command[0]) {
if !user.CanExecute(strings.Split(raw, " ")[0]) {
err := conn.WriteMessage(websocket.TextMessage, cmdNotAllowed)
if err != nil {
wsErr(conn, r, http.StatusInternalServerError, err)
@ -62,15 +62,7 @@ func (e *Env) commandsHandler(w http.ResponseWriter, r *http.Request) {
return
}
if _, err := exec.LookPath(command[0]); err != nil {
err := conn.WriteMessage(websocket.TextMessage, cmdNotImplemented)
if err != nil {
wsErr(conn, r, http.StatusInternalServerError, err)
}
return
}
command, err := e.Settings.ParseCommand(raw)
path := strings.TrimPrefix(r.URL.Path, "/api/command")
dir := afero.FullBaseFsPath(user.Fs.(*afero.BasePathFs), path)
cmd := exec.Command(command[0], command[1:]...)
@ -81,6 +73,7 @@ func (e *Env) commandsHandler(w http.ResponseWriter, r *http.Request) {
wsErr(conn, r, http.StatusInternalServerError, err)
return
}
stderr, err := cmd.StderrPipe()
if err != nil {
wsErr(conn, r, http.StatusInternalServerError, err)

View File

@ -19,81 +19,90 @@ var defaultEvents = []string{
"delete",
}
// Runner runs certain commands.
type Runner struct {
Commands map[string][]string `json:"commands"`
}
// Run runs the hooks for the before and after event.
func (r Runner) Run(fn func() error, event string, path string, dst string, u *User) error {
path = afero.FullBaseFsPath(u.Fs.(*afero.BasePathFs), path)
dst = afero.FullBaseFsPath(u.Fs.(*afero.BasePathFs), dst)
err := r.do("before_"+event, path, dst, u)
func (s *Settings) Run(fn func() error, evt, path, dst string, user *User) error {
path = afero.FullBaseFsPath(user.Fs.(*afero.BasePathFs), path)
dst = afero.FullBaseFsPath(user.Fs.(*afero.BasePathFs), dst)
if val, ok := s.Commands["before_"+evt]; ok {
for _, command := range val {
err := s.exec(command, "before_"+evt, path, dst, user)
if err != nil {
return err
}
}
}
err := fn()
if err != nil {
return err
}
err = fn()
if val, ok := s.Commands["after_"+evt]; ok {
for _, command := range val {
err := s.exec(command, "after_"+evt, path, dst, user)
if err != nil {
return err
}
return r.do("after_"+event, path, dst, u)
}
func (r Runner) do(event string, path string, destination string, user *User) error {
commands := []string{}
if val, ok := r.Commands[event]; ok {
commands = append(commands, val...)
}
for _, command := range commands {
if command == "" {
continue
}
args := strings.Split(command, " ")
nonblock := false
if len(args) > 1 && args[len(args)-1] == "&" {
nonblock = true
args = args[:len(args)-1]
}
command, args, err := caddy.SplitCommandAndArgs(strings.Join(args, " "))
if err != nil {
return err
}
cmd := exec.Command(command, args...)
cmd.Env = append(os.Environ(), fmt.Sprintf("FILE=%s", path))
cmd.Env = append(cmd.Env, fmt.Sprintf("SCOPE=%s", user.Scope))
cmd.Env = append(cmd.Env, fmt.Sprintf("TRIGGER=%s", event))
cmd.Env = append(cmd.Env, fmt.Sprintf("USERNAME=%s", user.Username))
if destination != "" {
cmd.Env = append(cmd.Env, fmt.Sprintf("DESTINATION=%s", destination))
}
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if nonblock {
log.Printf("[INFO] Nonblocking Command:\"%s %s\"", command, strings.Join(args, " "))
if err := cmd.Start(); err != nil {
return err
}
continue
}
log.Printf("[INFO] Blocking Command:\"%s %s\"", command, strings.Join(args, " "))
if err := cmd.Run(); err != nil {
return err
}
}
return nil
}
// ParseCommand parses the command taking in account
func (s *Settings) ParseCommand(raw string) ([]string, error) {
command := []string{}
if len(s.Shell) == 0 {
cmd, args, err := caddy.SplitCommandAndArgs(raw)
if err != nil {
return nil, err
}
_, err = exec.LookPath(cmd)
if err != nil {
return nil, err
}
command = append(command, cmd)
command = append(command, args...)
} else {
command = append(s.Shell, raw)
}
return command, nil
}
func (s *Settings) exec(raw, evt, path, dst string, user *User) error {
blocking := true
if strings.HasSuffix(raw, "&") {
blocking = false
raw = strings.TrimSpace(strings.TrimSuffix(raw, "&"))
}
command, err := s.ParseCommand(raw)
if err != nil {
return err
}
cmd := exec.Command(command[0], command[1:]...)
cmd.Env = append(os.Environ(), fmt.Sprintf("FILE=%s", path))
cmd.Env = append(cmd.Env, fmt.Sprintf("SCOPE=%s", user.Scope))
cmd.Env = append(cmd.Env, fmt.Sprintf("TRIGGER=%s", evt))
cmd.Env = append(cmd.Env, fmt.Sprintf("USERNAME=%s", user.Username))
cmd.Env = append(cmd.Env, fmt.Sprintf("DESTINATION=%s", dst))
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if !blocking {
log.Printf("[INFO] Nonblocking Command: \"%s\"", strings.Join(command, " "))
return cmd.Start()
}
log.Printf("[INFO] Blocking Command: \"%s\"", strings.Join(command, " "))
return cmd.Run()
}

View File

@ -11,6 +11,8 @@ type Settings struct {
Defaults UserDefaults `json:"defaults"`
AuthMethod AuthMethod `json:"authMethod"`
Branding Branding `json:"branding"`
Commands map[string][]string `json:"commands"`
Shell []string `json:"shell"` // TODO add to cli
Rules []Rule `json:"rules"` // TODO: use this add to cli
}

View File

@ -6,8 +6,6 @@ import "strings"
type ConfigStore interface {
GetSettings() (*Settings, error)
SaveSettings(*Settings) error
SaveRunner(*Runner) error
GetRunner() (*Runner, error)
GetAuther(AuthMethod) (Auther, error)
SaveAuther(Auther) error
}
@ -46,31 +44,25 @@ func (v ConfigVerify) SaveSettings(s *Settings) error {
s.Rules = []Rule{}
}
return v.Store.SaveSettings(s)
}
if s.Shell == nil {
s.Shell = []string{}
}
// GetRunner wraps a ConfigStore.GetRunner
func (v ConfigVerify) GetRunner() (*Runner, error) {
return v.Store.GetRunner()
}
// SaveRunner wraps a ConfigStore.SaveRunner
func (v ConfigVerify) SaveRunner(r *Runner) error {
if r.Commands == nil {
r.Commands = map[string][]string{}
if s.Commands == nil {
s.Commands = map[string][]string{}
}
for _, event := range defaultEvents {
if _, ok := r.Commands["before_"+event]; !ok {
r.Commands["before_"+event] = []string{}
if _, ok := s.Commands["before_"+event]; !ok {
s.Commands["before_"+event] = []string{}
}
if _, ok := r.Commands["after_"+event]; !ok {
r.Commands["after_"+event] = []string{}
if _, ok := s.Commands["after_"+event]; !ok {
s.Commands["after_"+event] = []string{}
}
}
return v.Store.SaveRunner(r)
return v.Store.SaveSettings(s)
}
// GetAuther wraps a ConfigStore.GetAuther