358 lines
8.1 KiB
Go

package shared
import (
"context"
"encoding/json"
"net/http"
"time"
"git.markbailey.dev/cerbervs/ptpp/app/controller"
"git.markbailey.dev/cerbervs/ptpp/app/session"
"git.markbailey.dev/cerbervs/ptpp/lib/database"
"git.markbailey.dev/cerbervs/ptpp/lib/database/dto"
"git.markbailey.dev/cerbervs/ptpp/lib/logger"
"git.markbailey.dev/cerbervs/ptpp/util"
"git.markbailey.dev/cerbervs/ptpp/view/user"
)
type SignUpHandler struct {
controller.Controller
}
func (c SignUpHandler) Init(s session.IManager, d database.IDB, l logger.ILogger) controller.IController {
c.Logger = l
c.Db = d
c.Session = s
return c
}
type SignInHandler struct {
controller.Controller
}
func (c SignInHandler) Init(s session.IManager, d database.IDB, l logger.ILogger) controller.IController {
c.Logger = l
c.Db = d
c.Session = s
return c
}
type PopulateHandler struct {
controller.Controller
}
func (c PopulateHandler) Init(s session.IManager, d database.IDB, l logger.ILogger) controller.IController {
c.Logger = l
c.Db = d
c.Session = s
return c
}
type SignOutHandler struct {
controller.Controller
}
func (c SignOutHandler) Init(s session.IManager, d database.IDB, l logger.ILogger) controller.IController {
c.Logger = l
c.Db = d
c.Session = s
return c
}
func (c PopulateHandler) Get(w http.ResponseWriter, r *http.Request) error {
existingOrg, err := c.Db.Repo().FindOrganizationByName("CerbervsSoft")
if existingOrg != nil && err == nil {
return util.Redirect(w, r, "/signup", http.StatusSeeOther, false)
}
authToken, err := util.CreateTokenForUser("CerbervsSoft")
if err != nil {
return err
}
organization := dto.Organization{
Name: "CerbervsSoft",
OwnerName: "Mark Bailey",
OwnerEmail: "email@provider.com",
OwnerPhone: "+11111111111",
Authorized: 1,
AuthToken: authToken,
CreatedAt: time.Now(),
}
_, err = c.Db.Repo().CreateOrganization(organization)
if err != nil {
c.Db.Error(err)
return err
}
return util.Redirect(w, r, "/signup", http.StatusSeeOther, false)
}
type UserSignInForm struct {
Username string `json:"username"`
Password string `json:"password"`
}
func (c SignInHandler) Get(w http.ResponseWriter, r *http.Request) error {
sess := c.Session.SessionStart(w, r)
username, ok := r.Context().Value("username").(string)
if ok {
if username == "" {
return failWithFormError(w, r, "Invalid Username or Password.", sess)
} else {
foundUser, err := c.Db.Repo().FindUserByUsername(username)
if foundUser == nil || err != nil {
return failWithFormError(w, r, "Invalid Username or Password.", sess)
}
if foundUser.Admin == 1 {
return util.Redirect(w, r, "/admin/", http.StatusSeeOther, false)
} else {
return util.Redirect(w, r, "/", http.StatusSeeOther, false)
}
}
}
formError, ok := sess.Get("formError").(string)
if !ok {
formError = ""
}
if err := user.SignIn(formError).Render(context.Background(), w); err != nil {
c.Logger.Error(c.Logger.Wrap(err, "Error rendering sign in form"))
return err
}
return nil
}
func (c SignInHandler) Post(w http.ResponseWriter, r *http.Request) error {
sess := c.Session.SessionStart(w, r)
fd := &UserSignInForm{}
jsonDecoder := json.NewDecoder(r.Body)
if err := jsonDecoder.Decode(&fd); err != nil {
c.Logger.Error(c.Logger.Wrap(err, "Error decoding JSON"))
return err
}
foundUser, err := c.Db.Repo().FindUserByUsername(fd.Username)
if foundUser == nil || err != nil {
return util.Redirect(w, r, "/sign-in", http.StatusPermanentRedirect, false)
}
authenticated, err := util.CheckPassword(fd.Password, foundUser.Password)
if err != nil || !authenticated {
return failWithFormError(w, r, "Invalid Username or Password.", sess)
}
err = sess.Set("username", foundUser.Username)
if err != nil {
return err
}
token, err := util.CreateTokenForUser(foundUser.Username)
if err != nil {
c.Logger.Error(c.Logger.Wrap(err, "Error creating token"))
return err
}
err = sess.Set("token", token)
if err != nil {
return err
}
authToken, err := util.CreateTokenForUser(foundUser.Username)
if err != nil {
c.Logger.Error(err)
return err
}
_, err = c.Db.Repo().CreateHeartbeat(&dto.Heartbeat{
User: foundUser.Identifier,
CreatedAt: time.Now(),
IpAddr: r.RemoteAddr,
AuthToken: authToken,
})
if err != nil {
c.Db.Error(err)
return err
}
jwtToken, err := util.CreateTokenForUser(fd.Username)
if err != nil {
return err
}
http.SetCookie(w, &http.Cookie{
Name: "token",
Value: jwtToken,
Expires: time.Now().Add(time.Hour * 1),
Path: "/",
})
err = sess.Delete("formError")
if err != nil {
return err
}
c.Logger.Info(foundUser.Username + " logged in")
if foundUser.Admin == 1 {
return util.Redirect(w, r, "/admin/", http.StatusSeeOther, false)
} else {
return util.Redirect(w, r, "/", http.StatusSeeOther, false)
}
}
type UserSignUpForm struct {
Name string `json:"name"`
Email string `json:"email"`
Username string `json:"username"`
Password string `json:"password"`
PasswordConfirmation string `json:"password_confirmation"`
}
func (c SignUpHandler) Get(w http.ResponseWriter, r *http.Request) error {
sess := c.Session.SessionStart(w, r)
uname, ok := sess.Get("username").(string)
if !ok {
uname = ""
}
if uname != "" {
return util.Redirect(w, r, "/", http.StatusSeeOther, false)
}
if err := user.SignUpForm().Render(context.Background(), w); err != nil {
c.Logger.Error(c.Logger.Wrap(err, "Error rendering sign up form"))
return err
}
return nil
}
func (c SignUpHandler) Post(w http.ResponseWriter, r *http.Request) error {
fd := &UserSignUpForm{}
jsonDecoder := json.NewDecoder(r.Body)
if err := jsonDecoder.Decode(&fd); err == nil {
if fd.Password != fd.PasswordConfirmation {
if _, err := w.Write([]byte("Passwords don't match")); err != nil {
c.Logger.Error(c.Logger.Wrap(err, "Error writing response"))
return err
}
return nil
}
foundUser, err := c.Db.Repo().FindUserByUsername(fd.Username)
if err != nil {
c.Db.Error(err)
return err
}
if foundUser != nil {
if _, err := w.Write([]byte("Invalid username. Please try another")); err != nil {
c.Logger.Error(c.Logger.Wrap(err, "Error writing response"))
}
return nil
}
token, err := util.CreateTokenForUser(fd.Username)
if err != nil {
c.Logger.Error(c.Logger.Wrap(err, "Error creating token"))
return err
}
password, err := util.HashPassword(fd.PasswordConfirmation)
if err != nil {
c.Logger.Error(c.Logger.Wrap(err, "Error hashing password"))
return err
}
org, err := c.Db.Repo().FindOrganizationByName("CerbervsSoft")
if org == nil || err != nil {
c.Db.Error(err)
return err
}
newUser, err := c.Db.Repo().CreateUser(dto.User{
Username: fd.Username,
Password: password,
Name: fd.Name,
Email: fd.Email,
AuthToken: token,
Authorized: 1,
Admin: 1,
Organization: org.Identifier,
})
if err != nil {
c.Db.Error(err)
return err
}
_, err = c.Db.Repo().CreateHeartbeat(&dto.Heartbeat{
User: newUser.Identifier,
CreatedAt: time.Now(),
IpAddr: r.RemoteAddr,
AuthToken: token,
})
if err != nil {
c.Db.Error(err)
return err
}
jwtToken, err := util.CreateTokenForUser(fd.Username)
if err != nil {
return err
}
http.SetCookie(w, &http.Cookie{
Name: "token",
Value: jwtToken,
Expires: time.Now().Add(time.Hour * 1),
Path: "/",
})
return util.Redirect(w, r, "/sign-in", http.StatusSeeOther, false)
}
return nil
}
func (c SignOutHandler) Get(w http.ResponseWriter, r *http.Request) error {
sess := c.Session.SessionStart(w, r)
err := sess.Delete("username")
if err != nil {
return err
}
err = sess.Delete("token")
if err != nil {
return err
}
http.SetCookie(w, &http.Cookie{
Name: "token",
Value: "",
Expires: time.Now().Add(-time.Hour),
Path: "/",
})
return util.Redirect(w, r, "/", http.StatusSeeOther, true)
}
func failWithFormError(w http.ResponseWriter, r *http.Request, formError string, sess session.ISession) error {
err := sess.Set("formError", formError)
if err != nil {
return err
}
return util.Redirect(w, r, r.URL.Path, http.StatusSeeOther, false)
}