mirror of
https://github.com/ddworken/hishtory.git
synced 2024-11-29 03:35:09 +01:00
Website landing page, install instructions, update command, status command, set up postgres, and fixing broken tests
This commit is contained in:
parent
f72c88bbd9
commit
252e9ab122
2
.dockerignore
Normal file
2
.dockerignore
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
.git
|
||||||
|
node_modules/
|
14
Makefile
14
Makefile
@ -1,12 +1,14 @@
|
|||||||
test:
|
test:
|
||||||
go test -p ./...
|
HISHTORY_TEST=1 go test -p 1 ./...
|
||||||
|
|
||||||
build:
|
build:
|
||||||
|
go build -o web/landing/www/hishtory-offline clients/local/client.go
|
||||||
|
go build -o web/landing/www/hishtory-online clients/remote/client.go
|
||||||
docker build -t gcr.io/dworken-k8s/hishtory-static -f web/caddy/Dockerfile .
|
docker build -t gcr.io/dworken-k8s/hishtory-static -f web/caddy/Dockerfile .
|
||||||
# docker build -t gcr.io/dworken-k8s/hishtory-api -f deploy/api/Dockerfile .
|
docker build -t gcr.io/dworken-k8s/hishtory-api -f server/Dockerfile .
|
||||||
|
|
||||||
deploy: build
|
deploy: build
|
||||||
docker push gcr.io/dworken-k8s/hishtory-static
|
# docker push gcr.io/dworken-k8s/hishtory-static
|
||||||
# docker push gcr.io/dworken-k8s/hishtory-api
|
docker push gcr.io/dworken-k8s/hishtory-api
|
||||||
kubectl patch deployment hishtory-static -p "{\"spec\":{\"template\":{\"metadata\":{\"labels\":{\"ts\":\"`date|sed -e 's/ /_/g'|sed -e 's/:/-/g'`\"}}}}}}"
|
# kubectl patch deployment hishtory-static -p "{\"spec\":{\"template\":{\"metadata\":{\"labels\":{\"ts\":\"`date|sed -e 's/ /_/g'|sed -e 's/:/-/g'`\"}}}}}}"
|
||||||
# kubectl patch deployment cascara-keys -p "{\"spec\":{\"template\":{\"metadata\":{\"labels\":{\"ts\":\"`date|sed -e 's/ /_/g'|sed -e 's/:/-/g'`\"}}}}}}"
|
kubectl patch deployment hishtory-api -p "{\"spec\":{\"template\":{\"metadata\":{\"labels\":{\"ts\":\"`date|sed -e 's/ /_/g'|sed -e 's/:/-/g'`\"}}}}}}"
|
||||||
|
@ -28,6 +28,14 @@ func main() {
|
|||||||
shared.CheckFatalError(shared.Enable())
|
shared.CheckFatalError(shared.Enable())
|
||||||
case "disable":
|
case "disable":
|
||||||
shared.CheckFatalError(shared.Disable())
|
shared.CheckFatalError(shared.Disable())
|
||||||
|
case "status":
|
||||||
|
config, err := shared.GetConfig()
|
||||||
|
shared.CheckFatalError(err)
|
||||||
|
fmt.Print("Hishtory: Offline Mode\nEnabled: ")
|
||||||
|
fmt.Print(config.IsEnabled)
|
||||||
|
fmt.Print("\n")
|
||||||
|
case "update":
|
||||||
|
shared.CheckFatalError(shared.Update("https://hishtory.dev/hishtory-offline"))
|
||||||
default:
|
default:
|
||||||
shared.CheckFatalError(fmt.Errorf("unknown command: %s", os.Args[1]))
|
shared.CheckFatalError(fmt.Errorf("unknown command: %s", os.Args[1]))
|
||||||
}
|
}
|
||||||
@ -43,7 +51,7 @@ func getServerHostname() string {
|
|||||||
func query(query string) {
|
func query(query string) {
|
||||||
userSecret, err := shared.GetUserSecret()
|
userSecret, err := shared.GetUserSecret()
|
||||||
shared.CheckFatalError(err)
|
shared.CheckFatalError(err)
|
||||||
db, err := shared.OpenDB()
|
db, err := shared.OpenLocalSqliteDb()
|
||||||
shared.CheckFatalError(err)
|
shared.CheckFatalError(err)
|
||||||
data, err := shared.Search(db, userSecret, query, 25)
|
data, err := shared.Search(db, userSecret, query, 25)
|
||||||
shared.CheckFatalError(err)
|
shared.CheckFatalError(err)
|
||||||
@ -58,14 +66,16 @@ func saveHistoryEntry() {
|
|||||||
}
|
}
|
||||||
entry, err := shared.BuildHistoryEntry(os.Args)
|
entry, err := shared.BuildHistoryEntry(os.Args)
|
||||||
shared.CheckFatalError(err)
|
shared.CheckFatalError(err)
|
||||||
err = shared.Persist(*entry)
|
db, err := shared.OpenLocalSqliteDb()
|
||||||
|
shared.CheckFatalError(err)
|
||||||
|
err = shared.Persist(db, *entry)
|
||||||
shared.CheckFatalError(err)
|
shared.CheckFatalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func export() {
|
func export() {
|
||||||
userSecret, err := shared.GetUserSecret()
|
userSecret, err := shared.GetUserSecret()
|
||||||
shared.CheckFatalError(err)
|
shared.CheckFatalError(err)
|
||||||
db, err := shared.OpenDB()
|
db, err := shared.OpenLocalSqliteDb()
|
||||||
shared.CheckFatalError(err)
|
shared.CheckFatalError(err)
|
||||||
data, err := shared.Search(db, userSecret, "", 0)
|
data, err := shared.Search(db, userSecret, "", 0)
|
||||||
shared.CheckFatalError(err)
|
shared.CheckFatalError(err)
|
||||||
|
@ -25,7 +25,9 @@ func RunInteractiveBashCommands(t *testing.T, script string) string {
|
|||||||
|
|
||||||
func TestIntegration(t *testing.T) {
|
func TestIntegration(t *testing.T) {
|
||||||
// Set up
|
// Set up
|
||||||
defer shared.BackupAndRestore(t)
|
defer shared.BackupAndRestore(t)()
|
||||||
|
|
||||||
|
// TODO(ddworken): Test status
|
||||||
|
|
||||||
// Test install
|
// Test install
|
||||||
out := RunInteractiveBashCommands(t, `
|
out := RunInteractiveBashCommands(t, `
|
||||||
@ -84,4 +86,5 @@ func TestIntegration(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(ddworken): Test export
|
// TODO(ddworken): Test export
|
@ -32,6 +32,15 @@ func main() {
|
|||||||
shared.CheckFatalError(shared.Enable())
|
shared.CheckFatalError(shared.Enable())
|
||||||
case "disable":
|
case "disable":
|
||||||
shared.CheckFatalError(shared.Disable())
|
shared.CheckFatalError(shared.Disable())
|
||||||
|
case "status":
|
||||||
|
config, err := shared.GetConfig()
|
||||||
|
shared.CheckFatalError(err)
|
||||||
|
fmt.Print("Hishtory: Online Mode\nEnabled: ")
|
||||||
|
fmt.Print(config.IsEnabled)
|
||||||
|
fmt.Print("\nSecret Key: " + config.UserSecret + "\n")
|
||||||
|
fmt.Print("Remote Server: " + getServerHostname() + "\n")
|
||||||
|
case "update":
|
||||||
|
shared.CheckFatalError(shared.Update("https://hishtory.dev/hishtory-online"))
|
||||||
default:
|
default:
|
||||||
shared.CheckFatalError(fmt.Errorf("unknown command: %s", os.Args[1]))
|
shared.CheckFatalError(fmt.Errorf("unknown command: %s", os.Args[1]))
|
||||||
}
|
}
|
||||||
@ -41,7 +50,7 @@ func getServerHostname() string {
|
|||||||
if server := os.Getenv("HISHTORY_SERVER"); server != "" {
|
if server := os.Getenv("HISHTORY_SERVER"); server != "" {
|
||||||
return server
|
return server
|
||||||
}
|
}
|
||||||
return "http://localhost:8080"
|
return "https://api.hishtory.dev"
|
||||||
}
|
}
|
||||||
|
|
||||||
func query(query string) {
|
func query(query string) {
|
||||||
@ -111,5 +120,5 @@ func send(entry shared.HistoryEntry) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func export() {
|
func export() {
|
||||||
|
// TODO(ddworken)
|
||||||
}
|
}
|
@ -12,7 +12,7 @@ import (
|
|||||||
|
|
||||||
func RunInteractiveBashCommands(t *testing.T, script string) string {
|
func RunInteractiveBashCommands(t *testing.T, script string) string {
|
||||||
cmd := exec.Command("bash", "-i")
|
cmd := exec.Command("bash", "-i")
|
||||||
cmd.Stdin = strings.NewReader(script)
|
cmd.Stdin = strings.NewReader("export HISHTORY_SERVER=http://localhost:8080; /tmp/server &; sleep 2; " + script + "\nsleep 2; killall server || true;")
|
||||||
var out bytes.Buffer
|
var out bytes.Buffer
|
||||||
cmd.Stdout = &out
|
cmd.Stdout = &out
|
||||||
var err bytes.Buffer
|
var err bytes.Buffer
|
||||||
@ -23,9 +23,11 @@ func RunInteractiveBashCommands(t *testing.T, script string) string {
|
|||||||
|
|
||||||
func TestIntegration(t *testing.T) {
|
func TestIntegration(t *testing.T) {
|
||||||
// Set up
|
// Set up
|
||||||
defer shared.BackupAndRestore(t)
|
defer shared.BackupAndRestore(t)()
|
||||||
|
|
||||||
// Test init
|
// TODO(ddworken): Test status
|
||||||
|
|
||||||
|
// Test install
|
||||||
out := RunInteractiveBashCommands(t, `
|
out := RunInteractiveBashCommands(t, `
|
||||||
gvm use go1.17
|
gvm use go1.17
|
||||||
cd ../../
|
cd ../../
|
||||||
@ -39,8 +41,7 @@ func TestIntegration(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Test recording commands
|
// Test recording commands
|
||||||
out = RunInteractiveBashCommands(t, `/tmp/server &
|
out = RunInteractiveBashCommands(t, `
|
||||||
sleep 2 # to give the server time to start
|
|
||||||
ls /a
|
ls /a
|
||||||
ls /bar
|
ls /bar
|
||||||
ls /foo
|
ls /foo
|
||||||
@ -51,12 +52,12 @@ func TestIntegration(t *testing.T) {
|
|||||||
hishtory enable
|
hishtory enable
|
||||||
echo thisisrecorded
|
echo thisisrecorded
|
||||||
`)
|
`)
|
||||||
if out != "Listening on localhost:8080\nfoo\nbar\nthisisnotrecorded\nthisisrecorded\n" {
|
if out != "foo\nbar\nthisisnotrecorded\nthisisrecorded\n" {
|
||||||
t.Fatalf("unexpected output from running commands: %#v", out)
|
t.Fatalf("unexpected output from running commands: %#v", out)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test querying for all commands
|
// Test querying for all commands
|
||||||
out = RunInteractiveBashCommands(t, `/tmp/server & hishtory query`)
|
out = RunInteractiveBashCommands(t, `hishtory query`)
|
||||||
expected := []string{"echo thisisrecorded", "hishtory enable", "echo bar", "echo foo", "ls /foo", "ls /bar", "ls /a", "ms"}
|
expected := []string{"echo thisisrecorded", "hishtory enable", "echo bar", "echo foo", "ls /foo", "ls /bar", "ls /a", "ms"}
|
||||||
for _, item := range expected {
|
for _, item := range expected {
|
||||||
if !strings.Contains(out, item) {
|
if !strings.Contains(out, item) {
|
||||||
|
16
go.mod
16
go.mod
@ -5,16 +5,28 @@ go 1.17
|
|||||||
require (
|
require (
|
||||||
github.com/fatih/color v1.13.0
|
github.com/fatih/color v1.13.0
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
|
github.com/lib/pq v1.10.2
|
||||||
github.com/rodaine/table v1.0.1
|
github.com/rodaine/table v1.0.1
|
||||||
|
gorm.io/driver/postgres v1.3.1
|
||||||
gorm.io/driver/sqlite v1.2.6
|
gorm.io/driver/sqlite v1.2.6
|
||||||
gorm.io/gorm v1.22.4
|
gorm.io/gorm v1.23.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
|
||||||
|
github.com/jackc/pgconn v1.10.1 // indirect
|
||||||
|
github.com/jackc/pgio v1.0.0 // indirect
|
||||||
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
|
github.com/jackc/pgproto3/v2 v2.2.0 // indirect
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
|
||||||
|
github.com/jackc/pgtype v1.9.1 // indirect
|
||||||
|
github.com/jackc/pgx/v4 v4.14.1 // indirect
|
||||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
github.com/jinzhu/now v1.1.3 // indirect
|
github.com/jinzhu/now v1.1.4 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.9 // indirect
|
github.com/mattn/go-colorable v0.1.9 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||||
github.com/mattn/go-sqlite3 v1.14.9 // indirect
|
github.com/mattn/go-sqlite3 v1.14.9 // indirect
|
||||||
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
|
||||||
|
golang.org/x/text v0.3.7 // indirect
|
||||||
)
|
)
|
||||||
|
176
go.sum
176
go.sum
@ -1,17 +1,96 @@
|
|||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
|
||||||
|
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||||
|
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
|
||||||
|
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||||
|
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
|
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
|
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||||
|
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||||
|
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||||
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
|
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
|
||||||
|
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||||
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
|
||||||
|
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
|
||||||
|
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||||
|
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
|
||||||
|
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||||
|
github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
|
||||||
|
github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
|
||||||
|
github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
|
||||||
|
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
|
||||||
|
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
|
||||||
|
github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
|
||||||
|
github.com/jackc/pgconn v1.10.1 h1:DzdIHIjG1AxGwoEEqS+mGsURyjt4enSmqzACXvVzOT8=
|
||||||
|
github.com/jackc/pgconn v1.10.1/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
|
||||||
|
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
|
||||||
|
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
|
||||||
|
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
|
||||||
|
github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=
|
||||||
|
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc=
|
||||||
|
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
|
github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A=
|
||||||
|
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.2.0 h1:r7JypeP2D3onoQTCxWdTpCtJ4D+qpKr0TxvoyMhZ5ns=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.2.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||||
|
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
|
||||||
|
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
|
||||||
|
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
|
||||||
|
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
|
||||||
|
github.com/jackc/pgtype v1.9.1 h1:MJc2s0MFS8C3ok1wQTdQxWuXQcB6+HwAm5x1CzW7mf0=
|
||||||
|
github.com/jackc/pgtype v1.9.1/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
|
||||||
|
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
|
||||||
|
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
|
||||||
|
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
|
||||||
|
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
|
||||||
|
github.com/jackc/pgx/v4 v4.14.1 h1:71oo1KAGI6mXhLiTMn6iDFcp3e7+zon/capWjl2OEFU=
|
||||||
|
github.com/jackc/pgx/v4 v4.14.1/go.mod h1:RgDuE4Z34o7XE92RpLsvFiOEfrAUT0Xt2KxvX73W06M=
|
||||||
|
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
|
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
|
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
|
github.com/jackc/puddle v1.2.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
github.com/jinzhu/now v1.1.3 h1:PlHq1bSCSZL9K0wUhbm2pGLoTWs2GwVhsP6emvGV/ZI=
|
github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas=
|
||||||
github.com/jinzhu/now v1.1.3/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
|
||||||
|
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
|
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
||||||
|
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
|
github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
|
||||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
|
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
|
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||||
@ -19,23 +98,112 @@ github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/Qd
|
|||||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
github.com/mattn/go-sqlite3 v1.14.9 h1:10HX2Td0ocZpYEjhilsuo6WWtUqttj2Kb0KtD86/KYA=
|
github.com/mattn/go-sqlite3 v1.14.9 h1:10HX2Td0ocZpYEjhilsuo6WWtUqttj2Kb0KtD86/KYA=
|
||||||
github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||||
|
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||||
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/rodaine/table v1.0.1 h1:U/VwCnUxlVYxw8+NJiLIuCxA/xa6jL38MY3FYysVWWQ=
|
github.com/rodaine/table v1.0.1 h1:U/VwCnUxlVYxw8+NJiLIuCxA/xa6jL38MY3FYysVWWQ=
|
||||||
github.com/rodaine/table v1.0.1/go.mod h1:UVEtfBsflpeEcD56nF4F5AocNFta0ZuolpSVdPtlmP4=
|
github.com/rodaine/table v1.0.1/go.mod h1:UVEtfBsflpeEcD56nF4F5AocNFta0ZuolpSVdPtlmP4=
|
||||||
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
|
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||||
|
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
|
||||||
|
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
|
||||||
|
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||||
|
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||||
|
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
|
||||||
|
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||||
|
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||||
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
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.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||||
|
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
|
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
|
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||||
|
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||||
|
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||||
|
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||||
|
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||||
|
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||||
|
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
|
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
|
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||||
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
|
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
|
||||||
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||||
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/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-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
|
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gorm.io/driver/postgres v1.3.1 h1:Pyv+gg1Gq1IgsLYytj/S2k7ebII3CzEdpqQkPOdH24g=
|
||||||
|
gorm.io/driver/postgres v1.3.1/go.mod h1:WwvWOuR9unCLpGWCL6Y3JOeBWvbKi6JLhayiVclSZZU=
|
||||||
gorm.io/driver/sqlite v1.2.6 h1:SStaH/b+280M7C8vXeZLz/zo9cLQmIGwwj3cSj7p6l4=
|
gorm.io/driver/sqlite v1.2.6 h1:SStaH/b+280M7C8vXeZLz/zo9cLQmIGwwj3cSj7p6l4=
|
||||||
gorm.io/driver/sqlite v1.2.6/go.mod h1:gyoX0vHiiwi0g49tv+x2E7l8ksauLK0U/gShcdUsjWY=
|
gorm.io/driver/sqlite v1.2.6/go.mod h1:gyoX0vHiiwi0g49tv+x2E7l8ksauLK0U/gShcdUsjWY=
|
||||||
gorm.io/gorm v1.22.3/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
|
gorm.io/gorm v1.22.3/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
|
||||||
gorm.io/gorm v1.22.4 h1:8aPcyEJhY0MAt8aY6Dc524Pn+pO29K+ydu+e/cXSpQM=
|
gorm.io/gorm v1.23.1 h1:aj5IlhDzEPsoIyOPtTRVI+SyaN1u6k613sbt4pwbxG0=
|
||||||
gorm.io/gorm v1.22.4/go.mod h1:1aeVC+pe9ZmvKZban/gW4QPra7PRoTEssyc922qCAkk=
|
gorm.io/gorm v1.23.1/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
|
||||||
|
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||||
|
47
k8s/hishtory-api.yaml
Normal file
47
k8s/hishtory-api.yaml
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: hishtory-api
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
targetPort: 8080
|
||||||
|
selector:
|
||||||
|
app: hishtory-api
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: hishtory-api
|
||||||
|
spec:
|
||||||
|
revisionHistoryLimit: 5
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: hishtory-api
|
||||||
|
replicas: 1
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: hishtory-api
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: hishtory-api
|
||||||
|
image: gcr.io/dworken-k8s/hishtory-api
|
||||||
|
imagePullPolicy: Always
|
||||||
|
ports:
|
||||||
|
- containerPort: 80
|
||||||
|
volumeMounts:
|
||||||
|
- name: aws-secret-volume
|
||||||
|
mountPath: /root/awscreds/
|
||||||
|
env:
|
||||||
|
- name: AWS_SHARED_CREDENTIALS_FILE
|
||||||
|
value: /root/.aws/credentials
|
||||||
|
- name: DOGSTATSD_HOST_IP
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: status.hostIP
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- name: aws-secret-volume
|
||||||
|
secret:
|
||||||
|
secretName: aws-secret
|
8
server/Dockerfile
Normal file
8
server/Dockerfile
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
FROM golang:1.17
|
||||||
|
COPY go.mod ./
|
||||||
|
COPY go.sum ./
|
||||||
|
RUN unset GOPATH; go mod download
|
||||||
|
COPY . ./
|
||||||
|
RUN unset GOPATH; go build -o /server server/server.go
|
||||||
|
CMD [ "/server" ]
|
||||||
|
|
@ -8,6 +8,13 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/ddworken/hishtory/shared"
|
"github.com/ddworken/hishtory/shared"
|
||||||
|
_ "github.com/lib/pq"
|
||||||
|
"gorm.io/driver/postgres"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
POSTGRES_DB = "postgresql://postgres:O74Ji4735C@postgres-postgresql.default.svc.cluster.local:5432/hishtory?sslmode=disable"
|
||||||
)
|
)
|
||||||
|
|
||||||
func apiSubmitHandler(w http.ResponseWriter, r *http.Request) {
|
func apiSubmitHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -17,10 +24,27 @@ func apiSubmitHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
err = shared.Persist(entry)
|
db, err := OpenDB()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
err = shared.Persist(db, entry)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func OpenDB() (*gorm.DB, error) {
|
||||||
|
if shared.IsTestEnvironment() {
|
||||||
|
return shared.OpenLocalSqliteDb()
|
||||||
|
}
|
||||||
|
|
||||||
|
db, err := gorm.Open(postgres.Open(POSTGRES_DB), &gorm.Config{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to connect to the DB: %v", err)
|
||||||
|
}
|
||||||
|
db.AutoMigrate(&shared.HistoryEntry{})
|
||||||
|
return db, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiSearchHandler(w http.ResponseWriter, r *http.Request) {
|
func apiSearchHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -32,7 +56,7 @@ func apiSearchHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
limit = 0
|
limit = 0
|
||||||
}
|
}
|
||||||
db, err := shared.OpenDB()
|
db, err := OpenDB()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -51,7 +75,18 @@ func apiSearchHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
_, err := shared.OpenDB()
|
// db, err := sql.Open("postgres", "postgresql://postgres:O74Ji4735C@postgres-postgresql.default.svc.cluster.local:5432/cascara_prod?sslmode=disable")
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
// defer db.Close()
|
||||||
|
|
||||||
|
// _, err = db.Exec(fmt.Sprintf("CREATE DATABASE %s;", "hishtory"))
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
_, err := OpenDB()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ import (
|
|||||||
|
|
||||||
func TestSubmitThenQuery(t *testing.T) {
|
func TestSubmitThenQuery(t *testing.T) {
|
||||||
// Set up
|
// Set up
|
||||||
defer shared.BackupAndRestore(t)
|
defer shared.BackupAndRestore(t)()
|
||||||
shared.Check(t, shared.Setup([]string{}))
|
shared.Check(t, shared.Setup([]string{}))
|
||||||
|
|
||||||
// Submit an entry
|
// Submit an entry
|
||||||
@ -59,7 +59,7 @@ func TestSubmitThenQuery(t *testing.T) {
|
|||||||
|
|
||||||
func TestNoUserSecretGivesNoResults(t *testing.T) {
|
func TestNoUserSecretGivesNoResults(t *testing.T) {
|
||||||
// Set up
|
// Set up
|
||||||
defer shared.BackupAndRestore(t)
|
defer shared.BackupAndRestore(t)()
|
||||||
shared.Check(t, shared.Setup([]string{}))
|
shared.Check(t, shared.Setup([]string{}))
|
||||||
|
|
||||||
// Submit an entry
|
// Submit an entry
|
||||||
@ -87,7 +87,7 @@ func TestNoUserSecretGivesNoResults(t *testing.T) {
|
|||||||
|
|
||||||
func TestSearchQuery(t *testing.T) {
|
func TestSearchQuery(t *testing.T) {
|
||||||
// Set up
|
// Set up
|
||||||
defer shared.BackupAndRestore(t)
|
defer shared.BackupAndRestore(t)()
|
||||||
shared.Check(t, shared.Setup([]string{}))
|
shared.Check(t, shared.Setup([]string{}))
|
||||||
|
|
||||||
// Submit an entry that we'll match
|
// Submit an entry that we'll match
|
||||||
|
@ -3,6 +3,7 @@ package shared
|
|||||||
import (
|
import (
|
||||||
_ "embed"
|
_ "embed"
|
||||||
|
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@ -40,6 +41,9 @@ func getCwd() (string, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to get user's home directory: %v", err)
|
return "", fmt.Errorf("failed to get user's home directory: %v", err)
|
||||||
}
|
}
|
||||||
|
if cwd == homedir {
|
||||||
|
return "~/", nil
|
||||||
|
}
|
||||||
if strings.HasPrefix(cwd, homedir) {
|
if strings.HasPrefix(cwd, homedir) {
|
||||||
return strings.Replace(cwd, homedir, "~", 1), nil
|
return strings.Replace(cwd, homedir, "~", 1), nil
|
||||||
}
|
}
|
||||||
@ -242,6 +246,12 @@ func Install() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func configureBashrc(homedir, binaryPath string) error {
|
func configureBashrc(homedir, binaryPath string) error {
|
||||||
|
// Create the file we're going to source in our bashrc. Do this no matter what in case there are updates to it.
|
||||||
|
bashConfigPath := path.Join(filepath.Dir(binaryPath), "config.sh")
|
||||||
|
err := ioutil.WriteFile(bashConfigPath, []byte(CONFIG_SH_CONTENTS), 0644)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to write config.sh file: %v", err)
|
||||||
|
}
|
||||||
// Check if we need to configure the bashrc
|
// Check if we need to configure the bashrc
|
||||||
bashrc, err := ioutil.ReadFile(path.Join(homedir, ".bashrc"))
|
bashrc, err := ioutil.ReadFile(path.Join(homedir, ".bashrc"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -250,13 +260,6 @@ func configureBashrc(homedir, binaryPath string) error {
|
|||||||
if strings.Contains(string(bashrc), "# Hishtory Config:") {
|
if strings.Contains(string(bashrc), "# Hishtory Config:") {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// Create the file we're going to source in our bashrc
|
|
||||||
bashConfigPath := path.Join(filepath.Dir(binaryPath), "config.sh")
|
|
||||||
err = ioutil.WriteFile(bashConfigPath, []byte(CONFIG_SH_CONTENTS), 0644)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to write config.sh file: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add to bashrc
|
// Add to bashrc
|
||||||
f, err := os.OpenFile(path.Join(homedir, ".bashrc"), os.O_APPEND|os.O_WRONLY, 0644)
|
f, err := os.OpenFile(path.Join(homedir, ".bashrc"), os.O_APPEND|os.O_WRONLY, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -272,15 +275,14 @@ func configureBashrc(homedir, binaryPath string) error {
|
|||||||
|
|
||||||
func installBinary(homedir string) (string, error) {
|
func installBinary(homedir string) (string, error) {
|
||||||
clientPath, err := exec.LookPath("hishtory")
|
clientPath, err := exec.LookPath("hishtory")
|
||||||
if err == nil {
|
|
||||||
return clientPath, nil
|
|
||||||
}
|
|
||||||
clientDir := path.Join(homedir, ".hishtory-bin")
|
|
||||||
err = os.MkdirAll(clientDir, 0744)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to create folder for hishtory binary: %v", err)
|
clientDir := path.Join(homedir, ".hishtory-bin")
|
||||||
|
err = os.MkdirAll(clientDir, 0744)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to create folder for hishtory binary: %v", err)
|
||||||
|
}
|
||||||
|
clientPath = path.Join(clientDir, "hishtory")
|
||||||
}
|
}
|
||||||
clientPath = path.Join(clientDir, "hishtory")
|
|
||||||
err = copyFile(os.Args[0], clientPath)
|
err = copyFile(os.Args[0], clientPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to copy hishtory binary to $PATH: %v", err)
|
return "", fmt.Errorf("failed to copy hishtory binary to $PATH: %v", err)
|
||||||
@ -316,3 +318,16 @@ func copyFile(src, dst string) error {
|
|||||||
_, err = io.Copy(destination, source)
|
_, err = io.Copy(destination, source)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Update(url string) error {
|
||||||
|
var stdout bytes.Buffer
|
||||||
|
var stderr bytes.Buffer
|
||||||
|
cmd := exec.Command("bash", "-c", "curl -o /tmp/hishtory-client "+url+"; chmod +x /tmp/hishtory-client; /tmp/hishtory-client install")
|
||||||
|
cmd.Stdout = &stdout
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to update: %v, stdout=%+v, stderr=%+v", err, stdout.String(), stderr.String())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -22,6 +22,6 @@ function PostCommand() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Run after every prompt
|
# Run after every prompt
|
||||||
hishtory saveHistoryEntry $? "`history 1`" $HISHTORY_START_TIME
|
(hishtory saveHistoryEntry $? "`history 1`" $HISHTORY_START_TIME &)
|
||||||
}
|
}
|
||||||
PROMPT_COMMAND="PostCommand"
|
PROMPT_COMMAND="PostCommand"
|
||||||
|
@ -2,13 +2,13 @@ package shared
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
|
||||||
"gorm.io/driver/sqlite"
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
"gorm.io/driver/sqlite"
|
||||||
)
|
)
|
||||||
|
|
||||||
type HistoryEntry struct {
|
type HistoryEntry struct {
|
||||||
@ -26,18 +26,11 @@ const (
|
|||||||
DB_PATH = ".hishtory.db"
|
DB_PATH = ".hishtory.db"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Persist(entry HistoryEntry) error {
|
func IsTestEnvironment() bool {
|
||||||
db, err := OpenDB()
|
return os.Getenv("HISHTORY_TEST") != ""
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
conn, err := db.DB()
|
|
||||||
defer conn.Close()
|
|
||||||
db.Create(&entry).Commit()
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func OpenDB() (*gorm.DB, error) {
|
func OpenLocalSqliteDb() (*gorm.DB, error) {
|
||||||
homedir, err := os.UserHomeDir()
|
homedir, err := os.UserHomeDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to get user's home directory: %v", err)
|
return nil, fmt.Errorf("failed to get user's home directory: %v", err)
|
||||||
@ -50,6 +43,12 @@ func OpenDB() (*gorm.DB, error) {
|
|||||||
return db, nil
|
return db, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func Persist(db *gorm.DB, entry HistoryEntry) error {
|
||||||
|
db.Create(&entry).Commit()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func Search(db *gorm.DB, userSecret, query string, limit int) ([]*HistoryEntry, error) {
|
func Search(db *gorm.DB, userSecret, query string, limit int) ([]*HistoryEntry, error) {
|
||||||
tokens, err := tokenize(query)
|
tokens, err := tokenize(query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -7,12 +7,12 @@ import (
|
|||||||
func TestPersist(t *testing.T) {
|
func TestPersist(t *testing.T) {
|
||||||
defer BackupAndRestore(t)
|
defer BackupAndRestore(t)
|
||||||
Check(t, Setup([]string{}))
|
Check(t, Setup([]string{}))
|
||||||
|
db, err := OpenLocalSqliteDb()
|
||||||
|
Check(t, err)
|
||||||
|
|
||||||
entry, err := BuildHistoryEntry([]string{"unused", "saveHistoryEntry", "120", " 123 ls / ", "1641774958326745663"})
|
entry, err := BuildHistoryEntry([]string{"unused", "saveHistoryEntry", "120", " 123 ls / ", "1641774958326745663"})
|
||||||
Check(t, err)
|
Check(t, err)
|
||||||
Check(t, Persist(*entry))
|
Check(t, Persist(db, *entry))
|
||||||
|
|
||||||
db, err := OpenDB()
|
|
||||||
Check(t, err)
|
|
||||||
var historyEntries []*HistoryEntry
|
var historyEntries []*HistoryEntry
|
||||||
result := db.Find(&historyEntries)
|
result := db.Find(&historyEntries)
|
||||||
Check(t, result.Error)
|
Check(t, result.Error)
|
||||||
@ -28,21 +28,21 @@ func TestPersist(t *testing.T) {
|
|||||||
func TestSearch(t *testing.T) {
|
func TestSearch(t *testing.T) {
|
||||||
defer BackupAndRestore(t)
|
defer BackupAndRestore(t)
|
||||||
Check(t, Setup([]string{}))
|
Check(t, Setup([]string{}))
|
||||||
|
db, err := OpenLocalSqliteDb()
|
||||||
|
Check(t, err)
|
||||||
|
|
||||||
// Insert data
|
// Insert data
|
||||||
entry1, err := BuildHistoryEntry([]string{"unused", "saveHistoryEntry", "120", " 123 ls / ", "1641774958326745663"})
|
entry1, err := BuildHistoryEntry([]string{"unused", "saveHistoryEntry", "120", " 123 ls / ", "1641774958326745663"})
|
||||||
Check(t, err)
|
Check(t, err)
|
||||||
Check(t, Persist(*entry1))
|
Check(t, Persist(db, *entry1))
|
||||||
entry2, err := BuildHistoryEntry([]string{"unused", "saveHistoryEntry", "120", " 123 ls /foo ", "1641774958326745663"})
|
entry2, err := BuildHistoryEntry([]string{"unused", "saveHistoryEntry", "120", " 123 ls /foo ", "1641774958326745663"})
|
||||||
Check(t, err)
|
Check(t, err)
|
||||||
Check(t, Persist(*entry2))
|
Check(t, Persist(db, *entry2))
|
||||||
entry3, err := BuildHistoryEntry([]string{"unused", "saveHistoryEntry", "120", " 123 echo hi ", "1641774958326745663"})
|
entry3, err := BuildHistoryEntry([]string{"unused", "saveHistoryEntry", "120", " 123 echo hi ", "1641774958326745663"})
|
||||||
Check(t, err)
|
Check(t, err)
|
||||||
Check(t, Persist(*entry3))
|
Check(t, Persist(db, *entry3))
|
||||||
|
|
||||||
// Search for data
|
// Search for data
|
||||||
db, err := OpenDB()
|
|
||||||
Check(t, err)
|
|
||||||
secret, err := GetUserSecret()
|
secret, err := GetUserSecret()
|
||||||
Check(t, err)
|
Check(t, err)
|
||||||
results, err := Search(db, secret, "ls", 5)
|
results, err := Search(db, secret, "ls", 5)
|
||||||
|
BIN
web/landing/www/demo.png
Normal file
BIN
web/landing/www/demo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 45 KiB |
BIN
web/landing/www/hishtory-offline
Executable file
BIN
web/landing/www/hishtory-offline
Executable file
Binary file not shown.
BIN
web/landing/www/hishtory-online
Executable file
BIN
web/landing/www/hishtory-online
Executable file
Binary file not shown.
@ -23,27 +23,6 @@
|
|||||||
gtag('config', 'UA-144207374-1');
|
gtag('config', 'UA-144207374-1');
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Begin Inspectlet Asynchronous Code -->
|
|
||||||
<script type="text/javascript">
|
|
||||||
(function () {
|
|
||||||
window.__insp = window.__insp || [];
|
|
||||||
__insp.push(['wid', 1312045050]);
|
|
||||||
var ldinsp = function () {
|
|
||||||
if (typeof window.__inspld != "undefined") return;
|
|
||||||
window.__inspld = 1;
|
|
||||||
var insp = document.createElement('script');
|
|
||||||
insp.type = 'text/javascript';
|
|
||||||
insp.async = true;
|
|
||||||
insp.id = "inspsync";
|
|
||||||
insp.src = ('https:' == document.location.protocol ? 'https' : 'http') + '://cdn.inspectlet.com/inspectlet.js?wid=1312045050&r=' + Math.floor(new Date().getTime() / 3600000);
|
|
||||||
var x = document.getElementsByTagName('script')[0];
|
|
||||||
x.parentNode.insertBefore(insp, x);
|
|
||||||
};
|
|
||||||
setTimeout(ldinsp, 0);
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
<!-- End Inspectlet Asynchronous Code -->
|
|
||||||
|
|
||||||
<!-- Font Awesome Icons -->
|
<!-- Font Awesome Icons -->
|
||||||
<link href="vendor/fontawesome-free/css/all.min.css" rel="stylesheet" type="text/css">
|
<link href="vendor/fontawesome-free/css/all.min.css" rel="stylesheet" type="text/css">
|
||||||
|
|
||||||
@ -82,13 +61,10 @@
|
|||||||
<a class="nav-link js-scroll-trigger" href="#about">About</a>
|
<a class="nav-link js-scroll-trigger" href="#about">About</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link js-scroll-trigger" href="#setup">How it works</a>
|
<a class="nav-link js-scroll-trigger" href="#setup">Setup</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link js-scroll-trigger" href="#signup">Sign up</a>
|
<a class="nav-link js-scroll-trigger" href="#contact">FAQ</a>
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link js-scroll-trigger" href="#contact">Contact</a>
|
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@ -99,15 +75,12 @@
|
|||||||
<header class="masthead">
|
<header class="masthead">
|
||||||
<div class="container h-100">
|
<div class="container h-100">
|
||||||
<div class="row h-100 align-items-center justify-content-center text-center">
|
<div class="row h-100 align-items-center justify-content-center text-center">
|
||||||
<div class="col-lg-10 align-self-end">
|
<div class="col-lg-12 align-self-end">
|
||||||
<h1 class="text-uppercase text-white font-weight-bold">Low Friction Security for Startups</h1>
|
<h2 class="text-white font-weight-bold">Your shell history: synced, queryable, and in context</h1>
|
||||||
<hr class="divider my-4">
|
<hr class="divider my-4">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-8 align-self-baseline">
|
<div class="col-lg-8 align-self-baseline">
|
||||||
<p class="text-white-75 font-weight-light mb-5">
|
<script id="asciicast-xW0ddZdZm7Utv1iLATegM4Oe4" src="https://asciinema.org/a/xW0ddZdZm7Utv1iLATegM4Oe4.js" async data-autoplay="true" data-loop="true"></script>
|
||||||
Cascara SSH eliminates the need to manage SSH keys and increases security.
|
|
||||||
</p>
|
|
||||||
<a class="btn btn-primary btn-xl js-scroll-trigger" href="#about">Find Out More</a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -118,30 +91,15 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-lg-8 text-center">
|
<div class="col-lg-8 text-center">
|
||||||
<h2 class="text-white mt-0">SSH security integrated with GSuite</h2>
|
<h2 class="text-white mt-0">Bash scripting is powerful, keep track of how you use it</h2>
|
||||||
<hr class="divider light my-4">
|
<hr class="divider light my-4">
|
||||||
<p class="text-white-50 mb-4">
|
<p class="text-white-50 mb-4">
|
||||||
Rely on Google's security for authenticating users and enforcing 2FA. Cascara builds on top of
|
Bash keeps a list of every command you run locally, but this list of commands is devoid of context (where did I run that command? what was the output of the command? how long did it take to run?) and it is easily corrupted (open two terminals at once? Say goodbye to your bash history!).
|
||||||
Google
|
<br>
|
||||||
in order to seamlessly provision short lived SSH keys. Deploy one key to all of your servers and use
|
<br>
|
||||||
SSH the same way everyone from Facebook to Uber does.
|
Hishtory keeps track of the command you ran, how long it took to run, whether it succeeded or failed, where you ran it, and on what machine. It syncs this information across all your machines, so you can always find that useful bash pipeline you wrote a month ago.
|
||||||
<br/><br/>
|
|
||||||
According to <a style="color:inherit;text-decoration:underline"
|
|
||||||
href="https://www.ssh.com/iam/how-to-kill-a-fortune-500">ssh.com</a>, 90% of SSH
|
|
||||||
keys in a typical enterprise company are not actively used. Cascara ensures
|
|
||||||
that no inactive keys exist thereby reducing the threat of a breach.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-white-50 mb-4">
|
|
||||||
<pre>
|
|
||||||
<code class="bash">
|
|
||||||
david@laptop $ cssh david@cascarasecurity.com
|
|
||||||
Logging in with Google... # Opens a browser window to login using OAuth
|
|
||||||
Provisioning a new SSH key...
|
|
||||||
Session Established
|
|
||||||
david@cascarasecurity.com $
|
|
||||||
</code>
|
|
||||||
</pre>
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -152,54 +110,13 @@ david@cascarasecurity.com $
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<h2 class="text-center mt-0">Setup Process</h2>
|
<h2 class="text-center mt-0">Setup Process</h2>
|
||||||
<hr class="divider my-4">
|
<hr class="divider my-4">
|
||||||
<div class="row">
|
<p class="text-black-50 mb-4">
|
||||||
<div class="col-lg-3 col-md-6 text-center">
|
To install hishtory in offline mode: `curl -o hishtory https://hishtory.dev/hishtory-offline; chmod +x hishtory; ./hishtory install`
|
||||||
<div class="mt-5">
|
<br>
|
||||||
<i class="fas fa-4x fa-user-plus text-primary mb-4"></i>
|
To install hishtory in online mode on your first machine: `curl -o hishtory https://hishtory.dev/hishtory-online; chmod +x hishtory; ./hishtory install`
|
||||||
<h3 class="h4 mb-2">Signup</h3>
|
<br>
|
||||||
<p class="text-muted mb-0">Configure custom SSH permissions through our website</p>
|
To install hishtory in online mode on your other machines: `curl -o hishtory https://hishtory.dev/hishtory-online; chmod +x hishtory; ./hishtory install $YOUR_HISHTORY_SECRET`
|
||||||
</div>
|
</p>
|
||||||
</div>
|
|
||||||
<div class="col-lg-3 col-md-6 text-center">
|
|
||||||
<div class="mt-5">
|
|
||||||
<i class="fas fa-4x fa-server code text-primary mb-4"></i>
|
|
||||||
<h3 class="h4 mb-2">Deploy</h3>
|
|
||||||
<p class="text-muted mb-0">Deploy a single SSH certificate on all of your servers</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-lg-3 col-md-6 text-center">
|
|
||||||
<div class="mt-5">
|
|
||||||
<i class="fas fa-4x fa-laptop-code text-primary mb-4"></i>
|
|
||||||
<h3 class="h4 mb-2">Install</h3>
|
|
||||||
<p class="text-muted mb-0">Install the open source cssh binary on your computer</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-lg-3 col-md-6 text-center">
|
|
||||||
<div class="mt-5">
|
|
||||||
<i class="fas fa-4x fa-heart text-primary mb-4"></i>
|
|
||||||
<h3 class="h4 mb-2">Work, securely</h3>
|
|
||||||
<p class="text-muted mb-0">Get back to the things that matter, confident that your servers are
|
|
||||||
secure</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- Signup Section -->
|
|
||||||
<section class="page-section bg-primary" id="signup">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row justify-content-center">
|
|
||||||
<div class="col-lg-8 text-center">
|
|
||||||
<h2 class="text-white mt-0">Sign up</h2>
|
|
||||||
<p class="text-white-50 mb-4">
|
|
||||||
Sign up now and get started within 10 minutes
|
|
||||||
</p>
|
|
||||||
<hr class="divider light my-4">
|
|
||||||
<a class="btn btn-light btn-xl js-scroll-trigger" href="https://console.cascarasecurity.com/signup">Get
|
|
||||||
Started!</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@ -225,10 +142,11 @@ david@cascarasecurity.com $
|
|||||||
<div id="collapseZero" class="collapse show" aria-labelledby="headingZero"
|
<div id="collapseZero" class="collapse show" aria-labelledby="headingZero"
|
||||||
data-parent="#accordionExample">
|
data-parent="#accordionExample">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
SSH has a concept of "certificate authorities" where a single master key is placed on
|
Hishtory hooks into your shell to record everything you could want to know about your shell history. It supports two modes:
|
||||||
the server, and that key is used to create temporary keys that automatically expire
|
<ol>
|
||||||
after a set period of time. Cascara automates this process of provisioning new temporary
|
<li>Offline mode: It persists all this data in a local SQLite database</li>
|
||||||
keys based off of authenticating with your Google account.
|
<li>Online mode: Only if you opt-in, it syncs this data between all your machines so your history is immediately queryable from every one of your machines</li>
|
||||||
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -237,7 +155,7 @@ david@cascarasecurity.com $
|
|||||||
<h2 class="mb-0">
|
<h2 class="mb-0">
|
||||||
<button class="btn btn-link collapsed" type="button" data-toggle="collapse"
|
<button class="btn btn-link collapsed" type="button" data-toggle="collapse"
|
||||||
data-target="#collapseOne" aria-expanded="false" aria-controls="collapseOne">
|
data-target="#collapseOne" aria-expanded="false" aria-controls="collapseOne">
|
||||||
Is this secure?
|
Can I use this without syncing my bash history to the internet?
|
||||||
</button>
|
</button>
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
@ -245,14 +163,7 @@ david@cascarasecurity.com $
|
|||||||
<div id="collapseOne" class="collapse" aria-labelledby="headingOne"
|
<div id="collapseOne" class="collapse" aria-labelledby="headingOne"
|
||||||
data-parent="#accordionExample">
|
data-parent="#accordionExample">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
Security is never absolute, but we believe that this does raise the bar for security.
|
Yes! The default is to use it in the offline mode where your shell history stays 100% local and never leaves your machine. Only if you opt-in will it be synced to your other machines with hishtory installed.
|
||||||
Cascara centralizes your SSH security so that one must attack Google in order to
|
|
||||||
attack your SSH servers.
|
|
||||||
<br/><br/>
|
|
||||||
Using this service also requires trusting us. We maintain a <a href="/canary.txt">warrant
|
|
||||||
canary</a> that we promise to keep up to date. In order to ensure that compromising us
|
|
||||||
does not allow an attacker to compromise your servers, we recommend either configuring
|
|
||||||
2FA or a bastion host following the guides in the admin console.
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -262,22 +173,14 @@ david@cascarasecurity.com $
|
|||||||
<button class="btn btn-link collapsed" type="button" data-toggle="collapse"
|
<button class="btn btn-link collapsed" type="button" data-toggle="collapse"
|
||||||
data-target="#collapseTwo" aria-expanded="false"
|
data-target="#collapseTwo" aria-expanded="false"
|
||||||
aria-controls="collapseTwo">
|
aria-controls="collapseTwo">
|
||||||
Is this really easier?
|
What about `.bash_history`?
|
||||||
</button>
|
</button>
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<div id="collapseTwo" class="collapse" aria-labelledby="headingTwo"
|
<div id="collapseTwo" class="collapse" aria-labelledby="headingTwo"
|
||||||
data-parent="#accordionExample">
|
data-parent="#accordionExample">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
Cascara eliminates the need to onboard and offboard engineers by manually editing the
|
If it works for you, great! But I find I constantly get annoyed with `.bash_history` either because I lose context (which directory do I need to run that command from?) or because I full on lose the file (it tends to get corrupted if you open multiple terminals at the same time).
|
||||||
SSH keys deployed on servers.
|
|
||||||
Even if you use Ansible or Chef to manage SSH keys, Cascara ensures that you do not need
|
|
||||||
to wait for every server to get updated. With Cascara, you simply add or remove
|
|
||||||
someone's
|
|
||||||
email and it is done.
|
|
||||||
<br/> <br/>
|
|
||||||
Cascara also removes the time consuming process of rotating SSH keys since all keys
|
|
||||||
automatically expire.
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -287,16 +190,14 @@ david@cascarasecurity.com $
|
|||||||
<button class="btn btn-link collapsed" type="button" data-toggle="collapse"
|
<button class="btn btn-link collapsed" type="button" data-toggle="collapse"
|
||||||
data-target="#collapseThree" aria-expanded="false"
|
data-target="#collapseThree" aria-expanded="false"
|
||||||
aria-controls="collapseThree">
|
aria-controls="collapseThree">
|
||||||
How much does this cost?
|
Is this free?
|
||||||
</button>
|
</button>
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<div id="collapseThree" class="collapse" aria-labelledby="headingThree"
|
<div id="collapseThree" class="collapse" aria-labelledby="headingThree"
|
||||||
data-parent="#accordionExample">
|
data-parent="#accordionExample">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
Nothing! We are dedicated to ensuring that hobbyists and small startups can use our
|
Yes! This is just a hobby project for me that I use, and I hope other people get some use out of.
|
||||||
product. Overtime we plan on
|
|
||||||
adding more enterprise focused features which we will charge for.
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -315,12 +216,7 @@ david@cascarasecurity.com $
|
|||||||
Made with ❤️ by <a
|
Made with ❤️ by <a
|
||||||
href="https://daviddworken.com">David Dworken</a>.
|
href="https://daviddworken.com">David Dworken</a>.
|
||||||
|
|
||||||
I'm a Security Engineer with experience working
|
I'm a Security Engineer working at Google. I built this to solve a problem for myself, and wanted to share it with others who may find it useful.
|
||||||
everywhere from Snapchat to Keybase. I designed Cascara to solve a recurring
|
|
||||||
security problem I've seen time and time again. In addition, it has been designed
|
|
||||||
from the ground up with a secure scalable architecture.
|
|
||||||
<br/><br/>
|
|
||||||
More features are incoming!
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -334,7 +230,7 @@ david@cascarasecurity.com $
|
|||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-lg-4 text-center">
|
<div class="col-lg-4 text-center">
|
||||||
<i class="fas fa-envelope fa-3x mb-3 text-muted"></i>
|
<i class="fas fa-envelope fa-3x mb-3 text-muted"></i>
|
||||||
<a class="d-block" href="mailto:david@cascarasecurity.com">david@cascarasecurity.com</a>
|
<a class="d-block" href="mailto:david@daviddworken.com">david@daviddworken.com</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -343,7 +239,7 @@ david@cascarasecurity.com $
|
|||||||
<!-- Footer -->
|
<!-- Footer -->
|
||||||
<footer class="bg-light py-5">
|
<footer class="bg-light py-5">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="small text-center text-muted">Copyright © 2019 - Cascara Security</div>
|
<div class="small text-center text-muted">Copyright © 2022 - David Dworken</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user