Skip to content

Commit

Permalink
support terms
Browse files Browse the repository at this point in the history
  • Loading branch information
sunwei committed Nov 26, 2024
1 parent 87ef888 commit 4035c4e
Show file tree
Hide file tree
Showing 20 changed files with 354 additions and 70 deletions.
133 changes: 100 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,46 +1,113 @@
# Hugoverse
# Hugoverse: Headless CMS for Hugo

Hugo headless CMS server
**Hugoverse** is a headless CMS designed for Hugo, providing a seamless way to manage your static website content. With its powerful APIs, you can upload articles and resources like images, preview your site in real-time, and deploy it effortlessly to the cloud—all tailored to your selected Hugo theme.

## Prerequisite
---

Take MacOS for example:
## 🚀 Features

- Go:
- Dart Sass: brew install sass/sass/sass
- jq: brew install jq
1. **Content Management API**
Easily upload and manage articles, images, and other resources through Hugoverse's API.

## PoC
2. **Theme Compatibility**
Automatically adapts to your chosen Hugo theme, ensuring your site looks great without additional configuration.

- [x] DDD Ponzu to expose API
- [x] DDD Hugo to get content info
- [x] DDD Hugo to build site
- [x] Load Hugo project from file system
- [x] Manage Post through API
- [x] Build Site through API
- [x] Deploy Site through API
3. **Live Preview**
Preview your Hugo site in real-time to ensure your content and design align before deploying.

## MVP
4. **Cloud Deployment**
Deploy your site with a single click to the cloud, making it live and accessible instantly.

### 目标
5. **Streamlined Resource Handling**
Efficiently manage images and files for your Hugo website, ensuring all assets are properly organized and accessible.

