From 6243cb28f305e824913869f195b4f06cf1a0e503 Mon Sep 17 00:00:00 2001 From: Tine Date: Thu, 15 Feb 2024 18:43:35 +0100 Subject: [PATCH] feat: read yaml as config for potentialy configuring the app --- cmd/server/main.go | 6 +- cmd/worker/main.go | 2 +- go.mod | 23 +- go.sum | 42 +++ internal/config.go | 145 ++++++--- internal/handlers/handlers.go | 2 +- internal/handlers/oauth2.go | 16 +- internal/handlers/settings.go | 1 + internal/handlers/temporal.go | 2 +- pkg/temporal/config.go | 4 +- pkg/temporal/ui.go | 4 +- tools/generate/main.go | 2 +- web/static/css/main.css | 2 +- web/static/css/tailwind.css | 280 +++++++++++++++++- .../pages/settings_healthchecks.tmpl | 141 ++++++++- web/templates/pages/settings_overview.tmpl | 13 +- zdravko.yaml | 27 ++ 17 files changed, 641 insertions(+), 71 deletions(-) create mode 100644 zdravko.yaml diff --git a/cmd/server/main.go b/cmd/server/main.go index 2629fa0..05522bb 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -16,7 +16,7 @@ func main() { r := mux.NewRouter() - db, query, err := internal.ConnectToDatabase(config.ZDRAVKO_DATABASE_PATH) + db, query, err := internal.ConnectToDatabase(config.DatabasePath) if err != nil { log.Fatal(err) } @@ -60,6 +60,6 @@ func main() { // 404 r.PathPrefix("/").HandlerFunc(h.Error404).Methods("GET") - log.Println("Server started on", config.PORT) - log.Fatal(http.ListenAndServe(":"+config.PORT, r)) + log.Println("Server started on", config.Port) + log.Fatal(http.ListenAndServe(":"+config.Port, r)) } diff --git a/cmd/worker/main.go b/cmd/worker/main.go index 9d3b1c9..37e66e3 100644 --- a/cmd/worker/main.go +++ b/cmd/worker/main.go @@ -14,7 +14,7 @@ func main() { // Initialize a Temporal Client // Specify the Namespace in the Client options clientOptions := client.Options{ - HostPort: config.TEMPORAL_SERVER_HOST, + HostPort: config.Temporal.ServerHost, Namespace: "default", } temporalClient, err := client.Dial(clientOptions) diff --git a/go.mod b/go.mod index 45d82d3..142d77b 100644 --- a/go.mod +++ b/go.mod @@ -33,14 +33,19 @@ require ( github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/coreos/go-oidc/v3 v3.1.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a // indirect github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/go-logr/logr v1.3.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.18.0 // indirect github.com/go-sql-driver/mysql v1.7.1 // indirect github.com/gocql/gocql v1.5.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect @@ -58,6 +63,7 @@ require ( github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 // indirect github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect + github.com/hashicorp/hcl v1.0.0 // indirect github.com/iancoleman/strcase v0.3.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect @@ -70,18 +76,22 @@ require ( github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/labstack/echo/v4 v4.9.1 // indirect github.com/labstack/gommon v0.4.0 // indirect + github.com/leodido/go-urn v1.4.0 // indirect github.com/lib/pq v1.10.9 // indirect + github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-sqlite3 v1.14.16 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/ncruces/go-strftime v0.1.9 // indirect github.com/olivere/elastic/v7 v7.0.32 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/pborman/uuid v1.2.1 // indirect + github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_golang v1.16.0 // indirect github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/common v0.44.0 // indirect @@ -90,9 +100,17 @@ require ( github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/robfig/cron v1.2.0 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/viper v1.18.2 // indirect github.com/stretchr/objx v0.5.0 // indirect github.com/stretchr/testify v1.8.4 // indirect + github.com/subosito/gotenv v1.6.0 // indirect github.com/temporalio/ringpop-go v0.0.0-20230606200434-b5c079f412d3 // indirect github.com/temporalio/sqlparser v0.0.0-20231115171017-f4060bcfa6cb // indirect github.com/temporalio/tchannel-go v1.22.1-0.20231116015023-bd4fb7678499 // indirect @@ -140,6 +158,7 @@ require ( google.golang.org/grpc v1.61.0 // indirect google.golang.org/protobuf v1.32.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/validator.v2 v2.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index f0825de..010122d 100644 --- a/go.sum +++ b/go.sum @@ -65,6 +65,8 @@ github.com/crossdock/crossdock-go v0.0.0-20160816171116-049aabb0122b/go.mod h1:v 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/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgryski/go-farm v0.0.0-20140601200337-fc41e106ee0e/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= @@ -85,6 +87,10 @@ github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSw github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= +github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= github.com/go-faker/faker/v4 v4.2.0 h1:dGebOupKwssrODV51E0zbMrv5e2gO9VWSLNC1WDCpWg= github.com/go-faker/faker/v4 v4.2.0/go.mod h1:F/bBy8GH9NxOxMInug5Gx4WYeG6fHJZ8Ol/dhcpRub4= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -99,6 +105,12 @@ github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.18.0 h1:BvolUXjp4zuvkZ5YN5t7ebzbhlUtPsPm2S9NAZ5nl9U= +github.com/go-playground/validator/v10 v10.18.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= @@ -186,6 +198,8 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 h1:/c3QmbOGMGTOumP2iT/rCwB7b0Q github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHgRbTWrTHu6tqPeKxEQv15giM= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI= github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= @@ -235,9 +249,13 @@ github.com/labstack/echo/v4 v4.9.1 h1:GliPYSpzGKlyOhqIbG8nmHBo3i1saKWFOgh41AN3b+ github.com/labstack/echo/v4 v4.9.1/go.mod h1:Pop5HLc+xoc4qhTZ1ip6C0RtP7Z+4VzRLWZZFKqbbjo= github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= @@ -256,6 +274,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zk github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/microsoft/go-mssqldb v0.17.0 h1:Fto83dMZPnYv1Zwx5vHHxpNraeEaUlQ/hhHLgZiaenE= github.com/microsoft/go-mssqldb v0.17.0/go.mod h1:OkoNGhGEs8EZqchVTtochlXruEhEOaO4S0d2sB5aeGQ= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= @@ -272,12 +292,16 @@ github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+ github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw= github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= +github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.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/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prashantv/protectmem v0.0.0-20171002184600-e20412882b3a h1:AA9vgIBDjMHPC2McaGPojgV2dcI78ZC0TLNhYCXEKH8= github.com/prashantv/protectmem v0.0.0-20171002184600-e20412882b3a/go.mod h1:lzZQ3Noex5pfAy7mkAeCjcBDteYU85uWWnJ/y6gKU8k= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -315,6 +339,10 @@ github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzG github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/samuel/go-thrift v0.0.0-20190219015601-e8b6b52668fe/go.mod h1:Vrkh1pnjV9Bl8c3P9zH0/D4NlOHWP5d4/hF4YTULaec= github.com/sirupsen/logrus v1.0.2-0.20170726183946-abee6f9b0679/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= @@ -322,6 +350,16 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= +github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= 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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= @@ -337,6 +375,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/temporalio/ringpop-go v0.0.0-20230606200434-b5c079f412d3 h1:V1U9fvhusDJ1pyAvQWg0+u6mQ+o5WtRfMbnnTIZe0Fo= github.com/temporalio/ringpop-go v0.0.0-20230606200434-b5c079f412d3/go.mod h1:LA2yFb94r5XoEnuMVHkCC/P5174whMy2Dd+cu+AEcQA= github.com/temporalio/sqlparser v0.0.0-20231115171017-f4060bcfa6cb h1:YzHH/U/dN7vMP+glybzcXRTczTrgfdRisNTzAj7La04= @@ -626,6 +666,8 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= diff --git a/internal/config.go b/internal/config.go index 796ca26..d6ab20b 100644 --- a/internal/config.go +++ b/internal/config.go @@ -1,66 +1,125 @@ package internal import ( + "fmt" + "log" "os" "strings" + "time" + + "github.com/go-playground/validator/v10" + "github.com/spf13/viper" ) type Config struct { - PORT string - ROOT_URL string // Needed for oauth2 redirect + Port string `validate:"required"` + RootUrl string `validate:"required,url"` + DatabasePath string `validate:"required"` + SessionSecret string `validate:"required"` - SESSION_SECRET string + OAuth2 OAuth2 `validate:"required"` - OAUTH2_CLIENT_ID string - OAUTH2_CLIENT_SECRET string - OAUTH2_SCOPES []string - OAUTH2_ENDPOINT_TOKEN_URL string - OAUTH2_ENDPOINT_AUTH_URL string - OAUTH2_ENDPOINT_USER_INFO_URL string - OAUTH2_ENDPOINT_LOGOUT_URL string + Temporal Temporal `validate:"required"` - ZDRAVKO_DATABASE_PATH string - - TEMPORAL_DATABASE_PATH string - TEMPORAL_LISTEN_ADDRESS string - TEMPORAL_UI_HOST string - TEMPORAL_SERVER_HOST string + HealthChecks []Healthcheck + CronJobs []CronJob } -func getEnv(key, fallback string) string { - if value, ok := os.LookupEnv(key); ok { - return value - } - return fallback +type OAuth2 struct { + ClientID string `validate:"required"` + ClientSecret string `validate:"required"` + Scopes []string `validate:"required"` + EndpointTokenURL string `validate:"required"` + EndpointAuthURL string `validate:"required"` + EndpointUserInfoURL string `validate:"required"` + EndpointLogoutURL string `validate:"required"` } -func getEnvRequired(key string) string { - if value, ok := os.LookupEnv(key); ok { - return value - } - panic("Environment variable " + key + " is required") +type Temporal struct { + DatabasePath string `validate:"required"` + ListenAddress string `validate:"required"` + UIHost string `validate:"required"` + ServerHost string `validate:"required"` +} + +type HealthCheckHTTP struct { + URL string `validate:"required,url"` + Method string `validate:"required,oneof=GET POST PUT"` +} + +type HealthCheckTCP struct { + Host string `validate:"required,hostname"` + Port int `validate:"required,gte=1,lte=65535"` +} + +type Healthcheck struct { + Name string `validate:"required"` + Retries int `validate:"optional,gte=0"` + Schedule string `validate:"required,cron"` + Timeout time.Duration `validate:"required"` + + HTTP HealthCheckHTTP `validate:"required"` + TCP HealthCheckTCP `validate:"required"` +} + +type CronJob struct { + Name string `validate:"required"` + Schedule string `validate:"required,cron"` + Buffer time.Duration `validate:"required"` } func NewConfig() *Config { - return &Config{ - PORT: getEnv("PORT", "8000"), - ROOT_URL: getEnvRequired("ROOT_URL"), + viper.SetConfigName("zdravko") + viper.SetConfigType("yaml") + viper.AddConfigPath("/etc/zdravko/") + viper.AddConfigPath("$HOME/.zdravko") + viper.AddConfigPath("$HOME/.config/zdravko") + viper.AddConfigPath("$XDG_CONFIG_HOME/zdravko") + viper.AddConfigPath(".") - SESSION_SECRET: getEnvRequired("SESSION_SECRET"), + // Set defaults + viper.SetDefault("port", "8000") + viper.SetDefault("rooturl", "http://localhost:8000") + viper.SetDefault("databasepath", "zdravko.db") + viper.SetDefault("oauth2.scopes", "openid profile email") + viper.SetDefault("temporal.databasepath", "temporal.db") + viper.SetDefault("temporal.listenaddress", "0.0.0.0") + viper.SetDefault("temporal.uihost", "127.0.0.1:8223") + viper.SetDefault("temporal.serverhost", "127.0.0.1:7233") - OAUTH2_CLIENT_ID: getEnvRequired("OAUTH2_CLIENT_ID"), - OAUTH2_CLIENT_SECRET: getEnvRequired("OAUTH2_CLIENT_SECRET"), - OAUTH2_SCOPES: strings.Split(getEnv("OAUTH2_SCOPES", "openid,profile,email"), ","), - OAUTH2_ENDPOINT_TOKEN_URL: getEnvRequired("OAUTH2_ENDPOINT_TOKEN_URL"), - OAUTH2_ENDPOINT_AUTH_URL: getEnvRequired("OAUTH2_ENDPOINT_AUTH_URL"), - OAUTH2_ENDPOINT_USER_INFO_URL: getEnvRequired("OAUTH2_ENDPOINT_USER_INFO_URL"), - OAUTH2_ENDPOINT_LOGOUT_URL: getEnvRequired("OAUTH2_ENDPOINT_LOGOUT_URL"), + // Cant figure out the viper env, so lets just do it manually. + viper.SetDefault("sessionsecret", os.Getenv("SESSION_SECRET")) + viper.SetDefault("oauth2.clientid", os.Getenv("OAUTH2_CLIENT_ID")) + viper.SetDefault("oauth2.clientsecret", os.Getenv("OAUTH2_CLIENT_SECRET")) + viper.SetDefault("oauth2.scope", os.Getenv("OAUTH2_ENDPOINT_SCOPE")) + viper.SetDefault("oauth2.endpointtokenurl", os.Getenv("OAUTH2_ENDPOINT_TOKEN_URL")) + viper.SetDefault("oauth2.endpointauthurl", os.Getenv("OAUTH2_ENDPOINT_AUTH_URL")) + viper.SetDefault("oauth2.endpointuserinfourl", os.Getenv("OAUTH2_ENDPOINT_USER_INFO_URL")) + viper.SetDefault("oauth2.endpointlogouturl", os.Getenv("OAUTH2_ENDPOINT_LOGOUT_URL")) - ZDRAVKO_DATABASE_PATH: getEnv("ZDRAVKO_DATABASE_PATH", "zdravko.db"), - - TEMPORAL_DATABASE_PATH: getEnv("TEMPORAL_DATABASE_PATH", "temporal.db"), - TEMPORAL_LISTEN_ADDRESS: getEnv("TEMPORAL_LISTEN_ADDRESS", "0.0.0.0"), - TEMPORAL_UI_HOST: getEnv("TEMPORAL_UI_HOST", "127.0.0.1:8223"), - TEMPORAL_SERVER_HOST: getEnv("TEMPORAL_SERVER_HOST", "127.0.0.1:7233"), + err := viper.ReadInConfig() + if err != nil { + log.Fatalf("Error reading config file, %s", err) } + log.Println("Config file used: ", viper.ConfigFileUsed()) + + config := &Config{} + err = viper.Unmarshal(config) + if err != nil { + log.Fatalf("Error unmarshalling config, %s", err) + } + + // OAuth2 scopes are space separated + config.OAuth2.Scopes = strings.Split(viper.GetString("oauth2.scopes"), " ") + + // Validate config + validate := validator.New(validator.WithRequiredStructEnabled()) + err = validate.Struct(config) + if err != nil { + log.Fatalf("Error validating config, %s", err) + } + + fmt.Printf("Config: %+v\n", config) + + return config } diff --git a/internal/handlers/handlers.go b/internal/handlers/handlers.go index 67cb443..7ab71b3 100644 --- a/internal/handlers/handlers.go +++ b/internal/handlers/handlers.go @@ -32,7 +32,7 @@ type BaseHandler struct { } func NewBaseHandler(db *gorm.DB, q *query.Query, config *internal.Config) *BaseHandler { - store := sessions.NewCookieStore([]byte(config.SESSION_SECRET)) + store := sessions.NewCookieStore([]byte(config.SessionSecret)) return &BaseHandler{db, q, config, store} } diff --git a/internal/handlers/oauth2.go b/internal/handlers/oauth2.go index 7e789fa..e03dbe8 100644 --- a/internal/handlers/oauth2.go +++ b/internal/handlers/oauth2.go @@ -31,13 +31,13 @@ func newRandomState() string { func newOAuth2(config *internal.Config) *oauth2.Config { return &oauth2.Config{ - ClientID: config.OAUTH2_CLIENT_ID, - ClientSecret: config.OAUTH2_CLIENT_SECRET, - Scopes: config.OAUTH2_SCOPES, - RedirectURL: config.ROOT_URL + "/oauth2/callback", + ClientID: config.OAuth2.ClientID, + ClientSecret: config.OAuth2.ClientSecret, + Scopes: config.OAuth2.Scopes, + RedirectURL: config.RootUrl + "/oauth2/callback", Endpoint: oauth2.Endpoint{ - TokenURL: config.OAUTH2_ENDPOINT_TOKEN_URL, - AuthURL: config.OAUTH2_ENDPOINT_AUTH_URL, + TokenURL: config.OAuth2.EndpointTokenURL, + AuthURL: config.OAuth2.EndpointAuthURL, }, } } @@ -119,7 +119,7 @@ func (h *BaseHandler) OAuth2CallbackGET(w http.ResponseWriter, r *http.Request) // Ge the user information. client := oauth2.NewClient(ctx, oauth2.StaticTokenSource(tok)) - resp, err := client.Get(h.config.OAUTH2_ENDPOINT_USER_INFO_URL) + resp, err := client.Get(h.config.OAuth2.EndpointUserInfoURL) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return @@ -156,7 +156,7 @@ func (h *BaseHandler) OAuth2CallbackGET(w http.ResponseWriter, r *http.Request) func (h *BaseHandler) OAuth2LogoutGET(w http.ResponseWriter, r *http.Request, user *AuthenticatedUser) { tok := h.AuthenticatedUserToOAuth2Token(user) client := oauth2.NewClient(context.Background(), oauth2.StaticTokenSource(tok)) - _, err := client.Get(h.config.OAUTH2_ENDPOINT_USER_INFO_URL) + _, err := client.Get(h.config.OAuth2.EndpointLogoutURL) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return diff --git a/internal/handlers/settings.go b/internal/handlers/settings.go index b18a84c..9e40faa 100644 --- a/internal/handlers/settings.go +++ b/internal/handlers/settings.go @@ -18,6 +18,7 @@ type Settings struct { var SettingsPages = []*components.Page{ {Path: "/settings", Title: "Overview"}, {Path: "/settings/healthchecks", Title: "Healthchecks"}, + {Path: "/settings/cronjobs", Title: "CronJobs"}, {Path: "/settings/workers", Title: "Workers"}, {Path: "/temporal", Title: "Temporal"}, {Path: "/oauth2/logout", Title: "Logout"}, diff --git a/internal/handlers/temporal.go b/internal/handlers/temporal.go index 2c5b988..4f8dc7e 100644 --- a/internal/handlers/temporal.go +++ b/internal/handlers/temporal.go @@ -8,7 +8,7 @@ import ( func (h *BaseHandler) Temporal(w http.ResponseWriter, r *http.Request, user *AuthenticatedUser) { proxy := httputil.NewSingleHostReverseProxy(&url.URL{ - Host: h.config.TEMPORAL_UI_HOST, + Host: h.config.Temporal.UIHost, Scheme: "http", }) diff --git a/pkg/temporal/config.go b/pkg/temporal/config.go index b15bb08..a1f1d85 100644 --- a/pkg/temporal/config.go +++ b/pkg/temporal/config.go @@ -29,7 +29,7 @@ func NewServerConfig(cfg *internal.Config) *config.Config { ConnectAttributes: map[string]string{ "mode": "rwc", }, - DatabaseName: cfg.TEMPORAL_DATABASE_PATH, + DatabaseName: cfg.Temporal.DatabasePath, }, }, }, @@ -50,7 +50,7 @@ func NewServerConfig(cfg *internal.Config) *config.Config { GRPCPort: FrontendPort, MembershipPort: FrontendPort + 100, BindOnLocalHost: false, - BindOnIP: cfg.TEMPORAL_LISTEN_ADDRESS, + BindOnIP: cfg.Temporal.ListenAddress, }, }, "history": { diff --git a/pkg/temporal/ui.go b/pkg/temporal/ui.go index 938e8f3..e3ed271 100644 --- a/pkg/temporal/ui.go +++ b/pkg/temporal/ui.go @@ -9,9 +9,9 @@ import ( func NewUiConfig(cfg *internal.Config) *config.Config { return &config.Config{ - Host: cfg.TEMPORAL_LISTEN_ADDRESS, + Host: cfg.Temporal.ListenAddress, Port: 8223, - TemporalGRPCAddress: cfg.TEMPORAL_SERVER_HOST, + TemporalGRPCAddress: cfg.Temporal.ServerHost, EnableUI: true, PublicPath: "/temporal", Codec: config.Codec{ diff --git a/tools/generate/main.go b/tools/generate/main.go index e1b3b9e..3842184 100644 --- a/tools/generate/main.go +++ b/tools/generate/main.go @@ -16,7 +16,7 @@ func main() { FieldNullable: true, }) - db, _, _ := internal.ConnectToDatabase(config.ZDRAVKO_DATABASE_PATH) + db, _, _ := internal.ConnectToDatabase(config.DatabasePath) // Use the above `*gorm.DB` instance to initialize the generator, // which is required to generate structs from db when using `GenerateModel/GenerateModelAs` diff --git a/web/static/css/main.css b/web/static/css/main.css index 8994e85..d94a7eb 100644 --- a/web/static/css/main.css +++ b/web/static/css/main.css @@ -48,7 +48,7 @@ } .healthchecks { - @apply grid justify-items-stretch justify-stretch items-center mt-20 bg-gray-200 shadow-inner p-5 rounded-lg; + @apply grid justify-items-stretch justify-stretch items-center mt-20 bg-white shadow-md p-5 rounded-lg; } .healthchecks > div:not(:last-child) { diff --git a/web/static/css/tailwind.css b/web/static/css/tailwind.css index 1814501..d4dac62 100644 --- a/web/static/css/tailwind.css +++ b/web/static/css/tailwind.css @@ -590,6 +590,10 @@ video { } } +.relative { + position: relative; +} + .col-span-2 { grid-column: span 2 / span 2; } @@ -603,10 +607,22 @@ video { margin-bottom: 1rem; } +.mb-8 { + margin-bottom: 2rem; +} + .me-2 { margin-inline-end: 0.5rem; } +.ms-2 { + margin-inline-start: 0.5rem; +} + +.mt-1 { + margin-top: 0.25rem; +} + .mt-20 { margin-top: 5rem; } @@ -619,6 +635,14 @@ video { display: flex; } +.inline-flex { + display: inline-flex; +} + +.table { + display: table; +} + .grid { display: grid; } @@ -631,6 +655,10 @@ video { height: 0.75rem; } +.h-3\.5 { + height: 0.875rem; +} + .h-8 { height: 2rem; } @@ -643,10 +671,22 @@ video { width: 0.75rem; } +.w-3\.5 { + width: 0.875rem; +} + +.w-full { + width: 100%; +} + .max-w-screen-md { max-width: 768px; } +.max-w-screen-xl { + max-width: 1280px; +} + .flex-auto { flex: 1 1 auto; } @@ -671,6 +711,10 @@ video { align-items: center; } +.justify-center { + justify-content: center; +} + .gap-2 { gap: 0.5rem; } @@ -683,6 +727,12 @@ video { gap: 1px; } +.space-y-4 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(1rem * var(--tw-space-y-reverse)); +} + .justify-self-start { justify-self: start; } @@ -699,6 +749,14 @@ video { overflow: visible; } +.overflow-x-auto { + overflow-x: auto; +} + +.whitespace-nowrap { + white-space: nowrap; +} + .rounded { border-radius: 0.25rem; } @@ -707,11 +765,29 @@ video { border-radius: 9999px; } +.rounded-lg { + border-radius: 0.5rem; +} + +.border-b { + border-bottom-width: 1px; +} + +.bg-blue-700 { + --tw-bg-opacity: 1; + background-color: rgb(29 78 216 / var(--tw-bg-opacity)); +} + .bg-gray-100 { --tw-bg-opacity: 1; background-color: rgb(243 244 246 / var(--tw-bg-opacity)); } +.bg-gray-50 { + --tw-bg-opacity: 1; + background-color: rgb(249 250 251 / var(--tw-bg-opacity)); +} + .bg-green-300 { --tw-bg-opacity: 1; background-color: rgb(134 239 172 / var(--tw-bg-opacity)); @@ -732,19 +808,77 @@ video { background-color: rgb(248 113 113 / var(--tw-bg-opacity)); } +.bg-white { + --tw-bg-opacity: 1; + background-color: rgb(255 255 255 / var(--tw-bg-opacity)); +} + .p-4 { padding: 1rem; } +.p-5 { + padding: 1.25rem; +} + +.px-4 { + padding-left: 1rem; + padding-right: 1rem; +} + +.px-5 { + padding-left: 1.25rem; + padding-right: 1.25rem; +} + +.px-6 { + padding-left: 1.5rem; + padding-right: 1.5rem; +} + +.py-3 { + padding-top: 0.75rem; + padding-bottom: 0.75rem; +} + +.py-4 { + padding-top: 1rem; + padding-bottom: 1rem; +} + +.py-8 { + padding-top: 2rem; + padding-bottom: 2rem; +} + +.text-left { + text-align: left; +} + .text-center { text-align: center; } +.text-2xl { + font-size: 1.5rem; + line-height: 2rem; +} + .text-3xl { font-size: 1.875rem; line-height: 2.25rem; } +.text-base { + font-size: 1rem; + line-height: 1.5rem; +} + +.text-lg { + font-size: 1.125rem; + line-height: 1.75rem; +} + .text-sm { font-size: 0.875rem; line-height: 1.25rem; @@ -759,6 +893,34 @@ video { font-weight: 700; } +.font-extrabold { + font-weight: 800; +} + +.font-medium { + font-weight: 500; +} + +.font-normal { + font-weight: 400; +} + +.font-semibold { + font-weight: 600; +} + +.uppercase { + text-transform: uppercase; +} + +.leading-none { + line-height: 1; +} + +.tracking-tight { + letter-spacing: -0.025em; +} + .text-blue-600 { --tw-text-opacity: 1; color: rgb(37 99 235 / var(--tw-text-opacity)); @@ -769,6 +931,16 @@ video { color: rgb(107 114 128 / var(--tw-text-opacity)); } +.text-gray-700 { + --tw-text-opacity: 1; + color: rgb(55 65 81 / var(--tw-text-opacity)); +} + +.text-gray-900 { + --tw-text-opacity: 1; + color: rgb(17 24 39 / var(--tw-text-opacity)); +} + .text-red-600 { --tw-text-opacity: 1; color: rgb(220 38 38 / var(--tw-text-opacity)); @@ -779,10 +951,21 @@ video { color: rgb(100 116 139 / var(--tw-text-opacity)); } +.text-white { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); +} + .underline { text-decoration-line: underline; } +.shadow-md { + --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + .navbar { margin-top: 2.5rem; display: flex; @@ -900,10 +1083,10 @@ video { justify-items: stretch; border-radius: 0.5rem; --tw-bg-opacity: 1; - background-color: rgb(229 231 235 / var(--tw-bg-opacity)); + background-color: rgb(255 255 255 / var(--tw-bg-opacity)); padding: 1.25rem; - --tw-shadow: inset 0 2px 4px 0 rgb(0 0 0 / 0.05); - --tw-shadow-colored: inset 0 2px 4px 0 var(--tw-shadow-color); + --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); } @@ -911,6 +1094,21 @@ video { margin-bottom: 0.75rem; } +.odd\:bg-white:nth-child(odd) { + --tw-bg-opacity: 1; + background-color: rgb(255 255 255 / var(--tw-bg-opacity)); +} + +.even\:bg-gray-50:nth-child(even) { + --tw-bg-opacity: 1; + background-color: rgb(249 250 251 / var(--tw-bg-opacity)); +} + +.hover\:bg-blue-800:hover { + --tw-bg-opacity: 1; + background-color: rgb(30 64 175 / var(--tw-bg-opacity)); +} + .hover\:bg-green-500:hover { --tw-bg-opacity: 1; background-color: rgb(34 197 94 / var(--tw-bg-opacity)); @@ -924,3 +1122,79 @@ video { .hover\:underline:hover { text-decoration-line: underline; } + +.focus\:ring-4:focus { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); +} + +.focus\:ring-blue-300:focus { + --tw-ring-opacity: 1; + --tw-ring-color: rgb(147 197 253 / var(--tw-ring-opacity)); +} + +@media (min-width: 640px) { + .sm\:flex-row { + flex-direction: row; + } + + .sm\:justify-center { + justify-content: center; + } + + .sm\:space-y-0 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0px * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0px * var(--tw-space-y-reverse)); + } + + .sm\:rounded-lg { + border-radius: 0.5rem; + } + + .sm\:px-8 { + padding-left: 2rem; + padding-right: 2rem; + } +} + +@media (min-width: 768px) { + .md\:text-3xl { + font-size: 1.875rem; + line-height: 2.25rem; + } +} + +@media (min-width: 1024px) { + .lg\:px-40 { + padding-left: 10rem; + padding-right: 10rem; + } + + .lg\:py-16 { + padding-top: 4rem; + padding-bottom: 4rem; + } + + .lg\:text-4xl { + font-size: 2.25rem; + line-height: 2.5rem; + } +} + +.rtl\:rotate-180:where([dir="rtl"], [dir="rtl"] *) { + --tw-rotate: 180deg; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.rtl\:text-right:where([dir="rtl"], [dir="rtl"] *) { + text-align: right; +} + +@media (prefers-color-scheme: dark) { + .dark\:text-gray-400 { + --tw-text-opacity: 1; + color: rgb(156 163 175 / var(--tw-text-opacity)); + } +} diff --git a/web/templates/pages/settings_healthchecks.tmpl b/web/templates/pages/settings_healthchecks.tmpl index 76fba81..264806e 100644 --- a/web/templates/pages/settings_healthchecks.tmpl +++ b/web/templates/pages/settings_healthchecks.tmpl @@ -1,5 +1,142 @@ {{define "settings"}} -

Hello, {{.User.Email}}

-

Imagine you see a list of healthchecks and how to configure them.

+{{ $description := "Healthchecks represent periodic checks of some HTTP or TCP service, to see if it's responding correctly to deterime if it's healthy or not." }} + +

+
+

+ There are no healthchecks yet. +

+

+ {{ $description }} +

+ +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ List of Healthchecks +

+ Healthchecks represent periodic checks of some HTTP or TCP service, to see if it's + responding correctly to deterime if it's healthy or not. +

+
+ Name + + Type + + Status + + Cron + + Action +
+ Apple MacBook Pro 17" + + Silver + + Laptop + + $2999 + + Details +
+ Microsoft Surface Pro + + White + + Laptop PC + + $1999 + + Details +
+ Magic Mouse 2 + + Black + + Accessories + + $99 + + Details +
+ Google Pixel Phone + + Gray + + Phone + + $799 + + Details +
+ Apple Watch 5 + + Red + + Wearables + + $999 + + Details +
+
{{end}} diff --git a/web/templates/pages/settings_overview.tmpl b/web/templates/pages/settings_overview.tmpl index de3129d..03f15bc 100644 --- a/web/templates/pages/settings_overview.tmpl +++ b/web/templates/pages/settings_overview.tmpl @@ -1,3 +1,14 @@ {{define "settings"}} -

Hello, {{.User.Email}}

+
+
+

+ Hi there, {{.User.Email}}. +

+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +

+
+
+ + {{end}} diff --git a/zdravko.yaml b/zdravko.yaml new file mode 100644 index 0000000..c133d7f --- /dev/null +++ b/zdravko.yaml @@ -0,0 +1,27 @@ +healthchecks: + - name: "Google" + http: + url: "https://www.google.com" + method: GET + timeout: 5s + schedule: "* * * * *" + retries: 3 + - name: "GitHub" + http: + url: "https://www.github.com" + method: GET + timeout: 5s + schedule: "* * * * *" + retries: 3 + - name: "Docker" + tcp: + hostname: "docker.com" + port: 443 + schedule: "* * * * *" + timeout: 60s + retries: 3 + +cronjobs: + - name: "Backup" + schedule: "0 0 * * *" + buffer: 1h