diff --git a/.gh-dash.yml b/.gh-dash.yml
deleted file mode 100644
index cedab49..0000000
--- a/.gh-dash.yml
+++ /dev/null
@@ -1,96 +0,0 @@
-prSections:
- - title: Responsible PRs
- filters: repo:tlinden/anydb is:open NOT dependabot
- layout:
- repoName:
- hidden: true
-
- - title: Responsible Dependabot PRs
- filters: repo:tlinden/anydb is:open dependabot
- layout:
- repoName:
- hidden: true
-
-issuesSections:
- - title: Responsible Issues
- filters: is:open repo:tlinden/anydb -author:@me
- layout:
- repoName:
- hidden: true
-
- - title: Note-to-Self Issues
- filters: is:open repo:tlinden/anydb author:@me
- layout:
- creator:
- hidden: true
- repoName:
- hidden: true
-
-defaults:
- preview:
- open: false
- width: 100
-
-keybindings:
- universal:
- - key: "shift+down"
- builtin: pageDown
- - key: "shift+up"
- builtin: pageUp
- prs:
- - key: g
- name: gitu
- command: >
- cd {{.RepoPath}} && /home/scip/bin/gitu
- - key: M
- name: squash-merge
- command: gh pr merge --rebase --squash --admin --repo {{.RepoName}} {{.PrNumber}}
- - key: i
- name: show ci checks
- command: gh pr checks --repo {{.RepoName}} {{.PrNumber}} | glow -p
- - key: e
- name: edit pr
- command: ~/.config/gh-dash/edit-gh-pr {{.RepoName}} {{.PrNumber}}
- - key: E
- name: open repo in emacs
- command: emacsclient {{.RepoPath}} &
- issues:
- - key: v
- name: view
- command: gh issue view --repo {{.RepoName}} {{.IssueNumber}} | glow -p
- - key: l
- name: add label
- command: gh issue --repo {{.RepoName}} edit {{.IssueNumber}} --add-label $(gum choose bug enhancement question dependencies wontfix)
- - key: L
- name: remove label
- command: gh issue --repo {{.RepoName}} edit {{.IssueNumber}} --remove-label $(gum choose bug enhancement question dependencies wontfix)
- - key: E
- name: open repo in emacs
- command: emacsclient {{.RepoPath}} &
-
-theme:
- ui:
- sectionsShowCount: true
- table:
- compact: false
- showSeparator: true
- colors:
- text:
- primary: "#E2E1ED"
- secondary: "#6770cb"
- inverted: "#242347"
- faint: "#b0793b"
- warning: "#E0AF68"
- success: "#3DF294"
- background:
- selected: "#1B1B33"
- border:
- primary: "#383B5B"
- secondary: "#39386B"
- faint: "#8d3e0b"
-
-repoPaths:
- :owner/:repo: ~/dev/:repo
-
-pager:
- diff: delta
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
deleted file mode 100644
index 49c5582..0000000
--- a/.github/workflows/ci.yaml
+++ /dev/null
@@ -1,56 +0,0 @@
-name: build-and-test
-
-#on: [push]
-on:
- pull_request:
- branches:
- - main
- push:
- branches:
- - main
-
-jobs:
- build:
- strategy:
- matrix:
- version: ['1.24']
- os: [ubuntu-latest, macos-latest, windows-latest]
- name: Build
- runs-on: ${{ matrix.os }}
- steps:
- - name: Set up Go
- uses: actions/setup-go@v6
- with:
- go-version: ${{ matrix.version }}
- id: go
-
- - name: checkout
- uses: actions/checkout@v5
-
- - name: build
- run: go build
-
- - name: test
- run: make test
-
- - name: Update coverage report
- uses: ncruces/go-coverage-report@main
- with:
- report: true
- chart: true
- amend: true
- if: |
- matrix.os == 'ubuntu-latest' &&
- github.event_name == 'push'
- continue-on-error: true
-
- golangci:
- name: lint
- runs-on: ubuntu-latest
- steps:
- - uses: actions/setup-go@v6
- with:
- go-version: 1.24
- - uses: actions/checkout@v5
- - name: golangci-lint
- uses: golangci/golangci-lint-action@v8
diff --git a/.github/workflows/pushimage.yaml b/.github/workflows/pushimage.yaml
deleted file mode 100644
index 6ea4956..0000000
--- a/.github/workflows/pushimage.yaml
+++ /dev/null
@@ -1,34 +0,0 @@
-name: build-push-image
-
-on:
- push:
- tags:
- - 'v*'
-
-jobs:
- build-and-push-image:
- runs-on: ubuntu-latest
- permissions:
- packages: write
- steps:
- - name: Checkout repository
- uses: actions/checkout@v5
-
- - name: Log in to the Container registry
- uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef
- with:
- registry: https://ghcr.io
- username: ${{ github.actor }}
- password: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Build and push Docker image
- uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83
- with:
- push: true
- tags: ghcr.io/tlinden/anydb:${{ github.ref_name}}
-
- - name: Build and push latest Docker image
- uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83
- with:
- push: true
- tags: ghcr.io/tlinden/anydb:latest
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
deleted file mode 100644
index a5337a8..0000000
--- a/.github/workflows/release.yaml
+++ /dev/null
@@ -1,87 +0,0 @@
-name: build-release
-on:
- push:
- tags:
- - "v*.*.*"
-
-jobs:
- release:
- name: Build Release Assets
- runs-on: ubuntu-latest
- steps:
- - name: Checkout code
- uses: actions/checkout@v5
-
- - name: Set up Go
- uses: actions/setup-go@v6
- with:
- go-version: 1.24.1
-
- - name: Build the executables
- run: ./mkrel.sh anydb ${{ github.ref_name}}
-
- - name: List the executables
- run: ls -l ./releases
-
- - name: Upload the binaries
- uses: svenstaro/upload-release-action@v2
- with:
- repo_token: ${{ secrets.GITHUB_TOKEN }}
- tag: ${{ github.ref_name }}
- file: ./releases/*
- file_glob: true
-
- - name: Build Changelog
- id: github_release
- uses: mikepenz/release-changelog-builder-action@v5
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- with:
- mode: "PR"
- configurationJson: |
- {
- "template": "#{{CHANGELOG}}\n\n**Full Changelog**: #{{RELEASE_DIFF}}",
- "pr_template": "- #{{TITLE}} (##{{NUMBER}}) by #{{AUTHOR}}\n#{{BODY}}",
- "empty_template": "- no changes",
- "categories": [
- {
- "title": "## New Features",
- "labels": ["add", "feature"]
- },
- {
- "title": "## Bug Fixes",
- "labels": ["fix", "bug", "revert"]
- },
- {
- "title": "## Documentation Enhancements",
- "labels": ["doc"]
- },
- {
- "title": "## Refactoring Efforts",
- "labels": ["refactor"]
- },
- {
- "title": "## Miscellaneus Changes",
- "labels": []
- }
- ],
- "ignore_labels": [
- "duplicate", "good first issue", "help wanted", "invalid", "question", "wontfix"
- ],
- "label_extractor": [
- {
- "pattern": "(.) (.+)",
- "target": "$1"
- },
- {
- "pattern": "(.) (.+)",
- "target": "$1",
- "on_property": "title"
- }
- ]
- }
-
- - name: Create Release
- uses: softprops/action-gh-release@v2
- with:
- body: ${{steps.github_release.outputs.changelog}}
diff --git a/.golangci.bck.yaml b/.golangci.bck.yaml
deleted file mode 100644
index 58aeea5..0000000
--- a/.golangci.bck.yaml
+++ /dev/null
@@ -1,6 +0,0 @@
-linters:
- exclusions:
- rules:
- - linters:
- - staticcheck
- text: "QF1008:"
diff --git a/.golangci.yaml b/.golangci.yaml
deleted file mode 100644
index 4cdd836..0000000
--- a/.golangci.yaml
+++ /dev/null
@@ -1,7 +0,0 @@
-version: "2"
-linters:
- exclusions:
- rules:
- - linters:
- - staticcheck
- text: "QF1008:"
diff --git a/Dockerfile b/Dockerfile
deleted file mode 100644
index 0fd15a0..0000000
--- a/Dockerfile
+++ /dev/null
@@ -1,42 +0,0 @@
-#
-# 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 .
-#
-FROM golang:1.24-alpine as builder
-
-RUN apk update
-RUN apk upgrade
-RUN apk add --no-cache git make
-
-RUN git --version
-
-WORKDIR /work
-
-COPY go.mod .
-COPY . .
-RUN go mod download
-RUN make
-
-FROM alpine:latest
-LABEL maintainer="Thomas von Dein "
-
-WORKDIR /app
-COPY --from=builder /work/anydb /app/anydb
-
-ENV LANG C.UTF-8
-USER 1001:1001
-
-ENTRYPOINT ["/app/anydb"]
-CMD ["-h"]
diff --git a/Makefile b/Makefile
deleted file mode 100644
index f198825..0000000
--- a/Makefile
+++ /dev/null
@@ -1,111 +0,0 @@
-
-# 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 .
-
-
-#
-# no need to modify anything below
-tool = anydb
-version = $(shell egrep "= .v" cfg/config.go | cut -d'=' -f2 | cut -d'"' -f 2)
-archs = android darwin freebsd linux netbsd openbsd windows
-PREFIX = /usr/local
-UID = root
-GID = 0
-BRANCH = $(shell git branch --show-current)
-COMMIT = $(shell git rev-parse --short=8 HEAD)
-BUILD = $(shell date +%Y.%m.%d.%H%M%S)
-VERSION := $(if $(filter $(BRANCH), development),$(version)-$(BRANCH)-$(COMMIT)-$(BUILD),$(version))
-HAVE_POD := $(shell pod2text -h 2>/dev/null)
-
-all: $(tool).1 cmd/$(tool).go app/dbentry.pb.go buildlocal
-
-%.1: %.pod
-ifdef HAVE_POD
- pod2man -c "User Commands" -r 1 -s 1 $*.pod > $*.1
-endif
-
-cmd/%.go: %.pod
-ifdef HAVE_POD
- echo "package cmd" > cmd/$*.go
- echo >> cmd/$*.go
- echo "var manpage = \`" >> cmd/$*.go
- pod2text $*.pod >> cmd/$*.go
- echo "\`" >> cmd/$*.go
-endif
-
-# echo "var usage = \`" >> cmd/$*.go
-# awk '/SYNOPS/{f=1;next} /DESCR/{f=0} f' $*.pod | sed 's/^ //' >> cmd/$*.go
-# echo "\`" >> cmd/$*.go
-
-app/dbentry.pb.go: app/dbentry.proto
- protoc -I=. --go_out=app app/dbentry.proto
- mv app/github.com/tlinden/anydb/app/dbentry.pb.go app/dbentry.pb.go
- rm -rf app/github.com
-
-buildlocal:
- go build -ldflags "-X 'github.com/tlinden/anydb/cfg.VERSION=$(VERSION)'"
-
-# binaries are being built by ci workflow on tag creation
-release:
- gh release create $(version) --generate-notes
-
-install: buildlocal
- install -d -o $(UID) -g $(GID) $(PREFIX)/bin
- install -d -o $(UID) -g $(GID) $(PREFIX)/man/man1
- install -o $(UID) -g $(GID) -m 555 $(tool) $(PREFIX)/sbin/
- install -o $(UID) -g $(GID) -m 444 $(tool).1 $(PREFIX)/man/man1/
-
-clean:
- rm -rf $(tool) releases coverage.out
-
-test: clean
- ANYDB_PASSWORD=test go test -v ./...
-
-singletest:
- @echo "Call like this: ''make singletest TEST=TestPrepareColumns MOD=lib"
- ANYDB_PASSWORD=test go test -run $(TEST) github.com/tlinden/anydb/$(MOD)
-
-cover-report:
- go test ./... -cover -coverprofile=coverage.out
- go tool cover -html=coverage.out
-
-show-versions: buildlocal
- @echo "### anydb version:"
- @./anydb --version
-
- @echo
- @echo "### go module versions:"
- @go list -m all
-
- @echo
- @echo "### go version used for building:"
- @grep -m 1 go go.mod
-
-goupdate:
- go get -t -u=patch ./...
-
-lint:
- golangci-lint run
-
-# keep til ireturn
-lint-full:
- golangci-lint run --enable-all --exclude-use-default --disable exhaustivestruct,exhaustruct,depguard,interfacer,deadcode,golint,structcheck,scopelint,varcheck,ifshort,maligned,nosnakecase,godot,funlen,gofumpt,cyclop,noctx,gochecknoglobals,paralleltest,forbidigo,gci,godox,goimports,ireturn,stylecheck,testpackage,mirror,nestif,revive,goerr113,gomnd
- gocritic check -enableAll *.go
-
-demo:
- make -C demo demo
-
-
-.PHONY: demo
diff --git a/README.md b/README.md
index 2c7466d..0acf7de 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,14 @@
-## A personal key value store
+> [!IMPORTANT]
+> This software is now being maintained on [Codeberg](https://codeberg.org/scip/anydb/).
-[](https://github.com/tlinden/anydb/actions)
-[](https://github.com/tlinden/anydb/blob/master/LICENSE)
-[](https://raw.githack.com/wiki/tlinden/anydb/coverage.html)
-[](https://goreportcard.com/report/github.com/tlinden/anydb)
-[](https://github.com/TLINDEN/anydb/releases/latest)
+[](https://ci.codeberg.org/repos/15517)
+[](https://codeberg.org/scip/anydb/blob/master/LICENSE)
+[](https://goreportcard.com/report/codeberg.org/scip/anydb)
+[](https://codeberg.org/scip/anydb/releases)
[](https://github.com/TLINDEN/anydb/blob/master/anydb.pod)
+## A personal key value store
+
> [!CAUTION]
> Between version 0.1.0 and version 0.2.1 deletion of keys did not work. There
> is a unit test to check for this, but this unit test had a bug as well and
diff --git a/TODO.md b/TODO.md
deleted file mode 100644
index ee91abd..0000000
--- a/TODO.md
+++ /dev/null
@@ -1,19 +0,0 @@
-## Features
-
-- repl
-- mime-type => exec app + value
-- add waitgroup to db.go funcs
-- RestList does not support any params?
-
-## DB Structure
-
-- put tags into sub bucket see #1
-
-tags bucket
-key/tag => tag/key
-tag/key => tag
-
-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
-remove all tag/key's. Then deleting a key would not leave any residue
-behind.
diff --git a/anydb.1 b/anydb.1
deleted file mode 100644
index 280e9d4..0000000
--- a/anydb.1
+++ /dev/null
@@ -1,763 +0,0 @@
-.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.40)
-.\"
-.\" Standard preamble:
-.\" ========================================================================
-.de Sp \" Vertical space (when we can't use .PP)
-.if t .sp .5v
-.if n .sp
-..
-.de Vb \" Begin verbatim text
-.ft CW
-.nf
-.ne \\$1
-..
-.de Ve \" End verbatim text
-.ft R
-.fi
-..
-.\" Set up some character translations and predefined strings. \*(-- will
-.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
-.\" double quote, and \*(R" will give a right double quote. \*(C+ will
-.\" give a nicer C++. Capital omega is used to do unbreakable dashes and
-.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff,
-.\" nothing in troff, for use with C<>.
-.tr \(*W-
-.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
-.ie n \{\
-. ds -- \(*W-
-. ds PI pi
-. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
-. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
-. ds L" ""
-. ds R" ""
-. ds C` ""
-. ds C' ""
-'br\}
-.el\{\
-. ds -- \|\(em\|
-. ds PI \(*p
-. ds L" ``
-. ds R" ''
-. ds C`
-. ds C'
-'br\}
-.\"
-.\" Escape single quotes in literal strings from groff's Unicode transform.
-.ie \n(.g .ds Aq \(aq
-.el .ds Aq '
-.\"
-.\" If the F register is >0, we'll generate index entries on stderr for
-.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
-.\" entries marked with X<> in POD. Of course, you'll have to process the
-.\" output yourself in some meaningful fashion.
-.\"
-.\" Avoid warning from groff about undefined register 'F'.
-.de IX
-..
-.nr rF 0
-.if \n(.g .if rF .nr rF 1
-.if (\n(rF:(\n(.g==0)) \{\
-. if \nF \{\
-. de IX
-. tm Index:\\$1\t\\n%\t"\\$2"
-..
-. if !\nF==2 \{\
-. nr % 0
-. nr F 2
-. \}
-. \}
-.\}
-.rr rF
-.\"
-.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
-.\" Fear. Run. Save yourself. No user-serviceable parts.
-. \" fudge factors for nroff and troff
-.if n \{\
-. ds #H 0
-. ds #V .8m
-. ds #F .3m
-. ds #[ \f1
-. ds #] \fP
-.\}
-.if t \{\
-. ds #H ((1u-(\\\\n(.fu%2u))*.13m)
-. ds #V .6m
-. ds #F 0
-. ds #[ \&
-. ds #] \&
-.\}
-. \" simple accents for nroff and troff
-.if n \{\
-. ds ' \&
-. ds ` \&
-. ds ^ \&
-. ds , \&
-. ds ~ ~
-. ds /
-.\}
-.if t \{\
-. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u"
-. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u'
-. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u'
-. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u'
-. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u'
-. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u'
-.\}
-. \" troff and (daisy-wheel) nroff accents
-.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V'
-.ds 8 \h'\*(#H'\(*b\h'-\*(#H'
-.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#]
-.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H'
-.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u'
-.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#]
-.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#]
-.ds ae a\h'-(\w'a'u*4/10)'e
-.ds Ae A\h'-(\w'A'u*4/10)'E
-. \" corrections for vroff
-.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u'
-.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u'
-. \" for low resolution devices (crt and lpr)
-.if \n(.H>23 .if \n(.V>19 \
-\{\
-. ds : e
-. ds 8 ss
-. ds o a
-. ds d- d\h'-1'\(ga
-. ds D- D\h'-1'\(hy
-. ds th \o'bp'
-. ds Th \o'LP'
-. ds ae ae
-. ds Ae AE
-.\}
-.rm #[ #] #H #V #F C
-.\" ========================================================================
-.\"
-.IX Title "ANYDB 1"
-.TH ANYDB 1 "2025-02-11" "1" "User Commands"
-.\" For nroff, turn off justification. Always turn off hyphenation; it makes
-.\" way too many mistakes in technical documents.
-.if n .ad l
-.nh
-.SH "anydb"
-.IX Header "anydb"
-anydb \- a personal key value store
-.SH "SYNOPSIS"
-.IX Header "SYNOPSIS"
-.Vb 3
-\& Usage:
-\& anydb [options] [flags]
-\& anydb [command]
-\&
-\& Available Commands:
-\& completion Generate the autocompletion script for the specified shell
-\& del Delete key
-\& edit Edit a key
-\& export Export database to json
-\& get Retrieve value for a key
-\& help Help about any command
-\& import Import database dump
-\& info info
-\& list List database contents
-\& man show manual page
-\& serve run REST API listener
-\& set Insert key/value pair
-\&
-\& Flags:
-\& \-b, \-\-bucket string use other bucket (default: data) (default "data")
-\& \-c, \-\-config string toml config file
-\& \-f, \-\-dbfile string DB file to use (default "/home/scip/.config/anydb/default.db")
-\& \-d, \-\-debug Enable debugging
-\& \-h, \-\-help help for anydb
-\& \-v, \-\-version Print program version
-\&
-\& Use "anydb [command] \-\-help" for more information about a command.
-.Ve
-.SH "DESCRIPTION"
-.IX Header "DESCRIPTION"
-Anydb is a commandline personal key value store, it is simple to use
-and can be used to store anything you'd like, even binary files
-etc. It uses a key/value store (bbolt) in your home directory.
-.PP
-The tool provides a number of subcommands to use it, there are global
-options and each subcommand has its own set of options.
-.SH "GLOBAL OPTIONS"
-.IX Header "GLOBAL OPTIONS"
-.ie n .IP """\-f, \-\-dbfile filename""" 4
-.el .IP "\f(CW\-f, \-\-dbfile filename\fR" 4
-.IX Item "-f, --dbfile filename"
-The default location of your databas is
-\&\f(CW\*(C`$HOME/.config/anydb/default.db\*(C'\fR. You can change this with the \f(CW\*(C`\-f\*(C'\fR
-option.
-.ie n .IP """\-b, \-\-bucket name""" 4
-.el .IP "\f(CW\-b, \-\-bucket name\fR" 4
-.IX Item "-b, --bucket name"
-Data in a bbolt key-value-store are managed in so called
-buckets. These are kind of namespaces, where each key must be
-unique. However, a database may contain more than one bucket.
-.Sp
-By default anydb uses a bucket named \*(L"data\*(R", but you can change this
-using the option \f(CW\*(C`\-b\*(C'\fR.
-.Sp
-Buckets can be configured to always encrypt values, see \s-1ENCRYTPTION\s0.
-.ie n .IP """\-c, \-\-config filename""" 4
-.el .IP "\f(CW\-c, \-\-config filename\fR" 4
-.IX Item "-c, --config filename"
-Under normal circumstances you don't need a configuration file. But if
-you want, you can provide one using the option \f(CW\*(C`\-c\*(C'\fR.
-.Sp
-Anydb looks for a couple of default locations for a config file. You
-only need this option if you want to supply a configuration on a
-non-standard location. See \s-1CONFIGURATION\s0 for more details.
-.ie n .IP """\-d, \-\-debug""" 4
-.el .IP "\f(CW\-d, \-\-debug\fR" 4
-.IX Item "-d, --debug"
-Enable debug output.
-.ie n .IP """\-h, \-\-help""" 4
-.el .IP "\f(CW\-h, \-\-help\fR" 4
-.IX Item "-h, --help"
-Show the usage of anydb.
-.ie n .IP """\-v, \-\-version""" 4
-.el .IP "\f(CW\-v, \-\-version\fR" 4
-.IX Item "-v, --version"
-Show the program version.
-.PP
-All of these options can be used with subcommands as well.
-.SH "SUBCOMMANDS"
-.IX Header "SUBCOMMANDS"
-.SS "completion"
-.IX Subsection "completion"
-The \fBcompletion\fR command can be used to setup completion for
-anydb. Just put something like this into your shell's configuration
-file:
-.PP
-.Vb 1
-\& source <(anydb completion bash)
-.Ve
-.PP
-If you use another shell, specify it instead of bash, of course.
-.SS "set"
-.IX Subsection "set"
-The \fBset\fR command is being used to insert or update a key-value pair.
-.PP
-Usage:
-.PP
-.Vb 2
-\& Usage:
-\& anydb set [ | \-r ] [\-t ] [flags]
-\&
-\& Aliases:
-\& set, add, s, +
-\&
-\& Flags:
-\& \-e, \-\-encrypt encrypt value
-\& \-r, \-\-file string Filename or \- for STDIN
-\& \-h, \-\-help help for set
-\& \-t, \-\-tags tag,tag,... tags, multiple allowed
-.Ve
-.PP
-The standard way to insert a new entry is really simple:
-.PP
-.Vb 1
-\& anydb set key value
-.Ve
-.PP
-If you don't specify a value, anydb expects you to feed it some data
-via \s-1STDIN.\s0 For example:
-.PP
-.Vb 1
-\& anydb set key < file
-.Ve
-.PP
-You might as well specify a file directly using the \f(CW\*(C`\-f\*(C'\fR option:
-.PP
-.Vb 1
-\& anydb set key \-f file
-.Ve
-.PP
-Values can be encrypted using \fBChaCha20Poly1305\fR when you specify the
-\&\f(CW\*(C`\-e\*(C'\fR option. Anydb will ask you interactively for a passphrase. You
-can also provide the passphrase using the environment variable
-\&\f(CW\*(C`ANYDB_PASSWORD\*(C'\fR. To encrypt the value, a cryptographically secure
-key will be derived from the passphrase using the ArgonID2
-algorithm. Each value can be encrypted with another passphrase. So,
-the database itself is not encrypted, just the values.
-.PP
-You can supply tags by using the option \f(CW\*(C`\-t\*(C'\fR. Multiple tags can be
-provided either by separating them with a comma or by using multiple
-\&\f(CW\*(C`\-t\*(C'\fR parameters:
-.PP
-.Vb 2
-\& anydb set key value \-t tag1,tag2
-\& anydb set key value \-t tag1 \-t tag2
-.Ve
-.PP
-You can later filter entries by tag or by a combination of tags.
-.PP
-To edit or modify an entry, just use the \fBset\fR command with the same
-key, the value in the database will be overwritten with the new
-value. An alternative option is the \fBedit\fR command, see below.
-.SS "get"
-.IX Subsection "get"
-To retrieve the value of a key, use the \fBget\fR subcommand.
-.PP
-Usage:
-.PP
-.Vb 2
-\& Usage:
-\& anydb get [\-o ] [\-m ] [\-n \-N] [\-T ] [flags]
-\&
-\& Aliases:
-\& get, show, g, .
-\&
-\& Flags:
-\& \-h, \-\-help help for get
-\& \-m, \-\-mode string output format (simple|wide|json|template) (default \*(Aqsimple\*(Aq)
-\& \-n, \-\-no\-headers omit headers in tables
-\& \-N, \-\-no\-human do not translate to human readable values
-\& \-o, \-\-output string output value to file (ignores \-m)
-\& \-T, \-\-template string go template for \*(Aq\-m template\*(Aq
-.Ve
-.PP
-In its simplest form you just call the \fBget\fR subcommand with the key
-you want to have the value for. The value is being printed to \s-1STDOUT\s0
-by default:
-.PP
-.Vb 1
-\& anydb get key
-.Ve
-.PP
-If the value is binary content, it will not just being printed. In
-those cases you need to either redirect output into a file or use the
-option \f(CW\*(C`\-o\*(C'\fR to write to a file:
-.PP
-.Vb 2
-\& anydb get key > file
-\& anydb get key \-o file
-.Ve
-.PP
-If the value is encrypted, you will be asked for the passphrase to
-decrypt it. If the environment variable \f(CW\*(C`ANYDB_PASSWORD\*(C'\fR is set, its
-value will be used instead.
-.PP
-There are different output modes you can choose from: simple, wide and
-json. The \*(L"simple\*(R" mode is the default one, it just prints the value
-as is. The \*(L"wide\*(R" mode prints a tabular output similar to the \fBlist\fR
-subcommand, see there for more details. The options \f(CW\*(C`\-n\*(C'\fR and \f(CW\*(C`\-N\*(C'\fR
-have the same meaning as in the list command. The \*(L"json\*(R" mode prints
-the raw \s-1JSON\s0 representation of the whole database entry. Decryption
-will only take place in \*(L"simple\*(R" and \*(L"json\*(R" mode. The \*(L"template\*(R" mode
-provides the most flexibily, it is detailed in the section
-\&\s-1TEMPLATES\s0.
-.SS "list"
-.IX Subsection "list"
-The \fBlist\fR subcommand displays a list of all database entries.
-.PP
-Usage:
-.PP
-.Vb 2
-\& Usage:
-\& anydb list [ | \-t ] [\-m ] [\-nNif] [\-T ] [flags]
-\&
-\& Aliases:
-\& list, ls, /, find, search
-\&
-\& Flags:
-\& \-i, \-\-case\-insensitive filter case insensitive
-\& \-h, \-\-help help for list
-\& \-m, \-\-mode string output format (table|wide|json|template), wide is a verbose table. (default \*(Aqtable\*(Aq)
-\& \-n, \-\-no\-headers omit headers in tables
-\& \-N, \-\-no\-human do not translate to human readable values
-\& \-s, \-\-search\-fulltext perform a full text search
-\& \-t, \-\-tags stringArray tags, multiple allowed
-\& \-T, \-\-template string go template for \*(Aq\-m template\*(Aq
-\& \-l, \-\-wide\-output output mode: wide
-.Ve
-.PP
-In its simplest form \- without any options \- , the \fBlist\fR command
-just prints all keys with their values to \s-1STDOUT.\s0 Values are being
-truncated to maximum of 60 characters, that is, multiline values are
-not completely shown in order to keep the tabular view readable.
-.PP
-To get more informations about each entry, use the \f(CW\*(C`\-o wide\*(C'\fR or \f(CW\*(C`\-l\*(C'\fR
-option. In addition to the key and value also the size, update
-timestamp and tags will be printed. Time and size values are converted
-into a human readable form, you can suppress this behavior with the
-\&\f(CW\*(C`\-N\*(C'\fR option. You may omit the headers using the option \f(CW\*(C`\-n\*(C'\fR
-.PP
-Sometimes you might want to filter the list of entries. Either because
-your database grew too large or because you're searching for
-something. In that case you have two options: You may supply one or
-more tags or provide a filter regexp. To filter by tag, do:
-.PP
-.Vb 3
-\& anydb list \-t tag1
-\& anydb list \-t tag1,tag2
-\& anydb list \-t tag1 \-t tag2
-.Ve
-.PP
-To filter using a regular expression, do:
-.PP
-.Vb 1
-\& anydb list "foo.*bar"
-.Ve
-.PP
-Regular expressions follow the golang \fBre2\fR syntax. For more details
-about the syntax, refer to
-. Please note, that this
-regexp dialect is not \s-1PCRE\s0 compatible, but supports most of its
-features.
-.PP
-If you want to search case insensitive, add the option \f(CW\*(C`\-i\*(C'\fR.
-.PP
-By default anydb only searches through the keys. If you want to search
-through the values as well, then use the \f(CW\*(C`\-s\*(C'\fR option, which enables
-full-text search.
-.PP
-You can \- as with the \fBget\fR command \- use other output modes. The
-default mode is \*(L"table\*(R". The \*(L"wide\*(R" mode is, as already mentioned, a
-more detailed table. Also supported is \*(L"json\*(R" mode and \*(L"template\*(R"
-mode. For details about using templates see \s-1TEMPLATES\s0.
-.SS "del"
-.IX Subsection "del"
-Use the \fBdel\fR command to delete database entries.
-.PP
-Usage:
-.PP
-.Vb 2
-\& Usage:
-\& anydb del [flags]
-\&
-\& Aliases:
-\& del, d, rm
-\&
-\& Flags:
-\& \-h, \-\-help help for del
-.Ve
-.PP
-The subcommand \fBdel\fR does not provide any further options, it just
-deletes the entry referred to by the given key. No questions are being
-asked.
-.SS "edit"
-.IX Subsection "edit"
-The \fBedit\fR command makes it easier to modify larger entries.
-.PP
-Usage:
-.PP
-.Vb 2
-\& Usage:
-\& anydb edit [flags]
-\&
-\& Aliases:
-\& edit, modify, mod, ed, vi
-\&
-\& Flags:
-\& \-h, \-\-help help for edit
-.Ve
-.PP
-The subcommand \fBedit\fR does not provide any further options. It
-works like this:
-.IP "1. Write the value info a temporary file." 4
-.IX Item "1. Write the value info a temporary file."
-.PD 0
-.IP "2. Execute the editor (which one, see below!) with that file." 4
-.IX Item "2. Execute the editor (which one, see below!) with that file."
-.IP "3. Now you can edit the file and save+close it when done." 4
-.IX Item "3. Now you can edit the file and save+close it when done."
-.IP "4. Anydb picks up the file and if the content has changed, puts its value into the \s-1DB.\s0" 4
-.IX Item "4. Anydb picks up the file and if the content has changed, puts its value into the DB."
-.PD
-.PP
-By default anydb executes the \f(CW\*(C`vi\*(C'\fR command. You can modify this
-behavior by setting the environment variable \f(CW\*(C`EDITOR\*(C'\fR appropriately.
-.PP
-Please note, that this does not work with binary content!
-.SS "export"
-.IX Subsection "export"
-Since the bbolt database file is not portable across platforms (it is
-bound to the endianess of the \s-1CPU\s0 it was being created on), you might
-want to create a backup file of your database. You can do this with
-the \fBexport\fR subcommand.
-.PP
-Usage:
-.PP
-.Vb 2
-\& Usage:
-\& anydb export \-o [flags]
-\&
-\& Aliases:
-\& export, dump, backup
-\&
-\& Flags:
-\& \-h, \-\-help help for export
-\& \-o, \-\-output string output to file
-.Ve
-.PP
-The database dump is a \s-1JSON\s0 representation of the whole database and
-will be printed to the file specified with the \f(CW\*(C`\-o\*(C'\fR option. If you
-specify \*(L"\-\*(R" as the filename, it will be written to \s-1STDIN.\s0
-.PP
-.Vb 2
-\& anydb export \-o dump.json
-\& anydb export \-o \- > dump.json
-.Ve
-.PP
-Please note, that encrypted values will not be decrypted. This might
-change in a future version of anydb.
-.SS "import"
-.IX Subsection "import"
-The \fBimport\fR subcommand can be used to restore a database from a \s-1JSON\s0
-dump.
-.PP
-Usage:
-.PP
-.Vb 2
-\& Usage:
-\& anydb import \-i [flags]
-\&
-\& Aliases:
-\& import, restore
-\&
-\& Flags:
-\& \-r, \-\-file string Filename or \- for STDIN
-\& \-h, \-\-help help for import
-\& \-t, \-\-tags stringArray tags, multiple allowed
-.Ve
-.PP
-The \f(CW\*(C`import\*(C'\fR subcommand reads the \s-1JSON\s0 contents from
-the file specified with the \f(CW\*(C`\-i\*(C'\fR option. If you specify \*(L"\-\*(R" as the
-filename, it will be read from \s-1STDIN.\s0
-.PP
-.Vb 3
-\& anydb import \-i \- < dump.json
-\& anydb import \-i dump.json
-\& cat dump.json | anydb import \-i \-
-.Ve
-.PP
-If there is already a database, it will be saved by appending a
-timestamp and a new database with the contents of the dump will be
-created.
-.SS "serve"
-.IX Subsection "serve"
-Anydb provides a RESTful \s-1API,\s0 which you can use to manage the database
-from somewhere else. The \s-1API\s0 does not provide any authentication or
-any other security measures, so better only use it on localhost.
-.PP
-Usage:
-.PP
-.Vb 2
-\& Usage:
-\& anydb serve [\-l host:port] [flags]
-\&
-\& Flags:
-\& \-h, \-\-help help for serve
-\& \-l, \-\-listen string host:port (default "localhost:8787")
-.Ve
-.PP
-To start the listener, just execute the \fBserve\fR subcommand. You can
-tweak the ip address and tcp port using the \f(CW\*(C`\-l\*(C'\fR option. The listener
-will not fork and run in the foreground. Logs are being printed to
-\&\s-1STDOUT\s0 as long as the listener runs.
-.PP
-For more details about the \s-1API,\s0 please see the \*(L"\s-1REST API\*(R"\s0 section.
-.SS "info"
-.IX Subsection "info"
-The \fBinfo\fR subcommand shows you some information about your current
-database.
-.PP
-Usage:
-.PP
-.Vb 2
-\& Usage:
-\& anydb info [flags]
-\&
-\& Flags:
-\& \-h, \-\-help help for info
-\& \-N, \-\-no\-human do not translate to human readable values
-.Ve
-.PP
-Data being shown are: filename and size, number of keys per bucket. If
-you supply the \f(CW\*(C`\-d\*(C'\fR option (debug), some bbolt internals are being
-displayed as well.
-.SS "man"
-.IX Subsection "man"
-The \fBman\fR subcommand shows an unformatted text variant of the manual
-page (which are currently reading).
-.PP
-Usage:
-.PP
-.Vb 2
-\& Usage:
-\& anydb man [flags]
-\&
-\& Flags:
-\& \-h, \-\-help help for man
-.Ve
-.PP
-The manual is being piped into the \f(CW\*(C`more\*(C'\fR command, which is being
-expected to exist according to the \s-1POSIX\s0 standard on all supported
-unix platforms. It might not work on Windows.
-.SH "TEMPLATES"
-.IX Header "TEMPLATES"
-The \fBget\fR and \fBlist\fR commands support a template feature, which is
-very handy to create you own kind of formatting. The template syntax
-being used is the \s-1GO\s0 template language, refer to
- for details.
-.PP
-Each template operates on one or more entries, no loop construct is
-required, the template provided applies to every matching entry
-separatley.
-.PP
-The following template variables can be used:
-.IP "\fB.Key\fR \- string" 4
-.IX Item ".Key - string"
-.PD 0
-.IP "\fB.Value\fR \- string" 4
-.IX Item ".Value - string"
-.IP "\fB.Bin\fR \- []byte" 4
-.IX Item ".Bin - []byte"
-.IP "\fB.Created\fR \- timestamp.Time" 4
-.IX Item ".Created - timestamp.Time"
-.PD
-To retrieve a string representation of the timestamp, use \f(CW\*(C`.Created.AsTime\*(C'\fR.
-If you need a unix timestamp since epoch, use \f(CW\*(C`.Created.Unix\*(C'\fR.
-.IP "\fB.Tags\fR \- []string" 4
-.IX Item ".Tags - []string"
-.PD 0
-.IP "\fB.Encrypted\fR bool" 4
-.IX Item ".Encrypted bool"
-.PD
-.PP
-Prepend a single dot (\*(L".\*(R") before each variable name.
-.PP
-Here are some examples how to use the feature:
-.PP
-Only show the keys of all entries:
-.PP
-.Vb 1
-\& anydb list \-m template \-T "{{ .Key }}"
-.Ve
-.PP
-Format the list in a way so that is possible to evaluate it in a
-shell:
-.PP
-.Vb 2
-\& eval $(anydb get foo \-m template \-T "key=\*(Aq{{ .Key }}\*(Aq value=\*(Aq{{ .Value }}\*(Aq ts=\*(Aq{{ .Created.AsTime}}\*(Aq")
-\& echo "Key: $key, Value: $value, When: $ts"
-.Ve
-.PP
-Print the values in \s-1CSV\s0 format \s-1ONLY\s0 if they have some tag:
-.PP
-.Vb 1
-\& anydb list \-m template \-T "{{ if .Tags }}{{ .Key }},{{ .Value }},{{ .Created.AsTime}}{{ end }}"
-.Ve
-.SH "CONFIGURATION"
-.IX Header "CONFIGURATION"
-Anydb looks at the following locations for a configuration file, in
-that order:
-.ie n .IP """$HOME/.config/anydb/anydb.toml""" 4
-.el .IP "\f(CW$HOME/.config/anydb/anydb.toml\fR" 4
-.IX Item "$HOME/.config/anydb/anydb.toml"
-.PD 0
-.ie n .IP """$HOME/.anydb.toml""" 4
-.el .IP "\f(CW$HOME/.anydb.toml\fR" 4
-.IX Item "$HOME/.anydb.toml"
-.ie n .IP """anydb.toml"" in the current directory" 4
-.el .IP "\f(CWanydb.toml\fR in the current directory" 4
-.IX Item "anydb.toml in the current directory"
-.ie n .IP "or specify one using ""\-c""" 4
-.el .IP "or specify one using \f(CW\-c\fR" 4
-.IX Item "or specify one using -c"
-.PD
-.PP
-The configuration format uses the \s-1TOML\s0 language, refer to
- for more details. The key names correspond to
-the commandline options in most cases.
-.PP
-Configuration follows a certain precedence: the files are tried to be
-read in the given order, followed by commandline options. That is, the
-last configuration file wins, unless the user provides a commandline
-option, then this setting will be taken.
-.PP
-A complete configuration file might look like this:
-.PP
-.Vb 7
-\& # defaults
-\& dbfile = "~/.config/anydb/default.db"
-\& dbbucket = "data"
-\& noheaders = false
-\& nohumanize = false
-\& encrypt = false
-\& listen = "localhost:8787"
-\&
-\& # different setups for different buckets
-\& [buckets.data]
-\& encrypt = true
-\&
-\& [buckets.test]
-\& encrypt = false
-.Ve
-.PP
-Under normal circumstances you don't need a configuration
-file. However, if you want to use different buckets, then this might
-be a handy option. Buckets are being configured in ini-style with the
-term \*(L"bucket.\*(R" followed by the bucket name. In the example above we
-enable encryption for the default bucket \*(L"data\*(R" and disable it for a
-bucket \*(L"test\*(R". To use different buckets, use the \f(CW\*(C`\-b\*(C'\fR option.
-.SH "REST API"
-.IX Header "REST API"
-The subcommand \fBserve\fR starts a simple \s-1HTTP\s0 service, which responds
-to RESTful \s-1HTTP\s0 requests. The listener responds to all requests with a
-\&\s-1JSON\s0 encoded response. The response contains the status and the
-content \- if any \- of the requested resource.
-.PP
-The following requests are supported:
-.IP "\fB\s-1GET\s0 /anydb/v1/\fR" 4
-.IX Item "GET /anydb/v1/"
-Returns a \s-1JSON\s0 encoded list of all entries.
-.IP "\fB\s-1GET\s0 /anydb/v1/key\fR" 4
-.IX Item "GET /anydb/v1/key"
-Returns the \s-1JSON\s0 encoded entry, if found.
-.IP "\fB\s-1PUT\s0 /anydb/v1/\fR" 4
-.IX Item "PUT /anydb/v1/"
-Create an entry. Expects a \s-1JSON\s0 encoded request object in \s-1POST\s0 data.
-.IP "\fB\s-1DELETE\s0 /anydb/v1/key\fR" 4
-.IX Item "DELETE /anydb/v1/key"
-Delete an entry.
-.PP
-Some curl example calls to the \s-1API:\s0
-.PP
-Post a new key:
- curl \-X \s-1PUT\s0 localhost:8787/anydb/v1/ \e
- \-H 'Content\-Type: application/json' \e
- \-d '{\*(L"key\*(R":\*(L"foo\*(R",\*(L"val\*(R":\*(L"bar\*(R"}'
-.PP
-Retrieve the value:
-.PP
-.Vb 1
-\& curl localhost:8787/anydb/v1/foo
-.Ve
-.PP
-List all keys:
-.PP
-.Vb 1
-\& curl localhost:8787/anydb/v1/
-.Ve
-.SH "BUGS"
-.IX Header "BUGS"
-In order to report a bug, unexpected behavior, feature requests
-or to submit a patch, please open an issue on github:
-.
-.PP
-Please repeat the failing command with debugging enabled \f(CW\*(C`\-d\*(C'\fR and
-include the output in the issue.
-.SH "LIMITATIONS"
-.IX Header "LIMITATIONS"
-The \s-1REST API\s0 list request doesn't provide any filtering capabilities yet.
-.SH "LICENSE"
-.IX Header "LICENSE"
-This software is licensed under the \s-1GNU GENERAL PUBLIC LICENSE\s0 version 3.
-.PP
-Copyright (c) 2024 by Thomas von Dein
-.SH "AUTHORS"
-.IX Header "AUTHORS"
-Thomas von Dein \fBtom \s-1AT\s0 vondein \s-1DOT\s0 org\fR
diff --git a/anydb.pod b/anydb.pod
deleted file mode 100644
index 6da0378..0000000
--- a/anydb.pod
+++ /dev/null
@@ -1,606 +0,0 @@
-=head1 anydb
-
-anydb - a personal key value store
-
-=head1 SYNOPSIS
-
- Usage:
- anydb [options] [flags]
- anydb [command]
-
- Available Commands:
- completion Generate the autocompletion script for the specified shell
- del Delete key
- edit Edit a key
- export Export database to json
- get Retrieve value for a key
- help Help about any command
- import Import database dump
- info info
- list List database contents
- man show manual page
- serve run REST API listener
- set Insert key/value pair
-
- Flags:
- -b, --bucket string use other bucket (default: data) (default "data")
- -c, --config string toml config file
- -f, --dbfile string DB file to use (default "/home/scip/.config/anydb/default.db")
- -d, --debug Enable debugging
- -h, --help help for anydb
- -v, --version Print program version
-
- Use "anydb [command] --help" for more information about a command.
-
-
-=head1 DESCRIPTION
-
-Anydb is a commandline personal key value store, it is simple to use
-and can be used to store anything you'd like, even binary files
-etc. It uses a key/value store (bbolt) in your home directory.
-
-The tool provides a number of subcommands to use it, there are global
-options and each subcommand has its own set of options.
-
-=head1 GLOBAL OPTIONS
-
-=over
-
-=item C<-f, --dbfile filename>
-
-The default location of your databas is
-C<$HOME/.config/anydb/default.db>. You can change this with the C<-f>
-option.
-
-=item C<-b, --bucket name>
-
-Data in a bbolt key-value-store are managed in so called
-buckets. These are kind of namespaces, where each key must be
-unique. However, a database may contain more than one bucket.
-
-By default anydb uses a bucket named "data", but you can change this
-using the option C<-b>.
-
-Buckets can be configured to always encrypt values, see L.
-
-=item C<-c, --config filename>
-
-Under normal circumstances you don't need a configuration file. But if
-you want, you can provide one using the option C<-c>.
-
-Anydb looks for a couple of default locations for a config file. You
-only need this option if you want to supply a configuration on a
-non-standard location. See L for more details.
-
-=item C<-d, --debug>
-
-Enable debug output.
-
-=item C<-h, --help>
-
-Show the usage of anydb.
-
-=item C<-v, --version>
-
-Show the program version.
-
-=back
-
-All of these options can be used with subcommands as well.
-
-=head1 SUBCOMMANDS
-
-=head2 completion
-
-The B command can be used to setup completion for
-anydb. Just put something like this into your shell's configuration
-file:
-
- source <(anydb completion bash)
-
-If you use another shell, specify it instead of bash, of course.
-
-=head2 set
-
-The B command is being used to insert or update a key-value pair.
-
-Usage:
-
- Usage:
- anydb set [ | -r ] [-t ] [flags]
-
- Aliases:
- set, add, s, +
-
- Flags:
- -e, --encrypt encrypt value
- -r, --file string Filename or - for STDIN
- -h, --help help for set
- -t, --tags tag,tag,... tags, multiple allowed
-
-The standard way to insert a new entry is really simple:
-
- anydb set key value
-
-If you don't specify a value, anydb expects you to feed it some data
-via STDIN. For example:
-
- anydb set key < file
-
-You might as well specify a file directly using the C<-f> option:
-
- anydb set key -f file
-
-Values can be encrypted using B when you specify the
-C<-e> option. Anydb will ask you interactively for a passphrase. You
-can also provide the passphrase using the environment variable
-C. To encrypt the value, a cryptographically secure
-key will be derived from the passphrase using the ArgonID2
-algorithm. Each value can be encrypted with another passphrase. So,
-the database itself is not encrypted, just the values.
-
-You can supply tags by using the option C<-t>. Multiple tags can be
-provided either by separating them with a comma or by using multiple
-C<-t> parameters:
-
- anydb set key value -t tag1,tag2
- anydb set key value -t tag1 -t tag2
-
-You can later filter entries by tag or by a combination of tags.
-
-To edit or modify an entry, just use the B command with the same
-key, the value in the database will be overwritten with the new
-value. An alternative option is the B command, see below.
-
-=head2 get
-
-To retrieve the value of a key, use the B subcommand.
-
-Usage:
-
- Usage:
- anydb get [-o ] [-m ] [-n -N] [-T ] [flags]
-
- Aliases:
- get, show, g, .
-
- Flags:
- -h, --help help for get
- -m, --mode string output format (simple|wide|json|template) (default 'simple')
- -n, --no-headers omit headers in tables
- -N, --no-human do not translate to human readable values
- -o, --output string output value to file (ignores -m)
- -T, --template string go template for '-m template'
-
-In its simplest form you just call the B subcommand with the key
-you want to have the value for. The value is being printed to STDOUT
-by default:
-
- anydb get key
-
-If the value is binary content, it will not just being printed. In
-those cases you need to either redirect output into a file or use the
-option C<-o> to write to a file:
-
- anydb get key > file
- anydb get key -o file
-
-If the value is encrypted, you will be asked for the passphrase to
-decrypt it. If the environment variable C is set, its
-value will be used instead.
-
-There are different output modes you can choose from: simple, wide and
-json. The "simple" mode is the default one, it just prints the value
-as is. The "wide" mode prints a tabular output similar to the B
-subcommand, see there for more details. The options C<-n> and C<-N>
-have the same meaning as in the list command. The "json" mode prints
-the raw JSON representation of the whole database entry. Decryption
-will only take place in "simple" and "json" mode. The "template" mode
-provides the most flexibily, it is detailed in the section
-L.
-
-=head2 list
-
-The B subcommand displays a list of all database entries.
-
-Usage:
-
- Usage:
- anydb list [ | -t ] [-m ] [-nNif] [-T ] [flags]
-
- Aliases:
- list, ls, /, find, search
-
- Flags:
- -i, --case-insensitive filter case insensitive
- -h, --help help for list
- -m, --mode string output format (table|wide|json|template), wide is a verbose table. (default 'table')
- -n, --no-headers omit headers in tables
- -N, --no-human do not translate to human readable values
- -s, --search-fulltext perform a full text search
- -t, --tags stringArray tags, multiple allowed
- -T, --template string go template for '-m template'
- -l, --wide-output output mode: wide
-
-In its simplest form - without any options - , the B command
-just prints all keys with their values to STDOUT. Values are being
-truncated to maximum of 60 characters, that is, multiline values are
-not completely shown in order to keep the tabular view readable.
-
-To get more informations about each entry, use the C<-o wide> or C<-l>
-option. In addition to the key and value also the size, update
-timestamp and tags will be printed. Time and size values are converted
-into a human readable form, you can suppress this behavior with the
-C<-N> option. You may omit the headers using the option C<-n>
-
-Sometimes you might want to filter the list of entries. Either because
-your database grew too large or because you're searching for
-something. In that case you have two options: You may supply one or
-more tags or provide a filter regexp. To filter by tag, do:
-
- anydb list -t tag1
- anydb list -t tag1,tag2
- anydb list -t tag1 -t tag2
-
-To filter using a regular expression, do:
-
- anydb list "foo.*bar"
-
-Regular expressions follow the golang B syntax. For more details
-about the syntax, refer to
-L. Please note, that this
-regexp dialect is not PCRE compatible, but supports most of its
-features.
-
-If you want to search case insensitive, add the option C<-i>.
-
-By default anydb only searches through the keys. If you want to search
-through the values as well, then use the C<-s> option, which enables
-full-text search.
-
-You can - as with the B command - use other output modes. The
-default mode is "table". The "wide" mode is, as already mentioned, a
-more detailed table. Also supported is "json" mode and "template"
-mode. For details about using templates see L.
-
-=head2 del
-
-Use the B command to delete database entries.
-
-Usage:
-
- Usage:
- anydb del [flags]
-
- Aliases:
- del, d, rm
-
- Flags:
- -h, --help help for del
-
-The subcommand B does not provide any further options, it just
-deletes the entry referred to by the given key. No questions are being
-asked.
-
-=head2 edit
-
-The B command makes it easier to modify larger entries.
-
-Usage:
-
- Usage:
- anydb edit [flags]
-
- Aliases:
- edit, modify, mod, ed, vi
-
- Flags:
- -h, --help help for edit
-
-The subcommand B does not provide any further options. It
-works like this:
-
-=over
-
-=item 1. Write the value info a temporary file.
-
-=item 2. Execute the editor (which one, see below!) with that file.
-
-=item 3. Now you can edit the file and save+close it when done.
-
-=item 4. Anydb picks up the file and if the content has changed, puts its value into the DB.
-
-=back
-
-By default anydb executes the C command. You can modify this
-behavior by setting the environment variable C appropriately.
-
-Please note, that this does not work with binary content!
-
-=head2 export
-
-Since the bbolt database file is not portable across platforms (it is
-bound to the endianess of the CPU it was being created on), you might
-want to create a backup file of your database. You can do this with
-the B subcommand.
-
-Usage:
-
- Usage:
- anydb export -o [flags]
-
- Aliases:
- export, dump, backup
-
- Flags:
- -h, --help help for export
- -o, --output string output to file
-
-The database dump is a JSON representation of the whole database and
-will be printed to the file specified with the C<-o> option. If you
-specify "-" as the filename, it will be written to STDIN.
-
- anydb export -o dump.json
- anydb export -o - > dump.json
-
-Please note, that encrypted values will not be decrypted. This might
-change in a future version of anydb.
-
-=head2 import
-
-The B subcommand can be used to restore a database from a JSON
-dump.
-
-Usage:
-
- Usage:
- anydb import -i [flags]
-
- Aliases:
- import, restore
-
- Flags:
- -r, --file string Filename or - for STDIN
- -h, --help help for import
- -t, --tags stringArray tags, multiple allowed
-
-The C subcommand reads the JSON contents from
-the file specified with the C<-i> option. If you specify "-" as the
-filename, it will be read from STDIN.
-
- anydb import -i - < dump.json
- anydb import -i dump.json
- cat dump.json | anydb import -i -
-
-If there is already a database, it will be saved by appending a
-timestamp and a new database with the contents of the dump will be
-created.
-
-=head2 serve
-
-Anydb provides a RESTful API, which you can use to manage the database
-from somewhere else. The API does not provide any authentication or
-any other security measures, so better only use it on localhost.
-
-Usage:
-
- Usage:
- anydb serve [-l host:port] [flags]
-
- Flags:
- -h, --help help for serve
- -l, --listen string host:port (default "localhost:8787")
-
-To start the listener, just execute the B subcommand. You can
-tweak the ip address and tcp port using the C<-l> option. The listener
-will not fork and run in the foreground. Logs are being printed to
-STDOUT as long as the listener runs.
-
-For more details about the API, please see the L section.
-
-=head2 info
-
-The B subcommand shows you some information about your current
-database.
-
-Usage:
-
- Usage:
- anydb info [flags]
-
- Flags:
- -h, --help help for info
- -N, --no-human do not translate to human readable values
-
-Data being shown are: filename and size, number of keys per bucket. If
-you supply the C<-d> option (debug), some bbolt internals are being
-displayed as well.
-
-=head2 man
-
-The B subcommand shows an unformatted text variant of the manual
-page (which are currently reading).
-
-Usage:
-
- Usage:
- anydb man [flags]
-
- Flags:
- -h, --help help for man
-
-The manual is being piped into the C command, which is being
-expected to exist according to the POSIX standard on all supported
-unix platforms. It might not work on Windows.
-
-=head1 TEMPLATES
-
-The B and B commands support a template feature, which is
-very handy to create you own kind of formatting. The template syntax
-being used is the GO template language, refer to
-L for details.
-
-Each template operates on one or more entries, no loop construct is
-required, the template provided applies to every matching entry
-separatley.
-
-The following template variables can be used:
-
-=over
-
-=item B<.Key> - string
-
-=item B<.Value> - string
-
-=item B<.Bin> - []byte
-
-=item B<.Created> - timestamp.Time
-
-To retrieve a string representation of the timestamp, use C<.Created.AsTime>.
-If you need a unix timestamp since epoch, use C<.Created.Unix>.
-
-=item B<.Tags> - []string
-
-=item B<.Encrypted> bool
-
-=back
-
-Prepend a single dot (".") before each variable name.
-
-Here are some examples how to use the feature:
-
-Only show the keys of all entries:
-
- anydb list -m template -T "{{ .Key }}"
-
-Format the list in a way so that is possible to evaluate it in a
-shell:
-
- eval $(anydb get foo -m template -T "key='{{ .Key }}' value='{{ .Value }}' ts='{{ .Created.AsTime}}'")
- echo "Key: $key, Value: $value, When: $ts"
-
-Print the values in CSV format ONLY if they have some tag:
-
- anydb list -m template -T "{{ if .Tags }}{{ .Key }},{{ .Value }},{{ .Created.AsTime}}{{ end }}"
-
-
-=head1 CONFIGURATION
-
-Anydb looks at the following locations for a configuration file, in
-that order:
-
-=over
-
-=item C<$HOME/.config/anydb/anydb.toml>
-
-=item C<$HOME/.anydb.toml>
-
-=item C in the current directory
-
-=item or specify one using C<-c>
-
-=back
-
-The configuration format uses the TOML language, refer to
-L for more details. The key names correspond to
-the commandline options in most cases.
-
-Configuration follows a certain precedence: the files are tried to be
-read in the given order, followed by commandline options. That is, the
-last configuration file wins, unless the user provides a commandline
-option, then this setting will be taken.
-
-A complete configuration file might look like this:
-
- # defaults
- dbfile = "~/.config/anydb/default.db"
- dbbucket = "data"
- noheaders = false
- nohumanize = false
- encrypt = false
- listen = "localhost:8787"
-
- # different setups for different buckets
- [buckets.data]
- encrypt = true
-
- [buckets.test]
- encrypt = false
-
-Under normal circumstances you don't need a configuration
-file. However, if you want to use different buckets, then this might
-be a handy option. Buckets are being configured in ini-style with the
-term "bucket." followed by the bucket name. In the example above we
-enable encryption for the default bucket "data" and disable it for a
-bucket "test". To use different buckets, use the C<-b> option.
-
-
-=head1 REST API
-
-The subcommand B starts a simple HTTP service, which responds
-to RESTful HTTP requests. The listener responds to all requests with a
-JSON encoded response. The response contains the status and the
-content - if any - of the requested resource.
-
-The following requests are supported:
-
-=over
-
-=item B
-
-Returns a JSON encoded list of all entries.
-
-=item B
-
-Returns the JSON encoded entry, if found.
-
-=item B
-
-Create an entry. Expects a JSON encoded request object in POST data.
-
-=item B
-
-Delete an entry.
-
-=back
-
-Some curl example calls to the API:
-
-Post a new key:
- curl -X PUT localhost:8787/anydb/v1/ \
- -H 'Content-Type: application/json' \
- -d '{"key":"foo","val":"bar"}'
-
-Retrieve the value:
-
- curl localhost:8787/anydb/v1/foo
-
-List all keys:
-
- curl localhost:8787/anydb/v1/
-
-=head1 BUGS
-
-In order to report a bug, unexpected behavior, feature requests
-or to submit a patch, please open an issue on github:
-L.
-
-Please repeat the failing command with debugging enabled C<-d> and
-include the output in the issue.
-
-=head1 LIMITATIONS
-
-The REST API list request doesn't provide any filtering capabilities yet.
-
-=head1 LICENSE
-
-This software is licensed under the GNU GENERAL PUBLIC LICENSE version 3.
-
-Copyright (c) 2024 by Thomas von Dein
-
-=head1 AUTHORS
-
-Thomas von Dein B
-
-
-=cut
diff --git a/app/attr.go b/app/attr.go
deleted file mode 100644
index ecc08bf..0000000
--- a/app/attr.go
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
-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 .
-*/
-package app
-
-import (
- "fmt"
- "io"
- "os"
- "strings"
- "unicode/utf8"
-)
-
-type DbAttr struct {
- Key string
- Preview string
- Val []byte
- Args []string
- Tags []string
- File string
- Encrypted 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
-// text according to flags, lowercase key
-func (attr *DbAttr) ParseKV() error {
- attr.Key = strings.ToLower(attr.Args[0])
-
- switch len(attr.Args) {
- case 1:
- // 1 arg = key + read from file or stdin
- if attr.File == "" {
- attr.File = "-"
- }
- case 2:
- attr.Val = []byte(attr.Args[1])
-
- if attr.Args[1] == "-" {
- attr.File = "-"
- }
- }
-
- if attr.File != "" {
- if err := attr.GetFileValue(); err != nil {
- return err
- }
- }
-
- switch {
- case attr.Binary:
- attr.Preview = ""
- case attr.Encrypted:
- attr.Preview = ""
- default:
- 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)
- }
- }
-
- return nil
-}
-
-func (attr *DbAttr) GetFileValue() error {
- var fd io.Reader
-
- if attr.File == "-" {
- stat, _ := os.Stdin.Stat()
- if (stat.Mode() & os.ModeCharDevice) == 0 {
- fd = os.Stdin
- }
- } else {
- filehandle, err := os.OpenFile(attr.File, os.O_RDONLY, 0600)
- if err != nil {
- return fmt.Errorf("failed to open file %s: %w", attr.File, err)
- }
-
- fd = filehandle
- }
-
- if fd != nil {
- // read from file or stdin pipe
- data, err := io.ReadAll(fd)
- if err != nil {
- return fmt.Errorf("failed to read from pipe: %w", err)
- }
-
- // poor man's text file test
- attr.Val = data
-
- if utf8.ValidString(string(data)) {
- attr.Binary = false
- } else {
- attr.Binary = true
- }
- } else {
- // read from console stdin
- var input string
- var data string
-
- for {
- _, err := fmt.Scanln(&input)
- if err != nil {
- break
- }
- data += input + "\n"
- }
-
- attr.Val = []byte(data)
- }
-
- return nil
-}
diff --git a/app/crypto.go b/app/crypto.go
deleted file mode 100644
index 75f0587..0000000
--- a/app/crypto.go
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
-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 .
-*/
-package app
-
-import (
- "crypto/rand"
- "errors"
- "fmt"
- "log/slog"
- "os"
- "syscall"
-
- "golang.org/x/crypto/argon2"
- "golang.org/x/crypto/chacha20poly1305"
- "golang.org/x/term"
-)
-
-const (
- ArgonMem uint32 = 64 * 1024
- ArgonIter uint32 = 5
- ArgonParallel uint8 = 2
- ArgonSaltLen int = 16
- ArgonKeyLen uint32 = 32
- B64SaltLen int = 16 //22
-)
-
-type Key struct {
- Salt []byte
- Key []byte
-}
-
-// called from interactive thread, hides input and returns clear text
-// password
-func AskForPassword() ([]byte, error) {
- fmt.Fprint(os.Stderr, "Password: ")
- pass, err := term.ReadPassword(int(syscall.Stdin))
- if err != nil {
- return nil, fmt.Errorf("failed to read password: %w", err)
- }
-
- fmt.Fprintln(os.Stderr)
-
- return pass, nil
-}
-
-// We're using the Argon2id key derivation algorithm to derive a
-// secure key from the given password. This is important, because
-// users might use unsecure passwords. The resulting encrypted data
-// might of course easily be decrypted using brute force methods if a
-// weak password was used, but that would cost, because of the key
-// derivation. It does several rounds of hash calculations which take
-// a considerable amount of cpu time. For our legal user that's no
-// problem because it's being executed only once, but an attacker has
-// to do it in a forever loop, which will take a lot of time.
-func DeriveKey(password []byte, salt []byte) (*Key, error) {
- if salt == nil {
- // none given, new password
- newsalt, err := GetRandom(ArgonSaltLen, ArgonSaltLen)
- if err != nil {
- return nil, err
- }
-
- salt = newsalt
- }
-
- hash := argon2.IDKey(
- []byte(password), salt,
- ArgonIter,
- ArgonMem,
- ArgonParallel,
- ArgonKeyLen,
- )
-
- key := &Key{Key: hash, Salt: salt}
-
- slog.Debug("derived key", "key", string(key.Key), "salt", string(key.Salt))
-
- return key, nil
-}
-
-// Retrieve a random chunk of given size
-func GetRandom(size int, capacity int) ([]byte, error) {
- buf := make([]byte, size, capacity)
- _, err := rand.Read(buf)
- if err != nil {
- return nil, fmt.Errorf("failed to retrieve random bytes: %w", err)
- }
-
- return buf, nil
-}
-
-// Encrypt clear text given in attr using ChaCha20 and auhtenticate
-// using the mac Poly1305. The cipher text will be put into attr, thus
-// modifying it.
-//
-// The cipher text consists of:
-// password-salt) + (12 byte nonce + ciphertext + 16 byte mac)
-func Encrypt(pass []byte, attr *DbAttr) error {
- key, err := DeriveKey(pass, nil)
- if err != nil {
- return err
- }
-
- aead, err := chacha20poly1305.New(key.Key)
- if err != nil {
- return fmt.Errorf("failed to create AEAD cipher: %w", err)
- }
-
- total := aead.NonceSize() + len(attr.Val) + aead.Overhead()
-
- nonce, err := GetRandom(aead.NonceSize(), total)
- if err != nil {
- return err
- }
-
- cipher := aead.Seal(nonce, nonce, attr.Val, nil)
-
- attr.Val = key.Salt
- attr.Val = append(attr.Val, cipher...)
-
- attr.Encrypted = true
- attr.Preview = ""
-
- slog.Debug("encrypted attr", "salt", string(key.Salt), "cipher", string(attr.Val))
-
- return nil
-}
-
-// Do the reverse
-func Decrypt(pass []byte, cipherb []byte) ([]byte, error) {
- if len(cipherb) < B64SaltLen {
- return nil, fmt.Errorf("encrypted cipher block too small")
- }
-
- key, err := DeriveKey(pass, cipherb[0:B64SaltLen])
- if err != nil {
- return nil, err
- }
-
- cipher := cipherb[B64SaltLen:]
-
- aead, err := chacha20poly1305.New(key.Key)
- if err != nil {
- return nil, fmt.Errorf("failed to create AEAD cipher: %w", err)
- }
-
- if len(cipher) < aead.NonceSize() {
- return nil, errors.New("ciphertext too short")
- }
-
- nonce, ciphertext := cipher[:aead.NonceSize()], cipher[aead.NonceSize():]
-
- clear, err := aead.Open(nil, nonce, ciphertext, nil)
- if err != nil {
- return nil, err
- }
-
- slog.Debug("decrypted attr", "salt", string(key.Salt), "clear", string(clear))
-
- return clear, err
-}
diff --git a/app/db.go b/app/db.go
deleted file mode 100644
index 06720d6..0000000
--- a/app/db.go
+++ /dev/null
@@ -1,591 +0,0 @@
-/*
-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 .
-*/
-package app
-
-import (
- "encoding/json"
- "errors"
- "fmt"
- "log"
- "log/slog"
- "os"
- "path/filepath"
- "regexp"
- "strings"
- "time"
-
- common "github.com/tlinden/anydb/common"
-
- bolt "go.etcd.io/bbolt"
- "google.golang.org/protobuf/proto"
- timestamppb "google.golang.org/protobuf/types/known/timestamppb"
-)
-
-const MaxValueWidth int = 60
-
-type DB struct {
- Debug bool
- Dbfile string
- Bucket string
- DB *bolt.DB
-}
-
-type BucketInfo struct {
- Name string
- Keys int
- Size int
- Sequence uint64
- Stats bolt.BucketStats
-}
-
-type DbInfo struct {
- Buckets []BucketInfo
- Path string
-}
-
-type DbEntries []*DbEntry
-
-type DbTag struct {
- Keys []string `json:"key"`
-}
-
-func (entry *DbEntry) Taglist() string {
- return strings.Join(entry.Tags, ",")
-}
-
-const BucketData string = "data"
-
-func GetDbFile(file string) string {
- if file != "" {
- return file
- }
-
- file = os.Getenv("ANYDB_DB")
- if file != "" {
- return file
- }
-
- home, err := os.UserHomeDir()
- if err != nil {
- panic(err)
- }
-
- return filepath.Join(home, ".config", "anydb", "default.db")
-}
-
-func New(file string, bucket string, debug bool) (*DB, error) {
- return &DB{Debug: debug, Dbfile: file, Bucket: bucket}, nil
-}
-
-func (db *DB) Open() error {
- slog.Debug("opening DB", "dbfile", db.Dbfile)
-
- if _, err := os.Stat(filepath.Dir(db.Dbfile)); os.IsNotExist(err) {
- if err := os.MkdirAll(filepath.Dir(db.Dbfile), 0700); err != nil {
- return err
- }
- }
-
- var opts *bolt.Options
-
- if db.Debug {
- log := common.Slogger{Logger: slog.Default()}
- opts = &bolt.Options{Logger: log}
- }
-
- b, err := bolt.Open(db.Dbfile, 0600, opts)
- if err != nil {
- return fmt.Errorf("failed to open DB %s: %w", db.Dbfile, err)
- }
-
- db.DB = b
- return nil
-}
-
-func (db *DB) Close() {
- if err := db.DB.Close(); err != nil {
- log.Fatal(err)
- }
-}
-
-func (db *DB) List(attr *DbAttr, fulltext bool) (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 {
- // via cli
- 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 {
- root := tx.Bucket([]byte(db.Bucket))
- if root == nil {
- return nil
- }
-
- slog.Debug("opened root bucket", "root", root)
-
- bucket := root.Bucket([]byte("meta"))
- if bucket == nil {
- return nil
- }
-
- slog.Debug("opened buckets", "root", root, "data", bucket)
-
- 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)
- }
-
- 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 = string(vc)
- }
-
- var include bool
-
- switch {
- case filter != nil:
- if filter.MatchString(entry.Key) ||
- filter.MatchString(strings.Join(entry.Tags, " ")) {
- include = true
- }
-
- if !entry.Binary && !include && fulltext {
- 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
-}
-
-func (db *DB) Set(attr *DbAttr) error {
- if err := db.Open(); err != nil {
- return err
- }
- defer db.Close()
-
- entry := DbEntry{
- Key: attr.Key,
- Binary: attr.Binary,
- Tags: attr.Tags,
- Encrypted: attr.Encrypted,
- Created: timestamppb.Now(),
- Size: uint64(len(attr.Val)),
- Preview: attr.Preview,
- }
-
- // check if the entry already exists and if yes, check if it has
- // any tags. if so, we initialize our update struct with these
- // tags unless it has new tags configured.
- slog.Debug("+++ GET")
- oldentry, err := db.txGet(attr)
- if err != nil {
- if !strings.Contains(err.Error(), "no such key") {
- return err
- }
- }
-
- if oldentry != nil {
- if len(oldentry.Tags) > 0 && len(entry.Tags) == 0 {
- // initialize update entry with tags from old entry
- entry.Tags = oldentry.Tags
- }
- }
-
- slog.Debug("+++ MARSHAL")
- // marshall our data
- pbentry, err := proto.Marshal(&entry)
- if err != nil {
- return fmt.Errorf("failed to marshall protobuf: %w", err)
- }
-
- slog.Debug("+++ UPDATE")
- err = db.DB.Update(func(tx *bolt.Tx) error {
- // create root bucket
- root, err := tx.CreateBucketIfNotExists([]byte(db.Bucket))
- if err != nil {
- 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)
- }
-
- slog.Debug("opened/created buckets", "root", root, "data", bucket)
-
- // write meta data
- err = bucket.Put([]byte(entry.Key), []byte(pbentry))
- if err != nil {
- 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
- })
-
- if err != nil {
- return err
- }
-
- return nil
-}
-
-// internal DB getter, assumes db.DB has already been
-// opened successfully. Do NOT call this w/o valid
-// DB handle!
-func (db *DB) txGet(attr *DbAttr) (*DbEntry, error) {
- entry := DbEntry{}
-
- err := db.DB.View(func(tx *bolt.Tx) error {
- // root bucket
- root := tx.Bucket([]byte(db.Bucket))
- if root == nil {
- return nil
- }
-
- // get meta sub bucket
- bucket := root.Bucket([]byte("meta"))
- if bucket == nil {
- return nil
- }
-
- slog.Debug("opened buckets", "root", root, "data", bucket)
-
- // retrieve meta data
- pbentry := bucket.Get([]byte(attr.Key))
- if pbentry == nil {
- return fmt.Errorf("no such key: %s", attr.Key)
- }
-
- // put into struct
- if err := proto.Unmarshal(pbentry, &entry); err != nil {
- return fmt.Errorf("failed to unmarshal from protobuf: %w", err)
- }
-
- // get data sub bucket
- databucket := root.Bucket([]byte("data"))
- if databucket == nil {
- return fmt.Errorf("failed to retrieve data sub bucket")
- }
-
- // retrieve actual data value
- value := databucket.Get([]byte(attr.Key))
- if len(value) == 0 {
- return fmt.Errorf("no such key: %s", attr.Key)
- }
-
- // we need to make a copy of it, otherwise we'll get an
- // "unexpected fault address" error
- vc := make([]byte, len(value))
- copy(vc, value)
-
- entry.Value = string(vc)
-
- return nil
- })
-
- if err != nil {
- return nil, err
- }
-
- return &entry, nil
-}
-
-func (db *DB) Get(attr *DbAttr) (*DbEntry, error) {
- if err := db.Open(); err != nil {
- return nil, err
- }
- defer db.Close()
-
- entry, err := db.txGet(attr)
-
- if err != nil {
- return nil, fmt.Errorf("failed to read from DB: %w", err)
- }
-
- return entry, nil
-}
-
-func (db *DB) Del(attr *DbAttr) error {
- if err := db.Open(); err != nil {
- return err
- }
- defer db.Close()
-
- err := db.DB.Update(func(tx *bolt.Tx) error {
- // root bucket
- root := tx.Bucket([]byte(db.Bucket))
- if root == nil {
- return nil
- }
-
- // get data sub bucket
- bucket := root.Bucket([]byte("meta"))
- if bucket == nil {
- return nil
- }
-
- slog.Debug("opened buckets", "data", bucket)
-
- return bucket.Delete([]byte(attr.Key))
- })
-
- return err
-}
-
-func (db *DB) Import(attr *DbAttr) (string, error) {
- // open json file into attr.Val
- if err := attr.GetFileValue(); err != nil {
- return "", err
- }
-
- if len(attr.Val) == 0 {
- return "", errors.New("empty json file")
- }
-
- var entries DbEntries
- now := time.Now()
- newfile := db.Dbfile + now.Format("-02.01.2006T03:04.05")
-
- if err := json.Unmarshal([]byte(attr.Val), &entries); err != nil {
- return "", cleanError(newfile, fmt.Errorf("failed to unmarshal json: %w", err))
- }
-
- if fileExists(db.Dbfile) {
- // backup the old file
- err := os.Rename(db.Dbfile, newfile)
- if err != nil {
- return "", fmt.Errorf("failed to rename file %s to %s: %w", db.Dbfile, newfile, err)
- }
-
- }
-
- // should now be a new db file
- if err := db.Open(); err != nil {
- return "", cleanError(newfile, err)
- }
- defer db.Close()
-
- err := db.DB.Update(func(tx *bolt.Tx) error {
- // create root bucket
- root, err := tx.CreateBucketIfNotExists([]byte(db.Bucket))
- if err != nil {
- 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)
- }
-
- slog.Debug("opened buckets", "root", root, "data", bucket)
-
- for _, entry := range entries {
- pbentry, err := proto.Marshal(entry)
- if err != nil {
- return fmt.Errorf("failed to marshall protobuf: %w", err)
- }
-
- // write meta data
- err = bucket.Put([]byte(entry.Key), []byte(pbentry))
- if err != nil {
- 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), []byte(entry.Value))
- if err != nil {
- return fmt.Errorf("failed to insert data: %w", err)
- }
- }
-
- return nil
- })
-
- if err != nil {
- return "", cleanError(newfile, err)
- }
-
- return fmt.Sprintf("backed up database file to %s\nimported %d database entries\n",
- newfile, len(entries)), nil
-}
-
-func (db *DB) Info() (*DbInfo, error) {
- if err := db.Open(); err != nil {
- return nil, err
- }
- defer db.Close()
-
- info := &DbInfo{Path: db.Dbfile}
-
- err := db.DB.View(func(tx *bolt.Tx) error {
- err := tx.ForEach(func(name []byte, bucket *bolt.Bucket) error {
- stats := bucket.Stats()
-
- binfo := BucketInfo{
- Name: string(name),
- Sequence: bucket.Sequence(),
- Keys: stats.KeyN,
- Stats: bucket.Stats(),
- }
-
- err := bucket.ForEach(func(key, entry []byte) error {
- binfo.Size += len(entry) + len(key)
-
- return nil
- })
- if err != nil {
- return fmt.Errorf("failed to read keys: %w", err)
- }
-
- info.Buckets = append(info.Buckets, binfo)
- return nil
-
- })
-
- if err != nil {
- return fmt.Errorf("failed to read from DB: %w", err)
- }
-
- return nil
-
- })
-
- return info, err
-}
-
-func (db *DB) Getall(attr *DbAttr) (DbEntries, error) {
- if err := db.Open(); err != nil {
- return nil, err
- }
- defer db.Close()
-
- var entries DbEntries
-
- err := db.DB.View(func(tx *bolt.Tx) error {
- // root bucket
- root := tx.Bucket([]byte(db.Bucket))
- if root == nil {
- return nil
- }
-
- // get meta sub bucket
- bucket := root.Bucket([]byte("meta"))
- if bucket == nil {
- return nil
- }
-
- // get data sub bucket
- databucket := root.Bucket([]byte("data"))
- if databucket == nil {
- return fmt.Errorf("failed to retrieve data sub bucket")
- }
-
- slog.Debug("opened buckets", "root", root, "data", bucket)
-
- // iterate over all db entries in meta 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)
- }
-
- // retrieve the value from the data sub bucket
- value := databucket.Get([]byte(entry.Key))
-
- // we need to make a copy of it, otherwise we'll get an
- // "unexpected fault address" error
- vc := make([]byte, len(value))
- copy(vc, value)
-
- entry.Value = string(vc)
- entries = append(entries, &entry)
-
- return nil
- })
-
- return err
- })
- return entries, err
-}
diff --git a/app/dbentry.pb.go b/app/dbentry.pb.go
deleted file mode 100644
index 6bf1417..0000000
--- a/app/dbentry.pb.go
+++ /dev/null
@@ -1,210 +0,0 @@
-// -*-c++-*-
-
-// Code generated by protoc-gen-go. DO NOT EDIT.
-// versions:
-// protoc-gen-go v1.36.5
-// protoc v4.24.4
-// source: app/dbentry.proto
-
-package app
-
-import (
- protoreflect "google.golang.org/protobuf/reflect/protoreflect"
- protoimpl "google.golang.org/protobuf/runtime/protoimpl"
- timestamppb "google.golang.org/protobuf/types/known/timestamppb"
- reflect "reflect"
- sync "sync"
- unsafe "unsafe"
-)
-
-const (
- // Verify that this generated code is sufficiently up-to-date.
- _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
- // Verify that runtime/protoimpl is sufficiently up-to-date.
- _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
-)
-
-type DbEntry struct {
- state protoimpl.MessageState `protogen:"open.v1"`
- Id string `protobuf:"bytes,1,opt,name=Id,proto3" json:"Id,omitempty"`
- Key string `protobuf:"bytes,2,opt,name=Key,proto3" json:"Key,omitempty"`
- Preview string `protobuf:"bytes,3,opt,name=Preview,proto3" json:"Preview,omitempty"`
- Tags []string `protobuf:"bytes,4,rep,name=Tags,proto3" json:"Tags,omitempty"`
- Created *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=Created,proto3" json:"Created,omitempty"`
- Size uint64 `protobuf:"varint,6,opt,name=Size,proto3" json:"Size,omitempty"`
- Encrypted bool `protobuf:"varint,7,opt,name=Encrypted,proto3" json:"Encrypted,omitempty"`
- Binary bool `protobuf:"varint,8,opt,name=Binary,proto3" json:"Binary,omitempty"`
- Value string `protobuf:"bytes,9,opt,name=Value,proto3" json:"Value,omitempty"`
- unknownFields protoimpl.UnknownFields
- sizeCache protoimpl.SizeCache
-}
-
-func (x *DbEntry) Reset() {
- *x = DbEntry{}
- mi := &file_app_dbentry_proto_msgTypes[0]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
-}
-
-func (x *DbEntry) String() string {
- return protoimpl.X.MessageStringOf(x)
-}
-
-func (*DbEntry) ProtoMessage() {}
-
-func (x *DbEntry) ProtoReflect() protoreflect.Message {
- mi := &file_app_dbentry_proto_msgTypes[0]
- if x != nil {
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- if ms.LoadMessageInfo() == nil {
- ms.StoreMessageInfo(mi)
- }
- return ms
- }
- return mi.MessageOf(x)
-}
-
-// Deprecated: Use DbEntry.ProtoReflect.Descriptor instead.
-func (*DbEntry) Descriptor() ([]byte, []int) {
- return file_app_dbentry_proto_rawDescGZIP(), []int{0}
-}
-
-func (x *DbEntry) GetId() string {
- if x != nil {
- return x.Id
- }
- return ""
-}
-
-func (x *DbEntry) GetKey() string {
- if x != nil {
- return x.Key
- }
- return ""
-}
-
-func (x *DbEntry) GetPreview() string {
- if x != nil {
- return x.Preview
- }
- return ""
-}
-
-func (x *DbEntry) GetTags() []string {
- if x != nil {
- return x.Tags
- }
- return nil
-}
-
-func (x *DbEntry) GetCreated() *timestamppb.Timestamp {
- if x != nil {
- return x.Created
- }
- return nil
-}
-
-func (x *DbEntry) GetSize() uint64 {
- if x != nil {
- return x.Size
- }
- return 0
-}
-
-func (x *DbEntry) GetEncrypted() bool {
- if x != nil {
- return x.Encrypted
- }
- return false
-}
-
-func (x *DbEntry) GetBinary() bool {
- if x != nil {
- return x.Binary
- }
- return false
-}
-
-func (x *DbEntry) GetValue() string {
- if x != nil {
- return x.Value
- }
- return ""
-}
-
-var File_app_dbentry_proto protoreflect.FileDescriptor
-
-var file_app_dbentry_proto_rawDesc = string([]byte{
- 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,
- 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, 0xef, 0x01, 0x0a, 0x07, 0x44, 0x62,
- 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,
- 0x28, 0x09, 0x52, 0x03, 0x4b, 0x65, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x50, 0x72, 0x65, 0x76, 0x69,
- 0x65, 0x77, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x50, 0x72, 0x65, 0x76, 0x69, 0x65,
- 0x77, 0x12, 0x12, 0x0a, 0x04, 0x54, 0x61, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52,
- 0x04, 0x54, 0x61, 0x67, 0x73, 0x12, 0x34, 0x0a, 0x07, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64,
- 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
- 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
- 0x6d, 0x70, 0x52, 0x07, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x53,
- 0x69, 0x7a, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x53, 0x69, 0x7a, 0x65, 0x12,
- 0x1c, 0x0a, 0x09, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01,
- 0x28, 0x08, 0x52, 0x09, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x12, 0x16, 0x0a,
- 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, 0x09, 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,
- 0x6e, 0x2f, 0x61, 0x6e, 0x79, 0x64, 0x62, 0x2f, 0x61, 0x70, 0x70, 0x62, 0x06, 0x70, 0x72, 0x6f,
- 0x74, 0x6f, 0x33,
-})
-
-var (
- file_app_dbentry_proto_rawDescOnce sync.Once
- file_app_dbentry_proto_rawDescData []byte
-)
-
-func file_app_dbentry_proto_rawDescGZIP() []byte {
- file_app_dbentry_proto_rawDescOnce.Do(func() {
- file_app_dbentry_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_dbentry_proto_rawDesc), len(file_app_dbentry_proto_rawDesc)))
- })
- return file_app_dbentry_proto_rawDescData
-}
-
-var file_app_dbentry_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
-var file_app_dbentry_proto_goTypes = []any{
- (*DbEntry)(nil), // 0: app.DbEntry
- (*timestamppb.Timestamp)(nil), // 1: google.protobuf.Timestamp
-}
-var file_app_dbentry_proto_depIdxs = []int32{
- 1, // 0: app.DbEntry.Created:type_name -> google.protobuf.Timestamp
- 1, // [1:1] is the sub-list for method output_type
- 1, // [1:1] is the sub-list for method input_type
- 1, // [1:1] is the sub-list for extension type_name
- 1, // [1:1] is the sub-list for extension extendee
- 0, // [0:1] is the sub-list for field type_name
-}
-
-func init() { file_app_dbentry_proto_init() }
-func file_app_dbentry_proto_init() {
- if File_app_dbentry_proto != nil {
- return
- }
- type x struct{}
- out := protoimpl.TypeBuilder{
- File: protoimpl.DescBuilder{
- GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
- RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_dbentry_proto_rawDesc), len(file_app_dbentry_proto_rawDesc)),
- NumEnums: 0,
- NumMessages: 1,
- NumExtensions: 0,
- NumServices: 0,
- },
- GoTypes: file_app_dbentry_proto_goTypes,
- DependencyIndexes: file_app_dbentry_proto_depIdxs,
- MessageInfos: file_app_dbentry_proto_msgTypes,
- }.Build()
- File_app_dbentry_proto = out.File
- file_app_dbentry_proto_goTypes = nil
- file_app_dbentry_proto_depIdxs = nil
-}
diff --git a/app/dbentry.proto b/app/dbentry.proto
deleted file mode 100644
index 820dcfa..0000000
--- a/app/dbentry.proto
+++ /dev/null
@@ -1,20 +0,0 @@
-// -*-c++-*-
-
-syntax = "proto3";
-package app;
-
-import "google/protobuf/timestamp.proto";
-
-option go_package = "github.com/tlinden/anydb/app";
-
-message DbEntry {
- string Id = 1;
- string Key = 2;
- string Preview = 3;
- repeated string Tags = 4;
- google.protobuf.Timestamp Created = 5;
- uint64 Size = 6;
- bool Encrypted = 7;
- bool Binary = 8;
- string Value = 9;
-}
diff --git a/app/generic.go b/app/generic.go
deleted file mode 100644
index f86005f..0000000
--- a/app/generic.go
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
-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 .
-*/
-package app
-
-// look if a key in a map exists, generic variant
-func Exists[K comparable, V any](m map[K]V, v K) bool {
- if _, ok := m[v]; ok {
- return true
- }
-
- return false
-}
diff --git a/app/io.go b/app/io.go
deleted file mode 100644
index 7754bf1..0000000
--- a/app/io.go
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
-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 .
-*/
-package app
-
-import "os"
-
-func cleanError(file string, err error) error {
- // remove given [backup] file and forward the given error
- return os.Remove(file)
-}
-
-func fileExists(filename string) bool {
- info, err := os.Stat(filename)
-
- if err != nil {
- // return false on any error
- return false
- }
-
- return !info.IsDir()
-}
diff --git a/cfg/config.go b/cfg/config.go
deleted file mode 100644
index c04267c..0000000
--- a/cfg/config.go
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
-Copyright © 2025 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 .
-*/
-package cfg
-
-import (
- "fmt"
- "io"
- "os"
-
- "github.com/pelletier/go-toml"
- "github.com/tlinden/anydb/app"
- "github.com/tlinden/anydb/common"
-)
-
-var Version string = "v0.2.6"
-
-type BucketConfig struct {
- Encrypt bool
-}
-
-type Config struct {
- Debug bool
- Dbfile string
- Dbbucket string
- Template string
- Mode string // wide, table, yaml, json
- NoHeaders bool
- NoHumanize bool
- Encrypt bool // one entry
- CaseInsensitive bool
- Fulltext bool
- Listen string
- Buckets map[string]BucketConfig // config file only
-
- Tags []string // internal
- DB *app.DB // internal
- File string // internal
-}
-
-func (conf *Config) GetConfig(files []string) error {
- for _, file := range files {
- if err := conf.ParseConfigFile(file); err != nil {
- return err
- }
- }
-
- return nil
-}
-
-func (conf *Config) ParseConfigFile(file string) error {
- if !common.FileExists(file) {
- return nil
- }
-
- fd, err := os.OpenFile(file, os.O_RDONLY, 0600)
- if err != nil {
- return fmt.Errorf("failed to open config file %s: %w", file, err)
- }
-
- data, err := io.ReadAll(fd)
- if err != nil {
- return fmt.Errorf("failed to read from config file: %w", err)
- }
-
- add := Config{}
- err = toml.Unmarshal(data, &add)
- if err != nil {
- return fmt.Errorf("failed to unmarshall toml: %w", err)
- }
-
- // merge new values into existing config
- switch {
- case add.Debug != conf.Debug:
- conf.Debug = add.Debug
- case add.Dbfile != "":
- conf.Dbfile = add.Dbfile
- case add.Dbbucket != "":
- conf.Dbbucket = add.Dbbucket
- case add.Template != "":
- conf.Template = add.Template
- case add.NoHeaders != conf.NoHeaders:
- conf.NoHeaders = add.NoHeaders
- case add.NoHumanize != conf.NoHumanize:
- conf.NoHumanize = add.NoHumanize
- case add.Encrypt != conf.Encrypt:
- conf.Encrypt = add.Encrypt
- case add.Listen != "":
- conf.Listen = add.Listen
- }
-
- // only supported in config files
- conf.Buckets = add.Buckets
-
- // determine bucket encryption mode
- for name, bucket := range conf.Buckets {
- if name == conf.Dbbucket {
- conf.Encrypt = bucket.Encrypt
- }
- }
-
- return nil
-}
diff --git a/cmd/anydb.go b/cmd/anydb.go
deleted file mode 100644
index 3f038fa..0000000
--- a/cmd/anydb.go
+++ /dev/null
@@ -1,538 +0,0 @@
-package cmd
-
-var manpage = `
-anydb
- anydb - a personal key value store
-
-SYNOPSIS
- Usage:
- anydb [options] [flags]
- anydb [command]
-
- Available Commands:
- completion Generate the autocompletion script for the specified shell
- del Delete key
- edit Edit a key
- export Export database to json
- get Retrieve value for a key
- help Help about any command
- import Import database dump
- info info
- list List database contents
- man show manual page
- serve run REST API listener
- set Insert key/value pair
-
- Flags:
- -b, --bucket string use other bucket (default: data) (default "data")
- -c, --config string toml config file
- -f, --dbfile string DB file to use (default "/home/scip/.config/anydb/default.db")
- -d, --debug Enable debugging
- -h, --help help for anydb
- -v, --version Print program version
-
- Use "anydb [command] --help" for more information about a command.
-
-DESCRIPTION
- Anydb is a commandline personal key value store, it is simple to use and
- can be used to store anything you'd like, even binary files etc. It uses
- a key/value store (bbolt) in your home directory.
-
- The tool provides a number of subcommands to use it, there are global
- options and each subcommand has its own set of options.
-
-GLOBAL OPTIONS
- "-f, --dbfile filename"
- The default location of your databas is
- "$HOME/.config/anydb/default.db". You can change this with the "-f"
- option.
-
- "-b, --bucket name"
- Data in a bbolt key-value-store are managed in so called buckets.
- These are kind of namespaces, where each key must be unique.
- However, a database may contain more than one bucket.
-
- By default anydb uses a bucket named "data", but you can change this
- using the option "-b".
-
- Buckets can be configured to always encrypt values, see ENCRYTPTION.
-
- "-c, --config filename"
- Under normal circumstances you don't need a configuration file. But
- if you want, you can provide one using the option "-c".
-
- Anydb looks for a couple of default locations for a config file. You
- only need this option if you want to supply a configuration on a
- non-standard location. See CONFIGURATION for more details.
-
- "-d, --debug"
- Enable debug output.
-
- "-h, --help"
- Show the usage of anydb.
-
- "-v, --version"
- Show the program version.
-
- All of these options can be used with subcommands as well.
-
-SUBCOMMANDS
- completion
- The completion command can be used to setup completion for anydb. Just
- put something like this into your shell's configuration file:
-
- source <(anydb completion bash)
-
- If you use another shell, specify it instead of bash, of course.
-
- set
- The set command is being used to insert or update a key-value pair.
-
- Usage:
-
- Usage:
- anydb set [ | -r ] [-t ] [flags]
-
- Aliases:
- set, add, s, +
-
- Flags:
- -e, --encrypt encrypt value
- -r, --file string Filename or - for STDIN
- -h, --help help for set
- -t, --tags tag,tag,... tags, multiple allowed
-
- The standard way to insert a new entry is really simple:
-
- anydb set key value
-
- If you don't specify a value, anydb expects you to feed it some data via
- STDIN. For example:
-
- anydb set key < file
-
- You might as well specify a file directly using the "-f" option:
-
- anydb set key -f file
-
- Values can be encrypted using ChaCha20Poly1305 when you specify the "-e"
- option. Anydb will ask you interactively for a passphrase. You can also
- provide the passphrase using the environment variable "ANYDB_PASSWORD".
- To encrypt the value, a cryptographically secure key will be derived
- from the passphrase using the ArgonID2 algorithm. Each value can be
- encrypted with another passphrase. So, the database itself is not
- encrypted, just the values.
-
- You can supply tags by using the option "-t". Multiple tags can be
- provided either by separating them with a comma or by using multiple
- "-t" parameters:
-
- anydb set key value -t tag1,tag2
- anydb set key value -t tag1 -t tag2
-
- You can later filter entries by tag or by a combination of tags.
-
- To edit or modify an entry, just use the set command with the same key,
- the value in the database will be overwritten with the new value. An
- alternative option is the edit command, see below.
-
- get
- To retrieve the value of a key, use the get subcommand.
-
- Usage:
-
- Usage:
- anydb get [-o ] [-m ] [-n -N] [-T ] [flags]
-
- Aliases:
- get, show, g, .
-
- Flags:
- -h, --help help for get
- -m, --mode string output format (simple|wide|json|template) (default 'simple')
- -n, --no-headers omit headers in tables
- -N, --no-human do not translate to human readable values
- -o, --output string output value to file (ignores -m)
- -T, --template string go template for '-m template'
-
- In its simplest form you just call the get subcommand with the key you
- want to have the value for. The value is being printed to STDOUT by
- default:
-
- anydb get key
-
- If the value is binary content, it will not just being printed. In those
- cases you need to either redirect output into a file or use the option
- "-o" to write to a file:
-
- anydb get key > file
- anydb get key -o file
-
- If the value is encrypted, you will be asked for the passphrase to
- decrypt it. If the environment variable "ANYDB_PASSWORD" is set, its
- value will be used instead.
-
- There are different output modes you can choose from: simple, wide and
- json. The "simple" mode is the default one, it just prints the value as
- is. The "wide" mode prints a tabular output similar to the list
- subcommand, see there for more details. The options "-n" and "-N" have
- the same meaning as in the list command. The "json" mode prints the raw
- JSON representation of the whole database entry. Decryption will only
- take place in "simple" and "json" mode. The "template" mode provides the
- most flexibily, it is detailed in the section TEMPLATES.
-
- list
- The list subcommand displays a list of all database entries.
-
- Usage:
-
- Usage:
- anydb list [ | -t ] [-m ] [-nNif] [-T ] [flags]
-
- Aliases:
- list, ls, /, find, search
-
- Flags:
- -i, --case-insensitive filter case insensitive
- -h, --help help for list
- -m, --mode string output format (table|wide|json|template), wide is a verbose table. (default 'table')
- -n, --no-headers omit headers in tables
- -N, --no-human do not translate to human readable values
- -s, --search-fulltext perform a full text search
- -t, --tags stringArray tags, multiple allowed
- -T, --template string go template for '-m template'
- -l, --wide-output output mode: wide
-
- In its simplest form - without any options - , the list command just
- prints all keys with their values to STDOUT. Values are being truncated
- to maximum of 60 characters, that is, multiline values are not
- completely shown in order to keep the tabular view readable.
-
- To get more informations about each entry, use the "-o wide" or "-l"
- option. In addition to the key and value also the size, update timestamp
- and tags will be printed. Time and size values are converted into a
- human readable form, you can suppress this behavior with the "-N"
- option. You may omit the headers using the option "-n"
-
- Sometimes you might want to filter the list of entries. Either because
- your database grew too large or because you're searching for something.
- In that case you have two options: You may supply one or more tags or
- provide a filter regexp. To filter by tag, do:
-
- anydb list -t tag1
- anydb list -t tag1,tag2
- anydb list -t tag1 -t tag2
-
- To filter using a regular expression, do:
-
- anydb list "foo.*bar"
-
- Regular expressions follow the golang re2 syntax. For more details about
- the syntax, refer to . Please
- note, that this regexp dialect is not PCRE compatible, but supports most
- of its features.
-
- If you want to search case insensitive, add the option "-i".
-
- By default anydb only searches through the keys. If you want to search
- through the values as well, then use the "-s" option, which enables
- full-text search.
-
- You can - as with the get command - use other output modes. The default
- mode is "table". The "wide" mode is, as already mentioned, a more
- detailed table. Also supported is "json" mode and "template" mode. For
- details about using templates see TEMPLATES.
-
- del
- Use the del command to delete database entries.
-
- Usage:
-
- Usage:
- anydb del [flags]
-
- Aliases:
- del, d, rm
-
- Flags:
- -h, --help help for del
-
- The subcommand del does not provide any further options, it just deletes
- the entry referred to by the given key. No questions are being asked.
-
- edit
- The edit command makes it easier to modify larger entries.
-
- Usage:
-
- Usage:
- anydb edit [flags]
-
- Aliases:
- edit, modify, mod, ed, vi
-
- Flags:
- -h, --help help for edit
-
- The subcommand edit does not provide any further options. It works like
- this:
-
- 1. Write the value info a temporary file.
- 2. Execute the editor (which one, see below!) with that file.
- 3. Now you can edit the file and save+close it when done.
- 4. Anydb picks up the file and if the content has changed, puts its
- value into the DB.
-
- By default anydb executes the "vi" command. You can modify this behavior
- by setting the environment variable "EDITOR" appropriately.
-
- Please note, that this does not work with binary content!
-
- export
- Since the bbolt database file is not portable across platforms (it is
- bound to the endianess of the CPU it was being created on), you might
- want to create a backup file of your database. You can do this with the
- export subcommand.
-
- Usage:
-
- Usage:
- anydb export -o [flags]
-
- Aliases:
- export, dump, backup
-
- Flags:
- -h, --help help for export
- -o, --output string output to file
-
- The database dump is a JSON representation of the whole database and
- will be printed to the file specified with the "-o" option. If you
- specify "-" as the filename, it will be written to STDIN.
-
- anydb export -o dump.json
- anydb export -o - > dump.json
-
- Please note, that encrypted values will not be decrypted. This might
- change in a future version of anydb.
-
- import
- The import subcommand can be used to restore a database from a JSON
- dump.
-
- Usage:
-
- Usage:
- anydb import -i [flags]
-
- Aliases:
- import, restore
-
- Flags:
- -r, --file string Filename or - for STDIN
- -h, --help help for import
- -t, --tags stringArray tags, multiple allowed
-
- The "import" subcommand reads the JSON contents from the file specified
- with the "-i" option. If you specify "-" as the filename, it will be
- read from STDIN.
-
- anydb import -i - < dump.json
- anydb import -i dump.json
- cat dump.json | anydb import -i -
-
- If there is already a database, it will be saved by appending a
- timestamp and a new database with the contents of the dump will be
- created.
-
- serve
- Anydb provides a RESTful API, which you can use to manage the database
- from somewhere else. The API does not provide any authentication or any
- other security measures, so better only use it on localhost.
-
- Usage:
-
- Usage:
- anydb serve [-l host:port] [flags]
-
- Flags:
- -h, --help help for serve
- -l, --listen string host:port (default "localhost:8787")
-
- To start the listener, just execute the serve subcommand. You can tweak
- the ip address and tcp port using the "-l" option. The listener will not
- fork and run in the foreground. Logs are being printed to STDOUT as long
- as the listener runs.
-
- For more details about the API, please see the "REST API" section.
-
- info
- The info subcommand shows you some information about your current
- database.
-
- Usage:
-
- Usage:
- anydb info [flags]
-
- Flags:
- -h, --help help for info
- -N, --no-human do not translate to human readable values
-
- Data being shown are: filename and size, number of keys per bucket. If
- you supply the "-d" option (debug), some bbolt internals are being
- displayed as well.
-
- man
- The man subcommand shows an unformatted text variant of the manual page
- (which are currently reading).
-
- Usage:
-
- Usage:
- anydb man [flags]
-
- Flags:
- -h, --help help for man
-
- The manual is being piped into the "more" command, which is being
- expected to exist according to the POSIX standard on all supported unix
- platforms. It might not work on Windows.
-
-TEMPLATES
- The get and list commands support a template feature, which is very
- handy to create you own kind of formatting. The template syntax being
- used is the GO template language, refer to
- for details.
-
- Each template operates on one or more entries, no loop construct is
- required, the template provided applies to every matching entry
- separatley.
-
- The following template variables can be used:
-
- .Key - string
- .Value - string
- .Bin - []byte
- .Created - timestamp.Time
- To retrieve a string representation of the timestamp, use
- ".Created.AsTime". If you need a unix timestamp since epoch, use
- ".Created.Unix".
-
- .Tags - []string
- .Encrypted bool
-
- Prepend a single dot (".") before each variable name.
-
- Here are some examples how to use the feature:
-
- Only show the keys of all entries:
-
- anydb list -m template -T "{{ .Key }}"
-
- Format the list in a way so that is possible to evaluate it in a shell:
-
- eval $(anydb get foo -m template -T "key='{{ .Key }}' value='{{ .Value }}' ts='{{ .Created.AsTime}}'")
- echo "Key: $key, Value: $value, When: $ts"
-
- Print the values in CSV format ONLY if they have some tag:
-
- anydb list -m template -T "{{ if .Tags }}{{ .Key }},{{ .Value }},{{ .Created.AsTime}}{{ end }}"
-
-CONFIGURATION
- Anydb looks at the following locations for a configuration file, in that
- order:
-
- "$HOME/.config/anydb/anydb.toml"
- "$HOME/.anydb.toml"
- "anydb.toml" in the current directory
- or specify one using "-c"
-
- The configuration format uses the TOML language, refer to
- for more details. The key names correspond to the
- commandline options in most cases.
-
- Configuration follows a certain precedence: the files are tried to be
- read in the given order, followed by commandline options. That is, the
- last configuration file wins, unless the user provides a commandline
- option, then this setting will be taken.
-
- A complete configuration file might look like this:
-
- # defaults
- dbfile = "~/.config/anydb/default.db"
- dbbucket = "data"
- noheaders = false
- nohumanize = false
- encrypt = false
- listen = "localhost:8787"
-
- # different setups for different buckets
- [buckets.data]
- encrypt = true
-
- [buckets.test]
- encrypt = false
-
- Under normal circumstances you don't need a configuration file. However,
- if you want to use different buckets, then this might be a handy option.
- Buckets are being configured in ini-style with the term "bucket."
- followed by the bucket name. In the example above we enable encryption
- for the default bucket "data" and disable it for a bucket "test". To use
- different buckets, use the "-b" option.
-
-REST API
- The subcommand serve starts a simple HTTP service, which responds to
- RESTful HTTP requests. The listener responds to all requests with a JSON
- encoded response. The response contains the status and the content - if
- any - of the requested resource.
-
- The following requests are supported:
-
- GET /anydb/v1/
- Returns a JSON encoded list of all entries.
-
- GET /anydb/v1/key
- Returns the JSON encoded entry, if found.
-
- PUT /anydb/v1/
- Create an entry. Expects a JSON encoded request object in POST data.
-
- DELETE /anydb/v1/key
- Delete an entry.
-
- Some curl example calls to the API:
-
- Post a new key: curl -X PUT localhost:8787/anydb/v1/ \ -H 'Content-Type:
- application/json' \ -d '{"key":"foo","val":"bar"}'
-
- Retrieve the value:
-
- curl localhost:8787/anydb/v1/foo
-
- List all keys:
-
- curl localhost:8787/anydb/v1/
-
-BUGS
- In order to report a bug, unexpected behavior, feature requests or to
- submit a patch, please open an issue on github:
- .
-
- Please repeat the failing command with debugging enabled "-d" and
- include the output in the issue.
-
-LIMITATIONS
- The REST API list request doesn't provide any filtering capabilities
- yet.
-
-LICENSE
- This software is licensed under the GNU GENERAL PUBLIC LICENSE version
- 3.
-
- Copyright (c) 2024 by Thomas von Dein
-
-AUTHORS
- Thomas von Dein tom AT vondein DOT org
-
-`
diff --git a/cmd/crud.go b/cmd/crud.go
deleted file mode 100644
index 4e125c8..0000000
--- a/cmd/crud.go
+++ /dev/null
@@ -1,253 +0,0 @@
-/*
-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 .
-*/
-package cmd
-
-import (
- "errors"
- "os"
- "strings"
-
- "github.com/spf13/cobra"
- "github.com/tlinden/anydb/app"
- "github.com/tlinden/anydb/cfg"
- "github.com/tlinden/anydb/output"
-)
-
-func Set(conf *cfg.Config) *cobra.Command {
- var (
- attr app.DbAttr
- )
-
- var cmd = &cobra.Command{
- Use: "set [ | -r ] [-t ]",
- Short: "Insert key/value pair",
- Long: `Insert key/value pair`,
- RunE: func(cmd *cobra.Command, args []string) error {
- if len(args) == 0 {
- return errors.New("no key/value pair specified")
- }
-
- // errors at this stage do not cause the usage to be shown
- cmd.SilenceUsage = true
-
- if len(args) > 0 {
- 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], ",")
- }
-
- // check if value given as file or via stdin and fill attr accordingly
- if err := attr.ParseKV(); err != nil {
- return err
- }
-
- // encrypt?
- if conf.Encrypt {
- pass, err := getPassword()
- if err != nil {
- return err
- }
-
- err = app.Encrypt(pass, &attr)
- if err != nil {
- return err
- }
- }
-
- return conf.DB.Set(&attr)
- },
- }
-
- cmd.PersistentFlags().BoolVarP(&conf.Encrypt, "encrypt", "e", false, "encrypt value")
- 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 Get(conf *cfg.Config) *cobra.Command {
- var (
- attr app.DbAttr
- )
-
- var cmd = &cobra.Command{
- Use: "get [-o ] [-m ] [-n -N] [-T ]",
- Short: "Retrieve value for a key",
- Long: `Retrieve value for 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
-
- if len(args) > 0 {
- attr.Key = args[0]
- }
-
- entry, err := conf.DB.Get(&attr)
- if err != nil {
- return err
- }
-
- if entry.Encrypted {
- pass, err := getPassword()
- if err != nil {
- return err
- }
-
- clear, err := app.Decrypt(pass, []byte(entry.Value))
- if err != nil {
- return err
- }
-
- entry.Value = string(clear)
- entry.Size = uint64(len(entry.Value))
- entry.Encrypted = false
- }
-
- return output.Print(os.Stdout, conf, &attr, entry)
- },
- }
-
- cmd.PersistentFlags().StringVarP(&attr.File, "output", "o", "", "output value to file (ignores -m)")
- cmd.PersistentFlags().StringVarP(&conf.Mode, "mode", "m", "", "output format (simple|wide|json|template) (default 'simple')")
- 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().StringVarP(&conf.Template, "template", "T", "", "go template for '-m template'")
-
- cmd.Aliases = append(cmd.Aliases, "show")
- cmd.Aliases = append(cmd.Aliases, "g")
- cmd.Aliases = append(cmd.Aliases, ".")
-
- return cmd
-}
-
-func Del(conf *cfg.Config) *cobra.Command {
- var (
- attr app.DbAttr
- )
-
- var cmd = &cobra.Command{
- Use: "del ",
- Short: "Delete key",
- Long: `Delete key and value matching 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
-
- if len(args) > 0 {
- attr.Key = args[0]
- }
-
- return conf.DB.Del(&attr)
- },
- }
-
- cmd.Aliases = append(cmd.Aliases, "d")
- cmd.Aliases = append(cmd.Aliases, "rm")
-
- return cmd
-}
-
-func List(conf *cfg.Config) *cobra.Command {
- var (
- attr app.DbAttr
- wide bool
- )
-
- var cmd = &cobra.Command{
- Use: "list [ | -t ] [-m ] [-nNis] [-T ]",
- Short: "List database contents",
- Long: `List 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.List(&attr, conf.Fulltext)
- 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.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.PersistentFlags().BoolVarP(&conf.Fulltext, "search-fulltext", "s", false, "perform a full text search")
- cmd.PersistentFlags().StringArrayVarP(&attr.Tags, "tags", "t", nil, "tags, multiple allowed")
-
- cmd.Aliases = append(cmd.Aliases, "ls")
- cmd.Aliases = append(cmd.Aliases, "/")
- cmd.Aliases = append(cmd.Aliases, "find")
- cmd.Aliases = append(cmd.Aliases, "search")
-
- return cmd
-}
-
-func getPassword() ([]byte, error) {
- var pass []byte
-
- envpass := os.Getenv("ANYDB_PASSWORD")
-
- if envpass == "" {
- readpass, err := app.AskForPassword()
- if err != nil {
- return nil, err
- }
-
- pass = readpass
- } else {
- pass = []byte(envpass)
- }
-
- return pass, nil
-}
diff --git a/cmd/extra.go b/cmd/extra.go
deleted file mode 100644
index a86112d..0000000
--- a/cmd/extra.go
+++ /dev/null
@@ -1,335 +0,0 @@
-/*
-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 .
-*/
-package cmd
-
-import (
- "bytes"
- "errors"
- "fmt"
- "io"
- "log"
- "os"
- "os/exec"
-
- "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 ",
- Short: "Export database to json file",
- Long: `Export database to json file`,
- 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.Getall(&attr)
- if err != nil {
- return err
- }
-
- return output.WriteJSON(&attr, conf, entries)
- },
- }
-
- cmd.PersistentFlags().StringVarP(&attr.File, "output-file", "o", "", "filename or - for STDIN")
- if err := cmd.MarkPersistentFlagRequired("output-file"); err != nil {
- panic(err)
- }
-
- 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 -i ",
- 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, "import-file", "i", "", "filename or - for STDIN")
- cmd.PersistentFlags().StringArrayVarP(&attr.Tags, "tags", "t", nil, "tags, multiple allowed")
- if err := cmd.MarkPersistentFlagRequired("import-file"); err != nil {
- panic(err)
- }
-
- 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 ",
- 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 && entry.Binary {
- 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, []byte(entry.Value))
- if err != nil {
- return err
- }
-
- entry.Value = string(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, string(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: []byte(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 func() {
- if err := os.Remove(tmp.Name()); err != nil {
- log.Fatal(err)
- }
- }()
-
- // 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 func() {
- if err := modified.Close(); err != nil {
- log.Fatal(err)
- }
- }()
-
- 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
-}
diff --git a/cmd/root.go b/cmd/root.go
deleted file mode 100644
index 4879c1d..0000000
--- a/cmd/root.go
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
-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 .
-*/
-package cmd
-
-import (
- "errors"
- "fmt"
- "log/slog"
- "os"
- "path/filepath"
- "runtime/debug"
-
- "github.com/spf13/cobra"
- "github.com/tlinden/anydb/app"
- "github.com/tlinden/anydb/cfg"
- "github.com/tlinden/yadu"
-)
-
-func completion(cmd *cobra.Command, mode string) error {
- switch mode {
- case "bash":
- return cmd.Root().GenBashCompletion(os.Stdout)
- case "zsh":
- return cmd.Root().GenZshCompletion(os.Stdout)
- case "fish":
- return cmd.Root().GenFishCompletion(os.Stdout, true)
- case "powershell":
- return cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout)
- default:
- return errors.New("invalid shell parameter! Valid ones: bash|zsh|fish|powershell")
- }
-}
-
-func Execute() {
- var (
- conf cfg.Config
- configfile string
- ShowVersion bool
- ShowCompletion string
- )
-
- home, err := os.UserHomeDir()
- if err != nil {
- panic(err)
- }
-
- SearchConfigs := []string{
- filepath.Join(home, ".config", "anydb", "anydb.toml"),
- filepath.Join(home, ".anydb.toml"),
- "anydb.toml",
- }
-
- var rootCmd = &cobra.Command{
- Use: "anydb [options]",
- Short: "anydb",
- Long: `A personal key value store`,
- PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
-
- var configs []string
- if configfile != "" {
- configs = []string{configfile}
- } else {
- configs = SearchConfigs
- }
-
- if err := conf.GetConfig(configs); err != nil {
- return err
- }
-
- if conf.Debug {
- buildInfo, _ := debug.ReadBuildInfo()
- opts := &yadu.Options{
- Level: slog.LevelDebug,
- AddSource: true,
- }
-
- slog.SetLogLoggerLevel(slog.LevelDebug)
-
- handler := yadu.NewHandler(os.Stdout, opts)
- debuglogger := slog.New(handler).With(
- slog.Group("program_info",
- slog.Int("pid", os.Getpid()),
- slog.String("go_version", buildInfo.GoVersion),
- ),
- )
- slog.SetDefault(debuglogger)
-
- slog.Debug("parsed config", "conf", conf)
- }
-
- dbfile := app.GetDbFile(conf.Dbfile)
-
- db, err := app.New(dbfile, conf.Dbbucket, conf.Debug)
- if err != nil {
- return err
- }
-
- conf.DB = db
- return nil
- },
-
- RunE: func(cmd *cobra.Command, args []string) error {
- if ShowVersion {
- fmt.Printf("This is anydb version %s\n", cfg.Version)
- return nil
- }
-
- if len(ShowCompletion) > 0 {
- return completion(cmd, ShowCompletion)
- }
-
- if len(args) == 0 {
- return errors.New("no command specified")
- }
-
- return nil
- },
- }
-
- // options
- rootCmd.PersistentFlags().BoolVarP(&ShowVersion, "version", "v", false, "Print program version")
- rootCmd.PersistentFlags().BoolVarP(&conf.Debug, "debug", "d", false, "Enable debugging")
- rootCmd.PersistentFlags().StringVarP(&conf.Dbfile, "dbfile", "f",
- "", "DB file to use (default: ~/.config/anydb/default.db)")
- rootCmd.PersistentFlags().StringVarP(&conf.Dbbucket, "bucket", "b",
- app.BucketData, "use other bucket (default: "+app.BucketData+")")
- rootCmd.PersistentFlags().StringVarP(&configfile, "config", "c", "", "toml config file")
-
- // CRUD
- rootCmd.AddCommand(Set(&conf))
- rootCmd.AddCommand(List(&conf))
- rootCmd.AddCommand(Get(&conf))
- rootCmd.AddCommand(Del(&conf))
-
- // backup
- rootCmd.AddCommand(Export(&conf))
- rootCmd.AddCommand(Import(&conf))
-
- // REST API
- rootCmd.AddCommand(Serve(&conf))
-
- // auxiliary
- rootCmd.AddCommand(Man(&conf))
- rootCmd.AddCommand(Info(&conf))
- rootCmd.AddCommand(Edit(&conf))
-
- err = rootCmd.Execute()
- if err != nil {
- os.Exit(1)
- }
-}
diff --git a/common/io.go b/common/io.go
deleted file mode 100644
index 47933fb..0000000
--- a/common/io.go
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
-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 .
-*/
-package common
-
-import "os"
-
-func CleanError(file string, err error) error {
- // remove given [backup] file and forward the given error
- return os.Remove(file)
-}
-
-func FileExists(filename string) bool {
- info, err := os.Stat(filename)
-
- if err != nil {
- // return false on any error
- return false
- }
-
- return !info.IsDir()
-}
diff --git a/common/logger.go b/common/logger.go
deleted file mode 100644
index b11fef6..0000000
--- a/common/logger.go
+++ /dev/null
@@ -1,23 +0,0 @@
-package common
-
-import (
- "fmt"
- "log/slog"
-)
-
-type Slogger struct {
- *slog.Logger
-}
-
-func (l Slogger) Debug(v ...interface{}) {}
-func (l Slogger) Debugf(format string, v ...interface{}) { l.Logger.Debug(fmt.Sprintf(format, v...)) }
-func (l Slogger) Error(v ...interface{}) {}
-func (l Slogger) Errorf(format string, v ...interface{}) { l.Logger.Error(fmt.Sprintf(format, v...)) }
-func (l Slogger) Info(v ...interface{}) {}
-func (l Slogger) Infof(format string, v ...interface{}) { l.Logger.Info(fmt.Sprintf(format, v...)) }
-func (l Slogger) Warning(v ...interface{}) {}
-func (l Slogger) Warningf(format string, v ...interface{}) { l.Logger.Warn(fmt.Sprintf(format, v...)) }
-func (l Slogger) Fatal(v ...interface{}) {}
-func (l Slogger) Fatalf(format string, v ...interface{}) { l.Logger.Error(fmt.Sprintf(format, v...)) }
-func (l Slogger) Panic(v ...interface{}) {}
-func (l Slogger) Panicf(format string, v ...interface{}) { l.Logger.Error(fmt.Sprintf(format, v...)) }
diff --git a/contrib/anydbui b/contrib/anydbui
deleted file mode 100755
index d635260..0000000
--- a/contrib/anydbui
+++ /dev/null
@@ -1,95 +0,0 @@
-#!/bin/sh
-
-template="{{.Key}} {{.Created.AsTime.Year}}-{{.Created.AsTime.Month}}-{{.Created.AsTime.Day}} {{.Taglist}} {{.Preview}}"
-header="TITLE DATE TAGS PREVIEW"
-
-# its possible to use another version of anydb for testing purposes
-anydb="${ANYDB:-anydb}"
-
-# list command
-command="( echo '$header'; $anydb ls -m template --template '$template' ) | column -t -l4"
-
-for binary in fzf $anydb column diff awk less; do
- if ! type $binary > /dev/null 2>&1; then
- echo "$binary is not installed!"
- exit 1
- fi
-done
-
-if type gum > /dev/null 2>&1; then
- GUM=1
-fi
-
-_rand() {
- awk 'BEGIN {srand(); printf( "%d\n", 1024 * rand() )}'
-}
-
-_list() {
- (
- echo "$header"
- $anydb ls -m template --template "$template"
- ) | column -t -l4
-}
-
-_updater() {
- _port="$1"
- _cache="/tmp/anydbcache.$port"
- _current="/tmp/anydbcache.$port.current"
-
- touch $_current $_cache
-
- while :; do
- sleep 10
-
- _list > $_current
-
- if ! diff -q $_current $_cache > /dev/null 2>&1; then
- curl -d "reload($command)+clear-screen" "http://127.0.0.1:$_port"
- fi
- cp $_current $_cache
- done
-}
-
-_cleanup() {
- # get rid of the update child
- kill $pid
-
- # clean up reloader cache
- rm -f /tmp/anydbcache.${port}*
-}
-
-
-
-# fork background updater
-port=$((8000 + $(_rand) % 1000))
-_updater $port &
-pid=$!
-
-db=$($anydb info | grep Database | cut -d: -f2)
-shorthelp="$db - [enter]read [c-e]edit [c-k]kill [c-c]exit [c-k]delete"
-
-if test -n "$GUM"; then
- color="Color \"#ff\" \"#0000ff\"" # white on blue
- db="{{ $color \"Database:\"}}$db"
- enter="{{ $color \"[Enter]\"}} Read"
- edit="{{ $color \"[ctrl-c]\"}} Edit"
- delete="{{ $color \"[ctrl-k]\"}} Delete"
- template="$db $enter $edit $delete"
- shorthelp=$(echo "$template" | gum format -t template)
-fi
-
-trap '_cleanup; (exit $?); exit' INT TERM EXIT
-
-: | command="$command" fzf \
- --bind "start:reload:$command" \
- --header-lines 1 --layout=reverse --info=inline \
- --height 100% --pointer="→" --separator="─" \
- --scrollbar="│" --preview-window "right:50%" \
- --border-label="$shorthelp" \
- --border=bottom --layout=reverse --info=inline \
- --height 100% --header-first --cycle --header-lines=1 --listen=$port \
- --bind "enter:execute: clear; $anydb get {1} | less" \
- --bind "ctrl-e:execute: $anydb edit {1}" \
- --bind "ctrl-k:execute:$anydb del {1}"
-
-
diff --git a/demo/Makefile b/demo/Makefile
deleted file mode 100644
index 8afc687..0000000
--- a/demo/Makefile
+++ /dev/null
@@ -1,21 +0,0 @@
-.PHONY: demo clean check clean-demo
-
-VHS = vhs
-
-
-
-clean-demo:
- rm -f local.db*
-
-%.gif: %.tape
- @echo "vhs $<"
- env PATH=..:$(PATH) ANYDB_DB=local.db vhs $<
-
-clean:
- rm -vf *.db* *.json
-
-check:
- ls -l ../anydb
-
-demo: check clean-demo intro.gif advanced.gif
-
diff --git a/demo/advanced.gif b/demo/advanced.gif
deleted file mode 100644
index 442d1eb..0000000
Binary files a/demo/advanced.gif and /dev/null differ
diff --git a/demo/advanced.tape b/demo/advanced.tape
deleted file mode 100644
index 9cb631e..0000000
--- a/demo/advanced.tape
+++ /dev/null
@@ -1,182 +0,0 @@
-# -*-sh-*-
-
-Output advanced.gif
-Set FontSize 20
-Set Width 1000
-Set Height 800
-Set Theme { "name": "Whimsy", "black": "#535178", "red": "#ef6487", "green": "#5eca89", "yellow": "#fdd877", "blue": "#65aef7", "magenta": "#aa7ff0", "cyan": "#43c1be", "white": "#ffffff", "brightBlack": "#535178", "brightRed": "#ef6487", "brightGreen": "#5eca89", "brightYellow": "#fdd877", "brightBlue": "#65aef7", "brightMagenta": "#aa7ff0", "brightCyan": "#43c1be", "brightWhite": "#ffffff", "background": "#29283b", "foreground": "#b3b0d6", "selection": "#3d3c58", "cursor": "#b3b0d6" }
-Set WindowBar Colorful
-Set BorderRadius 10
-Set Shell zsh
-Set FontFamily "IBM Plex Mono"
-Set CursorBlink false
-Set PlaybackSpeed 1
-Set TypingSpeed .05
-
-Hide
-Type `PROMPT=''`
-Enter
-Type "setopt interactivecomments"
-Enter
-Type "autoload -U colors && colors"
-Enter
-Type `PS1="%{$fg[magenta]%}demo> %{$reset_color%}"`
-Enter
-Type "clear"
-Enter
-Show
-
-
-Type "# you can assign tags"
-Enter
-Sleep 1s
-Type "anydb set foo bar -t note,important"
-Enter
-Sleep 3s
-
-Enter
-Type "# and filter for them"
-Enter
-Sleep 1s
-Type "anydb list -t important"
-Enter
-Sleep 3s
-
-Enter
-Type "# beside tags filtering you can also use regexps for searching"
-Enter
-Type "# note, by default the list command only searches through keys"
-Enter
-Sleep 1s
-Type "anydb list '[a-z]+'"
-Enter
-Sleep 3s
-
-Enter
-Type "# do a full text search"
-Enter
-Sleep 1s
-Type "anydb list '[a-z]+' -s"
-Enter
-Sleep 3s
-
-Enter
-Type "# anydb also supports a wide output"
-Enter
-Sleep 1s
-Type "anydb list -m wide"
-Enter
-Sleep 3s
-
-Enter
-Type "# there are shortcuts as well"
-Enter
-Sleep 1s
-Type "anydb ls -l"
-Enter
-Sleep 2s
-Type "anydb /"
-Enter
-Sleep 3s
-
-Enter
-Type "# other outputs are possible as well"
-Enter
-Sleep 1s
-Type "anydb list -m json"
-Enter
-Sleep 3s
-
-Enter
-Type "# you can backup your database"
-Enter
-Sleep 1s
-Type "anydb export -o backup.json"
-Enter
-Sleep 3s
-
-Enter
-Type "# and import it somewhere else"
-Enter
-Sleep 1s
-Type "rm local.db"
-Enter
-Sleep 1s
-Type "anydb ls -l"
-Enter
-Sleep 1s
-Type "anydb import -i backup.json"
-Enter
-Sleep 1s
-Type "anydb ls -l"
-Enter
-Sleep 3s
-
-Enter
-Type "# you can encrypt entries. anydb asks for a passphrase"
-Enter
-Type "# and will do the same when you retrieve the key using the"
-Enter
-Type "# get command. anydb will ask you interactively for a password"
-Enter
-Sleep 1s
-Type "anydb set address 'Beatstreet 42' -e"
-Enter
-Type "pass"
-Enter
-Sleep 3s
-
-Enter
-Type "# but you can provide it via an environment variable too"
-Enter
-Sleep 1s
-Type "ANYDB_PASSWORD=foo anydb set -e secretkey blahblah"
-Enter
-Sleep 3s
-
-Enter
-Type "# using template output mode you can freely design how to print stuff"
-Enter
-Type "# here, we print the values in CSV format ONLY if they have some tag"
-Enter
-Type "# also note, that we're printing the creation timestamp as epoch"
-Sleep 1s
-Type `anydb ls -m template -T "{{ if .Tags }}{{ .Key }},{{ .Value }},{{ .Created.AsTime.Unix}}{{ end }}"`
-Enter
-Sleep 3s
-
-Enter
-Type "# or, to simulate skate's -k or -v"
-Enter
-Sleep 1s
-Type `anydb ls -m template -T "{{ .Key }}"`
-Enter
-Sleep 1s
-Type `anydb ls -m template -T "{{ .Value }}"`
-Enter
-Sleep 3s
-
-Enter
-Type "# maybe you want to digest the item in a shell script? also"
-Enter
-Type "# note, that both the list and get commands support templates"
-Enter
-Sleep 1s
-Type `eval $(anydb get kitty -m template -T "value='{{ .Value }}'"); echo "value: $value"`
-Enter
-Sleep 3s
-
-Enter
-Type "# sometimes you need to know some details about the current database"
-Enter
-Type "# add -d for more details"
-Enter
-Sleep 1
-Type "anydb info"
-Enter
-Sleep 3s
-
-Enter
-Type "# Try it out yourself: github.com/tlinden/anydb!"
-Enter
-Sleep 4s
diff --git a/demo/intro.gif b/demo/intro.gif
deleted file mode 100644
index 8b010b1..0000000
Binary files a/demo/intro.gif and /dev/null differ
diff --git a/demo/intro.tape b/demo/intro.tape
deleted file mode 100644
index ed90e6b..0000000
--- a/demo/intro.tape
+++ /dev/null
@@ -1,76 +0,0 @@
-# -*-sh-*-
-
-Output intro.gif
-Set FontSize 20
-Set Width 1000
-Set Height 800
-Set Theme { "name": "Whimsy", "black": "#535178", "red": "#ef6487", "green": "#5eca89", "yellow": "#fdd877", "blue": "#65aef7", "magenta": "#aa7ff0", "cyan": "#43c1be", "white": "#ffffff", "brightBlack": "#535178", "brightRed": "#ef6487", "brightGreen": "#5eca89", "brightYellow": "#fdd877", "brightBlue": "#65aef7", "brightMagenta": "#aa7ff0", "brightCyan": "#43c1be", "brightWhite": "#ffffff", "background": "#29283b", "foreground": "#b3b0d6", "selection": "#3d3c58", "cursor": "#b3b0d6" }
-Set WindowBar Colorful
-Set BorderRadius 10
-Set Shell zsh
-Set FontFamily "IBM Plex Mono"
-Set CursorBlink false
-Set PlaybackSpeed 1
-Set TypingSpeed .05
-
-Hide
-Type `PROMPT=''`
-Enter
-Type "setopt interactivecomments"
-Enter
-Type "autoload -U colors && colors"
-Enter
-Type `PS1="%{$fg[magenta]%}demo> %{$reset_color%}"`
-Enter
-Type "clear"
-Enter
-Show
-
-Type "# Store something"
-Enter
-Sleep 1s
-Type "anydb set kitty meow"
-Enter
-Sleep 3s
-
-Enter
-Type `# What's in the store?`
-Enter
-Sleep 1s
-Type "anydb ls"
-Enter
-Sleep 3s
-
-Enter
-Type "# Fetch something"
-Enter
-Sleep 1s
-Type "anydb get kitty"
-Enter
-Sleep 3s
-
-Enter
-Type "# Unicode also works, of course"
-Enter
-Sleep 1s
-Type "anydb set 猫咪 喵"
-Enter
-Sleep 2s
-Type "anydb get 猫咪"
-Enter
-Sleep 3s
-
-Enter
-Type "# Do creative things with anydb list"
-Enter
-Sleep 1s
-Type "anydb set penelope marmalade"
-Enter
-Type "anydb set christian tacos"
-Enter
-Type "anydb set muesli muesli"
-Enter
-Type "anydb list | xargs -n 2 printf '%s loves %s.\n'"
-Enter
-Sleep 3s
-
diff --git a/docker-compose.yaml b/docker-compose.yaml
deleted file mode 100644
index 5a4ad41..0000000
--- a/docker-compose.yaml
+++ /dev/null
@@ -1,38 +0,0 @@
-#
-# 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 .
-#
-version: "3.9"
-services:
- init:
- image: alpine:latest
- user: "root"
- group_add:
- - '${GROUP_ID}'
- volumes:
- - ${OUTDIR}:/backup
- command: chown -R ${USER_ID}:${USER_ID} /backup
-
- anydb:
- container_name: anydb
- user: "${USER_ID}:${USER_ID}"
- volumes:
- - ${OUTDIR}:/backup
- working_dir: /backup
- build: .
- image: anydb:latest
- depends_on:
- init:
- condition: service_completed_successfully
diff --git a/example.toml b/example.toml
deleted file mode 100644
index 654f6b6..0000000
--- a/example.toml
+++ /dev/null
@@ -1,14 +0,0 @@
-# defaults
-dbfile = "~/.config/anydb/default.db"
-dbbucket = "data"
-noheaders = false
-nohumanize = false
-encrypt = false
-listen = "localhost:8787"
-
-# different setups for different buckets
-[buckets.data]
-encrypt = true
-
-[buckets.test]
-encrypt = false
diff --git a/go.mod b/go.mod
deleted file mode 100644
index 17ea5b8..0000000
--- a/go.mod
+++ /dev/null
@@ -1,40 +0,0 @@
-module github.com/tlinden/anydb
-
-go 1.24.0
-
-toolchain go1.24.1
-
-require (
- github.com/dustin/go-humanize v1.0.1
- github.com/gofiber/fiber/v2 v2.52.9
- github.com/inconshreveable/mousetrap v1.1.0
- github.com/olekukonko/tablewriter v1.1.0
- github.com/pelletier/go-toml v1.9.5
- github.com/rogpeppe/go-internal v1.14.1
- github.com/spf13/cobra v1.10.1
- github.com/tlinden/yadu v0.1.3
- go.etcd.io/bbolt v1.4.3
- golang.org/x/crypto v0.42.0
- golang.org/x/term v0.35.0
- google.golang.org/protobuf v1.36.9
-)
-
-require (
- github.com/andybalholm/brotli v1.1.1 // indirect
- github.com/fatih/color v1.16.0 // indirect
- github.com/google/uuid v1.6.0 // indirect
- github.com/klauspost/compress v1.17.11 // indirect
- github.com/mattn/go-colorable v0.1.14 // indirect
- github.com/mattn/go-isatty v0.0.20 // indirect
- github.com/mattn/go-runewidth v0.0.16 // indirect
- github.com/olekukonko/errors v1.1.0 // indirect
- github.com/olekukonko/ll v0.0.9 // indirect
- github.com/rivo/uniseg v0.2.0 // indirect
- github.com/spf13/pflag v1.0.9 // indirect
- github.com/valyala/bytebufferpool v1.0.0 // indirect
- github.com/valyala/fasthttp v1.55.0 // indirect
- github.com/valyala/tcplisten v1.0.0 // indirect
- golang.org/x/sys v0.36.0 // indirect
- golang.org/x/tools v0.26.0 // indirect
- gopkg.in/yaml.v3 v3.0.1 // indirect
-)
diff --git a/go.sum b/go.sum
deleted file mode 100644
index 6e1ea9f..0000000
--- a/go.sum
+++ /dev/null
@@ -1,75 +0,0 @@
-github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
-github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
-github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
-github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
-github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
-github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
-github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
-github.com/gofiber/fiber/v2 v2.52.9 h1:YjKl5DOiyP3j0mO61u3NTmK7or8GzzWzCFzkboyP5cw=
-github.com/gofiber/fiber/v2 v2.52.9/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
-github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
-github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
-github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
-github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
-github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
-github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
-github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
-github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
-github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
-github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
-github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
-github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
-github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM=
-github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
-github.com/olekukonko/ll v0.0.9 h1:Y+1YqDfVkqMWuEQMclsF9HUR5+a82+dxJuL1HHSRpxI=
-github.com/olekukonko/ll v0.0.9/go.mod h1:En+sEW0JNETl26+K8eZ6/W4UQ7CYSrrgg/EdIYT2H8g=
-github.com/olekukonko/tablewriter v1.1.0 h1:N0LHrshF4T39KvI96fn6GT8HEjXRXYNDrDjKFDB7RIY=
-github.com/olekukonko/tablewriter v1.1.0/go.mod h1:5c+EBPeSqvXnLLgkm9isDdzR3wjfBkHR9Nhfp3NWrzo=
-github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
-github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
-github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
-github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
-github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
-github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
-github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
-github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
-github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
-github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
-github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
-github.com/tlinden/yadu v0.1.3 h1:5cRCUmj+l5yvlM2irtpFBIJwVV2DPEgYSaWvF19FtcY=
-github.com/tlinden/yadu v0.1.3/go.mod h1:l3bRmHKL9zGAR6pnBHY2HRPxBecf7L74BoBgOOpTcUA=
-github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
-github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
-github.com/valyala/fasthttp v1.55.0 h1:Zkefzgt6a7+bVKHnu/YaYSOPfNYNisSVBo/unVCf8k8=
-github.com/valyala/fasthttp v1.55.0/go.mod h1:NkY9JtkrpPKmgwV3HTaS2HWaJss9RSIsRVfcxxoHiOM=
-github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
-github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
-github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
-github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
-go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo=
-go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E=
-golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
-golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
-golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
-golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
-golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
-golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
-golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ=
-golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
-golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
-golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
-google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
-google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
-gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/main.go b/main.go
deleted file mode 100644
index 6e82089..0000000
--- a/main.go
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
-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 .
-*/
-package main
-
-import (
- "bufio"
- "fmt"
- "log/slog"
- "os"
- "runtime"
-
- "github.com/inconshreveable/mousetrap"
- "github.com/tlinden/anydb/cmd"
-)
-
-func main() {
- const NoLogsLevel = 100
- slog.SetLogLoggerLevel(NoLogsLevel)
-
- Main()
-}
-
-func init() {
- // if we're running on Windows AND if the user double clicked the
- // exe file from explorer, we tell them and then wait until any
- // key has been hit, which will make the cmd window disappear and
- // thus give the user time to read it.
- if runtime.GOOS == "windows" {
- if mousetrap.StartedByExplorer() {
- fmt.Println("Do no double click anydb.exe!")
- fmt.Println("Please open a command shell and run it from there.")
- fmt.Println()
- fmt.Print("Press any key to quit: ")
- _, err := bufio.NewReader(os.Stdin).ReadString('\n')
- if err != nil {
- panic(err)
- }
- }
- }
-}
-
-func Main() int {
- cmd.Execute()
- return 0
-}
-
-func init() {
- // if we're running on Windows AND if the user double clicked the
- // exe file from explorer, we tell them and then wait until any
- // key has been hit, which will make the cmd window disappear and
- // thus give the user time to read it.
- if runtime.GOOS == "windows" {
- if mousetrap.StartedByExplorer() {
- fmt.Println("Please do no double click anydb.exe!")
- fmt.Println("Please open a command shell and run it from there.")
- fmt.Println()
- fmt.Print("Press any key to quit: ")
- _, err := bufio.NewReader(os.Stdin).ReadString('\n')
- if err != nil {
- panic(err)
- }
- }
- }
-}
diff --git a/main_test.go b/main_test.go
deleted file mode 100644
index ae42335..0000000
--- a/main_test.go
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
-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 .
-*/
-package main
-
-import (
- "testing"
-
- "github.com/rogpeppe/go-internal/testscript"
-)
-
-func TestMain(m *testing.M) {
- testscript.Main(m, map[string]func(){
- "anydb": main,
- })
-}
-
-func TestAnydb(t *testing.T) {
- testscript.Run(t, testscript.Params{
- Dir: "t",
- })
-}
diff --git a/mkrel.sh b/mkrel.sh
deleted file mode 100755
index da821b1..0000000
--- a/mkrel.sh
+++ /dev/null
@@ -1,82 +0,0 @@
-#!/bin/bash
-
-# 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 .
-
-
-# get list with: go tool dist list
-DIST="darwin/amd64
-freebsd/amd64
-linux/amd64
-netbsd/amd64
-openbsd/amd64
-windows/amd64
-freebsd/arm64
-linux/arm64
-netbsd/arm64
-openbsd/arm64
-windows/arm64"
-
-tool="$1"
-version="$2"
-
-if test -z "$version"; then
- echo "Usage: $0 "
- exit 1
-fi
-
-rm -rf releases
-mkdir -p releases
-
-
-for D in $DIST; do
- os=${D/\/*/}
- arch=${D/*\//}
- binfile="releases/${tool}-${os}-${arch}-${version}"
-
- if test "$os" = "windows"; then
- binfile="${binfile}.exe"
- fi
-
- tardir="${tool}-${os}-${arch}-${version}"
- tarfile="releases/${tool}-${os}-${arch}-${version}.tar.gz"
-
- if test "$D" = "linux/amd64"; then
- pie="-buildmode=pie"
- fi
-
- set -x
- GOOS=${os} GOARCH=${arch} go build -tags osusergo,netgo -ldflags "-extldflags=-static -w" --trimpath $pie -o ${binfile}
- strip --strip-all ${binfile}
-
- mkdir -p ${tardir}
- cp ${binfile} README.md LICENSE ${tardir}/
- echo 'tool = anydb
-PREFIX = /usr/local
-UID = root
-GID = 0
-
-install:
- install -d -o $(UID) -g $(GID) $(PREFIX)/bin
- install -d -o $(UID) -g $(GID) $(PREFIX)/man/man1
- install -o $(UID) -g $(GID) -m 555 $(tool) $(PREFIX)/sbin/
- install -o $(UID) -g $(GID) -m 444 $(tool).1 $(PREFIX)/man/man1/' > ${tardir}/Makefile
- tar cpzf ${tarfile} ${tardir}
- sha256sum ${binfile} | cut -d' ' -f1 > ${binfile}.sha256
- sha256sum ${tarfile} | cut -d' ' -f1 > ${tarfile}.sha256
- rm -rf ${tardir}
- set +x
-done
-
diff --git a/output/export.go b/output/export.go
deleted file mode 100644
index 27ea719..0000000
--- a/output/export.go
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
-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 .
-*/
-package output
-
-import (
- "encoding/json"
- "fmt"
- "os"
-
- "github.com/tlinden/anydb/app"
- "github.com/tlinden/anydb/cfg"
-)
-
-func WriteJSON(attr *app.DbAttr, conf *cfg.Config, entries app.DbEntries) error {
- jsonentries, err := json.Marshal(entries)
- if err != nil {
- return fmt.Errorf("failed to marshall json: %w", err)
- }
-
- if attr.File == "-" {
- fmt.Println(string(jsonentries))
- } else {
- fd, err := os.OpenFile(attr.File, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
- if err != nil {
- return fmt.Errorf("failed to open file %s for writing: %w", attr.File, err)
- }
-
- if _, err := fd.Write(jsonentries); err != nil {
- return fmt.Errorf("failed writing to file %s: %w", attr.File, err)
- }
-
- fmt.Printf("database contents exported to %s\n", attr.File)
- }
-
- return nil
-}
diff --git a/output/list.go b/output/list.go
deleted file mode 100644
index 0048930..0000000
--- a/output/list.go
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
-Copyright © 2025 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 .
-*/
-package output
-
-import (
- "bytes"
- "encoding/json"
- "errors"
- "fmt"
- "io"
- "strconv"
- "strings"
- tpl "text/template"
-
- "github.com/dustin/go-humanize"
- "github.com/olekukonko/tablewriter"
- "github.com/olekukonko/tablewriter/renderer"
- "github.com/olekukonko/tablewriter/tw"
- "github.com/tlinden/anydb/app"
- "github.com/tlinden/anydb/cfg"
-)
-
-func List(writer io.Writer, conf *cfg.Config, entries app.DbEntries) error {
- switch conf.Mode {
- case "wide", "", "table":
- return ListTable(writer, conf, entries)
- case "json":
- return ListJson(writer, conf, entries)
- case "template":
- return ListTemplate(writer, conf, entries)
- default:
- return errors.New("unsupported mode")
- }
-}
-
-func ListJson(writer io.Writer, conf *cfg.Config, entries app.DbEntries) error {
- jsonentries, err := json.Marshal(entries)
- if err != nil {
- return fmt.Errorf("failed marshall json: %s", err)
- }
-
- fmt.Println(string(jsonentries))
- return nil
-}
-
-func ListTemplate(writer io.Writer, conf *cfg.Config, entries app.DbEntries) error {
- tmpl, err := tpl.New("list").Parse(conf.Template)
- if err != nil {
- return fmt.Errorf("failed to parse output template: %w", err)
- }
-
- buf := bytes.Buffer{}
-
- for _, row := range entries {
- buf.Reset()
- err = tmpl.Execute(&buf, row)
- if err != nil {
- return fmt.Errorf("failed to execute output template: %w", err)
- }
-
- if buf.Len() > 0 {
- if _, err := fmt.Fprintln(writer, buf.String()); err != nil {
- return fmt.Errorf("failed to write output: %w", err)
- }
- }
- }
-
- return nil
-}
-
-func ListTable(writer io.Writer, conf *cfg.Config, entries app.DbEntries) error {
- tableString := &strings.Builder{}
-
- styleTSV := tw.NewSymbolCustom("space").WithColumn("\t")
-
- table := tablewriter.NewTable(tableString,
- tablewriter.WithRenderer(
- renderer.NewBlueprint(tw.Rendition{
- Borders: tw.BorderNone,
- Symbols: styleTSV,
- Settings: tw.Settings{
- Separators: tw.Separators{BetweenRows: tw.Off, BetweenColumns: tw.On},
- Lines: tw.Lines{ShowFooterLine: tw.Off, ShowHeaderLine: tw.Off},
- },
- })),
- tablewriter.WithConfig(tablewriter.Config{
- Header: tw.CellConfig{
- Formatting: tw.CellFormatting{
- AutoFormat: tw.Off,
- },
- Padding: tw.CellPadding{
- Global: tw.Padding{Left: "", Right: ""},
- },
- },
- Row: tw.CellConfig{
- Formatting: tw.CellFormatting{
- AutoWrap: tw.WrapNone,
- Alignment: tw.AlignLeft,
- },
- Padding: tw.CellPadding{
- Global: tw.Padding{Left: "", Right: ""},
- },
- },
- }),
- tablewriter.WithPadding(tw.PaddingNone),
- )
-
- if !conf.NoHeaders {
- if conf.Mode == "wide" {
- table.Header([]string{"KEY", "TAGS", "SIZE", "UPDATED", "VALUE"})
- } else {
- table.Header([]string{"KEY", "VALUE"})
- }
- }
-
- for _, row := range entries {
- if conf.Mode == "wide" {
- switch conf.NoHumanize {
- case true:
- if err :=
- table.Append([]string{
- row.Key,
- strings.Join(row.Tags, ","),
- strconv.FormatUint(row.Size, 10),
- row.Created.AsTime().Format("02.01.2006T03:04.05"),
- row.Preview,
- }); err != nil {
- return fmt.Errorf("failed to add data to table: %w", err)
- }
- default:
- if err := table.Append([]string{
- row.Key,
- strings.Join(row.Tags, ","),
- humanize.Bytes(uint64(row.Size)),
- humanize.Time(row.Created.AsTime()),
- row.Preview,
- }); err != nil {
- return fmt.Errorf("failed to add data to table: %w", err)
- }
- }
-
- } else {
- if err := table.Append([]string{row.Key, row.Preview}); err != nil {
- return fmt.Errorf("failed to add data to table: %w", err)
- }
- }
- }
-
- if err := table.Render(); err != nil {
- return fmt.Errorf("failed to render table: %w", err)
- }
-
- if _, err := fmt.Fprint(writer, tableString.String()); err != nil {
- return fmt.Errorf("failed to write output: %w", err)
- }
-
- return nil
-}
diff --git a/output/single.go b/output/single.go
deleted file mode 100644
index 352ad9f..0000000
--- a/output/single.go
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
-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 .
-*/
-package output
-
-import (
- "encoding/json"
- "fmt"
- "io"
- "log"
- "os"
- "reflect"
-
- "github.com/dustin/go-humanize"
- "github.com/tlinden/anydb/app"
- "github.com/tlinden/anydb/cfg"
- "golang.org/x/term"
- //"github.com/alecthomas/repr"
-)
-
-func Print(writer io.Writer, conf *cfg.Config, attr *app.DbAttr, entry *app.DbEntry) error {
- if attr.File != "" {
- return WriteFile(writer, conf, attr, entry)
- }
-
- isatty := term.IsTerminal(int(os.Stdout.Fd()))
-
- switch conf.Mode {
- case "simple", "":
- if entry.Binary {
- if isatty {
- fmt.Println("binary data omitted")
- } else {
- if _, err := os.Stdout.WriteString(entry.Value); err != nil {
- return err
- }
- }
- } else {
- fmt.Print(string(entry.Value))
- if entry.Value[entry.Size-1] != '\n' {
- // always add a terminal newline
- fmt.Println()
- }
- }
- case "json":
- jsonentry, err := json.Marshal(entry)
- if err != nil {
- return fmt.Errorf("failed to marshall json: %s", err)
- }
-
- fmt.Println(string(jsonentry))
- case "wide":
- return ListTable(writer, conf, app.DbEntries{entry})
- case "template":
- return ListTemplate(writer, conf, app.DbEntries{entry})
- }
-
- return nil
-}
-
-func WriteFile(writer io.Writer, conf *cfg.Config, attr *app.DbAttr, entry *app.DbEntry) error {
- var fileHandle *os.File
- var err error
-
- 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 func() {
- if err := fd.Close(); err != nil {
- log.Fatal(err)
- }
- }()
-
- fileHandle = fd
- }
-
- // actually write file content
- _, err = fileHandle.WriteString(entry.Value)
-
- if !entry.Binary {
- if entry.Value[entry.Size-1] != '\n' {
- // always add a terminal newline
- _, err = fileHandle.Write([]byte{'\n'})
- }
- }
-
- if err != nil {
- return fmt.Errorf("failed to write to file %s: %w", attr.File, err)
- }
-
- return nil
-}
-
-func Info(writer io.Writer, conf *cfg.Config, info *app.DbInfo) error {
- if _, err := fmt.Fprintf(writer, "Database: %s\n", info.Path); err != nil {
- return fmt.Errorf("failed to write output: %w", err)
- }
-
- for _, bucket := range info.Buckets {
- if conf.NoHumanize {
- if _, err := fmt.Fprintf(
- writer,
- "%19s: %s\n%19s: %d\n%19s: %d\n%19s: %t\n",
- "Bucket", bucket.Name,
- "Size", bucket.Size,
- "Keys", bucket.Keys,
- "Encrypted", conf.Encrypt); err != nil {
- return fmt.Errorf("failed to write output: %w", err)
- }
- } else {
- if _, err := fmt.Fprintf(
- writer,
- "%19s: %s\n%19s: %s\n%19s: %d\n",
- "Bucket", bucket.Name,
- "Size", humanize.Bytes(uint64(bucket.Size)),
- "Keys", bucket.Keys); err != nil {
- return fmt.Errorf("failed to write output: %w", err)
- }
- }
-
- if conf.Debug {
- val := reflect.ValueOf(&bucket.Stats).Elem()
- for i := 0; i < val.NumField(); i++ {
- if _, err := fmt.Fprintf(writer, "%19s: %v\n", val.Type().Field(i).Name, val.Field(i)); err != nil {
- return fmt.Errorf("failed to write output: %w", err)
- }
- }
- }
-
- if _, err := fmt.Fprintln(writer); err != nil {
- return fmt.Errorf("failed to write output: %w", err)
- }
- }
-
- return nil
-}
diff --git a/rest/handlers.go b/rest/handlers.go
deleted file mode 100644
index d49e4de..0000000
--- a/rest/handlers.go
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
-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 .
-*/
-package rest
-
-import (
- //"github.com/alecthomas/repr"
-
- "github.com/gofiber/fiber/v2"
- "github.com/tlinden/anydb/app"
- "github.com/tlinden/anydb/cfg"
-)
-
-type SetContext struct {
- Query string `json:"query" form:"query"`
-}
-
-type ListResponse struct {
- Success bool
- Code int
- Entries app.DbEntries
-}
-
-type SingleResponse struct {
- Success bool
- Code int
- Entry *app.DbEntry
-}
-
-func RestList(c *fiber.Ctx, conf *cfg.Config) error {
- attr := new(app.DbAttr)
-
- if len(c.Body()) > 0 {
- if err := c.BodyParser(attr); err != nil {
- return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{
- "errors": err.Error(),
- })
-
- }
- }
-
- // get list
- entries, err := conf.DB.List(attr, attr.Fulltext)
- if err != nil {
- return JsonStatus(c, fiber.StatusForbidden,
- "Unable to list keys: "+err.Error())
- }
-
- return c.Status(fiber.StatusOK).JSON(
- ListResponse{
- Success: true,
- Code: fiber.StatusOK,
- Entries: entries,
- },
- )
-}
-
-func RestGet(c *fiber.Ctx, conf *cfg.Config) error {
- if c.Params("key") == "" {
- return JsonStatus(c, fiber.StatusForbidden,
- "key not provided")
- }
-
- // get list
- entry, err := conf.DB.Get(&app.DbAttr{Key: c.Params("key")})
- if err != nil {
- return JsonStatus(c, fiber.StatusForbidden,
- "Unable to get key: "+err.Error())
- }
- if entry.Key == "" {
- return JsonStatus(c, fiber.StatusForbidden,
- "Key does not exist")
- }
-
- return c.Status(fiber.StatusOK).JSON(
- SingleResponse{
- Success: true,
- Code: fiber.StatusOK,
- Entry: entry,
- },
- )
-}
-
-func RestDelete(c *fiber.Ctx, conf *cfg.Config) error {
- if c.Params("key") == "" {
- return JsonStatus(c, fiber.StatusForbidden,
- "key not provided")
- }
-
- // get list
- err := conf.DB.Del(&app.DbAttr{Key: c.Params("key")})
- if err != nil {
- return JsonStatus(c, fiber.StatusForbidden,
- "Unable to delete key: "+err.Error())
- }
-
- return c.Status(fiber.StatusOK).JSON(
- Result{
- Success: true,
- Code: fiber.StatusOK,
- Message: "key deleted",
- },
- )
-}
-
-func RestSet(c *fiber.Ctx, conf *cfg.Config) error {
- attr := new(app.DbAttr)
- if err := c.BodyParser(attr); err != nil {
- return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{
- "errors": err.Error(),
- })
-
- }
-
- err := conf.DB.Set(attr)
- if err != nil {
- return JsonStatus(c, fiber.StatusForbidden,
- "Unable to set key: "+err.Error())
- }
-
- return c.Status(fiber.StatusOK).JSON(
- Result{
- Success: true,
- Code: fiber.StatusOK,
- },
- )
-}
diff --git a/rest/serve.go b/rest/serve.go
deleted file mode 100644
index 837b08b..0000000
--- a/rest/serve.go
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
-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 .
-*/
-package rest
-
-import (
- "github.com/gofiber/fiber/v2"
- "github.com/gofiber/fiber/v2/middleware/compress"
- "github.com/gofiber/fiber/v2/middleware/cors"
- "github.com/gofiber/fiber/v2/middleware/logger"
- "github.com/tlinden/anydb/cfg"
-)
-
-// used to return to the api client
-type Result struct {
- Success bool `json:"success"`
- Message string `json:"message"`
- Code int `json:"code"`
-}
-
-func Runserver(conf *cfg.Config, args []string) error {
- // setup api server
- router := SetupServer(conf)
-
- // public rest api routes
- api := router.Group("/anydb/v1")
- {
- api.Get("/", func(c *fiber.Ctx) error {
- 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 {
- return RestGet(c, conf)
- })
-
- api.Delete("/:key", func(c *fiber.Ctx) error {
- return RestDelete(c, conf)
- })
-
- api.Put("/", func(c *fiber.Ctx) error {
- return RestSet(c, conf)
- })
- }
-
- // public routes
- {
- router.Get("/", func(c *fiber.Ctx) error {
- return c.Send([]byte("Use the REST API"))
- })
- }
-
- return router.Listen(conf.Listen)
-}
-
-func SetupServer(conf *cfg.Config) *fiber.App {
- // disable colors
- fiber.DefaultColors = fiber.Colors{}
-
- router := fiber.New(fiber.Config{
- CaseSensitive: true,
- StrictRouting: true,
- Immutable: true,
- ServerHeader: "anydb serve",
- AppName: "anydb",
- })
-
- router.Use(logger.New(logger.Config{
- Format: "${pid} ${ip}:${port} ${status} - ${method} ${path}\n",
- DisableColors: true,
- }))
-
- router.Use(cors.New(cors.Config{
- AllowMethods: "GET,PUT,POST,DELETE",
- ExposeHeaders: "Content-Type,Accept",
- }))
-
- router.Use(compress.New(compress.Config{
- Level: compress.LevelBestSpeed,
- }))
-
- return router
-}
-
-/*
-Wrapper to respond with proper json status, message and code,
-shall be prepared and called by the handlers directly.
-*/
-func JsonStatus(c *fiber.Ctx, code int, msg string) error {
- success := code == fiber.StatusOK
-
- return c.Status(code).JSON(Result{
- Code: code,
- Message: msg,
- Success: success,
- })
-}
diff --git a/t/crypt.txtar b/t/crypt.txtar
deleted file mode 100644
index 235d413..0000000
--- a/t/crypt.txtar
+++ /dev/null
@@ -1,37 +0,0 @@
-#
-# Copyright © 2025 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 .
-#
-
-# encrypt something
-exec env ANYDB_PASSWORD=12345 anydb -f test.db set -e secret eshishinusan
-
-# retrieve it
-exec env ANYDB_PASSWORD=12345 anydb -f test.db get secret
-stdout eshishinusan
-
-# but has it really been encrypted?
-! exec env ANYDB_PASSWORD=8d8d8 anydb -f test.db get secret
-! stdout eshishinusan
-stderr 'message authentication failed'
-
-# what about the listing
-exec anydb -f test.db ls -l
-stdout 'encrypted-content'
-! stdout eshishinusan
-
-# and the export?
-exec anydb -f test.db export -o -
-! stdout eshishinusan
diff --git a/t/files.txtar b/t/files.txtar
deleted file mode 100644
index 58bbaec..0000000
--- a/t/files.txtar
+++ /dev/null
@@ -1,51 +0,0 @@
-#
-# 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 .
-#
-
-# simple file, we cannot use redirection here, so dd is our friend
-exec dd if=/dev/random of=file.txt count=5 bs=10
-
-# add file to db
-exec anydb -f test.db set datum -r file.txt
-
-# check for existence
-exec anydb -f test.db get datum -o out.txt
-exists out.txt
-
-# check if its filled (50 bytes == count=5 x bs=10)
-exec ls -l out.txt
-stdout 50
-
-# look if it's inside the db
-exec anydb -f test.db ls
-stdout datum.*binary-content
-
-# do the same thing with text content, start with a new text entry
-exec anydb -f test.db set feed alpha
-
-# which we write to a file
-exec anydb -f test.db get feed -o out2.txt
-exists out2.txt
-
-# check if its filled (5 bytes + newline)
-exec ls -l out2.txt
-stdout 6
-
-# compare content
-exec cat out2.txt
-stdout alpha
-
-
diff --git a/t/interface.txtar b/t/interface.txtar
deleted file mode 100644
index f5bd861..0000000
--- a/t/interface.txtar
+++ /dev/null
@@ -1,25 +0,0 @@
-#
-# 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 .
-#
-
-# check default outputs
-
-exec anydb -v
-stdout 'This is anydb version'
-
-! exec anydb
-stderr 'Available Commands:'
-
diff --git a/t/restore.txtar b/t/restore.txtar
deleted file mode 100644
index e57f90e..0000000
--- a/t/restore.txtar
+++ /dev/null
@@ -1,32 +0,0 @@
-#
-# 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 .
-#
-
-# setup simple db
-exec anydb -f test.db set foo bar
-
-# create backup
-exec anydb -f test.db export -o backup.json
-stdout 'database contents exported to backup.json'
-
-# import into new db
-exec anydb -f new.db import -i backup.json
-stdout 'imported.*entries'
-
-# check contents
-exec anydb -f new.db list bar -s
-stdout foo.*bar
-
diff --git a/t/workflow.txtar b/t/workflow.txtar
deleted file mode 100644
index 8693d11..0000000
--- a/t/workflow.txtar
+++ /dev/null
@@ -1,81 +0,0 @@
-#
-# 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 .
-#
-
-# simple entry
-exec anydb -f test.db set foo bar
-
-# single entry uc()
-exec anydb -f test.db set MUCHAS gracias
-
-# entry with tags
-exec anydb -f test.db set color grey -t flower,plant
-
-# simple list
-exec anydb -f test.db list
-stdout foo.*bar
-
-# wide list
-exec anydb -f test.db list -m wide
-stdout 'plant.*now.*grey'
-
-# list tagged
-exec anydb -f test.db list -t flower
-! stdout bar
-
-# list with filter
-exec anydb -f test.db list b.r -s
-stdout bar
-
-# list with -i filter
-exec anydb -f test.db list -is mucha
-stdout mucha
-
-# get single entry
-exec anydb -f test.db get color
-stdout grey
-
-# modify
-exec anydb -f test.db set foo blah
-
-# check modified
-exec anydb -f test.db get foo
-stdout blah
-
-# modify tagged
-exec anydb -f test.db set color grey -t butterfly
-
-# check modified tagged
-exec anydb -f test.db list -t butterfly
-stdout grey
-
-# check modified tagged, make sure
-exec anydb -f test.db list -t flower
-! stdout grey
-
-# check json output
-exec anydb -f test.db list -m json
-stdout '^\[\{'
-
-exec anydb -f test.db get color -m json
-stdout '^\{'
-
-# delete entry
-exec anydb -f test.db del foo
-
-# check deleted
-exec anydb -f test.db list
-! stdout blah