Have got site creation working
This commit is contained in:
parent
4ecc12f035
commit
f8e7ea482b
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
.idea
|
||||
build/
|
22
config/config.go
Normal file
22
config/config.go
Normal file
|
@ -0,0 +1,22 @@
|
|||
package config
|
||||
|
||||
import "path/filepath"
|
||||
|
||||
type Config struct {
|
||||
DatabaseURL string `env:"DATABASE_URL"`
|
||||
|
||||
DataDir string `env:"DATA_DIR"`
|
||||
DataStagingDir string `env:"DATA_STAGING_DIR,default=staging"`
|
||||
}
|
||||
|
||||
func Load() (Config, error) {
|
||||
return Config{
|
||||
DatabaseURL: "postgres://postgres:postgres@localhost:5432/postgres?sslmode=disable",
|
||||
DataDir: "build/data",
|
||||
DataStagingDir: "staging",
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c Config) StagingDir() string {
|
||||
return filepath.Join(c.DataDir, c.DataStagingDir)
|
||||
}
|
28
main.go
28
main.go
|
@ -2,18 +2,37 @@ package main
|
|||
|
||||
import (
|
||||
"context"
|
||||
"lmika.dev/lmika/hugo-crm/models"
|
||||
"lmika.dev/lmika/hugo-crm/config"
|
||||
"lmika.dev/lmika/hugo-crm/providers/db"
|
||||
"lmika.dev/lmika/hugo-crm/providers/git"
|
||||
"lmika.dev/lmika/hugo-crm/providers/hugo"
|
||||
"lmika.dev/lmika/hugo-crm/providers/themes"
|
||||
"lmika.dev/lmika/hugo-crm/services/sites"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
dbp, err := db.New("postgres://postgres:postgres@localhost:5432/postgres?sslmode=disable")
|
||||
cfg, err := config.Load()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
dbp, err := db.New(cfg.DatabaseURL)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer dbp.Close()
|
||||
|
||||
hugoProvider, err := hugo.New(cfg.StagingDir())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
gitProvider := git.New()
|
||||
themesProvider := themes.New()
|
||||
|
||||
siteService := sites.NewService(cfg, dbp, themesProvider, gitProvider, hugoProvider)
|
||||
|
||||
log.Println("Connected to database")
|
||||
if err := dbp.Migrate(context.Background()); err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -21,10 +40,7 @@ func main() {
|
|||
|
||||
log.Println("Database migrated")
|
||||
|
||||
if err := dbp.InsertSite(context.Background(), &models.Site{
|
||||
Name: "Test site",
|
||||
URL: "https://www.testsite.com",
|
||||
}); err != nil {
|
||||
if _, err := siteService.CreateSite(context.Background(), "Test site "+time.Now().Format("2006-01-02T15:04:05")); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package models
|
||||
|
||||
type Site struct {
|
||||
ID int64
|
||||
Name string
|
||||
URL string
|
||||
ID int64
|
||||
Name string
|
||||
Title string
|
||||
URL string
|
||||
Theme string
|
||||
}
|
||||
|
|
6
models/theme.go
Normal file
6
models/theme.go
Normal file
|
@ -0,0 +1,6 @@
|
|||
package models
|
||||
|
||||
type ThemeMeta struct {
|
||||
Name string `json:"name"`
|
||||
URL string `json:"repo"`
|
||||
}
|
|
@ -10,7 +10,7 @@ func (db *DB) InsertSite(ctx context.Context, site *models.Site) error {
|
|||
id, err := db.q.NewSite(ctx, dbq.NewSiteParams{
|
||||
Name: site.Name,
|
||||
Url: site.URL,
|
||||
Theme: "default",
|
||||
Theme: site.Theme,
|
||||
Props: []byte("{}"),
|
||||
})
|
||||
if err != nil {
|
||||
|
|
17
providers/git/provider.go
Normal file
17
providers/git/provider.go
Normal file
|
@ -0,0 +1,17 @@
|
|||
package git
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
type Provider struct {
|
||||
}
|
||||
|
||||
func New() *Provider {
|
||||
return &Provider{}
|
||||
}
|
||||
|
||||
func (p *Provider) Clone(ctx context.Context, url string, targetDir string) error {
|
||||
return exec.CommandContext(ctx, "git", "clone", url, targetDir).Run()
|
||||
}
|
8
providers/hugo/dirs.go
Normal file
8
providers/hugo/dirs.go
Normal file
|
@ -0,0 +1,8 @@
|
|||
package hugo
|
||||
|
||||
type SiteDir string
|
||||
|
||||
const (
|
||||
BaseSiteDir SiteDir = "base"
|
||||
ThemeSiteDir SiteDir = "theme"
|
||||
)
|
70
providers/hugo/provider.go
Normal file
70
providers/hugo/provider.go
Normal file
|
@ -0,0 +1,70 @@
|
|||
package hugo
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"lmika.dev/lmika/hugo-crm/models"
|
||||
"lmika.dev/lmika/hugo-crm/providers/hugo/tmpls"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
type Provider struct {
|
||||
stagingDir string
|
||||
tmpls *template.Template
|
||||
}
|
||||
|
||||
func New(stagingDir string) (*Provider, error) {
|
||||
ts, err := template.ParseFS(tmpls.FS, "*.tmpl")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Provider{
|
||||
stagingDir: stagingDir,
|
||||
tmpls: ts,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Provider) SiteStagingDir(site models.Site, what SiteDir) string {
|
||||
switch what {
|
||||
case BaseSiteDir:
|
||||
return filepath.Join(p.stagingDir, site.Name)
|
||||
case ThemeSiteDir:
|
||||
return filepath.Join(p.stagingDir, site.Name, "themes", site.Theme)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (p *Provider) NewSite(ctx context.Context, site models.Site) error {
|
||||
log.Printf(" .. %v", p.SiteStagingDir(site, BaseSiteDir))
|
||||
if err := os.MkdirAll(p.SiteStagingDir(site, BaseSiteDir), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create the new site
|
||||
if err := exec.CommandContext(ctx, "hugo", "new", "site", p.SiteStagingDir(site, BaseSiteDir)).Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Provider) ReconfigureSite(ctx context.Context, site models.Site) error {
|
||||
// Reconfigure the site
|
||||
var hugoCfg bytes.Buffer
|
||||
if err := p.tmpls.ExecuteTemplate(&hugoCfg, "config.toml.tmpl", struct {
|
||||
Site models.Site
|
||||
}{
|
||||
Site: site,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.WriteFile(filepath.Join(p.SiteStagingDir(site, BaseSiteDir), "hugo.toml"), hugoCfg.Bytes(), 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
5
providers/hugo/tmpls/config.toml.tmpl
Normal file
5
providers/hugo/tmpls/config.toml.tmpl
Normal file
|
@ -0,0 +1,5 @@
|
|||
baseURL = {{.Site.URL | printf "%q"}}
|
||||
languageCode = 'en-us'
|
||||
title = {{.Site.Title | printf "%q"}}
|
||||
|
||||
theme = {{.Site.Theme | printf "%q"}}
|
6
providers/hugo/tmpls/fs.go
Normal file
6
providers/hugo/tmpls/fs.go
Normal file
|
@ -0,0 +1,6 @@
|
|||
package tmpls
|
||||
|
||||
import "embed"
|
||||
|
||||
//go:embed *.tmpl
|
||||
var FS embed.FS
|
10
providers/themes/meta.go
Normal file
10
providers/themes/meta.go
Normal file
|
@ -0,0 +1,10 @@
|
|||
package themes
|
||||
|
||||
import "lmika.dev/lmika/hugo-crm/models"
|
||||
|
||||
var themes = map[string]models.ThemeMeta{
|
||||
"bear": models.ThemeMeta{
|
||||
Name: "bear",
|
||||
URL: "https://github.com/janraasch/hugo-bearblog",
|
||||
},
|
||||
}
|
14
providers/themes/provider.go
Normal file
14
providers/themes/provider.go
Normal file
|
@ -0,0 +1,14 @@
|
|||
package themes
|
||||
|
||||
import "lmika.dev/lmika/hugo-crm/models"
|
||||
|
||||
type Provider struct{}
|
||||
|
||||
func New() *Provider {
|
||||
return &Provider{}
|
||||
}
|
||||
|
||||
func (p *Provider) Lookup(name string) (models.ThemeMeta, bool) {
|
||||
t, ok := themes[name]
|
||||
return t, ok
|
||||
}
|
91
services/sites/service.go
Normal file
91
services/sites/service.go
Normal file
|
@ -0,0 +1,91 @@
|
|||
package sites
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"lmika.dev/lmika/hugo-crm/config"
|
||||
"lmika.dev/lmika/hugo-crm/models"
|
||||
"lmika.dev/lmika/hugo-crm/providers/db"
|
||||
"lmika.dev/lmika/hugo-crm/providers/git"
|
||||
"lmika.dev/lmika/hugo-crm/providers/hugo"
|
||||
"lmika.dev/lmika/hugo-crm/providers/themes"
|
||||
"log"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
cfg config.Config
|
||||
db *db.DB
|
||||
themes *themes.Provider
|
||||
git *git.Provider
|
||||
hugo *hugo.Provider
|
||||
}
|
||||
|
||||
func NewService(
|
||||
cfg config.Config,
|
||||
db *db.DB,
|
||||
themes *themes.Provider,
|
||||
git *git.Provider,
|
||||
hugo *hugo.Provider,
|
||||
) *Service {
|
||||
return &Service{
|
||||
cfg: cfg,
|
||||
db: db,
|
||||
git: git,
|
||||
hugo: hugo,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) CreateSite(ctx context.Context, name string) (models.Site, error) {
|
||||
newSite := models.Site{
|
||||
Name: normaliseName(name),
|
||||
Title: name,
|
||||
Theme: "bear",
|
||||
}
|
||||
|
||||
themeMeta, 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
|
||||
}
|
||||
|
||||
// Build the site
|
||||
log.Printf(" .. build")
|
||||
if err := s.hugo.NewSite(ctx, newSite); err != nil {
|
||||
return models.Site{}, err
|
||||
}
|
||||
|
||||
// Setup the theme
|
||||
log.Printf(" .. theme")
|
||||
if err := s.git.Clone(ctx, themeMeta.URL, s.hugo.SiteStagingDir(newSite, hugo.ThemeSiteDir)); err != nil {
|
||||
return models.Site{}, err
|
||||
}
|
||||
|
||||
if err := s.hugo.ReconfigureSite(ctx, newSite); err != nil {
|
||||
return models.Site{}, err
|
||||
}
|
||||
|
||||
return newSite, nil
|
||||
}
|
||||
|
||||
func normaliseName(name string) string {
|
||||
var sb strings.Builder
|
||||
|
||||
seenDash := false
|
||||
for _, r := range strings.TrimSpace(name) {
|
||||
if unicode.IsLetter(r) || unicode.IsNumber(r) {
|
||||
sb.WriteRune(unicode.ToLower(r))
|
||||
seenDash = false
|
||||
} else if r == '_' || r == '-' || r == '.' || unicode.IsSpace(r) {
|
||||
if !seenDash {
|
||||
sb.WriteRune('-')
|
||||
}
|
||||
seenDash = true
|
||||
}
|
||||
}
|
||||
return sb.String()
|
||||
}
|
|
@ -4,8 +4,9 @@ SELECT * FROM site;
|
|||
-- name: NewSite :one
|
||||
INSERT INTO site (
|
||||
name,
|
||||
title,
|
||||
url,
|
||||
theme,
|
||||
props
|
||||
) VALUES ($1, $2, $3, $4)
|
||||
) VALUES ($1, $2, $3, $4, $5)
|
||||
RETURNING id;
|
Loading…
Reference in a new issue