Compare commits
1 commit
master
...
db_refacto
| Author | SHA1 | Date | |
|---|---|---|---|
| e47a874711 |
1201 changed files with 154751 additions and 228029 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -1,5 +1,4 @@
|
|||
/docstore
|
||||
/docstore_*
|
||||
docstore
|
||||
tmp
|
||||
run.sh
|
||||
TODO.txt
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
Copyright (C) 2021 Marius Schellenberger
|
||||
Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
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
|
||||
|
|
|
|||
57
Makefile
57
Makefile
|
|
@ -1,55 +1,44 @@
|
|||
CC=go
|
||||
BUILD=build -v -trimpath
|
||||
BUILD=build -v
|
||||
VERSION=$(shell cat VERSION)
|
||||
GCFLAGS=-gcflags '-e'
|
||||
LDFLAGS=-ldflags '-buildid=$(VERSION) -X main.version=$(VERSION) -s -w'
|
||||
GCFLAGS=-gcflags 'all=-e'
|
||||
LDFLAGS=-ldflags '-X main.version=$(VERSION) -s -w'
|
||||
PROGRAM=docstore
|
||||
CMD=-o $(PROGRAM) cmd/docstore/main.go
|
||||
ENV=GOGC=0 CGO_ENABLED=0 GO111MODULE=on
|
||||
|
||||
arch:
|
||||
GOOS=openbsd make release PROGRAM=docstore_openbsd_amd64
|
||||
GOOS=linux make release PROGRAM=docstore_linux_amd64
|
||||
GOOS=linux GOARCH=arm64 make release PROGRAM=docstore_linux_arm64
|
||||
ENV=CGO_ENABLED=0 GO111MODULE=on
|
||||
|
||||
all: $(PROGRAM)
|
||||
|
||||
setup:
|
||||
$(CC) install github.com/client9/misspell/cmd/misspell@latest
|
||||
$(CC) install honnef.co/go/tools/cmd/staticcheck@latest
|
||||
$(CC) get github.com/client9/misspell
|
||||
|
||||
release: clean gofmt codeqa
|
||||
$(ENV) $(CC) $(BUILD) $(GCFLAGS) $(LDFLAGS) $(CMD)
|
||||
release: clean generate gofmt codeqa
|
||||
$(ENV) $(CC) $(BUILD) $(LDFLAGS)
|
||||
|
||||
release-vendor: clean gofmt codeqa
|
||||
$(ENV) $(CC) $(BUILD) -mod=vendor $(GCFLAGS) $(LDFLAGS) $(CMD)
|
||||
release-vendor: clean generate gofmt codeqa
|
||||
$(ENV) $(CC) $(BUILD) -mod=vendor $(LDFLAGS)
|
||||
|
||||
vendor: clean gofmt
|
||||
$(ENV) $(CC) $(BUILD) -mod=vendor $(GCFLAGS) $(LDFLAGS) $(CMD)
|
||||
vendor: clean generate gofmt
|
||||
$(ENV) $(CC) $(BUILD) -mod=vendor $(LDFLAGS)
|
||||
|
||||
$(PROGRAM): clean gofmt
|
||||
$(ENV) $(CC) $(BUILD) $(GCFLAGS) $(LDFLAGS) $(CMD)
|
||||
$(PROGRAM): clean generate gofmt
|
||||
$(ENV) $(CC) $(BUILD) $(GCFLAGS) $(LDFLAGS)
|
||||
|
||||
clean:
|
||||
$(CC) clean -x
|
||||
|
||||
codeqa: misspell golint test
|
||||
|
||||
generate:
|
||||
$(CC) generate
|
||||
|
||||
gofmt:
|
||||
gofmt -w pkg/ cmd/
|
||||
gofmt -w pkg/ main.go
|
||||
|
||||
codeqa: govet misspell staticcheck test
|
||||
|
||||
govet:
|
||||
$(CC) vet ./...
|
||||
|
||||
update:
|
||||
$(ENV) $(CC) get -u ./...
|
||||
golint:
|
||||
$(GOPATH)/bin/golint .
|
||||
|
||||
misspell:
|
||||
$(GOPATH)/bin/misspell cmd/* pkg/* Makefile README.md sri.sh
|
||||
|
||||
staticcheck:
|
||||
/bin/true
|
||||
# $(GOPATH)/bin/staticcheck ./...
|
||||
$(GOPATH)/bin/misspell pkg/* templates/* main.go Makefile README.md sri.sh templates.sh
|
||||
|
||||
test:
|
||||
ifeq ($(shell go env GOARCH), $(shell go env GOHOSTARCH))
|
||||
|
|
@ -62,4 +51,4 @@ else
|
|||
$(info skipping tests of other platforms)
|
||||
endif
|
||||
|
||||
.PHONY: arch setup release release-vendor vendor build clean codeqa gofmt govet misspell staticcheck test
|
||||
.PHONY: setup release release-vendor vendor build clean codeqa generate gofmt golint misspell test
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
**Requirements:**
|
||||
|
||||
* Go (golang) >= 1.16
|
||||
* Go (golang) >= 1.12
|
||||
|
||||
Linux:
|
||||
|
||||
|
|
|
|||
3
TODO
3
TODO
|
|
@ -1,3 +0,0 @@
|
|||
implement pdftoppm to make pdfs readable by tesseract
|
||||
pdftoppm -tiff -r 300 "$pdf_in" "$temp_dir/pg"
|
||||
|
||||
67
go.mod
67
go.mod
|
|
@ -1,44 +1,37 @@
|
|||
module git.giftfish.de/ston1th/docstore
|
||||
|
||||
go 1.24.0
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
git.giftfish.de/ston1th/authdav v1.1.0
|
||||
git.giftfish.de/ston1th/godrop/v2 v2.2.0
|
||||
git.giftfish.de/ston1th/jwt/v3 v3.3.0
|
||||
git.giftfish.de/ston1th/godrop/v2 v2.1.0
|
||||
git.giftfish.de/ston1th/jwt/v3 v3.2.0
|
||||
github.com/RoaringBitmap/roaring v0.4.17 // indirect
|
||||
github.com/bcampbell/qs v0.0.0-20161004022730-15caa99abd01
|
||||
github.com/blevesearch/bleve v1.0.14
|
||||
github.com/go-logr/logr v1.4.3
|
||||
github.com/gorilla/mux v1.8.1
|
||||
github.com/pquerna/otp v1.5.0
|
||||
github.com/urfave/cli v1.22.17
|
||||
go.etcd.io/bbolt v1.4.3
|
||||
golang.org/x/crypto v0.43.0
|
||||
golang.org/x/net v0.46.0
|
||||
k8s.io/klog/v2 v2.130.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/RoaringBitmap/roaring v1.9.4 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.24.1 // indirect
|
||||
github.com/blevesearch/go-porterstemmer v1.0.3 // indirect
|
||||
github.com/blevesearch/mmap-go v1.0.4 // indirect
|
||||
github.com/blevesearch/segment v0.9.1 // indirect
|
||||
github.com/blevesearch/snowballstem v0.9.0 // indirect
|
||||
github.com/blevesearch/zap/v11 v11.0.14 // indirect
|
||||
github.com/blevesearch/zap/v12 v12.0.14 // indirect
|
||||
github.com/blevesearch/zap/v13 v13.0.6 // indirect
|
||||
github.com/blevesearch/zap/v14 v14.0.5 // indirect
|
||||
github.com/blevesearch/zap/v15 v15.0.3 // indirect
|
||||
github.com/boombuler/barcode v1.1.0 // indirect
|
||||
github.com/couchbase/vellum v1.0.2 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/golang/snappy v1.0.0 // indirect
|
||||
github.com/mschoch/smat v0.2.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/steveyen/gtreap v0.1.0 // indirect
|
||||
github.com/willf/bitset v1.1.11 // indirect
|
||||
golang.org/x/sys v0.37.0 // indirect
|
||||
google.golang.org/protobuf v1.36.10 // indirect
|
||||
github.com/blevesearch/bleve v0.8.0
|
||||
github.com/blevesearch/blevex v0.0.0-20180227211930-4b158bb555a3 // indirect
|
||||
github.com/blevesearch/go-porterstemmer v1.0.2 // indirect
|
||||
github.com/blevesearch/segment v0.0.0-20160915185041-762005e7a34f // indirect
|
||||
github.com/boltdb/bolt v1.3.1
|
||||
github.com/couchbase/vellum v0.0.0-20190328134517-462e86d8716b // indirect
|
||||
github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d // indirect
|
||||
github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 // indirect
|
||||
github.com/cznic/strutil v0.0.0-20181122101858-275e90344537 // indirect
|
||||
github.com/edsrzf/mmap-go v1.0.0 // indirect
|
||||
github.com/etcd-io/bbolt v1.3.3 // indirect
|
||||
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 // indirect
|
||||
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect
|
||||
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 // indirect
|
||||
github.com/golang/protobuf v1.3.1 // indirect
|
||||
github.com/gorilla/mux v1.7.1
|
||||
github.com/jmhodges/levigo v1.0.0 // indirect
|
||||
github.com/pquerna/otp v1.2.0
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20190321074620-2f0d2b0e0001 // indirect
|
||||
github.com/steveyen/gtreap v0.0.0-20150807155958-0abe01ef9be2 // indirect
|
||||
github.com/syndtr/goleveldb v1.0.0 // indirect
|
||||
github.com/tecbot/gorocksdb v0.0.0-20181010114359-8752a9433481 // indirect
|
||||
github.com/urfave/cli v1.20.0
|
||||
go.etcd.io/bbolt v1.3.3 // indirect
|
||||
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297
|
||||
)
|
||||
|
|
|
|||
218
go.sum
218
go.sum
|
|
@ -1,182 +1,124 @@
|
|||
git.giftfish.de/ston1th/authdav v1.1.0 h1:hGNmaw/bNKETcV02JaS6NQaf3cKVFc3iyQ32QaAwXeo=
|
||||
git.giftfish.de/ston1th/authdav v1.1.0/go.mod h1:UAVoOXY6jAVh0rOnIwy4wVWpLIPAom/EoptPkAhX1nQ=
|
||||
git.giftfish.de/ston1th/godrop/v2 v2.2.0 h1:jdcPJeotiuug3tC4zXvwnauen1Y7kfF9CbFKQbGttYU=
|
||||
git.giftfish.de/ston1th/godrop/v2 v2.2.0/go.mod h1:gDAD0cWURkN0cMAjjSKB6a6jqIXvOrY8Th25HdoJAyg=
|
||||
git.giftfish.de/ston1th/jwt/v3 v3.3.0 h1:lSBVbk1D+KmLHBlxhS7snK33+U975cNbx/Z4Md6S+0A=
|
||||
git.giftfish.de/ston1th/jwt/v3 v3.3.0/go.mod h1:jDALrW7OKeIaqYuMxs3GWyPRnQZmk/R+Lu9p/Em3Ziw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/RoaringBitmap/roaring v0.4.23/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo=
|
||||
github.com/RoaringBitmap/roaring v1.9.4 h1:yhEIoH4YezLYT04s1nHehNO64EKFTop/wBhxv2QzDdQ=
|
||||
github.com/RoaringBitmap/roaring v1.9.4/go.mod h1:6AXUsoIEzDTFFQCe1RbGA6uFONMhvejWj5rqITANK90=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
git.giftfish.de/ston1th/godrop/v2 v2.1.0 h1:vR4aWVeMGJ+tHQH0p44esZrSl5dYJzuCmZqhKWP/vwI=
|
||||
git.giftfish.de/ston1th/godrop/v2 v2.1.0/go.mod h1:z8KA3HP/vB04wddLXZfYVKURYDxBGau9hlHtV61x9mM=
|
||||
git.giftfish.de/ston1th/jwt/v3 v3.2.0 h1:V6Lh7OjXn4D3HPd/LoDf7w5N7cyiSXEPAQCIfSVZ05c=
|
||||
git.giftfish.de/ston1th/jwt/v3 v3.2.0/go.mod h1:jDALrW7OKeIaqYuMxs3GWyPRnQZmk/R+Lu9p/Em3Ziw=
|
||||
github.com/RoaringBitmap/roaring v0.4.17 h1:oCYFIFEMSQZrLHpywH7919esI1VSrQZ0pJXkZPGIJ78=
|
||||
github.com/RoaringBitmap/roaring v0.4.17/go.mod h1:D3qVegWTmfCaX4Bl5CrBE9hfrSrrXIr8KVNvRsDi1NI=
|
||||
github.com/bcampbell/qs v0.0.0-20161004022730-15caa99abd01 h1:ZkEYngtmNszw0BJt4GoWW5rdn5U6MaFlLAUD4S3KEas=
|
||||
github.com/bcampbell/qs v0.0.0-20161004022730-15caa99abd01/go.mod h1:ZgSGz2VRprXTqsfbn6HAH1ePnGiVCrlA3kflJ6Sjxqg=
|
||||
github.com/bits-and-blooms/bitset v1.12.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
||||
github.com/bits-and-blooms/bitset v1.24.1 h1:hqnfFbjjk3pxGa5E9Ho3hjoU7odtUuNmJ9Ao+Bo8s1c=
|
||||
github.com/bits-and-blooms/bitset v1.24.1/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
||||
github.com/blevesearch/bleve v1.0.14 h1:Q8r+fHTt35jtGXJUM0ULwM3Tzg+MRfyai4ZkWDy2xO4=
|
||||
github.com/blevesearch/bleve v1.0.14/go.mod h1:e/LJTr+E7EaoVdkQZTfoz7dt4KoDNvDbLb8MSKuNTLQ=
|
||||
github.com/blevesearch/blevex v1.0.0 h1:pnilj2Qi3YSEGdWgLj1Pn9Io7ukfXPoQcpAI1Bv8n/o=
|
||||
github.com/blevesearch/blevex v1.0.0/go.mod h1:2rNVqoG2BZI8t1/P1awgTKnGlx5MP9ZbtEciQaNhswc=
|
||||
github.com/blevesearch/cld2 v0.0.0-20200327141045-8b5f551d37f5/go.mod h1:PN0QNTLs9+j1bKy3d/GB/59wsNBFC4sWLWG3k69lWbc=
|
||||
github.com/blevesearch/go-porterstemmer v1.0.3 h1:GtmsqID0aZdCSNiY8SkuPJ12pD4jI+DdXTAn4YRcHCo=
|
||||
github.com/blevesearch/go-porterstemmer v1.0.3/go.mod h1:angGc5Ht+k2xhJdZi511LtmxuEf0OVpvUUNrwmM1P7M=
|
||||
github.com/blevesearch/mmap-go v1.0.2/go.mod h1:ol2qBqYaOUsGdm7aRMRrYGgPvnwLe6Y+7LMvAB5IbSA=
|
||||
github.com/blevesearch/mmap-go v1.0.4 h1:OVhDhT5B/M1HNPpYPBKIEJaD0F3Si+CrEKULGCDPWmc=
|
||||
github.com/blevesearch/mmap-go v1.0.4/go.mod h1:EWmEAOmdAS9z/pi/+Toxu99DnsbhG1TIxUoRmJw/pSs=
|
||||
github.com/blevesearch/segment v0.9.0/go.mod h1:9PfHYUdQCgHktBgvtUOF4x+pc4/l8rdH0u5spnW85UQ=
|
||||
github.com/blevesearch/segment v0.9.1 h1:+dThDy+Lvgj5JMxhmOVlgFfkUtZV2kw49xax4+jTfSU=
|
||||
github.com/blevesearch/segment v0.9.1/go.mod h1:zN21iLm7+GnBHWTao9I+Au/7MBiL8pPFtJBJTsk6kQw=
|
||||
github.com/blevesearch/snowballstem v0.9.0 h1:lMQ189YspGP6sXvZQ4WZ+MLawfV8wOmPoD/iWeNXm8s=
|
||||
github.com/blevesearch/snowballstem v0.9.0/go.mod h1:PivSj3JMc8WuaFkTSRDW2SlrulNWPl4ABg1tC/hlgLs=
|
||||
github.com/blevesearch/zap/v11 v11.0.14 h1:IrDAvtlzDylh6H2QCmS0OGcN9Hpf6mISJlfKjcwJs7k=
|
||||
github.com/blevesearch/zap/v11 v11.0.14/go.mod h1:MUEZh6VHGXv1PKx3WnCbdP404LGG2IZVa/L66pyFwnY=
|
||||
github.com/blevesearch/zap/v12 v12.0.14 h1:2o9iRtl1xaRjsJ1xcqTyLX414qPAwykHNV7wNVmbp3w=
|
||||
github.com/blevesearch/zap/v12 v12.0.14/go.mod h1:rOnuZOiMKPQj18AEKEHJxuI14236tTQ1ZJz4PAnWlUg=
|
||||
github.com/blevesearch/zap/v13 v13.0.6 h1:r+VNSVImi9cBhTNNR+Kfl5uiGy8kIbb0JMz/h8r6+O4=
|
||||
github.com/blevesearch/zap/v13 v13.0.6/go.mod h1:L89gsjdRKGyGrRN6nCpIScCvvkyxvmeDCwZRcjjPCrw=
|
||||
github.com/blevesearch/zap/v14 v14.0.5 h1:NdcT+81Nvmp2zL+NhwSvGSLh7xNgGL8QRVZ67njR0NU=
|
||||
github.com/blevesearch/zap/v14 v14.0.5/go.mod h1:bWe8S7tRrSBTIaZ6cLRbgNH4TUDaC9LZSpRGs85AsGY=
|
||||
github.com/blevesearch/zap/v15 v15.0.3 h1:Ylj8Oe+mo0P25tr9iLPp33lN6d4qcztGjaIsP51UxaY=
|
||||
github.com/blevesearch/zap/v15 v15.0.3/go.mod h1:iuwQrImsh1WjWJ0Ue2kBqY83a0rFtJTqfa9fp1rbVVU=
|
||||
github.com/blevesearch/bleve v0.8.0 h1:DCoCrxscCXrlzVWK92k7Vq4d28lTAFuigVmcgIX0VCo=
|
||||
github.com/blevesearch/bleve v0.8.0/go.mod h1:Y2lmIkzV6mcNfAnAdOd+ZxHkHchhBfU/xroGIp61wfw=
|
||||
github.com/blevesearch/blevex v0.0.0-20180227211930-4b158bb555a3 h1:U6vnxZrTfItfiUiYx0lf/LgHjRSfaKK5QHSom3lEbnA=
|
||||
github.com/blevesearch/blevex v0.0.0-20180227211930-4b158bb555a3/go.mod h1:WH+MU2F4T0VmSdaPX+Wu5GYoZBrYWdOZWSjzvYcDmqQ=
|
||||
github.com/blevesearch/go-porterstemmer v1.0.2 h1:qe7n69gBd1OLY5sHKnxQHIbzn0LNJA4hpAf+5XDxV2I=
|
||||
github.com/blevesearch/go-porterstemmer v1.0.2/go.mod h1:haWQqFT3RdOGz7PJuM3or/pWNJS1pKkoZJWCkWu0DVA=
|
||||
github.com/blevesearch/segment v0.0.0-20160915185041-762005e7a34f h1:kqbi9lqXLLs+zfWlgo1PIiRQ86n33K1JKotjj4rSYOg=
|
||||
github.com/blevesearch/segment v0.0.0-20160915185041-762005e7a34f/go.mod h1:IInt5XRvpiGE09KOk9mmCMLjHhydIhNPKPPFLFBB7L8=
|
||||
github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4=
|
||||
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||
github.com/boombuler/barcode v1.1.0 h1:ChaYjBR63fr4LFyGn8E8nt7dBSt3MiU3zMOZqFvVkHo=
|
||||
github.com/boombuler/barcode v1.1.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/couchbase/ghistogram v0.1.0/go.mod h1:s1Jhy76zqfEecpNWJfWUiKZookAFaiGOEoyzgHt9i7k=
|
||||
github.com/couchbase/moss v0.1.0/go.mod h1:9MaHIaRuy9pvLPUJxB8sh8OrLfyDczECVL37grCIubs=
|
||||
github.com/couchbase/vellum v1.0.2 h1:BrbP0NKiyDdndMPec8Jjhy0U47CZ0Lgx3xUC2r9rZqw=
|
||||
github.com/couchbase/vellum v1.0.2/go.mod h1:FcwrEivFpNi24R3jLOs3n+fs5RnuQnQqCLBJ1uAg1W4=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/couchbase/vellum v0.0.0-20190328134517-462e86d8716b h1:WgO2yt0pCXyPgU3K/pnY1qO/8FP8TxBAaYXqa+BQpx0=
|
||||
github.com/couchbase/vellum v0.0.0-20190328134517-462e86d8716b/go.mod h1:prYTC8EgTu3gwbqJihkud9zRXISvyulAplQ6exdCo1g=
|
||||
github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d h1:SwD98825d6bdB+pEuTxWOXiSjBrHdOl/UVp75eI7JT8=
|
||||
github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8=
|
||||
github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 h1:iwZdTE0PVqJCos1vaoKsclOGD3ADKpshg3SRtYBbwso=
|
||||
github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM=
|
||||
github.com/cznic/strutil v0.0.0-20181122101858-275e90344537 h1:MZRmHqDBd0vxNwenEbKSQqRVT24d3C05ft8kduSwlqM=
|
||||
github.com/cznic/strutil v0.0.0-20181122101858-275e90344537/go.mod h1:AHHPPPXTw0h6pVabbcbyGRK1DckRn7r/STdZEeIDzZc=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
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/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64=
|
||||
github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw=
|
||||
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||
github.com/etcd-io/bbolt v1.3.3 h1:gSJmxrs37LgTqR/oyJBWok6k6SvXEUerFTbltIhXkBM=
|
||||
github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
|
||||
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 h1:0JZ+dUmQeA8IIVUMzysrX4/AKuQwWhV2dYQuPZdvdSQ=
|
||||
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64=
|
||||
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A=
|
||||
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg=
|
||||
github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0=
|
||||
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 h1:E2s37DuLxFhQDg5gKsWoLBOB0n+ZW8s599zru8FJ2/Y=
|
||||
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2 h1:Ujru1hufTHVb++eG6OuNDKMxZnGIvF6o/u8q/8h2+I4=
|
||||
github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
|
||||
github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/glycerine/goconvey v0.0.0-20180728074245-46e3a41ad493 h1:OTanQnFt0bi5iLFSdbEVA/idR6Q2WhCm+deb7ir2CcM=
|
||||
github.com/glycerine/goconvey v0.0.0-20180728074245-46e3a41ad493/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
|
||||
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/mux v1.7.1 h1:Dw4jY2nghMMRsh1ol8dv1axHkDwMQK2DHerMNJsIpJU=
|
||||
github.com/gorilla/mux v1.7.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/ikawaha/kagome.ipadic v1.1.2/go.mod h1:DPSBbU0czaJhAb/5uKQZHMc9MTVRpDugJfX+HddPHHg=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U=
|
||||
github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/kljensen/snowball v0.6.0/go.mod h1:27N7E8fVU5H68RlUmnWwZCfxgt4POBJfENGMvNRhldw=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae h1:VeRdUYdCw49yizlSbMEn2SZ+gT+3IUKx8BqxyQdz+BY=
|
||||
github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg=
|
||||
github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM=
|
||||
github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/philhofer/fwd v1.0.0 h1:UbZqGr5Y38ApvM/V/jEljVxwocdweyH+vmYvRPBnbqQ=
|
||||
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
|
||||
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/pquerna/otp v1.5.0 h1:NMMR+WrmaqXU4EzdGJEE1aUUI0AMRzsp96fFFWNPwxs=
|
||||
github.com/pquerna/otp v1.5.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/steveyen/gtreap v0.1.0 h1:CjhzTa274PyJLJuMZwIzCO1PfC00oRa8d1Kc78bFXJM=
|
||||
github.com/steveyen/gtreap v0.1.0/go.mod h1:kl/5J7XbrOmlIbYIXdRHDDE5QxHqpk0cmkT7Z4dM9/Y=
|
||||
github.com/pquerna/otp v1.2.0 h1:/A3+Jn+cagqayeR3iHs/L62m5ue7710D35zl1zJ1kok=
|
||||
github.com/pquerna/otp v1.2.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20190321074620-2f0d2b0e0001 h1:YDeskXpkNDhPdWN3REluVa46HQOVuVkjkd2sWnrABNQ=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20190321074620-2f0d2b0e0001/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190306220146-200a235640ff h1:86HlEv0yBCry9syNuylzqznKXDK11p6D0DT596yNMys=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190306220146-200a235640ff/go.mod h1:KSQcGKpxUMHk3nbYzs/tIBAM2iDooCn0BmttHOJEbLs=
|
||||
github.com/steveyen/gtreap v0.0.0-20150807155958-0abe01ef9be2 h1:JNEGSiWg6D3lcBCMCBqN3ELniXujt+0QNHLhNnO0w3s=
|
||||
github.com/steveyen/gtreap v0.0.0-20150807155958-0abe01ef9be2/go.mod h1:mjqs7N0Q6m5HpR7QfXVBZXZWSqTjQLeTujjA/xUp2uw=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
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/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
|
||||
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
||||
github.com/tebeka/snowball v0.4.2/go.mod h1:4IfL14h1lvwZcp1sfXuuc7/7yCsvVffTWxWxCLfFpYg=
|
||||
github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c h1:g+WoO5jjkqGAzHWCjJB1zZfXPIAaDpzXIEJ0eS6B5Ok=
|
||||
github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c/go.mod h1:ahpPrc7HpcfEWDQRZEmnXMzHY03mLDYMCxeDzy46i+8=
|
||||
github.com/tecbot/gorocksdb v0.0.0-20181010114359-8752a9433481 h1:HOxvxvnntLiPn123Fk+twfUhCQdMDaqmb0cclArW0T0=
|
||||
github.com/tecbot/gorocksdb v0.0.0-20181010114359-8752a9433481/go.mod h1:ahpPrc7HpcfEWDQRZEmnXMzHY03mLDYMCxeDzy46i+8=
|
||||
github.com/tinylib/msgp v1.1.0 h1:9fQd+ICuRIu/ue4vxJZu6/LzxN0HwMds2nq/0cFvxHU=
|
||||
github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/urfave/cli v1.22.17 h1:SYzXoiPfQjHBbkYxbew5prZHS1TOLT3ierW8SYLqtVQ=
|
||||
github.com/urfave/cli v1.22.17/go.mod h1:b0ht0aqgH/6pBYzzxURyrM4xXNgsoT/n2ZzwQiEhNVo=
|
||||
github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=
|
||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/willf/bitset v1.1.10 h1:NotGKqX0KwQ72NUzqrjZq5ipPNDQex9lo3WpaS8L2sc=
|
||||
github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
||||
github.com/willf/bitset v1.1.11 h1:N7Z7E9UvjW+sGsEl7k/SJrvY2reP1A07MrGuCjIOjRE=
|
||||
github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
||||
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.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk=
|
||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
|
||||
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
|
||||
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734 h1:p/H982KKEjUnLJkM3tt/LemDnOc1GiZL5FCVlORJ5zo=
|
||||
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
|
||||
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
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.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181221143128-b4a75ba826a6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181026144532-2772b66316d2/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
||||
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
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/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
// Copyright (C) 2021 Marius Schellenberger
|
||||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
//go:build go1.16
|
||||
// +build go1.16
|
||||
//go:generate ./templates.sh
|
||||
|
||||
// +build go1.12
|
||||
|
||||
package main
|
||||
|
||||
220
pkg/cmd/cmd.go
220
pkg/cmd/cmd.go
|
|
@ -1,28 +1,24 @@
|
|||
// Copyright (C) 2021 Marius Schellenberger
|
||||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"git.giftfish.de/ston1th/docstore/pkg/core"
|
||||
"git.giftfish.de/ston1th/docstore/pkg/db"
|
||||
"git.giftfish.de/ston1th/docstore/pkg/log"
|
||||
"git.giftfish.de/ston1th/docstore/pkg/scan"
|
||||
"git.giftfish.de/ston1th/docstore/pkg/server"
|
||||
"git.giftfish.de/ston1th/godrop/v2"
|
||||
"github.com/urfave/cli"
|
||||
stdlog "log"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
|
||||
"git.giftfish.de/ston1th/docstore/pkg/core"
|
||||
"git.giftfish.de/ston1th/docstore/pkg/db"
|
||||
"git.giftfish.de/ston1th/docstore/pkg/scan"
|
||||
"git.giftfish.de/ston1th/docstore/pkg/server"
|
||||
"git.giftfish.de/ston1th/godrop/v2"
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/urfave/cli"
|
||||
"k8s.io/klog/v2"
|
||||
"k8s.io/klog/v2/klogr"
|
||||
//"github.com/go-logr/zerologr"
|
||||
//"github.com/rs/zerolog"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -30,6 +26,9 @@ const (
|
|||
defListen = "127.0.0.1:8080"
|
||||
defLogFile = "docstore.log"
|
||||
|
||||
defRunUser = "docstore"
|
||||
defRunGroup = "docstore"
|
||||
|
||||
defTimeout = 600
|
||||
|
||||
tesseractThreads = 1
|
||||
|
|
@ -42,12 +41,23 @@ var (
|
|||
configFile string
|
||||
)
|
||||
|
||||
func initServer(cfg *core.Config) (err error) {
|
||||
func initServer(cfg core.Config) (err error) {
|
||||
// remove initial pledge due to violation with go1.12
|
||||
// sysctl kern.somaxconn
|
||||
// if err = godrop.PledgePromises("stdio rpath wpath cpath inet fattr flock proc exec id unveil"); err != nil {
|
||||
// return
|
||||
// }
|
||||
//if err = godrop.PledgePromises("stdio rpath wpath cpath inet fattr flock proc exec id unveil"); err != nil {
|
||||
// return
|
||||
//}
|
||||
dropCfg := godrop.Config{
|
||||
User: cfg.User,
|
||||
Group: cfg.Group,
|
||||
Foreground: cfg.Foreground,
|
||||
}
|
||||
err = godrop.Drop(dropCfg, func() (net.Listener, error) {
|
||||
return net.Listen("tcp", cfg.ListenAddr)
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err = os.Chdir(cfg.RunDir); err != nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -56,25 +66,19 @@ func initServer(cfg *core.Config) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
l, err := net.Listen("tcp", cfg.ListenAddr)
|
||||
l, err := godrop.GetListener()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
ll, err := logFileLogger(cfg)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
log := ll.WithName("main")
|
||||
log.Info("starting docstore", "version", cfg.Version)
|
||||
log.InitLogger(cfg)
|
||||
|
||||
cfg.DataDir = filepath.Join(cfg.RunDir, core.DataDir)
|
||||
debugCfg := new(core.Config)
|
||||
*debugCfg = *cfg
|
||||
debugCfg.Secret = "[redacted]"
|
||||
log.V(2).Info("docstore config", "cfg", debugCfg)
|
||||
debugCfg := cfg
|
||||
debugCfg.Secret = "***hidden***"
|
||||
log.Debugf("docstore config: %+v", debugCfg)
|
||||
|
||||
scanner, err := scan.New(ll.WithName("scanner"), cfg)
|
||||
scanner, err := scan.New(cfg)
|
||||
if err != nil {
|
||||
return errors.New("scanner: " + err.Error())
|
||||
}
|
||||
|
|
@ -87,20 +91,25 @@ func initServer(cfg *core.Config) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
srv := server.NewHTTPServer(ll, cfg, l, scanner)
|
||||
srv := server.NewHTTPServer(cfg, l, scanner)
|
||||
err = srv.Start()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
sigs := make(chan os.Signal, 1)
|
||||
log.Println("docstore " + cfg.Version + " started")
|
||||
sigs := make(chan os.Signal)
|
||||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-sigs
|
||||
log.Info("docstore shutdown initiated")
|
||||
sig := <-sigs
|
||||
log.Println("signal: " + sig.String())
|
||||
go func() {
|
||||
time.Sleep(time.Second * 20)
|
||||
log.Fatal("stop timed out: killing")
|
||||
}()
|
||||
err = srv.Stop()
|
||||
if err != nil {
|
||||
log.Error(err, "shutdown")
|
||||
log.Println(err)
|
||||
}
|
||||
log.Info("docstore shutdown completed")
|
||||
log.Println("docstore stopped")
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -116,13 +125,15 @@ func loadConfig() error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if config.Debug {
|
||||
new(klog.Level).Set("2")
|
||||
//zerolog.SetGlobalLevel(zerolog.TraceLevel)
|
||||
}
|
||||
if config.RunDir == "" {
|
||||
config.RunDir = defRunDir
|
||||
}
|
||||
if config.User == "" {
|
||||
config.User = defRunUser
|
||||
}
|
||||
if config.Group == "" {
|
||||
config.Group = defRunGroup
|
||||
}
|
||||
if config.ListenAddr == "" {
|
||||
config.ListenAddr = defListen
|
||||
}
|
||||
|
|
@ -141,48 +152,6 @@ func loadConfig() error {
|
|||
return file.Close()
|
||||
}
|
||||
|
||||
func fatal(err error) {
|
||||
log.Error(err, "init failed")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var (
|
||||
log logr.Logger
|
||||
//zl zerolog.Logger
|
||||
)
|
||||
|
||||
func logFileLogger(cfg *core.Config) (l logr.Logger, err error) {
|
||||
f, err := os.OpenFile(filepath.Join(cfg.DataDir, cfg.LogFile), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0o640)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
klog.LogToStderr(false)
|
||||
klog.SetOutput(f)
|
||||
l = klogr.New()
|
||||
//zln := zl.Output(f)
|
||||
//l = zerologr.New(&zln)
|
||||
return
|
||||
}
|
||||
|
||||
func init() {
|
||||
//zerolog.CallerMarshalFunc = func(pc uintptr, file string, line int) string {
|
||||
// short := file
|
||||
// for i := len(file) - 1; i > 0; i-- {
|
||||
// if file[i] == '/' {
|
||||
// short = file[i+1:]
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// file = short
|
||||
// return file + ":" + strconv.Itoa(line)
|
||||
//}
|
||||
//zerologr.VerbosityFieldName = ""
|
||||
//zl = zerolog.New(os.Stderr)
|
||||
//zl = zl.With().Caller().Timestamp().Logger()
|
||||
//log = zerologr.New(&zl)
|
||||
log = klogr.New()
|
||||
}
|
||||
|
||||
func Run(version string) {
|
||||
app := cli.NewApp()
|
||||
app.Name = "docstore"
|
||||
|
|
@ -196,52 +165,18 @@ func Run(version string) {
|
|||
Action: func(c *cli.Context) error {
|
||||
config.Langs = c.StringSlice("langs")
|
||||
if err := loadConfig(); err != nil {
|
||||
fatal(err)
|
||||
stdlog.Fatal("server: ", err)
|
||||
}
|
||||
config.Version = app.Version
|
||||
if err := initServer(&config); err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "reset",
|
||||
Usage: "reset the users password and TOTP token (docstore must be stopped)",
|
||||
Flags: defaultFlags(nil),
|
||||
Action: func(c *cli.Context) error {
|
||||
if err := loadConfig(); err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
DB, err := db.NewPlain(log.WithName("db"), &config)
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
u, err := DB.GetUser()
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
fmt.Println("username:", u.Username)
|
||||
pw, err := password()
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
if err = DB.UpdateUserPassword(pw); err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
fmt.Println("password:", pw)
|
||||
if err = DB.UpdateUserSecret(""); err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
if err = DB.Close(); err != nil {
|
||||
fatal(err)
|
||||
if err := initServer(config); err != nil {
|
||||
stdlog.Fatal("server: ", err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "dump",
|
||||
Usage: "create a json dump of all users (docstore must be stopped)",
|
||||
Usage: "create a json dump of all users (docstore needs to be stopped)",
|
||||
Flags: defaultFlags(dumpRestoreFlags()),
|
||||
Action: func(c *cli.Context) error {
|
||||
var (
|
||||
|
|
@ -249,32 +184,32 @@ func Run(version string) {
|
|||
err error
|
||||
)
|
||||
if err = loadConfig(); err != nil {
|
||||
fatal(err)
|
||||
stdlog.Fatal("dump: ", err)
|
||||
}
|
||||
if dumpFile != "-" {
|
||||
file, err = os.OpenFile(dumpFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o640)
|
||||
file, err = os.OpenFile(dumpFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0640)
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
stdlog.Fatal("dump: ", err)
|
||||
}
|
||||
}
|
||||
DB, err := db.NewPlain(log.WithName("db"), &config)
|
||||
DB, err := db.New(config)
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
stdlog.Fatal("dump: ", err)
|
||||
}
|
||||
if err = DB.Dump(file); err != nil {
|
||||
fatal(err)
|
||||
stdlog.Fatal("dump: ", err)
|
||||
}
|
||||
file.Close()
|
||||
err = DB.Close()
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
stdlog.Fatal("dump: ", err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "restore",
|
||||
Usage: "restore a json dump (docstore must be stopped)",
|
||||
Usage: "restore a json dump (docstore needs to be stopped)",
|
||||
Flags: defaultFlags(dumpRestoreFlags()),
|
||||
Action: func(c *cli.Context) error {
|
||||
var (
|
||||
|
|
@ -282,25 +217,25 @@ func Run(version string) {
|
|||
err error
|
||||
)
|
||||
if err = loadConfig(); err != nil {
|
||||
fatal(err)
|
||||
stdlog.Fatal("restore: ", err)
|
||||
}
|
||||
if dumpFile != "-" {
|
||||
file, err = os.Open(dumpFile)
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
stdlog.Fatal("restore: ", err)
|
||||
}
|
||||
}
|
||||
DB, err := db.NewPlain(log.WithName("db"), &config)
|
||||
DB, err := db.New(config)
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
stdlog.Fatal("restore: ", err)
|
||||
}
|
||||
if err = DB.Restore(file); err != nil {
|
||||
fatal(err)
|
||||
stdlog.Fatal("restore: ", err)
|
||||
}
|
||||
file.Close()
|
||||
err = DB.Close()
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
stdlog.Fatal("restore: ", err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
|
|
@ -311,6 +246,18 @@ func Run(version string) {
|
|||
|
||||
func serverFlags() []cli.Flag {
|
||||
return []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "user, u",
|
||||
Value: defRunUser,
|
||||
Usage: "drop privileges to user",
|
||||
Destination: &config.User,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "group, g",
|
||||
Value: defRunGroup,
|
||||
Usage: "drop privileges to group",
|
||||
Destination: &config.Group,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "listen, l",
|
||||
Value: defListen,
|
||||
|
|
@ -350,6 +297,11 @@ func serverFlags() []cli.Flag {
|
|||
Usage: "tesseract oem value (1-4)",
|
||||
Destination: &config.TesseractOEM,
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "foreground, f",
|
||||
Usage: "do not fork into the background",
|
||||
Destination: &config.Foreground,
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "secure, s",
|
||||
Usage: "enable secure cookie flag",
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
// Copyright (C) 2021 Marius Schellenberger
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
func password() (string, error) {
|
||||
b := make([]byte, 16)
|
||||
_, err := rand.Read(b)
|
||||
return hex.EncodeToString(b), err
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (C) 2021 Marius Schellenberger
|
||||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package core
|
||||
|
||||
|
|
@ -10,7 +10,6 @@ const (
|
|||
SearchURI = "/search"
|
||||
LogsURI = "/logs"
|
||||
TagsURI = "/tags"
|
||||
StatsURI = "/stats"
|
||||
UserURI = "/user"
|
||||
|
||||
RawPrefix = "/raw"
|
||||
|
|
@ -22,7 +21,10 @@ const (
|
|||
TagsPrefix = "/tags"
|
||||
DeletePrefix = "/delete"
|
||||
WebDavPrefix = "/webdav"
|
||||
StaticPrefix = "/static"
|
||||
|
||||
Favicon = "/favicon.ico"
|
||||
BootstrapCSS = "/bootstrap.css"
|
||||
CustomCSS = "/custom.css"
|
||||
|
||||
DataDir = "data"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (C) 2021 Marius Schellenberger
|
||||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package core
|
||||
|
||||
|
|
@ -8,9 +8,8 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
fileFmt = "20060102_150405"
|
||||
logFmt = "2006/01/02 15:04:05: "
|
||||
createdFmt = "02.01.2006 15:04:05"
|
||||
fileFmt = "20060102_150405"
|
||||
logFmt = "2006/01/02 15:04:05: "
|
||||
)
|
||||
|
||||
var m sync.Mutex
|
||||
|
|
@ -27,7 +26,3 @@ func FileName() string {
|
|||
func LogTime() string {
|
||||
return time.Now().Format(logFmt)
|
||||
}
|
||||
|
||||
func CreatedTime(t time.Time) string {
|
||||
return t.Format(createdFmt)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (C) 2021 Marius Schellenberger
|
||||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package core
|
||||
|
||||
|
|
@ -64,17 +64,11 @@ func (p Paths) Len() int { return len(p) }
|
|||
func (p Paths) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|
||||
func (p Paths) Less(i, j int) bool { return p[i].Name < p[j].Name }
|
||||
|
||||
type Stats struct {
|
||||
Goroutines int
|
||||
Alloc string
|
||||
Sys string
|
||||
Docs uint64
|
||||
GoVersion string
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
RunDir string `json:"run_dir,omitempty"`
|
||||
DataDir string `json:"-"`
|
||||
User string `json:"user,omitempty"`
|
||||
Group string `json:"group,omitempty"`
|
||||
ListenAddr string `json:"listen_addr,omitempty"`
|
||||
LogFile string `json:"log_file,omitempty"`
|
||||
Version string `json:"-"`
|
||||
|
|
@ -83,6 +77,7 @@ type Config struct {
|
|||
CMDTimeout int `json:"cmd_timeout,omitempty"`
|
||||
TesseractThreads int `json:"tesseract_threads,omitempty"`
|
||||
TesseractOEM int `json:"tesseract_oem,omitempty"`
|
||||
Foreground bool `json:"foreground,omitempty"`
|
||||
SecureCookie bool `json:"secure_cookie,omitempty"`
|
||||
Debug bool `json:"debug,omitempty"`
|
||||
WebDav bool `json:"webdav,omitempty"`
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (C) 2021 Marius Schellenberger
|
||||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package db
|
||||
|
||||
|
|
@ -15,7 +15,10 @@ func (db *DB) Remove(sig string) error {
|
|||
}
|
||||
|
||||
func (db *DB) Check(sig string) (ok bool) {
|
||||
return db.store.Get(blacklistPrefix+sig, nil) == nil
|
||||
if db.store.Get(blacklistPrefix+sig, nil) == nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (db *DB) Map() (list jwt.BlacklistMap, err error) {
|
||||
|
|
|
|||
27
pkg/db/db.go
27
pkg/db/db.go
|
|
@ -1,15 +1,13 @@
|
|||
// Copyright (C) 2021 Marius Schellenberger
|
||||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package db
|
||||
|
||||
import (
|
||||
"io"
|
||||
"path/filepath"
|
||||
|
||||
"git.giftfish.de/ston1th/docstore/pkg/core"
|
||||
"git.giftfish.de/ston1th/docstore/pkg/index"
|
||||
"git.giftfish.de/ston1th/docstore/pkg/store"
|
||||
"github.com/go-logr/logr"
|
||||
"io"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -20,15 +18,16 @@ const (
|
|||
const blevePath = "bleve"
|
||||
|
||||
type DB struct {
|
||||
log logr.Logger
|
||||
rm countMutex
|
||||
IndexMutex countMutex
|
||||
store store.Store
|
||||
Index *index.Index
|
||||
}
|
||||
|
||||
func New(log logr.Logger, cfg *core.Config) (db *DB, err error) {
|
||||
db, err = NewPlain(log, cfg)
|
||||
func New(cfg core.Config) (db *DB, err error) {
|
||||
db = new(DB)
|
||||
dbFile := filepath.Join(cfg.RunDir, storeFile)
|
||||
db.store, err = store.NewBoltStore(dbFile, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -51,13 +50,6 @@ func New(log logr.Logger, cfg *core.Config) (db *DB, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func NewPlain(log logr.Logger, cfg *core.Config) (db *DB, err error) {
|
||||
db = &DB{log: log}
|
||||
dbFile := filepath.Join(cfg.RunDir, storeFile)
|
||||
db.store, err = store.NewBoltStore(dbFile, nil)
|
||||
return
|
||||
}
|
||||
|
||||
func (db *DB) Dump(w io.Writer) error {
|
||||
return db.store.Dump(w)
|
||||
}
|
||||
|
|
@ -71,8 +63,5 @@ func (db *DB) Close() (err error) {
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
if db.Index != nil {
|
||||
return db.Index.Close()
|
||||
}
|
||||
return nil
|
||||
return db.Index.Close()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (C) 2021 Marius Schellenberger
|
||||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package db
|
||||
|
||||
|
|
|
|||
153
pkg/db/index.go
153
pkg/db/index.go
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (C) 2021 Marius Schellenberger
|
||||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package db
|
||||
|
||||
|
|
@ -16,82 +16,131 @@ const (
|
|||
)
|
||||
|
||||
func (db *DB) GetPaths(res core.Results) {
|
||||
for i, v := range res {
|
||||
err := db.store.Get(pathPrefix+v.ID, &res[i].Path)
|
||||
if err != nil {
|
||||
res[i].Path = err.Error()
|
||||
db.store.Tx(false, func(tx store.KVStore) error {
|
||||
for i, v := range res {
|
||||
err := tx.Get(pathPrefix+v.ID, &res[i].Path)
|
||||
if err != nil {
|
||||
res[i].Path = err.Error()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (db *DB) GetIndexed(p core.Paths) {
|
||||
for i, v := range p {
|
||||
p[i].Flag = db.IsIndexed(v.Abs)
|
||||
p[i].Tags, _ = db.GetTag(v.Abs)
|
||||
db.store.Tx(false, func(tx store.KVStore) error {
|
||||
for i, v := range p {
|
||||
p[i].Flag = db.IsIndexed(tx, v.Abs)
|
||||
p[i].Tags, _ = db.GetTag(tx, v.Abs)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (db *DB) IsIndexed(tx store.KVStore, path string) bool {
|
||||
f := func(tx store.KVStore) error {
|
||||
return tx.Get(idPrefix+path, nil)
|
||||
}
|
||||
if tx != nil {
|
||||
return f(tx) == nil
|
||||
}
|
||||
return f(db.store) == nil
|
||||
}
|
||||
|
||||
func (db *DB) IsIndexed(path string) bool {
|
||||
return db.store.Get(idPrefix+path, nil) == nil
|
||||
}
|
||||
|
||||
func (db *DB) GetPath(id string) (path string, err error) {
|
||||
err = db.store.Get(pathPrefix+id, &path)
|
||||
func (db *DB) GetPath(tx store.KVStore, id string) (path string, err error) {
|
||||
f := func(tx store.KVStore) error {
|
||||
return tx.Get(pathPrefix+id, &path)
|
||||
}
|
||||
if tx != nil {
|
||||
err = f(tx)
|
||||
return
|
||||
}
|
||||
err = f(db.store)
|
||||
return
|
||||
}
|
||||
|
||||
func (db *DB) GetID(path string) (id string, err error) {
|
||||
err = db.store.Get(idPrefix+path, &id)
|
||||
func (db *DB) GetID(tx store.KVStore, path string) (id string, err error) {
|
||||
f := func(tx store.KVStore) error {
|
||||
return tx.Get(idPrefix+path, &id)
|
||||
}
|
||||
if tx != nil {
|
||||
err = f(tx)
|
||||
return
|
||||
}
|
||||
err = f(db.store)
|
||||
return
|
||||
}
|
||||
|
||||
func (db *DB) DeletePath(id string) error {
|
||||
return db.store.Delete(pathPrefix + id)
|
||||
func (db *DB) DeletePath(tx store.KVStore, id string) error {
|
||||
f := func(tx store.KVStore) error {
|
||||
return tx.Delete(pathPrefix + id)
|
||||
}
|
||||
if tx != nil {
|
||||
return f(tx)
|
||||
}
|
||||
return f(db.store)
|
||||
}
|
||||
|
||||
func (db *DB) DeleteID(path string) error {
|
||||
return db.store.Delete(idPrefix + path)
|
||||
func (db *DB) DeleteID(tx store.KVStore, path string) error {
|
||||
f := func(tx store.KVStore) error {
|
||||
return tx.Delete(idPrefix + path)
|
||||
}
|
||||
if tx != nil {
|
||||
return f(tx)
|
||||
}
|
||||
return f(db.store)
|
||||
}
|
||||
|
||||
func (db *DB) NewFile(id, path string, tag []string) (err error) {
|
||||
err = db.store.Set(idPrefix+path, id)
|
||||
if err != nil {
|
||||
return
|
||||
func (db *DB) NewFile(tx store.KVStore, id, path string, tag []string) (err error) {
|
||||
f := func(tx store.KVStore) error {
|
||||
err = tx.Set(idPrefix+path, id)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = tx.Set(pathPrefix+id, path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return tx.Set(tagPrefix+path, tag)
|
||||
}
|
||||
err = db.store.Set(pathPrefix+id, path)
|
||||
if err != nil {
|
||||
return
|
||||
if tx != nil {
|
||||
return f(tx)
|
||||
}
|
||||
return db.SetTag(path, tag)
|
||||
return db.store.Tx(true, f)
|
||||
}
|
||||
|
||||
func (db *DB) MoveFile(op, np string) (err error) {
|
||||
if !db.IsIndexed(op) {
|
||||
return nil
|
||||
}
|
||||
id, tag, err := db.DeleteFile(op)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return db.NewFile(id, np, tag)
|
||||
return db.store.Tx(true, func(tx store.KVStore) error {
|
||||
id, tag, err := db.DeleteFile(tx, op)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return db.NewFile(tx, id, np, tag)
|
||||
})
|
||||
}
|
||||
|
||||
func (db *DB) DeleteFile(path string) (id string, tag []string, err error) {
|
||||
if !db.IsIndexed(path) {
|
||||
func (db *DB) DeleteFile(tx store.KVStore, path string) (id string, tags []string, err error) {
|
||||
f := func(tx store.KVStore) error {
|
||||
err = tx.Get(idPrefix+path, &id)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = tx.Delete(idPrefix + path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = tx.Get(tagPrefix+path, &tags)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = tx.Delete(tagPrefix + path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return tx.Delete(pathPrefix + id)
|
||||
}
|
||||
if tx != nil {
|
||||
err = f(tx)
|
||||
return
|
||||
}
|
||||
id, err = db.GetID(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = db.DeleteID(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
tag, err = db.UnsetTag(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = db.DeletePath(id)
|
||||
err = db.store.Tx(true, f)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
// Copyright (C) 2021 Marius Schellenberger
|
||||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package db
|
||||
|
||||
import (
|
||||
"git.giftfish.de/ston1th/docstore/pkg/log"
|
||||
"git.giftfish.de/ston1th/docstore/pkg/store"
|
||||
)
|
||||
|
||||
|
|
@ -31,7 +32,6 @@ var migrators = []migrator{
|
|||
}
|
||||
|
||||
func (db *DB) runMigrations() (err error) {
|
||||
log := db.log
|
||||
var version int
|
||||
err = db.store.Get(versionKey, &version)
|
||||
if err == store.ErrKeyNotFound {
|
||||
|
|
@ -44,10 +44,10 @@ func (db *DB) runMigrations() (err error) {
|
|||
return
|
||||
}
|
||||
}
|
||||
log.Info("database version", "version", version)
|
||||
log.Printf("db: database version %d", version)
|
||||
for version < currentVersion {
|
||||
v := version + 1
|
||||
log.Info("running migration", "version", v)
|
||||
log.Printf("db: running migration version %d", v)
|
||||
err = migrators[v](db, v)
|
||||
if err != nil {
|
||||
return
|
||||
|
|
@ -57,7 +57,7 @@ func (db *DB) runMigrations() (err error) {
|
|||
return
|
||||
}
|
||||
version = v
|
||||
log.Info("database version", "version", version)
|
||||
log.Printf("db: database version %d", version)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (C) 2021 Marius Schellenberger
|
||||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package db
|
||||
|
||||
|
|
@ -10,7 +10,7 @@ type countMutex struct {
|
|||
c uint64
|
||||
}
|
||||
|
||||
func (cm *countMutex) Lock() (max, c uint64, ok bool) {
|
||||
func (cm *countMutex) Lock() (uint64, uint64, bool) {
|
||||
if atomic.CompareAndSwapUint64(&cm.mu, 0, 1) {
|
||||
return 0, 0, true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
// Copyright (C) 2021 Marius Schellenberger
|
||||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package db
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"git.giftfish.de/ston1th/docstore/pkg/core"
|
||||
"git.giftfish.de/ston1th/docstore/pkg/log"
|
||||
"regexp"
|
||||
"sort"
|
||||
|
||||
"git.giftfish.de/ston1th/docstore/pkg/core"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -17,10 +17,10 @@ const (
|
|||
)
|
||||
|
||||
var defaultTags = core.Tags{
|
||||
{Name: "invoice", Regex: `(?i)[^e]rechnung|quittung|beleg|invoice|bill|check`},
|
||||
{Name: "tax", Regex: `(?i)steuer|lohn|tax|salary`},
|
||||
{Name: "contract", Regex: `(?i)vertrag|kündigung|vereinbarung|contract|termination|agreement`},
|
||||
{Name: "insurance", Regex: `(?i)versicherung|versichert|insurance|insured`},
|
||||
{"invoice", `(?i)[^e]rechnung|quittung|beleg|invoice|bill|check`},
|
||||
{"tax", `(?i)steuer|lohn|tax|salary`},
|
||||
{"contract", `(?i)vertrag|kündigung|vereinbarung|contract|termination|agreement`},
|
||||
{"insurance", `(?i)versicherung|versichert|insurance|insured`},
|
||||
}
|
||||
|
||||
func (db *DB) addDefaultTags() (err error) {
|
||||
|
|
@ -34,7 +34,6 @@ func (db *DB) addDefaultTags() (err error) {
|
|||
}
|
||||
|
||||
func (db *DB) Retag(path string, tags core.RTags) (err error) {
|
||||
log := db.log.WithValues("file", path)
|
||||
if tags == nil {
|
||||
tags, err = db.GetAllRTags()
|
||||
if err != nil {
|
||||
|
|
@ -49,14 +48,14 @@ func (db *DB) Retag(path string, tags core.RTags) (err error) {
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
log.V(2).Info("retagging")
|
||||
log.Debugf("db: retagging: %s", path)
|
||||
doc, err := db.Index.Get(id)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
doc.Tags = tags.Match(doc.Text)
|
||||
if eq(t, doc.Tags) {
|
||||
log.V(2).Info("retagging skipped")
|
||||
log.Debugf("db: retagging: skipped %s", path)
|
||||
return
|
||||
}
|
||||
err = db.Index.Update(id, doc)
|
||||
|
|
@ -67,7 +66,6 @@ func (db *DB) Retag(path string, tags core.RTags) (err error) {
|
|||
}
|
||||
|
||||
func (db *DB) RetagAll() (err error) {
|
||||
log := db.log
|
||||
if m, c, ok := db.rm.Lock(); !ok {
|
||||
return fmt.Errorf("retagging already in progress: %d/%d", c, m)
|
||||
}
|
||||
|
|
@ -91,7 +89,7 @@ func (db *DB) RetagAll() (err error) {
|
|||
if err == nil {
|
||||
err = errors.New("retagAll had errors")
|
||||
}
|
||||
log.Error(e, "error retagging file", "file", p)
|
||||
log.Printf("db: error retagging %s: %s", p, e)
|
||||
}
|
||||
db.rm.Inc()
|
||||
}
|
||||
|
|
@ -117,10 +115,7 @@ func (db *DB) GetAllRTags() (rtags core.RTags, err error) {
|
|||
return
|
||||
}
|
||||
for _, v := range tags {
|
||||
rtags = append(rtags, core.RTag{
|
||||
Name: v.Name,
|
||||
Regex: regexp.MustCompile(v.Regex),
|
||||
})
|
||||
rtags = append(rtags, core.RTag{v.Name, regexp.MustCompile(v.Regex)})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
@ -140,10 +135,7 @@ func (db *DB) NewTag(name, regex string) (err error) {
|
|||
return
|
||||
}
|
||||
name = nameRe.ReplaceAllString(name, "")
|
||||
err = db.store.Set(tagsPrefix+name, core.Tag{
|
||||
Name: name,
|
||||
Regex: regex,
|
||||
})
|
||||
err = db.store.Set(tagsPrefix+name, core.Tag{name, regex})
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (C) 2021 Marius Schellenberger
|
||||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package db
|
||||
|
||||
|
|
@ -66,7 +66,10 @@ func (db *DB) CreateUser(username, password string) error {
|
|||
|
||||
func (db *DB) BasicAuth(username, password string) bool {
|
||||
_, err := db.Login(username, password)
|
||||
return err == nil
|
||||
if err == nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (db *DB) Login(username, password string) (u core.User, err error) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (C) 2021 Marius Schellenberger
|
||||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package fs
|
||||
|
||||
|
|
|
|||
15
pkg/fs/fs.go
15
pkg/fs/fs.go
|
|
@ -1,22 +1,21 @@
|
|||
// Copyright (C) 2021 Marius Schellenberger
|
||||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package fs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"git.giftfish.de/ston1th/docstore/pkg/core"
|
||||
"git.giftfish.de/ston1th/docstore/pkg/log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"git.giftfish.de/ston1th/docstore/pkg/core"
|
||||
"github.com/go-logr/logr"
|
||||
)
|
||||
|
||||
const (
|
||||
DirMode = 0o750
|
||||
FileMode = 0o640
|
||||
DirMode = 0750
|
||||
FileMode = 0640
|
||||
)
|
||||
|
||||
// Directory represents a directory and its contents
|
||||
|
|
@ -33,7 +32,7 @@ type Filesystem struct {
|
|||
scan map[string]struct{}
|
||||
}
|
||||
|
||||
func NewFilesystem(log logr.Logger, base string) (fs *Filesystem, err error) {
|
||||
func NewFilesystem(base string) (fs *Filesystem, err error) {
|
||||
if !filepath.IsAbs(base) {
|
||||
err = errors.New("base dir '" + base + "' is not an absolute path")
|
||||
return
|
||||
|
|
@ -44,7 +43,7 @@ func NewFilesystem(log logr.Logger, base string) (fs *Filesystem, err error) {
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
log.Info("created data directory", "dir", base)
|
||||
log.Printf("fs: created data directory '%s'", base)
|
||||
fi, err = os.Stat(base)
|
||||
if err != nil {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (C) 2021 Marius Schellenberger
|
||||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package fs
|
||||
|
||||
|
|
@ -27,7 +27,6 @@ var reserved = []string{
|
|||
core.SearchURI,
|
||||
core.LogsURI,
|
||||
core.TagsURI,
|
||||
core.StatsURI,
|
||||
core.UserURI,
|
||||
core.RawPrefix,
|
||||
core.IndexPrefix,
|
||||
|
|
@ -37,7 +36,9 @@ var reserved = []string{
|
|||
core.MovePrefix,
|
||||
core.DeletePrefix,
|
||||
core.WebDavPrefix,
|
||||
core.StaticPrefix,
|
||||
core.Favicon,
|
||||
core.BootstrapCSS,
|
||||
core.CustomCSS,
|
||||
}
|
||||
|
||||
func checkPath(path string) error {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// Copyright (C) 2021 Marius Schellenberger
|
||||
|
||||
package fs
|
||||
|
||||
import "errors"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (C) 2021 Marius Schellenberger
|
||||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package index
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (C) 2021 Marius Schellenberger
|
||||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package index
|
||||
|
||||
|
|
@ -66,11 +66,6 @@ func (i *Index) Close() error {
|
|||
return i.i.Close()
|
||||
}
|
||||
|
||||
func (i *Index) DocCount() (c uint64) {
|
||||
c, _ = i.i.DocCount()
|
||||
return
|
||||
}
|
||||
|
||||
func (i *Index) Add(text string, tags []string) (id string, err error) {
|
||||
id = newID()
|
||||
for {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (C) 2021 Marius Schellenberger
|
||||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package index
|
||||
|
||||
|
|
|
|||
52
pkg/log/log.go
Normal file
52
pkg/log/log.go
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package log
|
||||
|
||||
import (
|
||||
"git.giftfish.de/ston1th/docstore/pkg/core"
|
||||
stdlog "log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
var (
|
||||
log = stdlog.New(os.Stdout, "", stdlog.LstdFlags)
|
||||
debug = false
|
||||
)
|
||||
|
||||
func InitLogger(cfg core.Config) {
|
||||
debug = cfg.Debug
|
||||
if cfg.LogFile == "-" {
|
||||
return
|
||||
}
|
||||
f, err := os.OpenFile(filepath.Join(cfg.DataDir, cfg.LogFile), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0640)
|
||||
if err != nil {
|
||||
stdlog.Fatal(err)
|
||||
}
|
||||
stdlog.SetOutput(f)
|
||||
log = stdlog.New(f, "", stdlog.LstdFlags)
|
||||
}
|
||||
|
||||
func Println(v ...interface{}) {
|
||||
log.Println(v...)
|
||||
}
|
||||
|
||||
func Printf(fmt string, v ...interface{}) {
|
||||
log.Printf(fmt, v...)
|
||||
}
|
||||
|
||||
func Fatal(v ...interface{}) {
|
||||
log.Fatal(v...)
|
||||
}
|
||||
|
||||
func Debug(v ...interface{}) {
|
||||
if debug {
|
||||
log.Println(append([]interface{}{"debug:"}, v...)...)
|
||||
}
|
||||
}
|
||||
|
||||
func Debugf(fmt string, v ...interface{}) {
|
||||
if debug {
|
||||
log.Printf("debug: "+fmt, v...)
|
||||
}
|
||||
}
|
||||
70
pkg/log/scanlog.go
Normal file
70
pkg/log/scanlog.go
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.giftfish.de/ston1th/docstore/pkg/core"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const defaultSize = 500
|
||||
|
||||
type ScanLog struct {
|
||||
// protects logs
|
||||
sync.RWMutex
|
||||
|
||||
size int
|
||||
logs []string
|
||||
}
|
||||
|
||||
func NewScanLog(size int) *ScanLog {
|
||||
if size <= 0 {
|
||||
size = defaultSize
|
||||
}
|
||||
return &ScanLog{size: size}
|
||||
}
|
||||
|
||||
func (l *ScanLog) Printf(f string, v ...interface{}) {
|
||||
s := fmt.Sprintf(f, v...)
|
||||
Println(s)
|
||||
s = core.LogTime() + s
|
||||
l.Lock()
|
||||
llen := len(l.logs)
|
||||
if llen < l.size {
|
||||
l.logs = append(l.logs, s)
|
||||
} else if llen == l.size {
|
||||
l.logs = append(l.logs[1:], s)
|
||||
}
|
||||
l.Unlock()
|
||||
}
|
||||
|
||||
func (l *ScanLog) Logs() (logs string) {
|
||||
l.RLock()
|
||||
logs = reverseJoin(l.logs, "\n")
|
||||
l.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// reverseJoin is a reverse version of strings.Join
|
||||
// code taken from strings.Join
|
||||
func reverseJoin(a []string, sep string) string {
|
||||
switch len(a) {
|
||||
case 0:
|
||||
return ""
|
||||
case 1:
|
||||
return a[0]
|
||||
}
|
||||
n := len(sep) * (len(a) - 1)
|
||||
for i := 0; i < len(a); i++ {
|
||||
n += len(a[i])
|
||||
}
|
||||
|
||||
var b strings.Builder
|
||||
b.Grow(n)
|
||||
b.WriteString(a[len(a)-1])
|
||||
for i := len(a) - 2; i >= 0; i-- {
|
||||
b.WriteString(sep)
|
||||
b.WriteString(a[i])
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (C) 2021 Marius Schellenberger
|
||||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package otp
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (C) 2021 Marius Schellenberger
|
||||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package scan
|
||||
|
||||
|
|
@ -6,7 +6,6 @@ import (
|
|||
"bytes"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -19,8 +18,6 @@ const (
|
|||
pnmT = "image/x-portable-anymap"
|
||||
jp2T = "image/jp2"
|
||||
tiffT = "image/tiff"
|
||||
textT = "text/"
|
||||
rawT = "application/octet-stream"
|
||||
maxSniffSize = 512
|
||||
)
|
||||
|
||||
|
|
@ -48,11 +45,35 @@ func extendedContentType(buf []byte) string {
|
|||
return sig.ct
|
||||
}
|
||||
}
|
||||
return rawT
|
||||
return "application/octet-stream"
|
||||
}
|
||||
|
||||
func isImage(ct string, buf []byte) bool {
|
||||
switch ct {
|
||||
func isPDF(path string) bool {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer f.Close()
|
||||
buf := make([]byte, maxSniffSize)
|
||||
_, err = f.Read(buf)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return http.DetectContentType(buf) == pdfT
|
||||
}
|
||||
|
||||
func isImage(path string) bool {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer f.Close()
|
||||
buf := make([]byte, maxSniffSize)
|
||||
_, err = f.Read(buf)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
switch http.DetectContentType(buf) {
|
||||
case jpegT, pngT, bmpT, gifT, webpT:
|
||||
return true
|
||||
default:
|
||||
|
|
@ -63,29 +84,3 @@ func isImage(ct string, buf []byte) bool {
|
|||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type filetype struct {
|
||||
text bool
|
||||
pdf bool
|
||||
image bool
|
||||
}
|
||||
|
||||
func (s *Scanner) newFileType(path string) (ft *filetype) {
|
||||
ft = new(filetype)
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
buf := make([]byte, maxSniffSize)
|
||||
n, err := f.Read(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
buf = buf[:n]
|
||||
ct := http.DetectContentType(buf)
|
||||
ft.text = strings.HasPrefix(ct, textT)
|
||||
ft.pdf = ct == pdfT
|
||||
ft.image = isImage(ct, buf)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,19 @@
|
|||
// Copyright (C) 2021 Marius Schellenberger
|
||||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package scan
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"git.giftfish.de/ston1th/docstore/pkg/core"
|
||||
"git.giftfish.de/ston1th/docstore/pkg/fs"
|
||||
"git.giftfish.de/ston1th/docstore/pkg/log"
|
||||
"git.giftfish.de/ston1th/godrop/v2"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"git.giftfish.de/ston1th/docstore/pkg/core"
|
||||
"git.giftfish.de/ston1th/docstore/pkg/fs"
|
||||
"git.giftfish.de/ston1th/godrop/v2"
|
||||
"github.com/go-logr/logr"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -39,20 +38,19 @@ type Tesseract struct {
|
|||
|
||||
var (
|
||||
wsr = regexp.MustCompile(`[\t\f ]+`)
|
||||
nlr = regexp.MustCompile(`\n{3,}`)
|
||||
nlr = regexp.MustCompile(`[\n]{3,}`)
|
||||
ws = []byte(" ")
|
||||
nl = []byte("\n")
|
||||
)
|
||||
|
||||
type Scanner struct {
|
||||
log logr.Logger
|
||||
base string
|
||||
timeout time.Duration
|
||||
t Tesseract
|
||||
p PDF
|
||||
}
|
||||
|
||||
func New(log logr.Logger, c *core.Config) (s *Scanner, err error) {
|
||||
func New(c core.Config) (s *Scanner, err error) {
|
||||
var (
|
||||
tcmd string
|
||||
pcmd string
|
||||
|
|
@ -94,7 +92,6 @@ func New(log logr.Logger, c *core.Config) (s *Scanner, err error) {
|
|||
}
|
||||
args := []string{"-l", lang, "--oem", fmt.Sprintf("%d", c.TesseractOEM)}
|
||||
s = &Scanner{
|
||||
log,
|
||||
c.DataDir,
|
||||
time.Second * time.Duration(c.CMDTimeout),
|
||||
Tesseract{tcmd, args, []string{fmt.Sprintf("OMP_THREAD_LIMIT=%d", threads)}},
|
||||
|
|
@ -104,27 +101,14 @@ func New(log logr.Logger, c *core.Config) (s *Scanner, err error) {
|
|||
}
|
||||
|
||||
func (s *Scanner) Scan(file string) (filename, text string, err error) {
|
||||
log := s.log
|
||||
file = fs.Clean(file)
|
||||
scanfile := filepath.Join(s.base, file)
|
||||
var pdffile string
|
||||
ft := s.newFileType(scanfile)
|
||||
if !ft.pdf {
|
||||
if !ft.image {
|
||||
if ft.text {
|
||||
log.V(2).Info("indexing plaintext file", "file", scanfile)
|
||||
var buf []byte
|
||||
buf, err = os.ReadFile(scanfile)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
filename = file
|
||||
text = string(nlr.ReplaceAll(wsr.ReplaceAll(buf, ws), nl))
|
||||
return
|
||||
} else {
|
||||
err = errors.New("error: input: no image, pdf or text file")
|
||||
return
|
||||
}
|
||||
pdf := isPDF(scanfile)
|
||||
if !pdf {
|
||||
if !isImage(scanfile) {
|
||||
err = errors.New("error: input file is not an image")
|
||||
return
|
||||
}
|
||||
var (
|
||||
pdf []byte
|
||||
|
|
@ -133,7 +117,7 @@ func (s *Scanner) Scan(file string) (filename, text string, err error) {
|
|||
p, _ := filepath.Split(file)
|
||||
pdfCmd := exec.Command(s.t.cmd, append(s.t.args, []string{scanfile, "stdout", "pdf"}...)...)
|
||||
pdfCmd.Env = s.t.env
|
||||
log.V(2).Info("tesseract command", "env", pdfCmd.Env, "path", pdfCmd.Path, "args", pdfCmd.Args)
|
||||
log.Debugf("%v %s %v", pdfCmd.Env, pdfCmd.Path, pdfCmd.Args)
|
||||
pdf, err = timeout(pdfCmd, s.timeout)
|
||||
if err != nil {
|
||||
return
|
||||
|
|
@ -156,7 +140,7 @@ func (s *Scanner) Scan(file string) (filename, text string, err error) {
|
|||
filename = file
|
||||
}
|
||||
txtCmd := exec.Command(s.p.cmd, append(s.p.args, []string{pdffile, "-"}...)...)
|
||||
log.V(2).Info("text2pdf command", "path", txtCmd.Path, "args", txtCmd.Args)
|
||||
log.Debugf("%s %v", txtCmd.Path, txtCmd.Args)
|
||||
txt, err := timeout(txtCmd, s.timeout)
|
||||
if err != nil {
|
||||
return
|
||||
|
|
@ -166,7 +150,7 @@ func (s *Scanner) Scan(file string) (filename, text string, err error) {
|
|||
return
|
||||
}
|
||||
text = string(nlr.ReplaceAll(wsr.ReplaceAll(txt, ws), nl))
|
||||
if !ft.pdf {
|
||||
if !pdf {
|
||||
err = os.Remove(scanfile)
|
||||
}
|
||||
return
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (C) 2021 Marius Schellenberger
|
||||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package scan
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
// Copyright (C) 2021 Marius Schellenberger
|
||||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"git.giftfish.de/ston1th/docstore/pkg/core"
|
||||
"git.giftfish.de/ston1th/docstore/pkg/log"
|
||||
"git.giftfish.de/ston1th/jwt/v3"
|
||||
"github.com/gorilla/mux"
|
||||
"html/template"
|
||||
"io"
|
||||
"net/http"
|
||||
|
|
@ -11,10 +15,6 @@ import (
|
|||
"reflect"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"git.giftfish.de/ston1th/docstore/pkg/core"
|
||||
"git.giftfish.de/ston1th/jwt/v3"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -74,11 +74,10 @@ type webData struct {
|
|||
Msg string
|
||||
Query string
|
||||
|
||||
Path string
|
||||
Paths core.Paths
|
||||
Tags []string
|
||||
Created string
|
||||
Data interface{}
|
||||
Path string
|
||||
Paths core.Paths
|
||||
Tags []string
|
||||
Data interface{}
|
||||
}
|
||||
|
||||
type loginData struct {
|
||||
|
|
@ -93,7 +92,7 @@ func (c *Context) Exec() {
|
|||
c.Err = errors.New("template is nil")
|
||||
return
|
||||
}
|
||||
c.Data.Token = newXsrf(c.Token.RawSig())
|
||||
c.Data.Token = newXsrf(c.Token.RawSig()[:keySize])
|
||||
|
||||
c.Data.Version = c.Srv.Config.Version
|
||||
if c.Data.BodyTitle == "" {
|
||||
|
|
@ -123,20 +122,10 @@ func (c *Context) Log() {
|
|||
c.HTTPStatus = int(reflect.Indirect(reflect.ValueOf(c.Response)).FieldByName("status").Int())
|
||||
}
|
||||
if c.Err != nil {
|
||||
c.Srv.Log.Error(c.Err, "access",
|
||||
"client", c.Request.RemoteAddr,
|
||||
"method", c.Request.Method,
|
||||
"status", c.HTTPStatus,
|
||||
"uri", c.Request.URL.Path,
|
||||
)
|
||||
log.Printf("%s %s %s %d error: %s\n", c.Request.RemoteAddr, c.Request.Method, c.Request.URL, c.HTTPStatus, c.Err)
|
||||
return
|
||||
}
|
||||
c.Srv.Log.Info("access",
|
||||
"client", c.Request.RemoteAddr,
|
||||
"method", c.Request.Method,
|
||||
"status", c.HTTPStatus,
|
||||
"uri", c.Request.URL.Path,
|
||||
)
|
||||
log.Printf("%s %s %s %d\n", c.Request.RemoteAddr, c.Request.Method, c.Request.URL, c.HTTPStatus)
|
||||
}
|
||||
|
||||
func (c *Context) Error(i interface{}) {
|
||||
|
|
@ -164,7 +153,7 @@ func (c *Context) Forbidden() {
|
|||
}
|
||||
|
||||
func (c *Context) SwitchHandler() {
|
||||
c.Srv.Log.V(2).Info("switching to normal handler")
|
||||
log.Debug("server: switching to normal handler")
|
||||
c.Srv.srv.Handler = c.Srv.handler
|
||||
c.Redirect(core.IndexURI, http.StatusFound)
|
||||
}
|
||||
|
|
@ -191,13 +180,7 @@ func (c *Context) Redirect(uri string, code int) {
|
|||
if len(uri) > 0 && uri[0] != '/' {
|
||||
uri = "/" + uri
|
||||
}
|
||||
c.Srv.Log.Info("access",
|
||||
"client", c.Request.RemoteAddr,
|
||||
"method", c.Request.Method,
|
||||
"status", code,
|
||||
"uri", c.Request.URL.Path,
|
||||
"redirect", uri,
|
||||
)
|
||||
log.Printf("%s %s %s %d %s\n", c.Request.RemoteAddr, c.Request.Method, c.Request.URL, code, uri)
|
||||
http.Redirect(c.Response, c.Request, uri, code)
|
||||
}
|
||||
|
||||
|
|
@ -232,7 +215,7 @@ func (c *Context) GetForm(name string) string {
|
|||
}
|
||||
|
||||
func (c *Context) Var(name string) (ret string) {
|
||||
ret = mux.Vars(c.Request)[name]
|
||||
ret, _ = mux.Vars(c.Request)[name]
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -247,7 +230,7 @@ func (c *Context) File(name string) (io.Reader, string, error) {
|
|||
|
||||
// CheckXsrf validates the xsrf token
|
||||
func (c *Context) CheckXsrf() (ok bool) {
|
||||
ok = checkXsrf(c.Form("token"), c.Token.RawSig())
|
||||
ok = checkXsrf(c.Form("token"), c.Token.RawSig()[:keySize])
|
||||
if !ok {
|
||||
c.Error("wrong csrf token")
|
||||
}
|
||||
|
|
@ -260,7 +243,7 @@ func (c *Context) LoggedOn() (ok bool) {
|
|||
}
|
||||
|
||||
func (c *Context) LogSetCookie(msg string, err error) {
|
||||
c.Srv.Log.Error(err, msg)
|
||||
log.Printf("ctx: %s %s\n", msg, err)
|
||||
c.SetCookie(nil, 0)
|
||||
}
|
||||
|
||||
|
|
@ -282,7 +265,7 @@ func (c *Context) setCookie(t *jwt.Token, d time.Duration) {
|
|||
}
|
||||
t.Claims[jwt.ExpClaim] = jwt.NewExp(d)
|
||||
if err := c.Srv.JWT.Sign(t); err != nil {
|
||||
c.Srv.Log.Error(err, "jwt sign")
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
c.Token = *t
|
||||
|
|
|
|||
|
|
@ -1,18 +1,18 @@
|
|||
// Copyright (C) 2021 Marius Schellenberger
|
||||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"git.giftfish.de/ston1th/docstore/pkg/core"
|
||||
"git.giftfish.de/ston1th/docstore/pkg/fs"
|
||||
"git.giftfish.de/ston1th/docstore/pkg/log"
|
||||
"git.giftfish.de/ston1th/docstore/pkg/otp"
|
||||
"git.giftfish.de/ston1th/jwt/v3"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.giftfish.de/ston1th/docstore/pkg/core"
|
||||
"git.giftfish.de/ston1th/docstore/pkg/fs"
|
||||
"git.giftfish.de/ston1th/docstore/pkg/otp"
|
||||
"git.giftfish.de/ston1th/jwt/v3"
|
||||
)
|
||||
|
||||
const noSuchFile = "no such file or directory"
|
||||
|
|
@ -97,6 +97,24 @@ func authHandler(h ctxHandler) ctxHandler {
|
|||
}
|
||||
}
|
||||
|
||||
func staticHandler(ctx *Context) {
|
||||
var err error
|
||||
ctx.SetHeader("Content-Type", "text/css; charset=utf-8")
|
||||
ctx.SetHeader("Expires", time.Now().UTC().Add(max).Format(http.TimeFormat))
|
||||
switch ctx.Path() {
|
||||
case core.BootstrapCSS:
|
||||
err = ctx.Write(ctx.Srv.res["bootstrap.css"])
|
||||
case core.CustomCSS:
|
||||
err = ctx.Write(ctx.Srv.res["custom.css"])
|
||||
case core.Favicon:
|
||||
ctx.SetHeader("Content-Type", "image/x-icon")
|
||||
err = ctx.Write(ctx.Srv.res["favicon.ico"])
|
||||
}
|
||||
if err != nil {
|
||||
log.Println("static:", err)
|
||||
}
|
||||
}
|
||||
|
||||
type registerNotFoundHandler struct {
|
||||
s *HTTPServer
|
||||
}
|
||||
|
|
@ -146,46 +164,69 @@ func rawHandler(ctx *Context) {
|
|||
f, fi, err := ctx.Srv.FS.GetFile(path)
|
||||
if err != nil {
|
||||
ctx.Status(http.StatusNotFound)
|
||||
ctx.Err = ctx.Write([]byte(noSuchFile))
|
||||
err = ctx.Write([]byte(noSuchFile))
|
||||
if err != nil {
|
||||
log.Println("file:", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
ctx.Response.Header().Del("Content-Security-Policy")
|
||||
http.ServeContent(ctx.Response, ctx.Request, fi.Name(), fi.ModTime(), f)
|
||||
}
|
||||
|
||||
func scanFile(srv *HTTPServer, path string) (err error) {
|
||||
err = srv.FS.AddScan(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
defer srv.FS.RemoveScan(path)
|
||||
l := srv.Log
|
||||
file, txt, err := srv.Scanner.Scan(path)
|
||||
if err != nil {
|
||||
l.Printf("scanner: %s: %s", path, err)
|
||||
return
|
||||
}
|
||||
tags, err := srv.DB.GetAllRTags()
|
||||
if err != nil {
|
||||
l.Printf("getTags: %s: %s", path, err)
|
||||
}
|
||||
found := tags.Match(txt)
|
||||
id, err := srv.DB.Index.Add(txt, found)
|
||||
if err != nil {
|
||||
l.Printf("index: %s: %s", path, err)
|
||||
return
|
||||
}
|
||||
err = srv.DB.NewFile(id, file, found)
|
||||
if err != nil {
|
||||
l.Printf("newFile: %s: %s", path, err)
|
||||
}
|
||||
}()
|
||||
return
|
||||
}
|
||||
|
||||
func indexHandler(ctx *Context) {
|
||||
log := ctx.Srv.Log
|
||||
switch ctx.Method() {
|
||||
case "GET":
|
||||
var (
|
||||
err error
|
||||
ok bool
|
||||
fpath string
|
||||
)
|
||||
path := ctx.Path()
|
||||
if fpath, ok = hasTrimPrefix(path, core.IndexPrefix); ok {
|
||||
log = log.WithValues("file", fpath)
|
||||
log.V(2).Info("started indexing file")
|
||||
err = ctx.Srv.SQ.Add(fpath, nil)
|
||||
if err != nil {
|
||||
log.Error(err, "error indexing file")
|
||||
}
|
||||
} else if fpath, ok = hasTrimPrefix(path, core.RetagPrefix); ok {
|
||||
if strings.HasPrefix(path, core.IndexPrefix) {
|
||||
fpath = strings.TrimPrefix(path, core.IndexPrefix)
|
||||
err = scanFile(ctx.Srv, fpath)
|
||||
}
|
||||
if strings.HasPrefix(path, core.RetagPrefix) {
|
||||
fpath = strings.TrimPrefix(path, core.RetagPrefix)
|
||||
if fpath == "" {
|
||||
log.V(2).Info("started retagging all files")
|
||||
go func() {
|
||||
err := ctx.Srv.DB.RetagAll()
|
||||
if err != nil {
|
||||
log.Error(err, "error retagging all files")
|
||||
ctx.Srv.Log.Printf("retagAll: %s", err)
|
||||
}
|
||||
}()
|
||||
} else {
|
||||
log = log.WithValues("file", fpath)
|
||||
log.V(2).Info("started retagging file")
|
||||
err = ctx.Srv.DB.Retag(fpath, nil)
|
||||
if err != nil {
|
||||
log.Error(err, "error retagging file")
|
||||
}
|
||||
}
|
||||
}
|
||||
p, _ := filepath.Split(fpath)
|
||||
|
|
@ -207,22 +248,19 @@ func indexHandler(ctx *Context) {
|
|||
return
|
||||
}
|
||||
if m, c, ok := ctx.Srv.DB.IndexMutex.Lock(); ok {
|
||||
log.V(2).Info("started indexing all files")
|
||||
go func() {
|
||||
defer ctx.Srv.DB.IndexMutex.Unlock()
|
||||
files := ctx.Srv.FS.RecursiveFiles("/")
|
||||
ctx.Srv.DB.IndexMutex.Set(uint64(len(files)))
|
||||
for _, f := range files {
|
||||
if !ctx.Srv.DB.IsIndexed(f) {
|
||||
err := ctx.Srv.SQ.Add(f, ctx.Srv.DB.IndexMutex.Inc)
|
||||
if err != nil {
|
||||
log.Error(err, "error indexing file", "file", f)
|
||||
}
|
||||
scanFile(ctx.Srv, f)
|
||||
}
|
||||
ctx.Srv.DB.IndexMutex.Inc()
|
||||
}
|
||||
}()
|
||||
} else {
|
||||
log.Info("indexing of all files is already in progress", "indexed", c, "remaning", m)
|
||||
ctx.Srv.Log.Printf("indexAll: indexing already in progress: %d/%d", c, m)
|
||||
}
|
||||
ctx.Redirect(core.IndexURI, http.StatusFound)
|
||||
}
|
||||
|
|
@ -282,7 +320,6 @@ func newDirHandler(ctx *Context) {
|
|||
}
|
||||
|
||||
func moveHandler(ctx *Context) {
|
||||
log := ctx.Srv.Log
|
||||
path := strings.TrimPrefix(ctx.Path(), core.MovePrefix)
|
||||
mode, _, err := ctx.Srv.FS.Mode(path)
|
||||
if err != nil {
|
||||
|
|
@ -327,19 +364,18 @@ func moveHandler(ctx *Context) {
|
|||
return
|
||||
}
|
||||
for _, m := range moved {
|
||||
log.V(2).Info("moving file", "old", m.Old, "new", m.New)
|
||||
err = ctx.Srv.DB.MoveFile(m.Old, m.New)
|
||||
if err != nil {
|
||||
log.Error(err, "moving file failed", "old", m.Old, "new", m.New)
|
||||
log.Printf("move: %s: %s", m, err)
|
||||
continue
|
||||
}
|
||||
log.Debugf("move: %s to %s", m.Old, m.New)
|
||||
}
|
||||
ctx.Redirect(newfile, http.StatusFound)
|
||||
}
|
||||
}
|
||||
|
||||
func deleteHandler(ctx *Context) {
|
||||
log := ctx.Srv.Log
|
||||
path := strings.TrimPrefix(ctx.Path(), core.DeletePrefix)
|
||||
mode, enoent, err := ctx.Srv.FS.Mode(path)
|
||||
if !enoent && err != nil {
|
||||
|
|
@ -382,21 +418,18 @@ func deleteHandler(ctx *Context) {
|
|||
clean := ctx.Form("clean")
|
||||
if clean == "true" {
|
||||
f := fs.Clean(path)
|
||||
log = log.WithValues("file", f)
|
||||
log.V(2).Info("deleting file from DB")
|
||||
id, _, err := ctx.Srv.DB.DeleteFile(f)
|
||||
if err != nil {
|
||||
log.Error(err, "delete from DB failed")
|
||||
log.Printf("db: delete: %s %s", f, err)
|
||||
}
|
||||
if id == "" {
|
||||
ctx.Redirect(p, http.StatusFound)
|
||||
return
|
||||
}
|
||||
log = log.WithValues("id", id)
|
||||
log.V(2).Info("deleting file from index")
|
||||
log.Debugf("delete %s id: %s from index", f, id)
|
||||
err = ctx.Srv.DB.Index.Delete(id)
|
||||
if err != nil {
|
||||
log.Error(err, "delete from index failed")
|
||||
log.Printf("index: delete: %s %s", f, err)
|
||||
}
|
||||
ctx.Redirect(p, http.StatusFound)
|
||||
return
|
||||
|
|
@ -407,18 +440,17 @@ func deleteHandler(ctx *Context) {
|
|||
return
|
||||
}
|
||||
for _, f := range files {
|
||||
log.V(2).Info("deleting file from DB", "file", f)
|
||||
id, _, err := ctx.Srv.DB.DeleteFile(f)
|
||||
if err != nil {
|
||||
log.Error(err, "delete from DB failed", "file", f)
|
||||
log.Printf("db: delete: %s %s", f, err)
|
||||
}
|
||||
if id == "" {
|
||||
continue
|
||||
}
|
||||
log.V(2).Info("deleting file from index", "file", f, "id", id)
|
||||
log.Debugf("delete %s id: %s from index", f, id)
|
||||
err = ctx.Srv.DB.Index.Delete(id)
|
||||
if err != nil {
|
||||
log.Error(err, "delete from index failed", "file", f, "id", id)
|
||||
log.Printf("index: delete: %s %s", f, err)
|
||||
}
|
||||
}
|
||||
ctx.Redirect(p, http.StatusFound)
|
||||
|
|
@ -428,7 +460,8 @@ func deleteHandler(ctx *Context) {
|
|||
func dirHandler(ctx *Context) {
|
||||
ctx.Template("dirHandler")
|
||||
path := ctx.Path()
|
||||
if ctx.Method() == "GET" {
|
||||
switch ctx.Method() {
|
||||
case "GET":
|
||||
mode, enoent, err := ctx.Srv.FS.Mode(path)
|
||||
if !enoent && err != nil {
|
||||
ctx.Error(err)
|
||||
|
|
@ -450,15 +483,7 @@ func dirHandler(ctx *Context) {
|
|||
Title: "Document Viewer | " + path,
|
||||
}
|
||||
ctx.Template("fileHandler")
|
||||
// f := ctx.Srv.DB.IsIndexed(path)
|
||||
id, err := ctx.Srv.DB.GetID(path)
|
||||
f := err == nil
|
||||
if id != "" {
|
||||
d, err := ctx.Srv.DB.Index.Get(id)
|
||||
if err == nil {
|
||||
ctx.Data.Created = core.CreatedTime(d.Created)
|
||||
}
|
||||
}
|
||||
f := ctx.Srv.DB.IsIndexed(path)
|
||||
ctx.Data.Data = core.Path{
|
||||
Name: path,
|
||||
Abs: core.RawPrefix + path,
|
||||
|
|
@ -500,7 +525,6 @@ func loginHandler(ctx *Context) {
|
|||
Title: "Login",
|
||||
BodyTitle: "Login",
|
||||
}
|
||||
log := ctx.Srv.Log
|
||||
var data loginData
|
||||
switch ctx.Method() {
|
||||
case "GET":
|
||||
|
|
@ -524,12 +548,12 @@ func loginHandler(ctx *Context) {
|
|||
}
|
||||
u, err := ctx.Srv.DB.Login(data.User, password)
|
||||
if err != nil {
|
||||
log.Error(err, "login failed", "user", data.User)
|
||||
log.Println("login:", data.User, err)
|
||||
ctx.Error("bad username or password")
|
||||
return
|
||||
}
|
||||
if u.Secret == "" {
|
||||
log.Info("login successful", "user", u.Username)
|
||||
log.Println("login:", u.Username)
|
||||
ctx.Login(u, ctx.Form("remember"))
|
||||
ctx.Redirect(data.Referer, http.StatusFound)
|
||||
return
|
||||
|
|
@ -553,7 +577,6 @@ func loginTotpHandler(ctx *Context) {
|
|||
Title: "TOTP Verification",
|
||||
BodyTitle: "TOTP Veriftcation",
|
||||
}
|
||||
log := ctx.Srv.Log
|
||||
switch ctx.Method() {
|
||||
case "GET":
|
||||
ctx.Exec()
|
||||
|
|
@ -572,7 +595,7 @@ func loginTotpHandler(ctx *Context) {
|
|||
return
|
||||
}
|
||||
ref := ctx.Referer()
|
||||
log.Info("login successful", "user", u.Username)
|
||||
log.Println("login:", u.Username)
|
||||
ctx.Login(u, ctx.Remember())
|
||||
ctx.Redirect(ref, http.StatusFound)
|
||||
}
|
||||
|
|
@ -612,7 +635,6 @@ func userEditHandler(ctx *Context) {
|
|||
Title: "Edit User",
|
||||
BodyTitle: "Edit User",
|
||||
}
|
||||
log := ctx.Srv.Log
|
||||
u, err := ctx.Srv.DB.GetUserWithoutPassword()
|
||||
if err != nil {
|
||||
ctx.Error(err)
|
||||
|
|
@ -637,7 +659,7 @@ func userEditHandler(ctx *Context) {
|
|||
ctx.Error(err)
|
||||
return
|
||||
}
|
||||
log.Info("user updated", "user", ctx.User())
|
||||
log.Printf("update: %s updated\n", ctx.User())
|
||||
ctx.Redirect(core.IndexURI, http.StatusFound)
|
||||
}
|
||||
}
|
||||
|
|
@ -648,7 +670,6 @@ func userTotpHandler(ctx *Context) {
|
|||
Title: "TOTP",
|
||||
BodyTitle: "TOTP",
|
||||
}
|
||||
log := ctx.Srv.Log
|
||||
req := otp.Request{Username: ctx.User()}
|
||||
u, err := ctx.Srv.DB.GetUser()
|
||||
if err != nil {
|
||||
|
|
@ -702,9 +723,9 @@ func userTotpHandler(ctx *Context) {
|
|||
return
|
||||
}
|
||||
if req.Secret == "" {
|
||||
log.Info("totp disabled", "user", user)
|
||||
log.Printf("totp: disabled for %s\n", user)
|
||||
} else {
|
||||
log.Info("totp enabled", "user", user)
|
||||
log.Printf("totp: enabled for %s\n", user)
|
||||
}
|
||||
ctx.Redirect("/user/edit", http.StatusFound)
|
||||
}
|
||||
|
|
@ -716,9 +737,9 @@ func logsHandler(ctx *Context) {
|
|||
Title: "Logs",
|
||||
BodyTitle: "Logs",
|
||||
}
|
||||
if ctx.Method() == "GET" {
|
||||
// TODO remove
|
||||
//ctx.Data.Data = ctx.Srv.Log.Logs()
|
||||
switch ctx.Method() {
|
||||
case "GET":
|
||||
ctx.Data.Data = ctx.Srv.Log.Logs()
|
||||
ctx.Exec()
|
||||
}
|
||||
}
|
||||
|
|
@ -729,7 +750,8 @@ func tagsHandler(ctx *Context) {
|
|||
Title: "Tags",
|
||||
BodyTitle: "Tags",
|
||||
}
|
||||
if ctx.Method() == "GET" {
|
||||
switch ctx.Method() {
|
||||
case "GET":
|
||||
t, err := ctx.Srv.DB.GetAllTags()
|
||||
if err != nil {
|
||||
ctx.Error(err)
|
||||
|
|
@ -746,7 +768,6 @@ func tagsNewHandler(ctx *Context) {
|
|||
Title: "New Tag",
|
||||
BodyTitle: "New Tag",
|
||||
}
|
||||
log := ctx.Srv.Log
|
||||
switch ctx.Method() {
|
||||
case "GET":
|
||||
ctx.Data.Data = core.Tag{}
|
||||
|
|
@ -771,7 +792,7 @@ func tagsNewHandler(ctx *Context) {
|
|||
ctx.Error(err)
|
||||
return
|
||||
}
|
||||
log.Info("new tag created", "user", ctx.User(), "tag", t.Name)
|
||||
log.Printf("tag %s created by: %s\n", t.Name, ctx.User())
|
||||
ctx.Redirect(core.TagsURI, http.StatusFound)
|
||||
}
|
||||
}
|
||||
|
|
@ -782,7 +803,6 @@ func tagsEditHandler(ctx *Context) {
|
|||
Title: "Edit Tag",
|
||||
BodyTitle: "Edit Tag",
|
||||
}
|
||||
log := ctx.Srv.Log
|
||||
name := ctx.Var("tag")
|
||||
switch ctx.Method() {
|
||||
case "GET":
|
||||
|
|
@ -809,7 +829,7 @@ func tagsEditHandler(ctx *Context) {
|
|||
ctx.Error(err)
|
||||
return
|
||||
}
|
||||
log.Info("tag updated", "user", ctx.User(), "tag", t.Name)
|
||||
log.Printf("tag %s updated by: %s\n", t.Name, ctx.User())
|
||||
ctx.Redirect(core.TagsURI, http.StatusFound)
|
||||
}
|
||||
}
|
||||
|
|
@ -820,7 +840,6 @@ func tagsDelHandler(ctx *Context) {
|
|||
Title: "Delete Tag",
|
||||
BodyTitle: "Delete Tag",
|
||||
}
|
||||
log := ctx.Srv.Log
|
||||
name := ctx.Var("tag")
|
||||
ctx.Data.Data = name
|
||||
switch ctx.Method() {
|
||||
|
|
@ -834,30 +853,17 @@ func tagsDelHandler(ctx *Context) {
|
|||
ctx.Error(err)
|
||||
return
|
||||
}
|
||||
log.Info("tag deleted", "user", ctx.User(), "tag", name)
|
||||
log.Printf("tag %s deleted by: %s\n", name, ctx.User())
|
||||
ctx.Redirect(core.TagsURI, http.StatusFound)
|
||||
}
|
||||
}
|
||||
|
||||
func statsHandler(ctx *Context) {
|
||||
ctx.Template("statsHandler")
|
||||
ctx.Data = webData{
|
||||
Title: "Stats",
|
||||
BodyTitle: "Stats",
|
||||
}
|
||||
if ctx.Method() == "GET" {
|
||||
ctx.Data.Data = getStats(ctx.Srv.DB.Index)
|
||||
ctx.Exec()
|
||||
}
|
||||
}
|
||||
|
||||
func logoutHandler(ctx *Context) {
|
||||
log := ctx.Srv.Log
|
||||
user := ctx.User()
|
||||
if user == "" {
|
||||
user = ctx.Totp() + " (totp)"
|
||||
}
|
||||
log.Info("user logged out", "user", user)
|
||||
log.Println("logout:", user)
|
||||
ctx.Srv.JWT.Invalidate(&ctx.Token)
|
||||
ctx.SetCookie(nil, 0)
|
||||
ctx.Redirect(core.LoginURI, http.StatusFound)
|
||||
|
|
|
|||
|
|
@ -1,37 +0,0 @@
|
|||
// Copyright (C) 2021 Marius Schellenberger
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"strconv"
|
||||
|
||||
"git.giftfish.de/ston1th/docstore/pkg/core"
|
||||
bleve "git.giftfish.de/ston1th/docstore/pkg/index"
|
||||
)
|
||||
|
||||
const div = 1048576
|
||||
|
||||
func fmtMem(i uint64) string {
|
||||
return strconv.FormatFloat(float64(i)/div, 'f', 2, 64)
|
||||
}
|
||||
|
||||
func getStats(i *bleve.Index) *core.Stats {
|
||||
mem := new(runtime.MemStats)
|
||||
runtime.ReadMemStats(mem)
|
||||
return &core.Stats{
|
||||
Goroutines: runtime.NumGoroutine(),
|
||||
Alloc: fmtMem(mem.Alloc),
|
||||
Sys: fmtMem(mem.Sys),
|
||||
Docs: i.DocCount(),
|
||||
GoVersion: runtime.Version(),
|
||||
}
|
||||
}
|
||||
|
||||
// Parts taken from strings.TrimPrefix
|
||||
func hasTrimPrefix(s, prefix string) (string, bool) {
|
||||
if len(s) >= len(prefix) && s[0:len(prefix)] == prefix {
|
||||
return s[len(prefix):], true
|
||||
}
|
||||
return s, false
|
||||
}
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
// Copyright (C) 2022 Marius Schellenberger
|
||||
|
||||
package server
|
||||
|
||||
import "context"
|
||||
|
||||
type ScanQueue struct {
|
||||
ctx context.Context
|
||||
srv *HTTPServer
|
||||
jobs chan job
|
||||
}
|
||||
|
||||
type job struct {
|
||||
path string
|
||||
callback func()
|
||||
}
|
||||
|
||||
func NewScanQueue(ctx context.Context, srv *HTTPServer, buffer int) *ScanQueue {
|
||||
return &ScanQueue{
|
||||
ctx: ctx,
|
||||
srv: srv,
|
||||
jobs: make(chan job, buffer),
|
||||
}
|
||||
}
|
||||
|
||||
func (sq *ScanQueue) Add(path string, callback func()) (err error) {
|
||||
err = sq.srv.FS.AddScan(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
select {
|
||||
case <-sq.ctx.Done():
|
||||
return
|
||||
case sq.jobs <- job{path, callback}:
|
||||
}
|
||||
}()
|
||||
return
|
||||
}
|
||||
|
||||
func (sq *ScanQueue) Scan() {
|
||||
for {
|
||||
select {
|
||||
case <-sq.ctx.Done():
|
||||
//close(sq.jobs)
|
||||
return
|
||||
case j := <-sq.jobs:
|
||||
sq.scanFile(j)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (sq *ScanQueue) scanFile(j job) {
|
||||
srv := sq.srv
|
||||
path := j.path
|
||||
if j.callback != nil {
|
||||
defer j.callback()
|
||||
}
|
||||
defer srv.FS.RemoveScan(path)
|
||||
log := srv.Log.WithValues("file", path)
|
||||
file, txt, err := srv.Scanner.Scan(path)
|
||||
if err != nil {
|
||||
log.Error(err, "error scanning file")
|
||||
return
|
||||
}
|
||||
tags, err := srv.DB.GetAllRTags()
|
||||
if err != nil {
|
||||
log.Error(err, "error getting tags")
|
||||
}
|
||||
found := tags.Match(txt)
|
||||
id, err := srv.DB.Index.Add(txt, found)
|
||||
if err != nil {
|
||||
log.Error(err, "error adding file to index")
|
||||
return
|
||||
}
|
||||
err = srv.DB.NewFile(id, file, found)
|
||||
if err != nil {
|
||||
log.Error(err, "error adding file to DB")
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (C) 2021 Marius Schellenberger
|
||||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package server
|
||||
|
||||
|
|
@ -10,15 +10,31 @@ type route struct {
|
|||
Methods []string
|
||||
}
|
||||
|
||||
var registerRoutes = []route{
|
||||
var static = []route{
|
||||
{
|
||||
core.IndexURI,
|
||||
jwtRegisterHandler(
|
||||
registerHandler),
|
||||
[]string{"GET", "POST"},
|
||||
core.Favicon,
|
||||
staticHandler,
|
||||
[]string{"GET"},
|
||||
},
|
||||
{
|
||||
core.BootstrapCSS,
|
||||
staticHandler,
|
||||
[]string{"GET"},
|
||||
},
|
||||
{
|
||||
core.CustomCSS,
|
||||
staticHandler,
|
||||
[]string{"GET"},
|
||||
},
|
||||
}
|
||||
|
||||
var registerRoutes = append(static, route{
|
||||
core.IndexURI,
|
||||
jwtRegisterHandler(
|
||||
registerHandler),
|
||||
[]string{"GET", "POST"},
|
||||
})
|
||||
|
||||
var prefixRoutes = []route{
|
||||
{
|
||||
core.RawPrefix,
|
||||
|
|
@ -78,7 +94,7 @@ var prefixRoutes = []route{
|
|||
},
|
||||
}
|
||||
|
||||
var routes = []route{
|
||||
var routes = append(static, []route{
|
||||
{
|
||||
core.SearchURI,
|
||||
jwtHandler(
|
||||
|
|
@ -127,13 +143,6 @@ var routes = []route{
|
|||
tagsHandler)),
|
||||
[]string{"GET"},
|
||||
},
|
||||
{
|
||||
core.StatsURI,
|
||||
jwtHandler(
|
||||
authHandler(
|
||||
statsHandler)),
|
||||
[]string{"GET"},
|
||||
},
|
||||
{
|
||||
"/tags/new",
|
||||
jwtHandler(
|
||||
|
|
@ -161,4 +170,4 @@ var routes = []route{
|
|||
logoutHandler),
|
||||
[]string{"GET", "POST"},
|
||||
},
|
||||
}
|
||||
}...)
|
||||
|
|
|
|||
|
|
@ -1,27 +1,25 @@
|
|||
// Copyright (C) 2021 Marius Schellenberger
|
||||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"embed"
|
||||
"errors"
|
||||
"html/template"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"git.giftfish.de/ston1th/authdav"
|
||||
"git.giftfish.de/ston1th/docstore/pkg/core"
|
||||
"git.giftfish.de/ston1th/docstore/pkg/db"
|
||||
"git.giftfish.de/ston1th/docstore/pkg/fs"
|
||||
"git.giftfish.de/ston1th/docstore/pkg/log"
|
||||
"git.giftfish.de/ston1th/docstore/pkg/scan"
|
||||
"git.giftfish.de/ston1th/godrop/v2"
|
||||
"git.giftfish.de/ston1th/jwt/v3"
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/gorilla/mux"
|
||||
"golang.org/x/net/webdav"
|
||||
"html/template"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -29,7 +27,7 @@ const (
|
|||
)
|
||||
|
||||
type HTTPServer struct {
|
||||
Config *core.Config
|
||||
Config core.Config
|
||||
|
||||
listener net.Listener
|
||||
srv *http.Server
|
||||
|
|
@ -39,39 +37,30 @@ type HTTPServer struct {
|
|||
JWT *jwt.JWT
|
||||
Scanner *scan.Scanner
|
||||
FS *fs.Filesystem
|
||||
SQ *ScanQueue
|
||||
Log logr.Logger
|
||||
plain logr.Logger
|
||||
|
||||
cancel func()
|
||||
Log *log.ScanLog
|
||||
|
||||
templ map[string]*template.Template
|
||||
res map[string][]byte
|
||||
}
|
||||
|
||||
func NewHTTPServer(log logr.Logger, cfg *core.Config, l net.Listener, s *scan.Scanner) (srv *HTTPServer) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func NewHTTPServer(cfg core.Config, l net.Listener, s *scan.Scanner) (srv *HTTPServer) {
|
||||
srv = &HTTPServer{
|
||||
Config: cfg,
|
||||
listener: l,
|
||||
Scanner: s,
|
||||
Log: log.WithName("server"),
|
||||
plain: log,
|
||||
cancel: cancel,
|
||||
Log: log.NewScanLog(0),
|
||||
}
|
||||
srv.SQ = NewScanQueue(ctx, srv, 100)
|
||||
srv.loadTemplates()
|
||||
srv.srv = &http.Server{}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *HTTPServer) Start() (err error) {
|
||||
log := s.Log
|
||||
s.FS, err = fs.NewFilesystem(s.plain.WithName("fs"), s.Config.DataDir)
|
||||
s.FS, err = fs.NewFilesystem(s.Config.DataDir)
|
||||
if err != nil {
|
||||
return errors.New("fs: " + err.Error())
|
||||
}
|
||||
s.DB, err = db.New(s.plain.WithName("db"), s.Config)
|
||||
s.DB, err = db.New(s.Config)
|
||||
if err != nil {
|
||||
return errors.New("db: " + err.Error())
|
||||
}
|
||||
|
|
@ -79,33 +68,28 @@ func (s *HTTPServer) Start() (err error) {
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
opts := []jwt.JWTOption{jwt.WithNonce(), jwt.WithBlacklist(s.DB)}
|
||||
if s.Config.Secret != "" {
|
||||
opts = append(opts, jwt.WithSecret(bytes.NewBufferString(s.Config.Secret)))
|
||||
s.JWT, err = jwt.New(jwt.DefaultExpiry, s.DB, bytes.NewBufferString(s.Config.Secret))
|
||||
} else {
|
||||
s.JWT, err = jwt.New(jwt.DefaultExpiry, s.DB, nil)
|
||||
}
|
||||
s.JWT, err = jwt.New(opts...)
|
||||
if err != nil {
|
||||
return errors.New("server: " + err.Error())
|
||||
}
|
||||
s.handler = s.buildRoutes()
|
||||
if !s.DB.UserExists() {
|
||||
log.V(2).Info("starting register handler")
|
||||
log.Debug("server: starting register handler")
|
||||
s.srv.Handler = s.register()
|
||||
} else {
|
||||
s.srv.Handler = s.handler
|
||||
}
|
||||
go s.SQ.Scan()
|
||||
go func() {
|
||||
err = s.srv.Serve(s.listener)
|
||||
if err != nil && err != http.ErrServerClosed {
|
||||
log.Error(err, "http server error")
|
||||
}
|
||||
log.Println(s.srv.Serve(s.listener))
|
||||
}()
|
||||
return
|
||||
}
|
||||
|
||||
func (s *HTTPServer) Stop() error {
|
||||
s.cancel()
|
||||
s.DB.Close()
|
||||
s.JWT.Stop()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
|
|
@ -116,33 +100,21 @@ func (s *HTTPServer) Stop() error {
|
|||
func (s *HTTPServer) register() http.Handler {
|
||||
r := mux.NewRouter()
|
||||
r.NotFoundHandler = ®isterNotFoundHandler{s}
|
||||
r.Handle("/static/{file}", http.StripPrefix("/", http.FileServer(http.FS(static))))
|
||||
for _, v := range registerRoutes {
|
||||
r.HandleFunc(v.Path, s.contextWrapper(v.Handler)).Methods(v.Methods...)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
//go:embed static/*
|
||||
var static embed.FS
|
||||
|
||||
func (s *HTTPServer) buildRoutes() http.Handler {
|
||||
r := mux.NewRouter()
|
||||
r.NotFoundHandler = ¬FoundHandler{s}
|
||||
if s.Config.WebDav {
|
||||
dav := authdav.NewWriteOnlyOnceFileSystem(webdav.Dir(s.Config.DataDir))
|
||||
dav.Filters = []authdav.Filter{authdav.NewMacOSFilter()}
|
||||
h := authdav.NewWebdavBasicAuth(
|
||||
core.WebDavPrefix,
|
||||
dav,
|
||||
nil,
|
||||
webdavLogger(s.plain.WithName("webdav")),
|
||||
s.DB,
|
||||
"DocStore WebDav",
|
||||
)
|
||||
fs := authdav.NewWriteOnlyOnceFileSystem(webdav.Dir(s.Config.DataDir))
|
||||
fs.Filters = []authdav.Filter{authdav.NewMacOSFilter()}
|
||||
h := authdav.NewWebdavBasicAuth(core.WebDavPrefix, fs, nil, webdavLogger, s.DB, "DocStore WebDav")
|
||||
r.PathPrefix(core.WebDavPrefix).Handler(h)
|
||||
}
|
||||
r.Handle("/static/{file}", http.StripPrefix("/", http.FileServer(http.FS(static))))
|
||||
for _, v := range routes {
|
||||
r.HandleFunc(v.Path, s.contextWrapper(v.Handler)).Methods(v.Methods...)
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -1,33 +0,0 @@
|
|||
{{define "body"}}
|
||||
<div class="page-header">
|
||||
<div class="row">
|
||||
<h2>{{.BodyTitle}}</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<table class="table table-stripped table-hover">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><b>Goroutines</b></td>
|
||||
<td>{{.Data.Goroutines}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Memory usage</b></td>
|
||||
<td>{{.Data.Alloc}} MiB</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Memory allocated</b></td>
|
||||
<td>{{.Data.Sys}} MiB</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Documents</b></td>
|
||||
<td>{{.Data.Docs}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Go Version</b></td>
|
||||
<td>{{.Data.GoVersion}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{end}}
|
||||
|
|
@ -1,27 +1,14 @@
|
|||
// Copyright (C) 2022 Marius Schellenberger
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"git.giftfish.de/ston1th/docstore/pkg/log"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
)
|
||||
|
||||
func webdavLogger(log logr.Logger) func(r *http.Request, err error) {
|
||||
return func(r *http.Request, err error) {
|
||||
if err != nil {
|
||||
log.Error(err, "access",
|
||||
"client", r.RemoteAddr,
|
||||
"method", r.Method,
|
||||
"uri", r.URL.Path,
|
||||
)
|
||||
return
|
||||
}
|
||||
log.Info("access",
|
||||
"client", r.RemoteAddr,
|
||||
"method", r.Method,
|
||||
"uri", r.URL.Path,
|
||||
)
|
||||
func webdavLogger(r *http.Request, err error) {
|
||||
if err != nil {
|
||||
log.Printf("webdav: %s %s %s error: %s\n", r.RemoteAddr, r.Method, r.URL, err)
|
||||
return
|
||||
}
|
||||
log.Printf("webdav: %s %s %s\n", r.RemoteAddr, r.Method, r.URL)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,30 +1,28 @@
|
|||
// Copyright (C) 2021 Marius Schellenberger
|
||||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"crypto/subtle"
|
||||
"encoding/base64"
|
||||
"git.giftfish.de/ston1th/docstore/pkg/log"
|
||||
"git.giftfish.de/ston1th/jwt/v3"
|
||||
)
|
||||
|
||||
const (
|
||||
keySize = 16
|
||||
fullSize = keySize * 2
|
||||
)
|
||||
const keySize = jwt.KeySize / 2
|
||||
|
||||
func genKey(length int) (bytes []byte) {
|
||||
bytes = make([]byte, length)
|
||||
rand.Read(bytes)
|
||||
if _, err := rand.Reader.Read(bytes); err != nil {
|
||||
log.Println("genKey:", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func newXsrf(secret []byte) string {
|
||||
mac := hmac.New(sha256.New, secret)
|
||||
rnd := genKey(keySize)
|
||||
mac.Write(rnd)
|
||||
return base64.RawURLEncoding.EncodeToString(append(rnd, mac.Sum(nil)[:keySize]...))
|
||||
return base64.RawURLEncoding.EncodeToString(append(rnd, xor(rnd, secret)...))
|
||||
}
|
||||
|
||||
func checkXsrf(xsrf string, secret []byte) bool {
|
||||
|
|
@ -32,10 +30,20 @@ func checkXsrf(xsrf string, secret []byte) bool {
|
|||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if len(t) != fullSize {
|
||||
if len(t) != jwt.KeySize {
|
||||
return false
|
||||
}
|
||||
mac := hmac.New(sha256.New, secret)
|
||||
mac.Write(t[:keySize])
|
||||
return hmac.Equal(t[keySize:], mac.Sum(nil)[:keySize])
|
||||
return subtle.ConstantTimeCompare(secret, xor(t[keySize:], t[:keySize])) == 1
|
||||
}
|
||||
|
||||
func xor(a, b []byte) (c []byte) {
|
||||
n := len(a)
|
||||
if len(b) < n {
|
||||
return nil
|
||||
}
|
||||
c = make([]byte, n)
|
||||
for i := 0; i < n; i++ {
|
||||
c[i] = a[i] ^ b[i]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (C) 2021 Marius Schellenberger
|
||||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package server
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
// Copyright (C) 2021 Marius Schellenberger
|
||||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package store
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
"github.com/boltdb/bolt"
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultBoltBucket = "default"
|
||||
fileMode = 0o640
|
||||
fileMode = 0640
|
||||
)
|
||||
|
||||
type BoltStore struct {
|
||||
|
|
@ -26,7 +26,7 @@ func NewBoltStore(file string, m Marshaler) (Store, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := db.Update(func(tx *bolt.Tx) (err error) {
|
||||
if err = db.Update(func(tx *bolt.Tx) (err error) {
|
||||
_, err = tx.CreateBucketIfNotExists([]byte(defaultBoltBucket))
|
||||
return
|
||||
}); err != nil {
|
||||
|
|
@ -84,6 +84,19 @@ func (bs *BoltStore) Restore(r io.Reader) (err error) {
|
|||
})
|
||||
}
|
||||
|
||||
func (bs *BoltStore) Tx(writeable bool, f func(tx KVStore) error) error {
|
||||
tx, err := db.db.Begin(writeable)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = f(&BoltStoreTx{tx, bs})
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
func (bs *BoltStore) Get(key string, v interface{}) (err error) {
|
||||
err = bs.db.View(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket([]byte(defaultBoltBucket)).Get([]byte(key))
|
||||
|
|
@ -120,12 +133,23 @@ func (bs *BoltStore) ForEach(f func(string, []byte) error) error {
|
|||
}
|
||||
|
||||
func (bs *BoltStore) ForEachPrefix(prefix string, f func(string, []byte) error) error {
|
||||
return bs.ForEach(func(k string, v []byte) error {
|
||||
if trim, ok := hasTrimPrefix(k, prefix); ok {
|
||||
return f(trim, v)
|
||||
return bs.db.View(func(tx *bolt.Tx) error {
|
||||
c := tx.Bucket([]byte(defaultBoltBucket)).Cursor()
|
||||
p := []byte(prefix)
|
||||
for k, v := c.Seek(p); k != nil && bytes.HasPrefix(k, p); k, v = c.Next() {
|
||||
err := f(trimPrefix(k, p), v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
//return bs.ForEach(func(k string, v []byte) error {
|
||||
// if trim, ok := hasTrimPrefix(k, prefix); ok {
|
||||
// return f(trim, v)
|
||||
// }
|
||||
// return nil
|
||||
//})
|
||||
}
|
||||
|
||||
func (bs *BoltStore) Delete(key string) error {
|
||||
|
|
@ -137,3 +161,52 @@ func (bs *BoltStore) Delete(key string) error {
|
|||
func (bs *BoltStore) Close() error {
|
||||
return bs.db.Close()
|
||||
}
|
||||
|
||||
type BoltStoreTx struct {
|
||||
tx *bolt.Tx
|
||||
bs *BoltStore
|
||||
}
|
||||
|
||||
func (tx *BoltStoreTx) Get(key string, v interface{}) (err error) {
|
||||
b := tx.tx.Bucket([]byte(defaultBoltBucket)).Get([]byte(key))
|
||||
if b == nil {
|
||||
return ErrKeyNotFound
|
||||
}
|
||||
if v != nil {
|
||||
return tx.bs.Unmarshal(b, v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tx *BoltStoreTx) Set(key string, v interface{}) error {
|
||||
var b []byte
|
||||
if v != nil {
|
||||
b, err = tx.bs.Marshal(v)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return tx.tx.Bucket([]byte(defaultBoltBucket)).Put([]byte(key), b)
|
||||
}
|
||||
|
||||
func (tx *BoltStoreTx) ForEach(f func(string, []byte) error) error {
|
||||
return tx.tx.Bucket([]byte(defaultBoltBucket)).ForEach(func(k, v []byte) error {
|
||||
return f(string(k), v)
|
||||
})
|
||||
}
|
||||
|
||||
func (tx *BoltStoreTx) ForEachPrefix(prefix string, f func(string, []byte) error) error {
|
||||
c := tx.tx.Bucket([]byte(defaultBoltBucket)).Cursor()
|
||||
p := []byte(prefix)
|
||||
for k, v := c.Seek(p); k != nil && bytes.HasPrefix(k, p); k, v = c.Next() {
|
||||
err := f(trimPrefix(k, p), v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tx *BoltStoreTx) Delete(key string) error {
|
||||
return tx.tx.Bucket([]byte(defaultBoltBucket)).Delete([]byte(key))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
// Copyright (C) 2021 Marius Schellenberger
|
||||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package store
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
|
@ -19,7 +20,7 @@ func TestBoltStore(t *testing.T) {
|
|||
{"123"},
|
||||
{"hello"},
|
||||
}
|
||||
dir, err := os.MkdirTemp("", "")
|
||||
dir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (C) 2021 Marius Schellenberger
|
||||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package store
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (C) 2021 Marius Schellenberger
|
||||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package store
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (C) 2021 Marius Schellenberger
|
||||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package store
|
||||
|
||||
|
|
@ -9,3 +9,8 @@ func hasTrimPrefix(s, prefix string) (string, bool) {
|
|||
}
|
||||
return s, false
|
||||
}
|
||||
|
||||
// Parts taken from bytes.TrimPrefix
|
||||
func trimPrefix(b, prefix []byte) string {
|
||||
return string(b[len(prefix):])
|
||||
}
|
||||
|
|
|
|||
11
pkg/store/kvstore.go
Normal file
11
pkg/store/kvstore.go
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package store
|
||||
|
||||
type KVStore interface {
|
||||
Get(string, interface{}) error
|
||||
Set(string, interface{}) error
|
||||
ForEach(func(string, []byte) error) error
|
||||
ForEachPrefix(string, func(string, []byte) error) error
|
||||
Delete(string) error
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (C) 2021 Marius Schellenberger
|
||||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package store
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (C) 2021 Marius Schellenberger
|
||||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package store
|
||||
|
||||
|
|
@ -14,10 +14,7 @@ type Store interface {
|
|||
Marshaler
|
||||
Dumper
|
||||
Restorer
|
||||
Get(string, interface{}) error
|
||||
Set(string, interface{}) error
|
||||
ForEach(func(string, []byte) error) error
|
||||
ForEachPrefix(string, func(string, []byte) error) error
|
||||
Delete(string) error
|
||||
KVStore
|
||||
Tx(writeable bool, f func(tx KVStore) error) error
|
||||
Close() error
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,18 +5,12 @@ pkg_add poppler-utils tesseract tesseract-deu tesseract-eng
|
|||
|
||||
# rc script
|
||||
cat <<'EOF'> /etc/rc.d/docstore
|
||||
#!/bin/ksh
|
||||
#!/bin/sh
|
||||
|
||||
daemon="/usr/local/sbin/docstore"
|
||||
daemon_user="docstore"
|
||||
daemon_logger="daemon.info"
|
||||
|
||||
. /etc/rc.d/rc.subr
|
||||
|
||||
pexp=".*${daemon}.*"
|
||||
rc_bg=YES
|
||||
rc_reload=NO
|
||||
|
||||
rc_cmd $1
|
||||
EOF
|
||||
chmod 555 /etc/rc.d/docstore
|
||||
|
|
|
|||
4
sri.sh
4
sri.sh
|
|
@ -1,8 +1,8 @@
|
|||
#!/bin/bash
|
||||
# Copyright (C) 2021 Marius Schellenberger
|
||||
# Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
HASH="sha256"
|
||||
DIR="pkg/server/static"
|
||||
DIR="templates"
|
||||
FILES="*.css *.js"
|
||||
BINS="*.ico"
|
||||
echo "SRI (subresource integrity) hashes"
|
||||
|
|
|
|||
111
templates.sh
Executable file
111
templates.sh
Executable file
|
|
@ -0,0 +1,111 @@
|
|||
#!/bin/bash
|
||||
# Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
# this file generates Go source code for the gowiki
|
||||
# it contains html and css and uses the contents
|
||||
# of the templates directory
|
||||
|
||||
templates_go="pkg/server/templates.go"
|
||||
echo "Generating ${templates_go}"
|
||||
|
||||
if [ ! -d templates ]; then
|
||||
echo "error: templates directory not found" 1>&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
cat << EOF > ${templates_go}
|
||||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
// This file is auto-generated by templates.sh
|
||||
// using the contents of the templates directory
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"git.giftfish.de/ston1th/docstore/pkg/log"
|
||||
"html/template"
|
||||
)
|
||||
|
||||
EOF
|
||||
|
||||
echo "const (" >> ${templates_go}
|
||||
|
||||
for f in $(ls templates/*.html); do
|
||||
fname=${f/templates\//}
|
||||
fname=${fname/.html/}
|
||||
echo " ${fname} = \`$(cat ${f})\`" >> ${templates_go}
|
||||
done
|
||||
|
||||
for f in $(ls templates/*.css); do
|
||||
fname=${f/templates\//}
|
||||
fname=${fname/.css/CSS}
|
||||
echo " ${fname} = \`$(cat ${f})\`" >> ${templates_go}
|
||||
done
|
||||
|
||||
echo " favicon = \"$(base64 -w 0 templates/favicon.ico)\"" >> ${templates_go}
|
||||
|
||||
cat << EOF >> ${templates_go}
|
||||
)
|
||||
|
||||
func (s *HTTPServer) loadTemplates() {
|
||||
var fatal bool
|
||||
s.templ = make(map[string]*template.Template)
|
||||
s.res = make(map[string][]byte)
|
||||
parse := func(html ...string) (temp *template.Template) {
|
||||
var err error
|
||||
temp = template.New("")
|
||||
for _, s := range html {
|
||||
temp, err = temp.Parse(s)
|
||||
if err != nil {
|
||||
log.Println("parse:", err)
|
||||
fatal = true
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// resources
|
||||
s.res["bootstrap.css"] = []byte(bootstrapCSS)
|
||||
s.res["custom.css"] = []byte(customCSS)
|
||||
fav, err := base64.StdEncoding.DecodeString(favicon)
|
||||
if err != nil {
|
||||
log.Println("favicon:", err)
|
||||
fatal = true
|
||||
}
|
||||
s.res["favicon.ico"] = fav
|
||||
|
||||
// register
|
||||
s.templ["registerHandler"] = parse(index, menu, register)
|
||||
|
||||
s.templ["dirHandler"] = parse(index, menu, dir)
|
||||
s.templ["fileHandler"] = parse(index, menu, file)
|
||||
s.templ["uploadHandler"] = parse(index, menu, upload)
|
||||
s.templ["newDirHandler"] = parse(index, menu, newDir)
|
||||
s.templ["moveHandler"] = parse(index, menu, move)
|
||||
s.templ["enoentHandler"] = parse(index, menu, enoent)
|
||||
s.templ["deleteHandler"] = parse(index, menu, delete)
|
||||
s.templ["notFoundHandler"] = parse(index, menu, notFound)
|
||||
s.templ["forbiddenHandler"] = parse(index, menu, forbidden)
|
||||
s.templ["iseHandler"] = parse(index, menu, ise)
|
||||
s.templ["loginHandler"] = parse(index, menu, login)
|
||||
s.templ["loginTotpHandler"] = parse(index, menu, loginTotp)
|
||||
s.templ["searchHandler"] = parse(index, menu, search)
|
||||
s.templ["userEditHandler"] = parse(index, menu, userEdit)
|
||||
// totp
|
||||
s.templ["userTotpHandler"] = parse(index, menu, userTotp)
|
||||
// tags
|
||||
s.templ["tagsHandler"] = parse(index, menu, tagsList)
|
||||
s.templ["tagsNewHandler"] = parse(index, menu, tagsNew)
|
||||
s.templ["tagsEditHandler"] = parse(index, menu, tagsEdit)
|
||||
s.templ["tagsDelHandler"] = parse(index, menu, tagsDel)
|
||||
// logs
|
||||
s.templ["logsHandler"] = parse(index, menu, logs)
|
||||
|
||||
if fatal {
|
||||
log.Fatal("parse: ", errors.New("parsing templates failed"))
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
|
@ -106,8 +106,6 @@ code {
|
|||
}
|
||||
embed {
|
||||
background-color: #999999;
|
||||
width: 100%;
|
||||
height: 700px;
|
||||
}
|
||||
textarea,input,select {
|
||||
color: #e2e2e2 !important;
|
||||
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
|
|
@ -24,18 +24,17 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{if .Tags}}
|
||||
<div class="row">
|
||||
<div class="alert alert-secondary full-width">
|
||||
{{if .Tags}}
|
||||
Tags
|
||||
{{range $t := .Tags}}
|
||||
<a href="/search?query=tags:{{$t}}" class="btn badge btn-primary tag">{{$t}}</a>
|
||||
{{end}}
|
||||
{{end}}
|
||||
<span class="float-right">{{.Created}}</span>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="row">
|
||||
<embed src="{{.Data.Abs}}" toolbar="1" statusbar="1" navpanes="1"></embed>
|
||||
<embed src="{{.Data.Abs}}" width="100%" height="700px" toolbar="1" statusbar="1" navpanes="1"></embed>
|
||||
</div>
|
||||
{{end}}
|
||||
|
|
@ -2,9 +2,9 @@
|
|||
<html lang="en">
|
||||
<head>
|
||||
<title>DocStore | {{.Title}}</title>
|
||||
<link rel="shortcut icon" type="image/x-icon" href="/static/favicon.ico" integrity="sha256-bgotk/IPq4Yvt6s8AQGPTM5ZjPjQ6rrBRa2EyxpnFSc="></link>
|
||||
<link rel="stylesheet" type="text/css" href="/static/bootstrap.css" media="screen" integrity="sha256-3YeveuySvBgp2GItUS8eV1R/9T1zcPMyvw22SyVf8qo="></link>
|
||||
<link rel="stylesheet" type="text/css" href="/static/custom.css" media="screen" integrity="sha256-wLiyjr/D8ZaaIPLjUmZ3zdm45/HW/cy4kCVTViwkPeI="></link>
|
||||
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" integrity="sha256-bgotk/IPq4Yvt6s8AQGPTM5ZjPjQ6rrBRa2EyxpnFSc="></link>
|
||||
<link rel="stylesheet" type="text/css" href="/bootstrap.css" media="screen" integrity="sha256-3YeveuySvBgp2GItUS8eV1R/9T1zcPMyvw22SyVf8qo="></link>
|
||||
<link rel="stylesheet" type="text/css" href="/custom.css" media="screen" integrity="sha256-6jxgK5/GbgoDJ3zQ2bcYXgSUsOvQ1IF18Krslbwlk7w="></link>
|
||||
<meta name="theme-color" content="#375a7f">
|
||||
<meta name="description" content="{{.Title}}">
|
||||
</head>
|
||||
|
|
@ -15,7 +15,6 @@
|
|||
{{end}}
|
||||
<ul class="nav navbar-nav ml-auto">
|
||||
{{if .Login}}
|
||||
<li class="nav-item"><a class="nav-link" href="/stats">Stats</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="/user/edit">Profile</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="/logout">Logout</a></li>
|
||||
{{else}}
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
<div class="card-body">
|
||||
<div class="alert alert-dismissible alert-primary">
|
||||
<strong>Note</strong> Editing a tag does not update tags already assigned to a document.
|
||||
You need to retag all documents to avoid false-positive searches.
|
||||
You need to reindex all documents to avoid false-positive searches.
|
||||
</div>
|
||||
<form class="form-horizontal" action="/tags/edit/{{.Data.Name}}" method="post">
|
||||
<input type="hidden" name="token" value="{{.Token}}">
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
<form class="form-horizontal" action="/user/edit" method="post">
|
||||
<input type="hidden" name="token" value="{{.Token}}">
|
||||
<div class="form-group">
|
||||
<label class="col-form-label" for="password">New Password</label>
|
||||
<label class="col-form-label" for="password">Password</label>
|
||||
<input class="form-control input-sm" type="password" id="password" name="password" autofocus>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
24
vendor/git.giftfish.de/ston1th/authdav/LICENSE
vendored
24
vendor/git.giftfish.de/ston1th/authdav/LICENSE
vendored
|
|
@ -1,24 +0,0 @@
|
|||
Copyright (C) 2019 Marius Schellenberger
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* The names of the authors and/or contributors may not be used to
|
||||
endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL ston1th BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
59
vendor/git.giftfish.de/ston1th/authdav/README.md
vendored
59
vendor/git.giftfish.de/ston1th/authdav/README.md
vendored
|
|
@ -1,59 +0,0 @@
|
|||
# authdav - a x/net/webdav wrapper
|
||||
|
||||
Wrappers:
|
||||
* HTTP Basic Auth wrapper for `webdav.Handler`
|
||||
* write only once FileSystem wrapper for `webdav.FileSystem`
|
||||
|
||||
## Usage
|
||||
|
||||
### HTTP Basic Auth
|
||||
|
||||
```
|
||||
package main
|
||||
|
||||
import (
|
||||
"git.giftfish.de/ston1th/authdav"
|
||||
"golang.org/x/net/webdav"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type exampleAuthenticator struct {}
|
||||
|
||||
func (exampleAuthenticator) BasicAuth(user, password string) bool {
|
||||
if user == "user" && password == "password" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func main() {
|
||||
auth := exampleAuthenticator{}
|
||||
handler := authdav.NewWebdavBasicAuth("", webdav.Dir("."), nil, nil, auth, "webdav with basic auth")
|
||||
log.Fatal(http.ListenAndServe(":8080", handler))
|
||||
}
|
||||
```
|
||||
|
||||
### Write Only
|
||||
|
||||
```
|
||||
package main
|
||||
|
||||
import (
|
||||
"git.giftfish.de/ston1th/authdav"
|
||||
"golang.org/x/net/webdav"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fs := authdav.NewWriteOnlyOnceFileSystem(webdav.Dir("."))
|
||||
handler := &webdav.Handler{
|
||||
Prefix: "",
|
||||
FileSystem: fs,
|
||||
LockSystem: webdav.NewMemLS(),
|
||||
Logger: nil,
|
||||
}
|
||||
log.Fatal(http.ListenAndServe(":8080", handler))
|
||||
}
|
||||
```
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package authdav
|
||||
|
||||
import (
|
||||
"golang.org/x/net/webdav"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// DefaultRealm is the HTTP Basic Auth default realm
|
||||
const DefaultRealm = "WebDav"
|
||||
|
||||
// Authenticator is the interface for Webdav HTTP Basic Auth
|
||||
type Authenticator interface {
|
||||
BasicAuth(username string, password string) bool
|
||||
}
|
||||
|
||||
// WebdavBasicAuth is a WebDav wrapper for HTTP Basic Auth and implements the http.Handler interface
|
||||
type WebdavBasicAuth struct {
|
||||
dav *webdav.Handler
|
||||
auth Authenticator
|
||||
realm string
|
||||
}
|
||||
|
||||
// NewWebdavBasicAuth returns a new WebdavBasicAuth wrapper
|
||||
// prefix can be empty
|
||||
// ls and log can be nil
|
||||
// an empty authenticator disables HTTP Basic Auth
|
||||
// realm can be empty and defaults to authdav.DefaultRealm
|
||||
func NewWebdavBasicAuth(prefix string, fs webdav.FileSystem, ls webdav.LockSystem, log func(*http.Request, error), auth Authenticator, realm string) *WebdavBasicAuth {
|
||||
if ls == nil {
|
||||
ls = webdav.NewMemLS()
|
||||
}
|
||||
if realm == "" {
|
||||
realm = DefaultRealm
|
||||
}
|
||||
dav := &webdav.Handler{
|
||||
Prefix: prefix,
|
||||
FileSystem: fs,
|
||||
LockSystem: ls,
|
||||
Logger: log,
|
||||
}
|
||||
return &WebdavBasicAuth{dav, auth, `Basic realm="` + realm + `"`}
|
||||
}
|
||||
|
||||
func (h *WebdavBasicAuth) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if h.auth != nil {
|
||||
u, p, ok := r.BasicAuth()
|
||||
if !ok || !h.auth.BasicAuth(u, p) {
|
||||
w.Header().Set("WWW-Authenticate", h.realm)
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
w.Write([]byte("Unauthorised\n"))
|
||||
return
|
||||
}
|
||||
}
|
||||
h.dav.ServeHTTP(w, r)
|
||||
}
|
||||
46
vendor/git.giftfish.de/ston1th/authdav/file.go
vendored
46
vendor/git.giftfish.de/ston1th/authdav/file.go
vendored
|
|
@ -1,46 +0,0 @@
|
|||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package authdav
|
||||
|
||||
import (
|
||||
"golang.org/x/net/webdav"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// File is a wrapper for webdav.File providing write only functionality
|
||||
type File struct {
|
||||
f webdav.File
|
||||
}
|
||||
|
||||
func (f *File) Close() error {
|
||||
return f.f.Close()
|
||||
}
|
||||
|
||||
func (f *File) Read(p []byte) (int, error) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
func (f *File) Seek(offset int64, whence int) (int64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (f *File) Readdir(count int) (fi []os.FileInfo, err error) {
|
||||
ffi, err := f.f.Readdir(count)
|
||||
for _, f := range ffi {
|
||||
fi = append(fi, &FileInfo{f})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (f *File) Stat() (os.FileInfo, error) {
|
||||
fi, err := f.f.Stat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &FileInfo{fi}, nil
|
||||
}
|
||||
|
||||
func (f *File) Write(p []byte) (int, error) {
|
||||
return f.f.Write(p)
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package authdav
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
// FileInfo is a wrapper for os.FileInfo which always raturns a zero length for regular files
|
||||
type FileInfo struct {
|
||||
fi os.FileInfo
|
||||
}
|
||||
|
||||
func (fi *FileInfo) Name() string {
|
||||
return fi.fi.Name()
|
||||
}
|
||||
|
||||
func (fi *FileInfo) RealSize() int64 {
|
||||
return fi.fi.Size()
|
||||
}
|
||||
|
||||
func (fi *FileInfo) Size() int64 {
|
||||
if fi.Mode().IsRegular() {
|
||||
return 0
|
||||
}
|
||||
return fi.fi.Size()
|
||||
}
|
||||
|
||||
func (fi *FileInfo) Mode() os.FileMode {
|
||||
return fi.fi.Mode()
|
||||
}
|
||||
|
||||
func (fi *FileInfo) ModTime() time.Time {
|
||||
return fi.fi.ModTime()
|
||||
}
|
||||
|
||||
func (fi *FileInfo) IsDir() bool {
|
||||
return fi.fi.IsDir()
|
||||
}
|
||||
|
||||
func (fi *FileInfo) Sys() interface{} {
|
||||
return fi.fi.Sys()
|
||||
}
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package authdav
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"golang.org/x/net/webdav"
|
||||
"os"
|
||||
)
|
||||
|
||||
// ErrWonly indicates forbidden operation
|
||||
var ErrWonly = errors.New("authdav: only file write once operations permitted")
|
||||
|
||||
const writeFlag = os.O_RDWR | os.O_CREATE | os.O_TRUNC
|
||||
|
||||
// WriteOnlyOnceFileSystem is a file write only wrapper for a webdav.FileSystem
|
||||
type WriteOnlyOnceFileSystem struct {
|
||||
Filters []Filter
|
||||
fs webdav.FileSystem
|
||||
}
|
||||
|
||||
// NewWriteOnlyOnceFileSystem returns a new WriteOnlyOnceFileSystem wrapping a webdav.FileSystem
|
||||
func NewWriteOnlyOnceFileSystem(fs webdav.FileSystem) *WriteOnlyOnceFileSystem {
|
||||
return &WriteOnlyOnceFileSystem{fs: fs}
|
||||
}
|
||||
|
||||
// Mkdir is not permitted
|
||||
func (fs *WriteOnlyOnceFileSystem) Mkdir(ctx context.Context, name string, perm os.FileMode) error {
|
||||
return ErrWonly
|
||||
}
|
||||
|
||||
// OpenFile allows write only once operations
|
||||
func (fs *WriteOnlyOnceFileSystem) OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (webdav.File, error) {
|
||||
fi, err := fs.Stat(ctx, name)
|
||||
if err == nil && fi.Mode().IsRegular() {
|
||||
f, ok := fi.(*FileInfo)
|
||||
if ok && flag == writeFlag && f.RealSize() > 0 {
|
||||
return nil, ErrWonly
|
||||
}
|
||||
}
|
||||
for _, filter := range fs.Filters {
|
||||
err = filter.Filter(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
f, err := fs.fs.OpenFile(ctx, name, flag, perm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &File{f}, nil
|
||||
}
|
||||
|
||||
// RemoveAll only permits deleting empty files
|
||||
func (fs *WriteOnlyOnceFileSystem) RemoveAll(ctx context.Context, name string) error {
|
||||
fi, err := fs.fs.Stat(ctx, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if fi.Mode().IsRegular() && fi.Size() == 0 {
|
||||
return fs.fs.RemoveAll(ctx, name)
|
||||
}
|
||||
return ErrWonly
|
||||
}
|
||||
|
||||
// Rename is not permitted
|
||||
func (fs *WriteOnlyOnceFileSystem) Rename(ctx context.Context, oldName, newName string) error {
|
||||
return ErrWonly
|
||||
}
|
||||
|
||||
// Stat returns os.FileInfo wrapped in FileInfo
|
||||
func (fs *WriteOnlyOnceFileSystem) Stat(ctx context.Context, name string) (os.FileInfo, error) {
|
||||
fi, err := fs.fs.Stat(ctx, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &FileInfo{fi}, nil
|
||||
}
|
||||
50
vendor/git.giftfish.de/ston1th/authdav/filter.go
vendored
50
vendor/git.giftfish.de/ston1th/authdav/filter.go
vendored
|
|
@ -1,50 +0,0 @@
|
|||
// Copyright (C) 2019 Marius Schellenberger
|
||||
|
||||
package authdav
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Matcher is the matcher function type
|
||||
type Matcher func(path string) bool
|
||||
|
||||
// Filter defines an interface to filter paths or files
|
||||
type Filter interface {
|
||||
Filter(path string) error
|
||||
}
|
||||
|
||||
// ErrMacOSFilter error returned by the MacOSFilter
|
||||
var ErrMacOSFilter = errors.New("authdav: MacOS filter: access denied")
|
||||
|
||||
// MacOSFilter implements a filter for MacOS specific files
|
||||
type MacOSFilter struct {
|
||||
match []Matcher
|
||||
}
|
||||
|
||||
// NewMacOSFilter returns a new MacOSFilter
|
||||
func NewMacOSFilter() *MacOSFilter {
|
||||
return &MacOSFilter{
|
||||
match: []Matcher{
|
||||
func(p string) bool {
|
||||
return p == ".DS_Store"
|
||||
},
|
||||
func(p string) bool {
|
||||
return strings.HasPrefix(p, "._")
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Filter implements the Filter interface
|
||||
func (f *MacOSFilter) Filter(path string) error {
|
||||
_, file := filepath.Split(path)
|
||||
for _, m := range f.match {
|
||||
if m(file) {
|
||||
return ErrMacOSFilter
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
Copyright (C) 2022 Marius Schellenberger
|
||||
Copyright (C) 2017 Marius Schellenberger
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
|
|
|||
3
vendor/git.giftfish.de/ston1th/godrop/v2/go.mod
vendored
Normal file
3
vendor/git.giftfish.de/ston1th/godrop/v2/go.mod
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
module git.giftfish.de/ston1th/godrop/v2
|
||||
|
||||
require golang.org/x/sys v0.0.0-20181026144532-2772b66316d2 // indirect
|
||||
2
vendor/git.giftfish.de/ston1th/godrop/v2/go.sum
vendored
Normal file
2
vendor/git.giftfish.de/ston1th/godrop/v2/go.sum
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
golang.org/x/sys v0.0.0-20181026144532-2772b66316d2 h1:W7CqTdBJ1CmxLKe7LptKDnBYV6PHrVLiGnoyBjaG/JQ=
|
||||
golang.org/x/sys v0.0.0-20181026144532-2772b66316d2/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
// Copyright (C) 2022 Marius Schellenberger
|
||||
// Copyright (C) 2018 Marius Schellenberger
|
||||
|
||||
//go:build go1.11
|
||||
// +build go1.11
|
||||
|
||||
// Package godrop provides a simple library to drop privileges on Linux and OpenBSD.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
// Copyright (C) 2022 Marius Schellenberger
|
||||
// Copyright (C) 2018 Marius Schellenberger
|
||||
|
||||
//go:build go1.11
|
||||
// +build go1.11
|
||||
|
||||
package godrop
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
// Copyright (C) 2022 Marius Schellenberger
|
||||
// Copyright (C) 2018 Marius Schellenberger
|
||||
|
||||
//go:build !openbsd
|
||||
// +build !openbsd
|
||||
|
||||
package godrop
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
// Copyright (C) 2022 Marius Schellenberger
|
||||
// Copyright (C) 2018 Marius Schellenberger
|
||||
|
||||
//go:build openbsd
|
||||
// +build openbsd
|
||||
|
||||
package godrop
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
// Copyright (C) 2022 Marius Schellenberger
|
||||
// Copyright (C) 2018 Marius Schellenberger
|
||||
|
||||
//go:build !openbsd
|
||||
// +build !openbsd
|
||||
|
||||
package godrop
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
// Copyright (C) 2022 Marius Schellenberger
|
||||
// Copyright (C) 2018 Marius Schellenberger
|
||||
|
||||
//go:build openbsd
|
||||
// +build openbsd
|
||||
|
||||
package godrop
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
Copyright (C) 2025 Marius Schellenberger
|
||||
Copyright (C) 2018 Marius Schellenberger
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (C) 2025 Marius Schellenberger
|
||||
// Copyright (C) 2018 Marius Schellenberger
|
||||
|
||||
package jwt
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue