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,24 +20,20 @@ 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
 | 
						MetricsPassword string
 | 
				
			||||||
	DevAdminUsername string
 | 
						MetricsUsername string
 | 
				
			||||||
	DevAdminPassword string
 | 
						Port            int
 | 
				
			||||||
	MetricsPassword  string
 | 
						SentryDSN       string
 | 
				
			||||||
	MetricsUsername  string
 | 
						params          ConfigParams
 | 
				
			||||||
	Port             int
 | 
						log             *zerolog.Logger
 | 
				
			||||||
	SentryDSN        string
 | 
					 | 
				
			||||||
	params           *ConfigParams
 | 
					 | 
				
			||||||
	log              *zerolog.Logger
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func New(lc fx.Lifecycle, params ConfigParams) (*Config, error) {
 | 
					func New(lc fx.Lifecycle, params ConfigParams) (*Config, error) {
 | 
				
			||||||
@ -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", "")
 | 
				
			||||||
@ -76,23 +69,14 @@ func New(lc fx.Lifecycle, params ConfigParams) (*Config, error) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	s := &Config{
 | 
						s := &Config{
 | 
				
			||||||
		DBURL:            viper.GetString("DBURL"),
 | 
							DBURL:           viper.GetString("DBURL"),
 | 
				
			||||||
		Debug:            viper.GetBool("debug"),
 | 
							Debug:           viper.GetBool("debug"),
 | 
				
			||||||
		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"),
 | 
							MetricsUsername: viper.GetString("METRICS_USERNAME"),
 | 
				
			||||||
		DevAdminUsername: viper.GetString("DEV_ADMIN_USERNAME"),
 | 
							MetricsPassword: viper.GetString("METRICS_PASSWORD"),
 | 
				
			||||||
		DevAdminPassword: viper.GetString("DEV_ADMIN_PASSWORD"),
 | 
							log:             log,
 | 
				
			||||||
		MetricsUsername:  viper.GetString("METRICS_USERNAME"),
 | 
					 | 
				
			||||||
		MetricsPassword:  viper.GetString("METRICS_PASSWORD"),
 | 
					 | 
				
			||||||
		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 {
 | 
				
			||||||
}
 | 
							zerolog.SetGlobalLevel(zerolog.DebugLevel)
 | 
				
			||||||
 | 
							l.log.Debug().Bool("debug", true).Send()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (l *Logger) EnableDebugLogging() {
 | 
						return l, nil
 | 
				
			||||||
	zerolog.SetGlobalLevel(zerolog.DebugLevel)
 | 
					 | 
				
			||||||
	l.log.Debug().Bool("debug", true).Send()
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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,10 +28,74 @@
 | 
				
			|||||||
  </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>
 | 
				
			||||||
        <div class="form-group">
 | 
					        <div class="form-group">
 | 
				
			||||||
@ -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