Made some changes to how index pages are made
This commit is contained in:
		
							parent
							
								
									ba12398d2f
								
							
						
					
					
						commit
						573517565d
					
				|  | @ -28,6 +28,41 @@ func (q *Queries) GetBundleWithID(ctx context.Context, id int64) (Bundle, error) | |||
| 	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, | ||||
|  |  | |||
|  | @ -54,6 +54,47 @@ func (ns NullPageNameProvenance) Value() (driver.Value, error) { | |||
| 	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 ( | ||||
|  | @ -193,10 +234,11 @@ type Page struct { | |||
| 	Name           string | ||||
| 	NameProvenance PageNameProvenance | ||||
| 	Title          pgtype.Text | ||||
| 	Role           pgtype.Int8 | ||||
| 	PostTypeID     pgtype.Int8 | ||||
| 	Body           string | ||||
| 	State          PostState | ||||
| 	Props          []byte | ||||
| 	Role           NullPageRole | ||||
| 	PublishDate    pgtype.Timestamptz | ||||
| 	CreatedAt      pgtype.Timestamp | ||||
| 	UpdatedAt      pgtype.Timestamp | ||||
|  | @ -206,7 +248,7 @@ type Post struct { | |||
| 	ID          int64 | ||||
| 	SiteID      int64 | ||||
| 	Title       pgtype.Text | ||||
| 	Role        pgtype.Int8 | ||||
| 	PostTypeID  pgtype.Int8 | ||||
| 	Body        string | ||||
| 	State       PostState | ||||
| 	Props       []byte | ||||
|  | @ -215,7 +257,7 @@ type Post struct { | |||
| 	UpdatedAt   pgtype.Timestamp | ||||
| } | ||||
| 
 | ||||
| type PostRole struct { | ||||
| type PostType struct { | ||||
| 	ID         int64 | ||||
| 	SiteID     int64 | ||||
| 	LayoutName string | ||||
|  |  | |||
|  | @ -21,7 +21,7 @@ func (q *Queries) DeletePageWithID(ctx context.Context, id int64) error { | |||
| } | ||||
| 
 | ||||
| const getPageWithID = `-- name: GetPageWithID :one | ||||
| SELECT id, site_id, bundle_id, name, name_provenance, title, role, body, state, props, publish_date, created_at, updated_at FROM pages WHERE id = $1 | ||||
| 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) { | ||||
|  | @ -34,10 +34,11 @@ func (q *Queries) GetPageWithID(ctx context.Context, id int64) (Page, error) { | |||
| 		&i.Name, | ||||
| 		&i.NameProvenance, | ||||
| 		&i.Title, | ||||
| 		&i.Role, | ||||
| 		&i.PostTypeID, | ||||
| 		&i.Body, | ||||
| 		&i.State, | ||||
| 		&i.Props, | ||||
| 		&i.Role, | ||||
| 		&i.PublishDate, | ||||
| 		&i.CreatedAt, | ||||
| 		&i.UpdatedAt, | ||||
|  | @ -52,14 +53,15 @@ INSERT INTO pages ( | |||
|     name, | ||||
|     name_provenance, | ||||
|     title, | ||||
|     role, | ||||
|     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, $11) | ||||
| ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $12) | ||||
| RETURNING id | ||||
| ` | ||||
| 
 | ||||
|  | @ -69,10 +71,11 @@ type InsertPageParams struct { | |||
| 	Name           string | ||||
| 	NameProvenance PageNameProvenance | ||||
| 	Title          pgtype.Text | ||||
| 	Role           pgtype.Int8 | ||||
| 	PostTypeID     pgtype.Int8 | ||||
| 	Body           string | ||||
| 	State          PostState | ||||
| 	Props          []byte | ||||
| 	Role           NullPageRole | ||||
| 	PublishDate    pgtype.Timestamptz | ||||
| 	CreatedAt      pgtype.Timestamp | ||||
| } | ||||
|  | @ -84,10 +87,11 @@ func (q *Queries) InsertPage(ctx context.Context, arg InsertPageParams) (int64, | |||
| 		arg.Name, | ||||
| 		arg.NameProvenance, | ||||
| 		arg.Title, | ||||
| 		arg.Role, | ||||
| 		arg.PostTypeID, | ||||
| 		arg.Body, | ||||
| 		arg.State, | ||||
| 		arg.Props, | ||||
| 		arg.Role, | ||||
| 		arg.PublishDate, | ||||
| 		arg.CreatedAt, | ||||
| 	) | ||||
|  | @ -97,7 +101,7 @@ func (q *Queries) InsertPage(ctx context.Context, arg InsertPageParams) (int64, | |||
| } | ||||
| 
 | ||||
| const listPages = `-- name: ListPages :many | ||||
| SELECT id, site_id, bundle_id, name, name_provenance, title, role, body, state, props, publish_date, created_at, updated_at FROM pages WHERE site_id = $1 ORDER BY name ASC LIMIT 25 | ||||
| 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) { | ||||
|  | @ -116,10 +120,11 @@ func (q *Queries) ListPages(ctx context.Context, siteID int64) ([]Page, error) { | |||
| 			&i.Name, | ||||
| 			&i.NameProvenance, | ||||
| 			&i.Title, | ||||
| 			&i.Role, | ||||
| 			&i.PostTypeID, | ||||
| 			&i.Body, | ||||
| 			&i.State, | ||||
| 			&i.Props, | ||||
| 			&i.Role, | ||||
| 			&i.PublishDate, | ||||
| 			&i.CreatedAt, | ||||
| 			&i.UpdatedAt, | ||||
|  | @ -135,7 +140,7 @@ func (q *Queries) ListPages(ctx context.Context, siteID int64) ([]Page, error) { | |||
| } | ||||
| 
 | ||||
| const listPublishablePages = `-- name: ListPublishablePages :many | ||||
| SELECT id, site_id, bundle_id, name, name_provenance, title, role, body, state, props, publish_date, created_at, updated_at | ||||
| 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 | ||||
|  | @ -162,10 +167,11 @@ func (q *Queries) ListPublishablePages(ctx context.Context, arg ListPublishableP | |||
| 			&i.Name, | ||||
| 			&i.NameProvenance, | ||||
| 			&i.Title, | ||||
| 			&i.Role, | ||||
| 			&i.PostTypeID, | ||||
| 			&i.Body, | ||||
| 			&i.State, | ||||
| 			&i.Props, | ||||
| 			&i.Role, | ||||
| 			&i.PublishDate, | ||||
| 			&i.CreatedAt, | ||||
| 			&i.UpdatedAt, | ||||
|  | @ -187,13 +193,14 @@ UPDATE pages SET | |||
|     name = $4, | ||||
|     name_provenance = $5, | ||||
|     title = $6, | ||||
|     role = $7, | ||||
|     body = $8, | ||||
|     state = $9, | ||||
|     props = $10, | ||||
|     publish_date = $11, | ||||
|     created_at = $12, | ||||
|     updated_at = $13 | ||||
|     post_type_id = $7, | ||||
|     role = $8, | ||||
|     body = $9, | ||||
|     state = $10, | ||||
|     props = $11, | ||||
|     publish_date = $12, | ||||
|     created_at = $13, | ||||
|     updated_at = $14 | ||||
| WHERE id = $1 | ||||
| ` | ||||
| 
 | ||||
|  | @ -204,7 +211,8 @@ type UpdatePageParams struct { | |||
| 	Name           string | ||||
| 	NameProvenance PageNameProvenance | ||||
| 	Title          pgtype.Text | ||||
| 	Role           pgtype.Int8 | ||||
| 	PostTypeID     pgtype.Int8 | ||||
| 	Role           NullPageRole | ||||
| 	Body           string | ||||
| 	State          PostState | ||||
| 	Props          []byte | ||||
|  | @ -221,6 +229,7 @@ func (q *Queries) UpdatePage(ctx context.Context, arg UpdatePageParams) error { | |||
| 		arg.Name, | ||||
| 		arg.NameProvenance, | ||||
| 		arg.Title, | ||||
| 		arg.PostTypeID, | ||||
| 		arg.Role, | ||||
| 		arg.Body, | ||||
| 		arg.State, | ||||
|  |  | |||
|  | @ -21,7 +21,7 @@ func (q *Queries) DeletePost(ctx context.Context, id int64) error { | |||
| } | ||||
| 
 | ||||
| const getPostWithID = `-- name: GetPostWithID :one | ||||
| SELECT id, site_id, title, role, body, state, props, publish_date, created_at, updated_at FROM posts WHERE id = $1 LIMIT 1 | ||||
| SELECT id, site_id, title, post_type_id, body, state, props, publish_date, created_at, updated_at FROM posts WHERE id = $1 LIMIT 1 | ||||
| ` | ||||
| 
 | ||||
| func (q *Queries) GetPostWithID(ctx context.Context, id int64) (Post, error) { | ||||
|  | @ -31,7 +31,7 @@ func (q *Queries) GetPostWithID(ctx context.Context, id int64) (Post, error) { | |||
| 		&i.ID, | ||||
| 		&i.SiteID, | ||||
| 		&i.Title, | ||||
| 		&i.Role, | ||||
| 		&i.PostTypeID, | ||||
| 		&i.Body, | ||||
| 		&i.State, | ||||
| 		&i.Props, | ||||
|  | @ -84,7 +84,7 @@ func (q *Queries) InsertPost(ctx context.Context, arg InsertPostParams) (int64, | |||
| } | ||||
| 
 | ||||
| const listPosts = `-- name: ListPosts :many | ||||
| SELECT id, site_id, title, role, 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, 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 | ||||
| ` | ||||
| 
 | ||||
| func (q *Queries) ListPosts(ctx context.Context, siteID int64) ([]Post, error) { | ||||
|  | @ -100,7 +100,7 @@ func (q *Queries) ListPosts(ctx context.Context, siteID int64) ([]Post, error) { | |||
| 			&i.ID, | ||||
| 			&i.SiteID, | ||||
| 			&i.Title, | ||||
| 			&i.Role, | ||||
| 			&i.PostTypeID, | ||||
| 			&i.Body, | ||||
| 			&i.State, | ||||
| 			&i.Props, | ||||
|  | @ -119,7 +119,7 @@ func (q *Queries) ListPosts(ctx context.Context, siteID int64) ([]Post, error) { | |||
| } | ||||
| 
 | ||||
| const listPublishablePosts = `-- name: ListPublishablePosts :many | ||||
| SELECT id, site_id, title, role, body, state, props, publish_date, created_at, updated_at | ||||
| SELECT id, site_id, title, post_type_id, body, state, props, publish_date, created_at, updated_at | ||||
| FROM posts | ||||
| WHERE id > $1 AND site_id = $2 AND state = 'published' AND publish_date <= $3 | ||||
| ORDER BY id LIMIT 100 | ||||
|  | @ -144,7 +144,7 @@ func (q *Queries) ListPublishablePosts(ctx context.Context, arg ListPublishableP | |||
| 			&i.ID, | ||||
| 			&i.SiteID, | ||||
| 			&i.Title, | ||||
| 			&i.Role, | ||||
| 			&i.PostTypeID, | ||||
| 			&i.Body, | ||||
| 			&i.State, | ||||
| 			&i.Props, | ||||
|  |  | |||
|  | @ -15,6 +15,13 @@ const ( | |||
| 	DateNameProvenance  NameProvenance = iota | ||||
| ) | ||||
| 
 | ||||
| type PageRole int | ||||
| 
 | ||||
| const ( | ||||
| 	NormalPageRole PageRole = iota | ||||
| 	IndexPageRole | ||||
| ) | ||||
| 
 | ||||
| type Bundle struct { | ||||
| 	ID        int64 | ||||
| 	SiteID    int64 | ||||
|  | @ -30,11 +37,18 @@ type Page struct { | |||
| 	Name           string | ||||
| 	NameProvenance NameProvenance | ||||
| 	Title          string | ||||
| 	Role           int64 | ||||
| 	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 | ||||
| } | ||||
|  | @ -5,6 +5,7 @@ import ( | |||
| 	"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" | ||||
| ) | ||||
| 
 | ||||
|  | @ -39,6 +40,20 @@ func (db *DB) GetBundleWithID(ctx context.Context, id int64) (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, | ||||
|  |  | |||
|  | @ -16,6 +16,12 @@ var nameProvenanceToDBNameProvenance = map[models.NameProvenance]dbq.PageNamePro | |||
| } | ||||
| 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{ | ||||
|  | @ -23,6 +29,7 @@ func (db *DB) InsertPage(ctx context.Context, page *models.Page) error { | |||
| 		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), | ||||
|  | @ -43,6 +50,7 @@ func (db *DB) UpdatePage(ctx context.Context, page *models.Page) error { | |||
| 		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, | ||||
|  | @ -95,6 +103,7 @@ func dbPageToPage(p dbq.Page) models.Page { | |||
| 		SiteID:         p.SiteID, | ||||
| 		BundleID:       p.BundleID, | ||||
| 		Name:           p.Name, | ||||
| 		Role:           dbPageRoleToPageRole[p.Role], | ||||
| 		NameProvenance: dbNameProvenanceToNameProvenance[p.NameProvenance], | ||||
| 		Title:          p.Title.String, | ||||
| 		Body:           p.Body, | ||||
|  |  | |||
|  | @ -85,7 +85,7 @@ func (s *Service) Create(ctx context.Context, site models.Site, req NewPost) (mo | |||
| 	post := models.Page{ | ||||
| 		SiteID:         site.ID, | ||||
| 		BundleID:       rootBundle.ID, | ||||
| 		Name:           s.normalizePageName(req.Title), | ||||
| 		Name:           name, | ||||
| 		NameProvenance: nameProvenance, | ||||
| 		Title:          req.Title, | ||||
| 		Body:           req.Body, | ||||
|  |  | |||
|  | @ -27,7 +27,17 @@ func (s *Service) WritePage(site models.Site, bundle models.Bundle, page models. | |||
| 				return fmt.Errorf("theme %s not found in themes", site.Theme) | ||||
| 			} | ||||
| 
 | ||||
| 			if err := s.writePage(site, themeMeta, bundle, page); err != nil { | ||||
| 			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) | ||||
|  | @ -45,8 +55,23 @@ func (s *Service) DeletePage(site models.Site, page models.Page) models.Job { | |||
| 			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) | ||||
| 			} | ||||
| 
 | ||||
| 			postFilename := s.pageFilename(site, bundle, page) | ||||
| 			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 | ||||
|  | @ -71,6 +96,11 @@ func (s *Service) writeAllPages(ctx context.Context, site models.Site) error { | |||
| 
 | ||||
| 	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) | ||||
|  | @ -81,7 +111,12 @@ func (s *Service) writeAllPages(ctx context.Context, site models.Site) error { | |||
| 		} | ||||
| 
 | ||||
| 		for _, page := range pages { | ||||
| 			if err := s.writePage(site, themeMeta, bundlesByID[page.BundleID], page); err != nil { | ||||
| 			if err := s.writePage(pageBuildInfo{ | ||||
| 				site:       site, | ||||
| 				themeMeta:  themeMeta, | ||||
| 				bundle:     bundlesByID[page.BundleID], | ||||
| 				bundleInfo: bundleInfo, | ||||
| 			}, page); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 		} | ||||
|  | @ -89,17 +124,24 @@ func (s *Service) writeAllPages(ctx context.Context, site models.Site) error { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (s *Service) writePage(site models.Site, themeMeta models.ThemeMeta, bundle models.Bundle, page models.Page) error { | ||||
| 	postFilename := s.pageFilename(site, bundle, page) | ||||
| 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 | ||||
| 	} else if themeMeta.PreferTitle { | ||||
| 	} else if bi.themeMeta.PreferTitle { | ||||
| 		frontMatter["title"] = page.PublishDate.Format(time.ANSIC) | ||||
| 	} | ||||
| 
 | ||||
| 	return s.writeMarkdownFile(postFilename, frontMatter, page.Body) | ||||
| } | ||||
| 
 | ||||
| type pageBuildInfo struct { | ||||
| 	site       models.Site | ||||
| 	themeMeta  models.ThemeMeta | ||||
| 	bundle     models.Bundle | ||||
| 	bundleInfo map[int64]models.BundleInfo | ||||
| } | ||||
|  |  | |||
|  | @ -151,12 +151,32 @@ func (s *Service) postFilename(site models.Site, themeMeta models.ThemeMeta, pos | |||
| 	return filepath.Join(s.hugo.SiteStagingDir(site, hugo.ContentSiteDir), themeMeta.BlogPostBundle, post.CreatedAt.Format("2006-01-02-150405.md")) | ||||
| } | ||||
| 
 | ||||
| func (s *Service) pageFilename(site models.Site, bundle models.Bundle, page models.Page) string { | ||||
| 	bundleDir := "" | ||||
| 	if bundle.Name != models.RootBundleName { | ||||
| 		bundleDir = bundle.Name | ||||
| func (s *Service) pageFilename(bi pageBuildInfo, page models.Page) string { | ||||
| 	isIndex := false | ||||
| 	isLeafBundle := true | ||||
| 
 | ||||
| 	thisBundleInfo := bi.bundleInfo[bi.bundle.ID] | ||||
| 	if thisBundleInfo.PageCount > 1 { | ||||
| 		isLeafBundle = false | ||||
| 		isIndex = thisBundleInfo.IndexPageID == page.ID | ||||
| 	} else { | ||||
| 		isIndex = true | ||||
| 	} | ||||
| 
 | ||||
| 	pageName := page.Name + ".md" | ||||
| 	return filepath.Join(s.hugo.SiteStagingDir(site, hugo.ContentSiteDir), bundleDir, pageName) | ||||
| 	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) | ||||
| } | ||||
|  |  | |||
|  | @ -43,6 +43,7 @@ func (s *Service) CreateSite(ctx context.Context, user models.User, name string) | |||
| 		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(), | ||||
|  |  | |||
|  | @ -10,4 +10,12 @@ INSERT INTO bundles ( | |||
| SELECT * FROM bundles WHERE site_id = $1; | ||||
| 
 | ||||
| -- name: GetBundleWithID :one | ||||
| SELECT * FROM bundles WHERE id = $1; | ||||
| 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; | ||||
|  |  | |||
|  | @ -5,14 +5,15 @@ INSERT INTO pages ( | |||
|     name, | ||||
|     name_provenance, | ||||
|     title, | ||||
|     role, | ||||
|     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, $11) | ||||
| ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $12) | ||||
| RETURNING id; | ||||
| 
 | ||||
| -- name: UpdatePage :exec | ||||
|  | @ -22,13 +23,14 @@ UPDATE pages SET | |||
|     name = $4, | ||||
|     name_provenance = $5, | ||||
|     title = $6, | ||||
|     role = $7, | ||||
|     body = $8, | ||||
|     state = $9, | ||||
|     props = $10, | ||||
|     publish_date = $11, | ||||
|     created_at = $12, | ||||
|     updated_at = $13 | ||||
|     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 | ||||
|  |  | |||
|  | @ -17,6 +17,10 @@ CREATE TYPE page_name_provenance AS ENUM ( | |||
|     'date' | ||||
| ); | ||||
| 
 | ||||
| CREATE TYPE page_role AS ENUM ( | ||||
|     'index' | ||||
| ); | ||||
| 
 | ||||
| CREATE TABLE users ( | ||||
|     id       BIGSERIAL NOT NULL PRIMARY KEY, | ||||
|     email    TEXT      NOT NULL UNIQUE, | ||||
|  | @ -36,7 +40,7 @@ CREATE TABLE sites ( | |||
| 
 | ||||
| -- 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_roles ( | ||||
| CREATE TABLE post_types ( | ||||
|     id          BIGSERIAL NOT NULL PRIMARY KEY, | ||||
|     site_id     BIGINT    NOT NULL, | ||||
|     layout_name TEXT      NOT NULL, | ||||
|  | @ -48,7 +52,7 @@ CREATE TABLE posts ( | |||
|     id           BIGSERIAL  NOT NULL PRIMARY KEY, | ||||
|     site_id      BIGINT     NOT NULL, | ||||
|     title        TEXT, | ||||
|     role         BIGINT, | ||||
|     post_type_id BIGINT, | ||||
|     body         TEXT       NOT NULL, | ||||
|     state        post_state NOT NULL, | ||||
|     props        JSON       NOT NULL, | ||||
|  | @ -56,7 +60,7 @@ CREATE TABLE posts ( | |||
|     created_at   TIMESTAMP  NOT NULL, | ||||
|     updated_at   TIMESTAMP  NOT NULL, | ||||
| 
 | ||||
|     FOREIGN KEY (role) REFERENCES post_roles (id) ON DELETE CASCADE, | ||||
|     FOREIGN KEY (post_type_id) REFERENCES post_types (id) ON DELETE CASCADE, | ||||
|     FOREIGN KEY (site_id) REFERENCES sites (id) ON DELETE CASCADE | ||||
| ); | ||||
| 
 | ||||
|  | @ -77,19 +81,21 @@ CREATE TABLE pages ( | |||
|     name            TEXT                 NOT NULL, | ||||
|     name_provenance page_name_provenance NOT NULL, | ||||
|     title           TEXT, | ||||
|     role            BIGINT, | ||||
|     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 (role) REFERENCES post_roles (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, | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue