Website landing page, install instructions, update command, status command, set up postgres, and fixing broken tests

This commit is contained in:
David Dworken 2022-03-29 21:56:28 -07:00
parent f72c88bbd9
commit 252e9ab122
21 changed files with 419 additions and 212 deletions

2
.dockerignore Normal file
View File

@ -0,0 +1,2 @@
.git
node_modules/

View File

@ -1,12 +1,14 @@
test:
go test -p ./...
HISHTORY_TEST=1 go test -p 1 ./...
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-api -f deploy/api/Dockerfile .
docker build -t gcr.io/dworken-k8s/hishtory-api -f server/Dockerfile .
deploy: build
docker push gcr.io/dworken-k8s/hishtory-static
# 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 cascara-keys -p "{\"spec\":{\"template\":{\"metadata\":{\"labels\":{\"ts\":\"`date|sed -e 's/ /_/g'|sed -e 's/:/-/g'`\"}}}}}}"
# docker push gcr.io/dworken-k8s/hishtory-static
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-api -p "{\"spec\":{\"template\":{\"metadata\":{\"labels\":{\"ts\":\"`date|sed -e 's/ /_/g'|sed -e 's/:/-/g'`\"}}}}}}"

View File

@ -28,6 +28,14 @@ func main() {
shared.CheckFatalError(shared.Enable())
case "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:
shared.CheckFatalError(fmt.Errorf("unknown command: %s", os.Args[1]))
}
@ -43,7 +51,7 @@ func getServerHostname() string {
func query(query string) {
userSecret, err := shared.GetUserSecret()
shared.CheckFatalError(err)
db, err := shared.OpenDB()
db, err := shared.OpenLocalSqliteDb()
shared.CheckFatalError(err)
data, err := shared.Search(db, userSecret, query, 25)
shared.CheckFatalError(err)
@ -58,18 +66,20 @@ func saveHistoryEntry() {
}
entry, err := shared.BuildHistoryEntry(os.Args)
shared.CheckFatalError(err)
err = shared.Persist(*entry)
db, err := shared.OpenLocalSqliteDb()
shared.CheckFatalError(err)
err = shared.Persist(db, *entry)
shared.CheckFatalError(err)
}
func export() {
userSecret, err := shared.GetUserSecret()
shared.CheckFatalError(err)
db, err := shared.OpenDB()
db, err := shared.OpenLocalSqliteDb()
shared.CheckFatalError(err)
data, err := shared.Search(db, userSecret, "", 0)
shared.CheckFatalError(err)
for _, entry := range data {
fmt.Println(entry)
}
}
}

View File

