Add registration/login/logout

This commit is contained in:
m15o 2021-11-22 10:26:30 +01:00
parent 8b054e454a
commit 9425061964
10 changed files with 260 additions and 6 deletions

View file

@ -12,6 +12,17 @@ func (s *Storage) queryUser(q string, params ...interface{}) (user model.User, e
return return
} }
func (s *Storage) VerifyUser(user model.User) (model.User, error) {
u, err := s.queryUser(queryFindName, user.Name)
if err != nil {
return u, err
}
if err := user.CompareHashToPassword(u.Hash); err != nil {
return u, err
}
return u, nil
}
func (s *Storage) UserExists(name string) bool { func (s *Storage) UserExists(name string) bool {
var rv bool var rv bool
s.db.QueryRow(`SELECT true FROM users WHERE name=lower($1)`, name).Scan(&rv) s.db.QueryRow(`SELECT true FROM users WHERE name=lower($1)`, name).Scan(&rv)
@ -66,4 +77,3 @@ func (s *Storage) DeleteUser(username string) error {
_, err = stmt.Exec(username) _, err = stmt.Exec(username)
return err return err
} }

View file

@ -58,11 +58,11 @@ func New(cfg *config.Config, sess *session.Session, data *storage.Storage) (http
// Index // Index
router.HandleFunc("/", h.showIndexView).Methods(http.MethodGet) router.HandleFunc("/", h.showIndexView).Methods(http.MethodGet)
//router.HandleFunc("/login", h.showLoginView).Methods(http.MethodGet) router.HandleFunc("/login", h.showLoginView).Methods(http.MethodGet)
//router.HandleFunc("/check-login", h.checkLogin).Methods(http.MethodPost) router.HandleFunc("/check-login", h.checkLogin).Methods(http.MethodPost)
//router.HandleFunc("/help", h.showHelpView).Methods(http.MethodGet) //router.HandleFunc("/help", h.showHelpView).Methods(http.MethodGet)
//router.HandleFunc("/profile/{username}", h.showProfileView).Methods(http.MethodGet) //router.HandleFunc("/profile/{username}", h.showProfileView).Methods(http.MethodGet)
//router.HandleFunc("/register", h.handleRegister) router.HandleFunc("/register", h.handleRegister)
//router.HandleFunc("/editor", h.handleEditor) //router.HandleFunc("/editor", h.handleEditor)
//router.HandleFunc("/upload", h.upload) //router.HandleFunc("/upload", h.upload)
//router.HandleFunc("/files", h.handleFiles) //router.HandleFunc("/files", h.handleFiles)
@ -71,7 +71,7 @@ func New(cfg *config.Config, sess *session.Session, data *storage.Storage) (http
//router.HandleFunc("/homepages", h.showHomepagesView).Methods(http.MethodGet) //router.HandleFunc("/homepages", h.showHomepagesView).Methods(http.MethodGet)
//router.HandleFunc("/rename", h.handleRename) //router.HandleFunc("/rename", h.handleRename)
//router.HandleFunc("/delete", h.handleDelete).Methods(http.MethodPost) //router.HandleFunc("/delete", h.handleDelete).Methods(http.MethodPost)
//router.HandleFunc("/logout", h.logout).Methods(http.MethodGet) router.HandleFunc("/logout", h.logout).Methods(http.MethodGet)
//router.PathPrefix("/").Handler(http.FileServer(http.Dir(cfg.AssetsDir))) //router.PathPrefix("/").Handler(http.FileServer(http.Dir(cfg.AssetsDir)))
return router, nil return router, nil

View file

@ -5,5 +5,45 @@ package handler
var TplMap = map[string]string{ var TplMap = map[string]string{
"index": `{{ define "content" }} "index": `{{ define "content" }}
<p>This is the index</p> <p>This is the index</p>
{{ end }}`,
"login": `{{ define "content" }}
<h1>Login</h1>
{{ if .form.Error }}
<p>{{ .form.Error }}</p>
{{ end }}
<form action="/check-login" method="post" class="auth-form">
<div class="field">
<label for="name">Username</label>
<input type="text" id="name" name="name" autocomplete="off" required autofocus/>
</div>
<div class="field">
<label for="password">Password</label>
<input type="password" id="password" name="password" required/>
</div>
<br>
<input type="submit" value="Submit">
</form>
{{ end }}`,
"register": `{{ define "content" }}
<h1>Register</h1>
{{ if .form.Error }}
<p>{{ .form.Error }}</p>
{{ end }}
<form action="/register" method="post" class="auth-form">
<div class="field">
<label for="name">Username</label>
<input type="text" id="name" name="name" autocomplete="off" required autofocus/>
</div>
<div class="field">
<label for="password">Password</label>
<input type="password" id="password" name="password" required/>
</div>
<div class="field">
<label for="password-confirm">Confirm password</label>
<input type="password" id="password-confirm" name="password-confirm" required/>
</div>
<br>
<input type="submit" value="Submit">
</form>
{{ end }}`, {{ end }}`,
} }

View file

@ -0,0 +1,18 @@
{{ define "content" }}
<h1>Login</h1>
{{ if .form.Error }}
<p>{{ .form.Error }}</p>
{{ end }}
<form action="/check-login" method="post" class="auth-form">
<div class="field">
<label for="name">Username</label>
<input type="text" id="name" name="name" autocomplete="off" required autofocus/>
</div>
<div class="field">
<label for="password">Password</label>
<input type="password" id="password" name="password" required/>
</div>
<br>
<input type="submit" value="Submit">
</form>
{{ end }}

View file

@ -0,0 +1,22 @@
{{ define "content" }}
<h1>Register</h1>
{{ if .form.Error }}
<p>{{ .form.Error }}</p>
{{ end }}
<form action="/register" method="post" class="auth-form">
<div class="field">
<label for="name">Username</label>
<input type="text" id="name" name="name" autocomplete="off" required autofocus/>
</div>
<div class="field">
<label for="password">Password</label>
<input type="password" id="password" name="password" required/>
</div>
<div class="field">
<label for="password-confirm">Confirm password</label>
<input type="password" id="password-confirm" name="password-confirm" required/>
</div>
<br>
<input type="submit" value="Submit">
</form>
{{ end }}

View file

@ -18,5 +18,5 @@ func (h *Handler) showIndexView(w http.ResponseWriter, r *http.Request) {
// "News": news, // "News": news,
//}, user) //}, user)
h.renderLayout(w, "index", map[string]interface{}{}, "") h.renderLayout(w, "index", nil, "")
} }

View file

@ -0,0 +1,27 @@
package handler
import (
"net/http"
"status/model"
"status/web/handler/form"
)
func (h *Handler) checkLogin(w http.ResponseWriter, r *http.Request) {
f := form.NewLoginForm(r)
user, err := h.storage.VerifyUser(model.User{
Name: f.Username,
Password: f.Password,
})
if err != nil {
f.Error = "incorrect password"
h.renderLayout(w, "login", map[string]interface{}{
"form": f,
}, "")
return
}
if err := h.sess.Save(r, w, user.Name); err != nil {
serverError(w, err)
return
}
http.Redirect(w, r, "/", http.StatusFound)
}

View file

@ -0,0 +1,9 @@
package handler
import (
"net/http"
)
func (h *Handler) showLoginView(w http.ResponseWriter, r *http.Request) {
h.renderLayout(w, "login", nil, "")
}

11
web/handler/logout.go Normal file
View file

@ -0,0 +1,11 @@
package handler
import "net/http"
func (h *Handler) logout(w http.ResponseWriter, r *http.Request) {
if err := h.sess.Delete(w, r); err != nil {
serverError(w, err)
return
}
http.Redirect(w, r, "/", http.StatusFound)
}

117
web/handler/register.go Normal file
View file

@ -0,0 +1,117 @@
package handler
import (
"bytes"
"errors"
"html/template"
"log"
"net/http"
"status/model"
"status/web/handler/form"
)
func (h *Handler) handleRegister(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
h.showRegisterView(w, r)
case "POST":
h.register(w, r)
}
}
func (h *Handler) showRegisterView(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
h.renderLayout(w, "register", nil, "")
}
}
type keyStatus struct {
Success bool `json:"success"`
Uses int `json:"uses"`
}
//func verifyKey(key string) error {
// hc := http.Client{}
//
// f := url.Values{}
// f.Add("product_permalink", "YccHL")
// f.Add("license_key", key)
// req, err := http.NewRequest("POST", "https://api.gumroad.com/v2/licenses/verify", strings.NewReader(f.Encode()))
// if err != nil {
// return err
// }
// req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
//
// resp, err := hc.Do(req)
// if err != nil {
// return err
// }
//
// if resp.Body != nil {
// defer resp.Body.Close()
// }
//
// body, readErr := ioutil.ReadAll(resp.Body)
// if readErr != nil {
// return err
// }
//
// ks := keyStatus{}
// jsonErr := json.Unmarshal(body, &ks)
// if jsonErr != nil {
// return err
// }
//
// if ks.Success != true || ks.Uses > 1 {
// return errors.New("invalid license key")
// }
//
// return nil
//}
func buildIndex(t *template.Template, name string) []byte {
html := bytes.NewBufferString("")
err := t.Execute(html, name)
if err != nil {
log.Fatal(err)
}
return html.Bytes()
}
func (h *Handler) register(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "POST":
f := form.NewHomepageForm(r)
showError := func(err error) {
f.Error = err.Error()
h.renderLayout(w, "register", map[string]interface{}{"form": *f}, "")
return
}
if err := f.Validate(); err != nil {
showError(err)
return
}
user := model.User{
Name: f.Username,
Password: f.Password,
}
if err := user.Validate(); err != nil {
showError(err)
return
}
if h.storage.UserExists(user.Name) {
showError(errors.New("username already exists"))
return
}
if err := h.storage.CreateUser(user); err != nil {
showError(err)
return
}
if err := h.sess.Save(r, w, r.FormValue("name")); err != nil {
serverError(w, err)
return
}
h.renderLayout(w, "index", nil, user.Name)
}
}