Compare commits
No commits in common. "01073aca78cfd7ffcf0454439ef040afa1a19070" and "75442d261d00c4032e2dd95a94fa6f73d07202b5" have entirely different histories.
01073aca78
...
75442d261d
@ -5,9 +5,7 @@ import (
|
|||||||
"git.eeqj.de/sneak/gohttpserver/internal/database"
|
"git.eeqj.de/sneak/gohttpserver/internal/database"
|
||||||
"git.eeqj.de/sneak/gohttpserver/internal/globals"
|
"git.eeqj.de/sneak/gohttpserver/internal/globals"
|
||||||
"git.eeqj.de/sneak/gohttpserver/internal/handlers"
|
"git.eeqj.de/sneak/gohttpserver/internal/handlers"
|
||||||
"git.eeqj.de/sneak/gohttpserver/internal/healthcheck"
|
|
||||||
"git.eeqj.de/sneak/gohttpserver/internal/logger"
|
"git.eeqj.de/sneak/gohttpserver/internal/logger"
|
||||||
"git.eeqj.de/sneak/gohttpserver/internal/middleware"
|
|
||||||
"git.eeqj.de/sneak/gohttpserver/internal/server"
|
"git.eeqj.de/sneak/gohttpserver/internal/server"
|
||||||
"go.uber.org/fx"
|
"go.uber.org/fx"
|
||||||
)
|
)
|
||||||
@ -31,8 +29,6 @@ func main() {
|
|||||||
handlers.New,
|
handlers.New,
|
||||||
logger.New,
|
logger.New,
|
||||||
server.New,
|
server.New,
|
||||||
middleware.New,
|
|
||||||
healthcheck.New,
|
|
||||||
),
|
),
|
||||||
fx.Invoke(func(*server.Server) {}),
|
fx.Invoke(func(*server.Server) {}),
|
||||||
).Run()
|
).Run()
|
||||||
|
5
go.mod
5
go.mod
@ -12,8 +12,6 @@ require (
|
|||||||
github.com/rs/zerolog v1.28.0
|
github.com/rs/zerolog v1.28.0
|
||||||
github.com/slok/go-http-metrics v0.10.0
|
github.com/slok/go-http-metrics v0.10.0
|
||||||
github.com/spf13/viper v1.14.0
|
github.com/spf13/viper v1.14.0
|
||||||
go.uber.org/fx v1.18.2
|
|
||||||
google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e
|
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@ -39,12 +37,11 @@ require (
|
|||||||
github.com/subosito/gotenv v1.4.1 // indirect
|
github.com/subosito/gotenv v1.4.1 // indirect
|
||||||
go.uber.org/atomic v1.9.0 // indirect
|
go.uber.org/atomic v1.9.0 // indirect
|
||||||
go.uber.org/dig v1.15.0 // indirect
|
go.uber.org/dig v1.15.0 // indirect
|
||||||
|
go.uber.org/fx v1.18.2 // indirect
|
||||||
go.uber.org/multierr v1.8.0 // indirect
|
go.uber.org/multierr v1.8.0 // indirect
|
||||||
go.uber.org/zap v1.21.0 // indirect
|
go.uber.org/zap v1.21.0 // indirect
|
||||||
golang.org/x/net v0.0.0-20221014081412-f15817d10f9b // indirect
|
|
||||||
golang.org/x/sys v0.2.0 // indirect
|
golang.org/x/sys v0.2.0 // indirect
|
||||||
golang.org/x/text v0.4.0 // indirect
|
golang.org/x/text v0.4.0 // indirect
|
||||||
google.golang.org/grpc v1.50.1 // indirect
|
|
||||||
google.golang.org/protobuf v1.28.1 // indirect
|
google.golang.org/protobuf v1.28.1 // indirect
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
|
5
go.sum
5
go.sum
@ -380,7 +380,6 @@ golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qx
|
|||||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
golang.org/x/net v0.0.0-20221014081412-f15817d10f9b h1:tvrvnPFcdzp294diPnrdZZZ8XUt2Tyj7svb7X52iDuU=
|
golang.org/x/net v0.0.0-20221014081412-f15817d10f9b h1:tvrvnPFcdzp294diPnrdZZZ8XUt2Tyj7svb7X52iDuU=
|
||||||
golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
@ -585,8 +584,6 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D
|
|||||||
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e h1:S9GbmC1iCgvbLyAokVCwiO6tVIrU9Y7c5oMx1V/ki/Y=
|
|
||||||
google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s=
|
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
@ -603,8 +600,6 @@ google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM
|
|||||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||||
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
||||||
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||||
google.golang.org/grpc v1.50.1 h1:DS/BukOZWp8s6p4Dt/tOaJaTQyPyOoCcrjroHuCeLzY=
|
|
||||||
google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
|
@ -20,23 +20,19 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type ConfigParams struct {
|
type ConfigParams struct {
|
||||||
fx.In
|
Globals globals.Globals
|
||||||
Globals *globals.Globals
|
Logger logger.Logger
|
||||||
Logger *logger.Logger
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
DBURL string
|
DBURL string
|
||||||
Debug bool
|
Debug bool
|
||||||
MaintenanceMode bool
|
MaintenanceMode bool
|
||||||
DevelopmentMode bool
|
|
||||||
DevAdminUsername string
|
|
||||||
DevAdminPassword string
|
|
||||||
MetricsPassword string
|
MetricsPassword string
|
||||||
MetricsUsername string
|
MetricsUsername string
|
||||||
Port int
|
Port int
|
||||||
SentryDSN string
|
SentryDSN string
|
||||||
params *ConfigParams
|
params ConfigParams
|
||||||
log *zerolog.Logger
|
log *zerolog.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,9 +51,6 @@ func New(lc fx.Lifecycle, params ConfigParams) (*Config, error) {
|
|||||||
|
|
||||||
viper.SetDefault("DEBUG", "false")
|
viper.SetDefault("DEBUG", "false")
|
||||||
viper.SetDefault("MAINTENANCE_MODE", "false")
|
viper.SetDefault("MAINTENANCE_MODE", "false")
|
||||||
viper.SetDefault("DEVELOPMENT_MODE", "false")
|
|
||||||
viper.SetDefault("DEV_ADMIN_USERNAME", "")
|
|
||||||
viper.SetDefault("DEV_ADMIN_PASSWORD", "")
|
|
||||||
viper.SetDefault("PORT", "8080")
|
viper.SetDefault("PORT", "8080")
|
||||||
viper.SetDefault("DBURL", "")
|
viper.SetDefault("DBURL", "")
|
||||||
viper.SetDefault("SENTRY_DSN", "")
|
viper.SetDefault("SENTRY_DSN", "")
|
||||||
@ -81,18 +74,9 @@ func New(lc fx.Lifecycle, params ConfigParams) (*Config, error) {
|
|||||||
Port: viper.GetInt("PORT"),
|
Port: viper.GetInt("PORT"),
|
||||||
SentryDSN: viper.GetString("SENTRY_DSN"),
|
SentryDSN: viper.GetString("SENTRY_DSN"),
|
||||||
MaintenanceMode: viper.GetBool("MAINTENANCE_MODE"),
|
MaintenanceMode: viper.GetBool("MAINTENANCE_MODE"),
|
||||||
DevelopmentMode: viper.GetBool("DEVELOPMENT_MODE"),
|
|
||||||
DevAdminUsername: viper.GetString("DEV_ADMIN_USERNAME"),
|
|
||||||
DevAdminPassword: viper.GetString("DEV_ADMIN_PASSWORD"),
|
|
||||||
MetricsUsername: viper.GetString("METRICS_USERNAME"),
|
MetricsUsername: viper.GetString("METRICS_USERNAME"),
|
||||||
MetricsPassword: viper.GetString("METRICS_PASSWORD"),
|
MetricsPassword: viper.GetString("METRICS_PASSWORD"),
|
||||||
log: log,
|
log: log,
|
||||||
params: ¶ms,
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.Debug {
|
|
||||||
params.Logger.EnableDebugLogging()
|
|
||||||
s.log = params.Logger.Get()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return s, nil
|
return s, nil
|
||||||
|
@ -20,32 +20,29 @@ import (
|
|||||||
|
|
||||||
type DatabaseParams struct {
|
type DatabaseParams struct {
|
||||||
fx.In
|
fx.In
|
||||||
Logger *logger.Logger
|
Logger logger.Logger
|
||||||
Config *config.Config
|
Config config.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
type Database struct {
|
type Database struct {
|
||||||
URL string
|
URL string
|
||||||
log *zerolog.Logger
|
log *zerolog.Logger
|
||||||
params *DatabaseParams
|
params DatabaseParams
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(lc fx.Lifecycle, params DatabaseParams) (*Database, error) {
|
func New(lc fx.Lifecycle, params DatabaseParams) (*Database, error) {
|
||||||
s := new(Database)
|
|
||||||
s.params = ¶ms
|
|
||||||
s.log = params.Logger.Get()
|
|
||||||
|
|
||||||
s.log.Info().Msg("Database instantiated")
|
s.log.Info().Msg("Database instantiated")
|
||||||
|
s := new(Database)
|
||||||
|
s.params = params
|
||||||
|
s.log = params.Logger.Get()
|
||||||
|
|
||||||
lc.Append(fx.Hook{
|
lc.Append(fx.Hook{
|
||||||
OnStart: func(ctx context.Context) error {
|
OnStart: func(ctx context.Context) error {
|
||||||
s.log.Info().Msg("Database OnStart Hook")
|
s.log.Info().Msg("Database OnStart Hook")
|
||||||
// FIXME connect to db
|
// FIXME connect to db
|
||||||
return nil
|
|
||||||
},
|
},
|
||||||
OnStop: func(ctx context.Context) error {
|
OnStop: func(ctx context.Context) error {
|
||||||
// FIXME disconnect from db
|
// FIXME disconnect from db
|
||||||
return nil
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
return s, nil
|
return s, nil
|
||||||
|
30
internal/handlers/handlehealthcheck.go
Normal file
30
internal/handlers/handlehealthcheck.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Handlers) handleHealthCheck() http.HandlerFunc {
|
||||||
|
type response struct {
|
||||||
|
Status string `json:"status"`
|
||||||
|
Now string `json:"now"`
|
||||||
|
UptimeSeconds int64 `json:"uptime_seconds"`
|
||||||
|
UptimeHuman string `json:"uptime_human"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
Appname string `json:"appname"`
|
||||||
|
Maintenance bool `json:"maintenance_mode"`
|
||||||
|
}
|
||||||
|
return func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
resp := &response{
|
||||||
|
Status: "ok",
|
||||||
|
Now: time.Now().UTC().Format(time.RFC3339Nano),
|
||||||
|
UptimeSeconds: int64(s.uptime().Seconds()),
|
||||||
|
UptimeHuman: s.uptime().String(),
|
||||||
|
Maintenance: s.maintenance(),
|
||||||
|
Appname: s.appname,
|
||||||
|
Version: s.version,
|
||||||
|
}
|
||||||
|
s.respondJSON(w, req, resp, 200)
|
||||||
|
}
|
||||||
|
}
|
@ -1,18 +1,19 @@
|
|||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"html/template"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"git.eeqj.de/sneak/gohttpserver/templates"
|
"git.eeqj.de/sneak/gohttpserver/templates"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Handlers) HandleIndex() http.HandlerFunc {
|
func (s *Handlers) HandleIndex() http.HandlerFunc {
|
||||||
t := templates.GetParsed()
|
indexTemplate := template.Must(template.New("index").Parse(templates.MustString("index.html")))
|
||||||
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
err := t.ExecuteTemplate(w, "index.html", nil)
|
err := indexTemplate.ExecuteTemplate(w, "index", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Error().Err(err).Msg("")
|
s.log.Println(err.Error())
|
||||||
http.Error(w, http.StatusText(500), 500)
|
http.Error(w, http.StatusText(500), 500)
|
||||||
}
|
}
|
||||||
}
|
}
|
12
internal/handlers/handlelogin.go
Normal file
12
internal/handlers/handlelogin.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Handlers) HandleLogin() http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fmt.Fprintf(w, "hello login")
|
||||||
|
}
|
||||||
|
}
|
@ -4,43 +4,47 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
"git.eeqj.de/sneak/gohttpserver/internal/database"
|
|
||||||
"git.eeqj.de/sneak/gohttpserver/internal/globals"
|
"git.eeqj.de/sneak/gohttpserver/internal/globals"
|
||||||
"git.eeqj.de/sneak/gohttpserver/internal/healthcheck"
|
|
||||||
"git.eeqj.de/sneak/gohttpserver/internal/logger"
|
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"go.uber.org/fx"
|
"go.uber.org/fx"
|
||||||
|
"google.golang.org/genproto/googleapis/spanner/admin/database/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type HandlersParams struct {
|
type HandlersParams struct {
|
||||||
fx.In
|
fx.In
|
||||||
Logger *logger.Logger
|
Logger *zerolog.Logger
|
||||||
Globals *globals.Globals
|
Globals globals.Globals
|
||||||
Database *database.Database
|
Database database.Database
|
||||||
Healthcheck *healthcheck.Healthcheck
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Handlers struct {
|
type Handlers struct {
|
||||||
params *HandlersParams
|
params HandlersParams
|
||||||
log *zerolog.Logger
|
log *zerolog.Logger
|
||||||
hc *healthcheck.Healthcheck
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(lc fx.Lifecycle, params HandlersParams) (*Handlers, error) {
|
func New(lc fx.Lifecycle, params HandlersParams) (*Handlers, error) {
|
||||||
s := new(Handlers)
|
s := new(Handlers)
|
||||||
s.params = ¶ms
|
s.params = params
|
||||||
s.log = params.Logger.Get()
|
s.log = params.Logger.Get()
|
||||||
s.hc = params.Healthcheck
|
|
||||||
lc.Append(fx.Hook{
|
lc.Append(fx.Hook{
|
||||||
OnStart: func(ctx context.Context) error {
|
OnStart: func(ctx context.Context) error {
|
||||||
// FIXME compile some templates here or something
|
// FIXME compile some templates here or something
|
||||||
return nil
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Handlers) HandleNow() http.HandlerFunc {
|
||||||
|
type response struct {
|
||||||
|
Now time.Time `json:"now"`
|
||||||
|
}
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
s.respondJSON(w, r, &response{Now: time.Now()}, 200)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Handlers) respondJSON(w http.ResponseWriter, r *http.Request, data interface{}, status int) {
|
func (s *Handlers) respondJSON(w http.ResponseWriter, r *http.Request, data interface{}, status int) {
|
||||||
w.WriteHeader(status)
|
w.WriteHeader(status)
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
package handlers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Handlers) HandleHealthCheck() http.HandlerFunc {
|
|
||||||
return func(w http.ResponseWriter, req *http.Request) {
|
|
||||||
resp := s.hc.Healthcheck()
|
|
||||||
s.respondJSON(w, req, resp, 200)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
package handlers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"git.eeqj.de/sneak/gohttpserver/templates"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Handlers) HandleLoginGET() http.HandlerFunc {
|
|
||||||
t := templates.GetParsed()
|
|
||||||
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
err := t.ExecuteTemplate(w, "login.html", nil)
|
|
||||||
if err != nil {
|
|
||||||
s.log.Error().Err(err).Msg("")
|
|
||||||
http.Error(w, http.StatusText(500), 500)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
package handlers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"git.eeqj.de/sneak/gohttpserver/templates"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s *Handlers) HandleSignupGET() http.HandlerFunc {
|
|
||||||
t := templates.GetParsed()
|
|
||||||
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
err := t.ExecuteTemplate(w, "signup.html", nil)
|
|
||||||
if err != nil {
|
|
||||||
s.log.Error().Err(err).Msg("")
|
|
||||||
http.Error(w, http.StatusText(500), 500)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Handlers) HandleSignupPOST() http.HandlerFunc {
|
|
||||||
t := templates.GetParsed()
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
|
|
||||||
_ = r.ParseForm()
|
|
||||||
|
|
||||||
err := t.ExecuteTemplate(w, "signup.html", nil)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
s.log.Error().Err(err).Msg("")
|
|
||||||
http.Error(w, http.StatusText(500), 500)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,71 +0,0 @@
|
|||||||
package healthcheck
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"git.eeqj.de/sneak/gohttpserver/internal/config"
|
|
||||||
"git.eeqj.de/sneak/gohttpserver/internal/database"
|
|
||||||
"git.eeqj.de/sneak/gohttpserver/internal/globals"
|
|
||||||
"git.eeqj.de/sneak/gohttpserver/internal/logger"
|
|
||||||
"github.com/rs/zerolog"
|
|
||||||
"go.uber.org/fx"
|
|
||||||
)
|
|
||||||
|
|
||||||
type HealthcheckParams struct {
|
|
||||||
fx.In
|
|
||||||
Globals *globals.Globals
|
|
||||||
Config *config.Config
|
|
||||||
Logger *logger.Logger
|
|
||||||
Database *database.Database
|
|
||||||
}
|
|
||||||
|
|
||||||
type Healthcheck struct {
|
|
||||||
StartupTime time.Time
|
|
||||||
log *zerolog.Logger
|
|
||||||
params *HealthcheckParams
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(lc fx.Lifecycle, params HealthcheckParams) (*Healthcheck, error) {
|
|
||||||
s := new(Healthcheck)
|
|
||||||
s.params = ¶ms
|
|
||||||
s.log = params.Logger.Get()
|
|
||||||
|
|
||||||
lc.Append(fx.Hook{
|
|
||||||
OnStart: func(ctx context.Context) error {
|
|
||||||
s.StartupTime = time.Now()
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
OnStop: func(ctx context.Context) error {
|
|
||||||
// FIXME do server shutdown here
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
})
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type HealthcheckResponse struct {
|
|
||||||
Status string `json:"status"`
|
|
||||||
Now string `json:"now"`
|
|
||||||
UptimeSeconds int64 `json:"uptime_seconds"`
|
|
||||||
UptimeHuman string `json:"uptime_human"`
|
|
||||||
Version string `json:"version"`
|
|
||||||
Appname string `json:"appname"`
|
|
||||||
Maintenance bool `json:"maintenance_mode"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Healthcheck) uptime() time.Duration {
|
|
||||||
return time.Since(s.StartupTime)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Healthcheck) Healthcheck() *HealthcheckResponse {
|
|
||||||
resp := &HealthcheckResponse{
|
|
||||||
Status: "ok",
|
|
||||||
Now: time.Now().UTC().Format(time.RFC3339Nano),
|
|
||||||
UptimeSeconds: int64(s.uptime().Seconds()),
|
|
||||||
UptimeHuman: s.uptime().String(),
|
|
||||||
Appname: s.params.Globals.Appname,
|
|
||||||
Version: s.params.Globals.Version,
|
|
||||||
}
|
|
||||||
return resp
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
package logger
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
@ -8,11 +8,13 @@ import (
|
|||||||
"git.eeqj.de/sneak/gohttpserver/internal/globals"
|
"git.eeqj.de/sneak/gohttpserver/internal/globals"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"go.uber.org/fx"
|
"go.uber.org/fx"
|
||||||
|
"honnef.co/go/tools/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
type LoggerParams struct {
|
type LoggerParams struct {
|
||||||
fx.In
|
fx.In
|
||||||
Globals *globals.Globals
|
Globals globals.Globals
|
||||||
|
Config config.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
type Logger struct {
|
type Logger struct {
|
||||||
@ -76,12 +78,12 @@ func New(lc fx.Lifecycle, params LoggerParams) (*Logger, error) {
|
|||||||
l.log = &logger
|
l.log = &logger
|
||||||
// log.Logger = logger
|
// log.Logger = logger
|
||||||
|
|
||||||
return l, nil
|
if l.params.Config.Debug {
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Logger) EnableDebugLogging() {
|
|
||||||
zerolog.SetGlobalLevel(zerolog.DebugLevel)
|
zerolog.SetGlobalLevel(zerolog.DebugLevel)
|
||||||
l.log.Debug().Bool("debug", true).Send()
|
l.log.Debug().Bool("debug", true).Send()
|
||||||
|
}
|
||||||
|
|
||||||
|
return l, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Logger) Get() *zerolog.Logger {
|
func (l *Logger) Get() *zerolog.Logger {
|
||||||
|
@ -1,43 +1,19 @@
|
|||||||
package middleware
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.eeqj.de/sneak/gohttpserver/internal/config"
|
|
||||||
"git.eeqj.de/sneak/gohttpserver/internal/globals"
|
|
||||||
"git.eeqj.de/sneak/gohttpserver/internal/logger"
|
|
||||||
basicauth "github.com/99designs/basicauth-go"
|
basicauth "github.com/99designs/basicauth-go"
|
||||||
"github.com/go-chi/chi/middleware"
|
"github.com/go-chi/chi/middleware"
|
||||||
"github.com/go-chi/cors"
|
"github.com/go-chi/cors"
|
||||||
"github.com/rs/zerolog"
|
|
||||||
metrics "github.com/slok/go-http-metrics/metrics/prometheus"
|
metrics "github.com/slok/go-http-metrics/metrics/prometheus"
|
||||||
ghmm "github.com/slok/go-http-metrics/middleware"
|
ghmm "github.com/slok/go-http-metrics/middleware"
|
||||||
"github.com/slok/go-http-metrics/middleware/std"
|
"github.com/slok/go-http-metrics/middleware/std"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"go.uber.org/fx"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type MiddlewareParams struct {
|
|
||||||
fx.In
|
|
||||||
Logger *logger.Logger
|
|
||||||
Globals *globals.Globals
|
|
||||||
Config *config.Config
|
|
||||||
}
|
|
||||||
|
|
||||||
type Middleware struct {
|
|
||||||
log *zerolog.Logger
|
|
||||||
params *MiddlewareParams
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(lc fx.Lifecycle, params MiddlewareParams) (*Middleware, error) {
|
|
||||||
s := new(Middleware)
|
|
||||||
s.params = ¶ms
|
|
||||||
s.log = params.Logger.Get()
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// the following is from
|
// the following is from
|
||||||
// https://learning-cloud-native-go.github.io/docs/a6.adding_zerolog_logger/
|
// https://learning-cloud-native-go.github.io/docs/a6.adding_zerolog_logger/
|
||||||
|
|
||||||
@ -69,7 +45,7 @@ func (lrw *loggingResponseWriter) WriteHeader(code int) {
|
|||||||
// type Middleware func(http.Handler) http.Handler
|
// type Middleware func(http.Handler) http.Handler
|
||||||
// this returns a Middleware that is designed to do every request through the
|
// this returns a Middleware that is designed to do every request through the
|
||||||
// mux, note the signature:
|
// mux, note the signature:
|
||||||
func (s *Middleware) Logging() func(http.Handler) http.Handler {
|
func (s *Server) LoggingMiddleware() func(http.Handler) http.Handler {
|
||||||
// FIXME this should use https://github.com/google/go-cloud/blob/master/server/requestlog/requestlog.go
|
// FIXME this should use https://github.com/google/go-cloud/blob/master/server/requestlog/requestlog.go
|
||||||
return func(next http.Handler) http.Handler {
|
return func(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -97,7 +73,7 @@ func (s *Middleware) Logging() func(http.Handler) http.Handler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Middleware) CORS() func(http.Handler) http.Handler {
|
func (s *Server) CORSMiddleware() func(http.Handler) http.Handler {
|
||||||
return cors.Handler(cors.Options{
|
return cors.Handler(cors.Options{
|
||||||
// CHANGEME! these are defaults, change them to suit your needs or
|
// CHANGEME! these are defaults, change them to suit your needs or
|
||||||
// read from environment/viper.
|
// read from environment/viper.
|
||||||
@ -112,7 +88,7 @@ func (s *Middleware) CORS() func(http.Handler) http.Handler {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Middleware) Auth() func(http.Handler) http.Handler {
|
func (s *Server) AuthMiddleware() func(http.Handler) http.Handler {
|
||||||
return func(next http.Handler) http.Handler {
|
return func(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
// CHANGEME you'll want to change this to do stuff.
|
// CHANGEME you'll want to change this to do stuff.
|
||||||
@ -122,7 +98,7 @@ func (s *Middleware) Auth() func(http.Handler) http.Handler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Middleware) Metrics() func(http.Handler) http.Handler {
|
func (s *Server) MetricsMiddleware() func(http.Handler) http.Handler {
|
||||||
mdlw := ghmm.New(ghmm.Config{
|
mdlw := ghmm.New(ghmm.Config{
|
||||||
Recorder: metrics.NewRecorder(metrics.Config{}),
|
Recorder: metrics.NewRecorder(metrics.Config{}),
|
||||||
})
|
})
|
||||||
@ -131,7 +107,7 @@ func (s *Middleware) Metrics() func(http.Handler) http.Handler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Middleware) MetricsAuth() func(http.Handler) http.Handler {
|
func (s *Server) MetricsAuthMiddleware() func(http.Handler) http.Handler {
|
||||||
return basicauth.New(
|
return basicauth.New(
|
||||||
"metrics",
|
"metrics",
|
||||||
map[string][]string{
|
map[string][]string{
|
@ -7,7 +7,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (s *Server) serveUntilShutdown() {
|
func (s *Server) serveUntilShutdown() {
|
||||||
listenAddr := fmt.Sprintf(":%d", s.params.Config.Port)
|
listenAddr := fmt.Sprintf(":%d", s.port)
|
||||||
s.httpServer = &http.Server{
|
s.httpServer = &http.Server{
|
||||||
Addr: listenAddr,
|
Addr: listenAddr,
|
||||||
ReadTimeout: 10 * time.Second,
|
ReadTimeout: 10 * time.Second,
|
||||||
@ -18,7 +18,7 @@ func (s *Server) serveUntilShutdown() {
|
|||||||
|
|
||||||
// add routes
|
// add routes
|
||||||
// this does any necessary setup in each handler
|
// this does any necessary setup in each handler
|
||||||
s.SetupRoutes()
|
s.routes()
|
||||||
|
|
||||||
s.log.Info().Str("listenaddr", listenAddr).Msg("http begin listen")
|
s.log.Info().Str("listenaddr", listenAddr).Msg("http begin listen")
|
||||||
if err := s.httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
if err := s.httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||||
|
@ -23,16 +23,16 @@ func (s *Server) SetupRoutes() {
|
|||||||
|
|
||||||
s.router.Use(middleware.Recoverer)
|
s.router.Use(middleware.Recoverer)
|
||||||
s.router.Use(middleware.RequestID)
|
s.router.Use(middleware.RequestID)
|
||||||
s.router.Use(s.mw.Logging())
|
s.router.Use(s.LoggingMiddleware())
|
||||||
|
|
||||||
// add metrics middleware only if we can serve them behind auth
|
// add metrics middleware only if we can serve them behind auth
|
||||||
if viper.GetString("METRICS_USERNAME") != "" {
|
if viper.GetString("METRICS_USERNAME") != "" {
|
||||||
s.router.Use(s.mw.Metrics())
|
s.router.Use(s.MetricsMiddleware())
|
||||||
}
|
}
|
||||||
|
|
||||||
// set up CORS headers. you'll probably want to configure that
|
// set up CORS headers. you'll probably want to configure that
|
||||||
// in middlewares.go.
|
// in middlewares.go.
|
||||||
s.router.Use(s.mw.CORS())
|
s.router.Use(s.CORSMiddleware())
|
||||||
|
|
||||||
// CHANGEME to suit your needs, or pull from config.
|
// CHANGEME to suit your needs, or pull from config.
|
||||||
// timeout for request context; your handlers must finish within
|
// timeout for request context; your handlers must finish within
|
||||||
@ -57,48 +57,39 @@ func (s *Server) SetupRoutes() {
|
|||||||
// complete docs: https://github.com/go-chi/chi
|
// complete docs: https://github.com/go-chi/chi
|
||||||
////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
s.router.Get("/", s.h.HandleIndex())
|
s.router.Get("/", s.handleIndex())
|
||||||
|
|
||||||
s.router.Mount("/s", http.StripPrefix("/s", http.FileServer(http.FS(static.Static))))
|
s.router.Mount("/s", http.StripPrefix("/s", http.FileServer(http.FS(static.Static))))
|
||||||
|
|
||||||
s.router.Route("/api/v1", func(r chi.Router) {
|
s.router.Route("/api/v1", func(r chi.Router) {
|
||||||
r.Get("/now", s.h.HandleNow())
|
r.Get("/now", s.handleNow())
|
||||||
})
|
})
|
||||||
|
|
||||||
// if you want to use a general purpose middleware (http.Handler
|
// if you want to use a general purpose middleware (http.Handler
|
||||||
// wrapper) on a specific HandleFunc route, you need to take the
|
// wrapper) on a specific HandleFunc route, you need to take the
|
||||||
// .ServeHTTP of the http.Handler to get its HandleFunc, viz:
|
// .ServeHTTP of the http.Handler to get its HandleFunc, viz:
|
||||||
auth := s.mw.Auth()
|
authMiddleware := s.AuthMiddleware()
|
||||||
s.router.Get(
|
s.router.Get(
|
||||||
"/login",
|
"/login",
|
||||||
auth(s.h.HandleLoginGET()).ServeHTTP,
|
authMiddleware(s.handleLogin()).ServeHTTP,
|
||||||
)
|
)
|
||||||
|
|
||||||
s.router.Get(
|
|
||||||
"/signup",
|
|
||||||
auth(s.h.HandleSignupGET()).ServeHTTP,
|
|
||||||
)
|
|
||||||
|
|
||||||
s.router.Post(
|
|
||||||
"/signup",
|
|
||||||
auth(s.h.HandleSignupPOST()).ServeHTTP,
|
|
||||||
)
|
|
||||||
// route that panics for testing
|
// route that panics for testing
|
||||||
// CHANGEME remove this
|
// CHANGEME remove this
|
||||||
s.router.Get(
|
s.router.Get(
|
||||||
"/panic",
|
"/panic",
|
||||||
s.h.HandlePanic(),
|
s.handlePanic(),
|
||||||
)
|
)
|
||||||
|
|
||||||
s.router.Get(
|
s.router.Get(
|
||||||
"/.well-known/healthcheck.json",
|
"/.well-known/healthcheck.json",
|
||||||
s.h.HandleHealthCheck(),
|
s.handleHealthCheck(),
|
||||||
)
|
)
|
||||||
|
|
||||||
// set up authenticated /metrics route:
|
// set up authenticated /metrics route:
|
||||||
if viper.GetString("METRICS_USERNAME") != "" {
|
if viper.GetString("METRICS_USERNAME") != "" {
|
||||||
s.router.Group(func(r chi.Router) {
|
s.router.Group(func(r chi.Router) {
|
||||||
r.Use(s.mw.MetricsAuth())
|
r.Use(s.MetricsAuthMiddleware())
|
||||||
r.Get("/metrics", http.HandlerFunc(promhttp.Handler().ServeHTTP))
|
r.Get("/metrics", http.HandlerFunc(promhttp.Handler().ServeHTTP))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -9,13 +9,13 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.eeqj.de/sneak/gohttpserver/internal/config"
|
|
||||||
"git.eeqj.de/sneak/gohttpserver/internal/globals"
|
"git.eeqj.de/sneak/gohttpserver/internal/globals"
|
||||||
"git.eeqj.de/sneak/gohttpserver/internal/handlers"
|
"github.com/docker/docker/daemon/logger"
|
||||||
"git.eeqj.de/sneak/gohttpserver/internal/logger"
|
|
||||||
"git.eeqj.de/sneak/gohttpserver/internal/middleware"
|
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
"github.com/spf13/viper"
|
||||||
"go.uber.org/fx"
|
"go.uber.org/fx"
|
||||||
|
"honnef.co/go/tools/config"
|
||||||
|
|
||||||
"github.com/getsentry/sentry-go"
|
"github.com/getsentry/sentry-go"
|
||||||
"github.com/go-chi/chi"
|
"github.com/go-chi/chi"
|
||||||
@ -32,15 +32,17 @@ import (
|
|||||||
|
|
||||||
type ServerParams struct {
|
type ServerParams struct {
|
||||||
fx.In
|
fx.In
|
||||||
Logger *logger.Logger
|
Logger logger.Logger
|
||||||
Globals *globals.Globals
|
Globals globals.Globals
|
||||||
Config *config.Config
|
Config config.Config
|
||||||
Middleware *middleware.Middleware
|
|
||||||
Handlers *handlers.Handlers
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
|
appname string
|
||||||
|
version string
|
||||||
|
buildarch string
|
||||||
startupTime time.Time
|
startupTime time.Time
|
||||||
|
port int
|
||||||
exitCode int
|
exitCode int
|
||||||
sentryEnabled bool
|
sentryEnabled bool
|
||||||
log *zerolog.Logger
|
log *zerolog.Logger
|
||||||
@ -49,26 +51,21 @@ type Server struct {
|
|||||||
httpServer *http.Server
|
httpServer *http.Server
|
||||||
router *chi.Mux
|
router *chi.Mux
|
||||||
params ServerParams
|
params ServerParams
|
||||||
mw *middleware.Middleware
|
|
||||||
h *handlers.Handlers
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(lc fx.Lifecycle, params ServerParams) (*Server, error) {
|
func New(lc fx.Lifecycle, params ServerParams) (*Server, error) {
|
||||||
s := new(Server)
|
s := new(Server)
|
||||||
s.params = params
|
s.params = params
|
||||||
s.mw = params.Middleware
|
|
||||||
s.h = params.Handlers
|
|
||||||
s.log = params.Logger.Get()
|
s.log = params.Logger.Get()
|
||||||
|
|
||||||
lc.Append(fx.Hook{
|
lc.Append(fx.Hook{
|
||||||
OnStart: func(ctx context.Context) error {
|
OnStart: func(ctx context.Context) error {
|
||||||
s.startupTime = time.Now()
|
s.startupTime = time.Now()
|
||||||
go s.Run() // background FIXME
|
s.version = params.Globals.Version
|
||||||
return nil
|
s.Run()
|
||||||
},
|
},
|
||||||
OnStop: func(ctx context.Context) error {
|
OnStop: func(ctx context.Context) error {
|
||||||
// FIXME do server shutdown here
|
// FIXME do server shutdown here
|
||||||
return nil
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
return s, nil
|
return s, nil
|
||||||
@ -88,22 +85,22 @@ func (s *Server) Run() {
|
|||||||
// logging before sentry, because sentry logs
|
// logging before sentry, because sentry logs
|
||||||
s.enableSentry()
|
s.enableSentry()
|
||||||
|
|
||||||
s.serve() // FIXME deal with return value
|
return s.serve()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) enableSentry() {
|
func (s *Server) enableSentry() {
|
||||||
s.sentryEnabled = false
|
s.sentryEnabled = false
|
||||||
|
|
||||||
if s.params.Config.SentryDSN == "" {
|
if s.Config.SentryDSN == "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err := sentry.Init(sentry.ClientOptions{
|
err := sentry.Init(sentry.ClientOptions{
|
||||||
Dsn: s.params.Config.SentryDSN,
|
Dsn: viper.GetString("SENTRY_DSN"),
|
||||||
Release: fmt.Sprintf("%s-%s", s.params.Globals.Appname, s.params.Globals.Version),
|
Release: fmt.Sprintf("%s-%s", s.params.Globals.Appname, s.params.Globals.Version),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Fatal().Err(err).Msg("sentry init failure")
|
log.Fatal().Err(err).Msg("sentry init failure")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s.log.Info().Msg("sentry error reporting activated")
|
s.log.Info().Msg("sentry error reporting activated")
|
||||||
@ -165,7 +162,11 @@ func (s *Server) cleanShutdown() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) MaintenanceMode() bool {
|
func (s *Server) uptime() time.Duration {
|
||||||
|
return time.Since(s.startupTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) maintenance() bool {
|
||||||
return s.params.Config.MaintenanceMode
|
return s.params.Config.MaintenanceMode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
<script src="/s/js/jquery-3.5.1.slim.min.js"></script>
|
|
||||||
<script src="/s/js/bootstrap-4.5.3.bundle.min.js"></script>
|
|
||||||
<script src="/s/js/main.js"></script>
|
|
@ -1,4 +0,0 @@
|
|||||||
<meta charset="utf-8" />
|
|
||||||
<title>{{ .HTMLTitle }}</title>
|
|
||||||
<link rel="stylesheet" href="/s/css/bootstrap-4.5.3.min.css" />
|
|
||||||
<link rel="stylesheet" href="/s/css/style.css" />
|
|
@ -1,29 +1,84 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
{{ template "htmlheader.html" . }}
|
<meta charset="utf-8" />
|
||||||
|
<title>Changeme: Go HTTP Server Boilerplate</title>
|
||||||
|
<link rel="stylesheet" href="/s/css/bootstrap-4.5.3.min.css" />
|
||||||
|
<link rel="stylesheet" href="/s/css/style.css" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
{{ template "navbar.html" .}}
|
<nav class="navbar navbar-expand-md navbar-dark fixed-top bg-dark">
|
||||||
|
<a class="navbar-brand" href="#">Quickstart</a>
|
||||||
|
<button
|
||||||
|
class="navbar-toggler"
|
||||||
|
type="button"
|
||||||
|
data-toggle="collapse"
|
||||||
|
data-target="#navbarsExampleDefault"
|
||||||
|
aria-controls="navbarsExampleDefault"
|
||||||
|
aria-expanded="false"
|
||||||
|
aria-label="Toggle navigation"
|
||||||
|
>
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="collapse navbar-collapse" id="navbarsExampleDefault">
|
||||||
|
<ul class="navbar-nav mr-auto">
|
||||||
|
<li class="nav-item active">
|
||||||
|
<a class="nav-link" href="#"
|
||||||
|
>Home <span class="sr-only">(current)</span></a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="#">Link</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a
|
||||||
|
class="nav-link disabled"
|
||||||
|
href="#"
|
||||||
|
tabindex="-1"
|
||||||
|
aria-disabled="true"
|
||||||
|
>Disabled</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item dropdown">
|
||||||
|
<a
|
||||||
|
class="nav-link dropdown-toggle"
|
||||||
|
href="#"
|
||||||
|
id="dropdown01"
|
||||||
|
data-toggle="dropdown"
|
||||||
|
aria-haspopup="true"
|
||||||
|
aria-expanded="false"
|
||||||
|
>Dropdown</a
|
||||||
|
>
|
||||||
|
<div class="dropdown-menu" aria-labelledby="dropdown01">
|
||||||
|
<a class="dropdown-item" href="#">Action</a>
|
||||||
|
<a class="dropdown-item" href="#">Another action</a>
|
||||||
|
<a class="dropdown-item" href="#">Something else here</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<form class="form-inline my-2 my-lg-0">
|
||||||
|
<input
|
||||||
|
class="form-control mr-sm-2"
|
||||||
|
type="text"
|
||||||
|
placeholder="Search"
|
||||||
|
aria-label="Search"
|
||||||
|
/>
|
||||||
|
<button class="btn btn-outline-success my-2 my-sm-0" type="submit">
|
||||||
|
Search
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
<main role="main">
|
<main role="main">
|
||||||
<div class="jumbotron">
|
<div class="jumbotron">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1 class="display-3">Hello, world!</h1>
|
<h1 class="display-3">Hello, world!</h1>
|
||||||
<h2><a
|
|
||||||
href="https://git.eeqj.de/sneak/gohttpserver">gohttpserver</a></h2>
|
|
||||||
<p>
|
<p>
|
||||||
This is a boilerplate application for you to use as a base for your
|
This is a boilerplate application for you to use as a base for your
|
||||||
own sites and services.
|
own sites and services.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
|
||||||
Find more info at <a
|
|
||||||
href="https://git.eeqj.de/sneak/gohttpserver">https://git.eeqj.de/sneak/gohttpserver</a>.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
This software is provided by <a
|
|
||||||
href="https://sneak.berlin">@sneak</a>
|
|
||||||
and is released unconditionally into the public domain.
|
|
||||||
</p>
|
|
||||||
<p>
|
<p>
|
||||||
<a class="btn btn-primary btn-lg" href="#" role="button"
|
<a class="btn btn-primary btn-lg" href="#" role="button"
|
||||||
>Learn more »</a
|
>Learn more »</a
|
||||||
@ -75,7 +130,11 @@
|
|||||||
<!-- /container -->
|
<!-- /container -->
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
{{ template "pagefooter.html" . }}
|
<footer class="container">
|
||||||
|
<p>© No rights reserved - This is in the public domain!</p>
|
||||||
|
</footer>
|
||||||
|
<script src="/s/js/jquery-3.5.1.slim.min.js"></script>
|
||||||
|
<script src="/s/js/bootstrap-4.5.3.bundle.min.js"></script>
|
||||||
|
<script src="/s/js/main.js"></script>
|
||||||
</body>
|
</body>
|
||||||
{{ template "htmlfooter.html" . }}
|
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
{{ template "htmlheader.html" . }}
|
<meta charset="utf-8" />
|
||||||
|
<title>Changeme: Go HTTP Server Boilerplate</title>
|
||||||
|
<link rel="stylesheet" href="/s/css/bootstrap-4.5.3.min.css" />
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
padding-top: 3.5rem;
|
padding-top: 3.5rem;
|
||||||
@ -25,9 +28,73 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
{{ template "navbar.html" .}}
|
<nav class="navbar navbar-expand-md navbar-dark fixed-top bg-dark">
|
||||||
|
<a class="navbar-brand" href="#">Quickstart</a>
|
||||||
|
<button
|
||||||
|
class="navbar-toggler"
|
||||||
|
type="button"
|
||||||
|
data-toggle="collapse"
|
||||||
|
data-target="#navbarsExampleDefault"
|
||||||
|
aria-controls="navbarsExampleDefault"
|
||||||
|
aria-expanded="false"
|
||||||
|
aria-label="Toggle navigation"
|
||||||
|
>
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="collapse navbar-collapse" id="navbarsExampleDefault">
|
||||||
|
<ul class="navbar-nav mr-auto">
|
||||||
|
<li class="nav-item active">
|
||||||
|
<a class="nav-link" href="#"
|
||||||
|
>Home <span class="sr-only">(current)</span></a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="#">Link</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a
|
||||||
|
class="nav-link disabled"
|
||||||
|
href="#"
|
||||||
|
tabindex="-1"
|
||||||
|
aria-disabled="true"
|
||||||
|
>Disabled</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item dropdown">
|
||||||
|
<a
|
||||||
|
class="nav-link dropdown-toggle"
|
||||||
|
href="#"
|
||||||
|
id="dropdown01"
|
||||||
|
data-toggle="dropdown"
|
||||||
|
aria-haspopup="true"
|
||||||
|
aria-expanded="false"
|
||||||
|
>Dropdown</a
|
||||||
|
>
|
||||||
|
<div class="dropdown-menu" aria-labelledby="dropdown01">
|
||||||
|
<a class="dropdown-item" href="#">Action</a>
|
||||||
|
<a class="dropdown-item" href="#">Another action</a>
|
||||||
|
<a class="dropdown-item" href="#">Something else here</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<form class="form-inline my-2 my-lg-0">
|
||||||
|
<input
|
||||||
|
class="form-control mr-sm-2"
|
||||||
|
type="text"
|
||||||
|
placeholder="Search"
|
||||||
|
aria-label="Search"
|
||||||
|
/>
|
||||||
|
<button class="btn btn-outline-success my-2 my-sm-0" type="submit">
|
||||||
|
Search
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
<main role="main">
|
<main role="main">
|
||||||
|
|
||||||
|
|
||||||
<div class="login-form">
|
<div class="login-form">
|
||||||
<form action="/login" method="post">
|
<form action="/login" method="post">
|
||||||
<h2 class="text-center">Log in</h2>
|
<h2 class="text-center">Log in</h2>
|
||||||
@ -51,6 +118,11 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
{{ template "pagefooter.html" . }}
|
<footer class="container">
|
||||||
|
<p>© No rights reserved - This is in the public domain!</p>
|
||||||
|
</footer>
|
||||||
|
<script src="/s/js/jquery-3.5.1.slim.min.js"></script>
|
||||||
|
<script src="/s/js/bootstrap-4.5.3.bundle.min.js"></script>
|
||||||
|
<script src="/s/js/main.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,64 +0,0 @@
|
|||||||
<nav class="navbar navbar-expand-md navbar-dark fixed-top bg-dark">
|
|
||||||
<a class="navbar-brand" href="/">{{.SiteName}}</a>
|
|
||||||
<button
|
|
||||||
class="navbar-toggler"
|
|
||||||
type="button"
|
|
||||||
data-toggle="collapse"
|
|
||||||
data-target="#navbarsExampleDefault"
|
|
||||||
aria-controls="navbarsExampleDefault"
|
|
||||||
aria-expanded="false"
|
|
||||||
aria-label="Toggle navigation"
|
|
||||||
>
|
|
||||||
<span class="navbar-toggler-icon"> </span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<div class="collapse navbar-collapse" id="navbarsExampleDefault">
|
|
||||||
<ul class="navbar-nav mr-auto">
|
|
||||||
<li class="nav-item active">
|
|
||||||
<a class="nav-link" href="#"
|
|
||||||
>Home <span class="sr-only">(current)</span></a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" href="#">Link</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link disabled" href="#" aria-disabled="true"
|
|
||||||
>Disabled</a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item dropdown">
|
|
||||||
<a
|
|
||||||
class="nav-link dropdown-toggle"
|
|
||||||
href="#"
|
|
||||||
id="dropdown01"
|
|
||||||
data-toggle="dropdown"
|
|
||||||
aria-haspopup="true"
|
|
||||||
aria-expanded="false"
|
|
||||||
>Dropdown</a
|
|
||||||
>
|
|
||||||
<div class="dropdown-menu" aria-labelledby="dropdown01">
|
|
||||||
<a class="dropdown-item" href="#">Action</a>
|
|
||||||
<a class="dropdown-item" href="#">Another action</a>
|
|
||||||
<a class="dropdown-item" href="#"
|
|
||||||
>Something else here</a
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<form action="POST" class="form-inline my-2 my-lg-0">
|
|
||||||
<input
|
|
||||||
class="form-control mr-sm-2"
|
|
||||||
type="text"
|
|
||||||
placeholder="Search"
|
|
||||||
aria-label="Search"
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-success my-2 my-sm-0"
|
|
||||||
type="submit"
|
|
||||||
>
|
|
||||||
Search
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
@ -1,3 +0,0 @@
|
|||||||
<footer class="container">
|
|
||||||
<p>© No rights reserved - This is in the public domain!</p>
|
|
||||||
</footer>
|
|
@ -1,76 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
{{ template "htmlheader.html" .HTMLHeader }}
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
padding-top: 3.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bd-placeholder-img {
|
|
||||||
font-size: 1.125rem;
|
|
||||||
text-anchor: middle;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-moz-user-select: none;
|
|
||||||
-ms-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
|
||||||
.bd-placeholder-img-lg {
|
|
||||||
font-size: 3.5rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
{{ template "navbar.html" .Navbar}}
|
|
||||||
|
|
||||||
<main role="main">
|
|
||||||
<div class="signup-form">
|
|
||||||
<form action="/signup" method="post">
|
|
||||||
<h2 class="text-center">Create New Account</h2>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<span>Email:</span>
|
|
||||||
<input type="text" class="form-control" name="email"
|
|
||||||
placeholder="user@domain.com" required="required">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<span>Desired Username:</span>
|
|
||||||
<input type="text" class="form-control" name="desiredUsername" placeholder="Username" required="required">
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<p>Please use a unique password that you don't use anywhere
|
|
||||||
else. Minimum 12 characters.</p>
|
|
||||||
<span>New Password:</span>
|
|
||||||
<input type="password" class="form-control"
|
|
||||||
name="desiredPassword1" placeholder="Password" required="required">
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<span>New Password (again):</span>
|
|
||||||
<input type="password" class="form-control"
|
|
||||||
name="desiredPassword2" placeholder="Password
|
|
||||||
(again)" required="required">
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<button type="submit" class="btn btn-primary btn-block">Create
|
|
||||||
New Account</button>
|
|
||||||
</div>
|
|
||||||
<!-- <div class="clearfix">
|
|
||||||
<label class="float-left form-check-label"><input type="checkbox"> Remember me</label>
|
|
||||||
<a href="#" class="float-right">Forgot Password?</a>
|
|
||||||
</div>
|
|
||||||
-->
|
|
||||||
</form>
|
|
||||||
<p class="text-center"><a href="#">Create an Account</a></p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
</main>
|
|
||||||
|
|
||||||
{{ template "pagefooter.html" .PageFooter }}
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -2,21 +2,12 @@ package templates
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"embed"
|
"embed"
|
||||||
"text/template"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed *.html
|
//go:embed *.html
|
||||||
var TemplatesRaw embed.FS
|
var Templates embed.FS
|
||||||
var TemplatesParsed *template.Template
|
|
||||||
|
|
||||||
func GetParsed() *template.Template {
|
|
||||||
if TemplatesParsed == nil {
|
|
||||||
TemplatesParsed = template.Must(template.ParseFS(TemplatesRaw, "*"))
|
|
||||||
}
|
|
||||||
return TemplatesParsed
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
func MustString(filename string) string {
|
func MustString(filename string) string {
|
||||||
bytes, error := Templates.ReadFile(filename)
|
bytes, error := Templates.ReadFile(filename)
|
||||||
if error != nil {
|
if error != nil {
|
||||||
@ -26,4 +17,3 @@ func MustString(filename string) string {
|
|||||||
out.Write(bytes)
|
out.Write(bytes)
|
||||||
return out.String()
|
return out.String()
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
Loading…
Reference in New Issue
Block a user