260 lines
6.9 KiB
Go

package controllers
import (
"fmt"
"net/http"
"net/url"
"time"
userctx "git.kealoha.me/lks/lenslocked/context"
"git.kealoha.me/lks/lenslocked/models"
"git.kealoha.me/lks/lenslocked/templates"
"git.kealoha.me/lks/lenslocked/views"
)
type Users struct {
Templates struct {
Signup Template
Signin Template
ForgotPass Template
ResetUrlSent Template
ResetPass Template
}
UserService *models.UserService
SessionService *models.SessionService
PassResetService *models.PasswordResetService
EmailService *models.EmailService
}
func (u Users) GetSignup(w http.ResponseWriter, r *http.Request) {
var data struct {
Email string
}
data.Email = r.FormValue("email")
u.Templates.Signup.Execute(w, r, data)
}
func (u Users) PostSignup(w http.ResponseWriter, r *http.Request) {
email := r.FormValue("email")
password := r.FormValue("password")
user, err := u.UserService.Create(email, password)
if err != nil {
fmt.Println(err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
session, err := u.SessionService.Create(user.ID)
if err != nil {
fmt.Println(err)
http.Redirect(w, r, "/signin", http.StatusFound)
return
}
cookie := http.Cookie{
Name: "session",
Value: session.Token,
Path: "/",
HttpOnly: true,
}
http.SetCookie(w, &cookie)
http.Redirect(w, r, "/user", http.StatusFound)
}
func (u Users) GetSignin(w http.ResponseWriter, r *http.Request) {
var data struct {
Email string
}
data.Email = r.FormValue("email")
u.Templates.Signin.Execute(w, r, data)
}
func (u Users) PostSignin(w http.ResponseWriter, r *http.Request) {
var data struct {
Email string
Password string
}
data.Email = r.FormValue("email")
data.Password = r.FormValue("password")
user, err := u.UserService.Authenticate(data.Email, data.Password)
if err != nil {
fmt.Println(err)
http.Error(w, "Something went wrong.", http.StatusInternalServerError)
return
}
session, err := u.SessionService.Create(user.ID)
if err != nil {
fmt.Println(err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
cookie := http.Cookie{
Name: "session",
Value: session.Token,
Path: "/",
HttpOnly: true,
}
http.SetCookie(w, &cookie)
fmt.Fprintf(w, "Current user: %s\n", user.Email)
//http.Redirect(w, r, "/user", http.StatusFound)
}
func (u Users) GetSignout(w http.ResponseWriter, r *http.Request) {
sessionCookie, err := r.Cookie("session")
if err != nil {
http.Redirect(w, r, "/signin", http.StatusFound)
return
}
err = u.SessionService.Delete(sessionCookie.Value)
if err != nil {
fmt.Println(err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
c := http.Cookie{
Name: "session",
MaxAge: -1,
}
http.SetCookie(w, &c)
http.Redirect(w, r, "/signin", http.StatusFound)
}
func (u Users) GetForgotPassword(w http.ResponseWriter, r *http.Request) {
var data struct {
Email string
}
data.Email = r.FormValue("email")
u.Templates.ForgotPass.Execute(w, r, data)
}
func (u Users) PostForgotPassword(w http.ResponseWriter, r *http.Request) {
var data struct {
Email string
}
data.Email = r.FormValue("email")
pwReset, err := u.PassResetService.Create(data.Email)
if err != nil {
fmt.Println(err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
vals := url.Values{
"token": {pwReset.Token},
}
// TODO: Make the URL here configurable and use https
resetURL := "http://" + r.Host + "/reset-pw?" + vals.Encode()
fmt.Println(resetURL)
err = u.EmailService.SendPasswordReset(data.Email, resetURL)
if err != nil {
fmt.Println(err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
u.Templates.ResetUrlSent.Execute(w, r, data)
}
func (u Users) GetResetPass(w http.ResponseWriter, r *http.Request) {
var data struct {
Token string
}
data.Token = r.FormValue("token")
u.Templates.ResetPass.Execute(w, r, data)
}
func (u Users) PostResetPass(w http.ResponseWriter, r *http.Request) {
var data struct {
Token, Password string
}
data.Token = r.FormValue("token")
data.Password = r.FormValue("password")
user, err := u.PassResetService.Consume(data.Token)
if err != nil {
fmt.Println(err)
// TODO: Distinguish between server errors and invalid token errors.
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
err = u.UserService.UpdatePassword(user.ID, data.Password)
if err != nil {
fmt.Println(err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
// Sign the user in now that they have reset their password.
// Any errors from this point onward should redirect to the sign in page.
session, err := u.SessionService.Create(user.ID)
if err != nil {
fmt.Println(err)
http.Redirect(w, r, "/signin", http.StatusFound)
return
}
//setCookie(w, CookieSession, session.Token)
cookie := http.Cookie{
Name: "session",
Value: session.Token,
Path: "/",
HttpOnly: true,
}
http.SetCookie(w, &cookie)
http.Redirect(w, r, "/users/me", http.StatusFound)
}
func (u Users) CurrentUser(w http.ResponseWriter, r *http.Request) {
user := userctx.User(r.Context())
if user == nil {
http.Redirect(w, r, "/signin", http.StatusFound)
return
}
fmt.Fprintf(w, "Current user: %s\n", user.Email)
}
func WithTemplates(user_service *models.UserService, session_service *models.SessionService, email_service *models.EmailService, signup, signin, forgotPass, resetUrlSent, resetPass Template) Users {
u := Users{}
u.Templates.Signup = signup
u.Templates.Signin = signin
u.Templates.ForgotPass = forgotPass
u.Templates.ResetUrlSent = resetUrlSent
u.Templates.ResetPass = resetPass
u.UserService = user_service
u.SessionService = session_service
u.EmailService = email_service
u.PassResetService = &models.PasswordResetService{
DB: u.UserService.DB,
Duration: time.Hour / 2,
}
return u
}
func Default(user_service *models.UserService, session_service *models.SessionService, email_service *models.EmailService) Users {
signup_tpl := views.Must(views.FromFS(templates.FS, "signup.gohtml", "tailwind.gohtml"))
signin_tpl := views.Must(views.FromFS(templates.FS, "signin.gohtml", "tailwind.gohtml"))
pwReset_tpl := views.Must(views.FromFS(templates.FS, "pwReset.gohtml", "tailwind.gohtml"))
pwResetSent_tpl := views.Must(views.FromFS(templates.FS, "pwResetSent.gohtml", "tailwind.gohtml"))
resetPass_tpl := views.Must(views.FromFS(templates.FS, "pwChange.gohtml", "tailwind.gohtml"))
err := signup_tpl.TestTemplate(nil)
if err != nil {
panic(err)
}
err = signin_tpl.TestTemplate(nil)
if err != nil {
panic(err)
}
err = pwReset_tpl.TestTemplate(nil)
if err != nil {
panic(err)
}
err = pwResetSent_tpl.TestTemplate(nil)
if err != nil {
panic(err)
}
return WithTemplates(user_service, session_service, email_service, signup_tpl, signin_tpl, pwReset_tpl, pwResetSent_tpl, resetPass_tpl)
}