Compare commits
No commits in common. "v2/refactor/routing" and "main" have entirely different histories.
v2/refacto
...
main
@ -4,8 +4,8 @@ tmp_dir = "tmp"
|
||||
|
||||
[build]
|
||||
args_bin = []
|
||||
bin = "./air-bin"
|
||||
cmd = "go build -o ./air-bin ./cmd/"
|
||||
bin = "./tmp/main"
|
||||
cmd = "go build -o ./tmp/main ./cmd/"
|
||||
delay = 1000
|
||||
exclude_dir = ["public", "data", "tmp"]
|
||||
exclude_file = []
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -10,4 +10,3 @@ tmp/*
|
||||
data/*
|
||||
data/database.db
|
||||
.idea
|
||||
air-bin
|
||||
|
19
app/app.go
19
app/app.go
@ -1,19 +0,0 @@
|
||||
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()
|
||||
}
|
@ -1,106 +0,0 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"git.markbailey.dev/cerbervs/ptpp/app/session"
|
||||
"git.markbailey.dev/cerbervs/ptpp/lib/logger"
|
||||
|
||||
"git.markbailey.dev/cerbervs/ptpp/lib/database"
|
||||
)
|
||||
|
||||
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, IControllerCtx) IController
|
||||
}
|
||||
|
||||
type Controller struct {
|
||||
Session session.IManager
|
||||
Db database.IDB
|
||||
Logger logger.ILogger
|
||||
Ctx IControllerCtx
|
||||
}
|
||||
|
||||
func (c *Controller) Init(s session.IManager, d database.IDB, l logger.ILogger, ctx IControllerCtx) IController {
|
||||
return nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
package controller
|
||||
|
||||
type IRouterCtx interface {
|
||||
GetRouteByName(string) (string, error)
|
||||
}
|
||||
|
||||
type IControllerCtx interface {
|
||||
GetRouteByName(string) string
|
||||
}
|
||||
|
||||
type ControllerCtx struct {
|
||||
RouterCtx IRouterCtx
|
||||
}
|
||||
|
||||
func (c ControllerCtx) GetRouteByName(name string) string {
|
||||
path, err := c.RouterCtx.GetRouteByName(name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return path
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type HandlerFunc func(http.ResponseWriter, *http.Request) error
|
||||
|
||||
func (fn HandlerFunc) 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)
|
||||
}
|
||||
}
|
@ -1,125 +0,0 @@
|
||||
package routing
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"git.markbailey.dev/cerbervs/ptpp/app/controller"
|
||||
"git.markbailey.dev/cerbervs/ptpp/app/handler"
|
||||
"git.markbailey.dev/cerbervs/ptpp/app/session"
|
||||
"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/util"
|
||||
)
|
||||
|
||||
type IRouter interface {
|
||||
RegisterRoutes() http.Handler
|
||||
}
|
||||
|
||||
type Route struct {
|
||||
Controller controller.IController
|
||||
Path string
|
||||
Name string
|
||||
}
|
||||
|
||||
type Router struct {
|
||||
Mux *http.ServeMux
|
||||
Middleware *[]middleware.Func
|
||||
BasePath string
|
||||
SubRouters []*Router
|
||||
Routes []Route
|
||||
}
|
||||
|
||||
var (
|
||||
rtr *Router
|
||||
rtrOnce sync.Once
|
||||
mux *http.ServeMux
|
||||
muxOnce sync.Once
|
||||
fsOnce sync.Once
|
||||
sess session.IManager
|
||||
)
|
||||
|
||||
func (r *Router) HandleAllRequestMethods(route Route, fullPath string) {
|
||||
ctx := controller.ControllerCtx{RouterCtx: rtr}
|
||||
c := route.Controller.Init(sess, database.ChooseDB(), logger.NewCompositeLogger(), ctx)
|
||||
r.Mux.Handle("GET "+fullPath, handler.HandlerFunc(c.Get))
|
||||
r.Mux.Handle("OPTIONS "+fullPath, handler.HandlerFunc(c.Options))
|
||||
r.Mux.Handle("TRACE "+fullPath, handler.HandlerFunc(c.Trace))
|
||||
r.Mux.Handle("PUT "+fullPath, handler.HandlerFunc(c.Put))
|
||||
r.Mux.Handle("DELETE "+fullPath, handler.HandlerFunc(c.Delete))
|
||||
r.Mux.Handle("POST "+fullPath, handler.HandlerFunc(c.Post))
|
||||
r.Mux.Handle("PATCH "+fullPath, handler.HandlerFunc(c.Patch))
|
||||
r.Mux.Handle("CONNECT "+fullPath, handler.HandlerFunc(c.Connect))
|
||||
}
|
||||
|
||||
func (r *Router) RegisterRoutes() http.Handler {
|
||||
rtrOnce.Do(func() { rtr = r })
|
||||
muxOnce.Do(func() { mux = http.NewServeMux(); r.Mux = mux })
|
||||
fsOnce.Do(func() { r.RegisterFs() })
|
||||
|
||||
if r.Mux == nil {
|
||||
r.Mux = http.NewServeMux()
|
||||
}
|
||||
|
||||
for _, route := range r.Routes {
|
||||
r.HandleAllRequestMethods(route, r.BasePath+route.Path)
|
||||
}
|
||||
|
||||
for _, subRouter := range r.SubRouters {
|
||||
r.Mux.Handle(
|
||||
"GET "+r.BasePath+subRouter.BasePath,
|
||||
http.StripPrefix(subRouter.BasePath, subRouter.RegisterRoutes()),
|
||||
)
|
||||
}
|
||||
|
||||
if r.Middleware != nil {
|
||||
mw := middleware.Compose(*r.Middleware)
|
||||
mctx := middleware.MiddlewareCtx{RouterCtx: rtr}
|
||||
return mw(mux, mctx)
|
||||
}
|
||||
|
||||
return mux
|
||||
}
|
||||
|
||||
func (r *Router) RegisterFs() {
|
||||
fs := http.FileServer(http.Dir(util.GetFullyQualifiedPath("/public")))
|
||||
r.Mux.Handle("GET "+r.BasePath+"public/", http.StripPrefix("/public/", fs))
|
||||
}
|
||||
|
||||
type RouteMapping struct {
|
||||
Path string
|
||||
Name string
|
||||
}
|
||||
|
||||
func (r *Router) GetFlatRouteList() []RouteMapping {
|
||||
routes := []RouteMapping{}
|
||||
for _, route := range r.Routes {
|
||||
routes = append(routes, RouteMapping{Path: r.BasePath + route.Path, Name: route.Name})
|
||||
}
|
||||
for _, subRouter := range r.SubRouters {
|
||||
routes = append(routes, subRouter.GetFlatRouteList()...)
|
||||
}
|
||||
return routes
|
||||
}
|
||||
|
||||
func (r *Router) GetRouteByName(name string) (string, error) {
|
||||
for _, route := range r.GetFlatRouteList() {
|
||||
if route.Name == name {
|
||||
return route.Path, nil
|
||||
}
|
||||
}
|
||||
return "", werror.Wrap(errors.New(name+" does not exist"), "Route not found")
|
||||
}
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
|
||||
sess, err = session.NewManager("memory", "ptpp", 3600)
|
||||
go sess.GC()
|
||||
if err != nil {
|
||||
panic(werror.Wrap(err, "Error creating session manager"))
|
||||
}
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
package routing
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"git.markbailey.dev/cerbervs/ptpp/handlers/admin"
|
||||
"git.markbailey.dev/cerbervs/ptpp/handlers/shared"
|
||||
"git.markbailey.dev/cerbervs/ptpp/lib/middleware"
|
||||
)
|
||||
|
||||
var (
|
||||
rtrinst *Router
|
||||
rtrinstOnce sync.Once
|
||||
)
|
||||
|
||||
func NewRouter() *Router {
|
||||
rtrinstOnce.Do(func() {
|
||||
rtrinst = &Router{
|
||||
Mux: nil,
|
||||
BasePath: "/",
|
||||
Routes: []Route{
|
||||
{Controller: shared.HomePageController{}, 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},
|
||||
},
|
||||
},
|
||||
Middleware: &[]middleware.Func{middleware.DontPanic, middleware.WithLogger, middleware.WithUsername},
|
||||
}
|
||||
})
|
||||
|
||||
return rtrinst
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
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)
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
package session
|
2
bin/app
2
bin/app
@ -290,7 +290,6 @@ __sqlc() {
|
||||
echo ================================================================================
|
||||
echo = Generating SQLC ==============================================================
|
||||
echo ================================================================================
|
||||
rm -rf ./models/*
|
||||
sqlc generate
|
||||
echo -e "\n"
|
||||
}
|
||||
@ -299,7 +298,6 @@ __templ() {
|
||||
echo ================================================================================
|
||||
echo = Generating templates =========================================================
|
||||
echo ================================================================================
|
||||
find . -name '*_templ.go' -delete
|
||||
templ generate
|
||||
echo -e "\n"
|
||||
}
|
||||
|
71
cmd/main.go
71
cmd/main.go
@ -1,75 +1,10 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"git.markbailey.dev/cerbervs/ptpp/app"
|
||||
"git.markbailey.dev/cerbervs/ptpp/app/routing"
|
||||
"git.markbailey.dev/cerbervs/ptpp/app/server"
|
||||
"git.markbailey.dev/cerbervs/ptpp/lib/logger"
|
||||
|
||||
_ "git.markbailey.dev/cerbervs/ptpp/app/session"
|
||||
inf "git.markbailey.dev/cervers/ptpp/infrastructure"
|
||||
_ "git.markbailey.dev/cervers/ptpp/lib/session/memory"
|
||||
)
|
||||
|
||||
func main() {
|
||||
const (
|
||||
addr = "0.0.0.0"
|
||||
prodPort = 8080
|
||||
devPort = 8080
|
||||
)
|
||||
|
||||
logger := logger.NewCompositeLogger()
|
||||
|
||||
r := routing.NewRouter()
|
||||
mux := r.RegisterRoutes()
|
||||
|
||||
rl := r.GetFlatRouteList()
|
||||
for _, route := range rl {
|
||||
path, err := r.GetRouteByName(route.Name)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Errorf(fmt.Sprintf("Error getting route by name: %s", route.Name)))
|
||||
}
|
||||
logger.Info(fmt.Sprintf("Name: %s, Path: %s", route.Name, path))
|
||||
}
|
||||
|
||||
logger.Info(fmt.Sprintf("%+v", mux))
|
||||
|
||||
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: mux,
|
||||
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()
|
||||
inf.NewServer().Serve()
|
||||
}
|
||||
|
4
go.mod
4
go.mod
@ -1,6 +1,6 @@
|
||||
module git.markbailey.dev/cerbervs/ptpp
|
||||
module git.markbailey.dev/cervers/ptpp
|
||||
|
||||
go 1.23
|
||||
go 1.23.2
|
||||
|
||||
require (
|
||||
github.com/a-h/templ v0.2.793
|
||||
|
@ -3,35 +3,32 @@ package admin
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"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/logger"
|
||||
"git.markbailey.dev/cerbervs/ptpp/view/layout"
|
||||
"git.markbailey.dev/cervers/ptpp/view/layout"
|
||||
"net/http"
|
||||
|
||||
"git.markbailey.dev/cerbervs/ptpp/view/admin"
|
||||
"git.markbailey.dev/cervers/ptpp/lib/logger"
|
||||
"git.markbailey.dev/cervers/ptpp/lib/session"
|
||||
"git.markbailey.dev/cervers/ptpp/view/admin"
|
||||
)
|
||||
|
||||
type IndexHandler struct {
|
||||
controller.Controller
|
||||
logger logger.ILogger
|
||||
session session.IManager
|
||||
}
|
||||
|
||||
func (h IndexHandler) Init(s session.IManager, d database.IDB, l logger.ILogger, ctx controller.IControllerCtx) controller.IController {
|
||||
h.Logger = l
|
||||
h.Db = d
|
||||
h.Session = s
|
||||
h.Ctx = ctx
|
||||
|
||||
return h
|
||||
func NewAdminIndexHandler(l logger.ILogger, s session.IManager) *IndexHandler {
|
||||
return &IndexHandler{
|
||||
logger: l,
|
||||
session: s,
|
||||
}
|
||||
}
|
||||
|
||||
func (h IndexHandler) Get(w http.ResponseWriter, r *http.Request) error {
|
||||
func (h IndexHandler) Index(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"))
|
||||
h.logger.Error(h.logger.Wrap(err, "Error rendering 404 page"))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@ -40,12 +37,12 @@ func (h IndexHandler) Get(w http.ResponseWriter, r *http.Request) error {
|
||||
username, ok := r.Context().Value("username").(string)
|
||||
if !ok {
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
40
handlers/homepage.go
Normal file
40
handlers/homepage.go
Normal file
@ -0,0 +1,40 @@
|
||||
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
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
package shared
|
||||
|
||||
import (
|
||||
"context"
|
||||
"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/logger"
|
||||
"git.markbailey.dev/cerbervs/ptpp/view/layout"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"git.markbailey.dev/cerbervs/ptpp/view/homepage"
|
||||
)
|
||||
|
||||
type HomePageController struct {
|
||||
controller.Controller
|
||||
}
|
||||
|
||||
func (c *HomePageController) Init(s session.IManager, d database.IDB, l logger.ILogger, ctx controller.IControllerCtx) controller.IController {
|
||||
c.Logger = l
|
||||
c.Db = d
|
||||
c.Session = s
|
||||
c.Ctx = ctx
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func (h HomePageController) 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"), h.Ctx).Render(context.Background(), w); err != nil {
|
||||
h.Logger.Error(h.Logger.Wrap(err, "Error rendering homepage"))
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -1,361 +0,0 @@
|
||||
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 PopulateHandler struct {
|
||||
controller.Controller
|
||||
}
|
||||
|
||||
func (c PopulateHandler) Init(s session.IManager, d database.IDB, l logger.ILogger, ctx controller.IControllerCtx) controller.IController {
|
||||
c.Logger = l
|
||||
c.Db = d
|
||||
c.Session = s
|
||||
c.Ctx = ctx
|
||||
|
||||
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, c.Ctx.GetRouteByName("app.user.sign_up"), 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, c.Ctx.GetRouteByName("app.user.sign_up"), http.StatusSeeOther, false)
|
||||
}
|
||||
|
||||
type SignInHandler struct {
|
||||
controller.Controller
|
||||
}
|
||||
|
||||
func (c SignInHandler) Init(s session.IManager, d database.IDB, l logger.ILogger, ctx controller.IControllerCtx) controller.IController {
|
||||
c.Logger = l
|
||||
c.Db = d
|
||||
c.Session = s
|
||||
c.Ctx = ctx
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
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, c.Ctx.GetRouteByName("app.admin.index"), http.StatusSeeOther, false)
|
||||
} else {
|
||||
return util.Redirect(w, r, c.Ctx.GetRouteByName("app.index"), http.StatusSeeOther, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
formError, ok := sess.Get("formError").(string)
|
||||
if !ok {
|
||||
formError = ""
|
||||
}
|
||||
|
||||
if err := user.SignIn(formError, c.Ctx).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, c.Ctx.GetRouteByName("app.user.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, c.Ctx.GetRouteByName("app.admin.index"), http.StatusSeeOther, false)
|
||||
} else {
|
||||
return util.Redirect(w, r, c.Ctx.GetRouteByName("app.index"), http.StatusSeeOther, false)
|
||||
}
|
||||
}
|
||||
|
||||
type SignUpHandler struct {
|
||||
controller.Controller
|
||||
}
|
||||
|
||||
func (c SignUpHandler) Init(s session.IManager, d database.IDB, l logger.ILogger, ctx controller.IControllerCtx) controller.IController {
|
||||
c.Logger = l
|
||||
c.Db = d
|
||||
c.Session = s
|
||||
c.Ctx = ctx
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
type SignOutHandler struct {
|
||||
controller.Controller
|
||||
}
|
||||
|
||||
func (c SignOutHandler) Init(s session.IManager, d database.IDB, l logger.ILogger, ctx controller.IControllerCtx) controller.IController {
|
||||
c.Logger = l
|
||||
c.Db = d
|
||||
c.Session = s
|
||||
c.Ctx = ctx
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
315
handlers/user.go
Normal file
315
handlers/user.go
Normal file
@ -0,0 +1,315 @@
|
||||
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)
|
||||
}
|
79
infrastructure/router.go
Normal file
79
infrastructure/router.go
Normal file
@ -0,0 +1,79 @@
|
||||
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()
|
||||
}
|
64
infrastructure/server.go
Normal file
64
infrastructure/server.go
Normal file
@ -0,0 +1,64 @@
|
||||
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)
|
||||
}
|
||||
}
|
@ -1,12 +1,12 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"git.markbailey.dev/cerbervs/ptpp/lib/database/development"
|
||||
"git.markbailey.dev/cerbervs/ptpp/lib/database/production"
|
||||
"git.markbailey.dev/cerbervs/ptpp/lib/repository"
|
||||
"git.markbailey.dev/cervers/ptpp/lib/database/development"
|
||||
"git.markbailey.dev/cervers/ptpp/lib/database/production"
|
||||
"git.markbailey.dev/cervers/ptpp/lib/repository"
|
||||
"os"
|
||||
|
||||
"git.markbailey.dev/cerbervs/ptpp/lib/logger"
|
||||
"git.markbailey.dev/cervers/ptpp/lib/logger"
|
||||
)
|
||||
|
||||
type IDB interface {
|
||||
|
@ -2,9 +2,9 @@ package development
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"git.markbailey.dev/cerbervs/ptpp/lib/logger"
|
||||
"git.markbailey.dev/cerbervs/ptpp/lib/repository"
|
||||
"git.markbailey.dev/cerbervs/ptpp/models/sqlite"
|
||||
"git.markbailey.dev/cervers/ptpp/lib/logger"
|
||||
"git.markbailey.dev/cervers/ptpp/lib/repository"
|
||||
"git.markbailey.dev/cervers/ptpp/models/sqlite"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
"sync"
|
||||
)
|
||||
|
@ -2,9 +2,9 @@ package development
|
||||
|
||||
import (
|
||||
"context"
|
||||
"git.markbailey.dev/cerbervs/ptpp/lib/database/dto"
|
||||
pterror "git.markbailey.dev/cerbervs/ptpp/lib/error"
|
||||
"git.markbailey.dev/cerbervs/ptpp/models/sqlite"
|
||||
"git.markbailey.dev/cervers/ptpp/lib/database/dto"
|
||||
pterror "git.markbailey.dev/cervers/ptpp/lib/error"
|
||||
"git.markbailey.dev/cervers/ptpp/models/sqlite"
|
||||
)
|
||||
|
||||
type HeartbeatRepository struct{}
|
||||
|
@ -2,9 +2,9 @@ package development
|
||||
|
||||
import (
|
||||
"context"
|
||||
"git.markbailey.dev/cerbervs/ptpp/lib/database/dto"
|
||||
pterror "git.markbailey.dev/cerbervs/ptpp/lib/error"
|
||||
"git.markbailey.dev/cerbervs/ptpp/models/sqlite"
|
||||
"git.markbailey.dev/cervers/ptpp/lib/database/dto"
|
||||
pterror "git.markbailey.dev/cervers/ptpp/lib/error"
|
||||
"git.markbailey.dev/cervers/ptpp/models/sqlite"
|
||||
)
|
||||
|
||||
type OrganizationRepository struct{}
|
||||
|
@ -2,9 +2,9 @@ package development
|
||||
|
||||
import (
|
||||
"context"
|
||||
"git.markbailey.dev/cerbervs/ptpp/lib/database/dto"
|
||||
pterror "git.markbailey.dev/cerbervs/ptpp/lib/error"
|
||||
"git.markbailey.dev/cerbervs/ptpp/models/sqlite"
|
||||
"git.markbailey.dev/cervers/ptpp/lib/database/dto"
|
||||
pterror "git.markbailey.dev/cervers/ptpp/lib/error"
|
||||
"git.markbailey.dev/cervers/ptpp/models/sqlite"
|
||||
)
|
||||
|
||||
type UserRepository struct{}
|
||||
|
@ -2,9 +2,9 @@ package production
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"git.markbailey.dev/cerbervs/ptpp/lib/logger"
|
||||
"git.markbailey.dev/cerbervs/ptpp/lib/repository"
|
||||
"git.markbailey.dev/cerbervs/ptpp/models/postgres"
|
||||
"git.markbailey.dev/cervers/ptpp/lib/logger"
|
||||
"git.markbailey.dev/cervers/ptpp/lib/repository"
|
||||
"git.markbailey.dev/cervers/ptpp/models/postgres"
|
||||
_ "github.com/lib/pq"
|
||||
"os"
|
||||
"sync"
|
||||
|
@ -2,9 +2,9 @@ package production
|
||||
|
||||
import (
|
||||
"context"
|
||||
"git.markbailey.dev/cerbervs/ptpp/lib/database/dto"
|
||||
pterror "git.markbailey.dev/cerbervs/ptpp/lib/error"
|
||||
"git.markbailey.dev/cerbervs/ptpp/models/postgres"
|
||||
"git.markbailey.dev/cervers/ptpp/lib/database/dto"
|
||||
pterror "git.markbailey.dev/cervers/ptpp/lib/error"
|
||||
"git.markbailey.dev/cervers/ptpp/models/postgres"
|
||||
)
|
||||
|
||||
type HeartbeatRepository struct{}
|
||||
|
@ -2,9 +2,9 @@ package production
|
||||
|
||||
import (
|
||||
"context"
|
||||
"git.markbailey.dev/cerbervs/ptpp/lib/database/dto"
|
||||
pterror "git.markbailey.dev/cerbervs/ptpp/lib/error"
|
||||
"git.markbailey.dev/cerbervs/ptpp/models/postgres"
|
||||
"git.markbailey.dev/cervers/ptpp/lib/database/dto"
|
||||
pterror "git.markbailey.dev/cervers/ptpp/lib/error"
|
||||
"git.markbailey.dev/cervers/ptpp/models/postgres"
|
||||
)
|
||||
|
||||
type OrganizationRepository struct{}
|
||||
|
@ -2,9 +2,9 @@ package production
|
||||
|
||||
import (
|
||||
"context"
|
||||
"git.markbailey.dev/cerbervs/ptpp/lib/database/dto"
|
||||
pterror "git.markbailey.dev/cerbervs/ptpp/lib/error"
|
||||
"git.markbailey.dev/cerbervs/ptpp/models/postgres"
|
||||
"git.markbailey.dev/cervers/ptpp/lib/database/dto"
|
||||
pterror "git.markbailey.dev/cervers/ptpp/lib/error"
|
||||
"git.markbailey.dev/cervers/ptpp/models/postgres"
|
||||
)
|
||||
|
||||
type UserRepository struct{}
|
||||
|
@ -1,8 +1,8 @@
|
||||
package locator
|
||||
|
||||
import (
|
||||
perr "git.markbailey.dev/cerbervs/ptpp/lib/error"
|
||||
"git.markbailey.dev/cerbervs/ptpp/lib/locator/service"
|
||||
perr "git.markbailey.dev/cervers/ptpp/lib/error"
|
||||
"git.markbailey.dev/cervers/ptpp/lib/locator/service"
|
||||
"sync"
|
||||
)
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"git.markbailey.dev/cerbervs/ptpp/lib/database"
|
||||
"git.markbailey.dev/cervers/ptpp/lib/database"
|
||||
"sync"
|
||||
)
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"git.markbailey.dev/cerbervs/ptpp/lib/logger"
|
||||
"git.markbailey.dev/cervers/ptpp/lib/logger"
|
||||
"sync"
|
||||
)
|
||||
|
||||
|
@ -2,8 +2,8 @@ package logger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
pterror "git.markbailey.dev/cerbervs/ptpp/lib/error"
|
||||
"git.markbailey.dev/cerbervs/ptpp/util"
|
||||
pterror "git.markbailey.dev/cervers/ptpp/lib/error"
|
||||
"git.markbailey.dev/cervers/ptpp/util"
|
||||
"log"
|
||||
"os"
|
||||
"sync"
|
||||
|
@ -2,8 +2,8 @@ package logger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
pterror "git.markbailey.dev/cerbervs/ptpp/lib/error"
|
||||
"git.markbailey.dev/cerbervs/ptpp/util"
|
||||
pterror "git.markbailey.dev/cervers/ptpp/lib/error"
|
||||
"git.markbailey.dev/cervers/ptpp/util"
|
||||
"log"
|
||||
"os"
|
||||
"sync"
|
||||
|
@ -2,71 +2,56 @@ package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.markbailey.dev/cervers/ptpp/lib/database"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"git.markbailey.dev/cerbervs/ptpp/app/session"
|
||||
"git.markbailey.dev/cerbervs/ptpp/util"
|
||||
|
||||
"git.markbailey.dev/cerbervs/ptpp/lib/logger"
|
||||
"git.markbailey.dev/cervers/ptpp/lib/logger"
|
||||
"git.markbailey.dev/cervers/ptpp/lib/session"
|
||||
"git.markbailey.dev/cervers/ptpp/util"
|
||||
)
|
||||
|
||||
type IRouterCtx interface {
|
||||
GetRouteByName(string) (string, error)
|
||||
type Middleware struct {
|
||||
l logger.ILogger
|
||||
db database.IDB
|
||||
}
|
||||
|
||||
type IMiddlewareCtx interface {
|
||||
GetRouteByName(string) string
|
||||
}
|
||||
type (
|
||||
ErrHandler func(http.ResponseWriter, *http.Request) error
|
||||
Func func(http.Handler) http.Handler
|
||||
)
|
||||
|
||||
type MiddlewareCtx struct {
|
||||
RouterCtx IRouterCtx
|
||||
}
|
||||
var (
|
||||
sess session.IManager
|
||||
)
|
||||
|
||||
func (m MiddlewareCtx) GetRouteByName(name string) string {
|
||||
path, err := m.RouterCtx.GetRouteByName(name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
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)
|
||||
}
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
type Func func(http.Handler, IMiddlewareCtx) http.Handler
|
||||
|
||||
var sess session.IManager
|
||||
|
||||
func Compose(xs []Func) Func {
|
||||
return func(next http.Handler, m IMiddlewareCtx) http.Handler {
|
||||
func Compose(xs ...Func) Func {
|
||||
return func(next http.Handler) http.Handler {
|
||||
for i := len(xs) - 1; i >= 0; i-- {
|
||||
x := xs[i]
|
||||
next = x(next, m)
|
||||
next = x(next)
|
||||
}
|
||||
|
||||
return next
|
||||
}
|
||||
}
|
||||
|
||||
func DontPanic(next http.Handler, m IMiddlewareCtx) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
http.Error(w, fmt.Sprintf("%v", r), http.StatusInternalServerError)
|
||||
}
|
||||
}()
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
func WithLogger(next http.Handler, m IMiddlewareCtx) http.Handler {
|
||||
func WithLogger(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
handlerSess := sess.SessionStart(w, r)
|
||||
|
||||
username, ok := handlerSess.Get("username").(string)
|
||||
if !ok {
|
||||
username = ""
|
||||
username = "<Not Found>"
|
||||
}
|
||||
username = "(username " + username + ")"
|
||||
|
||||
ipAddr := r.Header.Get("X-Real-IP")
|
||||
if ipAddr == "" {
|
||||
@ -78,7 +63,7 @@ func WithLogger(next http.Handler, m IMiddlewareCtx) http.Handler {
|
||||
|
||||
handlerLogger := logger.NewCompositeLogger()
|
||||
output := fmt.Sprintf(
|
||||
"%s Request sent from %s to %s %s",
|
||||
"%s Request sent from %s to %s (username? %s)",
|
||||
r.Method,
|
||||
ipAddr,
|
||||
r.URL.Path,
|
||||
@ -90,7 +75,7 @@ func WithLogger(next http.Handler, m IMiddlewareCtx) http.Handler {
|
||||
})
|
||||
}
|
||||
|
||||
func WithAuth(next http.Handler, m IMiddlewareCtx) http.Handler {
|
||||
func WithAuth(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
claims *util.CustomClaims
|
||||
@ -109,15 +94,15 @@ func WithAuth(next http.Handler, m IMiddlewareCtx) http.Handler {
|
||||
}
|
||||
|
||||
if cookie, err = r.Cookie("token"); err != nil {
|
||||
_ = util.Redirect(w, r, m.GetRouteByName("app.user.sign_in"), http.StatusPermanentRedirect, true)
|
||||
_ = util.Redirect(w, r, "/signin", http.StatusSeeOther, true)
|
||||
return
|
||||
}
|
||||
if token = cookie.Value; token == "" {
|
||||
_ = util.Redirect(w, r, m.GetRouteByName("app.user.sign_in"), http.StatusPermanentRedirect, true)
|
||||
_ = util.Redirect(w, r, "/signin", http.StatusSeeOther, true)
|
||||
return
|
||||
}
|
||||
if claims, err = util.ParseToken(token, os.Getenv("TOKEN_SECRET")); err != nil {
|
||||
_ = util.Redirect(w, r, m.GetRouteByName("app.user.sign_in"), http.StatusPermanentRedirect, true)
|
||||
_ = util.Redirect(w, r, "/signin", http.StatusSeeOther, true)
|
||||
return
|
||||
}
|
||||
|
||||
@ -128,7 +113,7 @@ func WithAuth(next http.Handler, m IMiddlewareCtx) http.Handler {
|
||||
})
|
||||
}
|
||||
|
||||
func WithUsername(next http.Handler, m IMiddlewareCtx) http.Handler {
|
||||
func WithUsername(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
claims *util.CustomClaims
|
||||
@ -170,9 +155,5 @@ func WithUsername(next http.Handler, m IMiddlewareCtx) http.Handler {
|
||||
}
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
sess, err = session.NewManager("memory", "ptpp", 3600)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
sess, _ = session.NewManager("memory", "ptpp", 3600)
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
package repository
|
||||
|
||||
import "git.markbailey.dev/cerbervs/ptpp/lib/database/dto"
|
||||
import "git.markbailey.dev/cervers/ptpp/lib/database/dto"
|
||||
|
||||
type IHeartbeat interface {
|
||||
CreateHeartbeat(h *dto.Heartbeat) (*dto.Heartbeat, error)
|
||||
|
1
lib/session/memory/cookieprovider.go
Normal file
1
lib/session/memory/cookieprovider.go
Normal file
@ -0,0 +1 @@
|
||||
package memory
|
@ -1,9 +1,11 @@
|
||||
package session
|
||||
package memory
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"git.markbailey.dev/cervers/ptpp/lib/session"
|
||||
)
|
||||
|
||||
var prov = &Provider{list: list.New()}
|
||||
@ -56,7 +58,7 @@ type Provider struct {
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
func (p *Provider) SessionInit(sid string) (ISession, error) {
|
||||
func (p *Provider) SessionInit(sid string) (session.ISession, error) {
|
||||
prov.lock.Lock()
|
||||
defer prov.lock.Unlock()
|
||||
v := make(map[interface{}]interface{}, 0)
|
||||
@ -66,7 +68,7 @@ func (p *Provider) SessionInit(sid string) (ISession, error) {
|
||||
return newSess, nil
|
||||
}
|
||||
|
||||
func (p *Provider) SessionRead(sid string) (ISession, error) {
|
||||
func (p *Provider) SessionRead(sid string) (session.ISession, error) {
|
||||
if element, ok := prov.sessions[sid]; ok {
|
||||
return element.Value.(*MemSessionStore), nil
|
||||
}
|
||||
@ -117,5 +119,5 @@ func (p *Provider) SessionUpdate(sid string) error {
|
||||
|
||||
func init() {
|
||||
prov.sessions = make(map[string]*list.Element, 0)
|
||||
Register("memory", prov)
|
||||
session.Register("memory", prov)
|
||||
}
|
148
package-lock.json
generated
148
package-lock.json
generated
@ -1,15 +1,16 @@
|
||||
{
|
||||
"name": "go-full-stack",
|
||||
"name": "ptpp",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"htmx.org": "^1.9.10"
|
||||
"htmx.org": "^1.9.10",
|
||||
"preline": "^2.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/forms": "^0.5.9",
|
||||
"tailwindcss": "^3.4.15"
|
||||
"tailwindcss": "^3.4.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@alloc/quick-lru": {
|
||||
@ -134,6 +135,15 @@
|
||||
"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": {
|
||||
"version": "0.5.9",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.9.tgz",
|
||||
@ -221,13 +231,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/braces": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
||||
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fill-range": "^7.1.1"
|
||||
"fill-range": "^7.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@ -324,7 +333,6 @@
|
||||
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
||||
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"cssesc": "bin/cssesc"
|
||||
},
|
||||
@ -394,11 +402,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/fill-range": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
},
|
||||
@ -555,7 +562,6 @@
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.12.0"
|
||||
}
|
||||
@ -585,11 +591,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/jiti": {
|
||||
"version": "1.21.6",
|
||||
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz",
|
||||
"integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==",
|
||||
"version": "1.21.0",
|
||||
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz",
|
||||
"integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"jiti": "bin/jiti.js"
|
||||
}
|
||||
@ -628,13 +633,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/micromatch": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
|
||||
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
|
||||
"integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"braces": "^3.0.3",
|
||||
"braces": "^3.0.2",
|
||||
"picomatch": "^2.3.1"
|
||||
},
|
||||
"engines": {
|
||||
@ -762,11 +766,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
||||
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
@ -799,9 +802,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.49",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz",
|
||||
"integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==",
|
||||
"version": "8.4.35",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz",
|
||||
"integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@ -817,11 +820,10 @@
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.7",
|
||||
"picocolors": "^1.1.1",
|
||||
"source-map-js": "^1.2.1"
|
||||
"picocolors": "^1.0.0",
|
||||
"source-map-js": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || >=14"
|
||||
@ -908,37 +910,29 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-nested": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz",
|
||||
"integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==",
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz",
|
||||
"integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/postcss/"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"postcss-selector-parser": "^6.1.1"
|
||||
"postcss-selector-parser": "^6.0.11"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/postcss/"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"postcss": "^8.2.14"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-selector-parser": {
|
||||
"version": "6.1.2",
|
||||
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
|
||||
"integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
|
||||
"version": "6.0.15",
|
||||
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz",
|
||||
"integrity": "sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cssesc": "^3.0.0",
|
||||
"util-deprecate": "^1.0.2"
|
||||
@ -953,6 +947,14 @@
|
||||
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
|
||||
"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": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||
@ -1078,11 +1080,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
|
||||
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@ -1218,34 +1219,33 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tailwindcss": {
|
||||
"version": "3.4.15",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.15.tgz",
|
||||
"integrity": "sha512-r4MeXnfBmSOuKUWmXe6h2CcyfzJCEk4F0pptO5jlnYSIViUkVmsawj80N5h2lO3gwcmSb4n3PuN+e+GC1Guylw==",
|
||||
"version": "3.4.1",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.1.tgz",
|
||||
"integrity": "sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@alloc/quick-lru": "^5.2.0",
|
||||
"arg": "^5.0.2",
|
||||
"chokidar": "^3.6.0",
|
||||
"chokidar": "^3.5.3",
|
||||
"didyoumean": "^1.2.2",
|
||||
"dlv": "^1.1.3",
|
||||
"fast-glob": "^3.3.2",
|
||||
"fast-glob": "^3.3.0",
|
||||
"glob-parent": "^6.0.2",
|
||||
"is-glob": "^4.0.3",
|
||||
"jiti": "^1.21.6",
|
||||
"jiti": "^1.19.1",
|
||||
"lilconfig": "^2.1.0",
|
||||
"micromatch": "^4.0.8",
|
||||
"micromatch": "^4.0.5",
|
||||
"normalize-path": "^3.0.0",
|
||||
"object-hash": "^3.0.0",
|
||||
"picocolors": "^1.1.1",
|
||||
"postcss": "^8.4.47",
|
||||
"picocolors": "^1.0.0",
|
||||
"postcss": "^8.4.23",
|
||||
"postcss-import": "^15.1.0",
|
||||
"postcss-js": "^4.0.1",
|
||||
"postcss-load-config": "^4.0.2",
|
||||
"postcss-nested": "^6.2.0",
|
||||
"postcss-selector-parser": "^6.1.2",
|
||||
"resolve": "^1.22.8",
|
||||
"sucrase": "^3.35.0"
|
||||
"postcss-load-config": "^4.0.1",
|
||||
"postcss-nested": "^6.0.1",
|
||||
"postcss-selector-parser": "^6.0.11",
|
||||
"resolve": "^1.22.2",
|
||||
"sucrase": "^3.32.0"
|
||||
},
|
||||
"bin": {
|
||||
"tailwind": "lib/cli.js",
|
||||
@ -1281,7 +1281,6 @@
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-number": "^7.0.0"
|
||||
},
|
||||
@ -1299,8 +1298,7 @@
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
|
@ -1,9 +1,10 @@
|
||||
{
|
||||
"devDependencies": {
|
||||
"@tailwindcss/forms": "^0.5.9",
|
||||
"tailwindcss": "^3.4.15"
|
||||
"tailwindcss": "^3.4.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"htmx.org": "^1.9.10"
|
||||
"htmx.org": "^1.9.10",
|
||||
"preline": "^2.0.3"
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
package admin
|
||||
|
||||
import "git.markbailey.dev/cerbervs/ptpp/view/layout"
|
||||
import "git.markbailey.dev/cervers/ptpp/view/layout"
|
||||
|
||||
templ Index(name string) {
|
||||
@layout.Layout() {
|
||||
|
@ -8,7 +8,7 @@ package admin
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import "git.markbailey.dev/cerbervs/ptpp/view/layout"
|
||||
import "git.markbailey.dev/cervers/ptpp/view/layout"
|
||||
|
||||
func Index(name string) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
|
@ -1,28 +1,27 @@
|
||||
package homepage
|
||||
|
||||
import (
|
||||
"git.markbailey.dev/cerbervs/ptpp/view/layout"
|
||||
"git.markbailey.dev/cerbervs/ptpp/app/controller"
|
||||
)
|
||||
import "git.markbailey.dev/cervers/ptpp/view/layout"
|
||||
|
||||
templ Homepage(env string, context controller.IControllerCtx) {
|
||||
templ Homepage(env string) {
|
||||
@layout.Layout() {
|
||||
<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={ templ.SafeURL(context.GetRouteByName("app.user.sign_up")) }>
|
||||
<button class="text-gray-400 text-md border-black rounded-lg bg-gray-300 p-2">
|
||||
Sign Up
|
||||
</button>
|
||||
</a>
|
||||
<a href={ templ.SafeURL(context.GetRouteByName("app.user.sign_in")) }>
|
||||
<button class="text-gray-400 text-md border-black rounded-lg bg-gray-300 p-2">
|
||||
Sign In
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
if env == "development" {
|
||||
<div>
|
||||
<a href="/signup">
|
||||
<button class="text-gray-400 text-md border-black rounded-lg bg-gray-300 p-2">
|
||||
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>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
@ -8,12 +8,9 @@ package homepage
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import (
|
||||
"git.markbailey.dev/cerbervs/ptpp/app/controller"
|
||||
"git.markbailey.dev/cerbervs/ptpp/view/layout"
|
||||
)
|
||||
import "git.markbailey.dev/cervers/ptpp/view/layout"
|
||||
|
||||
func Homepage(env string, context controller.IControllerCtx) templ.Component {
|
||||
func Homepage(env string) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
@ -46,25 +43,17 @@ func Homepage(env string, context controller.IControllerCtx) templ.Component {
|
||||
}()
|
||||
}
|
||||
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><div><a href=\"")
|
||||
_, 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>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 templ.SafeURL = templ.SafeURL(context.GetRouteByName("app.user.sign_up"))
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var3)))
|
||||
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("\"><button class=\"text-gray-400 text-md border-black rounded-lg bg-gray-300 p-2\">Sign Up</button></a> <a href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var4 templ.SafeURL = templ.SafeURL(context.GetRouteByName("app.user.sign_in"))
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var4)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><button class=\"text-gray-400 text-md border-black rounded-lg bg-gray-300 p-2\">Sign In</button></a></div></div>")
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ templ Layout() {
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="#layout_content" hx-ext="response-targets" hx-target-error="this">
|
||||
<div id="#layout_content" hx-ext="response-targets" hx-target-5*="this">
|
||||
{ children... }
|
||||
</div>
|
||||
<script type="text/javascript" src="/public/assets/js/index.js"></script>
|
||||
|
@ -29,7 +29,7 @@ func Layout() templ.Component {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
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-error=\"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-5*=\"this\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -1,11 +1,8 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"git.markbailey.dev/cerbervs/ptpp/view/layout"
|
||||
"git.markbailey.dev/cerbervs/ptpp/app/controller"
|
||||
)
|
||||
import "git.markbailey.dev/cervers/ptpp/view/layout"
|
||||
|
||||
templ SignIn(err string, context controller.IControllerCtx) {
|
||||
templ SignIn(err string) {
|
||||
@layout.Layout() {
|
||||
<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">
|
||||
@ -18,7 +15,9 @@ templ SignIn(err string, context controller.IControllerCtx) {
|
||||
</h1>
|
||||
<form
|
||||
class="space-y-4 md:space-y-6"
|
||||
hx-post={ context.GetRouteByName("app.user.sign_in") }
|
||||
action="/signin"
|
||||
method="post"
|
||||
hx-post="/signin"
|
||||
hx-swap="outerHTML"
|
||||
hx-target="#section"
|
||||
>
|
||||
|
@ -8,12 +8,9 @@ package user
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import (
|
||||
"git.markbailey.dev/cerbervs/ptpp/app/controller"
|
||||
"git.markbailey.dev/cerbervs/ptpp/view/layout"
|
||||
)
|
||||
import "git.markbailey.dev/cervers/ptpp/view/layout"
|
||||
|
||||
func SignIn(err string, context controller.IControllerCtx) templ.Component {
|
||||
func SignIn(err string) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
@ -46,20 +43,7 @@ func SignIn(err string, context controller.IControllerCtx) templ.Component {
|
||||
}()
|
||||
}
|
||||
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\" hx-post=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(context.GetRouteByName("app.user.sign_in"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/user/signin.templ`, Line: 21, Col: 59}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" 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=\"/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>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@ -68,12 +52,12 @@ func SignIn(err string, context controller.IControllerCtx) templ.Component {
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var4 string
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(err)
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(err)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/user/signin.templ`, Line: 55, Col: 14}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/user/signin.templ`, Line: 54, Col: 14}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
package user
|
||||
|
||||
import "git.markbailey.dev/cerbervs/ptpp/view/layout"
|
||||
import "git.markbailey.dev/cervers/ptpp/view/layout"
|
||||
|
||||
templ SignUpForm() {
|
||||
@layout.Layout() {
|
||||
|
@ -8,7 +8,7 @@ package user
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import "git.markbailey.dev/cerbervs/ptpp/view/layout"
|
||||
import "git.markbailey.dev/cervers/ptpp/view/layout"
|
||||
|
||||
func SignUpForm() templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
package user
|
||||
|
||||
import "git.markbailey.dev/cerbervs/ptpp/view/layout"
|
||||
import "git.markbailey.dev/cervers/ptpp/view/layout"
|
||||
|
||||
templ SignUpSuccess() {
|
||||
@layout.Layout() {
|
||||
|
@ -8,7 +8,7 @@ package user
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import "git.markbailey.dev/cerbervs/ptpp/view/layout"
|
||||
import "git.markbailey.dev/cervers/ptpp/view/layout"
|
||||
|
||||
func SignUpSuccess() templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user