add home handler + add templates folder

This commit is contained in:
rmanach 2025-01-03 13:54:35 +01:00
parent f712242e39
commit 874378cf2d
14 changed files with 267 additions and 216 deletions

37
handlers/home/handler.go Normal file
View File

@ -0,0 +1,37 @@
package home
import (
"bytes"
"fmt"
"librapi/templates"
"net/http"
"github.com/rs/zerolog/log"
)
func Handler() func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
getHome(w, r)
default:
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
}
}
}
func getHome(w http.ResponseWriter, _ *http.Request) {
home := templates.GetHome()
buf := bytes.NewBufferString("")
if err := home.Execute(buf, nil); err != nil {
log.Err(err).Msg("unable to generate template")
http.Error(w, "unexpected error occurred", http.StatusInternalServerError)
return
}
if _, err := fmt.Fprint(w, buf); err != nil {
log.Err(err).Msg("unable to write to response")
http.Error(w, "unexpected error occurred", http.StatusInternalServerError)
}
}

View File

@ -4,13 +4,14 @@ import (
"bytes" "bytes"
"errors" "errors"
"fmt" "fmt"
"librapi/handlers/login/templates"
"librapi/services"
"net/http" "net/http"
"os" "os"
"sync" "sync"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"librapi/services"
"librapi/templates"
) )
var ( var (
@ -140,16 +141,18 @@ func postLogin(w http.ResponseWriter, r *http.Request, s *services.SessionStore)
http.SetCookie(w, cookie) http.SetCookie(w, cookie)
loginSuccess := templates.GetLoginSuccess() loginSuccess := templates.GetLoginSuccess()
fmt.Fprint(w, loginSuccess.Tree.Root.String()) buf := bytes.NewBufferString("")
if err := loginSuccess.Execute(buf, nil); err != nil {
log.Err(err).Msg("unable to generate template")
http.Error(w, "unexpected error occurred", http.StatusInternalServerError)
return
}
fmt.Fprint(w, buf)
} }
func getLogin(w http.ResponseWriter, r *http.Request, s *services.SessionStore) { func getLogin(w http.ResponseWriter, r *http.Request, s *services.SessionStore) {
loginForm := templates.GetLoginForm() loginForm := templates.GetLoginForm()
if loginForm == nil {
log.Error().Msg("unable to load login form")
http.Error(w, "unexpected error occurred", http.StatusInternalServerError)
return
}
if s.IsLogged(r) { if s.IsLogged(r) {
loginSuccess := templates.GetLoginSuccess() loginSuccess := templates.GetLoginSuccess()

View File

@ -1,66 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<style>
.main-container {
display: flex;
flex-direction: column;
flex-wrap: wrap;
justify-content: center;
}
.form-item {
align-self: flex-start;
margin: 10px;
width: 300px;
}
.form-container {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-between;
}
.error {
color: red;
}
</style>
</head>
<body>
<h1>Login</h1>
<form action="/login" method="post" enctype="multipart/form-data">
<div class="main-container">
<div class="form-item">
<div class="form-container">
<label>Username:</label>
<input type="text" name="username" value="{{.Username.Value}}" />
</div>
{{ if .Username.Err }}
<div class="error">{{.Username.Err}}</div>
{{ end }}
</div>
<div class="form-item">
<div class="form-container">
<label>Password:</label>
<input type="password" name="password" value="{{.Password.Value}}" />
</div>
{{ if .Password.Err }}
<div class="error">{{.Password.Err}}</div>
{{ end }}
</div>
<div class="form-item">
<div class="form-container">
<button id="submit" type="submit">Login</button>
</div>
</div>
</div>
</form>
{{ if ne (errStr .Error) "" }}
<div class="error">{{.Error | errStr}}</div>
{{ end }}
</body>
</html>

View File

@ -1,13 +0,0 @@
<!DOCTYPE html>
<html>
<body>
<h1>Login</h1>
<div>You're logged</div>
<h2>Available urls</h2>
<ul>
<li><a href="/upload">Upload a book</a></li>
</ul>
</body>
</html>

View File

@ -1,43 +0,0 @@
package templates
import (
_ "embed"
"html/template"
"os"
"sync"
"github.com/rs/zerolog/log"
)
//go:embed form.html.tpl
var form string
//go:embed success.html.tpl
var success string
var funcMap = template.FuncMap{
"errStr": func(err error) string {
if err != nil {
return err.Error()
}
return ""
},
}
var GetLoginForm = sync.OnceValue[*template.Template](func() *template.Template {
tmpl, err := template.New("loginForm").Funcs(funcMap).Parse(form)
if err != nil {
log.Err(err).Msg("unable to parse login form")
os.Exit(1)
}
return tmpl
})
var GetLoginSuccess = sync.OnceValue[*template.Template](func() *template.Template {
tmpl, err := template.New("loginSuccess").Parse(success)
if err != nil {
log.Err(err).Msg("unable to parse login success")
os.Exit(1)
}
return tmpl
})

View File

@ -13,8 +13,8 @@ import (
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"librapi/handlers/upload/templates"
"librapi/services" "librapi/services"
"librapi/templates"
) )
const MaxFileSize = 200 // in MB const MaxFileSize = 200 // in MB

View File

@ -1,47 +0,0 @@
package templates
import (
_ "embed"
"html/template"
"mime/multipart"
"os"
"strconv"
"strings"
"sync"
"github.com/rs/zerolog/log"
)
var funcMap = template.FuncMap{
"year": func(s int) string {
if s == 0 {
return ""
}
return strconv.Itoa(s)
},
"join": func(s []string) string {
if len(s) == 0 {
return ""
} else {
return strings.Join(s, ",")
}
},
"filename": func(h *multipart.FileHeader) string {
if h != nil {
return h.Filename
}
return ""
},
}
//go:embed form.html.tpl
var form string
var GetUploadForm = sync.OnceValue[*template.Template](func() *template.Template {
tmpl, err := template.New("uploadForm").Funcs(funcMap).Parse(form)
if err != nil {
log.Err(err).Msg("unable to parse upload form")
os.Exit(1)
}
return tmpl
})

View File

@ -10,6 +10,7 @@ import (
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"librapi/handlers/home"
"librapi/handlers/login" "librapi/handlers/login"
"librapi/handlers/upload" "librapi/handlers/upload"
) )
@ -30,6 +31,7 @@ func main() {
srv := server.NewServer( srv := server.NewServer(
ctx, ctx,
services.GetEnv().GetPort(), services.GetEnv().GetPort(),
server.NewHandler("/", home.Handler()),
server.NewHandler("/upload", upload.Handler(sessionStore)), server.NewHandler("/upload", upload.Handler(sessionStore)),
server.NewHandler("/login", login.Handler(sessionStore)), server.NewHandler("/login", login.Handler(sessionStore)),
) )

60
templates/base.html.tpl Normal file
View File

@ -0,0 +1,60 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Librapi</title>
<style>
.main-container {
display: flex;
flex-direction: column;
flex-wrap: wrap;
justify-content: center;
}
.form-item {
align-self: flex-start;
margin: 10px;
width: 300px;
}
.form-container {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-between;
}
.error {
color: red;
}
</style>
</head>
<body>
<header>
<h1>Librapi</h1>
</header>
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/login">Login</a></li>
<li><a href="/upload">Upload</a></li>
</ul>
</nav>
<main>
{{ block "content" . }}{{ end }}
</main>
<footer>
<p>&copy; 2025 Librapi</p>
</footer>
</body>
<script type="text/javascript">
{{ block "script" . }}{{ end }}
</script>
</html>

3
templates/home.html.tpl Normal file
View File

@ -0,0 +1,3 @@
{{ define "content" }}
<h3>A simple API to store, search and download books.</h3>
{{ end }}

View File

@ -0,0 +1,33 @@
{{ define "content" }}
<h2>Login</h2>
<form action="/login" method="post" enctype="multipart/form-data">
<div class="main-container">
<div class="form-item">
<div class="form-container">
<label>Username:</label>
<input type="text" name="username" value="{{.Username.Value}}" />
</div>
{{ if .Username.Err }}
<div class="error">{{.Username.Err}}</div>
{{ end }}
</div>
<div class="form-item">
<div class="form-container">
<label>Password:</label>
<input type="password" name="password" value="{{.Password.Value}}" />
</div>
{{ if .Password.Err }}
<div class="error">{{.Password.Err}}</div>
{{ end }}
</div>
<div class="form-item">
<div class="form-container">
<button id="submit" type="submit">Login</button>
</div>
</div>
</div>
</form>
{{ if ne (errStr .Error) "" }}
<div class="error">{{.Error | errStr}}</div>
{{ end }}
{{ end }}

View File

@ -0,0 +1,4 @@
{{ define "content" }}
<h1>Login</h1>
<div>You're logged</div>
{{ end }}

110
templates/templates.go Normal file
View File

@ -0,0 +1,110 @@
package templates
import (
_ "embed"
"html/template"
"mime/multipart"
"os"
"strconv"
"strings"
"sync"
"github.com/rs/zerolog/log"
)
//go:embed base.html.tpl
var base string
//go:embed login/login_form.html.tpl
var loginForm string
//go:embed login/login_success.html.tpl
var loginSuccess string
//go:embed upload_form.html.tpl
var uploadForm string
//go:embed home.html.tpl
var home string
var funcMap = template.FuncMap{
"year": func(s int) string {
if s == 0 {
return ""
}
return strconv.Itoa(s)
},
"join": func(s []string) string {
if len(s) == 0 {
return ""
} else {
return strings.Join(s, ",")
}
},
"filename": func(h *multipart.FileHeader) string {
if h != nil {
return h.Filename
}
return ""
},
"errStr": func(err error) string {
if err != nil {
return err.Error()
}
return ""
},
}
var GetHome = sync.OnceValue[*template.Template](func() *template.Template {
baseTmpl, err := template.New("base").Parse(base)
if err != nil {
log.Err(err).Msg("unable to parse base tmpl")
os.Exit(1)
}
if _, err := baseTmpl.New("home").Parse(home); err != nil {
log.Err(err).Msg("unable to parse home tmpl")
os.Exit(1)
}
return baseTmpl
})
var GetUploadForm = sync.OnceValue[*template.Template](func() *template.Template {
baseTmpl, err := template.New("base").Parse(base)
if err != nil {
log.Err(err).Msg("unable to parse base tmpl")
os.Exit(1)
}
if _, err := baseTmpl.New("uploadForm").Funcs(funcMap).Parse(uploadForm); err != nil {
log.Err(err).Msg("unable to parse upload tmpl")
os.Exit(1)
}
return baseTmpl
})
var GetLoginForm = sync.OnceValue[*template.Template](func() *template.Template {
baseTmpl, err := template.New("base").Parse(base)
if err != nil {
log.Err(err).Msg("unable to parse base tmpl")
os.Exit(1)
}
if _, err := baseTmpl.New("loginForm").Funcs(funcMap).Parse(loginForm); err != nil {
log.Err(err).Msg("unable to parse login tmpl")
os.Exit(1)
}
return baseTmpl
})
var GetLoginSuccess = sync.OnceValue[*template.Template](func() *template.Template {
tmpl, err := template.New("loginSuccess").Parse(loginSuccess)
if err != nil {
log.Err(err).Msg("unable to parse login success")
os.Exit(1)
}
return tmpl
})

View File

@ -1,36 +1,5 @@
<!DOCTYPE html> {{define "content" }}
<html> <h2>Upload a book</h2>
<head>
<style>
.main-container {
display: flex;
flex-direction: column;
flex-wrap: wrap;
justify-content: center;
}
.form-item {
align-self: flex-start;
margin: 10px;
width: 300px;
}
.form-container {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-between;
}
.error {
color: red;
}
</style>
</head>
<body>
<h1>Upload a book</h1>
<form action="/upload" method="post" enctype="multipart/form-data"> <form action="/upload" method="post" enctype="multipart/form-data">
<div class="main-container"> <div class="main-container">
<div class="form-item"> <div class="form-item">
@ -92,8 +61,9 @@
{{ if ne .Error "" }} {{ if ne .Error "" }}
<div class="error">{{.Error}}</div> <div class="error">{{.Error}}</div>
{{ end }} {{ end }}
</body> {{ end }}
<script type="text/javascript">
{{ define "script" }}
var isSuccess = {{.IsSuccess }}; var isSuccess = {{.IsSuccess }};
var submit = document.getElementById("submit"); var submit = document.getElementById("submit");
submit.addEventListener("submit", function () { submit.addEventListener("submit", function () {
@ -102,6 +72,4 @@
if (isSuccess) { if (isSuccess) {
alert("file uploaded successfully"); alert("file uploaded successfully");
} }
</script> {{ end }}
</html>