WIP: sneak/next #21
@ -1,12 +0,0 @@
|
|||||||
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")
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,17 +1,16 @@
|
|||||||
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 {
|
||||||
indexTemplate := template.Must(template.New("index").Parse(templates.MustString("index.html")))
|
t := templates.GetParsed()
|
||||||
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
err := indexTemplate.ExecuteTemplate(w, "index", nil)
|
err := t.ExecuteTemplate(w, "index.html", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Error().Err(err).Msg("")
|
s.log.Error().Err(err).Msg("")
|
||||||
http.Error(w, http.StatusText(500), 500)
|
http.Error(w, http.StatusText(500), 500)
|
19
internal/handlers/login.go
Normal file
19
internal/handlers/login.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -69,7 +69,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) LoggingMiddleware() func(http.Handler) http.Handler {
|
func (s *Middleware) Logging() 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 +97,7 @@ func (s *Middleware) LoggingMiddleware() func(http.Handler) http.Handler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Middleware) CORSMiddleware() func(http.Handler) http.Handler {
|
func (s *Middleware) CORS() 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 +112,7 @@ func (s *Middleware) CORSMiddleware() func(http.Handler) http.Handler {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Middleware) AuthMiddleware() func(http.Handler) http.Handler {
|
func (s *Middleware) Auth() 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 +122,7 @@ func (s *Middleware) AuthMiddleware() func(http.Handler) http.Handler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Middleware) MetricsMiddleware() func(http.Handler) http.Handler {
|
func (s *Middleware) Metrics() 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 +131,7 @@ func (s *Middleware) MetricsMiddleware() func(http.Handler) http.Handler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Middleware) MetricsAuthMiddleware() func(http.Handler) http.Handler {
|
func (s *Middleware) MetricsAuth() 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.port)
|
listenAddr := fmt.Sprintf(":%d", s.params.Config.Port)
|
||||||
s.httpServer = &http.Server{
|
s.httpServer = &http.Server{
|
||||||
Addr: listenAddr,
|
Addr: listenAddr,
|
||||||
ReadTimeout: 10 * time.Second,
|
ReadTimeout: 10 * time.Second,
|
||||||
|
@ -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.LoggingMiddleware())
|
s.router.Use(s.mw.Logging())
|
||||||
|
|
||||||
// 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.MetricsMiddleware())
|
s.router.Use(s.mw.Metrics())
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.CORSMiddleware())
|
s.router.Use(s.mw.CORS())
|
||||||
|
|
||||||
// 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
|
||||||
@ -68,12 +68,21 @@ func (s *Server) SetupRoutes() {
|
|||||||
// 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:
|
||||||
authMiddleware := s.mw.AuthMiddleware()
|
auth := s.mw.Auth()
|
||||||
s.router.Get(
|
s.router.Get(
|
||||||
"/login",
|
"/login",
|
||||||
authMiddleware(s.h.HandleLogin()).ServeHTTP,
|
auth(s.h.HandleLoginGET()).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(
|
||||||
@ -89,7 +98,7 @@ func (s *Server) SetupRoutes() {
|
|||||||
// 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.MetricsAuthMiddleware())
|
r.Use(s.mw.MetricsAuth())
|
||||||
r.Get("/metrics", http.HandlerFunc(promhttp.Handler().ServeHTTP))
|
r.Get("/metrics", http.HandlerFunc(promhttp.Handler().ServeHTTP))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -64,7 +64,7 @@ func New(lc fx.Lifecycle, params ServerParams) (*Server, error) {
|
|||||||
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()
|
||||||
s.Run() // background FIXME
|
go s.Run() // background FIXME
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
OnStop: func(ctx context.Context) error {
|
OnStop: func(ctx context.Context) error {
|
||||||
|
3
templates/htmlfooter.html
Normal file
3
templates/htmlfooter.html
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<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>
|
4
templates/htmlheader.html
Normal file
4
templates/htmlheader.html
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<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,84 +1,29 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
{{ template "htmlheader.html" . }}
|
||||||
<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>
|
||||||
<nav class="navbar navbar-expand-md navbar-dark fixed-top bg-dark">
|
{{ template "navbar.html" .}}
|
||||||
<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
|
||||||
@ -130,11 +75,7 @@
|
|||||||
<!-- /container -->
|
<!-- /container -->
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<footer class="container">
|
{{ template "pagefooter.html" . }}
|
||||||
<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,10 +1,7 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
{{ template "htmlheader.html" . }}
|
||||||
<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;
|
||||||
@ -28,73 +25,9 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<nav class="navbar navbar-expand-md navbar-dark fixed-top bg-dark">
|
{{ template "navbar.html" .}}
|
||||||
<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>
|
||||||
@ -118,11 +51,6 @@
|
|||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<footer class="container">
|
{{ template "pagefooter.html" . }}
|
||||||
<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>
|
||||||
|
64
templates/navbar.html
Normal file
64
templates/navbar.html
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
<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>
|
3
templates/pagefooter.html
Normal file
3
templates/pagefooter.html
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<footer class="container">
|
||||||
|
<p>© No rights reserved - This is in the public domain!</p>
|
||||||
|
</footer>
|
76
templates/signup.html
Normal file
76
templates/signup.html
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
<!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,12 +2,21 @@ package templates
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"embed"
|
"embed"
|
||||||
"strings"
|
"text/template"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed *.html
|
//go:embed *.html
|
||||||
var Templates embed.FS
|
var TemplatesRaw 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 {
|
||||||
@ -17,3 +26,4 @@ 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