WIP: cyclic import problem

This commit is contained in:
Mark Bailey 2024-11-24 06:23:37 -05:00
parent 1ddf8f333e
commit 114b7b1387
48 changed files with 947 additions and 710 deletions

19
app/app.go Normal file
View File

@ -0,0 +1,19 @@
package app
import (
"git.markbailey.dev/cerbervs/ptpp/app/routing"
"git.markbailey.dev/cerbervs/ptpp/app/server"
)
type IApp interface {
Serve()
}
type App struct {
Router routing.IRouter
Server server.IServer
}
func (a App) Serve() {
a.Server.ListenAndServe()
}

View File

@ -0,0 +1,97 @@
package controller
import (
"git.markbailey.dev/cerbervs/ptpp/lib/logger"
"net/http"
"git.markbailey.dev/cerbervs/ptpp/lib/database"
werror "git.markbailey.dev/cerbervs/ptpp/lib/error"
"git.markbailey.dev/cerbervs/ptpp/lib/session"
)
type IController interface {
Get(w http.ResponseWriter, r *http.Request) error
Head(w http.ResponseWriter, r *http.Request) error
Options(w http.ResponseWriter, r *http.Request) error
Trace(w http.ResponseWriter, r *http.Request) error
Put(w http.ResponseWriter, r *http.Request) error
Delete(w http.ResponseWriter, r *http.Request) error
Post(w http.ResponseWriter, r *http.Request) error
Patch(w http.ResponseWriter, r *http.Request) error
Connect(w http.ResponseWriter, r *http.Request) error
Init(session.IManager, database.IDB, logger.ILogger) error
}
type Controller struct {
Session session.IManager
Db database.IDB
Logger logger.ILogger
}
func (c Controller) Init(s session.IManager, d database.IDB, l logger.ILogger) error {
return werror.Wrap(nil, "You must implement the init method in your extended controller")
}
func (c Controller) Get(w http.ResponseWriter, _ *http.Request) (_ error) {
if _, err := w.Write([]byte("not implemented")); err != nil {
c.Logger.Error(c.Logger.Wrap(err, "Error writing response"))
return err
}
return nil
}
func (c Controller) Head(w http.ResponseWriter, _ *http.Request) (_ error) {
if _, err := w.Write([]byte("not implemented")); err != nil {
c.Logger.Error(c.Logger.Wrap(err, "Error writing response"))
return err
}
return nil
}
func (c Controller) Options(w http.ResponseWriter, _ *http.Request) (_ error) {
if _, err := w.Write([]byte("not implemented")); err != nil {
c.Logger.Error(c.Logger.Wrap(err, "Error writing response"))
return err
}
return nil
}
func (c Controller) Trace(w http.ResponseWriter, _ *http.Request) (_ error) {
if _, err := w.Write([]byte("not implemented")); err != nil {
c.Logger.Error(c.Logger.Wrap(err, "Error writing response"))
return err
}
return nil
}
func (c Controller) Put(w http.ResponseWriter, _ *http.Request) (_ error) {
if _, err := w.Write([]byte("not implemented")); err != nil {
c.Logger.Error(c.Logger.Wrap(err, "Error writing response"))
return err
}
return nil
}
func (c Controller) Delete(w http.ResponseWriter, _ *http.Request) (_ error) {
if _, err := w.Write([]byte("not implemented")); err != nil {
c.Logger.Error(c.Logger.Wrap(err, "Error writing response"))
return err
}
return nil
}
func (c Controller) Post(w http.ResponseWriter, _ *http.Request) (_ error) {
if _, err := w.Write([]byte("not implemented")); err != nil {
c.Logger.Error(c.Logger.Wrap(err, "Error writing response"))
return err
}
return nil
}
func (c Controller) Patch(w http.ResponseWriter, _ *http.Request) (_ error) {
if _, err := w.Write([]byte("not implemented")); err != nil {
c.Logger.Error(c.Logger.Wrap(err, "Error writing response"))
return err
}
return nil
}
func (c Controller) Connect(w http.ResponseWriter, _ *http.Request) (_ error) {
if _, err := w.Write([]byte("not implemented")); err != nil {
c.Logger.Error(c.Logger.Wrap(err, "Error writing response"))
return err
}
return nil
}

13
app/handler/handler.go Normal file
View File