为敏捷UP主 [老袁讲敏捷](https://space.bilibili.com/36395967) 打造一体化的个人品牌官网,增强专业形象并提升影响力。
---

### 需要实现的功能
## 🌟 Getting Started

- [ ] Hugoverse Notion Plugin
- [ ] 登陆Hugoverse服务
- [ ] 创建个人主页站点
- [ ] 创建商业服务站点 - 个人陪跑教练
- [ ] 创建Unfix敏捷小组介绍
- [x] Hugoverse待补全功能
- [x] 注册用户
- [x] 多租户管理
Follow these steps to start using Hugoverse for your Hugo project:

## Next
### Prerequisites

- [ ] I18N
- [ ] Sitemap
- [ ] Inline shortcode
- [ ] Output Format: connect page layouts and page outputs, should separate the config info service and business logic
- [ ] Docs
- Install [Hugo](https://gohugo.io/getting-started/installing/) on your machine.
- Create or clone a Hugo site.

### Installation

1. Clone the repository:
```bash
git clone https://github.com/your-username/hugoverse.git
cd hugoverse
```

2. Install dependencies (if applicable):
```bash
# Example: pipenv, npm, etc.
```

3. Start the Hugoverse server:
```bash
hugoverse serve
```

### API Usage

**Upload an Article:**
Use the API to upload a markdown file.
```bash
curl -X POST -F "[email protected]" https://your-hugoverse-instance/api/upload
```

**Upload Resources (e.g., images):**
```bash
curl -X POST -F "[email protected]" https://your-hugoverse-instance/api/resources
```

**Preview Your Site:**
Visit `https://your-hugoverse-instance/preview`.

**Deploy Your Site to the Cloud:**
```bash
curl -X POST https://your-hugoverse-instance/api/deploy
```

---

## 📄 Documentation

Visit the [Hugoverse Documentation](https://hugoverse.example.com/docs) for detailed guides and API references.

---

## 🛠️ Contributing

We welcome contributions from the community! Feel free to open issues, suggest features, or submit pull requests.

1. Fork the repository.
2. Create a feature branch:
```bash
git checkout -b feature-name
```
3. Commit your changes:
```bash
git commit -m "Add new feature"
```
4. Push the branch and open a pull request.

---

## 📝 License

Hugoverse is licensed under the [MIT License](LICENSE).

---

## ✨ Contact

For questions or support, feel free to reach out:

- **Email:** [email protected]
- **Website:** [hugoverse.com](https://hugoverse.com)
- **GitHub Issues:** [Create an Issue](https://github.com/your-username/hugoverse/issues)

Start building and managing your Hugo site effortlessly with **Hugoverse**! 🎉s
18 changes: 17 additions & 1 deletion internal/domain/content/entity/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,23 @@ func (c *Content) writeSiteResource(siteId int, dir string) error {
}

if res.Asset != "" {
go c.copyFiles(dir, getParentPath(sr.Path), []string{res.Asset})
_, p, err := parseURL(res.Asset)
if err != nil {
c.Log.Printf("parse url %s failed: %v\n", res.Asset, err)
return err
}

src := path.Join(c.Hugo.DirService.UploadDir(), p)
dst := path.Join(dir, sr.Path)

if err := c.Hugo.Fs.MkdirAll(path.Join(dir, getParentPath(sr.Path)), 0755); err != nil {
c.Log.Printf("mkdir %s failed: %v\n", path.Join(dir, getParentPath(sr.Path)), err)
continue
}

if err := c.copyFile(src, dst); err != nil {
return err
}
}
}

Expand Down
2 changes: 2 additions & 0 deletions internal/domain/contenthub/entity/layout.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const (
TaxonomyList = "taxonomy" + "/" + LayoutList

TermTerm = "term/term.html"
TermTag = "taxonomy/tag.html"
TermList = "taxonomy" + "/" + LayoutList

Sitemap = "sitemap.xml"
Expand Down Expand Up @@ -67,6 +68,7 @@ func (l *Layout) taxonomy() []string {

func (l *Layout) term() []string {
return []string{
TermTag,
TermTerm,
TermList,
DefaultTerm,
Expand Down
4 changes: 4 additions & 0 deletions internal/domain/contenthub/entity/page.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ func newTaxonomy(source *Source, content *Content, singular string) (*TaxonomyPa
singular: singular,
}

taxonomy.Page.title = singular

return taxonomy, nil
}

Expand All @@ -140,5 +142,7 @@ func newTerm(source *Source, content *Content, singular string, term string) (*T
term: term,
}

tp.TaxonomyPage.Page.title = term

return tp, nil
}
4 changes: 4 additions & 0 deletions internal/domain/contenthub/entity/pagebuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,8 @@ func (b *PageBuilder) buildTaxonomy() (*TaxonomyPage, error) {
return nil, err
}

tp.pageMap = b.PageMapper

if err := b.buildOutput(tp.Page); err != nil {
return nil, err
}
Expand All @@ -211,6 +213,8 @@ func (b *PageBuilder) buildTerm() (*TermPage, error) {
return nil, err
}

t.pageMap = b.PageMapper

if err := b.buildOutput(t.Page); err != nil {
return nil, err
}
Expand Down
4 changes: 2 additions & 2 deletions internal/domain/contenthub/entity/pagefields.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func (p *Page) Pages(langIndex int) contenthub.Pages {
case valueobject.KindTerm:
return p.pageMap.getPagesWithTerm(
pageMapQueryPagesBelowPath{
Path: p.Path(),
Path: p.Paths().Base(),
},
)
case valueobject.KindTaxonomy:
Expand All @@ -37,7 +37,7 @@ func (p *Page) Pages(langIndex int) contenthub.Pages {
pageMapQueryPagesInSection{
Index: langIndex,
pageMapQueryPagesBelowPath: pageMapQueryPagesBelowPath{
Path: p.Path(),
Path: p.Paths().Base(),
KeyPart: "term",
Include: pagePredicates.ShouldListLocal.And(pagePredicates.KindTerm),
},
Expand Down
7 changes: 5 additions & 2 deletions internal/domain/contenthub/entity/pagemap.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,14 +328,16 @@ func (m *PageMap) getPagesWithTerm(q pageMapQueryPagesBelowPath) contenthub.Page
doctree.LockTypeNone,
paths.AddTrailingSlash(q.Path),
func(s string, n *WeightedTermTreeNode) (bool, error) {
if include(n.term.Page) {
pas = append(pas, n.term)
p, found := n.getPage()
if found && include(p) {
pas = append(pas, p)
}

return false, nil
},
)
if err != nil {
m.Log.Errorf("getPagesWithTerm error: %v", err)
return nil, err
}

Expand All @@ -344,6 +346,7 @@ func (m *PageMap) getPagesWithTerm(q pageMapQueryPagesBelowPath) contenthub.Page
return pas, nil
})
if err != nil {
m.Log.Errorf("getPagesWithTerm: %v", err)
panic(err)
}

Expand Down
2 changes: 1 addition & 1 deletion internal/domain/contenthub/entity/term.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ func (t *Term) Assemble(pages *doctree.NodeShiftTree[*PageTreesNode],
}

entries.Insert(key, &WeightedTermTreeNode{
PageTreesNode: term,
PageTreesNode: newPageTreesNode(ps),
term: &ordinalWeightPage{Page: tp.(*TermPage), ordinal: i, weight: weight},
})
}
Expand Down
2 changes: 0 additions & 2 deletions internal/domain/contenthub/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,6 @@ type PageInfo interface {
Buffer() *bytes.Buffer
}

//TODO remove unless we need to expose those kind for other domains

const (
KindPage = "page"
KindHome = "home"
Expand Down
40 changes: 35 additions & 5 deletions internal/domain/site/entity/navigation.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package entity

import (
"github.com/gohugonet/hugoverse/internal/domain/contenthub"
"fmt"
"github.com/gohugonet/hugoverse/internal/domain/site/valueobject"
"github.com/gohugonet/hugoverse/pkg/compare"
"sort"
Expand Down Expand Up @@ -29,7 +29,7 @@ type OrderedTaxonomy []OrderedTaxonomyEntry

// getOneOPage returns one page in the taxonomy,
// nil if there is none.
func (t OrderedTaxonomy) getOneOPage() contenthub.Page {
func (t OrderedTaxonomy) getOneOPage() *WeightedPage {
if len(t) == 0 {
return nil
}
Expand All @@ -38,25 +38,55 @@ func (t OrderedTaxonomy) getOneOPage() contenthub.Page {

// WeightedPages is a list of Pages with their corresponding (and relative) weight
// [{Weight: 30, Page: *1}, {Weight: 40, Page: *2}]
type WeightedPages []contenthub.OrdinalWeightPage
type WeightedPages []*WeightedPage

// Page will return the Page (of Kind taxonomyList) that represents this set
// of pages. This method will panic if p is empty, as that should never happen.
func (p WeightedPages) Page() contenthub.Page {
func (p WeightedPages) Page() *WeightedPage {
if len(p) == 0 {
panic("WeightedPages is empty")
_ = fmt.Errorf("page called on empty WeightedPages")
return nil
}

return p[0]
}

func (p WeightedPages) Pages() []*WeightedPage {
pages := make([]*WeightedPage, len(p))
for i := range p {
pages[i] = p[i]
}
return pages
}

func (p WeightedPages) Sort() { sort.Stable(p) }
func (p WeightedPages) Count() int {
return len(p)
}

func (p WeightedPages) Len() int { return len(p) }
func (p WeightedPages) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (p WeightedPages) Less(i, j int) bool {
return p[i].Weight() <= p[j].Weight()
}

// OrderedTaxonomyEntry is similar to an element of a Taxonomy, but with the key embedded (as name)
// e.g: {Name: Technology, WeightedPages: TaxonomyPages}
type OrderedTaxonomyEntry struct {
Name string
WeightedPages
}

// Count returns the count the pages in this taxonomy.
func (ie OrderedTaxonomyEntry) Count() int {
return ie.WeightedPages.Count()
}

// Term returns the name given to this taxonomy.
func (ie OrderedTaxonomyEntry) Term() string {
return ie.Name
}

// ByCount returns an ordered taxonomy sorted by # of pages per key.
// If taxonomies have the same # of pages, sort them alphabetical
func (i Taxonomy) ByCount() OrderedTaxonomy {
Expand Down
15 changes: 7 additions & 8 deletions internal/domain/site/entity/page.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,12 @@ import (
bp "github.com/gohugonet/hugoverse/pkg/bufferpool"
"github.com/gohugonet/hugoverse/pkg/herrors"
"path"
"sync"
)

type Pages []Page

// Len returns the number of pages in the list.
func (p Pages) Len() int {
return len(p)
}
func (p Pages) String() string {
return fmt.Sprintf("Pages(%d)", len(p))
type WeightedPage struct {
*Page
contenthub.OrdinalWeightPage
}

type Page struct {
Expand All @@ -36,6 +32,9 @@ type Page struct {
*Site

resources []resources.Resource

dataInit sync.Once
data Data
}

func (p *Page) processResources(pageSources []contenthub.PageSource) error {
Expand Down
Loading

0 comments on commit 4035c4e

Please sign in to comment.