diff --git a/config/config.go b/config/config.go index 643334e..e7bd671 100644 --- a/config/config.go +++ b/config/config.go @@ -10,11 +10,9 @@ type Config struct { NetlifyAuthToken string `env:"NETLIFY_AUTH_TOKEN"` DataDir string `env:"DATA_DIR"` EncryptedCookieKey string `env:"ENCRYPTED_COOKIE_KEY"` - BaseURL string `env:"BASE_URL,default=http://localhost:3000/"` DataStagingDir string `env:"DATA_STAGING_DIR,default=staging"` DataScratchDir string `env:"DATA_SCRATCH_DIR,default=scratch"` - DataPreviewDir string `env:"DATA_PREVIEW_DIR,default=preview"` } func Load() (cfg Config, err error) { @@ -29,10 +27,6 @@ func (c Config) StagingDir() string { return filepath.Join(c.DataDir, c.DataStagingDir) } -func (c Config) PreviewDir() string { - return filepath.Join(c.DataDir, c.DataPreviewDir) -} - func (c Config) ScratchDir() string { return filepath.Join(c.DataDir, c.DataScratchDir) } diff --git a/gen/sqlc/dbq/bundles.sql.go b/gen/sqlc/dbq/bundles.sql.go deleted file mode 100644 index d98eb37..0000000 --- a/gen/sqlc/dbq/bundles.sql.go +++ /dev/null @@ -1,116 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.28.0 -// source: bundles.sql - -package dbq - -import ( - "context" - - "github.com/jackc/pgx/v5/pgtype" -) - -const getBundleWithID = `-- name: GetBundleWithID :one -SELECT id, site_id, name, created_at, updated_at FROM bundles WHERE id = $1 -` - -func (q *Queries) GetBundleWithID(ctx context.Context, id int64) (Bundle, error) { - row := q.db.QueryRow(ctx, getBundleWithID, id) - var i Bundle - err := row.Scan( - &i.ID, - &i.SiteID, - &i.Name, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const getSiteBundleInfo = `-- name: GetSiteBundleInfo :many -WITH page_counts AS ( - SELECT b.bundle_id, count(*) AS page_count FROM pages b WHERE b.site_id = $1 GROUP BY bundle_id -), index_pages AS ( - SELECT p.id AS index_page_id, p.bundle_id FROM pages p WHERE p.site_id = $1 AND p.role = 'index' -) -SELECT b.bundle_id, b.page_count, p.index_page_id FROM page_counts b LEFT OUTER JOIN index_pages p ON b.bundle_id = p.bundle_id -` - -type GetSiteBundleInfoRow struct { - BundleID int64 - PageCount int64 - IndexPageID pgtype.Int8 -} - -func (q *Queries) GetSiteBundleInfo(ctx context.Context, siteID int64) ([]GetSiteBundleInfoRow, error) { - rows, err := q.db.Query(ctx, getSiteBundleInfo, siteID) - if err != nil { - return nil, err - } - defer rows.Close() - var items []GetSiteBundleInfoRow - for rows.Next() { - var i GetSiteBundleInfoRow - if err := rows.Scan(&i.BundleID, &i.PageCount, &i.IndexPageID); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const insertBundle = `-- name: InsertBundle :one -INSERT INTO bundles ( - site_id, - name, - created_at, - updated_at -) VALUES ($1, $2, $3, $3) RETURNING id -` - -type InsertBundleParams struct { - SiteID int64 - Name string - CreatedAt pgtype.Timestamp -} - -func (q *Queries) InsertBundle(ctx context.Context, arg InsertBundleParams) (int64, error) { - row := q.db.QueryRow(ctx, insertBundle, arg.SiteID, arg.Name, arg.CreatedAt) - var id int64 - err := row.Scan(&id) - return id, err -} - -const listBundles = `-- name: ListBundles :many -SELECT id, site_id, name, created_at, updated_at FROM bundles WHERE site_id = $1 -` - -func (q *Queries) ListBundles(ctx context.Context, siteID int64) ([]Bundle, error) { - rows, err := q.db.Query(ctx, listBundles, siteID) - if err != nil { - return nil, err - } - defer rows.Close() - var items []Bundle - for rows.Next() { - var i Bundle - if err := rows.Scan( - &i.ID, - &i.SiteID, - &i.Name, - &i.CreatedAt, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} diff --git a/gen/sqlc/dbq/models.go b/gen/sqlc/dbq/models.go index e027f2d..d4052a2 100644 --- a/gen/sqlc/dbq/models.go +++ b/gen/sqlc/dbq/models.go @@ -11,90 +11,6 @@ import ( "github.com/jackc/pgx/v5/pgtype" ) -type PageNameProvenance string - -const ( - PageNameProvenanceUser PageNameProvenance = "user" - PageNameProvenanceTitle PageNameProvenance = "title" - PageNameProvenanceDate PageNameProvenance = "date" -) - -func (e *PageNameProvenance) Scan(src interface{}) error { - switch s := src.(type) { - case []byte: - *e = PageNameProvenance(s) - case string: - *e = PageNameProvenance(s) - default: - return fmt.Errorf("unsupported scan type for PageNameProvenance: %T", src) - } - return nil -} - -type NullPageNameProvenance struct { - PageNameProvenance PageNameProvenance - Valid bool // Valid is true if PageNameProvenance is not NULL -} - -// Scan implements the Scanner interface. -func (ns *NullPageNameProvenance) Scan(value interface{}) error { - if value == nil { - ns.PageNameProvenance, ns.Valid = "", false - return nil - } - ns.Valid = true - return ns.PageNameProvenance.Scan(value) -} - -// Value implements the driver Valuer interface. -func (ns NullPageNameProvenance) Value() (driver.Value, error) { - if !ns.Valid { - return nil, nil - } - return string(ns.PageNameProvenance), nil -} - -type PageRole string - -const ( - PageRoleIndex PageRole = "index" -) - -func (e *PageRole) Scan(src interface{}) error { - switch s := src.(type) { - case []byte: - *e = PageRole(s) - case string: - *e = PageRole(s) - default: - return fmt.Errorf("unsupported scan type for PageRole: %T", src) - } - return nil -} - -type NullPageRole struct { - PageRole PageRole - Valid bool // Valid is true if PageRole is not NULL -} - -// Scan implements the Scanner interface. -func (ns *NullPageRole) Scan(value interface{}) error { - if value == nil { - ns.PageRole, ns.Valid = "", false - return nil - } - ns.Valid = true - return ns.PageRole.Scan(value) -} - -// Value implements the driver Valuer interface. -func (ns NullPageRole) Value() (driver.Value, error) { - if !ns.Valid { - return nil, nil - } - return string(ns.PageRole), nil -} - type PostState string const ( @@ -219,48 +135,15 @@ func (ns NullTargetType) Value() (driver.Value, error) { return string(ns.TargetType), nil } -type Bundle struct { +type Post struct { ID int64 SiteID int64 - Name string + Title pgtype.Text + Body string + State PostState + Props []byte + PostDate pgtype.Timestamptz CreatedAt pgtype.Timestamp - UpdatedAt pgtype.Timestamp -} - -type Page struct { - ID int64 - SiteID int64 - BundleID int64 - Name string - NameProvenance PageNameProvenance - Title pgtype.Text - PostTypeID pgtype.Int8 - Body string - State PostState - Props []byte - Role NullPageRole - PublishDate pgtype.Timestamptz - CreatedAt pgtype.Timestamp - UpdatedAt pgtype.Timestamp -} - -type Post struct { - ID int64 - SiteID int64 - Title pgtype.Text - PostTypeID pgtype.Int8 - Body string - State PostState - Props []byte - PublishDate pgtype.Timestamptz - CreatedAt pgtype.Timestamp - UpdatedAt pgtype.Timestamp -} - -type PostType struct { - ID int64 - SiteID int64 - LayoutName string } type PublishTarget struct { diff --git a/gen/sqlc/dbq/pages.sql.go b/gen/sqlc/dbq/pages.sql.go deleted file mode 100644 index 89c3376..0000000 --- a/gen/sqlc/dbq/pages.sql.go +++ /dev/null @@ -1,242 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.28.0 -// source: pages.sql - -package dbq - -import ( - "context" - - "github.com/jackc/pgx/v5/pgtype" -) - -const deletePageWithID = `-- name: DeletePageWithID :exec -DELETE FROM pages WHERE id = $1 -` - -func (q *Queries) DeletePageWithID(ctx context.Context, id int64) error { - _, err := q.db.Exec(ctx, deletePageWithID, id) - return err -} - -const getPageWithID = `-- name: GetPageWithID :one -SELECT id, site_id, bundle_id, name, name_provenance, title, post_type_id, body, state, props, role, publish_date, created_at, updated_at FROM pages WHERE id = $1 -` - -func (q *Queries) GetPageWithID(ctx context.Context, id int64) (Page, error) { - row := q.db.QueryRow(ctx, getPageWithID, id) - var i Page - err := row.Scan( - &i.ID, - &i.SiteID, - &i.BundleID, - &i.Name, - &i.NameProvenance, - &i.Title, - &i.PostTypeID, - &i.Body, - &i.State, - &i.Props, - &i.Role, - &i.PublishDate, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const insertPage = `-- name: InsertPage :one -INSERT INTO pages ( - site_id, - bundle_id, - name, - name_provenance, - title, - post_type_id, - body, - state, - props, - role, - publish_date, - created_at, - updated_at -) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $12) -RETURNING id -` - -type InsertPageParams struct { - SiteID int64 - BundleID int64 - Name string - NameProvenance PageNameProvenance - Title pgtype.Text - PostTypeID pgtype.Int8 - Body string - State PostState - Props []byte - Role NullPageRole - PublishDate pgtype.Timestamptz - CreatedAt pgtype.Timestamp -} - -func (q *Queries) InsertPage(ctx context.Context, arg InsertPageParams) (int64, error) { - row := q.db.QueryRow(ctx, insertPage, - arg.SiteID, - arg.BundleID, - arg.Name, - arg.NameProvenance, - arg.Title, - arg.PostTypeID, - arg.Body, - arg.State, - arg.Props, - arg.Role, - arg.PublishDate, - arg.CreatedAt, - ) - var id int64 - err := row.Scan(&id) - return id, err -} - -const listPages = `-- name: ListPages :many -SELECT id, site_id, bundle_id, name, name_provenance, title, post_type_id, body, state, props, role, publish_date, created_at, updated_at FROM pages WHERE site_id = $1 ORDER BY name ASC LIMIT 25 -` - -func (q *Queries) ListPages(ctx context.Context, siteID int64) ([]Page, error) { - rows, err := q.db.Query(ctx, listPages, siteID) - if err != nil { - return nil, err - } - defer rows.Close() - var items []Page - for rows.Next() { - var i Page - if err := rows.Scan( - &i.ID, - &i.SiteID, - &i.BundleID, - &i.Name, - &i.NameProvenance, - &i.Title, - &i.PostTypeID, - &i.Body, - &i.State, - &i.Props, - &i.Role, - &i.PublishDate, - &i.CreatedAt, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const listPublishablePages = `-- name: ListPublishablePages :many -SELECT id, site_id, bundle_id, name, name_provenance, title, post_type_id, body, state, props, role, publish_date, created_at, updated_at -FROM pages -WHERE id > $1 AND site_id = $2 AND state = 'published' -ORDER BY id LIMIT 100 -` - -type ListPublishablePagesParams struct { - ID int64 - SiteID int64 -} - -func (q *Queries) ListPublishablePages(ctx context.Context, arg ListPublishablePagesParams) ([]Page, error) { - rows, err := q.db.Query(ctx, listPublishablePages, arg.ID, arg.SiteID) - if err != nil { - return nil, err - } - defer rows.Close() - var items []Page - for rows.Next() { - var i Page - if err := rows.Scan( - &i.ID, - &i.SiteID, - &i.BundleID, - &i.Name, - &i.NameProvenance, - &i.Title, - &i.PostTypeID, - &i.Body, - &i.State, - &i.Props, - &i.Role, - &i.PublishDate, - &i.CreatedAt, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const updatePage = `-- name: UpdatePage :exec -UPDATE pages SET - site_id = $2, - bundle_id = $3, - name = $4, - name_provenance = $5, - title = $6, - post_type_id = $7, - role = $8, - body = $9, - state = $10, - props = $11, - publish_date = $12, - created_at = $13, - updated_at = $14 -WHERE id = $1 -` - -type UpdatePageParams struct { - ID int64 - SiteID int64 - BundleID int64 - Name string - NameProvenance PageNameProvenance - Title pgtype.Text - PostTypeID pgtype.Int8 - Role NullPageRole - Body string - State PostState - Props []byte - PublishDate pgtype.Timestamptz - CreatedAt pgtype.Timestamp - UpdatedAt pgtype.Timestamp -} - -func (q *Queries) UpdatePage(ctx context.Context, arg UpdatePageParams) error { - _, err := q.db.Exec(ctx, updatePage, - arg.ID, - arg.SiteID, - arg.BundleID, - arg.Name, - arg.NameProvenance, - arg.Title, - arg.PostTypeID, - arg.Role, - arg.Body, - arg.State, - arg.Props, - arg.PublishDate, - arg.CreatedAt, - arg.UpdatedAt, - ) - return err -} diff --git a/gen/sqlc/dbq/posts.sql.go b/gen/sqlc/dbq/posts.sql.go index 0500cce..6724a64 100644 --- a/gen/sqlc/dbq/posts.sql.go +++ b/gen/sqlc/dbq/posts.sql.go @@ -21,7 +21,7 @@ func (q *Queries) DeletePost(ctx context.Context, id int64) error { } const getPostWithID = `-- name: GetPostWithID :one -SELECT id, site_id, title, post_type_id, body, state, props, publish_date, created_at, updated_at FROM posts WHERE id = $1 LIMIT 1 +SELECT id, site_id, title, body, state, props, post_date, created_at FROM posts WHERE id = $1 LIMIT 1 ` func (q *Queries) GetPostWithID(ctx context.Context, id int64) (Post, error) { @@ -31,13 +31,11 @@ func (q *Queries) GetPostWithID(ctx context.Context, id int64) (Post, error) { &i.ID, &i.SiteID, &i.Title, - &i.PostTypeID, &i.Body, &i.State, &i.Props, - &i.PublishDate, + &i.PostDate, &i.CreatedAt, - &i.UpdatedAt, ) return i, err } @@ -49,22 +47,20 @@ INSERT INTO posts ( body, state, props, - publish_date, - created_at, - updated_at -) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) + post_date, + created_at +) VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING id ` type InsertPostParams struct { - SiteID int64 - Title pgtype.Text - Body string - State PostState - Props []byte - PublishDate pgtype.Timestamptz - CreatedAt pgtype.Timestamp - UpdatedAt pgtype.Timestamp + SiteID int64 + Title pgtype.Text + Body string + State PostState + Props []byte + PostDate pgtype.Timestamptz + CreatedAt pgtype.Timestamp } func (q *Queries) InsertPost(ctx context.Context, arg InsertPostParams) (int64, error) { @@ -74,9 +70,8 @@ func (q *Queries) InsertPost(ctx context.Context, arg InsertPostParams) (int64, arg.Body, arg.State, arg.Props, - arg.PublishDate, + arg.PostDate, arg.CreatedAt, - arg.UpdatedAt, ) var id int64 err := row.Scan(&id) @@ -84,7 +79,7 @@ func (q *Queries) InsertPost(ctx context.Context, arg InsertPostParams) (int64, } const listPosts = `-- name: ListPosts :many -SELECT id, site_id, title, post_type_id, body, state, props, publish_date, created_at, updated_at FROM posts WHERE site_id = $1 ORDER BY publish_date DESC LIMIT 25 +SELECT id, site_id, title, body, state, props, post_date, created_at FROM posts WHERE site_id = $1 ORDER BY post_date DESC LIMIT 25 ` func (q *Queries) ListPosts(ctx context.Context, siteID int64) ([]Post, error) { @@ -100,13 +95,11 @@ func (q *Queries) ListPosts(ctx context.Context, siteID int64) ([]Post, error) { &i.ID, &i.SiteID, &i.Title, - &i.PostTypeID, &i.Body, &i.State, &i.Props, - &i.PublishDate, + &i.PostDate, &i.CreatedAt, - &i.UpdatedAt, ); err != nil { return nil, err } @@ -119,20 +112,20 @@ func (q *Queries) ListPosts(ctx context.Context, siteID int64) ([]Post, error) { } const listPublishablePosts = `-- name: ListPublishablePosts :many -SELECT id, site_id, title, post_type_id, body, state, props, publish_date, created_at, updated_at +SELECT id, site_id, title, body, state, props, post_date, created_at FROM posts -WHERE id > $1 AND site_id = $2 AND state = 'published' AND publish_date <= $3 +WHERE id > $1 AND site_id = $2 AND state = 'published' AND post_date <= $3 ORDER BY id LIMIT 100 ` type ListPublishablePostsParams struct { - ID int64 - SiteID int64 - PublishDate pgtype.Timestamptz + ID int64 + SiteID int64 + PostDate pgtype.Timestamptz } func (q *Queries) ListPublishablePosts(ctx context.Context, arg ListPublishablePostsParams) ([]Post, error) { - rows, err := q.db.Query(ctx, listPublishablePosts, arg.ID, arg.SiteID, arg.PublishDate) + rows, err := q.db.Query(ctx, listPublishablePosts, arg.ID, arg.SiteID, arg.PostDate) if err != nil { return nil, err } @@ -144,13 +137,11 @@ func (q *Queries) ListPublishablePosts(ctx context.Context, arg ListPublishableP &i.ID, &i.SiteID, &i.Title, - &i.PostTypeID, &i.Body, &i.State, &i.Props, - &i.PublishDate, + &i.PostDate, &i.CreatedAt, - &i.UpdatedAt, ); err != nil { return nil, err } @@ -169,20 +160,19 @@ UPDATE posts SET body = $4, state = $5, props = $6, - publish_date = $7, - updated_at = $8 + post_date = $7 + -- updated_at = $7 WHERE id = $1 ` type UpdatePostParams struct { - ID int64 - SiteID int64 - Title pgtype.Text - Body string - State PostState - Props []byte - PublishDate pgtype.Timestamptz - UpdatedAt pgtype.Timestamp + ID int64 + SiteID int64 + Title pgtype.Text + Body string + State PostState + Props []byte + PostDate pgtype.Timestamptz } func (q *Queries) UpdatePost(ctx context.Context, arg UpdatePostParams) error { @@ -193,8 +183,7 @@ func (q *Queries) UpdatePost(ctx context.Context, arg UpdatePostParams) error { arg.Body, arg.State, arg.Props, - arg.PublishDate, - arg.UpdatedAt, + arg.PostDate, ) return err } diff --git a/go.mod b/go.mod index 9e10103..d032137 100644 --- a/go.mod +++ b/go.mod @@ -48,5 +48,5 @@ require ( golang.org/x/sys v0.29.0 // indirect golang.org/x/text v0.21.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - lmika.dev/pkg/modash v0.0.0-20250216001243-c73e50a0913d // indirect + lmika.dev/pkg/modash v0.0.0-20250201221851-97d4b9b4a1ac // indirect ) diff --git a/go.sum b/go.sum index fcd8439..cc84584 100644 --- a/go.sum +++ b/go.sum @@ -99,5 +99,3 @@ lmika.dev/pkg/modash v0.0.0-20250127022145-5dcbffe270a1 h1:Seqp9vlIw3uJBL0V/eWIM lmika.dev/pkg/modash v0.0.0-20250127022145-5dcbffe270a1/go.mod h1:8NDl/yR1eCCEhip9FJlVuMNXIeaztQ0Ks/tizExFcTI= lmika.dev/pkg/modash v0.0.0-20250201221851-97d4b9b4a1ac h1:i/C+DYDCVQTQHtv7w1O8m20RMez6YS9fUIlhAGjTZhU= lmika.dev/pkg/modash v0.0.0-20250201221851-97d4b9b4a1ac/go.mod h1:8NDl/yR1eCCEhip9FJlVuMNXIeaztQ0Ks/tizExFcTI= -lmika.dev/pkg/modash v0.0.0-20250216001243-c73e50a0913d h1:x5aMBOkCr4cjJyFmq+qJVUsByfffD9k56HYDx1yZSR4= -lmika.dev/pkg/modash v0.0.0-20250216001243-c73e50a0913d/go.mod h1:8NDl/yR1eCCEhip9FJlVuMNXIeaztQ0Ks/tizExFcTI= diff --git a/handlers/page.go b/handlers/page.go deleted file mode 100644 index e9a3f7f..0000000 --- a/handlers/page.go +++ /dev/null @@ -1,123 +0,0 @@ -package handlers - -import ( - "errors" - "fmt" - "github.com/gofiber/fiber/v3" - "lmika.dev/lmika/hugo-cms/models" - "lmika.dev/lmika/hugo-cms/services/pages" - "net/http" -) - -type Pages struct { - Svc *pages.Service -} - -func (h *Pages) Index(c fiber.Ctx) error { - site := GetSite(c) - - pages, err := h.Svc.ListPagesOfSite(c.Context(), site) - if err != nil { - return err - } - - return c.Render("pages/index", fiber.Map{ - "pages": pages, - }, "layouts/site") -} - -func (h *Pages) New(c fiber.Ctx) error { - return c.Render("pages/edit", fiber.Map{ - "page": models.Page{}, - }, "layouts/site") -} - -func (h *Pages) Create(c fiber.Ctx) error { - site := GetSite(c) - - var req struct { - Title string `json:"title" form:"title"` - Body string `json:"body" form:"body"` - } - if err := c.Bind().Body(&req); err != nil { - return err - } - - _, err := h.Svc.Create(c.Context(), site, pages.NewPost{ - Title: req.Title, - Body: req.Body, - }) - if err != nil { - return err - } - - return c.Redirect().To(fmt.Sprintf("/sites/%v/pages", site.ID)) -} - -func (h *Pages) Edit(c fiber.Ctx) error { - site := GetSite(c) - - pageID := fiber.Params[int](c, "pageId") - if pageID == 0 { - return errors.New("pageId is required") - } - - page, err := h.Svc.GetPage(c.Context(), pageID) - if err != nil { - return err - } else if page.SiteID != site.ID { - return fmt.Errorf("page id %v not equal to site id %v", pageID, site.ID) - } - - return c.Render("pages/edit", fiber.Map{ - "page": page, - }, "layouts/site") -} - -func (h *Pages) Update(c fiber.Ctx) error { - site := GetSite(c) - - pageID := fiber.Params[int](c, "pageId") - if pageID == 0 { - return errors.New("pageId is required") - } - - var req struct { - Title string `json:"title" form:"title"` - Body string `json:"body" form:"body"` - } - if err := c.Bind().Body(&req); err != nil { - return err - } - - if _, err := h.Svc.Update(c.Context(), site, int64(pageID), pages.NewPost{ - Title: req.Title, - Body: req.Body, - }); err != nil { - return err - } - - return c.Redirect().To(fmt.Sprintf("/sites/%v/pages", site.ID)) -} - -func (h *Pages) Delete(c fiber.Ctx) error { - site := GetSite(c) - - pageID := fiber.Params[int](c, "pageId") - if pageID == 0 { - return errors.New("pageID is required") - } - - if err := h.Svc.DeletePage(c.Context(), site, pageID); err != nil { - return err - } - - return Select(c, - HTMX(func(c fiber.Ctx) error { - return c.Status(http.StatusOK).SendString("") - }), - Otherwise(func(c fiber.Ctx) error { - return c.Redirect().To(fmt.Sprintf("/sites/%v/pages", site.ID)) - }), - ) -} diff --git a/main.go b/main.go index 6d4da92..d267d7e 100644 --- a/main.go +++ b/main.go @@ -21,7 +21,6 @@ import ( "lmika.dev/lmika/hugo-cms/providers/netlify" "lmika.dev/lmika/hugo-cms/providers/themes" "lmika.dev/lmika/hugo-cms/services/jobs" - "lmika.dev/lmika/hugo-cms/services/pages" "lmika.dev/lmika/hugo-cms/services/posts" "lmika.dev/lmika/hugo-cms/services/sitebuilder" "lmika.dev/lmika/hugo-cms/services/sites" @@ -72,7 +71,7 @@ func main() { return } - hugoProvider, err := hugo.New(cfg.StagingDir(), cfg.BaseURL, cfg.PreviewDir(), cfg.ScratchDir()) + hugoProvider, err := hugo.New(cfg.StagingDir(), cfg.ScratchDir()) if err != nil { log.Fatal(err) } @@ -86,12 +85,10 @@ func main() { siteService := sites.NewService(cfg, dbp, themesProvider, siteBuilderService, jobService) postService := posts.New(dbp, siteBuilderService, jobService) - pageService := pages.New(dbp, siteBuilderService, jobService) indexHandlers := handlers.IndexHandler{} siteHandlers := handlers.Site{Site: siteService, Bus: bus} postHandlers := handlers.Post{Post: postService} - pageHandlers := handlers.Pages{Svc: pageService} authHandlers := handlers.AuthHandler{UserService: userService} tmplEngine := html.NewFileSystem(http.FS(templates.FS), ".html") @@ -129,10 +126,6 @@ func main() { app.Post("/sites", siteHandlers.Create) app.Get("/sites/:siteId", siteHandlers.Show) - app.Use("/preview", static.New(cfg.PreviewDir(), static.Config{ - Browse: true, - })) - sr := app.Group("/sites/:siteId") sr.Use(siteHandlers.WithSite()) sr.Post("/rebuild", siteHandlers.Rebuild) @@ -144,13 +137,6 @@ func main() { sr.Post("/posts/:postId", postHandlers.Update) sr.Delete("/posts/:postId", postHandlers.Delete) - sr.Get("/pages", pageHandlers.Index) - sr.Get("/pages/new", pageHandlers.New) - sr.Post("/pages", pageHandlers.Create) - sr.Get("/pages/:pageId", pageHandlers.Edit) - sr.Post("/pages/:pageId", pageHandlers.Update) - sr.Delete("/pages/:pageId", pageHandlers.Delete) - sr.Get("/settings", siteHandlers.Settings) sr.Post("/settings", siteHandlers.SaveSettings) sr.Get("/sse", siteHandlers.SSE) diff --git a/models/page.go b/models/page.go deleted file mode 100644 index d0bb592..0000000 --- a/models/page.go +++ /dev/null @@ -1,54 +0,0 @@ -package models - -import "time" - -const ( - RootBundleName = "_root" -) - -// NameProvenance encodes where the name came from, whether it was set by the user or autogenerated in some way -type NameProvenance int - -const ( - UserNameProvenance NameProvenance = iota - TitleNameProvenance NameProvenance = iota - DateNameProvenance NameProvenance = iota -) - -type PageRole int - -const ( - NormalPageRole PageRole = iota - IndexPageRole -) - -type Bundle struct { - ID int64 - SiteID int64 - Name string - CreatedAt time.Time - UpdatedAt time.Time -} - -type Page struct { - ID int64 - SiteID int64 - BundleID int64 - Name string - NameProvenance NameProvenance - Title string - Role PageRole - Body string - State PostState - PageTypeID int64 - Props []byte - PublishDate time.Time - CreatedAt time.Time - UpdatedAt time.Time -} - -type BundleInfo struct { - BundleID int64 - PageCount int - IndexPageID int64 -} diff --git a/models/posts.go b/models/posts.go index 83bb663..a95fb36 100644 --- a/models/posts.go +++ b/models/posts.go @@ -10,13 +10,13 @@ const ( ) type Post struct { - ID int64 - SiteID int64 - OwnerID int64 - Title string - Body string - State PostState - PublishDate time.Time - CreatedAt time.Time - UpdatedAt time.Time + ID int64 + SiteID int64 + OwnerID int64 + Title string + Body string + State PostState + PostDate time.Time + CreatedAt time.Time + UpdatedAt time.Time } diff --git a/models/theme.go b/models/theme.go index 69e9d81..3b07e0e 100644 --- a/models/theme.go +++ b/models/theme.go @@ -8,9 +8,6 @@ type ThemeMeta struct { // Indicates that this theme prefers posts have titles. PreferTitle bool - // Indicates that the theme doesn't automatically put titles on pages - AddTitleToPages bool - - // Page bundle for "blog" posts - BlogPostBundle string `json:"post_dir"` + // Content directory for "blog" posts + PostDir string `json:"post_dir"` } diff --git a/providers/db/bundles.go b/providers/db/bundles.go deleted file mode 100644 index 730a29e..0000000 --- a/providers/db/bundles.go +++ /dev/null @@ -1,65 +0,0 @@ -package db - -import ( - "context" - "github.com/jackc/pgx/v5/pgtype" - "lmika.dev/lmika/hugo-cms/gen/sqlc/dbq" - "lmika.dev/lmika/hugo-cms/models" - "lmika.dev/pkg/modash/momap" - "lmika.dev/pkg/modash/moslice" -) - -func (db *DB) InsertBundle(ctx context.Context, bundle *models.Bundle) error { - id, err := db.q.InsertBundle(ctx, dbq.InsertBundleParams{ - SiteID: bundle.SiteID, - Name: bundle.Name, - CreatedAt: pgtype.Timestamp{Time: bundle.CreatedAt, Valid: true}, - }) - if err != nil { - return err - } - bundle.ID = id - return nil -} - -func (db *DB) ListBundles(ctx context.Context, siteID int64) ([]models.Bundle, error) { - res, err := db.q.ListBundles(ctx, siteID) - if err != nil { - return nil, err - } - - return moslice.Map(res, dbBundleToBundle), nil -} - -func (db *DB) GetBundleWithID(ctx context.Context, id int64) (models.Bundle, error) { - res, err := db.q.GetBundleWithID(ctx, id) - if err != nil { - return models.Bundle{}, err - } - - return dbBundleToBundle(res), nil -} - -func (db *DB) GetSiteBundleInfo(ctx context.Context, siteID int64) (map[int64]models.BundleInfo, error) { - res, err := db.q.GetSiteBundleInfo(ctx, siteID) - if err != nil { - return nil, err - } - return momap.FromSlice(res, func(bi dbq.GetSiteBundleInfoRow) (int64, models.BundleInfo) { - return bi.BundleID, models.BundleInfo{ - BundleID: bi.BundleID, - PageCount: int(bi.PageCount), - IndexPageID: bi.IndexPageID.Int64, - } - }), nil -} - -func dbBundleToBundle(b dbq.Bundle) models.Bundle { - return models.Bundle{ - ID: b.ID, - SiteID: b.SiteID, - Name: b.Name, - CreatedAt: b.CreatedAt.Time, - UpdatedAt: b.UpdatedAt.Time, - } -} diff --git a/providers/db/page.go b/providers/db/page.go deleted file mode 100644 index 788e157..0000000 --- a/providers/db/page.go +++ /dev/null @@ -1,114 +0,0 @@ -package db - -import ( - "context" - "github.com/jackc/pgx/v5/pgtype" - "lmika.dev/lmika/hugo-cms/gen/sqlc/dbq" - "lmika.dev/lmika/hugo-cms/models" - "lmika.dev/pkg/modash/momap" - "lmika.dev/pkg/modash/moslice" -) - -var nameProvenanceToDBNameProvenance = map[models.NameProvenance]dbq.PageNameProvenance{ - models.UserNameProvenance: dbq.PageNameProvenanceUser, - models.TitleNameProvenance: dbq.PageNameProvenanceTitle, - models.DateNameProvenance: dbq.PageNameProvenanceDate, -} -var dbNameProvenanceToNameProvenance = momap.ReverseMap(nameProvenanceToDBNameProvenance) - -var pageRoleToDBPageRole = map[models.PageRole]dbq.NullPageRole{ - models.NormalPageRole: {}, - models.IndexPageRole: {PageRole: dbq.PageRoleIndex, Valid: true}, -} -var dbPageRoleToPageRole = momap.ReverseMap(pageRoleToDBPageRole) - -func (db *DB) InsertPage(ctx context.Context, page *models.Page) error { - - id, err := db.q.InsertPage(ctx, dbq.InsertPageParams{ - SiteID: page.SiteID, - BundleID: page.BundleID, - Name: page.Name, - NameProvenance: nameProvenanceToDBNameProvenance[page.NameProvenance], - Role: pageRoleToDBPageRole[page.Role], - Title: pgtype.Text{String: page.Title, Valid: page.Title != ""}, - Body: page.Body, - State: dbq.PostState(page.State), - Props: []byte(`{}`), - PublishDate: pgtype.Timestamptz{Time: page.PublishDate, Valid: !page.PublishDate.IsZero()}, - CreatedAt: pgtype.Timestamp{Time: page.CreatedAt, Valid: true}, - }) - if err != nil { - return err - } - page.ID = id - return nil -} - -func (db *DB) UpdatePage(ctx context.Context, page *models.Page) error { - return db.q.UpdatePage(ctx, dbq.UpdatePageParams{ - ID: page.ID, - SiteID: page.SiteID, - BundleID: page.BundleID, - Name: page.Name, - Role: pageRoleToDBPageRole[page.Role], - NameProvenance: nameProvenanceToDBNameProvenance[page.NameProvenance], - Title: pgtype.Text{String: page.Title, Valid: page.Title != ""}, - Body: page.Body, - State: dbq.PostState(page.State), - Props: []byte(`{}`), - PublishDate: pgtype.Timestamptz{Time: page.PublishDate, Valid: true}, - CreatedAt: pgtype.Timestamp{Time: page.CreatedAt, Valid: true}, - UpdatedAt: pgtype.Timestamp{Time: page.UpdatedAt, Valid: true}, - }) -} - -func (db *DB) ListPagesOfSite(ctx context.Context, siteID int64) ([]models.Page, error) { - res, err := db.q.ListPages(ctx, siteID) - if err != nil { - return nil, err - } - - return moslice.Map(res, dbPageToPage), nil -} - -func (db *DB) ListPublishablePages(ctx context.Context, fromID, siteID int64) ([]models.Page, error) { - res, err := db.q.ListPublishablePages(ctx, dbq.ListPublishablePagesParams{ - ID: fromID, - SiteID: siteID, - }) - if err != nil { - return nil, err - } - - return moslice.Map(res, dbPageToPage), nil -} - -func (db *DB) GetPage(ctx context.Context, postID int64) (models.Page, error) { - res, err := db.q.GetPageWithID(ctx, postID) - if err != nil { - return models.Page{}, err - } - - return dbPageToPage(res), nil -} - -func (db *DB) DeletePage(ctx context.Context, pageID int64) error { - - return db.q.DeletePageWithID(ctx, pageID) -} - -func dbPageToPage(p dbq.Page) models.Page { - return models.Page{ - ID: p.ID, - SiteID: p.SiteID, - BundleID: p.BundleID, - Name: p.Name, - Role: dbPageRoleToPageRole[p.Role], - NameProvenance: dbNameProvenanceToNameProvenance[p.NameProvenance], - Title: p.Title.String, - Body: p.Body, - State: models.PostState(p.State), - PublishDate: p.PublishDate.Time, - CreatedAt: p.CreatedAt.Time, - } -} diff --git a/providers/db/posts.go b/providers/db/posts.go index 45b9924..a647453 100644 --- a/providers/db/posts.go +++ b/providers/db/posts.go @@ -33,9 +33,9 @@ func (db *DB) DeletePost(ctx context.Context, postID int64) error { func (db *DB) ListPublishablePosts(ctx context.Context, fromID, siteID int64, now time.Time) ([]models.Post, error) { res, err := db.q.ListPublishablePosts(ctx, dbq.ListPublishablePostsParams{ - ID: fromID, - SiteID: siteID, - PublishDate: pgtype.Timestamptz{Time: now, Valid: true}, + ID: fromID, + SiteID: siteID, + PostDate: pgtype.Timestamptz{Time: now, Valid: true}, }) if err != nil { return nil, err @@ -46,14 +46,13 @@ func (db *DB) ListPublishablePosts(ctx context.Context, fromID, siteID int64, no func (db *DB) InsertPost(ctx context.Context, p *models.Post) error { res, err := db.q.InsertPost(ctx, dbq.InsertPostParams{ - SiteID: p.SiteID, - Title: pgtype.Text{String: p.Title, Valid: p.Title != ""}, - Body: p.Body, - State: dbq.PostState(p.State), - Props: []byte(`{}`), - PublishDate: pgtype.Timestamptz{Time: p.PublishDate, Valid: !p.PublishDate.IsZero()}, - CreatedAt: pgtype.Timestamp{Time: p.CreatedAt, Valid: !p.CreatedAt.IsZero()}, - UpdatedAt: pgtype.Timestamp{Time: p.UpdatedAt, Valid: !p.UpdatedAt.IsZero()}, + SiteID: p.SiteID, + Title: pgtype.Text{String: p.Title, Valid: p.Title != ""}, + Body: p.Body, + State: dbq.PostState(p.State), + Props: []byte(`{}`), + PostDate: pgtype.Timestamptz{Time: p.PostDate, Valid: !p.PostDate.IsZero()}, + CreatedAt: pgtype.Timestamp{Time: p.CreatedAt, Valid: !p.CreatedAt.IsZero()}, }) if err != nil { return err @@ -65,25 +64,25 @@ func (db *DB) InsertPost(ctx context.Context, p *models.Post) error { func (db *DB) UpdatePost(ctx context.Context, p *models.Post) error { return db.q.UpdatePost(ctx, dbq.UpdatePostParams{ - ID: p.ID, - SiteID: p.SiteID, - Title: pgtype.Text{String: p.Title, Valid: p.Title != ""}, - Body: p.Body, - State: dbq.PostState(p.State), - Props: []byte(`{}`), - PublishDate: pgtype.Timestamptz{Time: p.PublishDate, Valid: !p.PublishDate.IsZero()}, - UpdatedAt: pgtype.Timestamp{Time: p.UpdatedAt, Valid: !p.UpdatedAt.IsZero()}, + ID: p.ID, + SiteID: p.SiteID, + Title: pgtype.Text{String: p.Title, Valid: p.Title != ""}, + Body: p.Body, + State: dbq.PostState(p.State), + Props: []byte(`{}`), + PostDate: pgtype.Timestamptz{Time: p.PostDate, Valid: !p.PostDate.IsZero()}, + //CreatedAt: pgtype.Timestamp{Time: p.CreatedAt, Valid: !p.CreatedAt.IsZero()}, }) } func dbPostToPost(p dbq.Post) models.Post { return models.Post{ - ID: p.ID, - SiteID: p.SiteID, - Title: p.Title.String, - Body: p.Body, - State: models.PostState(p.State), - PublishDate: p.PublishDate.Time, - CreatedAt: p.CreatedAt.Time, + ID: p.ID, + SiteID: p.SiteID, + Title: p.Title.String, + Body: p.Body, + State: models.PostState(p.State), + PostDate: p.PostDate.Time, + CreatedAt: p.CreatedAt.Time, } } diff --git a/providers/hugo/config.go b/providers/hugo/config.go index e28a736..e888090 100644 --- a/providers/hugo/config.go +++ b/providers/hugo/config.go @@ -5,7 +5,6 @@ type hugoConfig struct { LanguageCode string `yaml:"languageCode"` Title string `yaml:"title"` Theme string `yaml:"theme"` - CanonifyURLs bool `yaml:"canonifyURLs,omitempty"` Markup hugoConfigMarkup `yaml:"markup"` } diff --git a/providers/hugo/provider.go b/providers/hugo/provider.go index 2860c3a..7fc516a 100644 --- a/providers/hugo/provider.go +++ b/providers/hugo/provider.go @@ -5,30 +5,20 @@ import ( "gopkg.in/yaml.v3" "lmika.dev/lmika/hugo-cms/models" "log" - "net/url" "os" "os/exec" "path/filepath" ) type Provider struct { - stagingDir string - previewDir string - previewBaseURL *url.URL - scratchDir string + stagingDir string + scratchDir string } -func New(stagingDir, previewBaseURL, previewDir, scratchDir string) (*Provider, error) { - baseURL, err := url.Parse(previewBaseURL) - if err != nil { - return nil, err - } - +func New(stagingDir, scratchDir string) (*Provider, error) { return &Provider{ - stagingDir: stagingDir, - previewBaseURL: baseURL, - previewDir: previewDir, - scratchDir: scratchDir, + stagingDir: stagingDir, + scratchDir: scratchDir, }, nil } @@ -57,53 +47,38 @@ func (p *Provider) NewSite(ctx context.Context, site models.Site) error { return nil } -func (p *Provider) PreviewSite(ctx context.Context, site models.Site) (outDir string, err error) { - previewURL, err := p.previewBaseURL.Parse("preview/" + site.Name) +func (p *Provider) PublishSite(ctx context.Context, site models.Site, target models.PublishTarget) (outDir string, clean func(), err error) { + if err := os.MkdirAll(p.scratchDir, 0755); err != nil { + return "", nil, err + } + + outDir, err = os.MkdirTemp(p.scratchDir, site.Name+"-*") if err != nil { - return "", err + return "", nil, err } - previewTarget := models.PublishTarget{ - URL: previewURL.String(), + clean = func() { + os.RemoveAll(outDir) } - return p.publishSiteAt(ctx, p.previewDir, site, previewTarget, "hugoPreview.yaml") -} - -func (p *Provider) PublishSite(ctx context.Context, site models.Site, target models.PublishTarget) (outDir string, err error) { - return p.publishSiteAt(ctx, p.scratchDir, site, target, "hugo.yaml") -} - -func (p *Provider) publishSiteAt(ctx context.Context, dir string, site models.Site, target models.PublishTarget, configFile string) (outDir string, err error) { - baseSiteDir, err := filepath.Abs(p.SiteStagingDir(site, BaseSiteDir)) + outDir, err = filepath.Abs(outDir) if err != nil { - return "", err - } - - outDir, err = filepath.Abs(filepath.Join(dir, site.Name)) - if err != nil { - return "", err - } - - if err := os.MkdirAll(outDir, 0755); err != nil { - return "", err + return "", nil, err } cmd := exec.CommandContext(ctx, "hugo", - "--source", baseSiteDir, + "--source", p.SiteStagingDir(site, BaseSiteDir), "--destination", outDir, - "--quiet", - "--config", filepath.Join(baseSiteDir, configFile), "--baseURL", target.URL) cmd.Stderr = os.Stderr cmd.Stdout = os.Stdout if err := cmd.Run(); err != nil { - return "", err + return "", clean, err } - return outDir, nil + return outDir, clean, nil } -func (p *Provider) ReconfigureSite(ctx context.Context, isPreviewConfig bool, configBase string, site models.Site) error { +func (p *Provider) ReconfigureSite(ctx context.Context, site models.Site) error { hugoCfg := hugoConfig{ Title: site.Title, LanguageCode: "en", @@ -117,21 +92,17 @@ func (p *Provider) ReconfigureSite(ctx context.Context, isPreviewConfig bool, co }, } - if isPreviewConfig { - hugoCfg.CanonifyURLs = true - } - ymlBytes, err := yaml.Marshal(hugoCfg) if err != nil { return err } - if err := os.WriteFile(filepath.Join(p.SiteStagingDir(site, BaseSiteDir), configBase+".yaml"), ymlBytes, 0644); err != nil { + if err := os.WriteFile(filepath.Join(p.SiteStagingDir(site, BaseSiteDir), "hugo.yaml"), ymlBytes, 0644); err != nil { return err } - if _, err := os.Stat(filepath.Join(p.SiteStagingDir(site, BaseSiteDir), configBase+".toml")); err == nil { - if err := os.Remove(filepath.Join(p.SiteStagingDir(site, BaseSiteDir), configBase+".toml")); err != nil { + if _, err := os.Stat(filepath.Join(p.SiteStagingDir(site, BaseSiteDir), "hugo.toml")); err == nil { + if err := os.Remove(filepath.Join(p.SiteStagingDir(site, BaseSiteDir), "hugo.toml")); err != nil { return err } } diff --git a/providers/themes/meta.go b/providers/themes/meta.go index c2bf058..c0a48a2 100644 --- a/providers/themes/meta.go +++ b/providers/themes/meta.go @@ -4,24 +4,24 @@ import "lmika.dev/lmika/hugo-cms/models" var themes = []models.ThemeMeta{ { - ID: "bear", - Name: "Bear", - URL: "https://github.com/janraasch/hugo-bearblog", - PreferTitle: true, - BlogPostBundle: "blog", + ID: "bear", + Name: "Bear", + URL: "https://github.com/janraasch/hugo-bearblog", + PreferTitle: true, + PostDir: "blog", }, { - ID: "terminal", - Name: "Terminal", - URL: "https://github.com/panr/hugo-theme-terminal", - PreferTitle: true, - BlogPostBundle: "posts", + ID: "terminal", + Name: "Terminal", + URL: "https://github.com/panr/hugo-theme-terminal", + PreferTitle: true, + PostDir: "posts", }, { - ID: "yingyang", - Name: "Yingyang", - URL: "https://github.com/joway/hugo-theme-yinyang", - PreferTitle: true, - BlogPostBundle: "posts", + ID: "yingyang", + Name: "Yingyang", + URL: "https://github.com/joway/hugo-theme-yinyang", + PreferTitle: true, + PostDir: "posts", }, } diff --git a/services/pages/services.go b/services/pages/services.go deleted file mode 100644 index 8d43616..0000000 --- a/services/pages/services.go +++ /dev/null @@ -1,205 +0,0 @@ -package pages - -import ( - "context" - "errors" - "lmika.dev/lmika/hugo-cms/models" - "lmika.dev/lmika/hugo-cms/providers/db" - "lmika.dev/lmika/hugo-cms/services/jobs" - "lmika.dev/lmika/hugo-cms/services/sitebuilder" - "lmika.dev/pkg/modash/moslice" - "strings" - "time" - "unicode" -) - -type Service struct { - db *db.DB - sb *sitebuilder.Service - jobs *jobs.Service -} - -func New( - db *db.DB, - sb *sitebuilder.Service, - jobs *jobs.Service, -) *Service { - return &Service{ - db: db, - sb: sb, - jobs: jobs, - } -} - -func (s *Service) ListPagesOfSite(ctx context.Context, site models.Site) ([]models.Page, error) { - return s.db.ListPagesOfSite(ctx, site.ID) -} - -func (s *Service) GetPage(ctx context.Context, id int) (models.Page, error) { - post, err := s.db.GetPage(ctx, int64(id)) - if err != nil { - return models.Page{}, err - } - - return post, nil -} - -func (s *Service) DeletePage(ctx context.Context, site models.Site, id int) error { - post, err := s.db.GetPage(ctx, int64(id)) - if err != nil { - return err - } else if post.SiteID != site.ID { - return errors.New("page not found") - } - - if err := s.db.DeletePage(ctx, int64(id)); err != nil { - return err - } - - return s.jobs.Queue(ctx, s.sb.RebuildSiteContent(site, site)) -} - -func (s *Service) Create(ctx context.Context, site models.Site, req NewPost) (models.Page, error) { - siteBundles, err := s.db.ListBundles(ctx, site.ID) - if err != nil { - return models.Page{}, err - } else if len(siteBundles) == 0 { - return models.Page{}, errors.New("no bundles found") - } - - rootBundle, ok := moslice.FindWhere(siteBundles, func(t models.Bundle) bool { - return t.Name == models.RootBundleName - }) - if !ok { - return models.Page{}, errors.New("root bundle not found") - } - - publishTime := time.Now() - - name := s.normalizePageName(req.Title) - nameProvenance := models.TitleNameProvenance - if name == "" { - // Use the timestamp as the name - name = publishTime.Format("2006-01-02-150405") - nameProvenance = models.DateNameProvenance - } - - post := models.Page{ - SiteID: site.ID, - BundleID: rootBundle.ID, - Name: name, - NameProvenance: nameProvenance, - Title: req.Title, - Body: req.Body, - State: models.PostStatePublished, - PublishDate: time.Now(), - } - - if err := s.save(ctx, site, rootBundle, &post); err != nil { - return models.Page{}, err - } - - if err := s.jobs.Queue(ctx, s.sb.RebuildSiteContent(site, site)); err != nil { - return models.Page{}, err - } - - return post, nil -} - -func (s *Service) Update(ctx context.Context, site models.Site, pageID int64, req NewPost) (models.Page, error) { - page, err := s.db.GetPage(ctx, pageID) - if err != nil { - return models.Page{}, err - } - - if page.SiteID != site.ID { - return models.Page{}, errors.New("page not found") - } - - bundle, err := s.db.GetBundleWithID(ctx, page.BundleID) - if err != nil { - return models.Page{}, err - } else if bundle.SiteID != site.ID { - return models.Page{}, errors.New("page not found") - } - - oldPage := page - - // Update the title if it wasn't set by the user - if page.NameProvenance != models.UserNameProvenance { - if req.Title == "" { - page.Name = page.PublishDate.Format("2006-01-02-150405") - page.NameProvenance = models.DateNameProvenance - } else { - page.Name = s.normalizePageName(req.Title) - page.NameProvenance = models.TitleNameProvenance - } - } - page.Title = req.Title - page.Body = req.Body - - if err := s.save(ctx, site, bundle, &page); err != nil { - return models.Page{}, err - } - - // A content only change involves rewriting the file content. Anything else is a structural change - // that will need rewriting of all the page content. - contentOnlyChange := page.Name == oldPage.Name && page.Role == oldPage.Role - - if contentOnlyChange { - if err := s.jobs.Queue(ctx, s.sb.WritePage(site, bundle, page)); err != nil { - return models.Page{}, err - } - } else { - if err := s.jobs.Queue(ctx, s.sb.RebuildSiteContent(site, site)); err != nil { - return models.Page{}, err - } - } - - return page, nil -} - -func (s *Service) save(ctx context.Context, site models.Site, bundle models.Bundle, page *models.Page) error { - page.SiteID = site.ID - - if page.ID == 0 { - page.CreatedAt = time.Now() - page.UpdatedAt = time.Now() - if err := s.db.InsertPage(ctx, page); err != nil { - return err - } - } else { - page.UpdatedAt = time.Now() - if err := s.db.UpdatePage(ctx, page); err != nil { - return err - } - } - - return nil -} - -func (s *Service) normalizePageName(title string) string { - var sb strings.Builder - lastSpace := false - for _, r := range title { - switch { - case unicode.IsSpace(r): - if !lastSpace { - sb.WriteRune('-') - lastSpace = true - } - case unicode.IsNumber(r): - lastSpace = false - sb.WriteRune(r) - case unicode.IsLetter(r): - lastSpace = false - sb.WriteRune(unicode.ToLower(r)) - } - } - return sb.String() -} - -type NewPost struct { - Title string - Body string -} diff --git a/services/posts/services.go b/services/posts/services.go index cecb0b9..902c14c 100644 --- a/services/posts/services.go +++ b/services/posts/services.go @@ -55,11 +55,11 @@ func (s *Service) DeletePost(ctx context.Context, site models.Site, id int) erro func (s *Service) Create(ctx context.Context, site models.Site, req NewPost) (models.Post, error) { post := models.Post{ - SiteID: site.ID, - Title: req.Title, - Body: req.Body, - State: models.PostStatePublished, - PublishDate: time.Now(), + SiteID: site.ID, + Title: req.Title, + Body: req.Body, + State: models.PostStatePublished, + PostDate: time.Now(), } if err := s.Save(ctx, site, &post); err != nil { diff --git a/services/sitebuilder/pages.go b/services/sitebuilder/pages.go deleted file mode 100644 index c253c59..0000000 --- a/services/sitebuilder/pages.go +++ /dev/null @@ -1,146 +0,0 @@ -package sitebuilder - -import ( - "context" - "fmt" - "lmika.dev/lmika/hugo-cms/models" - "lmika.dev/pkg/modash/momap" - "os" - "time" -) - -func (s *Service) WritePage(site models.Site, bundle models.Bundle, page models.Page) models.Job { - return models.Job{ - Do: func(ctx context.Context) error { - s.signalSiteBuildingStarted(ctx, site) - defer s.signalSiteBuildingFinished(ctx, site) - - rbn, err := s.fullRebuildNecessary(ctx, site) - if err != nil { - return err - } else if rbn { - return s.rebuildSite(ctx, site, site) - } - - themeMeta, ok := s.themes.Lookup(site.Theme) - if !ok { - return fmt.Errorf("theme %s not found in themes", site.Theme) - } - - bundleInfo, err := s.db.GetSiteBundleInfo(ctx, site.ID) - if err != nil { - return err - } - - if err := s.writePage(pageBuildInfo{ - site: site, - themeMeta: themeMeta, - bundle: bundle, - bundleInfo: bundleInfo, - }, page); err != nil { - return err - } - return s.publish(ctx, site) - }, - } -} - -func (s *Service) DeletePage(site models.Site, page models.Page) models.Job { - return models.Job{ - Do: func(ctx context.Context) error { - s.signalSiteBuildingStarted(ctx, site) - defer s.signalSiteBuildingFinished(ctx, site) - - bundle, err := s.db.GetBundleWithID(ctx, page.BundleID) - if err != nil { - return err - } - - themeMeta, ok := s.themes.Lookup(site.Theme) - if !ok { - return fmt.Errorf("theme %s not found in themes", site.Theme) - } - - bundleInfo, err := s.db.GetSiteBundleInfo(ctx, site.ID) - if err != nil { - return err - } - - postFilename := s.pageFilename(pageBuildInfo{ - site: site, - themeMeta: themeMeta, - bundle: bundle, - bundleInfo: bundleInfo, - }, page) - - if os.Remove(postFilename) != nil { - return nil - } - // TODO: if dir is empty, delete it - - return s.publish(ctx, site) - }, - } -} - -func (s *Service) writeAllPages(ctx context.Context, site models.Site) error { - themeMeta, ok := s.themes.Lookup(site.Theme) - if !ok { - return fmt.Errorf("theme %s not found in themes", site.Theme) - } - - bundles, err := s.db.ListBundles(ctx, site.ID) - if err != nil { - return err - } - - bundlesByID := momap.FromSlice(bundles, func(b models.Bundle) (int64, models.Bundle) { return b.ID, b }) - - bundleInfo, err := s.db.GetSiteBundleInfo(ctx, site.ID) - if err != nil { - return err - } - - var startId int64 - for { - pages, err := s.db.ListPublishablePages(ctx, int64(startId), site.ID) - if err != nil { - return err - } else if len(pages) == 0 { - return nil - } - - for _, page := range pages { - if err := s.writePage(pageBuildInfo{ - site: site, - themeMeta: themeMeta, - bundle: bundlesByID[page.BundleID], - bundleInfo: bundleInfo, - }, page); err != nil { - return err - } - } - startId = pages[len(pages)-1].ID - } -} - -func (s *Service) writePage(bi pageBuildInfo, page models.Page) error { - postFilename := s.pageFilename(bi, page) - - frontMatter := map[string]any{ - "date": page.PublishDate.Format(time.RFC3339), - } - - if page.Title != "" { - frontMatter["title"] = page.Title - } - - return s.writeMarkdownFile(postFilename, frontMatter, page.Body) -} - -type pageBuildInfo struct { - site models.Site - themeMeta models.ThemeMeta - bundle models.Bundle - bundleInfo map[int64]models.BundleInfo -} diff --git a/services/sitebuilder/posts.go b/services/sitebuilder/posts.go index e67a5e2..72ac747 100644 --- a/services/sitebuilder/posts.go +++ b/services/sitebuilder/posts.go @@ -6,6 +6,7 @@ import ( "gopkg.in/yaml.v3" "lmika.dev/lmika/hugo-cms/models" "lmika.dev/lmika/hugo-cms/providers/hugo" + "log" "os" "path/filepath" "time" @@ -103,21 +104,19 @@ func (s *Service) writePost(site models.Site, post models.Post) error { postFilename := s.postFilename(site, themeMeta, post) - frontMatter := map[string]any{ - "date": post.PublishDate.Format(time.RFC3339), + log.Printf(" .. post %v", postFilename) + + if err := os.MkdirAll(filepath.Dir(postFilename), 0755); err != nil { + return err + } + + frontMatter := map[string]string{ + "date": post.PostDate.Format(time.RFC3339), } if post.Title != "" { frontMatter["title"] = post.Title } else if themeMeta.PreferTitle { - frontMatter["title"] = post.PublishDate.Format(time.ANSIC) - } - - return s.writeMarkdownFile(postFilename, frontMatter, post.Body) -} - -func (s *Service) writeMarkdownFile(outFile string, frontMatter map[string]any, body string) error { - if err := os.MkdirAll(filepath.Dir(outFile), 0755); err != nil { - return err + frontMatter["title"] = post.PostDate.Format(time.ANSIC) } fmBytes, err := yaml.Marshal(frontMatter) @@ -125,7 +124,7 @@ func (s *Service) writeMarkdownFile(outFile string, frontMatter map[string]any, return err } - f, err := os.Create(outFile) + f, err := os.Create(postFilename) if err != nil { return err } @@ -140,7 +139,7 @@ func (s *Service) writeMarkdownFile(outFile string, frontMatter map[string]any, if _, err := f.WriteString("---\n"); err != nil { return err } - if _, err := f.WriteString(body); err != nil { + if _, err := f.WriteString(post.Body); err != nil { return err } @@ -148,35 +147,5 @@ func (s *Service) writeMarkdownFile(outFile string, frontMatter map[string]any, } func (s *Service) postFilename(site models.Site, themeMeta models.ThemeMeta, post models.Post) string { - return filepath.Join(s.hugo.SiteStagingDir(site, hugo.ContentSiteDir), themeMeta.BlogPostBundle, post.CreatedAt.Format("2006-01-02-150405.md")) -} - -func (s *Service) pageFilename(bi pageBuildInfo, page models.Page) string { - isIndex := false - isLeafBundle := true - - thisBundleInfo := bi.bundleInfo[bi.bundle.ID] - if thisBundleInfo.PageCount > 1 || bi.bundle.Name == models.RootBundleName { - isLeafBundle = false - isIndex = thisBundleInfo.IndexPageID == page.ID - } else { - isIndex = true - } - - bundleDir := "" - if bi.bundle.Name != models.RootBundleName { - bundleDir = bi.bundle.Name - } - - pageName := page.Name - if isIndex { - if isLeafBundle { - pageName = "index" - } else { - pageName = "_index" - } - } - - pageName += ".md" - return filepath.Join(s.hugo.SiteStagingDir(bi.site, hugo.ContentSiteDir), bundleDir, pageName) + return filepath.Join(s.hugo.SiteStagingDir(site, hugo.ContentSiteDir), themeMeta.PostDir, post.CreatedAt.Format("2006-01-02-150405.md")) } diff --git a/services/sitebuilder/publish.go b/services/sitebuilder/publish.go index e730087..509d86e 100644 --- a/services/sitebuilder/publish.go +++ b/services/sitebuilder/publish.go @@ -10,17 +10,13 @@ func (s *Service) Publish(site models.Site) models.Job { Do: func(ctx context.Context) error { s.signalSiteBuildingStarted(ctx, site) defer s.signalSiteBuildingFinished(ctx, site) - + return s.publish(ctx, site) }, } } func (s *Service) publish(ctx context.Context, site models.Site) error { - if _, err := s.hugo.PreviewSite(ctx, site); err != nil { - return err - } - targets, err := s.db.GetPublishTargets(ctx, site.ID) if err != nil { return err @@ -35,7 +31,14 @@ func (s *Service) publish(ctx context.Context, site models.Site) error { } func (s *Service) publishTarget(ctx context.Context, site models.Site, target models.PublishTarget) error { - outDir, err := s.hugo.PublishSite(ctx, site, target) + outDir, cleanFn, err := s.hugo.PublishSite(ctx, site, target) + //defer func() { + // if cleanFn != nil { + // cleanFn() + // } + //}() + _ = cleanFn + if err != nil { return err } diff --git a/services/sitebuilder/service.go b/services/sitebuilder/service.go index 7ba2280..914f901 100644 --- a/services/sitebuilder/service.go +++ b/services/sitebuilder/service.go @@ -64,17 +64,6 @@ func (s *Service) RebuildSite(oldSite, newSite models.Site) models.Job { } } -func (s *Service) RebuildSiteContent(oldSite, newSite models.Site) models.Job { - return models.Job{ - Do: func(ctx context.Context) error { - s.signalSiteBuildingStarted(ctx, newSite) - defer s.signalSiteBuildingFinished(ctx, newSite) - - return s.rebuildContent(ctx, oldSite, newSite) - }, - } -} - func (s *Service) rebuildSite(ctx context.Context, oldSite, newSite models.Site) error { // Teardown the existing site siteDir := s.hugo.SiteStagingDir(oldSite, hugo.BaseSiteDir) @@ -86,37 +75,11 @@ func (s *Service) rebuildSite(ctx context.Context, oldSite, newSite models.Site) return err } - if err := s.writeAllContent(ctx, newSite); err != nil { - return err - } - - return s.publish(ctx, newSite) -} - -func (s *Service) rebuildContent(ctx context.Context, oldSite, newSite models.Site) error { - // Teardown the existing site - siteDir := s.hugo.SiteStagingDir(oldSite, hugo.ContentSiteDir) - if err := os.RemoveAll(siteDir); err != nil { - return err - } - - if err := s.writeAllContent(ctx, newSite); err != nil { - return err - } - - return s.publish(ctx, newSite) -} - -func (s *Service) writeAllContent(ctx context.Context, newSite models.Site) error { if err := s.writeAllPosts(ctx, newSite); err != nil { return err } - if err := s.writeAllPages(ctx, newSite); err != nil { - return err - } - - return nil + return s.publish(ctx, newSite) } func (s *Service) fullRebuildNecessary(ctx context.Context, site models.Site) (bool, error) { @@ -172,14 +135,9 @@ func (s *Service) createSite(ctx context.Context, site models.Site) error { return err } - if err := s.hugo.ReconfigureSite(ctx, false, "hugo", site); err != nil { + if err := s.hugo.ReconfigureSite(ctx, site); err != nil { return err } - - if err := s.hugo.ReconfigureSite(ctx, true, "hugoPreview", site); err != nil { - return err - } - return nil } diff --git a/services/sitebuilder/tracker.go b/services/sitebuilder/tracker.go deleted file mode 100644 index 51204a7..0000000 --- a/services/sitebuilder/tracker.go +++ /dev/null @@ -1,32 +0,0 @@ -package sitebuilder - -import ( - "lmika.dev/lmika/hugo-cms/models" - "lmika.dev/lmika/hugo-cms/providers/bus" -) - -type SiteBuildingTracker struct { - bus *bus.Bus - isBuildingState map[int64]models.Site -} - -func NewSiteBuildingTracker(bus *bus.Bus) *SiteBuildingTracker { - return &SiteBuildingTracker{ - bus: bus, - isBuildingState: map[int64]models.Site{}, - } -} - -func (sbt *SiteBuildingTracker) Listen() { - sub := sbt.bus.Subscribe() - - for e := range sub.C { - switch e.Type { - case models.EventSiteBuildingStart: - site := e.Data.(models.Site) - sbt.isBuildingState[site.ID] = site - case models.EventSiteBuildingDone: - delete(sbt.isBuildingState, e.Data.(models.Site).ID) - } - } -} diff --git a/services/sites/create.go b/services/sites/create.go deleted file mode 100644 index 4c3cb31..0000000 --- a/services/sites/create.go +++ /dev/null @@ -1,56 +0,0 @@ -package sites - -import ( - "context" - "errors" - "lmika.dev/lmika/hugo-cms/models" - "time" -) - -func (s *Service) CreateSite(ctx context.Context, user models.User, name string) (models.Site, error) { - // Create a new site - newSite := models.Site{ - Name: normaliseName(name), - OwnerUserID: user.ID, - Title: name, - Theme: "bear", - } - - _, ok := s.themes.Lookup(newSite.Theme) - if !ok { - return models.Site{}, errors.New("theme not found") - } - - if err := s.db.InsertSite(ctx, &newSite); err != nil { - return models.Site{}, err - } - - // Add the default page bundle - rootBundle := models.Bundle{ - SiteID: newSite.ID, - Name: models.RootBundleName, - CreatedAt: time.Now(), - } - if err := s.db.InsertBundle(ctx, &rootBundle); err != nil { - return models.Site{}, err - } - - // TEMP: Add a home page - homePage := models.Page{ - SiteID: newSite.ID, - BundleID: rootBundle.ID, - Name: "index", - Title: "Welcome to the home page", - Body: "This is the home page", - State: models.PostStatePublished, - Role: models.IndexPageRole, - PublishDate: time.Now(), - CreatedAt: time.Now(), - UpdatedAt: time.Now(), - } - if err := s.db.InsertPage(ctx, &homePage); err != nil { - return models.Site{}, err - } - - return newSite, s.jobs.Queue(ctx, s.sb.RebuildSite(newSite, newSite)) -} diff --git a/services/sites/service.go b/services/sites/service.go index 8ad2bd7..8848b29 100644 --- a/services/sites/service.go +++ b/services/sites/service.go @@ -46,6 +46,27 @@ func (s *Service) GetProdTargetOfSite(ctx context.Context, siteID int) (models.P return s.db.GetPublishTargetBySiteRole(ctx, int64(siteID), models.TargetRoleProduction) } +func (s *Service) CreateSite(ctx context.Context, user models.User, name string) (models.Site, error) { + newSite := models.Site{ + Name: normaliseName(name), + OwnerUserID: user.ID, + Title: name, + Theme: "bear", + //Theme: "yingyang", + } + + _, ok := s.themes.Lookup(newSite.Theme) + if !ok { + return models.Site{}, errors.New("theme not found") + } + + if err := s.db.InsertSite(ctx, &newSite); err != nil { + return models.Site{}, err + } + + return newSite, s.jobs.Queue(ctx, s.sb.CreateNewSite(newSite)) +} + func (s *Service) SaveSettings(ctx context.Context, site models.Site, newSettings NewSettings) error { _, ok := s.themes.Lookup(newSettings.SiteTheme) if !ok { @@ -61,28 +82,26 @@ func (s *Service) SaveSettings(ctx context.Context, site models.Site, newSetting return err } - if newSettings.TargetRef != "" && newSettings.TargetURL != "" { - pubTarget, err := s.db.GetPublishTargetBySiteRole(ctx, newSite.ID, models.TargetRoleProduction) - if err == nil { - pubTarget.TargetRef = newSettings.TargetRef - pubTarget.URL = newSettings.TargetURL - if err := s.db.UpdatePublishTarget(ctx, pubTarget); err != nil { - return err - } - } else if errors.Is(err, pgx.ErrNoRows) { - pubTarget = models.PublishTarget{ - SiteID: newSite.ID, - Role: models.TargetRoleProduction, - Type: models.TargetTypeNetlify, - URL: newSettings.TargetURL, - TargetRef: newSettings.TargetRef, - } - if err := s.db.InsertPublishTarget(ctx, &pubTarget); err != nil { - return err - } - } else { + pubTarget, err := s.db.GetPublishTargetBySiteRole(ctx, newSite.ID, models.TargetRoleProduction) + if err == nil { + pubTarget.TargetRef = newSettings.TargetRef + pubTarget.URL = newSettings.TargetURL + if err := s.db.UpdatePublishTarget(ctx, pubTarget); err != nil { return err } + } else if errors.Is(err, pgx.ErrNoRows) { + pubTarget = models.PublishTarget{ + SiteID: newSite.ID, + Role: models.TargetRoleProduction, + Type: models.TargetTypeNetlify, + URL: newSettings.TargetURL, + TargetRef: newSettings.TargetRef, + } + if err := s.db.InsertPublishTarget(ctx, &pubTarget); err != nil { + return err + } + } else { + return err } return s.jobs.Queue(ctx, s.sb.RebuildSite(site, newSite)) diff --git a/sql/queries/bundles.sql b/sql/queries/bundles.sql deleted file mode 100644 index d11c9bd..0000000 --- a/sql/queries/bundles.sql +++ /dev/null @@ -1,21 +0,0 @@ --- name: InsertBundle :one -INSERT INTO bundles ( - site_id, - name, - created_at, - updated_at -) VALUES ($1, $2, $3, $3) RETURNING id; - --- name: ListBundles :many -SELECT * FROM bundles WHERE site_id = $1; - --- name: GetBundleWithID :one -SELECT * FROM bundles WHERE id = $1; - --- name: GetSiteBundleInfo :many -WITH page_counts AS ( - SELECT b.bundle_id, count(*) AS page_count FROM pages b WHERE b.site_id = $1 GROUP BY bundle_id -), index_pages AS ( - SELECT p.id AS index_page_id, p.bundle_id FROM pages p WHERE p.site_id = $1 AND p.role = 'index' -) -SELECT b.bundle_id, b.page_count, p.index_page_id FROM page_counts b LEFT OUTER JOIN index_pages p ON b.bundle_id = p.bundle_id; diff --git a/sql/queries/pages.sql b/sql/queries/pages.sql deleted file mode 100644 index db5fe71..0000000 --- a/sql/queries/pages.sql +++ /dev/null @@ -1,49 +0,0 @@ --- name: InsertPage :one -INSERT INTO pages ( - site_id, - bundle_id, - name, - name_provenance, - title, - post_type_id, - body, - state, - props, - role, - publish_date, - created_at, - updated_at -) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $12) -RETURNING id; - --- name: UpdatePage :exec -UPDATE pages SET - site_id = $2, - bundle_id = $3, - name = $4, - name_provenance = $5, - title = $6, - post_type_id = $7, - role = $8, - body = $9, - state = $10, - props = $11, - publish_date = $12, - created_at = $13, - updated_at = $14 -WHERE id = $1; - --- name: ListPublishablePages :many -SELECT * -FROM pages -WHERE id > $1 AND site_id = $2 AND state = 'published' -ORDER BY id LIMIT 100; - --- name: ListPages :many -SELECT * FROM pages WHERE site_id = $1 ORDER BY name ASC LIMIT 25; - --- name: GetPageWithID :one -SELECT * FROM pages WHERE id = $1; - --- name: DeletePageWithID :exec -DELETE FROM pages WHERE id = $1; \ No newline at end of file diff --git a/sql/queries/posts.sql b/sql/queries/posts.sql index f33bbfa..1ddf3cf 100644 --- a/sql/queries/posts.sql +++ b/sql/queries/posts.sql @@ -1,5 +1,5 @@ -- name: ListPosts :many -SELECT * FROM posts WHERE site_id = $1 ORDER BY publish_date DESC LIMIT 25; +SELECT * FROM posts WHERE site_id = $1 ORDER BY post_date DESC LIMIT 25; -- name: GetPostWithID :one SELECT * FROM posts WHERE id = $1 LIMIT 1; @@ -7,7 +7,7 @@ SELECT * FROM posts WHERE id = $1 LIMIT 1; -- name: ListPublishablePosts :many SELECT * FROM posts -WHERE id > $1 AND site_id = $2 AND state = 'published' AND publish_date <= $3 +WHERE id > $1 AND site_id = $2 AND state = 'published' AND post_date <= $3 ORDER BY id LIMIT 100; -- name: InsertPost :one @@ -17,10 +17,9 @@ INSERT INTO posts ( body, state, props, - publish_date, - created_at, - updated_at -) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) + post_date, + created_at +) VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING id; -- name: UpdatePost :exec @@ -30,8 +29,8 @@ UPDATE posts SET body = $4, state = $5, props = $6, - publish_date = $7, - updated_at = $8 + post_date = $7 + -- updated_at = $7 WHERE id = $1; -- name: DeletePost :exec diff --git a/sql/schema/1_init.up.sql b/sql/schema/1_init.up.sql index ab6f576..38492d1 100644 --- a/sql/schema/1_init.up.sql +++ b/sql/schema/1_init.up.sql @@ -3,10 +3,6 @@ CREATE TYPE post_state AS ENUM ( 'published' ); -CREATE TYPE post_format AS ENUM ( - 'markdown' -); - CREATE TYPE target_role AS ENUM ( 'production' ); @@ -15,16 +11,6 @@ CREATE TYPE target_type AS ENUM ( 'netlify' ); -CREATE TYPE page_name_provenance AS ENUM ( - 'user', - 'title', - 'date' -); - -CREATE TYPE page_role AS ENUM ( - 'index' -); - CREATE TABLE users ( id BIGSERIAL NOT NULL PRIMARY KEY, email TEXT NOT NULL UNIQUE, @@ -42,67 +28,19 @@ CREATE TABLE sites ( FOREIGN KEY (owner_user_id) REFERENCES users (id) ); --- Post role is used to describe a specific kind of post, such as a link. --- When set, it specifies the layout to use for the page -CREATE TABLE post_types ( - id BIGSERIAL NOT NULL PRIMARY KEY, - site_id BIGINT NOT NULL, - layout_name TEXT NOT NULL, - - FOREIGN KEY (site_id) REFERENCES sites (id) ON DELETE CASCADE -); - CREATE TABLE posts ( - id BIGSERIAL NOT NULL PRIMARY KEY, - site_id BIGINT NOT NULL, - title TEXT, - post_type_id BIGINT, - format post_format NOT NULL DEFAULT 'markdown', - body TEXT NOT NULL, - state post_state NOT NULL, - props JSON NOT NULL, - publish_date TIMESTAMP WITH TIME ZONE, - created_at TIMESTAMP NOT NULL, - updated_at TIMESTAMP NOT NULL, - - FOREIGN KEY (post_type_id) REFERENCES post_types (id) ON DELETE CASCADE, - FOREIGN KEY (site_id) REFERENCES sites (id) ON DELETE CASCADE -); - -CREATE TABLE bundles ( - id BIGSERIAL NOT NULL PRIMARY KEY, - site_id BIGINT NOT NULL, - name TEXT NOT NULL, - created_at TIMESTAMP NOT NULL, - updated_at TIMESTAMP NOT NULL, + id BIGSERIAL NOT NULL PRIMARY KEY, + site_id BIGINT NOT NULL, + title TEXT, + body TEXT NOT NULL, + state post_state NOT NULL, + props JSON NOT NULL, + post_date TIMESTAMP WITH TIME ZONE, + created_at TIMESTAMP NOT NULL, FOREIGN KEY (site_id) REFERENCES sites (id) ON DELETE CASCADE ); -CREATE TABLE pages ( - id BIGSERIAL NOT NULL PRIMARY KEY, - site_id BIGINT NOT NULL, - bundle_id BIGINT NOT NULL, - name TEXT NOT NULL, - name_provenance page_name_provenance NOT NULL, - format post_format NOT NULL DEFAULT 'markdown', - title TEXT, - post_type_id BIGINT, - body TEXT NOT NULL, - state post_state NOT NULL, - props JSON NOT NULL, - role page_role, - publish_date TIMESTAMP WITH TIME ZONE, - created_at TIMESTAMP NOT NULL, - updated_at TIMESTAMP NOT NULL, - - UNIQUE (bundle_id, name), - FOREIGN KEY (site_id) REFERENCES sites (id) ON DELETE CASCADE, - FOREIGN KEY (post_type_id) REFERENCES post_types (id) ON DELETE CASCADE, - FOREIGN KEY (bundle_id) REFERENCES sites (id) ON DELETE CASCADE -); -CREATE UNIQUE INDEX page_bundle_id_role ON pages (bundle_id, role) WHERE (role is NOT null); - CREATE TABLE publish_targets ( id BIGSERIAL NOT NULL PRIMARY KEY, site_id BIGINT NOT NULL, diff --git a/templates/fs.go b/templates/fs.go index 9fa97a7..a8b1117 100644 --- a/templates/fs.go +++ b/templates/fs.go @@ -6,6 +6,5 @@ import "embed" //go:embed auth/*.html //go:embed layouts/*.html //go:embed posts/*.html -//go:embed pages/*.html //go:embed sites/*.html var FS embed.FS diff --git a/templates/layouts/site.html b/templates/layouts/site.html index 81536c4..45bd5dc 100644 --- a/templates/layouts/site.html +++ b/templates/layouts/site.html @@ -14,7 +14,6 @@