@ -0,0 +1,13 @@
package handler
import "net/http"
type Handler func(http.ResponseWriter, *http.Request) error
func (fn Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if err := fn(w, r); err != nil {
w.Header().Set("HX-Retarget", "#layout_content")
w.Header().Set("HX-Reswap", "innerHTML")
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}

121
app/routing/router.go Normal file
View File

@ -0,0 +1,121 @@
package routing
import (
"git.markbailey.dev/cerbervs/ptpp/app/controller"
"git.markbailey.dev/cerbervs/ptpp/app/handler"
"git.markbailey.dev/cerbervs/ptpp/lib/database"
werror "git.markbailey.dev/cerbervs/ptpp/lib/error"
"git.markbailey.dev/cerbervs/ptpp/lib/logger"
"git.markbailey.dev/cerbervs/ptpp/lib/middleware"
"git.markbailey.dev/cerbervs/ptpp/lib/session"
"git.markbailey.dev/cerbervs/ptpp/util/shared"
"log"
"net/http"
)
var sess session.IManager
type IRouter interface {
RegisterRoutes() http.Handler
}
type Route struct {
Controller controller.IController
Path string
Name string
}
type Router struct {
Mux *http.ServeMux
BasePath string
Routes []Route
SubRouters *[]Router
Middleware *[]middleware.Func
}
func (r Router) HandleAllRequestMethods(route Route) {
r.Mux.Handle("GET "+r.BasePath+route.Path, handler.Handler(route.Controller.Get))
r.Mux.Handle("OPTIONS "+r.BasePath+route.Path, handler.Handler(route.Controller.Options))
r.Mux.Handle("TRACE "+r.BasePath+route.Path, handler.Handler(route.Controller.Trace))
r.Mux.Handle("PUT "+r.BasePath+route.Path, handler.Handler(route.Controller.Put))
r.Mux.Handle("DELETE "+r.BasePath+route.Path, handler.Handler(route.Controller.Delete))
r.Mux.Handle("POST "+r.BasePath+route.Path, handler.Handler(route.Controller.Post))
r.Mux.Handle("PATCH "+r.BasePath+route.Path, handler.Handler(route.Controller.Patch))
r.Mux.Handle("CONNECT "+r.BasePath+route.Path, handler.Handler(route.Controller.Connect))
}
func (r Router) RegisterRoutes() http.Handler {
if r.Mux == nil {
r.Mux = http.NewServeMux()
}
for _, route := range r.Routes {
if err := route.Controller.Init(sess, database.ChooseDB(), logger.NewCompositeLogger()); err != nil {
panic(err)
}
r.HandleAllRequestMethods(route)
}
if r.SubRouters != nil {
for _, subRouter := range *r.SubRouters {
sr := subRouter.RegisterRoutes()
r.Mux.Handle("GET "+r.BasePath+subRouter.BasePath, sr)
}
}
if r.Middleware != nil {
mw := middleware.Compose(*r.Middleware)
return mw(r.Mux)
}
return r.Mux
}
func (r Router) RegisterFs() {
if r.Mux == nil {
r.Mux = http.NewServeMux()
}
fs := http.FileServer(http.Dir(shared.GetFullyQualifiedPath("/public")))
log.Println("Serving static files from: " + shared.GetFullyQualifiedPath("/public"))
r.Mux.Handle("GET "+r.BasePath+"public/", http.StripPrefix("/public/", fs))
}
type RouteMapping struct {
Path string
Name string
}
func GetFlatRouteList(r Router) []RouteMapping {
var routes []RouteMapping
for _, route := range r.Routes {
routes = append(routes, RouteMapping{Path: route.Path, Name: route.Name})
}
for _, subRouter := range *r.SubRouters {
routes = append(routes, GetFlatRouteList(subRouter)...)
}
return routes
}
func GetRouteByName(name string) (string, error) {
r := AppRouter
for _, route := range GetFlatRouteList(r) {
if route.Name == name {
return route.Path, nil
}
}
return "", werror.Wrap(nil, "Route not found")
}
func init() {
var err error
sess, err = session.NewManager("memory", "ptpp", 3600)
if err != nil {
panic(werror.Wrap(err, "Error creating session manager"))
}
}

32
app/routing/routes.go Normal file
View File

@ -0,0 +1,32 @@
package routing
import (
"git.markbailey.dev/cerbervs/ptpp/handlers/admin"
"git.markbailey.dev/cerbervs/ptpp/handlers/shared"
"git.markbailey.dev/cerbervs/ptpp/lib/middleware"
"net/http"
)
var AppRouter = Router{
Mux: http.NewServeMux(),
BasePath: "/",
Routes: []Route{
{Controller: &shared.HomePageHandler{}, Path: "", Name: "app.index"},
{Controller: &shared.SignUpHandler{}, Path: "sign-up", Name: "app.user.sign_up"},
{Controller: &shared.SignInHandler{}, Path: "sign-in", Name: "app.user.sign_in"},
{Controller: &shared.SignOutHandler{}, Path: "sign-out", Name: "app.user.sign_out"},
{Controller: &shared.PopulateHandler{}, Path: "populate", Name: "app.populate"},
},
SubRouters: &[]Router{
{
Mux: nil,
BasePath: "admin/",
Routes: []Route{
{Controller: &admin.IndexHandler{}, Path: "", Name: "app.admin.index"},
{Controller: &admin.IndexHandler{}, Path: "butt", Name: "app.admin.butt"},
},
SubRouters: nil,
Middleware: &[]middleware.Func{middleware.WithAuth},
},
},
}

25
app/server/server.go Normal file
View File

@ -0,0 +1,25 @@
package server
import (
"log"
"net/http"
"strconv"
)
type IServer interface {
ListenAndServe()
}
type Server struct {
Addr string
Server http.Server
Port int
}
func (s *Server) ListenAndServe() {
s.Addr = s.Addr + ":" + strconv.Itoa(s.Port)
serverError := s.Server.ListenAndServe()
if serverError != nil {
log.Fatal(serverError)
}
}

View File

@ -1,10 +1,61 @@
package main package main
import ( import (
inf "git.markbailey.dev/cervers/ptpp/infrastructure" "git.markbailey.dev/cerbervs/ptpp/app"
_ "git.markbailey.dev/cervers/ptpp/lib/session/memory" "git.markbailey.dev/cerbervs/ptpp/app/routing"
"git.markbailey.dev/cerbervs/ptpp/app/server"
"net/http"
"os"
"strconv"
"time"
_ "git.markbailey.dev/cerbervs/ptpp/lib/session/memory"
) )
func main() { func main() {
inf.NewServer().Serve() const (
addr = "0.0.0.0"
prodPort = 8080
devPort = 8080
)
r := routing.AppRouter
rh := r.RegisterRoutes()
r.RegisterFs()
var port int
if os.Getenv("HTMX_APP_ENV") == "production" {
port = prodPort
} else {
port = devPort
}
s := server.Server{
Addr: addr,
Server: http.Server{
Addr: addr + ":" + strconv.Itoa(port),
Handler: rh,
DisableGeneralOptionsHandler: false,
TLSConfig: nil,
ReadTimeout: 5 * time.Second,
ReadHeaderTimeout: 0,
WriteTimeout: 5 * time.Second,
IdleTimeout: 0,
MaxHeaderBytes: 0,
TLSNextProto: nil,
ConnState: nil,
ErrorLog: nil,
BaseContext: nil,
ConnContext: nil,
},
Port: port,
}
a := app.App{
Router: r,
Server: &s,
}
a.Serve()
} }

2
go.mod
View File

@ -1,4 +1,4 @@
module git.markbailey.dev/cervers/ptpp module git.markbailey.dev/cerbervs/ptpp
go 1.23.2 go 1.23.2

View File

@ -3,32 +3,34 @@ package admin
import ( import (
"context" "context"
"errors" "errors"
"git.markbailey.dev/cervers/ptpp/view/layout" "git.markbailey.dev/cerbervs/ptpp/app/controller"
"git.markbailey.dev/cerbervs/ptpp/lib/database"
"git.markbailey.dev/cerbervs/ptpp/lib/logger"
"git.markbailey.dev/cerbervs/ptpp/lib/session"
"git.markbailey.dev/cerbervs/ptpp/view/layout"
"net/http" "net/http"
"git.markbailey.dev/cervers/ptpp/lib/logger" "git.markbailey.dev/cerbervs/ptpp/view/admin"
"git.markbailey.dev/cervers/ptpp/lib/session"
"git.markbailey.dev/cervers/ptpp/view/admin"
) )
type IndexHandler struct { type IndexHandler struct {
logger logger.ILogger controller.Controller
session session.IManager
} }
func NewAdminIndexHandler(l logger.ILogger, s session.IManager) *IndexHandler { func (h IndexHandler) Init(s session.IManager, d database.IDB, l logger.ILogger) error {
return &IndexHandler{ h.Logger = l
logger: l, h.Db = d
session: s, h.Session = s
}
return nil
} }
func (h IndexHandler) Index(w http.ResponseWriter, r *http.Request) error { func (h IndexHandler) Get(w http.ResponseWriter, r *http.Request) error {
if r.URL.Path != "/" { if r.URL.Path != "/" {
w.WriteHeader(http.StatusNotFound) w.WriteHeader(http.StatusNotFound)
err := layout.NotFound().Render(context.Background(), w) err := layout.NotFound().Render(context.Background(), w)
if err != nil { if err != nil {
h.logger.Error(h.logger.Wrap(err, "Error rendering 404 page")) h.Logger.Error(h.Logger.Wrap(err, "Error rendering 404 page"))
return err return err
} }
return nil return nil
@ -37,12 +39,12 @@ func (h IndexHandler) Index(w http.ResponseWriter, r *http.Request) error {
username, ok := r.Context().Value("username").(string) username, ok := r.Context().Value("username").(string)
if !ok { if !ok {
err := errors.New("cannot decode request context: for key \"username\"") err := errors.New("cannot decode request context: for key \"username\"")
h.logger.Error(h.logger.Wrap(err, "Error decoding request context")) h.Logger.Error(h.Logger.Wrap(err, "Error decoding request context"))
return err return err
} }
if err := admin.Index(username).Render(context.Background(), w); err != nil { if err := admin.Index(username).Render(context.Background(), w); err != nil {
h.logger.Error(h.logger.Wrap(err, "Error rendering admin index page")) h.Logger.Error(h.Logger.Wrap(err, "Error rendering admin index page"))
return err return err
} }

View File

@ -1,40 +0,0 @@
package handlers
import (
"context"
"git.markbailey.dev/cervers/ptpp/view/layout"
"net/http"
"os"
"git.markbailey.dev/cervers/ptpp/lib/logger"
"git.markbailey.dev/cervers/ptpp/view/homepage"
)
type HomePageHandler struct {
logger logger.ILogger
}
func NewHomePageHandler(l logger.ILogger) *HomePageHandler {
return &HomePageHandler{
logger: l,
}
}
func (h HomePageHandler) Homepage(w http.ResponseWriter, r *http.Request) error {
if r.URL.Path != "/" {
w.WriteHeader(http.StatusNotFound)
err := layout.NotFound().Render(context.Background(), w)
if err != nil {
h.logger.Error(h.logger.Wrap(err, "Error rendering 404 page"))
return err
}
return nil
}
if err := homepage.Homepage(os.Getenv("$HTMX_APP_ENV")).Render(context.Background(), w); err != nil {
h.logger.Error(h.logger.Wrap(err, "Error rendering homepage"))
return err
}
return nil
}

View File

@ -0,0 +1,45 @@
package shared
import (
"context"
"git.markbailey.dev/cerbervs/ptpp/app/controller"
"git.markbailey.dev/cerbervs/ptpp/lib/database"
"git.markbailey.dev/cerbervs/ptpp/lib/logger"
"git.markbailey.dev/cerbervs/ptpp/lib/session"
"git.markbailey.dev/cerbervs/ptpp/view/layout"
"net/http"
"os"
"git.markbailey.dev/cerbervs/ptpp/view/homepage"
)
type HomePageHandler struct {
controller.Controller
}
func (h HomePageHandler) Init(s session.IManager, d database.IDB, l logger.ILogger) error {
h.Logger = l
h.Db = d
h.Session = s
return nil
}
func (h HomePageHandler) Get(w http.ResponseWriter, r *http.Request) error {
if r.URL.Path != "/" {
w.WriteHeader(http.StatusNotFound)
err := layout.NotFound().Render(context.Background(), w)
if err != nil {
h.Logger.Error(h.Logger.Wrap(err, "Error rendering 404 page"))
return err
}
return nil
}
if err := homepage.Homepage(os.Getenv("$HTMX_APP_ENV")).Render(context.Background(), w); err != nil {
h.Logger.Error(h.Logger.Wrap(err, "Error rendering homepage"))
return err
}
return nil
}

353
handlers/shared/user.go Normal file
View File

@ -0,0 +1,353 @@
package shared
import (
"context"
"encoding/json"
"git.markbailey.dev/cerbervs/ptpp/app/controller"
"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/lib/session"
"git.markbailey.dev/cerbervs/ptpp/util/auth"
"git.markbailey.dev/cerbervs/ptpp/util/shared"
"net/http"
"time"
"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) error {
c.Logger = l
c.Db = d
c.Session = s
return nil
}
type SignInHandler struct {
controller.Controller
}
func (c SignInHandler) Init(s session.IManager, d database.IDB, l logger.ILogger) error {
c.Logger = l
c.Db = d
c.Session = s
return nil
}
type PopulateHandler struct {
controller.Controller
}
func (c PopulateHandler) Init(s session.IManager, d database.IDB, l logger.ILogger) error {
c.Logger = l
c.Db = d
c.Session = s
return nil
}
type SignOutHandler struct {
controller.Controller
}
func (c SignOutHandler) Init(s session.IManager, d database.IDB, l logger.ILogger) error {
c.Logger = l
c.Db = d
c.Session = s
return nil
}
func (c PopulateHandler) Get(w http.ResponseWriter, r *http.Request) error {
existingOrg, err := c.Db.Repo().FindOrganizationByName("CerbervsSoft")
if existingOrg != nil && err == nil {
return shared.Redirect(w, r, "/signup", http.StatusSeeOther, false)
}
authToken, err := auth.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 shared.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 shared.Redirect(w, r, "/admin/", http.StatusSeeOther, false)
} else {
return shared.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 shared.Redirect(w, r, "/sign-in", http.StatusSeeOther, false)
}
authenticated, err := auth.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 := auth.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 := auth.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 := auth.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 shared.Redirect(w, r, "/admin/", http.StatusSeeOther, false)
} else {
return shared.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 shared.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 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 := auth.CreateTokenForUser(fd.Username)
if err != nil {
c.Logger.Error(c.Logger.Wrap(err, "Error creating token"))
return err
}
password, err := auth.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 := auth.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 shared.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 shared.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 shared.Redirect(w, r, r.URL.Path, http.StatusSeeOther, false)
}

View File

@ -1,315 +0,0 @@
package handlers
import (
"context"
"encoding/json"
"git.markbailey.dev/cervers/ptpp/lib/database"
"git.markbailey.dev/cervers/ptpp/lib/database/dto"
"net/http"
"time"
"git.markbailey.dev/cervers/ptpp/lib/logger"
"git.markbailey.dev/cervers/ptpp/lib/session"
"git.markbailey.dev/cervers/ptpp/util"
"git.markbailey.dev/cervers/ptpp/view/user"
)
type UserHandler struct {
logger logger.ILogger
session session.IManager
db database.IDB
}
func NewUserHandler(l logger.ILogger, s session.IManager, db database.IDB) *UserHandler {
return &UserHandler{
logger: l,
session: s,
db: db,
}
}
func (u *UserHandler) Populate(w http.ResponseWriter, r *http.Request) error {
existingOrg, err := u.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 = u.db.Repo().CreateOrganization(organization)
if err != nil {
return u.logDBError(err)
}
return util.Redirect(w, r, "/signup", http.StatusSeeOther, false)
}
type UserSignInForm struct {
Username string `json:"username"`
Password string `json:"password"`
}
func (u *UserHandler) SignIn(w http.ResponseWriter, r *http.Request) error {
sess := u.session.SessionStart(w, r)
if r.Method == http.MethodGet {
username, ok := r.Context().Value("username").(string)
if ok {
if username == "" {
return u.failWithFormError(w, r, "Invalid Username or Password.")
} else {
foundUser, err := u.db.Repo().FindUserByUsername(username)
if foundUser == nil || err != nil {
return u.failWithFormError(w, r, "Invalid Username or Password.")
}
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 {
u.logger.Error(u.logger.Wrap(err, "Error rendering sign in form"))
return err
}
return nil
}
fd := &UserSignInForm{}
jsonDecoder := json.NewDecoder(r.Body)
if err := jsonDecoder.Decode(&fd); err != nil {
u.logger.Error(u.logger.Wrap(err, "Error decoding JSON"))
return err
}
foundUser, err := u.db.Repo().FindUserByUsername(fd.Username)
if foundUser == nil || err != nil {
return util.Redirect(w, r, "/signin", http.StatusSeeOther, false)
}
authenticated, err := util.CheckPassword(fd.Password, foundUser.Password)
if err != nil || !authenticated {
return u.failWithFormError(w, r, "Invalid Username or Password.")
}
err = sess.Set("username", foundUser.Username)
if err != nil {
return err
}
token, err := util.CreateTokenForUser(foundUser.Username)
if err != nil {
u.logger.Error(u.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 {
u.logger.Error(err)
return err
}
_, err = u.db.Repo().CreateHeartbeat(&dto.Heartbeat{
User: foundUser.Identifier,
CreatedAt: time.Now(),
IpAddr: r.RemoteAddr,
AuthToken: authToken,
})
if err != nil {
return u.logDBError(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
}
u.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 (u *UserHandler) SignUp(w http.ResponseWriter, r *http.Request) error {
handlerSess := u.session.SessionStart(w, r)
uname, ok := handlerSess.Get("username").(string)
if !ok {
uname = ""
}
if uname != "" {
return util.Redirect(w, r, "/", http.StatusSeeOther, false)
}
if r.Method == http.MethodGet {
if err := user.SignUpForm().Render(context.Background(), w); err != nil {
u.logger.Error(u.logger.Wrap(err, "Error rendering sign up form"))
return err
}
return nil
}
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 {
u.logger.Error(u.logger.Wrap(err, "Error writing response"))
return err
}
return nil
}
foundUser, err := u.db.Repo().FindUserByUsername(fd.Username)
if foundUser != nil {
if _, err := w.Write([]byte("Invalid username. Please try another")); err != nil {
u.logger.Error(u.logger.Wrap(err, "Error writing response"))
}
return nil
}
token, err := util.CreateTokenForUser(fd.Username)
if err != nil {
u.logger.Error(u.logger.Wrap(err, "Error creating token"))
return err
}
password, err := util.HashPassword(fd.PasswordConfirmation)
if err != nil {
u.logger.Error(u.logger.Wrap(err, "Error hashing password"))
return err
}
org, err := u.db.Repo().FindOrganizationByName("CerbervsSoft")
if org == nil || err != nil {
return u.logDBError(err)
}
newUser, err := u.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 {
return u.logDBError(err)
}
_, err = u.db.Repo().CreateHeartbeat(&dto.Heartbeat{
User: newUser.Identifier,
CreatedAt: time.Now(),
IpAddr: r.RemoteAddr,
AuthToken: token,
})
if err != nil {
return u.logDBError(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, "/signin", http.StatusSeeOther, false)
}
return nil
}
func (u *UserHandler) SignOut(w http.ResponseWriter, r *http.Request) error {
sess := u.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 (u *UserHandler) logDBError(err error) error {
u.db.Error(err)
return err
}
func (u *UserHandler) failWithFormError(w http.ResponseWriter, r *http.Request, formError string) error {
sess := u.session.SessionStart(w, r)
err := sess.Set("formError", formError)
if err != nil {
return err
}
return util.Redirect(w, r, r.URL.Path, http.StatusSeeOther, false)
}

View File

@ -1,79 +0,0 @@
package infrastructure
import (
"git.markbailey.dev/cervers/ptpp/lib/database"
"net/http"
"sync"
"git.markbailey.dev/cervers/ptpp/handlers"
"git.markbailey.dev/cervers/ptpp/handlers/admin"
"git.markbailey.dev/cervers/ptpp/lib/logger"
mw "git.markbailey.dev/cervers/ptpp/lib/middleware"
"git.markbailey.dev/cervers/ptpp/lib/session"
)
var (
globalSessions *session.Manager
commonRouter *http.ServeMux
adminStack mw.Func
commonStack mw.Func
lock = &sync.Mutex{}
il logger.ILogger
)
func GetRouter() http.Handler {
commonStack = mw.Compose(
mw.WithLogger,
mw.WithUsername,
)
if commonRouter == nil {
lock.Lock()
defer lock.Unlock()
adminStack = mw.Compose(
mw.WithAuth,
)
commonRouter = http.NewServeMux()
adminRouter := http.NewServeMux()
// Serve static files
fs := http.FileServer(http.Dir("./public/"))
commonRouter.Handle("GET /public/", http.StripPrefix("/public", fs))
commonRouter.HandleFunc("/favicon.ico", func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "./public/assets/favicon/favicon.ico")
})
// Homepage routes
homepageHandler := handlers.NewHomePageHandler(il)
commonRouter.Handle("/", mw.ErrHandler(homepageHandler.Homepage))
if env == "production" {
return commonStack(commonRouter)
}
// User routes
userHandler := handlers.NewUserHandler(il, globalSessions, database.ChooseDB())
commonRouter.Handle("GET /signup", mw.ErrHandler(userHandler.SignUp))
commonRouter.Handle("POST /signup", mw.ErrHandler(userHandler.SignUp))
commonRouter.Handle("GET /signin", mw.ErrHandler(userHandler.SignIn))
commonRouter.Handle("POST /signin", mw.ErrHandler(userHandler.SignIn))
commonRouter.Handle("GET /signout", mw.ErrHandler(userHandler.SignOut))
commonRouter.Handle("GET /populate", mw.ErrHandler(userHandler.Populate))
// Admin routes
adminIndexHandler := admin.NewAdminIndexHandler(il, globalSessions)
adminRouter.Handle("GET /", mw.ErrHandler(adminIndexHandler.Index))
commonRouter.Handle("/admin/", http.StripPrefix("/admin", adminStack(adminRouter)))
}
return commonStack(commonRouter)
}
func init() {
globalSessions, _ = session.NewManager("memory", "ptpp", 3600)
go globalSessions.GC()
il = logger.NewCompositeLogger()
}

View File

@ -1,64 +0,0 @@
package infrastructure
import (
"fmt"
"log"
"net/http"
"os"
"strconv"
"time"
)
type Server struct {
addr string
http.Server
port int
}
var (
env = os.Getenv("HTMX_APP_ENV")
)
func NewServer() *Server {
const (
addr = "0.0.0.0"
)
var port int
if env == "production" {
port = 8080
} else {
port = 8080
}
return &Server{
addr,
http.Server{
Addr: addr + ":" + strconv.Itoa(port),
Handler: GetRouter(),
DisableGeneralOptionsHandler: false,
TLSConfig: nil,
ReadTimeout: 5 * time.Second,
ReadHeaderTimeout: 0,
WriteTimeout: 5 * time.Second,
IdleTimeout: 0,
MaxHeaderBytes: 0,
TLSNextProto: nil,
ConnState: nil,
ErrorLog: nil,
BaseContext: nil,
ConnContext: nil,
},
port,
}
}
func (s *Server) Serve() {
fmt.Printf("Starting.\nListening at %s on port %s\n", s.addr, strconv.Itoa(s.port))
serverError := s.ListenAndServe()
if serverError != nil {
log.Fatal(serverError)
}
}

View File

@ -1,12 +1,12 @@
package database package database
import ( import (
"git.markbailey.dev/cervers/ptpp/lib/database/development" "git.markbailey.dev/cerbervs/ptpp/lib/database/development"
"git.markbailey.dev/cervers/ptpp/lib/database/production" "git.markbailey.dev/cerbervs/ptpp/lib/database/production"
"git.markbailey.dev/cervers/ptpp/lib/repository" "git.markbailey.dev/cerbervs/ptpp/lib/repository"
"os" "os"
"git.markbailey.dev/cervers/ptpp/lib/logger" "git.markbailey.dev/cerbervs/ptpp/lib/logger"
) )
type IDB interface { type IDB interface {

View File

@ -2,9 +2,9 @@ package development
import ( import (
"database/sql" "database/sql"
"git.markbailey.dev/cervers/ptpp/lib/logger" "git.markbailey.dev/cerbervs/ptpp/lib/logger"
"git.markbailey.dev/cervers/ptpp/lib/repository" "git.markbailey.dev/cerbervs/ptpp/lib/repository"
"git.markbailey.dev/cervers/ptpp/models/sqlite" "git.markbailey.dev/cerbervs/ptpp/models/sqlite"
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
"sync" "sync"
) )

View File

@ -2,9 +2,9 @@ package development
import ( import (
"context" "context"
"git.markbailey.dev/cervers/ptpp/lib/database/dto" "git.markbailey.dev/cerbervs/ptpp/lib/database/dto"
pterror "git.markbailey.dev/cervers/ptpp/lib/error" pterror "git.markbailey.dev/cerbervs/ptpp/lib/error"
"git.markbailey.dev/cervers/ptpp/models/sqlite" "git.markbailey.dev/cerbervs/ptpp/models/sqlite"
) )
type HeartbeatRepository struct{} type HeartbeatRepository struct{}

View File

@ -2,9 +2,9 @@ package development
import ( import (
"context" "context"
"git.markbailey.dev/cervers/ptpp/lib/database/dto" "git.markbailey.dev/cerbervs/ptpp/lib/database/dto"
pterror "git.markbailey.dev/cervers/ptpp/lib/error" pterror "git.markbailey.dev/cerbervs/ptpp/lib/error"
"git.markbailey.dev/cervers/ptpp/models/sqlite" "git.markbailey.dev/cerbervs/ptpp/models/sqlite"
) )
type OrganizationRepository struct{} type OrganizationRepository struct{}

View File

@ -2,9 +2,9 @@ package development
import ( import (
"context" "context"
"git.markbailey.dev/cervers/ptpp/lib/database/dto" "git.markbailey.dev/cerbervs/ptpp/lib/database/dto"
pterror "git.markbailey.dev/cervers/ptpp/lib/error" pterror "git.markbailey.dev/cerbervs/ptpp/lib/error"
"git.markbailey.dev/cervers/ptpp/models/sqlite" "git.markbailey.dev/cerbervs/ptpp/models/sqlite"
) )
type UserRepository struct{} type UserRepository struct{}

View File

@ -2,9 +2,9 @@ package production
import ( import (
"database/sql" "database/sql"
"git.markbailey.dev/cervers/ptpp/lib/logger" "git.markbailey.dev/cerbervs/ptpp/lib/logger"
"git.markbailey.dev/cervers/ptpp/lib/repository" "git.markbailey.dev/cerbervs/ptpp/lib/repository"
"git.markbailey.dev/cervers/ptpp/models/postgres" "git.markbailey.dev/cerbervs/ptpp/models/postgres"
_ "github.com/lib/pq" _ "github.com/lib/pq"
"os" "os"
"sync" "sync"

View File

@ -2,9 +2,9 @@ package production
import ( import (
"context" "context"
"git.markbailey.dev/cervers/ptpp/lib/database/dto" "git.markbailey.dev/cerbervs/ptpp/lib/database/dto"
pterror "git.markbailey.dev/cervers/ptpp/lib/error" pterror "git.markbailey.dev/cerbervs/ptpp/lib/error"
"git.markbailey.dev/cervers/ptpp/models/postgres" "git.markbailey.dev/cerbervs/ptpp/models/postgres"
) )
type HeartbeatRepository struct{} type HeartbeatRepository struct{}

View File

@ -2,9 +2,9 @@ package production
import ( import (
"context" "context"
"git.markbailey.dev/cervers/ptpp/lib/database/dto" "git.markbailey.dev/cerbervs/ptpp/lib/database/dto"
pterror "git.markbailey.dev/cervers/ptpp/lib/error" pterror "git.markbailey.dev/cerbervs/ptpp/lib/error"
"git.markbailey.dev/cervers/ptpp/models/postgres" "git.markbailey.dev/cerbervs/ptpp/models/postgres"
) )
type OrganizationRepository struct{} type OrganizationRepository struct{}

View File

@ -2,9 +2,9 @@ package production
import ( import (
"context" "context"
"git.markbailey.dev/cervers/ptpp/lib/database/dto" "git.markbailey.dev/cerbervs/ptpp/lib/database/dto"
pterror "git.markbailey.dev/cervers/ptpp/lib/error" pterror "git.markbailey.dev/cerbervs/ptpp/lib/error"
"git.markbailey.dev/cervers/ptpp/models/postgres" "git.markbailey.dev/cerbervs/ptpp/models/postgres"
) )
type UserRepository struct{} type UserRepository struct{}

View File

@ -1,8 +1,8 @@
package locator package locator
import ( import (
perr "git.markbailey.dev/cervers/ptpp/lib/error" perr "git.markbailey.dev/cerbervs/ptpp/lib/error"
"git.markbailey.dev/cervers/ptpp/lib/locator/service" "git.markbailey.dev/cerbervs/ptpp/lib/locator/service"
"sync" "sync"
) )

View File

@ -1,7 +1,7 @@
package service package service
import ( import (
"git.markbailey.dev/cervers/ptpp/lib/database" "git.markbailey.dev/cerbervs/ptpp/lib/database"
"sync" "sync"
) )

View File

@ -1,7 +1,7 @@
package service package service
import ( import (
"git.markbailey.dev/cervers/ptpp/lib/logger" "git.markbailey.dev/cerbervs/ptpp/lib/logger"
"sync" "sync"
) )

View File

@ -2,8 +2,8 @@ package logger
import ( import (
"fmt" "fmt"
pterror "git.markbailey.dev/cervers/ptpp/lib/error" pterror "git.markbailey.dev/cerbervs/ptpp/lib/error"
"git.markbailey.dev/cervers/ptpp/util" "git.markbailey.dev/cerbervs/ptpp/util/shared"
"log" "log"
"os" "os"
"sync" "sync"
@ -44,7 +44,7 @@ func (l CompositeLogger) getLogFile() *os.File {
compositeLogFileLock.Lock() compositeLogFileLock.Lock()
defer compositeLogFileLock.Unlock() defer compositeLogFileLock.Unlock()
absPath := util.GetFullyQualifiedPath("/log") absPath := shared.GetFullyQualifiedPath("/log")
err := os.MkdirAll(absPath, os.ModePerm) err := os.MkdirAll(absPath, os.ModePerm)
if err != nil { if err != nil {

View File

@ -2,8 +2,8 @@ package logger
import ( import (
"fmt" "fmt"
pterror "git.markbailey.dev/cervers/ptpp/lib/error" pterror "git.markbailey.dev/cerbervs/ptpp/lib/error"
"git.markbailey.dev/cervers/ptpp/util" "git.markbailey.dev/cerbervs/ptpp/util/shared"
"log" "log"
"os" "os"
"sync" "sync"
@ -44,7 +44,7 @@ func (l DBLogger) getLogFile() *os.File {
dbLogFileLock.Lock() dbLogFileLock.Lock()
defer dbLogFileLock.Unlock() defer dbLogFileLock.Unlock()
absPath := util.GetFullyQualifiedPath("/log") absPath := shared.GetFullyQualifiedPath("/log")
generalLog, err := os.OpenFile(absPath+"/db-log.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) generalLog, err := os.OpenFile(absPath+"/db-log.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil { if err != nil {

View File

@ -2,38 +2,20 @@ package middleware
import ( import (
"fmt" "fmt"
"git.markbailey.dev/cervers/ptpp/lib/database" "git.markbailey.dev/cerbervs/ptpp/util/auth"
"git.markbailey.dev/cerbervs/ptpp/util/shared"
"net/http" "net/http"
"os" "os"
"git.markbailey.dev/cervers/ptpp/lib/logger" "git.markbailey.dev/cerbervs/ptpp/lib/logger"
"git.markbailey.dev/cervers/ptpp/lib/session" "git.markbailey.dev/cerbervs/ptpp/lib/session"
"git.markbailey.dev/cervers/ptpp/util"
) )
type Middleware struct { type Func func(http.Handler) http.Handler
l logger.ILogger
db database.IDB
}
type ( var sess session.IManager
ErrHandler func(http.ResponseWriter, *http.Request) error
Func func(http.Handler) http.Handler
)
var ( func Compose(xs []Func) Func {
sess session.IManager
)
func (fn ErrHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if err := fn(w, r); err != nil {
w.Header().Set("HX-Retarget", "#layout_content")
w.Header().Set("HX-Reswap", "innerHTML")
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
func Compose(xs ...Func) Func {
return func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler {
for i := len(xs) - 1; i >= 0; i-- { for i := len(xs) - 1; i >= 0; i-- {
x := xs[i] x := xs[i]
@ -78,7 +60,7 @@ func WithLogger(next http.Handler) http.Handler {
func WithAuth(next http.Handler) http.Handler { func WithAuth(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var ( var (
claims *util.CustomClaims claims *auth.CustomClaims
cookie *http.Cookie cookie *http.Cookie
err error err error
token string token string
@ -86,7 +68,7 @@ func WithAuth(next http.Handler) http.Handler {
) )
if handlerSess.Get("username") != nil { if handlerSess.Get("username") != nil {
req := util.AddValuesToRequestContext(r, map[any]any{ req := shared.AddValuesToRequestContext(r, map[any]any{
"username": handlerSess.Get("username"), "username": handlerSess.Get("username"),
}) })
next.ServeHTTP(w, req) next.ServeHTTP(w, req)
@ -94,19 +76,19 @@ func WithAuth(next http.Handler) http.Handler {
} }
if cookie, err = r.Cookie("token"); err != nil { if cookie, err = r.Cookie("token"); err != nil {
_ = util.Redirect(w, r, "/signin", http.StatusSeeOther, true) _ = shared.Redirect(w, r, "/signin", http.StatusSeeOther, true)
return return
} }
if token = cookie.Value; token == "" { if token = cookie.Value; token == "" {
_ = util.Redirect(w, r, "/signin", http.StatusSeeOther, true) _ = shared.Redirect(w, r, "/signin", http.StatusSeeOther, true)
return return
} }
if claims, err = util.ParseToken(token, os.Getenv("TOKEN_SECRET")); err != nil { if claims, err = auth.ParseToken(token, os.Getenv("TOKEN_SECRET")); err != nil {
_ = util.Redirect(w, r, "/signin", http.StatusSeeOther, true) _ = shared.Redirect(w, r, "/signin", http.StatusSeeOther, true)
return return
} }
req := util.AddValuesToRequestContext(r, map[any]any{ req := shared.AddValuesToRequestContext(r, map[any]any{
"username": claims.Username, "username": claims.Username,
}) })
next.ServeHTTP(w, req) next.ServeHTTP(w, req)
@ -116,7 +98,7 @@ func WithAuth(next http.Handler) http.Handler {
func WithUsername(next http.Handler) http.Handler { func WithUsername(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var ( var (
claims *util.CustomClaims claims *auth.CustomClaims
cookie *http.Cookie cookie *http.Cookie
err error err error
token string token string
@ -124,7 +106,7 @@ func WithUsername(next http.Handler) http.Handler {
) )
if handlerSess.Get("username") != nil { if handlerSess.Get("username") != nil {
req := util.AddValuesToRequestContext(r, map[any]any{ req := shared.AddValuesToRequestContext(r, map[any]any{
"username": handlerSess.Get("username"), "username": handlerSess.Get("username"),
}) })
next.ServeHTTP(w, req) next.ServeHTTP(w, req)
@ -136,14 +118,14 @@ func WithUsername(next http.Handler) http.Handler {
if token = cookie.Value; token == "" { if token = cookie.Value; token == "" {
uname = nil uname = nil
} }
if claims, err = util.ParseToken(token, os.Getenv("TOKEN_SECRET")); err != nil { if claims, err = auth.ParseToken(token, os.Getenv("TOKEN_SECRET")); err != nil {
uname = nil uname = nil
} }
uname = &claims.Username uname = &claims.Username
} }
if uname != nil { if uname != nil {
req := util.AddValuesToRequestContext(r, map[any]any{ req := shared.AddValuesToRequestContext(r, map[any]any{
"username": uname, "username": uname,
}) })
next.ServeHTTP(w, req) next.ServeHTTP(w, req)

View File

@ -1,6 +1,6 @@
package repository package repository
import "git.markbailey.dev/cervers/ptpp/lib/database/dto" import "git.markbailey.dev/cerbervs/ptpp/lib/database/dto"
type IHeartbeat interface { type IHeartbeat interface {
CreateHeartbeat(h *dto.Heartbeat) (*dto.Heartbeat, error) CreateHeartbeat(h *dto.Heartbeat) (*dto.Heartbeat, error)

View File

@ -5,7 +5,7 @@ import (
"sync" "sync"
"time" "time"
"git.markbailey.dev/cervers/ptpp/lib/session" "git.markbailey.dev/cerbervs/ptpp/lib/session"
) )
var prov = &Provider{list: list.New()} var prov = &Provider{list: list.New()}

148
package-lock.json generated
View File

@ -1,16 +1,15 @@
{ {
"name": "ptpp", "name": "go-full-stack",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"dependencies": { "dependencies": {
"htmx.org": "^1.9.10", "htmx.org": "^1.9.10"
"preline": "^2.0.3"
}, },
"devDependencies": { "devDependencies": {
"@tailwindcss/forms": "^0.5.9", "@tailwindcss/forms": "^0.5.9",
"tailwindcss": "^3.4.1" "tailwindcss": "^3.4.15"
} }
}, },
"node_modules/@alloc/quick-lru": { "node_modules/@alloc/quick-lru": {
@ -135,15 +134,6 @@
"node": ">=14" "node": ">=14"
} }
}, },
"node_modules/@popperjs/core": {
"version": "2.11.8",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/popperjs"
}
},
"node_modules/@tailwindcss/forms": { "node_modules/@tailwindcss/forms": {
"version": "0.5.9", "version": "0.5.9",
"resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.9.tgz", "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.9.tgz",
@ -231,12 +221,13 @@
} }
}, },
"node_modules/braces": { "node_modules/braces": {
"version": "3.0.2", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"fill-range": "^7.0.1" "fill-range": "^7.1.1"
}, },
"engines": { "engines": {
"node": ">=8" "node": ">=8"
@ -333,6 +324,7 @@
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
"dev": true, "dev": true,
"license": "MIT",
"bin": { "bin": {
"cssesc": "bin/cssesc" "cssesc": "bin/cssesc"
}, },
@ -402,10 +394,11 @@
} }
}, },
"node_modules/fill-range": { "node_modules/fill-range": {
"version": "7.0.1", "version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"to-regex-range": "^5.0.1" "to-regex-range": "^5.0.1"
}, },
@ -562,6 +555,7 @@
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true, "dev": true,
"license": "MIT",
"engines": { "engines": {
"node": ">=0.12.0" "node": ">=0.12.0"
} }
@ -591,10 +585,11 @@
} }
}, },
"node_modules/jiti": { "node_modules/jiti": {
"version": "1.21.0", "version": "1.21.6",
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz",
"integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==",
"dev": true, "dev": true,
"license": "MIT",
"bin": { "bin": {
"jiti": "bin/jiti.js" "jiti": "bin/jiti.js"
} }
@ -633,12 +628,13 @@
} }
}, },
"node_modules/micromatch": { "node_modules/micromatch": {
"version": "4.0.5", "version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
"integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"braces": "^3.0.2", "braces": "^3.0.3",
"picomatch": "^2.3.1" "picomatch": "^2.3.1"
}, },
"engines": { "engines": {
@ -766,10 +762,11 @@
} }
}, },
"node_modules/picocolors": { "node_modules/picocolors": {
"version": "1.0.0", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"dev": true "dev": true,
"license": "ISC"
}, },
"node_modules/picomatch": { "node_modules/picomatch": {
"version": "2.3.1", "version": "2.3.1",
@ -802,9 +799,9 @@
} }
}, },
"node_modules/postcss": { "node_modules/postcss": {
"version": "8.4.35", "version": "8.4.49",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz",
"integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@ -820,10 +817,11 @@
"url": "https://github.com/sponsors/ai" "url": "https://github.com/sponsors/ai"
} }
], ],
"license": "MIT",
"dependencies": { "dependencies": {
"nanoid": "^3.3.7", "nanoid": "^3.3.7",
"picocolors": "^1.0.0", "picocolors": "^1.1.1",
"source-map-js": "^1.0.2" "source-map-js": "^1.2.1"
}, },
"engines": { "engines": {
"node": "^10 || ^12 || >=14" "node": "^10 || ^12 || >=14"
@ -910,29 +908,37 @@
} }
}, },
"node_modules/postcss-nested": { "node_modules/postcss-nested": {
"version": "6.0.1", "version": "6.2.0",
"resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz",
"integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==",
"dev": true, "dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": { "dependencies": {
"postcss-selector-parser": "^6.0.11" "postcss-selector-parser": "^6.1.1"
}, },
"engines": { "engines": {
"node": ">=12.0" "node": ">=12.0"
}, },
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
"peerDependencies": { "peerDependencies": {
"postcss": "^8.2.14" "postcss": "^8.2.14"
} }
}, },
"node_modules/postcss-selector-parser": { "node_modules/postcss-selector-parser": {
"version": "6.0.15", "version": "6.1.2",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
"integrity": "sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==", "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"cssesc": "^3.0.0", "cssesc": "^3.0.0",
"util-deprecate": "^1.0.2" "util-deprecate": "^1.0.2"
@ -947,14 +953,6 @@
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
"dev": true "dev": true
}, },
"node_modules/preline": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/preline/-/preline-2.0.3.tgz",
"integrity": "sha512-V/sLmRIHd23UCdvJNRKKszntgUqA0381erVzRpQ48NjjMOgI7DyFW4mr+lg387V0oeBc5Dx9Jxa5voppVoH9GA==",
"dependencies": {
"@popperjs/core": "^2.11.2"
}
},
"node_modules/queue-microtask": { "node_modules/queue-microtask": {
"version": "1.2.3", "version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
@ -1080,10 +1078,11 @@
} }
}, },
"node_modules/source-map-js": { "node_modules/source-map-js": {
"version": "1.0.2", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"dev": true, "dev": true,
"license": "BSD-3-Clause",
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@ -1219,33 +1218,34 @@
} }
}, },
"node_modules/tailwindcss": { "node_modules/tailwindcss": {
"version": "3.4.1", "version": "3.4.15",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.1.tgz", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.15.tgz",
"integrity": "sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==", "integrity": "sha512-r4MeXnfBmSOuKUWmXe6h2CcyfzJCEk4F0pptO5jlnYSIViUkVmsawj80N5h2lO3gwcmSb4n3PuN+e+GC1Guylw==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"@alloc/quick-lru": "^5.2.0", "@alloc/quick-lru": "^5.2.0",
"arg": "^5.0.2", "arg": "^5.0.2",
"chokidar": "^3.5.3", "chokidar": "^3.6.0",
"didyoumean": "^1.2.2", "didyoumean": "^1.2.2",
"dlv": "^1.1.3", "dlv": "^1.1.3",
"fast-glob": "^3.3.0", "fast-glob": "^3.3.2",
"glob-parent": "^6.0.2", "glob-parent": "^6.0.2",
"is-glob": "^4.0.3", "is-glob": "^4.0.3",
"jiti": "^1.19.1", "jiti": "^1.21.6",
"lilconfig": "^2.1.0", "lilconfig": "^2.1.0",
"micromatch": "^4.0.5", "micromatch": "^4.0.8",
"normalize-path": "^3.0.0", "normalize-path": "^3.0.0",
"object-hash": "^3.0.0", "object-hash": "^3.0.0",
"picocolors": "^1.0.0", "picocolors": "^1.1.1",
"postcss": "^8.4.23", "postcss": "^8.4.47",
"postcss-import": "^15.1.0", "postcss-import": "^15.1.0",
"postcss-js": "^4.0.1", "postcss-js": "^4.0.1",
"postcss-load-config": "^4.0.1", "postcss-load-config": "^4.0.2",
"postcss-nested": "^6.0.1", "postcss-nested": "^6.2.0",
"postcss-selector-parser": "^6.0.11", "postcss-selector-parser": "^6.1.2",
"resolve": "^1.22.2", "resolve": "^1.22.8",
"sucrase": "^3.32.0" "sucrase": "^3.35.0"
}, },
"bin": { "bin": {
"tailwind": "lib/cli.js", "tailwind": "lib/cli.js",
@ -1281,6 +1281,7 @@
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"is-number": "^7.0.0" "is-number": "^7.0.0"
}, },
@ -1298,7 +1299,8 @@
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"dev": true "dev": true,
"license": "MIT"
}, },
"node_modules/which": { "node_modules/which": {
"version": "2.0.2", "version": "2.0.2",

View File

@ -1,10 +1,9 @@
{ {
"devDependencies": { "devDependencies": {
"@tailwindcss/forms": "^0.5.9", "@tailwindcss/forms": "^0.5.9",
"tailwindcss": "^3.4.1" "tailwindcss": "^3.4.15"
}, },
"dependencies": { "dependencies": {
"htmx.org": "^1.9.10", "htmx.org": "^1.9.10"
"preline": "^2.0.3"
} }
} }

View File

@ -1,4 +1,4 @@
package util package auth
import ( import (
"errors" "errors"

View File

@ -1,4 +1,4 @@
package util package shared
import ( import (
"context" "context"

View File

@ -1,6 +1,6 @@
package admin package admin
import "git.markbailey.dev/cervers/ptpp/view/layout" import "git.markbailey.dev/cerbervs/ptpp/view/layout"
templ Index(name string) { templ Index(name string) {
@layout.Layout() { @layout.Layout() {

View File

@ -8,7 +8,7 @@ package admin
import "github.com/a-h/templ" import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime" import templruntime "github.com/a-h/templ/runtime"
import "git.markbailey.dev/cervers/ptpp/view/layout" import "git.markbailey.dev/cerbervs/ptpp/view/layout"
func Index(name string) templ.Component { func Index(name string) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {

View File

@ -1,6 +1,9 @@
package homepage package homepage
import "git.markbailey.dev/cervers/ptpp/view/layout" import (
"git.markbailey.dev/cerbervs/ptpp/view/layout"
"git.markbailey.dev/cerbervs/ptpp/app/routing"
)
templ Homepage(env string) { templ Homepage(env string) {
@layout.Layout() { @layout.Layout() {
@ -8,20 +11,18 @@ templ Homepage(env string) {
<div> <div>
Welcome to the homepage Welcome to the homepage
</div> </div>
if env == "development" {
<div> <div>
<a href="/signup"> <a href='{ routing.GetRouteByName("app.user.sign_up") }'>
<button class="text-gray-400 text-md border-black rounded-lg bg-gray-300 p-2"> <button class="text-gray-400 text-md border-black rounded-lg bg-gray-300 p-2">
Sign Up Sign Up
</button> </button>
</a> </a>
<a href="/signin"> <a href='{ routing.GetRouteByName("app.user.sign_up") }'>
<button class="text-gray-400 text-md border-black rounded-lg bg-gray-300 p-2"> <button class="text-gray-400 text-md border-black rounded-lg bg-gray-300 p-2">
Sign In Sign In
</button> </button>
</a> </a>
</div> </div>
}
</div> </div>
} }
} }

View File

@ -8,7 +8,10 @@ package homepage
import "github.com/a-h/templ" import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime" import templruntime "github.com/a-h/templ/runtime"
import "git.markbailey.dev/cervers/ptpp/view/layout" import (
"git.markbailey.dev/cerbervs/ptpp/app/routing"
"git.markbailey.dev/cerbervs/ptpp/view/layout"
)
func Homepage(env string) templ.Component { func Homepage(env string) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
@ -43,17 +46,7 @@ func Homepage(env string) templ.Component {
}() }()
} }
ctx = templ.InitializeContext(ctx) ctx = templ.InitializeContext(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"h-screen flex flex-col items-center justify-evenly text-blue-400 text-2xl\"><div>Welcome to the homepage</div>") _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"h-screen flex flex-col items-center justify-evenly text-blue-400 text-2xl\"><div>Welcome to the homepage</div><div><a href=\"{ routing.GetRouteByName(&#34;app.user.sign_up&#34;) }\"><button class=\"text-gray-400 text-md border-black rounded-lg bg-gray-300 p-2\">Sign Up</button></a> <a href=\"{ routing.GetRouteByName(&#34;app.user.sign_up&#34;) }\"><button class=\"text-gray-400 text-md border-black rounded-lg bg-gray-300 p-2\">Sign In</button></a></div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if env == "development" {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div><a href=\"/signup\"><button class=\"text-gray-400 text-md border-black rounded-lg bg-gray-300 p-2\" disabled>Sign Up</button></a> <a href=\"/signin\"><button class=\"text-gray-400 text-md border-black rounded-lg bg-gray-300 p-2\">Sign In</button></a></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }

View File

@ -15,7 +15,7 @@ templ Layout() {
</head> </head>
<body> <body>
<div id="#layout_content" hx-ext="response-targets" hx-target-5*="this"> <div id="#layout_content" hx-ext="response-targets" hx-target-error="this">
{ children... } { children... }
</div> </div>
<script type="text/javascript" src="/public/assets/js/index.js"></script> <script type="text/javascript" src="/public/assets/js/index.js"></script>

View File

@ -29,7 +29,7 @@ func Layout() templ.Component {
templ_7745c5c3_Var1 = templ.NopComponent templ_7745c5c3_Var1 = templ.NopComponent
} }
ctx = templ.ClearChildren(ctx) ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<!doctype html><html lang=\"en\"><head><title></title><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"device-width, initial-scale=1.0\"><script src=\"https://unpkg.com/htmx.org@1.9.10\"></script><script src=\"https://unpkg.com/htmx.org@1.9.10/dist/ext/json-enc.js\"></script><script src=\"https://unpkg.com/htmx.org/dist/ext/response-targets.js\"></script><link href=\"/public/assets/css/output.css\" rel=\"stylesheet\"></head><body><div id=\"#layout_content\" hx-ext=\"response-targets\" hx-target-5*=\"this\">") _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<!doctype html><html lang=\"en\"><head><title></title><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"device-width, initial-scale=1.0\"><script src=\"https://unpkg.com/htmx.org@1.9.10\"></script><script src=\"https://unpkg.com/htmx.org@1.9.10/dist/ext/json-enc.js\"></script><script src=\"https://unpkg.com/htmx.org/dist/ext/response-targets.js\"></script><link href=\"/public/assets/css/output.css\" rel=\"stylesheet\"></head><body><div id=\"#layout_content\" hx-ext=\"response-targets\" hx-target-error=\"this\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }

View File

@ -1,6 +1,6 @@
package user package user
import "git.markbailey.dev/cervers/ptpp/view/layout" import "git.markbailey.dev/cerbervs/ptpp/view/layout"
templ SignIn(err string) { templ SignIn(err string) {
@layout.Layout() { @layout.Layout() {
@ -15,7 +15,7 @@ templ SignIn(err string) {
</h1> </h1>
<form <form
class="space-y-4 md:space-y-6" class="space-y-4 md:space-y-6"
action="/signin" action="/sign-in"
method="post" method="post"
hx-post="/signin" hx-post="/signin"
hx-swap="outerHTML" hx-swap="outerHTML"

View File

@ -8,7 +8,7 @@ package user
import "github.com/a-h/templ" import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime" import templruntime "github.com/a-h/templ/runtime"
import "git.markbailey.dev/cervers/ptpp/view/layout" import "git.markbailey.dev/cerbervs/ptpp/view/layout"
func SignIn(err string) templ.Component { func SignIn(err string) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
@ -43,7 +43,7 @@ func SignIn(err string) templ.Component {
}() }()
} }
ctx = templ.InitializeContext(ctx) ctx = templ.InitializeContext(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<section class=\"bg-gray-50 dark:bg-gray-900\" id=\"section\"><div class=\"flex flex-col items-center justify-center px-6 py-8 mx-auto md:h-screen lg:py-0\"><div class=\"w-full bg-white rounded-lg shadow dark:border md:mt-0 sm:max-w-md xl:p-0 dark:bg-gray-800 dark:border-gray-700\"><div class=\"p-6 space-y-4 md:space-y-6 sm:p-8\"><h1 class=\"text-xl font-bold leading-tight tracking-tight text-gray-900 md:text-2xl dark:text-white\">Create an account</h1><form class=\"space-y-4 md:space-y-6\" action=\"/signin\" method=\"post\" hx-post=\"/signin\" hx-swap=\"outerHTML\" hx-target=\"#section\"><div><label for=\"username\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Username</label> <input type=\"text\" name=\"username\" id=\"username\" placeholder=\"Username\" class=\"bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" required=\"true\"></div><div><label for=\"password\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Password</label> <input type=\"password\" name=\"password\" id=\"password\" placeholder=\"••••••••\" class=\"bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" required=\"true\"></div>") _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<section class=\"bg-gray-50 dark:bg-gray-900\" id=\"section\"><div class=\"flex flex-col items-center justify-center px-6 py-8 mx-auto md:h-screen lg:py-0\"><div class=\"w-full bg-white rounded-lg shadow dark:border md:mt-0 sm:max-w-md xl:p-0 dark:bg-gray-800 dark:border-gray-700\"><div class=\"p-6 space-y-4 md:space-y-6 sm:p-8\"><h1 class=\"text-xl font-bold leading-tight tracking-tight text-gray-900 md:text-2xl dark:text-white\">Create an account</h1><form class=\"space-y-4 md:space-y-6\" action=\"/sign-in\" method=\"post\" hx-post=\"/signin\" hx-swap=\"outerHTML\" hx-target=\"#section\"><div><label for=\"username\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Username</label> <input type=\"text\" name=\"username\" id=\"username\" placeholder=\"Username\" class=\"bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" required=\"true\"></div><div><label for=\"password\" class=\"block mb-2 text-sm font-medium text-gray-900 dark:text-white\">Password</label> <input type=\"password\" name=\"password\" id=\"password\" placeholder=\"••••••••\" class=\"bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\" required=\"true\"></div>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }

View File

@ -1,6 +1,6 @@
package user package user
import "git.markbailey.dev/cervers/ptpp/view/layout" import "git.markbailey.dev/cerbervs/ptpp/view/layout"
templ SignUpForm() { templ SignUpForm() {
@layout.Layout() { @layout.Layout() {

View File

@ -8,7 +8,7 @@ package user
import "github.com/a-h/templ" import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime" import templruntime "github.com/a-h/templ/runtime"
import "git.markbailey.dev/cervers/ptpp/view/layout" import "git.markbailey.dev/cerbervs/ptpp/view/layout"
func SignUpForm() templ.Component { func SignUpForm() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {

View File

@ -1,6 +1,6 @@
package user package user
import "git.markbailey.dev/cervers/ptpp/view/layout" import "git.markbailey.dev/cerbervs/ptpp/view/layout"
templ SignUpSuccess() { templ SignUpSuccess() {
@layout.Layout() { @layout.Layout() {

View File

@ -8,7 +8,7 @@ package user
import "github.com/a-h/templ" import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime" import templruntime "github.com/a-h/templ/runtime"
import "git.markbailey.dev/cervers/ptpp/view/layout" import "git.markbailey.dev/cerbervs/ptpp/view/layout"
func SignUpSuccess() templ.Component { func SignUpSuccess() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {