Big refactor

This commit is contained in:
soup 2024-11-15 16:47:25 -05:00
parent 9e03bdb2de
commit cf950db825
32 changed files with 174 additions and 163 deletions

View file

@ -1,10 +1,10 @@
.PHONY: build
build:
go build bin/shelves.go
go build cmd/shelves/main.go
run:
go run bin/shelves.go
go run cmd/shelves/main.go
watch:
fd | entr -cr make run

View file

@ -1,32 +0,0 @@
package main
import (
"fmt"
"log"
"net/http"
_ "github.com/mattn/go-sqlite3"
"shelves/db"
"shelves/httpx"
"shelves/routes"
)
func main() {
db, migrateResult, err := db.Open("./shelves.db")
if err != nil {
log.Fatalf("Failed to open DB: %v", err)
}
if migrateResult.MigrationError != nil {
log.Printf("An error was encountered while upgrading the database schema. You are on version %v, but the latest is %v. The error was: %v", migrateResult.SchemaVerNew, migrateResult.SchemaVerLatest, migrateResult.MigrationError)
}
schemaNeedsUpdating := migrateResult.SchemaVerNew != migrateResult.SchemaVerLatest
if schemaNeedsUpdating {
log.Printf("Your database schema needs to be updated. The application will continue to run, but you may encounter errors.\n")
}
_ = db
routes := routes.Routes()
fmt.Println("Listening on localhost:8999")
http.ListenAndServe("localhost:8999", httpx.Log(httpx.WithCtx(db, routes)))
}

28
embeds.go Normal file
View file

@ -0,0 +1,28 @@
package shelves
import (
"embed"
"fmt"
"io/fs"
)
//go:embed frontend/*
var fe embed.FS
var Frontend, _ = fs.Sub(fe, "frontend")
//go:embed internal/templates/views/*.tmpl.* internal/templates/components/*.tmpl.*
var templates embed.FS
var Templates, _ = fs.Sub(templates, "internal/templates")
func printEmbeddedFiles(efs fs.FS) error {
return fs.WalkDir(efs, ".", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if !d.IsDir() {
fmt.Println(path)
}
return nil
})
}

View file

@ -1,11 +0,0 @@
package shelves
import (
"embed"
"io/fs"
)
//go:embed frontend/*
var fe embed.FS
var Frontend, _ = fs.Sub(fe, "frontend")

2
go.mod
View file

@ -1,4 +1,4 @@
module shelves
module git.soup.land/soup/shelves
go 1.23

View file

