Added Fiber and layouts

This commit is contained in:
Leon Mika 2025-01-27 10:19:31 +11:00
parent f8e7ea482b
commit 63b19a249a
17 changed files with 331 additions and 37 deletions

View file

@ -7,6 +7,7 @@ package dbq
type Site struct { type Site struct {
ID int64 ID int64
Name string Name string
Title string
Url string Url string
Theme string Theme string
Props []byte Props []byte

View file

@ -9,8 +9,26 @@ import (
"context" "context"
) )
const getSiteWithID = `-- name: GetSiteWithID :one
SELECT id, name, title, url, theme, props FROM site WHERE id = $1 LIMIT 1
`
func (q *Queries) GetSiteWithID(ctx context.Context, id int64) (Site, error) {
row := q.db.QueryRow(ctx, getSiteWithID, id)
var i Site
err := row.Scan(
&i.ID,
&i.Name,
&i.Title,
&i.Url,
&i.Theme,
&i.Props,
)
return i, err
}
const listSites = `-- name: ListSites :one const listSites = `-- name: ListSites :one
SELECT id, name, url, theme, props FROM site SELECT id, name, title, url, theme, props FROM site
` `
func (q *Queries) ListSites(ctx context.Context) (Site, error) { func (q *Queries) ListSites(ctx context.Context) (Site, error) {
@ -19,6 +37,7 @@ func (q *Queries) ListSites(ctx context.Context) (Site, error) {
err := row.Scan( err := row.Scan(
&i.ID, &i.ID,
&i.Name, &i.Name,
&i.Title,
&i.Url, &i.Url,
&i.Theme, &i.Theme,
&i.Props, &i.Props,
@ -29,15 +48,17 @@ func (q *Queries) ListSites(ctx context.Context) (Site, error) {
const newSite = `-- name: NewSite :one const newSite = `-- name: NewSite :one
INSERT INTO site ( INSERT INTO site (
name, name,
title,
url, url,
theme, theme,
props props
) VALUES ($1, $2, $3, $4) ) VALUES ($1, $2, $3, $4, $5)
RETURNING id RETURNING id
` `
type NewSiteParams struct { type NewSiteParams struct {
Name string Name string
Title string
Url string Url string
Theme string Theme string
Props []byte Props []byte
@ -46,6 +67,7 @@ type NewSiteParams struct {
func (q *Queries) NewSite(ctx context.Context, arg NewSiteParams) (int64, error) { func (q *Queries) NewSite(ctx context.Context, arg NewSiteParams) (int64, error) {
row := q.db.QueryRow(ctx, newSite, row := q.db.QueryRow(ctx, newSite,
arg.Name, arg.Name,
arg.Title,
arg.Url, arg.Url,
arg.Theme, arg.Theme,
arg.Props, arg.Props,

25
go.mod
View file

@ -8,14 +8,37 @@ require (
) )
require ( require (
github.com/andybalholm/brotli v1.1.1 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/gofiber/fiber/v2 v2.52.6 // indirect
github.com/gofiber/fiber/v3 v3.0.0-beta.4 // indirect
github.com/gofiber/schema v1.2.0 // indirect
github.com/gofiber/template v1.8.3 // indirect
github.com/gofiber/template/html/v2 v2.1.3 // indirect
github.com/gofiber/utils v1.1.0 // indirect
github.com/gofiber/utils/v2 v2.0.0-beta.7 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa // indirect github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/tinylib/msgp v1.2.5 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.58.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
go.uber.org/atomic v1.7.0 // indirect go.uber.org/atomic v1.7.0 // indirect
golang.org/x/crypto v0.31.0 // indirect golang.org/x/crypto v0.32.0 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/sync v0.10.0 // indirect golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/text v0.21.0 // indirect golang.org/x/text v0.21.0 // indirect
) )

50
go.sum
View file

@ -1,7 +1,27 @@
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/gofiber/fiber/v2 v2.52.6 h1:Rfp+ILPiYSvvVuIPvxrBns+HJp8qGLDnLJawAu27XVI=
github.com/gofiber/fiber/v2 v2.52.6/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
github.com/gofiber/fiber/v3 v3.0.0-beta.4 h1:KzDSavvhG7m81NIsmnu5l3ZDbVS4feCidl4xlIfu6V0=
github.com/gofiber/fiber/v3 v3.0.0-beta.4/go.mod h1:/WFUoHRkZEsGHyy2+fYcdqi109IVOFbVwxv1n1RU+kk=
github.com/gofiber/schema v1.2.0 h1:j+ZRrNnUa/0ZuWrn/6kAtAufEr4jCJ+JuTURAMxNSZg=
github.com/gofiber/schema v1.2.0/go.mod h1:YYwj01w3hVfaNjhtJzaqetymL56VW642YS3qZPhuE6c=
github.com/gofiber/template v1.8.3 h1:hzHdvMwMo/T2kouz2pPCA0zGiLCeMnoGsQZBTSYgZxc=
github.com/gofiber/template v1.8.3/go.mod h1:bs/2n0pSNPOkRa5VJ8zTIvedcI/lEYxzV3+YPXdBvq8=
github.com/gofiber/template/html/v2 v2.1.3 h1:n1LYBtmr9C0V/k/3qBblXyMxV5B0o/gpb6dFLp8ea+o=
github.com/gofiber/template/html/v2 v2.1.3/go.mod h1:U5Fxgc5KpyujU9OqKzy6Kn6Qup6Tm7zdsISR+VpnHRE=
github.com/gofiber/utils v1.1.0 h1:vdEBpn7AzIUJRhe+CiTOJdUcTg4Q9RK+pEa0KPbLdrM=
github.com/gofiber/utils v1.1.0/go.mod h1:poZpsnhBykfnY1Mc0KeEa6mSHrS3dV0+oBWyeQmb2e0=
github.com/gofiber/utils/v2 v2.0.0-beta.7 h1:NnHFrRHvhrufPABdWajcKZejz9HnCWmT/asoxRsiEbQ=
github.com/gofiber/utils/v2 v2.0.0-beta.7/go.mod h1:J/M03s+HMdZdvhAeyh76xT72IfVqBzuz/OJkrMa7cwU=
github.com/golang-migrate/migrate/v4 v4.18.1 h1:JML/k+t4tpHCpQTCAD62Nu43NUFzHY4CV3uAuvHGC+Y= github.com/golang-migrate/migrate/v4 v4.18.1 h1:JML/k+t4tpHCpQTCAD62Nu43NUFzHY4CV3uAuvHGC+Y=
github.com/golang-migrate/migrate/v4 v4.18.1/go.mod h1:HAX6m3sQgcdO81tdjn5exv20+3Kb13cmGli1hrD6hks= github.com/golang-migrate/migrate/v4 v4.18.1/go.mod h1:HAX6m3sQgcdO81tdjn5exv20+3Kb13cmGli1hrD6hks=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@ -17,16 +37,46 @@ github.com/jackc/pgx/v5 v5.7.2 h1:mLoDLV6sonKlvjIEsV56SkWNCnuNv531l94GaIzO+XI=
github.com/jackc/pgx/v5 v5.7.2/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ= github.com/jackc/pgx/v5 v5.7.2/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ=
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c h1:dAMKvw0MlJT1GshSTtih8C2gDs04w8dReiOGXrGLNoY=
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tinylib/msgp v1.2.5 h1:WeQg1whrXRFiZusidTQqzETkRpGjFjcIhW6uqWH09po=
github.com/tinylib/msgp v1.2.5/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.58.0 h1:GGB2dWxSbEprU9j0iMJHgdKYJVDyjrOwF9RE59PbRuE=
github.com/valyala/fasthttp v1.58.0/go.mod h1:SYXvHHaFp7QZHGKSHmoMipInhrI5StHrhDTYVEjK/Kw=
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

38
handlers/site.go Normal file
View file

@ -0,0 +1,38 @@
package handlers
import (
"fmt"
"github.com/gofiber/fiber/v2"
"lmika.dev/lmika/hugo-crm/services/sites"
"time"
)
type Site struct {
Site *sites.Service
}
func (s *Site) Create() fiber.Handler {
return func(c *fiber.Ctx) error {
site, err := s.Site.CreateSite(c.UserContext(), "New Site "+time.Now().Format("2006-01-02 15:04:05"))
if err != nil {
return err
}
return c.Redirect(fmt.Sprintf("/sites/%v", site.ID))
}
}
func (s *Site) Show() fiber.Handler {
return func(c *fiber.Ctx) error {
id, err := c.ParamsInt("siteId")
if err != nil {
return err
}
site, err := s.Site.GetSite(c.UserContext(), id)
return c.Render("sites/index", fiber.Map{
"site": site,
}, "layouts/main")
}
}

36
main.go
View file

@ -2,14 +2,20 @@ package main
import ( import (
"context" "context"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/template/html/v2"
"lmika.dev/lmika/hugo-crm/config" "lmika.dev/lmika/hugo-crm/config"
"lmika.dev/lmika/hugo-crm/handlers"
"lmika.dev/lmika/hugo-crm/providers/db" "lmika.dev/lmika/hugo-crm/providers/db"
"lmika.dev/lmika/hugo-crm/providers/git" "lmika.dev/lmika/hugo-crm/providers/git"
"lmika.dev/lmika/hugo-crm/providers/hugo" "lmika.dev/lmika/hugo-crm/providers/hugo"
"lmika.dev/lmika/hugo-crm/providers/themes" "lmika.dev/lmika/hugo-crm/providers/themes"
"lmika.dev/lmika/hugo-crm/services/jobs"
"lmika.dev/lmika/hugo-crm/services/sitebuilder"
"lmika.dev/lmika/hugo-crm/services/sites" "lmika.dev/lmika/hugo-crm/services/sites"
"lmika.dev/lmika/hugo-crm/templates"
"log" "log"
"time" "net/http"
) )
func main() { func main() {
@ -31,7 +37,12 @@ func main() {
gitProvider := git.New() gitProvider := git.New()
themesProvider := themes.New() themesProvider := themes.New()
siteService := sites.NewService(cfg, dbp, themesProvider, gitProvider, hugoProvider) jobService := jobs.New()
siteBuilderService := sitebuilder.New(themesProvider, gitProvider, hugoProvider)
siteService := sites.NewService(cfg, dbp, themesProvider, siteBuilderService, jobService)
siteHandlers := handlers.Site{Site: siteService}
log.Println("Connected to database") log.Println("Connected to database")
if err := dbp.Migrate(context.Background()); err != nil { if err := dbp.Migrate(context.Background()); err != nil {
@ -40,7 +51,24 @@ func main() {
log.Println("Database migrated") log.Println("Database migrated")
if _, err := siteService.CreateSite(context.Background(), "Test site "+time.Now().Format("2006-01-02T15:04:05")); err != nil { tmplEngine := html.NewFileSystem(http.FS(templates.FS), ".html")
log.Fatal(err) app := fiber.New(fiber.Config{
Views: tmplEngine,
})
app.Get("/", func(c *fiber.Ctx) error {
return c.Render("index", fiber.Map{}, "layouts/main")
})
app.Post("/sites", siteHandlers.Create())
app.Get("/sites/:siteId", siteHandlers.Show())
jobService.Start()
defer jobService.Stop()
if err := app.Listen(":3000"); err != nil {
log.Println(err)
} }
log.Println("Shutting down...")
} }

7
models/job.go Normal file
View file

@ -0,0 +1,7 @@
package models
import "context"
type Job struct {
Do func(ctx context.Context) error
}

View file

@ -9,6 +9,7 @@ import (
func (db *DB) InsertSite(ctx context.Context, site *models.Site) error { func (db *DB) InsertSite(ctx context.Context, site *models.Site) error {
id, err := db.q.NewSite(ctx, dbq.NewSiteParams{ id, err := db.q.NewSite(ctx, dbq.NewSiteParams{
Name: site.Name, Name: site.Name,
Title: site.Title,
Url: site.URL, Url: site.URL,
Theme: site.Theme, Theme: site.Theme,
Props: []byte("{}"), Props: []byte("{}"),
@ -19,3 +20,17 @@ func (db *DB) InsertSite(ctx context.Context, site *models.Site) error {
site.ID = id site.ID = id
return nil return nil
} }
func (db *DB) GetSite(ctx context.Context, id int64) (models.Site, error) {
site, err := db.q.GetSiteWithID(ctx, id)
if err != nil {
return models.Site{}, err
}
return models.Site{
ID: site.ID,
Name: site.Name,
Title: site.Title,
Theme: site.Theme,
}, nil
}

38
services/jobs/services.go Normal file
View file

@ -0,0 +1,38 @@
package jobs
import (
"context"
"lmika.dev/lmika/hugo-crm/models"
"log"
)
type Service struct {
queue chan models.Job
}
func New() *Service {
return &Service{
queue: make(chan models.Job, 50),
}
}
func (j *Service) Start() {
go func() {
for j := range j.queue {
if err := j.Do(context.Background()); err != nil {
log.Printf("job failed %v", err)
}
}
}()
}
func (j *Service) Stop() {
q := j.queue
j.queue = nil
close(q)
}
func (j *Service) Queue(ctx context.Context, job models.Job) error {
j.queue <- job
return nil
}

View file

@ -0,0 +1,57 @@
package sitebuilder
import (
"context"
"errors"
"lmika.dev/lmika/hugo-crm/models"
"lmika.dev/lmika/hugo-crm/providers/git"
"lmika.dev/lmika/hugo-crm/providers/hugo"
"lmika.dev/lmika/hugo-crm/providers/themes"
"log"
)
type Service struct {
themes *themes.Provider
git *git.Provider
hugo *hugo.Provider
}
func New(
themes *themes.Provider,
git *git.Provider,
hugo *hugo.Provider,
) *Service {
return &Service{
themes: themes,
git: git,
hugo: hugo,
}
}
func (s *Service) CreateNewSite(site models.Site) models.Job {
return models.Job{
Do: func(ctx context.Context) error {
themeMeta, ok := s.themes.Lookup(site.Theme)
if !ok {
return errors.New("theme not found")
}
// Build the site
log.Printf(" .. build")
if err := s.hugo.NewSite(ctx, site); err != nil {
return err
}
// Setup the theme
log.Printf(" .. theme")
if err := s.git.Clone(ctx, themeMeta.URL, s.hugo.SiteStagingDir(site, hugo.ThemeSiteDir)); err != nil {
return err
}
if err := s.hugo.ReconfigureSite(ctx, site); err != nil {
return err
}
return nil
},
}
}

View file

@ -6,10 +6,9 @@ import (
"lmika.dev/lmika/hugo-crm/config" "lmika.dev/lmika/hugo-crm/config"
"lmika.dev/lmika/hugo-crm/models" "lmika.dev/lmika/hugo-crm/models"
"lmika.dev/lmika/hugo-crm/providers/db" "lmika.dev/lmika/hugo-crm/providers/db"
"lmika.dev/lmika/hugo-crm/providers/git"
"lmika.dev/lmika/hugo-crm/providers/hugo"
"lmika.dev/lmika/hugo-crm/providers/themes" "lmika.dev/lmika/hugo-crm/providers/themes"
"log" "lmika.dev/lmika/hugo-crm/services/jobs"
"lmika.dev/lmika/hugo-crm/services/sitebuilder"
"strings" "strings"
"unicode" "unicode"
) )
@ -18,25 +17,30 @@ type Service struct {
cfg config.Config cfg config.Config
db *db.DB db *db.DB
themes *themes.Provider themes *themes.Provider
git *git.Provider sb *sitebuilder.Service
hugo *hugo.Provider jobs *jobs.Service
} }
func NewService( func NewService(
cfg config.Config, cfg config.Config,
db *db.DB, db *db.DB,
themes *themes.Provider, themes *themes.Provider,
git *git.Provider, sb *sitebuilder.Service,
hugo *hugo.Provider, jobs *jobs.Service,
) *Service { ) *Service {
return &Service{ return &Service{
cfg: cfg, cfg: cfg,
db: db, db: db,
git: git, themes: themes,
hugo: hugo, sb: sb,
jobs: jobs,
} }
} }
func (s *Service) GetSite(ctx context.Context, id int) (models.Site, error) {
return s.db.GetSite(ctx, int64(id))
}
func (s *Service) CreateSite(ctx context.Context, name string) (models.Site, error) { func (s *Service) CreateSite(ctx context.Context, name string) (models.Site, error) {
newSite := models.Site{ newSite := models.Site{
Name: normaliseName(name), Name: normaliseName(name),
@ -44,7 +48,7 @@ func (s *Service) CreateSite(ctx context.Context, name string) (models.Site, err
Theme: "bear", Theme: "bear",
} }
themeMeta, ok := s.themes.Lookup(newSite.Theme) _, ok := s.themes.Lookup(newSite.Theme)
if !ok { if !ok {
return models.Site{}, errors.New("theme not found") return models.Site{}, errors.New("theme not found")
} }
@ -53,23 +57,7 @@ func (s *Service) CreateSite(ctx context.Context, name string) (models.Site, err
return models.Site{}, err return models.Site{}, err
} }
// Build the site return newSite, s.jobs.Queue(ctx, s.sb.CreateNewSite(newSite))
log.Printf(" .. build")
if err := s.hugo.NewSite(ctx, newSite); err != nil {
return models.Site{}, err
}
// Setup the theme
log.Printf(" .. theme")
if err := s.git.Clone(ctx, themeMeta.URL, s.hugo.SiteStagingDir(newSite, hugo.ThemeSiteDir)); err != nil {
return models.Site{}, err
}
if err := s.hugo.ReconfigureSite(ctx, newSite); err != nil {
return models.Site{}, err
}
return newSite, nil
} }
func normaliseName(name string) string { func normaliseName(name string) string {

View file

@ -1,6 +1,9 @@
-- name: ListSites :one -- name: ListSites :one
SELECT * FROM site; SELECT * FROM site;
-- name: GetSiteWithID :one
SELECT * FROM site WHERE id = $1 LIMIT 1;
-- name: NewSite :one -- name: NewSite :one
INSERT INTO site ( INSERT INTO site (
name, name,

View file

@ -1,6 +1,7 @@
CREATE TABLE site ( CREATE TABLE site (
id BIGSERIAL NOT NULL PRIMARY KEY, id BIGSERIAL NOT NULL PRIMARY KEY,
name TEXT NOT NULL, name TEXT NOT NULL UNIQUE,
title TEXT NOT NULL,
url TEXT NOT NULL, url TEXT NOT NULL,
theme TEXT NOT NULL, theme TEXT NOT NULL,
props JSON NOT NULL props JSON NOT NULL

8
templates/fs.go Normal file
View file

@ -0,0 +1,8 @@
package templates
import "embed"
//go:embed *.html
//go:embed layouts/*.html
//go:embed sites/*.html
var FS embed.FS

5
templates/index.html Normal file
View file

@ -0,0 +1,5 @@
<h1>Thing</h1>
<form method="post" action="/sites">
<input type="submit" value="Create Site">
</form>

View file

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>TEMP</title>
</head>
<body>
{{embed}}
</body>
</html>

View file

@ -0,0 +1 @@
<h1>Site {{.site.Title}}</h1>