mirror of
https://codeberg.org/scip/anydb.git
synced 2025-12-17 12:31:02 +01:00
added edit command, fixed set -o -
This commit is contained in:
@@ -145,6 +145,14 @@ curl localhost:8787/anydb/v1/foo
|
|||||||
# list keys
|
# list keys
|
||||||
curl localhost:8787/anydb/v1/
|
curl localhost:8787/anydb/v1/
|
||||||
|
|
||||||
|
# as you might correctly suspect you can store multi-line values or
|
||||||
|
# the content of text files. but what to do if you want to change it?
|
||||||
|
# here's one way:
|
||||||
|
anydb get contract24 > file.txt && vi file.txt && anydb set contract24 -r file.txt
|
||||||
|
|
||||||
|
# annoying. better do this
|
||||||
|
anydb edit contract24
|
||||||
|
|
||||||
# sometimes you need to know some details about the current database
|
# sometimes you need to know some details about the current database
|
||||||
# add -d for more details
|
# add -d for more details
|
||||||
anydb info
|
anydb info
|
||||||
|
|||||||
1
TODO.md
1
TODO.md
@@ -1,3 +1,2 @@
|
|||||||
- repl
|
- repl
|
||||||
- mime-type => exec app + value
|
- mime-type => exec app + value
|
||||||
- [edit command](https://github.com/TLINDEN/rpnc/blob/master/command.go#L249)
|
|
||||||
|
|||||||
@@ -76,6 +76,13 @@ func (entry *DbEntry) Normalize() {
|
|||||||
entry.Size = len(entry.Bin)
|
entry.Size = len(entry.Bin)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if strings.Contains(entry.Value, "\n") {
|
||||||
|
parts := strings.Split(entry.Value, "\n")
|
||||||
|
if len(parts) > 0 {
|
||||||
|
entry.Value = parts[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if len(entry.Value) > MaxValueWidth {
|
if len(entry.Value) > MaxValueWidth {
|
||||||
entry.Value = entry.Value[0:MaxValueWidth] + "..."
|
entry.Value = entry.Value[0:MaxValueWidth] + "..."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import (
|
|||||||
"github.com/tlinden/anydb/common"
|
"github.com/tlinden/anydb/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Version string = "v0.0.5"
|
var Version string = "v0.0.6"
|
||||||
|
|
||||||
type BucketConfig struct {
|
type BucketConfig struct {
|
||||||
Encrypt bool
|
Encrypt bool
|
||||||
|
|||||||
146
cmd/crud.go
146
cmd/crud.go
@@ -17,11 +17,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"strings"
|
"strings"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
@@ -29,7 +26,6 @@ import (
|
|||||||
"github.com/tlinden/anydb/app"
|
"github.com/tlinden/anydb/app"
|
||||||
"github.com/tlinden/anydb/cfg"
|
"github.com/tlinden/anydb/cfg"
|
||||||
"github.com/tlinden/anydb/output"
|
"github.com/tlinden/anydb/output"
|
||||||
"github.com/tlinden/anydb/rest"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Set(conf *cfg.Config) *cobra.Command {
|
func Set(conf *cfg.Config) *cobra.Command {
|
||||||
@@ -185,38 +181,6 @@ func Del(conf *cfg.Config) *cobra.Command {
|
|||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func Export(conf *cfg.Config) *cobra.Command {
|
|
||||||
var (
|
|
||||||
attr app.DbAttr
|
|
||||||
)
|
|
||||||
|
|
||||||
var cmd = &cobra.Command{
|
|
||||||
Use: "export [-o <json filename>]",
|
|
||||||
Short: "Export database to json",
|
|
||||||
Long: `Export database to json`,
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
// errors at this stage do not cause the usage to be shown
|
|
||||||
cmd.SilenceUsage = true
|
|
||||||
|
|
||||||
conf.Mode = "json"
|
|
||||||
|
|
||||||
entries, err := conf.DB.List(&attr)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return output.WriteJSON(&attr, conf, entries)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.PersistentFlags().StringVarP(&attr.File, "output", "o", "", "output to file")
|
|
||||||
|
|
||||||
cmd.Aliases = append(cmd.Aliases, "dump")
|
|
||||||
cmd.Aliases = append(cmd.Aliases, "backup")
|
|
||||||
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
func List(conf *cfg.Config) *cobra.Command {
|
func List(conf *cfg.Config) *cobra.Command {
|
||||||
var (
|
var (
|
||||||
attr app.DbAttr
|
attr app.DbAttr
|
||||||
@@ -266,116 +230,6 @@ func List(conf *cfg.Config) *cobra.Command {
|
|||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func Import(conf *cfg.Config) *cobra.Command {
|
|
||||||
var (
|
|
||||||
attr app.DbAttr
|
|
||||||
)
|
|
||||||
|
|
||||||
var cmd = &cobra.Command{
|
|
||||||
Use: "import [<json file>]",
|
|
||||||
Short: "Import database dump",
|
|
||||||
Long: `Import database dump`,
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
// errors at this stage do not cause the usage to be shown
|
|
||||||
cmd.SilenceUsage = true
|
|
||||||
|
|
||||||
out, err := conf.DB.Import(&attr)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Print(out)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.PersistentFlags().StringVarP(&attr.File, "file", "r", "", "Filename or - for STDIN")
|
|
||||||
cmd.PersistentFlags().StringArrayVarP(&attr.Tags, "tags", "t", nil, "tags, multiple allowed")
|
|
||||||
|
|
||||||
cmd.Aliases = append(cmd.Aliases, "add")
|
|
||||||
cmd.Aliases = append(cmd.Aliases, "s")
|
|
||||||
cmd.Aliases = append(cmd.Aliases, "+")
|
|
||||||
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
func Help(conf *cfg.Config) *cobra.Command {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func Man(conf *cfg.Config) *cobra.Command {
|
|
||||||
var cmd = &cobra.Command{
|
|
||||||
Use: "man",
|
|
||||||
Short: "show manual page",
|
|
||||||
Long: `show manual page`,
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
// errors at this stage do not cause the usage to be shown
|
|
||||||
cmd.SilenceUsage = true
|
|
||||||
|
|
||||||
man := exec.Command("less", "-")
|
|
||||||
|
|
||||||
var b bytes.Buffer
|
|
||||||
|
|
||||||
b.WriteString(manpage)
|
|
||||||
|
|
||||||
man.Stdout = os.Stdout
|
|
||||||
man.Stdin = &b
|
|
||||||
man.Stderr = os.Stderr
|
|
||||||
|
|
||||||
err := man.Run()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to execute 'less': %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
func Serve(conf *cfg.Config) *cobra.Command {
|
|
||||||
var cmd = &cobra.Command{
|
|
||||||
Use: "serve [-l host:port]",
|
|
||||||
Short: "run REST API listener",
|
|
||||||
Long: `run REST API listener`,
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
// errors at this stage do not cause the usage to be shown
|
|
||||||
cmd.SilenceUsage = true
|
|
||||||
|
|
||||||
return rest.Runserver(conf, nil)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.PersistentFlags().StringVarP(&conf.Listen, "listen", "l", "localhost:8787", "host:port")
|
|
||||||
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
func Info(conf *cfg.Config) *cobra.Command {
|
|
||||||
var cmd = &cobra.Command{
|
|
||||||
Use: "info",
|
|
||||||
Short: "info",
|
|
||||||
Long: `show info about database`,
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
// errors at this stage do not cause the usage to be shown
|
|
||||||
cmd.SilenceUsage = true
|
|
||||||
|
|
||||||
info, err := conf.DB.Info()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return output.Info(os.Stdout, conf, info)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.PersistentFlags().BoolVarP(&conf.NoHumanize, "no-human", "N", false, "do not translate to human readable values")
|
|
||||||
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
func getPassword() ([]byte, error) {
|
func getPassword() ([]byte, error) {
|
||||||
var pass []byte
|
var pass []byte
|
||||||
|
|
||||||
|
|||||||
326
cmd/extra.go
Normal file
326
cmd/extra.go
Normal file
@@ -0,0 +1,326 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2024 Thomas von Dein
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/tlinden/anydb/app"
|
||||||
|
"github.com/tlinden/anydb/cfg"
|
||||||
|
"github.com/tlinden/anydb/output"
|
||||||
|
"github.com/tlinden/anydb/rest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Export(conf *cfg.Config) *cobra.Command {
|
||||||
|
var (
|
||||||
|
attr app.DbAttr
|
||||||
|
)
|
||||||
|
|
||||||
|
var cmd = &cobra.Command{
|
||||||
|
Use: "export [-o <json filename>]",
|
||||||
|
Short: "Export database to json",
|
||||||
|
Long: `Export database to json`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
// errors at this stage do not cause the usage to be shown
|
||||||
|
cmd.SilenceUsage = true
|
||||||
|
|
||||||
|
conf.Mode = "json"
|
||||||
|
|
||||||
|
entries, err := conf.DB.List(&attr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return output.WriteJSON(&attr, conf, entries)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.PersistentFlags().StringVarP(&attr.File, "output", "o", "", "output to file")
|
||||||
|
|
||||||
|
cmd.Aliases = append(cmd.Aliases, "dump")
|
||||||
|
cmd.Aliases = append(cmd.Aliases, "backup")
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func Import(conf *cfg.Config) *cobra.Command {
|
||||||
|
var (
|
||||||
|
attr app.DbAttr
|
||||||
|
)
|
||||||
|
|
||||||
|
var cmd = &cobra.Command{
|
||||||
|
Use: "import [<json file>]",
|
||||||
|
Short: "Import database dump",
|
||||||
|
Long: `Import database dump`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
// errors at this stage do not cause the usage to be shown
|
||||||
|
cmd.SilenceUsage = true
|
||||||
|
|
||||||
|
out, err := conf.DB.Import(&attr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Print(out)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.PersistentFlags().StringVarP(&attr.File, "file", "r", "", "Filename or - for STDIN")
|
||||||
|
cmd.PersistentFlags().StringArrayVarP(&attr.Tags, "tags", "t", nil, "tags, multiple allowed")
|
||||||
|
|
||||||
|
cmd.Aliases = append(cmd.Aliases, "restore")
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func Help(conf *cfg.Config) *cobra.Command {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func Man(conf *cfg.Config) *cobra.Command {
|
||||||
|
var cmd = &cobra.Command{
|
||||||
|
Use: "man",
|
||||||
|
Short: "show manual page",
|
||||||
|
Long: `show manual page`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
// errors at this stage do not cause the usage to be shown
|
||||||
|
cmd.SilenceUsage = true
|
||||||
|
|
||||||
|
man := exec.Command("less", "-")
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
|
||||||
|
b.WriteString(manpage)
|
||||||
|
|
||||||
|
man.Stdout = os.Stdout
|
||||||
|
man.Stdin = &b
|
||||||
|
man.Stderr = os.Stderr
|
||||||
|
|
||||||
|
err := man.Run()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to execute 'less': %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func Serve(conf *cfg.Config) *cobra.Command {
|
||||||
|
var cmd = &cobra.Command{
|
||||||
|
Use: "serve [-l host:port]",
|
||||||
|
Short: "run REST API listener",
|
||||||
|
Long: `run REST API listener`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
// errors at this stage do not cause the usage to be shown
|
||||||
|
cmd.SilenceUsage = true
|
||||||
|
|
||||||
|
return rest.Runserver(conf, nil)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.PersistentFlags().StringVarP(&conf.Listen, "listen", "l", "localhost:8787", "host:port")
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func Info(conf *cfg.Config) *cobra.Command {
|
||||||
|
var cmd = &cobra.Command{
|
||||||
|
Use: "info",
|
||||||
|
Short: "info",
|
||||||
|
Long: `show info about database`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
// errors at this stage do not cause the usage to be shown
|
||||||
|
cmd.SilenceUsage = true
|
||||||
|
|
||||||
|
info, err := conf.DB.Info()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return output.Info(os.Stdout, conf, info)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.PersistentFlags().BoolVarP(&conf.NoHumanize, "no-human", "N", false, "do not translate to human readable values")
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func Edit(conf *cfg.Config) *cobra.Command {
|
||||||
|
var (
|
||||||
|
attr app.DbAttr
|
||||||
|
)
|
||||||
|
|
||||||
|
var cmd = &cobra.Command{
|
||||||
|
Use: "edit <key>",
|
||||||
|
Short: "Edit a key",
|
||||||
|
Long: `Edit a key`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if len(args) == 0 {
|
||||||
|
return errors.New("no key specified")
|
||||||
|
}
|
||||||
|
|
||||||
|
// errors at this stage do not cause the usage to be shown
|
||||||
|
cmd.SilenceUsage = true
|
||||||
|
password := []byte{}
|
||||||
|
|
||||||
|
if len(args) > 0 {
|
||||||
|
attr.Key = args[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetch entry
|
||||||
|
entry, err := conf.DB.Get(&attr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(entry.Value) == 0 && len(entry.Bin) > 0 {
|
||||||
|
return errors.New("key contains binary uneditable content")
|
||||||
|
}
|
||||||
|
|
||||||
|
// decrypt if needed
|
||||||
|
if entry.Encrypted {
|
||||||
|
pass, err := getPassword()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
password = pass
|
||||||
|
|
||||||
|
clear, err := app.Decrypt(pass, entry.Value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if utf8.ValidString(string(clear)) {
|
||||||
|
entry.Value = string(clear)
|
||||||
|
} else {
|
||||||
|
entry.Bin = clear
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.Encrypted = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine editor, vi is default
|
||||||
|
editor := getEditor()
|
||||||
|
|
||||||
|
// save file to a temp file, call the editor with it, read
|
||||||
|
// it back in and compare the content with the original
|
||||||
|
// one
|
||||||
|
newcontent, err := editContent(editor, entry.Value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// all is valid, fill our DB feeder
|
||||||
|
newattr := app.DbAttr{
|
||||||
|
Key: attr.Key,
|
||||||
|
Tags: attr.Tags,
|
||||||
|
Encrypted: attr.Encrypted,
|
||||||
|
Val: newcontent,
|
||||||
|
}
|
||||||
|
|
||||||
|
// encrypt if needed
|
||||||
|
if conf.Encrypt {
|
||||||
|
err = app.Encrypt(password, &attr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// done
|
||||||
|
return conf.DB.Set(&newattr)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Aliases = append(cmd.Aliases, "modify")
|
||||||
|
cmd.Aliases = append(cmd.Aliases, "mod")
|
||||||
|
cmd.Aliases = append(cmd.Aliases, "ed")
|
||||||
|
cmd.Aliases = append(cmd.Aliases, "vi")
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func getEditor() string {
|
||||||
|
editor := "vi"
|
||||||
|
|
||||||
|
enveditor, present := os.LookupEnv("EDITOR")
|
||||||
|
if present {
|
||||||
|
if editor != "" {
|
||||||
|
editor = enveditor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return editor
|
||||||
|
}
|
||||||
|
|
||||||
|
// taken from github.com/tlinden/rpn/ (my own program)
|
||||||
|
func editContent(editor string, content string) (string, error) {
|
||||||
|
// create a temp file
|
||||||
|
tmp, err := os.CreateTemp("", "stack")
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to create templ file: %w", err)
|
||||||
|
}
|
||||||
|
defer os.Remove(tmp.Name())
|
||||||
|
|
||||||
|
// put the content into a tmp file
|
||||||
|
_, err = tmp.WriteString(content)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to write value to temp file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// execute editor with our tmp file containing current stack
|
||||||
|
cmd := exec.Command(editor, tmp.Name())
|
||||||
|
|
||||||
|
cmd.Stdin = os.Stdin
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
|
||||||
|
err = cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to run editor command %s: %w", editor, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// read the file back in
|
||||||
|
modified, err := os.Open(tmp.Name())
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to open temp file: %w", err)
|
||||||
|
}
|
||||||
|
defer modified.Close()
|
||||||
|
|
||||||
|
newcontent, err := io.ReadAll(modified)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to read from temp file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
newcontentstr := string(newcontent)
|
||||||
|
if content == newcontentstr {
|
||||||
|
return "", fmt.Errorf("content not modified, aborting")
|
||||||
|
}
|
||||||
|
|
||||||
|
return newcontentstr, nil
|
||||||
|
}
|
||||||
@@ -119,15 +119,23 @@ func Execute() {
|
|||||||
app.BucketData, "use other bucket (default: "+app.BucketData+")")
|
app.BucketData, "use other bucket (default: "+app.BucketData+")")
|
||||||
rootCmd.PersistentFlags().StringVarP(&configfile, "config", "c", "", "toml config file")
|
rootCmd.PersistentFlags().StringVarP(&configfile, "config", "c", "", "toml config file")
|
||||||
|
|
||||||
|
// CRUD
|
||||||
rootCmd.AddCommand(Set(&conf))
|
rootCmd.AddCommand(Set(&conf))
|
||||||
rootCmd.AddCommand(List(&conf))
|
rootCmd.AddCommand(List(&conf))
|
||||||
rootCmd.AddCommand(Get(&conf))
|
rootCmd.AddCommand(Get(&conf))
|
||||||
rootCmd.AddCommand(Del(&conf))
|
rootCmd.AddCommand(Del(&conf))
|
||||||
|
|
||||||
|
// backup
|
||||||
rootCmd.AddCommand(Export(&conf))
|
rootCmd.AddCommand(Export(&conf))
|
||||||
rootCmd.AddCommand(Import(&conf))
|
rootCmd.AddCommand(Import(&conf))
|
||||||
|
|
||||||
|
// REST API
|
||||||
rootCmd.AddCommand(Serve(&conf))
|
rootCmd.AddCommand(Serve(&conf))
|
||||||
|
|
||||||
|
// auxiliary
|
||||||
rootCmd.AddCommand(Man(&conf))
|
rootCmd.AddCommand(Man(&conf))
|
||||||
rootCmd.AddCommand(Info(&conf))
|
rootCmd.AddCommand(Info(&conf))
|
||||||
|
rootCmd.AddCommand(Edit(&conf))
|
||||||
|
|
||||||
err = rootCmd.Execute()
|
err = rootCmd.Execute()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -71,15 +71,25 @@ func Print(writer io.Writer, conf *cfg.Config, attr *app.DbAttr, entry *app.DbEn
|
|||||||
}
|
}
|
||||||
|
|
||||||
func WriteFile(writer io.Writer, conf *cfg.Config, attr *app.DbAttr, entry *app.DbEntry) error {
|
func WriteFile(writer io.Writer, conf *cfg.Config, attr *app.DbAttr, entry *app.DbEntry) error {
|
||||||
fd, err := os.OpenFile(attr.File, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
|
var fileHandle *os.File
|
||||||
if err != nil {
|
var err error
|
||||||
return fmt.Errorf("failed to open file %s for writing: %w", attr.File, err)
|
|
||||||
|
if attr.File == "-" {
|
||||||
|
fileHandle = os.Stdout
|
||||||
|
} else {
|
||||||
|
|
||||||
|
fd, err := os.OpenFile(attr.File, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to open file %s for writing: %w", attr.File, err)
|
||||||
|
}
|
||||||
|
defer fd.Close()
|
||||||
|
|
||||||
|
fileHandle = fd
|
||||||
}
|
}
|
||||||
defer fd.Close()
|
|
||||||
|
|
||||||
if len(entry.Bin) > 0 {
|
if len(entry.Bin) > 0 {
|
||||||
// binary file content
|
// binary file content
|
||||||
_, err = fd.Write(entry.Bin)
|
_, err = fileHandle.Write(entry.Bin)
|
||||||
} else {
|
} else {
|
||||||
val := entry.Value
|
val := entry.Value
|
||||||
if !strings.HasSuffix(val, "\n") {
|
if !strings.HasSuffix(val, "\n") {
|
||||||
@@ -87,7 +97,7 @@ func WriteFile(writer io.Writer, conf *cfg.Config, attr *app.DbAttr, entry *app.
|
|||||||
val += "\n"
|
val += "\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = fd.Write([]byte(val))
|
_, err = fileHandle.Write([]byte(val))
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user