ch9.3
This commit is contained in:
@@ -13,10 +13,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type snippetCreateForm struct {
|
type snippetCreateForm struct {
|
||||||
Title string
|
Title string `form:"title"`
|
||||||
Content string
|
Content string `form:"content"`
|
||||||
Expires int
|
Expires int `form:"expires"`
|
||||||
validator.Validator
|
validator.Validator `form:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *application) render(w http.ResponseWriter, status int, page string, data *templateData) {
|
func (app *application) render(w http.ResponseWriter, status int, page string, data *templateData) {
|
||||||
@@ -88,24 +88,13 @@ func (app *application) snippetCreate(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (app *application) snippetCreatePost(w http.ResponseWriter, r *http.Request) {
|
func (app *application) snippetCreatePost(w http.ResponseWriter, r *http.Request) {
|
||||||
err := r.ParseForm()
|
var form snippetCreateForm
|
||||||
|
err := app.decodePostForm(r, &form)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
app.clientError(w, http.StatusBadRequest)
|
app.clientError(w, http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
expires, err := strconv.Atoi(r.PostForm.Get("expires"))
|
|
||||||
if err != nil {
|
|
||||||
app.clientError(w, http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
form := snippetCreateForm{
|
|
||||||
Title: r.PostForm.Get("title"),
|
|
||||||
Content: r.PostForm.Get("content"),
|
|
||||||
Expires: expires,
|
|
||||||
}
|
|
||||||
|
|
||||||
form.CheckField(validator.NotBlank(form.Title), "title", "This field cannot be blank")
|
form.CheckField(validator.NotBlank(form.Title), "title", "This field cannot be blank")
|
||||||
form.CheckField(validator.MaxChars(form.Title, 100), "title", "This field cannot be more than 100 character long")
|
form.CheckField(validator.MaxChars(form.Title, 100), "title", "This field cannot be more than 100 character long")
|
||||||
form.CheckField(validator.NotBlank(form.Content), "content", "This field cannot be blank")
|
form.CheckField(validator.NotBlank(form.Content), "content", "This field cannot be blank")
|
||||||
@@ -124,5 +113,7 @@ func (app *application) snippetCreatePost(w http.ResponseWriter, r *http.Request
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
app.sessionManager.Put(r.Context(), "flash", "Snippet successfully created!")
|
||||||
|
|
||||||
http.Redirect(w, r, fmt.Sprintf("/snippet/view/%d", id), http.StatusSeeOther)
|
http.Redirect(w, r, fmt.Sprintf("/snippet/view/%d", id), http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
|
|
||||||
|
"github.com/go-playground/form/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (app *application) serverError(w http.ResponseWriter, err error) {
|
func (app *application) serverError(w http.ResponseWriter, err error) {
|
||||||
@@ -20,3 +23,20 @@ func (app *application) clientError(w http.ResponseWriter, status int) {
|
|||||||
func (app *application) notFound(w http.ResponseWriter) {
|
func (app *application) notFound(w http.ResponseWriter) {
|
||||||
app.clientError(w, http.StatusNotFound)
|
app.clientError(w, http.StatusNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (app *application) decodePostForm(r *http.Request, snippetForm *snippetCreateForm) error {
|
||||||
|
err := r.ParseForm()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = app.formDecoder.Decode(&snippetForm, r.PostForm)
|
||||||
|
if err != nil {
|
||||||
|
var invalidDecoderError *form.InvalidDecoderError
|
||||||
|
if errors.As(err, &invalidDecoderError) {
|
||||||
|
panic("Invalid decoder error")
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,17 +7,23 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
"gitea.local.lab/Lbenedar/snippetbox/internal/models"
|
"gitea.local.lab/Lbenedar/snippetbox/internal/models"
|
||||||
|
|
||||||
|
"github.com/alexedwards/scs/mysqlstore"
|
||||||
|
"github.com/alexedwards/scs/v2"
|
||||||
|
"github.com/go-playground/form/v4"
|
||||||
_ "github.com/go-sql-driver/mysql"
|
_ "github.com/go-sql-driver/mysql"
|
||||||
)
|
)
|
||||||
|
|
||||||
type application struct {
|
type application struct {
|
||||||
errorLog *log.Logger
|
errorLog *log.Logger
|
||||||
infoLog *log.Logger
|
infoLog *log.Logger
|
||||||
snippets *models.SnippetModel
|
snippets *models.SnippetModel
|
||||||
templateCache map[string]*template.Template
|
templateCache map[string]*template.Template
|
||||||
|
formDecoder *form.Decoder
|
||||||
|
sessionManager *scs.SessionManager
|
||||||
}
|
}
|
||||||
|
|
||||||
type config struct {
|
type config struct {
|
||||||
@@ -47,11 +53,18 @@ func main() {
|
|||||||
errorLog.Fatal(err)
|
errorLog.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
formDecoder := form.NewDecoder()
|
||||||
|
sessionManager := scs.New()
|
||||||
|
sessionManager.Store = mysqlstore.New(db)
|
||||||
|
sessionManager.Lifetime = 12 * time.Hour
|
||||||
|
|
||||||
app := &application{
|
app := &application{
|
||||||
errorLog: errorLog,
|
errorLog: errorLog,
|
||||||
infoLog: infoLog,
|
infoLog: infoLog,
|
||||||
snippets: &models.SnippetModel{DB: db},
|
snippets: &models.SnippetModel{DB: db},
|
||||||
templateCache: templateCache,
|
templateCache: templateCache,
|
||||||
|
formDecoder: formDecoder,
|
||||||
|
sessionManager: sessionManager,
|
||||||
}
|
}
|
||||||
|
|
||||||
srv := &http.Server{
|
srv := &http.Server{
|
||||||
|
|||||||
@@ -17,12 +17,13 @@ func (app *application) routes() http.Handler {
|
|||||||
fileServer := http.FileServer(http.Dir("./ui/static/"))
|
fileServer := http.FileServer(http.Dir("./ui/static/"))
|
||||||
router.Handler(http.MethodGet, "/static/", http.StripPrefix("/static", fileServer))
|
router.Handler(http.MethodGet, "/static/", http.StripPrefix("/static", fileServer))
|
||||||
|
|
||||||
router.HandlerFunc(http.MethodGet, "/", app.home)
|
dynamic := alice.New(app.sessionManager.LoadAndSave)
|
||||||
router.HandlerFunc(http.MethodGet, "/snippet/view/:id", app.snippetView)
|
|
||||||
router.HandlerFunc(http.MethodGet, "/snippet/create", app.snippetCreate)
|
router.Handler(http.MethodGet, "/", dynamic.ThenFunc(app.home))
|
||||||
router.HandlerFunc(http.MethodPost, "/snippet/create", app.snippetCreatePost)
|
router.Handler(http.MethodGet, "/snippet/view/:id", dynamic.ThenFunc(app.snippetView))
|
||||||
|
router.Handler(http.MethodGet, "/snippet/create", dynamic.ThenFunc(app.snippetCreate))
|
||||||
|
router.Handler(http.MethodPost, "/snippet/create", dynamic.ThenFunc(app.snippetCreatePost))
|
||||||
|
|
||||||
standard := alice.New(app.recoverPanic, app.logRequest, secureHeaders)
|
standard := alice.New(app.recoverPanic, app.logRequest, secureHeaders)
|
||||||
|
|
||||||
return standard.Then(router)
|
return standard.Then(router)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ type templateData struct {
|
|||||||
Snippet *models.Snippet
|
Snippet *models.Snippet
|
||||||
Snippets []*models.Snippet
|
Snippets []*models.Snippet
|
||||||
Form any
|
Form any
|
||||||
|
Flash string
|
||||||
}
|
}
|
||||||
|
|
||||||
func humanDate(t time.Time) string {
|
func humanDate(t time.Time) string {
|
||||||
@@ -27,6 +28,7 @@ var functions = template.FuncMap{
|
|||||||
func (app *application) newTemplateData(r *http.Request) *templateData {
|
func (app *application) newTemplateData(r *http.Request) *templateData {
|
||||||
return &templateData{
|
return &templateData{
|
||||||
CurrentYear: time.Now().Year(),
|
CurrentYear: time.Now().Year(),
|
||||||
|
Flash: app.sessionManager.PopString(r.Context(), "flash"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
3
go.mod
3
go.mod
@@ -4,6 +4,9 @@ go 1.25.0
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
filippo.io/edwards25519 v1.1.0 // indirect
|
filippo.io/edwards25519 v1.1.0 // indirect
|
||||||
|
github.com/alexedwards/scs/mysqlstore v0.0.0-20251002162104-209de6e426de // indirect
|
||||||
|
github.com/alexedwards/scs/v2 v2.9.0 // indirect
|
||||||
|
github.com/go-playground/form/v4 v4.3.0 // indirect
|
||||||
github.com/go-sql-driver/mysql v1.9.3 // indirect
|
github.com/go-sql-driver/mysql v1.9.3 // indirect
|
||||||
github.com/julienschmidt/httprouter v1.3.0 // indirect
|
github.com/julienschmidt/httprouter v1.3.0 // indirect
|
||||||
github.com/justinas/alice v1.2.0 // indirect
|
github.com/justinas/alice v1.2.0 // indirect
|
||||||
|
|||||||
7
go.sum
7
go.sum
@@ -1,5 +1,12 @@
|
|||||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||||
|
github.com/alexedwards/scs/mysqlstore v0.0.0-20251002162104-209de6e426de h1:/Y/iIFgV1Ofvk4Euv5gUQ74vgqFZOQ1wlJQ3yz/zYGs=
|
||||||
|
github.com/alexedwards/scs/mysqlstore v0.0.0-20251002162104-209de6e426de/go.mod h1:p8jK3D80sw1PFrCSdlcJF1O75bp55HqbgDyyCLM0FrE=
|
||||||
|
github.com/alexedwards/scs/v2 v2.9.0 h1:xa05mVpwTBm1iLeTMNFfAWpKUm4fXAW7CeAViqBVS90=
|
||||||
|
github.com/alexedwards/scs/v2 v2.9.0/go.mod h1:ToaROZxyKukJKT/xLcVQAChi5k6+Pn1Gvmdl7h3RRj8=
|
||||||
|
github.com/go-playground/form/v4 v4.3.0 h1:OVttojbQv2WNCs4P+VnjPtrt/+30Ipw4890W3OaFlvk=
|
||||||
|
github.com/go-playground/form/v4 v4.3.0/go.mod h1:Cpe1iYJKoXb1vILRXEwxpWMGWyQuqplQ/4cvPecy+Jo=
|
||||||
|
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||||
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
|
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
|
||||||
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
||||||
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
|
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
|
||||||
|
|||||||
@@ -14,6 +14,9 @@
|
|||||||
</header>
|
</header>
|
||||||
{{template "nav" .}}
|
{{template "nav" .}}
|
||||||
<main>
|
<main>
|
||||||
|
{{with .Flash}}
|
||||||
|
<div class='flash'>{{.}}</div>
|
||||||
|
{{end}}
|
||||||
{{template "main" .}}
|
{{template "main" .}}
|
||||||
</main>
|
</main>
|
||||||
<footer>
|
<footer>
|
||||||
|
|||||||
Reference in New Issue
Block a user