@ -25,7 +25,9 @@ func RunInteractiveBashCommands(t *testing.T, script string) string {
func TestIntegration(t *testing.T) {
// Set up
defer shared.BackupAndRestore(t)
defer shared.BackupAndRestore(t)()
// TODO(ddworken): Test status
// Test install
out := RunInteractiveBashCommands(t, `
@ -84,4 +86,5 @@ func TestIntegration(t *testing.T) {
}
}
}
// TODO(ddworken): Test export
// TODO(ddworken): Test export

View File

@ -32,6 +32,15 @@ func main() {
shared.CheckFatalError(shared.Enable())
case "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:
shared.CheckFatalError(fmt.Errorf("unknown command: %s", os.Args[1]))
}
@ -41,7 +50,7 @@ func getServerHostname() string {
if server := os.Getenv("HISHTORY_SERVER"); server != "" {
return server
}
return "http://localhost:8080"
return "https://api.hishtory.dev"
}
func query(query string) {
@ -53,11 +62,11 @@ func query(query string) {
func doQuery(query string) ([]*shared.HistoryEntry, error) {
userSecret, err := shared.GetUserSecret()
if err != nil {
return nil, err
return nil, err
}
req, err := http.NewRequest("GET", getServerHostname()+"/api/v1/search", nil)
if err != nil {
return nil, err
return nil, err
}
q := req.URL.Query()
@ -69,12 +78,12 @@ func doQuery(query string) ([]*shared.HistoryEntry, error) {
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, err
return nil, err
}
defer resp.Body.Close()
resp_body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
return nil, err
}
if resp.Status != "200 OK" {
return nil, fmt.Errorf("search API returned invalid result. status=" + resp.Status)
@ -82,7 +91,7 @@ func doQuery(query string) ([]*shared.HistoryEntry, error) {
var data []*shared.HistoryEntry
err = json.Unmarshal(resp_body, &data)
return data, err
return data, err
}
func saveHistoryEntry() {
@ -111,5 +120,5 @@ func send(entry shared.HistoryEntry) error {
}
func export() {
}
// TODO(ddworken)
}

View File

@ -12,7 +12,7 @@ import (
func RunInteractiveBashCommands(t *testing.T, script string) string {
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
cmd.Stdout = &out
var err bytes.Buffer
@ -23,9 +23,11 @@ func RunInteractiveBashCommands(t *testing.T, script string) string {
func TestIntegration(t *testing.T) {
// Set up
defer shared.BackupAndRestore(t)
defer shared.BackupAndRestore(t)()
// Test init
// TODO(ddworken): Test status
// Test install
out := RunInteractiveBashCommands(t, `
gvm use go1.17
cd ../../
@ -39,8 +41,7 @@ func TestIntegration(t *testing.T) {
}
// Test recording commands
out = RunInteractiveBashCommands(t, `/tmp/server &
sleep 2 # to give the server time to start
out = RunInteractiveBashCommands(t, `
ls /a
ls /bar
ls /foo
@ -51,12 +52,12 @@ func TestIntegration(t *testing.T) {
hishtory enable
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)
}
// 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"}
for _, item := range expected {
if !strings.Contains(out, item) {
@ -85,4 +86,4 @@ func TestIntegration(t *testing.T) {
}
}
// TODO(ddworken): Test export
// TODO(ddworken): Test export

16
go.mod
View File

@ -5,16 +5,28 @@ go 1.17
require (
github.com/fatih/color v1.13.0
github.com/google/uuid v1.3.0
github.com/lib/pq v1.10.2
github.com/rodaine/table v1.0.1
gorm.io/driver/postgres v1.3.1
gorm.io/driver/sqlite v1.2.6
gorm.io/gorm v1.22.4
gorm.io/gorm v1.23.1
)
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/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-isatty v0.0.14 // 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/text v0.3.7 // indirect
)

176
go.sum
View File

@ -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.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
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/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/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/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.3 h1:PlHq1bSCSZL9K0wUhbm2pGLoTWs2GwVhsP6emvGV/ZI=
github.com/jinzhu/now v1.1.3/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas=
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/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.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
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-sqlite3 v1.14.9 h1:10HX2Td0ocZpYEjhilsuo6WWtUqttj2Kb0KtD86/KYA=
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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
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/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.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.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
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-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/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 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/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/go.mod h1:gyoX0vHiiwi0g49tv+x2E7l8ksauLK0U/gShcdUsjWY=
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.22.4/go.mod h1:1aeVC+pe9ZmvKZban/gW4QPra7PRoTEssyc922qCAkk=
gorm.io/gorm v1.23.1 h1:aj5IlhDzEPsoIyOPtTRVI+SyaN1u6k613sbt4pwbxG0=
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=

BIN
hishtory Executable file

Binary file not shown.

47
k8s/hishtory-api.yaml Normal file
View 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
View 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" ]

View File

@ -8,6 +8,13 @@ import (
"strconv"
"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) {
@ -17,10 +24,27 @@ func apiSubmitHandler(w http.ResponseWriter, r *http.Request) {
if err != nil {
panic(err)
}
err = shared.Persist(entry)
db, err := OpenDB()
if err != nil {
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) {
@ -32,7 +56,7 @@ func apiSearchHandler(w http.ResponseWriter, r *http.Request) {
if err != nil {
limit = 0
}
db, err := shared.OpenDB()
db, err := OpenDB()
if err != nil {
panic(err)
}
@ -51,7 +75,18 @@ func apiSearchHandler(w http.ResponseWriter, r *http.Request) {
}
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 {
panic(err)
}

View File

@ -13,7 +13,7 @@ import (
func TestSubmitThenQuery(t *testing.T) {
// Set up
defer shared.BackupAndRestore(t)
defer shared.BackupAndRestore(t)()
shared.Check(t, shared.Setup([]string{}))
// Submit an entry
@ -59,7 +59,7 @@ func TestSubmitThenQuery(t *testing.T) {
func TestNoUserSecretGivesNoResults(t *testing.T) {
// Set up
defer shared.BackupAndRestore(t)
defer shared.BackupAndRestore(t)()
shared.Check(t, shared.Setup([]string{}))
// Submit an entry
@ -87,7 +87,7 @@ func TestNoUserSecretGivesNoResults(t *testing.T) {
func TestSearchQuery(t *testing.T) {
// Set up
defer shared.BackupAndRestore(t)
defer shared.BackupAndRestore(t)()
shared.Check(t, shared.Setup([]string{}))
// Submit an entry that we'll match

View File

@ -3,6 +3,7 @@ package shared
import (
_ "embed"
"bytes"
"encoding/json"
"fmt"
"io"
@ -40,6 +41,9 @@ func getCwd() (string, error) {
if err != nil {
return "", fmt.Errorf("failed to get user's home directory: %v", err)
}
if cwd == homedir {
return "~/", nil
}
if strings.HasPrefix(cwd, homedir) {
return strings.Replace(cwd, homedir, "~", 1), nil
}
@ -242,6 +246,12 @@ func Install() 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
bashrc, err := ioutil.ReadFile(path.Join(homedir, ".bashrc"))
if err != nil {
@ -250,13 +260,6 @@ func configureBashrc(homedir, binaryPath string) error {
if strings.Contains(string(bashrc), "# Hishtory Config:") {
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
f, err := os.OpenFile(path.Join(homedir, ".bashrc"), os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
@ -272,15 +275,14 @@ func configureBashrc(homedir, binaryPath string) error {
func installBinary(homedir string) (string, error) {
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 {
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)
if err != nil {
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)
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
}

View File

@ -22,6 +22,6 @@ function PostCommand() {
fi
# Run after every prompt
hishtory saveHistoryEntry $? "`history 1`" $HISHTORY_START_TIME
(hishtory saveHistoryEntry $? "`history 1`" $HISHTORY_START_TIME &)
}
PROMPT_COMMAND="PostCommand"

View File

@ -2,13 +2,13 @@ package shared
import (
"fmt"
"os"
"path"
"strings"
"time"
"os"
"path"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/driver/sqlite"
)
type HistoryEntry struct {
@ -26,18 +26,11 @@ const (
DB_PATH = ".hishtory.db"
)
func Persist(entry HistoryEntry) error {
db, err := OpenDB()
if err != nil {
return err
}
conn, err := db.DB()
defer conn.Close()
db.Create(&entry).Commit()
return nil
func IsTestEnvironment() bool {
return os.Getenv("HISHTORY_TEST") != ""
}
func OpenDB() (*gorm.DB, error) {
func OpenLocalSqliteDb() (*gorm.DB, error) {
homedir, err := os.UserHomeDir()
if err != nil {
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
}
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) {
tokens, err := tokenize(query)
if err != nil {

View File

@ -7,12 +7,12 @@ import (
func TestPersist(t *testing.T) {
defer BackupAndRestore(t)
Check(t, Setup([]string{}))
db, err := OpenLocalSqliteDb()
Check(t, err)
entry, err := BuildHistoryEntry([]string{"unused", "saveHistoryEntry", "120", " 123 ls / ", "1641774958326745663"})
Check(t, err)
Check(t, Persist(*entry))
db, err := OpenDB()
Check(t, err)
Check(t, Persist(db, *entry))
var historyEntries []*HistoryEntry
result := db.Find(&historyEntries)
Check(t, result.Error)
@ -28,21 +28,21 @@ func TestPersist(t *testing.T) {
func TestSearch(t *testing.T) {
defer BackupAndRestore(t)
Check(t, Setup([]string{}))
db, err := OpenLocalSqliteDb()
Check(t, err)
// Insert data
entry1, err := BuildHistoryEntry([]string{"unused", "saveHistoryEntry", "120", " 123 ls / ", "1641774958326745663"})
Check(t, err)
Check(t, Persist(*entry1))
Check(t, Persist(db, *entry1))
entry2, err := BuildHistoryEntry([]string{"unused", "saveHistoryEntry", "120", " 123 ls /foo ", "1641774958326745663"})
Check(t, err)
Check(t, Persist(*entry2))
Check(t, Persist(db, *entry2))
entry3, err := BuildHistoryEntry([]string{"unused", "saveHistoryEntry", "120", " 123 echo hi ", "1641774958326745663"})
Check(t, err)
Check(t, Persist(*entry3))
Check(t, Persist(db, *entry3))
// Search for data
db, err := OpenDB()
Check(t, err)
secret, err := GetUserSecret()
Check(t, err)
results, err := Search(db, secret, "ls", 5)

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

Binary file not shown.

BIN
web/landing/www/hishtory-online Executable file

Binary file not shown.

View File

@ -23,27 +23,6 @@
gtag('config', 'UA-144207374-1');
</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 -->
<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>
</li>
<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 class="nav-item">
<a class="nav-link js-scroll-trigger" href="#signup">Sign up</a>
</li>
<li class="nav-item">
<a class="nav-link js-scroll-trigger" href="#contact">Contact</a>
<a class="nav-link js-scroll-trigger" href="#contact">FAQ</a>
</li>
</ul>
</div>
@ -99,15 +75,12 @@
<header class="masthead">
<div class="container h-100">
<div class="row h-100 align-items-center justify-content-center text-center">
<div class="col-lg-10 align-self-end">
<h1 class="text-uppercase text-white font-weight-bold">Low Friction Security for Startups</h1>
<div class="col-lg-12 align-self-end">
<h2 class="text-white font-weight-bold">Your shell history: synced, queryable, and in context</h1>
<hr class="divider my-4">
</div>
<div class="col-lg-8 align-self-baseline">
<p class="text-white-75 font-weight-light mb-5">
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>
<script id="asciicast-xW0ddZdZm7Utv1iLATegM4Oe4" src="https://asciinema.org/a/xW0ddZdZm7Utv1iLATegM4Oe4.js" async data-autoplay="true" data-loop="true"></script>
</div>
</div>
</div>
@ -118,30 +91,15 @@
<div class="container">
<div class="row justify-content-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">
<p class="text-white-50 mb-4">
Rely on Google's security for authenticating users and enforcing 2FA. Cascara builds on top of
Google
in order to seamlessly provision short lived SSH keys. Deploy one key to all of your servers and use
SSH the same way everyone from Facebook to Uber does.
<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.
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!).
<br>
<br>
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.
</p>
</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>
</div>
</div>
@ -152,54 +110,13 @@ david@cascarasecurity.com $
<div class="container">
<h2 class="text-center mt-0">Setup Process</h2>
<hr class="divider my-4">
<div class="row">
<div class="col-lg-3 col-md-6 text-center">
<div class="mt-5">
<i class="fas fa-4x fa-user-plus text-primary mb-4"></i>
<h3 class="h4 mb-2">Signup</h3>
<p class="text-muted mb-0">Configure custom SSH permissions through our website</p>
</div>
</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>
<p class="text-black-50 mb-4">
To install hishtory in offline mode: `curl -o hishtory https://hishtory.dev/hishtory-offline; chmod +x hishtory; ./hishtory install`
<br>
To install hishtory in online mode on your first machine: `curl -o hishtory https://hishtory.dev/hishtory-online; chmod +x hishtory; ./hishtory install`
<br>
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`
</p>
</div>
</section>
@ -225,10 +142,11 @@ david@cascarasecurity.com $
<div id="collapseZero" class="collapse show" aria-labelledby="headingZero"
data-parent="#accordionExample">
<div class="card-body">
SSH has a concept of "certificate authorities" where a single master key is placed on
the server, and that key is used to create temporary keys that automatically expire
after a set period of time. Cascara automates this process of provisioning new temporary
keys based off of authenticating with your Google account.
Hishtory hooks into your shell to record everything you could want to know about your shell history. It supports two modes:
<ol>
<li>Offline mode: It persists all this data in a local SQLite database</li>
<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>
@ -237,7 +155,7 @@ david@cascarasecurity.com $
<h2 class="mb-0">
<button class="btn btn-link collapsed" type="button" data-toggle="collapse"
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>
</h2>
</div>
@ -245,14 +163,7 @@ david@cascarasecurity.com $
<div id="collapseOne" class="collapse" aria-labelledby="headingOne"
data-parent="#accordionExample">
<div class="card-body">
Security is never absolute, but we believe that this does raise the bar for security.
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.
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.
</div>
</div>
</div>
@ -262,22 +173,14 @@ david@cascarasecurity.com $
<button class="btn btn-link collapsed" type="button" data-toggle="collapse"
data-target="#collapseTwo" aria-expanded="false"
aria-controls="collapseTwo">
Is this really easier?
What about `.bash_history`?
</button>
</h2>
</div>
<div id="collapseTwo" class="collapse" aria-labelledby="headingTwo"
data-parent="#accordionExample">
<div class="card-body">
Cascara eliminates the need to onboard and offboard engineers by manually editing the
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.
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).
</div>
</div>
</div>
@ -287,16 +190,14 @@ david@cascarasecurity.com $
<button class="btn btn-link collapsed" type="button" data-toggle="collapse"
data-target="#collapseThree" aria-expanded="false"
aria-controls="collapseThree">
How much does this cost?
Is this free?
</button>
</h2>
</div>
<div id="collapseThree" class="collapse" aria-labelledby="headingThree"
data-parent="#accordionExample">
<div class="card-body">
Nothing! We are dedicated to ensuring that hobbyists and small startups can use our
product. Overtime we plan on
adding more enterprise focused features which we will charge for.
Yes! This is just a hobby project for me that I use, and I hope other people get some use out of.
</div>
</div>
</div>
@ -315,12 +216,7 @@ david@cascarasecurity.com $
Made with ❤️ by <a
href="https://daviddworken.com">David Dworken</a>.
I'm a Security Engineer with experience working
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!
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.
</div>
</div>
</div>
@ -334,7 +230,7 @@ david@cascarasecurity.com $
<div class="row justify-content-center">
<div class="col-lg-4 text-center">
<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>
@ -343,7 +239,7 @@ david@cascarasecurity.com $
<!-- Footer -->
<footer class="bg-light py-5">
<div class="container">
<div class="small text-center text-muted">Copyright &copy; 2019 - Cascara Security</div>
<div class="small text-center text-muted">Copyright &copy; 2022 - David Dworken</div>
</div>
</footer>