diff --git a/snippetbox/cmd/web/handlers.go b/snippetbox/cmd/web/handlers.go index bc2736c..a7921c2 100644 --- a/snippetbox/cmd/web/handlers.go +++ b/snippetbox/cmd/web/handlers.go @@ -101,3 +101,50 @@ func (app *application) snippetCreatePost(w http.ResponseWriter, r *http.Request http.Redirect(w, r, fmt.Sprintf("/snippet/view/%d", id), http.StatusSeeOther) } + +type userSignupForm struct { + Username string `form:"username"` + Password string `form:"password"` + validator.Validator `form:"-"` +} + +// userSignup ... +func (app *application) userSignup(w http.ResponseWriter, r *http.Request) { + data := app.newTemplateData(r) + + data.Form = userSignupForm{} + + app.render(w, r, http.StatusOK, "signup.tmpl", data) +} + + +// userSignup ... +func (app *application) userSignupPost(w http.ResponseWriter, r *http.Request) { + var form userSignupForm + + err := app.decodePostForm(r, &form) + if err != nil { + app.clientError(w, http.StatusBadRequest) + return + } + + form.CheckField(validator.NotBlank(form.Username), "username", "This field cannot be blank") + form.CheckField(validator.NotBlank(form.Password), "password", "This field cannot be blank") + + if !form.Valid() { + data := app.newTemplateData(r) + data.Form = form + app.render(w, r, http.StatusUnprocessableEntity, "signup.tmpl", data) + return + } + + id, err := app.users.Insert(form.Username, form.Password) + if err != nil { + app.serverError(w, r, err) + return + } + + app.sessionManager.Put(r.Context(), "flash", fmt.Sprintf("CREATED A USER! (%d)", id)) + + http.Redirect(w, r, "/", http.StatusSeeOther) +} diff --git a/snippetbox/cmd/web/main.go b/snippetbox/cmd/web/main.go index 16c7862..708ffe6 100644 --- a/snippetbox/cmd/web/main.go +++ b/snippetbox/cmd/web/main.go @@ -23,6 +23,7 @@ import ( type application struct { logger *slog.Logger snippets *models.SnippetModel + users *models.UserModel templateCache map[string]*template.Template formDecoder *form.Decoder sessionManager *scs.SessionManager @@ -63,6 +64,7 @@ func main() { app := &application{ logger: logger, snippets: &models.SnippetModel{DB: db}, + users: &models.UserModel{DB: db}, templateCache: templateCache, formDecoder: formDecoder, sessionManager: sessionManager, diff --git a/snippetbox/cmd/web/routes.go b/snippetbox/cmd/web/routes.go index d2cb8c5..a5fc868 100644 --- a/snippetbox/cmd/web/routes.go +++ b/snippetbox/cmd/web/routes.go @@ -29,6 +29,10 @@ func (app *application) routes() http.Handler { 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)) + router.Handler(http.MethodGet, "/user/signup", dynamic.ThenFunc(app.userSignup)) + router.Handler(http.MethodPost, "/user/signup", dynamic.ThenFunc(app.userSignupPost)) + // router.Handler(http.MethodGet, "/user/login", dynamic.ThenFunc(app.userLogin)) + // router.Handler(http.MethodPost, "/user/login", dynamic.ThenFunc(app.userLoginPost)) standard := alice.New(app.recoverPanic, app.logRequest, secureHeaders) diff --git a/snippetbox/db/initdb.d/init.sql b/snippetbox/db/initdb.d/init.sql index e7f49e3..eb83090 100644 --- a/snippetbox/db/initdb.d/init.sql +++ b/snippetbox/db/initdb.d/init.sql @@ -20,3 +20,11 @@ CREATE TABLE sessions ( ); CREATE INDEX sessions_expiry_idx on sessions(expiry); + +CREATE TABLE users ( + id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, + username VARCHAR(20) NOT NULL, + password VARCHAR(20) NOT NULL, + created DATETIME NOT NULL, + UNIQUE (username) +) diff --git a/snippetbox/internal/models/users.go b/snippetbox/internal/models/users.go new file mode 100644 index 0000000..b12fe4d --- /dev/null +++ b/snippetbox/internal/models/users.go @@ -0,0 +1,50 @@ +package models + +import ( + "database/sql" + "errors" +) + +type User struct { + Username string +} + +type UserModel struct { + DB *sql.DB +} + +// Insert +func (m *UserModel) Insert(username string, password string) (int, error) { + stmt := `INSERT INTO users (username, password, created) + VALUES(?, ?, UTC_TIMESTAMP())` + + result, err := m.DB.Exec(stmt, username, password) + if err != nil { + return 0, err + } + + id, err := result.LastInsertId() + if err != nil { + return 0, err + } + + return int(id), nil +} + +// Get +func (m *UserModel) Get(id int) (User, error) { + stmt := "SELECT id, username FROM users WHERE id = ?" + + var u User + + err := m.DB.QueryRow(stmt, id).Scan(&u.Username) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return User{}, ErrNoRecord + } else { + return User{}, err + } + } + + return u, nil +} diff --git a/snippetbox/ui/html/pages/signup.tmpl b/snippetbox/ui/html/pages/signup.tmpl new file mode 100644 index 0000000..14d2e00 --- /dev/null +++ b/snippetbox/ui/html/pages/signup.tmpl @@ -0,0 +1,22 @@ +{{define "title"}}User Signup{{end}} + +{{define "main"}} +
+
+ + {{with .Form.FieldErrors.username}} + + {{end}} + +
+
+ + {{with .Form.FieldErrors.password}} + + {{end}} + +
+
+ +
+{{end}}