lets-go:11.3 user signup
This commit is contained in:
parent
f116991de2
commit
79978c4385
@ -104,6 +104,7 @@ func (app *application) snippetCreatePost(w http.ResponseWriter, r *http.Request
|
|||||||
|
|
||||||
type userSignupForm struct {
|
type userSignupForm struct {
|
||||||
Username string `form:"username"`
|
Username string `form:"username"`
|
||||||
|
Email string `form:"email"`
|
||||||
Password string `form:"password"`
|
Password string `form:"password"`
|
||||||
validator.Validator `form:"-"`
|
validator.Validator `form:"-"`
|
||||||
}
|
}
|
||||||
@ -129,7 +130,10 @@ func (app *application) userSignupPost(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
form.CheckField(validator.NotBlank(form.Username), "username", "This field cannot be blank")
|
form.CheckField(validator.NotBlank(form.Username), "username", "This field cannot be blank")
|
||||||
|
form.CheckField(validator.NotBlank(form.Email), "email", "This field cannot be blank")
|
||||||
|
form.CheckField(validator.Matches(form.Email, validator.EmailRX), "email", "This field must be a valid email address")
|
||||||
form.CheckField(validator.NotBlank(form.Password), "password", "This field cannot be blank")
|
form.CheckField(validator.NotBlank(form.Password), "password", "This field cannot be blank")
|
||||||
|
form.CheckField(validator.MinChars(form.Password, 8), "password", "This field must be at least 8 characters long")
|
||||||
|
|
||||||
if !form.Valid() {
|
if !form.Valid() {
|
||||||
data := app.newTemplateData(r)
|
data := app.newTemplateData(r)
|
||||||
@ -138,13 +142,13 @@ func (app *application) userSignupPost(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
id, err := app.users.Insert(form.Username, form.Password)
|
// id, err := app.users.Insert(form.Username, form.Email, form.Password)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
app.serverError(w, r, err)
|
// app.serverError(w, r, err)
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
|
||||||
app.sessionManager.Put(r.Context(), "flash", fmt.Sprintf("CREATED A USER! (%d)", id))
|
app.sessionManager.Put(r.Context(), "flash", fmt.Sprintf("CREATED A USER! (%d)", 1))
|
||||||
|
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
|
@ -23,8 +23,10 @@ CREATE INDEX sessions_expiry_idx on sessions(expiry);
|
|||||||
|
|
||||||
CREATE TABLE users (
|
CREATE TABLE users (
|
||||||
id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
username VARCHAR(20) NOT NULL,
|
username VARCHAR(255) NOT NULL,
|
||||||
password VARCHAR(20) NOT NULL,
|
email VARCHAR(255) NOT NULL,
|
||||||
|
hashed_password CHAR(60) NOT NULL,
|
||||||
created DATETIME NOT NULL,
|
created DATETIME NOT NULL,
|
||||||
UNIQUE (username)
|
);
|
||||||
)
|
|
||||||
|
ALTER TABLE users ADD CONSTRAINT users_uc_email UNIQUE (email);
|
||||||
|
@ -4,4 +4,9 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrNoRecord = errors.New("models: no matching record found")
|
var (
|
||||||
|
ErrNoRecord = errors.New("models: no matching record found")
|
||||||
|
ErrInvalidCredentials = errors.New("models: invalid credentials")
|
||||||
|
ErrDuplicateEmail = errors.New("models: duplicate email")
|
||||||
|
|
||||||
|
)
|
||||||
|
@ -2,11 +2,15 @@ package models
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
Username string
|
ID int
|
||||||
|
Username string
|
||||||
|
Email string
|
||||||
|
HashedPassword []byte
|
||||||
|
Created time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserModel struct {
|
type UserModel struct {
|
||||||
@ -14,37 +18,29 @@ type UserModel struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Insert
|
// Insert
|
||||||
func (m *UserModel) Insert(username string, password string) (int, error) {
|
func (m *UserModel) Insert(username, email, password string) (int, error) {
|
||||||
stmt := `INSERT INTO users (username, password, created)
|
// stmt := `INSERT INTO users (username, email, password, created)
|
||||||
VALUES(?, ?, UTC_TIMESTAMP())`
|
// VALUES(?, ?, ?, UTC_TIMESTAMP())`
|
||||||
|
|
||||||
result, err := m.DB.Exec(stmt, username, password)
|
// result, err := m.DB.Exec(stmt, username, email, password)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return 0, err
|
// return 0, err
|
||||||
}
|
// }
|
||||||
|
|
||||||
id, err := result.LastInsertId()
|
// id, err := result.LastInsertId()
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return 0, err
|
// return 0, err
|
||||||
}
|
// }
|
||||||
|
|
||||||
return int(id), nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get
|
// Authenticate
|
||||||
func (m *UserModel) Get(id int) (User, error) {
|
func (m *UserModel) Authenticate(email int, password string) (int, error) {
|
||||||
stmt := "SELECT id, username FROM users WHERE id = ?"
|
return 0, nil
|
||||||
|
}
|
||||||
var u User
|
|
||||||
|
// Exists
|
||||||
err := m.DB.QueryRow(stmt, id).Scan(&u.Username)
|
func (m *UserModel) Exists(id int) (bool, error) {
|
||||||
if err != nil {
|
return false, nil
|
||||||
if errors.Is(err, sql.ErrNoRows) {
|
|
||||||
return User{}, ErrNoRecord
|
|
||||||
} else {
|
|
||||||
return User{}, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return u, nil
|
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
package validator
|
package validator
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"regexp"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var EmailRX = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")
|
||||||
|
|
||||||
type Validator struct {
|
type Validator struct {
|
||||||
FieldErrors map[string]string
|
FieldErrors map[string]string
|
||||||
}
|
}
|
||||||
@ -41,6 +44,15 @@ func MaxChars(value string, n int) bool {
|
|||||||
return utf8.RuneCountInString(value) <= n
|
return utf8.RuneCountInString(value) <= n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MinChars(value string, n int) bool {
|
||||||
|
return n <= utf8.RuneCountInString(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Matches ...
|
||||||
|
func Matches(value string, rx *regexp.Regexp) bool {
|
||||||
|
return rx.MatchString(value)
|
||||||
|
}
|
||||||
|
|
||||||
// PermittedValue[T comparable] ...
|
// PermittedValue[T comparable] ...
|
||||||
func PermittedValue[T comparable](value T, permittedValues ...T) bool {
|
func PermittedValue[T comparable](value T, permittedValues ...T) bool {
|
||||||
return slices.Contains(permittedValues, value)
|
return slices.Contains(permittedValues, value)
|
||||||
|
@ -9,12 +9,19 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
<input type='text' name='username' value='{{.Form.Username}}'>
|
<input type='text' name='username' value='{{.Form.Username}}'>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>Email:</label>
|
||||||
|
{{with .Form.FieldErrors.email}}
|
||||||
|
<label class='error'>{{.}}</label>
|
||||||
|
{{end}}
|
||||||
|
<input type='text' name='email' value='{{.Form.Email}}'>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label>Password:</label>
|
<label>Password:</label>
|
||||||
{{with .Form.FieldErrors.password}}
|
{{with .Form.FieldErrors.password}}
|
||||||
<label class='error'>{{.}}</label>
|
<label class='error'>{{.}}</label>
|
||||||
{{end}}
|
{{end}}
|
||||||
<input type='text' name='password' value='{{.Form.Password}}'>
|
<input type='text' name='password'>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<input type='submit' value='Signup'>
|
<input type='submit' value='Signup'>
|
||||||
|
Loading…
Reference in New Issue
Block a user