@ -1,11 +1,11 @@
package httpx
import (
"git.soup.land/soup/shelves/internal/errorsx"
"git.soup.land/soup/shelves/internal/urls"
"log"
"net/http"
"net/url"
"shelves/errorsx"
"shelves/urls"
)
func SeeOther(w http.ResponseWriter, location string) {

View file

@ -3,9 +3,9 @@ package httpx
import (
"context"
"database/sql"
"git.soup.land/soup/shelves/internal/auth"
"log"
"net/http"
"shelves/auth"
"time"
)

26
internal/routes/home.go Normal file
View file

@ -0,0 +1,26 @@
package routes
import (
"net/http"
"git.soup.land/soup/shelves/internal/httpx"
"git.soup.land/soup/shelves/internal/templates"
"git.soup.land/soup/shelves/internal/templates/components"
)
type homeTemplate struct {
components.Page
}
var homeTmpl = templates.MustParseEmbed("views/home.tmpl.html")
func HomeGet(w http.ResponseWriter, req *http.Request) {
ctx := httpx.GetCtx(req)
h := components.Page{
Title: "Home",
SessionInfo: ctx.SessionInfo,
}.HTML()
html(w, h)
}

View file

@ -5,11 +5,12 @@ import (
"html/template"
"net/http"
"net/url"
"shelves/auth"
"shelves/errorsx"
"shelves/forms"
"shelves/httpx"
"shelves/templates"
"git.soup.land/soup/shelves/internal/auth"
"git.soup.land/soup/shelves/internal/errorsx"
"git.soup.land/soup/shelves/internal/forms"
"git.soup.land/soup/shelves/internal/httpx"
"git.soup.land/soup/shelves/internal/templates/components"
)
type loginForm struct {
@ -21,26 +22,32 @@ type loginFormErrors struct {
password error
}
func loginRenderView(f loginForm, e loginFormErrors) template.HTML {
body := templates.Form{
func loginRenderView(f loginForm, e loginFormErrors, ctx httpx.Ctx) template.HTML {
formHtml := components.Form{
Action: "/login",
Fields: []templates.Field{
templates.Field{
Fields: []components.Field{
components.Field{
Label: "Password",
Type: "password",
Placeholder: "Password",
Name: "password",
Error: errorsx.String(e.password),
},
templates.Field{
components.Field{
Type: "hidden",
Name: "redirectTo",
Value: f.redirectTo,
},
},
}.HTML()
page := components.Page{
Title: "Login",
SessionInfo: ctx.SessionInfo,
Body: formHtml,
}
return templates.Page{Title: "Login", Body: body.HTML()}.HTML()
return page.HTML()
}
func getRedirectTo(r *http.Request) string {
@ -66,7 +73,7 @@ func LoginGet(w http.ResponseWriter, r *http.Request) {
return
}
html(w, loginRenderView(loginForm{redirectTo: redirectTo}, loginFormErrors{}))
html(w, loginRenderView(loginForm{redirectTo: redirectTo}, loginFormErrors{}, ctx))
}
func loginParseForm(f *loginForm, e *loginFormErrors, vs url.Values, v *forms.Validator) {
@ -84,7 +91,7 @@ func LoginPost(w http.ResponseWriter, r *http.Request) {
})
if failed {
httpx.BadRequest(w)
html(w, loginRenderView(form, errs))
html(w, loginRenderView(form, errs, ctx))
return
}
@ -102,7 +109,7 @@ func LoginPost(w http.ResponseWriter, r *http.Request) {
httpx.BadRequest(w)
errs.password = errors.New("Incorrect password")
html(w, loginRenderView(form, errs))
html(w, loginRenderView(form, errs, ctx))
return
}

View file

@ -1,13 +1,12 @@
package routes
import (
"git.soup.land/soup/shelves"
"git.soup.land/soup/shelves/internal/httpx"
"html/template"
"net/http"
"shelves/httpx"
"strings"
"time"
"shelves"
)
func html(w http.ResponseWriter, s template.HTML) {

View file

@ -6,41 +6,42 @@ import (
"html/template"
"net/http"
"net/url"
"shelves/auth"
"shelves/errorsx"
"shelves/forms"
"shelves/httpx"
"shelves/templates"
"git.soup.land/soup/shelves/internal/auth"
"git.soup.land/soup/shelves/internal/errorsx"
"git.soup.land/soup/shelves/internal/forms"
"git.soup.land/soup/shelves/internal/httpx"
"git.soup.land/soup/shelves/internal/templates/components"
)
func settingsRenderView(f settingsForm, e settingsFormErrors) template.HTML {
body := templates.Form{
body := components.Form{
Action: "/settings",
Fields: []templates.Field{
templates.Field{
Fields: []components.Field{
components.Field{
Label: "Display name",
Name: "displayName",
Placeholder: "Jane Doe",
Value: f.displayName,
Error: errorsx.String(e.displayName),
},
templates.Field{
components.Field{
Label: "Password",
Type: "password",
Placeholder: "Password",
Name: "password",
Error: errorsx.String(e.password),
},
templates.Field{
components.Field{
Label: "Confirm password",
Placeholder: "Confirm Password",
Type: "password",
Name: "passwordConfirmation",
},
},
}
}.HTML()
return templates.PageBase{Title: "Set up", Body: body.HTML()}.HTML()
return components.Page{Title: "Set up", Body: body}.HTML()
}
func queryGetOwnerSettings(db *sql.DB) (settingsForm, error) {

View file

@ -0,0 +1 @@
package components

View file

@ -1,6 +1,7 @@
package templates
package components
import (
"git.soup.land/soup/shelves/internal/templates"
"html/template"
)
@ -28,6 +29,8 @@ type Form struct {
Fields []Field
}
var formTmpl = templates.MustParseEmbed("components/form.tmpl.html")
func (f Form) HTML() template.HTML {
if f.Method == "" {
f.Method = "POST"
@ -37,5 +40,5 @@ func (f Form) HTML() template.HTML {
f.Fields[i].init()
}
return Tmpls.HTML("form.tmpl.html", f)
return templates.HTML(formTmpl, "form", f)
}

View file

@ -0,0 +1,5 @@
{{define "field"}}
{{end}}
{{define "form"}}
{{end}}

View file

@ -0,0 +1,23 @@
package components
import (
"git.soup.land/soup/shelves/internal/auth"
"git.soup.land/soup/shelves/internal/templates"
"html/template"
)
type Page struct {
SessionInfo auth.SessionInfo
Title string
Head template.HTML
Body template.HTML
BodyBefore template.HTML
BodyAfter template.HTML
}
var pageTmpl = templates.MustParseEmbed("components/page.tmpl.html")
func (p Page) HTML() template.HTML {
return templates.HTML(pageTmpl, "page", p)
}

View file

@ -0,0 +1,22 @@
package templates
import (
"git.soup.land/soup/shelves"
"git.soup.land/soup/shelves/internal/errorsx"
"html/template"
"strings"
)
var files = shelves.Templates
func MustParseEmbed(path string) *template.Template {
return template.Must(template.ParseFS(files, path))
}
func HTML(t *template.Template, name string, data any) template.HTML {
out := strings.Builder{}
err := t.ExecuteTemplate(&out, name, data)
out.WriteString(errorsx.String(err))
return template.HTML(out.String())
}

View file

@ -0,0 +1 @@
package views

View file

@ -0,0 +1,15 @@
{{define "Title"}}Home{{end}}
{{define "Body"}}
<section>
<h1>Featured Shelves</h1>
</section>
<section>
<h1>Featured Items</h1>
</section>
<section>
<h1>Recent Activity</h1>
</section>
{{end}}
{{template "page.tmpl.html" .}}

View file

@ -0,0 +1,6 @@
{{template "page.tmpl.html" .}}
{{define "Title"}}Login{{end}}
{{define "Body"}}
{{end}}

View file

@ -1,20 +0,0 @@
package routes
import (
"net/http"
"shelves/httpx"
"shelves/templates"
)
type homeTemplate struct {
templates.Page
}
func HomeGet(w http.ResponseWriter, req *http.Request) {
ctx := httpx.GetCtx(req)
tmpl := homeTemplate{
Page: templates.Page{SessionInfo: ctx.SessionInfo},
}
html(w, templates.Tmpls.HTML("home.tmpl.html", tmpl))
}

View file

@ -1,32 +0,0 @@
package templates
import (
"html/template"
"shelves/auth"
)
type PageBase struct {
Title string
Head template.HTML
Body template.HTML
BodyBefore template.HTML
BodyAfter template.HTML
}
func (pb PageBase) HTML() template.HTML {
return Tmpls.HTML("page_base.tmpl.html", pb)
}
type Page struct {
SessionInfo auth.SessionInfo
Title string
Head template.HTML
Body template.HTML
BodyBefore template.HTML
BodyAfter template.HTML
}
func (p Page) HTML() template.HTML {
return Tmpls.HTML("page.tmpl.html", p)
}

View file

@ -1,28 +0,0 @@
package templates
import (
"embed"
"fmt"
"html/template"
"strings"
)
//go:embed tmpls/*
var files embed.FS
type Template struct {
*template.Template
}
var Tmpls = Template{template.Must(template.ParseFS(files, "tmpls/*"))}
func (tmpl Template) HTML(name string, data any) template.HTML {
writer := &strings.Builder{}
err := tmpl.ExecuteTemplate(writer, name, data)
if err != nil {
fmt.Fprint(writer, err)
}
return template.HTML(writer.String())
}

View file

@ -1,3 +0,0 @@
{{define "Title"}}Home{{end}}
{{template "page.tmpl.html" .}}