fixed crash in api list, added filter support to api list

This commit is contained in:
2025-01-01 18:08:43 +01:00
parent 675d10d8fa
commit 9afca91159
10 changed files with 31 additions and 29 deletions

View File

@@ -154,6 +154,11 @@ curl localhost:8787/anydb/v1/foo
# list keys # list keys
curl localhost:8787/anydb/v1/ curl localhost:8787/anydb/v1/
# same, but do a full text search by content, searching for "foo"
curl -X POST http://127.0.0.1:8787/anydb/v1/ \
-H 'Content-Type: application/json'
-d '{"key":"foo", "fulltext": true}'
# as you might correctly suspect you can store multi-line values or # 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? # the content of text files. but what to do if you want to change it?
# here's one way: # here's one way:

20
TODO.md
View File

@@ -4,36 +4,16 @@
- mime-type => exec app + value - mime-type => exec app + value
- add waitgroup to db.go funcs - add waitgroup to db.go funcs
- RestList does not support any params? - RestList does not support any params?
- lc() incoming tags+keys
## DB Structure ## DB Structure
- put tags into sub bucket see #1 - put tags into sub bucket see #1
- change structure to:
data bucket
key => {key,value[0:60],isbin:bool}
value bucket
key => value (maybe always use []byte here)
tags bucket tags bucket
key/tag => tag/key key/tag => tag/key
tag/key => tag tag/key => tag
So, list just uses the data bucket, no large contents.
A tag search only looksup matching tags, see #1.
Only a full text search and get would need to dig into the value bucket.
A delete would just delete all keys from all values and then: A delete would just delete all keys from all values and then:
lookup in tags bucket for all key/*, then iterate over the values and lookup in tags bucket for all key/*, then iterate over the values and
remove all tag/key's. Then deleting a key would not leave any residue remove all tag/key's. Then deleting a key would not leave any residue
behind. behind.
However, maybe change the list command to just list everything and add
an extra find command for fulltext or tag search. Maybe still provide
filter options in list command but only filter for keys.
DONE: most of the above, except the tag stuff. manpage needs update and tests.
maybe stitch the find command and just add -f (full text search) to list.

View File

@@ -133,7 +133,7 @@
.\" ======================================================================== .\" ========================================================================
.\" .\"
.IX Title "ANYDB 1" .IX Title "ANYDB 1"
.TH ANYDB 1 "2024-12-30" "1" "User Commands" .TH ANYDB 1 "2025-01-01" "1" "User Commands"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents. .\" way too many mistakes in technical documents.
.if n .ad l .if n .ad l

View File

@@ -33,6 +33,9 @@ type DbAttr struct {
File string File string
Encrypted bool Encrypted bool
Binary bool Binary bool
// conf flags, needed for incoming rest requests
Fulltext bool
} }
// check if value is to be read from a file or stdin, setup preview // check if value is to be read from a file or stdin, setup preview

View File

@@ -95,9 +95,15 @@ func (db *DB) List(attr *DbAttr, fulltext bool) (DbEntries, error) {
var filter *regexp.Regexp var filter *regexp.Regexp
if len(attr.Args) > 0 { if len(attr.Args) > 0 {
// via cli
filter = regexp.MustCompile(attr.Args[0]) filter = regexp.MustCompile(attr.Args[0])
} }
if len(attr.Key) > 0 {
// via api
filter = regexp.MustCompile(attr.Key)
}
err := db.DB.View(func(tx *bolt.Tx) error { err := db.DB.View(func(tx *bolt.Tx) error {
root := tx.Bucket([]byte(db.Bucket)) root := tx.Bucket([]byte(db.Bucket))
if root == nil { if root == nil {
@@ -120,7 +126,13 @@ func (db *DB) List(attr *DbAttr, fulltext bool) (DbEntries, error) {
return fmt.Errorf("failed to unmarshal from protobuf: %w", err) return fmt.Errorf("failed to unmarshal from protobuf: %w", err)
} }
entry.Value = databucket.Get([]byte(entry.Key)) // empty is ok if fulltext {
// avoid crash due to access fault
value := databucket.Get([]byte(entry.Key)) // empty is ok
vc := make([]byte, len(value))
copy(vc, value)
entry.Value = vc
}
var include bool var include bool
@@ -327,7 +339,6 @@ func (db *DB) Get(attr *DbAttr) (*DbEntry, error) {
} }
func (db *DB) Del(attr *DbAttr) error { func (db *DB) Del(attr *DbAttr) error {
// FIXME: check if it exists prior to just call bucket.Delete()?
if err := db.Open(); err != nil { if err := db.Open(); err != nil {
return err return err
} }

View File

@@ -26,7 +26,7 @@ import (
"github.com/tlinden/anydb/common" "github.com/tlinden/anydb/common"
) )
var Version string = "v0.1.0" var Version string = "v0.1.1"
type BucketConfig struct { type BucketConfig struct {
Encrypt bool Encrypt bool

View File

@@ -182,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>] [-nNif] [-T <tpl>]", Use: "list [<filter-regex> | -t <tag> ] [-m <mode>] [-nNis] [-T <tpl>]",
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 {

View File

@@ -33,7 +33,6 @@ import (
) )
func List(writer io.Writer, conf *cfg.Config, entries app.DbEntries) error { func List(writer io.Writer, conf *cfg.Config, entries app.DbEntries) error {
// FIXME: call sort here
switch conf.Mode { switch conf.Mode {
case "wide", "", "table": case "wide", "", "table":
return ListTable(writer, conf, entries) return ListTable(writer, conf, entries)

View File

@@ -44,7 +44,6 @@ func RestList(c *fiber.Ctx, conf *cfg.Config) error {
attr := new(app.DbAttr) attr := new(app.DbAttr)
if len(c.Body()) > 0 { if len(c.Body()) > 0 {
if err := c.BodyParser(attr); err != nil { if err := c.BodyParser(attr); err != nil {
return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{ return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{
"errors": err.Error(), "errors": err.Error(),
@@ -54,7 +53,7 @@ func RestList(c *fiber.Ctx, conf *cfg.Config) error {
} }
// get list // get list
entries, err := conf.DB.List(attr, false) entries, err := conf.DB.List(attr, attr.Fulltext)
if err != nil { if err != nil {
return JsonStatus(c, fiber.StatusForbidden, return JsonStatus(c, fiber.StatusForbidden,
"Unable to list keys: "+err.Error()) "Unable to list keys: "+err.Error())

View File

@@ -42,6 +42,11 @@ func Runserver(conf *cfg.Config, args []string) error {
return RestList(c, conf) return RestList(c, conf)
}) })
api.Post("/", func(c *fiber.Ctx) error {
// same thing as above but allows to supply parameters, see app.Dbattr{}
return RestList(c, conf)
})
api.Get("/:key", func(c *fiber.Ctx) error { api.Get("/:key", func(c *fiber.Ctx) error {
return RestGet(c, conf) return RestGet(c, conf)
}) })
@@ -78,7 +83,7 @@ func SetupServer(conf *cfg.Config) *fiber.App {
}) })
router.Use(logger.New(logger.Config{ router.Use(logger.New(logger.Config{
Format: "${pid} ${ip}:${port} ${status} - ${method} ${path}\n", Format: "${pid} ${ip}:${port} ${status} - ${method} ${path}\n",
DisableColors: true, DisableColors: true,
})) }))