mirror of
https://codeberg.org/scip/anydb.git
synced 2025-12-17 20:41:00 +01:00
restructured data storage, values now have their own sub bucket
This commit is contained in:
2
TODO.md
2
TODO.md
@@ -33,3 +33,5 @@ behind.
|
|||||||
However, maybe change the list command to just list everything and add
|
However, maybe change the list command to just list everything and add
|
||||||
an extra find command for fulltext or tag search. Maybe still provide
|
an extra find command for fulltext or tag search. Maybe still provide
|
||||||
filter options in list command but only filter for keys.
|
filter options in list command but only filter for keys.
|
||||||
|
|
||||||
|
DONE: most of the above, except the tag stuff. manpage needs update and tests.
|
||||||
|
|||||||
43
app/attr.go
43
app/attr.go
@@ -20,17 +20,19 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DbAttr struct {
|
type DbAttr struct {
|
||||||
Key string
|
Key string
|
||||||
Val string
|
Preview string
|
||||||
Bin []byte
|
Val []byte
|
||||||
Args []string
|
Args []string
|
||||||
Tags []string
|
Tags []string
|
||||||
File string
|
File string
|
||||||
Encrypted bool
|
Encrypted bool
|
||||||
|
Binary bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (attr *DbAttr) ParseKV() error {
|
func (attr *DbAttr) ParseKV() error {
|
||||||
@@ -43,7 +45,7 @@ func (attr *DbAttr) ParseKV() error {
|
|||||||
}
|
}
|
||||||
case 2:
|
case 2:
|
||||||
attr.Key = attr.Args[0]
|
attr.Key = attr.Args[0]
|
||||||
attr.Val = attr.Args[1]
|
attr.Val = []byte(attr.Args[1])
|
||||||
|
|
||||||
if attr.Args[1] == "-" {
|
if attr.Args[1] == "-" {
|
||||||
attr.File = "-"
|
attr.File = "-"
|
||||||
@@ -51,7 +53,29 @@ func (attr *DbAttr) ParseKV() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if attr.File != "" {
|
if attr.File != "" {
|
||||||
return attr.GetFileValue()
|
if err := attr.GetFileValue(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if attr.Binary {
|
||||||
|
attr.Preview = "<encrypted-content>"
|
||||||
|
} else {
|
||||||
|
if len(attr.Val) > MaxValueWidth {
|
||||||
|
attr.Preview = string(attr.Val)[0:MaxValueWidth] + "..."
|
||||||
|
|
||||||
|
if strings.Contains(attr.Preview, "\n") {
|
||||||
|
parts := strings.Split(attr.Preview, "\n")
|
||||||
|
if len(parts) > 0 {
|
||||||
|
attr.Preview = parts[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
attr.Preview = string(attr.Val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if attr.Encrypted {
|
||||||
|
attr.Preview = "<encrypted-content>"
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -82,11 +106,12 @@ func (attr *DbAttr) GetFileValue() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// poor man's text file test
|
// poor man's text file test
|
||||||
sdata := string(data)
|
attr.Val = data
|
||||||
if utf8.ValidString(sdata) {
|
|
||||||
attr.Val = sdata
|
if utf8.ValidString(string(data)) {
|
||||||
|
attr.Binary = false
|
||||||
} else {
|
} else {
|
||||||
attr.Bin = data
|
attr.Binary = true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// read from console stdin
|
// read from console stdin
|
||||||
@@ -101,7 +126,7 @@ func (attr *DbAttr) GetFileValue() error {
|
|||||||
data += input + "\n"
|
data += input + "\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
attr.Val = data
|
attr.Val = []byte(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ package app
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/base64"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
@@ -104,7 +103,7 @@ func GetRandom(size int, capacity int) ([]byte, error) {
|
|||||||
// modifying it.
|
// modifying it.
|
||||||
//
|
//
|
||||||
// The cipher text consists of:
|
// The cipher text consists of:
|
||||||
// base64(password-salt) + base64(12 byte nonce + ciphertext + 16 byte mac)
|
// password-salt) + (12 byte nonce + ciphertext + 16 byte mac)
|
||||||
func Encrypt(pass []byte, attr *DbAttr) error {
|
func Encrypt(pass []byte, attr *DbAttr) error {
|
||||||
key, err := DeriveKey(pass, nil)
|
key, err := DeriveKey(pass, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -116,25 +115,17 @@ func Encrypt(pass []byte, attr *DbAttr) error {
|
|||||||
return fmt.Errorf("failed to create AEAD cipher: %w", err)
|
return fmt.Errorf("failed to create AEAD cipher: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var plain []byte
|
total := aead.NonceSize() + len(attr.Val) + aead.Overhead()
|
||||||
if attr.Val != "" {
|
|
||||||
plain = []byte(attr.Val)
|
|
||||||
} else {
|
|
||||||
plain = attr.Bin
|
|
||||||
}
|
|
||||||
|
|
||||||
total := aead.NonceSize() + len(plain) + aead.Overhead()
|
|
||||||
|
|
||||||
nonce, err := GetRandom(aead.NonceSize(), total)
|
nonce, err := GetRandom(aead.NonceSize(), total)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cipher := aead.Seal(nonce, nonce, plain, nil)
|
cipher := aead.Seal(nonce, nonce, attr.Val, nil)
|
||||||
|
|
||||||
attr.Bin = nil
|
attr.Val = append(attr.Val, key.Salt...)
|
||||||
attr.Val = base64.RawStdEncoding.EncodeToString(key.Salt) +
|
attr.Val = append(attr.Val, cipher...)
|
||||||
base64.RawStdEncoding.EncodeToString(cipher)
|
|
||||||
|
|
||||||
attr.Encrypted = true
|
attr.Encrypted = true
|
||||||
|
|
||||||
@@ -142,21 +133,17 @@ func Encrypt(pass []byte, attr *DbAttr) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Do the reverse
|
// Do the reverse
|
||||||
func Decrypt(pass []byte, cipherb64 string) ([]byte, error) {
|
func Decrypt(pass []byte, cipherb []byte) ([]byte, error) {
|
||||||
salt, err := base64.RawStdEncoding.Strict().DecodeString(cipherb64[0:B64SaltLen])
|
if len(cipherb) < B64SaltLen {
|
||||||
if err != nil {
|
return nil, fmt.Errorf("encrypted cipher block too small")
|
||||||
return nil, fmt.Errorf("failed to encode to base64: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
key, err := DeriveKey(pass, salt)
|
key, err := DeriveKey(pass, cipherb[0:B64SaltLen])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
cipher, err := base64.RawStdEncoding.Strict().DecodeString(cipherb64[B64SaltLen:])
|
cipher := cipherb[B64SaltLen:]
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to encode to base64: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
aead, err := chacha20poly1305.New(key.Key)
|
aead, err := chacha20poly1305.New(key.Key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
204
app/db.go
204
app/db.go
@@ -53,32 +53,6 @@ type DbInfo struct {
|
|||||||
Path string
|
Path string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Post process an entry for list output.
|
|
||||||
// Do NOT call it during write processing!
|
|
||||||
func (entry *DbEntry) Normalize() {
|
|
||||||
entry.Size = uint64(len(entry.Value))
|
|
||||||
|
|
||||||
if entry.Encrypted {
|
|
||||||
entry.Value = "<encrypted-content>"
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(entry.Bin) > 0 {
|
|
||||||
entry.Value = "<binary-content>"
|
|
||||||
entry.Size = uint64(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 {
|
|
||||||
entry.Value = entry.Value[0:MaxValueWidth] + "..."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type DbEntries []DbEntry
|
type DbEntries []DbEntry
|
||||||
|
|
||||||
type DbTag struct {
|
type DbTag struct {
|
||||||
@@ -126,7 +100,12 @@ func (db *DB) List(attr *DbAttr) (DbEntries, error) {
|
|||||||
|
|
||||||
err := db.DB.View(func(tx *bolt.Tx) error {
|
err := db.DB.View(func(tx *bolt.Tx) error {
|
||||||
|
|
||||||
bucket := tx.Bucket([]byte(db.Bucket))
|
root := tx.Bucket([]byte(db.Bucket))
|
||||||
|
if root == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
bucket := root.Bucket([]byte("meta"))
|
||||||
if bucket == nil {
|
if bucket == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -141,8 +120,7 @@ func (db *DB) List(attr *DbAttr) (DbEntries, error) {
|
|||||||
|
|
||||||
switch {
|
switch {
|
||||||
case filter != nil:
|
case filter != nil:
|
||||||
if filter.MatchString(entry.Value) ||
|
if filter.MatchString(entry.Key) ||
|
||||||
filter.MatchString(entry.Key) ||
|
|
||||||
filter.MatchString(strings.Join(entry.Tags, " ")) {
|
filter.MatchString(strings.Join(entry.Tags, " ")) {
|
||||||
include = true
|
include = true
|
||||||
}
|
}
|
||||||
@@ -183,18 +161,24 @@ func (db *DB) Set(attr *DbAttr) error {
|
|||||||
|
|
||||||
entry := DbEntry{
|
entry := DbEntry{
|
||||||
Key: attr.Key,
|
Key: attr.Key,
|
||||||
Value: attr.Val,
|
Binary: attr.Binary,
|
||||||
Bin: attr.Bin,
|
|
||||||
Tags: attr.Tags,
|
Tags: attr.Tags,
|
||||||
Encrypted: attr.Encrypted,
|
Encrypted: attr.Encrypted,
|
||||||
Created: timestamppb.Now(),
|
Created: timestamppb.Now(),
|
||||||
|
Size: uint64(len(attr.Val)),
|
||||||
|
Preview: attr.Preview,
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if the entry already exists and if yes, check if it has
|
// check if the entry already exists and if yes, check if it has
|
||||||
// any tags. if so, we initialize our update struct with these
|
// any tags. if so, we initialize our update struct with these
|
||||||
// tags unless it has new tags configured.
|
// tags unless it has new tags configured.
|
||||||
err := db.DB.View(func(tx *bolt.Tx) error {
|
err := db.DB.View(func(tx *bolt.Tx) error {
|
||||||
bucket := tx.Bucket([]byte(db.Bucket))
|
root := tx.Bucket([]byte(db.Bucket))
|
||||||
|
if root == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
bucket := root.Bucket([]byte("meta"))
|
||||||
if bucket == nil {
|
if bucket == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -221,23 +205,43 @@ func (db *DB) Set(attr *DbAttr) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// marshall our data
|
||||||
|
pbentry, err := proto.Marshal(&entry)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to marshall protobuf: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
err = db.DB.Update(func(tx *bolt.Tx) error {
|
err = db.DB.Update(func(tx *bolt.Tx) error {
|
||||||
// insert data
|
// create root bucket
|
||||||
bucket, err := tx.CreateBucketIfNotExists([]byte(db.Bucket))
|
root, err := tx.CreateBucketIfNotExists([]byte(db.Bucket))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create DB bucket: %w", err)
|
return fmt.Errorf("failed to create DB bucket: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
pbentry, err := proto.Marshal(&entry)
|
// create meta bucket
|
||||||
|
bucket, err := root.CreateBucketIfNotExists([]byte("meta"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to marshall protobuf: %w", err)
|
return fmt.Errorf("failed to create DB meta sub bucket: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// write meta data
|
||||||
err = bucket.Put([]byte(entry.Key), []byte(pbentry))
|
err = bucket.Put([]byte(entry.Key), []byte(pbentry))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to insert data: %w", err)
|
return fmt.Errorf("failed to insert data: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create data bucket
|
||||||
|
databucket, err := root.CreateBucketIfNotExists([]byte("data"))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create DB data sub bucket: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// write value
|
||||||
|
err = databucket.Put([]byte(entry.Key), attr.Val)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to insert data: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -257,21 +261,35 @@ func (db *DB) Get(attr *DbAttr) (*DbEntry, error) {
|
|||||||
entry := DbEntry{}
|
entry := DbEntry{}
|
||||||
|
|
||||||
err := db.DB.View(func(tx *bolt.Tx) error {
|
err := db.DB.View(func(tx *bolt.Tx) error {
|
||||||
bucket := tx.Bucket([]byte(db.Bucket))
|
root := tx.Bucket([]byte(db.Bucket))
|
||||||
|
if root == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
bucket := root.Bucket([]byte("meta"))
|
||||||
if bucket == nil {
|
if bucket == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
pbentry := bucket.Get([]byte(attr.Key))
|
pbentry := bucket.Get([]byte(attr.Key))
|
||||||
if pbentry == nil {
|
if pbentry == nil {
|
||||||
// FIXME: shall we return a key not found error?
|
return fmt.Errorf("no such key: %s", attr.Key)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := proto.Unmarshal(pbentry, &entry); err != nil {
|
if err := proto.Unmarshal(pbentry, &entry); err != nil {
|
||||||
return fmt.Errorf("failed to unmarshal from protobuf: %w", err)
|
return fmt.Errorf("failed to unmarshal from protobuf: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
databucket := root.Bucket([]byte("data"))
|
||||||
|
if databucket == nil {
|
||||||
|
return fmt.Errorf("failed to retrieve data sub bucket")
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.Value = databucket.Get([]byte(attr.Key))
|
||||||
|
if len(entry.Value) == 0 {
|
||||||
|
return fmt.Errorf("no such key: %s", attr.Key)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -308,7 +326,7 @@ func (db *DB) Import(attr *DbAttr) (string, error) {
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if attr.Val == "" {
|
if len(attr.Val) == 0 {
|
||||||
return "", errors.New("empty json file")
|
return "", errors.New("empty json file")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -336,10 +354,16 @@ func (db *DB) Import(attr *DbAttr) (string, error) {
|
|||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
err := db.DB.Update(func(tx *bolt.Tx) error {
|
err := db.DB.Update(func(tx *bolt.Tx) error {
|
||||||
// insert data
|
// create root bucket
|
||||||
bucket, err := tx.CreateBucketIfNotExists([]byte(db.Bucket))
|
root, err := tx.CreateBucketIfNotExists([]byte(db.Bucket))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create bucket: %w", err)
|
return fmt.Errorf("failed to create DB bucket: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// create meta bucket
|
||||||
|
bucket, err := root.CreateBucketIfNotExists([]byte("meta"))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create DB meta sub bucket: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
@@ -348,10 +372,23 @@ func (db *DB) Import(attr *DbAttr) (string, error) {
|
|||||||
return fmt.Errorf("failed to marshall protobuf: %w", err)
|
return fmt.Errorf("failed to marshall protobuf: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// write meta data
|
||||||
err = bucket.Put([]byte(entry.Key), []byte(pbentry))
|
err = bucket.Put([]byte(entry.Key), []byte(pbentry))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to insert data into DB: %w", err)
|
return fmt.Errorf("failed to insert data into DB: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create data bucket
|
||||||
|
databucket, err := root.CreateBucketIfNotExists([]byte("data"))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create DB data sub bucket: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// write value
|
||||||
|
err = databucket.Put([]byte(entry.Key), entry.Value)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to insert data: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -408,3 +445,84 @@ func (db *DB) Info() (*DbInfo, error) {
|
|||||||
|
|
||||||
return info, err
|
return info, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *DB) Find(attr *DbAttr) (DbEntries, error) {
|
||||||
|
if err := db.Open(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
var entries DbEntries
|
||||||
|
var filter *regexp.Regexp
|
||||||
|
|
||||||
|
if len(attr.Args) > 0 {
|
||||||
|
filter = regexp.MustCompile(attr.Args[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
err := db.DB.View(func(tx *bolt.Tx) error {
|
||||||
|
|
||||||
|
root := tx.Bucket([]byte(db.Bucket))
|
||||||
|
if root == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
bucket := root.Bucket([]byte("meta"))
|
||||||
|
if bucket == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
databucket := root.Bucket([]byte("data"))
|
||||||
|
if databucket == nil {
|
||||||
|
return fmt.Errorf("failed to retrieve data sub bucket")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := bucket.ForEach(func(key, pbentry []byte) error {
|
||||||
|
var entry DbEntry
|
||||||
|
if err := proto.Unmarshal(pbentry, &entry); err != nil {
|
||||||
|
return fmt.Errorf("failed to unmarshal from protobuf: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.Value = databucket.Get([]byte(entry.Key))
|
||||||
|
|
||||||
|
var include bool
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case filter != nil:
|
||||||
|
if filter.MatchString(entry.Key) ||
|
||||||
|
filter.MatchString(strings.Join(entry.Tags, " ")) {
|
||||||
|
include = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !entry.Binary && !include {
|
||||||
|
if filter.MatchString(string(entry.Value)) {
|
||||||
|
include = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case len(attr.Tags) > 0:
|
||||||
|
for _, search := range attr.Tags {
|
||||||
|
for _, tag := range entry.Tags {
|
||||||
|
if tag == search {
|
||||||
|
include = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if include {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
include = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if include {
|
||||||
|
entries = append(entries, entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
return entries, err
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
// -*-c++-*-
|
||||||
|
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.36.1
|
// protoc-gen-go v1.36.1
|
||||||
@@ -25,12 +27,13 @@ type DbEntry struct {
|
|||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
Id string `protobuf:"bytes,1,opt,name=Id,proto3" json:"Id,omitempty"`
|
Id string `protobuf:"bytes,1,opt,name=Id,proto3" json:"Id,omitempty"`
|
||||||
Key string `protobuf:"bytes,2,opt,name=Key,proto3" json:"Key,omitempty"`
|
Key string `protobuf:"bytes,2,opt,name=Key,proto3" json:"Key,omitempty"`
|
||||||
Value string `protobuf:"bytes,3,opt,name=Value,proto3" json:"Value,omitempty"`
|
Preview string `protobuf:"bytes,3,opt,name=Preview,proto3" json:"Preview,omitempty"`
|
||||||
Bin []byte `protobuf:"bytes,4,opt,name=Bin,proto3" json:"Bin,omitempty"`
|
Tags []string `protobuf:"bytes,4,rep,name=Tags,proto3" json:"Tags,omitempty"`
|
||||||
Tags []string `protobuf:"bytes,5,rep,name=Tags,proto3" json:"Tags,omitempty"`
|
Created *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=Created,proto3" json:"Created,omitempty"`
|
||||||
Created *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=Created,proto3" json:"Created,omitempty"`
|
Size uint64 `protobuf:"varint,6,opt,name=Size,proto3" json:"Size,omitempty"`
|
||||||
Size uint64 `protobuf:"varint,7,opt,name=Size,proto3" json:"Size,omitempty"`
|
Encrypted bool `protobuf:"varint,7,opt,name=Encrypted,proto3" json:"Encrypted,omitempty"`
|
||||||
Encrypted bool `protobuf:"varint,8,opt,name=Encrypted,proto3" json:"Encrypted,omitempty"`
|
Binary bool `protobuf:"varint,8,opt,name=Binary,proto3" json:"Binary,omitempty"`
|
||||||
|
Value []byte `protobuf:"bytes,9,opt,name=Value,proto3" json:"Value,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
@@ -79,20 +82,13 @@ func (x *DbEntry) GetKey() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *DbEntry) GetValue() string {
|
func (x *DbEntry) GetPreview() string {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.Value
|
return x.Preview
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *DbEntry) GetBin() []byte {
|
|
||||||
if x != nil {
|
|
||||||
return x.Bin
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *DbEntry) GetTags() []string {
|
func (x *DbEntry) GetTags() []string {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.Tags
|
return x.Tags
|
||||||
@@ -121,26 +117,42 @@ func (x *DbEntry) GetEncrypted() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *DbEntry) GetBinary() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.Binary
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *DbEntry) GetValue() []byte {
|
||||||
|
if x != nil {
|
||||||
|
return x.Value
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
var File_app_dbentry_proto protoreflect.FileDescriptor
|
var File_app_dbentry_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
var file_app_dbentry_proto_rawDesc = []byte{
|
var file_app_dbentry_proto_rawDesc = []byte{
|
||||||
0x0a, 0x11, 0x61, 0x70, 0x70, 0x2f, 0x64, 0x62, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72,
|
0x0a, 0x11, 0x61, 0x70, 0x70, 0x2f, 0x64, 0x62, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72,
|
||||||
0x6f, 0x74, 0x6f, 0x12, 0x03, 0x61, 0x70, 0x70, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
|
0x6f, 0x74, 0x6f, 0x12, 0x03, 0x61, 0x70, 0x70, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
|
||||||
0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74,
|
0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74,
|
||||||
0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xcf, 0x01, 0x0a, 0x07, 0x44, 0x62,
|
0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xef, 0x01, 0x0a, 0x07, 0x44, 0x62,
|
||||||
0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
|
0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||||
0x09, 0x52, 0x02, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01,
|
0x09, 0x52, 0x02, 0x49, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01,
|
||||||
0x28, 0x09, 0x52, 0x03, 0x4b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65,
|
0x28, 0x09, 0x52, 0x03, 0x4b, 0x65, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x50, 0x72, 0x65, 0x76, 0x69,
|
||||||
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x10, 0x0a,
|
0x65, 0x77, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x50, 0x72, 0x65, 0x76, 0x69, 0x65,
|
||||||
0x03, 0x42, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x42, 0x69, 0x6e, 0x12,
|
0x77, 0x12, 0x12, 0x0a, 0x04, 0x54, 0x61, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52,
|
||||||
0x12, 0x0a, 0x04, 0x54, 0x61, 0x67, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x54,
|
0x04, 0x54, 0x61, 0x67, 0x73, 0x12, 0x34, 0x0a, 0x07, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64,
|
||||||
0x61, 0x67, 0x73, 0x12, 0x34, 0x0a, 0x07, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x06,
|
0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
|
||||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
|
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
|
||||||
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
|
0x6d, 0x70, 0x52, 0x07, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x53,
|
||||||
0x52, 0x07, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x53, 0x69, 0x7a,
|
0x69, 0x7a, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x53, 0x69, 0x7a, 0x65, 0x12,
|
||||||
0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1c, 0x0a,
|
0x1c, 0x0a, 0x09, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01,
|
||||||
0x09, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08,
|
0x28, 0x08, 0x52, 0x09, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x12, 0x16, 0x0a,
|
||||||
0x52, 0x09, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x42, 0x1e, 0x5a, 0x1c, 0x67,
|
0x06, 0x42, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x42,
|
||||||
|
0x69, 0x6e, 0x61, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x09,
|
||||||
|
0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x1e, 0x5a, 0x1c, 0x67,
|
||||||
0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x6c, 0x69, 0x6e, 0x64, 0x65,
|
0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x6c, 0x69, 0x6e, 0x64, 0x65,
|
||||||
0x6e, 0x2f, 0x61, 0x6e, 0x79, 0x64, 0x62, 0x2f, 0x61, 0x70, 0x70, 0x62, 0x06, 0x70, 0x72, 0x6f,
|
0x6e, 0x2f, 0x61, 0x6e, 0x79, 0x64, 0x62, 0x2f, 0x61, 0x70, 0x70, 0x62, 0x06, 0x70, 0x72, 0x6f,
|
||||||
0x74, 0x6f, 0x33,
|
0x74, 0x6f, 0x33,
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
// -*-c++-*-
|
||||||
|
|
||||||
syntax = "proto3";
|
syntax = "proto3";
|
||||||
package app;
|
package app;
|
||||||
|
|
||||||
@@ -8,10 +10,11 @@ option go_package = "github.com/tlinden/anydb/app";
|
|||||||
message DbEntry {
|
message DbEntry {
|
||||||
string Id = 1;
|
string Id = 1;
|
||||||
string Key = 2;
|
string Key = 2;
|
||||||
string Value = 3;
|
string Preview = 3;
|
||||||
bytes Bin = 4;
|
repeated string Tags = 4;
|
||||||
repeated string Tags = 5;
|
google.protobuf.Timestamp Created = 5;
|
||||||
google.protobuf.Timestamp Created = 6;
|
uint64 Size = 6;
|
||||||
uint64 Size = 7;
|
bool Encrypted = 7;
|
||||||
bool Encrypted = 8;
|
bool Binary = 8;
|
||||||
|
bytes Value = 9;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import (
|
|||||||
"github.com/tlinden/anydb/common"
|
"github.com/tlinden/anydb/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Version string = "v0.0.7"
|
var Version string = "v0.1.0"
|
||||||
|
|
||||||
type BucketConfig struct {
|
type BucketConfig struct {
|
||||||
Encrypt bool
|
Encrypt bool
|
||||||
|
|||||||
65
cmd/crud.go
65
cmd/crud.go
@@ -20,7 +20,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode/utf8"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/tlinden/anydb/app"
|
"github.com/tlinden/anydb/app"
|
||||||
@@ -124,12 +123,7 @@ func Get(conf *cfg.Config) *cobra.Command {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if utf8.ValidString(string(clear)) {
|
entry.Value = clear
|
||||||
entry.Value = string(clear)
|
|
||||||
} else {
|
|
||||||
entry.Bin = clear
|
|
||||||
}
|
|
||||||
|
|
||||||
entry.Encrypted = false
|
entry.Encrypted = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,7 +182,7 @@ func List(conf *cfg.Config) *cobra.Command {
|
|||||||
)
|
)
|
||||||
|
|
||||||
var cmd = &cobra.Command{
|
var cmd = &cobra.Command{
|
||||||
Use: "list [<filter-regex>] [-t <tag>] [-m <mode>] [-n -N] [-T <tpl>] [-i]",
|
Use: "list [<filter-regex>] [-m <mode>] [-n -N] [-T <tpl>] [-i]",
|
||||||
Short: "List database contents",
|
Short: "List database contents",
|
||||||
Long: `List database contents`,
|
Long: `List database contents`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
@@ -221,6 +215,58 @@ func List(conf *cfg.Config) *cobra.Command {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmd.PersistentFlags().StringVarP(&conf.Mode, "mode", "m", "", "output format (table|wide|json|template), wide is a verbose table. (default 'table')")
|
||||||
|
cmd.PersistentFlags().StringVarP(&conf.Template, "template", "T", "", "go template for '-m template'")
|
||||||
|
cmd.PersistentFlags().BoolVarP(&wide, "wide-output", "l", false, "output mode: wide")
|
||||||
|
cmd.PersistentFlags().BoolVarP(&conf.NoHeaders, "no-headers", "n", false, "omit headers in tables")
|
||||||
|
cmd.PersistentFlags().BoolVarP(&conf.NoHumanize, "no-human", "N", false, "do not translate to human readable values")
|
||||||
|
cmd.PersistentFlags().BoolVarP(&conf.CaseInsensitive, "case-insensitive", "i", false, "filter case insensitive")
|
||||||
|
|
||||||
|
cmd.Aliases = append(cmd.Aliases, "ls")
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func Find(conf *cfg.Config) *cobra.Command {
|
||||||
|
var (
|
||||||
|
attr app.DbAttr
|
||||||
|
wide bool
|
||||||
|
)
|
||||||
|
|
||||||
|
var cmd = &cobra.Command{
|
||||||
|
Use: "find <filter-regex> | -t <tag> [-m <mode>] [-n -N] [-T <tpl>] [-i]",
|
||||||
|
Short: "Find database contents",
|
||||||
|
Long: `Find database contents`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
// errors at this stage do not cause the usage to be shown
|
||||||
|
cmd.SilenceUsage = true
|
||||||
|
|
||||||
|
if len(args) > 0 {
|
||||||
|
if conf.CaseInsensitive {
|
||||||
|
attr.Args = []string{"(?i)" + args[0]}
|
||||||
|
} else {
|
||||||
|
attr.Args = args
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// turn comma list into slice, if needed
|
||||||
|
if len(attr.Tags) == 1 && strings.Contains(attr.Tags[0], ",") {
|
||||||
|
attr.Tags = strings.Split(attr.Tags[0], ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
if wide {
|
||||||
|
conf.Mode = "wide"
|
||||||
|
}
|
||||||
|
|
||||||
|
entries, err := conf.DB.Find(&attr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return output.List(os.Stdout, conf, entries)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
cmd.PersistentFlags().StringVarP(&conf.Mode, "mode", "m", "", "output format (table|wide|json|template), wide is a verbose table. (default 'table')")
|
cmd.PersistentFlags().StringVarP(&conf.Mode, "mode", "m", "", "output format (table|wide|json|template), wide is a verbose table. (default 'table')")
|
||||||
cmd.PersistentFlags().StringVarP(&conf.Template, "template", "T", "", "go template for '-m template'")
|
cmd.PersistentFlags().StringVarP(&conf.Template, "template", "T", "", "go template for '-m template'")
|
||||||
cmd.PersistentFlags().BoolVarP(&wide, "wide-output", "l", false, "output mode: wide")
|
cmd.PersistentFlags().BoolVarP(&wide, "wide-output", "l", false, "output mode: wide")
|
||||||
@@ -230,7 +276,8 @@ func List(conf *cfg.Config) *cobra.Command {
|
|||||||
cmd.PersistentFlags().StringArrayVarP(&attr.Tags, "tags", "t", nil, "tags, multiple allowed")
|
cmd.PersistentFlags().StringArrayVarP(&attr.Tags, "tags", "t", nil, "tags, multiple allowed")
|
||||||
|
|
||||||
cmd.Aliases = append(cmd.Aliases, "/")
|
cmd.Aliases = append(cmd.Aliases, "/")
|
||||||
cmd.Aliases = append(cmd.Aliases, "ls")
|
cmd.Aliases = append(cmd.Aliases, "f")
|
||||||
|
cmd.Aliases = append(cmd.Aliases, "search")
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|||||||
14
cmd/extra.go
14
cmd/extra.go
@@ -23,7 +23,6 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"unicode/utf8"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/tlinden/anydb/app"
|
"github.com/tlinden/anydb/app"
|
||||||
@@ -199,7 +198,7 @@ func Edit(conf *cfg.Config) *cobra.Command {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(entry.Value) == 0 && len(entry.Bin) > 0 {
|
if len(entry.Value) == 0 && entry.Binary {
|
||||||
return errors.New("key contains binary uneditable content")
|
return errors.New("key contains binary uneditable content")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,12 +215,7 @@ func Edit(conf *cfg.Config) *cobra.Command {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if utf8.ValidString(string(clear)) {
|
entry.Value = clear
|
||||||
entry.Value = string(clear)
|
|
||||||
} else {
|
|
||||||
entry.Bin = clear
|
|
||||||
}
|
|
||||||
|
|
||||||
entry.Encrypted = false
|
entry.Encrypted = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,7 +225,7 @@ func Edit(conf *cfg.Config) *cobra.Command {
|
|||||||
// save file to a temp file, call the editor with it, read
|
// save file to a temp file, call the editor with it, read
|
||||||
// it back in and compare the content with the original
|
// it back in and compare the content with the original
|
||||||
// one
|
// one
|
||||||
newcontent, err := editContent(editor, entry.Value)
|
newcontent, err := editContent(editor, string(entry.Value))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -241,7 +235,7 @@ func Edit(conf *cfg.Config) *cobra.Command {
|
|||||||
Key: attr.Key,
|
Key: attr.Key,
|
||||||
Tags: attr.Tags,
|
Tags: attr.Tags,
|
||||||
Encrypted: attr.Encrypted,
|
Encrypted: attr.Encrypted,
|
||||||
Val: newcontent,
|
Val: []byte(newcontent),
|
||||||
}
|
}
|
||||||
|
|
||||||
// encrypt if needed
|
// encrypt if needed
|
||||||
|
|||||||
@@ -122,6 +122,7 @@ func Execute() {
|
|||||||
// CRUD
|
// CRUD
|
||||||
rootCmd.AddCommand(Set(&conf))
|
rootCmd.AddCommand(Set(&conf))
|
||||||
rootCmd.AddCommand(List(&conf))
|
rootCmd.AddCommand(List(&conf))
|
||||||
|
rootCmd.AddCommand(Find(&conf))
|
||||||
rootCmd.AddCommand(Get(&conf))
|
rootCmd.AddCommand(Get(&conf))
|
||||||
rootCmd.AddCommand(Del(&conf))
|
rootCmd.AddCommand(Del(&conf))
|
||||||
|
|
||||||
|
|||||||
@@ -65,8 +65,6 @@ func ListTemplate(writer io.Writer, conf *cfg.Config, entries app.DbEntries) err
|
|||||||
buf := bytes.Buffer{}
|
buf := bytes.Buffer{}
|
||||||
|
|
||||||
for _, row := range entries {
|
for _, row := range entries {
|
||||||
row.Normalize()
|
|
||||||
|
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
err = tmpl.Execute(&buf, row)
|
err = tmpl.Execute(&buf, row)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -94,8 +92,6 @@ func ListTable(writer io.Writer, conf *cfg.Config, entries app.DbEntries) error
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, row := range entries {
|
for _, row := range entries {
|
||||||
row.Normalize()
|
|
||||||
|
|
||||||
if conf.Mode == "wide" {
|
if conf.Mode == "wide" {
|
||||||
switch conf.NoHumanize {
|
switch conf.NoHumanize {
|
||||||
case true:
|
case true:
|
||||||
@@ -104,21 +100,20 @@ func ListTable(writer io.Writer, conf *cfg.Config, entries app.DbEntries) error
|
|||||||
strings.Join(row.Tags, ","),
|
strings.Join(row.Tags, ","),
|
||||||
strconv.FormatUint(row.Size, 10),
|
strconv.FormatUint(row.Size, 10),
|
||||||
row.Created.AsTime().Format("02.01.2006T03:04.05"),
|
row.Created.AsTime().Format("02.01.2006T03:04.05"),
|
||||||
row.Value,
|
row.Preview,
|
||||||
})
|
})
|
||||||
default:
|
default:
|
||||||
table.Append([]string{
|
table.Append([]string{
|
||||||
row.Key,
|
row.Key,
|
||||||
strings.Join(row.Tags, ","),
|
strings.Join(row.Tags, ","),
|
||||||
humanize.Bytes(uint64(row.Size)),
|
humanize.Bytes(uint64(row.Size)),
|
||||||
//row.Created.Format("02.01.2006T03:04.05"),
|
|
||||||
humanize.Time(row.Created.AsTime()),
|
humanize.Time(row.Created.AsTime()),
|
||||||
row.Value,
|
row.Preview,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
table.Append([]string{row.Key, row.Value})
|
table.Append([]string{row.Key, row.Preview})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/dustin/go-humanize"
|
"github.com/dustin/go-humanize"
|
||||||
"github.com/tlinden/anydb/app"
|
"github.com/tlinden/anydb/app"
|
||||||
@@ -40,16 +39,15 @@ func Print(writer io.Writer, conf *cfg.Config, attr *app.DbAttr, entry *app.DbEn
|
|||||||
|
|
||||||
switch conf.Mode {
|
switch conf.Mode {
|
||||||
case "simple", "":
|
case "simple", "":
|
||||||
if len(entry.Bin) > 0 {
|
if entry.Binary {
|
||||||
if isatty {
|
if isatty {
|
||||||
fmt.Println("binary data omitted")
|
fmt.Println("binary data omitted")
|
||||||
} else {
|
} else {
|
||||||
os.Stdout.Write(entry.Bin)
|
os.Stdout.Write(entry.Value)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fmt.Print(entry.Value)
|
fmt.Print(string(entry.Value))
|
||||||
|
if entry.Value[entry.Size-1] != '\n' {
|
||||||
if !strings.HasSuffix(entry.Value, "\n") {
|
|
||||||
// always add a terminal newline
|
// always add a terminal newline
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
}
|
}
|
||||||
@@ -87,17 +85,14 @@ func WriteFile(writer io.Writer, conf *cfg.Config, attr *app.DbAttr, entry *app.
|
|||||||
fileHandle = fd
|
fileHandle = fd
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(entry.Bin) > 0 {
|
if entry.Binary {
|
||||||
// binary file content
|
// binary file content
|
||||||
_, err = fileHandle.Write(entry.Bin)
|
_, err = fileHandle.Write(entry.Value)
|
||||||
} else {
|
|
||||||
val := entry.Value
|
|
||||||
if !strings.HasSuffix(val, "\n") {
|
|
||||||
// always add a terminal newline
|
|
||||||
val += "\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = fileHandle.Write([]byte(val))
|
if entry.Value[entry.Size-1] != '\n' {
|
||||||
|
// always add a terminal newline
|
||||||
|
_, err = fileHandle.Write([]byte{'\n'})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user