add home handler + add templates folder
This commit is contained in:
parent
f712242e39
commit
874378cf2d
37
handlers/home/handler.go
Normal file
37
handlers/home/handler.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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()
|
||||||
|
|||||||
@ -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>
|
|
||||||
@ -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>
|
|
||||||
@ -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
|
|
||||||
})
|
|
||||||
@ -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
|
||||||
|
|||||||
@ -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
|
|
||||||
})
|
|
||||||
2
main.go
2
main.go
@ -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
60
templates/base.html.tpl
Normal 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>© 2025 Librapi</p>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
{{ block "script" . }}{{ end }}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</html>
|
||||||
3
templates/home.html.tpl
Normal file
3
templates/home.html.tpl
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{{ define "content" }}
|
||||||
|
<h3>A simple API to store, search and download books.</h3>
|
||||||
|
{{ end }}
|
||||||
33
templates/login/login_form.html.tpl
Normal file
33
templates/login/login_form.html.tpl
Normal 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 }}
|
||||||
4
templates/login/login_success.html.tpl
Normal file
4
templates/login/login_success.html.tpl
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{{ define "content" }}
|
||||||
|
<h1>Login</h1>
|
||||||
|
<div>You're logged</div>
|
||||||
|
{{ end }}
|
||||||
110
templates/templates.go
Normal file
110
templates/templates.go
Normal 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
|
||||||
|
})
|
||||||
@ -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>
|
|
||||||
Loading…
x
Reference in New Issue
Block a user