v2 #43
|
@ -4,7 +4,6 @@ import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"fmt"
|
"fmt"
|
||||||
"owl-blogs/app/repository"
|
"owl-blogs/app/repository"
|
||||||
"owl-blogs/config"
|
|
||||||
"owl-blogs/domain/model"
|
"owl-blogs/domain/model"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -13,11 +12,11 @@ import (
|
||||||
|
|
||||||
type AuthorService struct {
|
type AuthorService struct {
|
||||||
repo repository.AuthorRepository
|
repo repository.AuthorRepository
|
||||||
config config.Config
|
siteConfigRepo repository.SiteConfigRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAuthorService(repo repository.AuthorRepository, config config.Config) *AuthorService {
|
func NewAuthorService(repo repository.AuthorRepository, siteConfigRepo repository.SiteConfigRepository) *AuthorService {
|
||||||
return &AuthorService{repo: repo, config: config}
|
return &AuthorService{repo: repo, siteConfigRepo: siteConfigRepo}
|
||||||
}
|
}
|
||||||
|
|
||||||
func hashPassword(password string) (string, error) {
|
func hashPassword(password string) (string, error) {
|
||||||
|
@ -50,7 +49,18 @@ func (s *AuthorService) Authenticate(name string, password string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AuthorService) getSecretKey() string {
|
func (s *AuthorService) getSecretKey() string {
|
||||||
return s.config.SECRET_KEY()
|
config, err := s.siteConfigRepo.Get()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if config.Secret == "" {
|
||||||
|
config.Secret = RandStringRunes(64)
|
||||||
|
err = s.siteConfigRepo.Update(config)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return config.Secret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AuthorService) CreateToken(name string) (string, error) {
|
func (s *AuthorService) CreateToken(name string) (string, error) {
|
||||||
|
|
|
@ -2,6 +2,7 @@ package app_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"owl-blogs/app"
|
"owl-blogs/app"
|
||||||
|
"owl-blogs/domain/model"
|
||||||
"owl-blogs/infra"
|
"owl-blogs/infra"
|
||||||
"owl-blogs/test"
|
"owl-blogs/test"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -10,17 +11,25 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
type testConfig struct {
|
type testConfigRepo struct {
|
||||||
|
config model.SiteConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *testConfig) SECRET_KEY() string {
|
// Get implements repository.SiteConfigRepository.
|
||||||
return "test"
|
func (c *testConfigRepo) Get() (model.SiteConfig, error) {
|
||||||
|
return c.config, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update implements repository.SiteConfigRepository.
|
||||||
|
func (c *testConfigRepo) Update(siteConfig model.SiteConfig) error {
|
||||||
|
c.config = siteConfig
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAutherService() *app.AuthorService {
|
func getAutherService() *app.AuthorService {
|
||||||
db := test.NewMockDb()
|
db := test.NewMockDb()
|
||||||
authorRepo := infra.NewDefaultAuthorRepo(db)
|
authorRepo := infra.NewDefaultAuthorRepo(db)
|
||||||
authorService := app.NewAuthorService(authorRepo, &testConfig{})
|
authorService := app.NewAuthorService(authorRepo, &testConfigRepo{})
|
||||||
return authorService
|
return authorService
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
)
|
||||||
|
|
||||||
|
var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||||
|
|
||||||
|
func RandStringRunes(n int) string {
|
||||||
|
b := make([]rune, n)
|
||||||
|
for i := range b {
|
||||||
|
b[i] = letterRunes[rand.Intn(len(letterRunes))]
|
||||||
|
}
|
||||||
|
return string(b)
|
||||||
|
}
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"os"
|
"os"
|
||||||
"owl-blogs/app"
|
"owl-blogs/app"
|
||||||
"owl-blogs/domain/model"
|
entrytypes "owl-blogs/entry_types"
|
||||||
"owl-blogs/infra"
|
"owl-blogs/infra"
|
||||||
"owl-blogs/test"
|
"owl-blogs/test"
|
||||||
"path"
|
"path"
|
||||||
|
@ -94,8 +94,8 @@ func TestEditorFormPost(t *testing.T) {
|
||||||
id := strings.Split(resp.Header.Get("Location"), "/")[2]
|
id := strings.Split(resp.Header.Get("Location"), "/")[2]
|
||||||
entry, err := repo.FindById(id)
|
entry, err := repo.FindById(id)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, "test content", entry.MetaData().(*model.ImageMetaData).Content)
|
require.Equal(t, "test content", entry.MetaData().(*entrytypes.ImageMetaData).Content)
|
||||||
imageId := entry.MetaData().(*model.ImageMetaData).ImageId
|
imageId := entry.MetaData().(*entrytypes.ImageMetaData).ImageId
|
||||||
require.NotZero(t, imageId)
|
require.NotZero(t, imageId)
|
||||||
bin, err := binRepo.FindById(imageId)
|
bin, err := binRepo.FindById(imageId)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"owl-blogs/domain/model"
|
"owl-blogs/domain/model"
|
||||||
|
entrytypes "owl-blogs/entry_types"
|
||||||
"owl-blogs/importer"
|
"owl-blogs/importer"
|
||||||
"owl-blogs/infra"
|
"owl-blogs/infra"
|
||||||
"path"
|
"path"
|
||||||
|
@ -49,7 +50,7 @@ var importCmd = &cobra.Command{
|
||||||
files := importer.ListDir(mediaDir)
|
files := importer.ListDir(mediaDir)
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
// mock entry to pass to binary service
|
// mock entry to pass to binary service
|
||||||
entry := &model.Article{}
|
entry := &entrytypes.Article{}
|
||||||
entry.SetID(post.Id)
|
entry.SetID(post.Id)
|
||||||
|
|
||||||
fileData, err := os.ReadFile(path.Join(mediaDir, file))
|
fileData, err := os.ReadFile(path.Join(mediaDir, file))
|
||||||
|
@ -63,10 +64,10 @@ var importCmd = &cobra.Command{
|
||||||
|
|
||||||
switch post.Meta.Type {
|
switch post.Meta.Type {
|
||||||
case "article":
|
case "article":
|
||||||
entry = &model.Article{}
|
entry = &entrytypes.Article{}
|
||||||
entry.SetID(post.Id)
|
entry.SetID(post.Id)
|
||||||
entry.SetPublishedAt(&post.Meta.Date)
|
entry.SetPublishedAt(&post.Meta.Date)
|
||||||
entry.SetMetaData(&model.ArticleMetaData{
|
entry.SetMetaData(&entrytypes.ArticleMetaData{
|
||||||
Title: post.Meta.Title,
|
Title: post.Meta.Title,
|
||||||
Content: post.Content,
|
Content: post.Content,
|
||||||
})
|
})
|
||||||
|
@ -75,26 +76,26 @@ var importCmd = &cobra.Command{
|
||||||
case "reply":
|
case "reply":
|
||||||
|
|
||||||
case "photo":
|
case "photo":
|
||||||
entry = &model.Image{}
|
entry = &entrytypes.Image{}
|
||||||
entry.SetID(post.Id)
|
entry.SetID(post.Id)
|
||||||
entry.SetPublishedAt(&post.Meta.Date)
|
entry.SetPublishedAt(&post.Meta.Date)
|
||||||
entry.SetMetaData(&model.ImageMetaData{
|
entry.SetMetaData(&entrytypes.ImageMetaData{
|
||||||
Title: post.Meta.Title,
|
Title: post.Meta.Title,
|
||||||
Content: post.Content,
|
Content: post.Content,
|
||||||
ImageId: post.Meta.PhotoPath,
|
ImageId: post.Meta.PhotoPath,
|
||||||
})
|
})
|
||||||
case "note":
|
case "note":
|
||||||
entry = &model.Note{}
|
entry = &entrytypes.Note{}
|
||||||
entry.SetID(post.Id)
|
entry.SetID(post.Id)
|
||||||
entry.SetPublishedAt(&post.Meta.Date)
|
entry.SetPublishedAt(&post.Meta.Date)
|
||||||
entry.SetMetaData(&model.NoteMetaData{
|
entry.SetMetaData(&entrytypes.NoteMetaData{
|
||||||
Content: post.Content,
|
Content: post.Content,
|
||||||
})
|
})
|
||||||
case "recipe":
|
case "recipe":
|
||||||
entry = &model.Recipe{}
|
entry = &entrytypes.Recipe{}
|
||||||
entry.SetID(post.Id)
|
entry.SetID(post.Id)
|
||||||
entry.SetPublishedAt(&post.Meta.Date)
|
entry.SetPublishedAt(&post.Meta.Date)
|
||||||
entry.SetMetaData(&model.RecipeMetaData{
|
entry.SetMetaData(&entrytypes.RecipeMetaData{
|
||||||
Title: post.Meta.Title,
|
Title: post.Meta.Title,
|
||||||
Yield: post.Meta.Recipe.Yield,
|
Yield: post.Meta.Recipe.Yield,
|
||||||
Duration: post.Meta.Recipe.Duration,
|
Duration: post.Meta.Recipe.Duration,
|
||||||
|
@ -102,10 +103,10 @@ var importCmd = &cobra.Command{
|
||||||
Content: post.Content,
|
Content: post.Content,
|
||||||
})
|
})
|
||||||
case "page":
|
case "page":
|
||||||
entry = &model.Page{}
|
entry = &entrytypes.Page{}
|
||||||
entry.SetID(post.Id)
|
entry.SetID(post.Id)
|
||||||
entry.SetPublishedAt(&post.Meta.Date)
|
entry.SetPublishedAt(&post.Meta.Date)
|
||||||
entry.SetMetaData(&model.PageMetaData{
|
entry.SetMetaData(&entrytypes.PageMetaData{
|
||||||
Title: post.Meta.Title,
|
Title: post.Meta.Title,
|
||||||
Content: post.Content,
|
Content: post.Content,
|
||||||
})
|
})
|
||||||
|
|
|
@ -4,8 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"owl-blogs/app"
|
"owl-blogs/app"
|
||||||
"owl-blogs/config"
|
entrytypes "owl-blogs/entry_types"
|
||||||
"owl-blogs/domain/model"
|
|
||||||
"owl-blogs/infra"
|
"owl-blogs/infra"
|
||||||
"owl-blogs/web"
|
"owl-blogs/web"
|
||||||
|
|
||||||
|
@ -27,22 +26,21 @@ func Execute() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func App(db infra.Database) *web.WebApp {
|
func App(db infra.Database) *web.WebApp {
|
||||||
config := config.NewConfig()
|
|
||||||
|
|
||||||
registry := app.NewEntryTypeRegistry()
|
registry := app.NewEntryTypeRegistry()
|
||||||
registry.Register(&model.Image{})
|
registry.Register(&entrytypes.Image{})
|
||||||
registry.Register(&model.Article{})
|
registry.Register(&entrytypes.Article{})
|
||||||
registry.Register(&model.Page{})
|
registry.Register(&entrytypes.Page{})
|
||||||
registry.Register(&model.Recipe{})
|
registry.Register(&entrytypes.Recipe{})
|
||||||
registry.Register(&model.Note{})
|
registry.Register(&entrytypes.Note{})
|
||||||
|
|
||||||
entryRepo := infra.NewEntryRepository(db, registry)
|
entryRepo := infra.NewEntryRepository(db, registry)
|
||||||
binRepo := infra.NewBinaryFileRepo(db)
|
binRepo := infra.NewBinaryFileRepo(db)
|
||||||
authorRepo := infra.NewDefaultAuthorRepo(db)
|
authorRepo := infra.NewDefaultAuthorRepo(db)
|
||||||
|
siteConfigRepo := infra.NewSiteConfigRepo(db)
|
||||||
|
|
||||||
entryService := app.NewEntryService(entryRepo)
|
entryService := app.NewEntryService(entryRepo)
|
||||||
binaryService := app.NewBinaryFileService(binRepo)
|
binaryService := app.NewBinaryFileService(binRepo)
|
||||||
authorService := app.NewAuthorService(authorRepo, config)
|
authorService := app.NewAuthorService(authorRepo, siteConfigRepo)
|
||||||
|
|
||||||
return web.NewWebApp(entryService, registry, binaryService, authorService)
|
return web.NewWebApp(entryService, registry, binaryService, authorService)
|
||||||
|
|
||||||
|
|
|
@ -3,11 +3,9 @@ package config
|
||||||
import "os"
|
import "os"
|
||||||
|
|
||||||
type Config interface {
|
type Config interface {
|
||||||
SECRET_KEY() string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type EnvConfig struct {
|
type EnvConfig struct {
|
||||||
secretKey string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getEnvOrPanic(key string) string {
|
func getEnvOrPanic(key string) string {
|
||||||
|
@ -19,11 +17,5 @@ func getEnvOrPanic(key string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewConfig() Config {
|
func NewConfig() Config {
|
||||||
return &EnvConfig{
|
return &EnvConfig{}
|
||||||
secretKey: getEnvOrPanic("OWL_SECRET_KEY"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *EnvConfig) SECRET_KEY() string {
|
|
||||||
return c.secretKey
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,4 +30,5 @@ type SiteConfig struct {
|
||||||
HeaderMenu []MenuItem
|
HeaderMenu []MenuItem
|
||||||
FooterMenu []MenuItem
|
FooterMenu []MenuItem
|
||||||
Secret string
|
Secret string
|
||||||
|
AvatarUrl string
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
package model
|
package entrytypes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"owl-blogs/domain/model"
|
||||||
"owl-blogs/render"
|
"owl-blogs/render"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Article struct {
|
type Article struct {
|
||||||
EntryBase
|
model.EntryBase
|
||||||
meta ArticleMetaData
|
meta ArticleMetaData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,12 +20,12 @@ func (e *Article) Title() string {
|
||||||
return e.meta.Title
|
return e.meta.Title
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Article) Content() EntryContent {
|
func (e *Article) Content() model.EntryContent {
|
||||||
str, err := render.RenderTemplateToString("entry/Article", e)
|
str, err := render.RenderTemplateToString("entry/Article", e)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
return EntryContent(str)
|
return model.EntryContent(str)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Article) MetaData() interface{} {
|
func (e *Article) MetaData() interface{} {
|
|
@ -1,12 +1,13 @@
|
||||||
package model
|
package entrytypes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"owl-blogs/domain/model"
|
||||||
"owl-blogs/render"
|
"owl-blogs/render"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Image struct {
|
type Image struct {
|
||||||
EntryBase
|
model.EntryBase
|
||||||
meta ImageMetaData
|
meta ImageMetaData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,12 +21,12 @@ func (e *Image) Title() string {
|
||||||
return e.meta.Title
|
return e.meta.Title
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Image) Content() EntryContent {
|
func (e *Image) Content() model.EntryContent {
|
||||||
str, err := render.RenderTemplateToString("entry/Image", e)
|
str, err := render.RenderTemplateToString("entry/Image", e)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
return EntryContent(str)
|
return model.EntryContent(str)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Image) MetaData() interface{} {
|
func (e *Image) MetaData() interface{} {
|
|
@ -1,7 +1,9 @@
|
||||||
package model
|
package entrytypes
|
||||||
|
|
||||||
|
import "owl-blogs/domain/model"
|
||||||
|
|
||||||
type Note struct {
|
type Note struct {
|
||||||
EntryBase
|
model.EntryBase
|
||||||
meta NoteMetaData
|
meta NoteMetaData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,8 +15,8 @@ func (e *Note) Title() string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Note) Content() EntryContent {
|
func (e *Note) Content() model.EntryContent {
|
||||||
return EntryContent(e.meta.Content)
|
return model.EntryContent(e.meta.Content)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Note) MetaData() interface{} {
|
func (e *Note) MetaData() interface{} {
|
|
@ -1,12 +1,13 @@
|
||||||
package model
|
package entrytypes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"owl-blogs/domain/model"
|
||||||
"owl-blogs/render"
|
"owl-blogs/render"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Page struct {
|
type Page struct {
|
||||||
EntryBase
|
model.EntryBase
|
||||||
meta PageMetaData
|
meta PageMetaData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,12 +20,12 @@ func (e *Page) Title() string {
|
||||||
return e.meta.Title
|
return e.meta.Title
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Page) Content() EntryContent {
|
func (e *Page) Content() model.EntryContent {
|
||||||
str, err := render.RenderTemplateToString("entry/Page", e)
|
str, err := render.RenderTemplateToString("entry/Page", e)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
return EntryContent(str)
|
return model.EntryContent(str)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Page) MetaData() interface{} {
|
func (e *Page) MetaData() interface{} {
|
|
@ -1,12 +1,13 @@
|
||||||
package model
|
package entrytypes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"owl-blogs/domain/model"
|
||||||
"owl-blogs/render"
|
"owl-blogs/render"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Recipe struct {
|
type Recipe struct {
|
||||||
EntryBase
|
model.EntryBase
|
||||||
meta RecipeMetaData
|
meta RecipeMetaData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,12 +23,12 @@ func (e *Recipe) Title() string {
|
||||||
return e.meta.Title
|
return e.meta.Title
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Recipe) Content() EntryContent {
|
func (e *Recipe) Content() model.EntryContent {
|
||||||
str, err := render.RenderTemplateToString("entry/Recipe", e)
|
str, err := render.RenderTemplateToString("entry/Recipe", e)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
return EntryContent(str)
|
return model.EntryContent(str)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Recipe) MetaData() interface{} {
|
func (e *Recipe) MetaData() interface{} {
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"embed"
|
"embed"
|
||||||
"io"
|
"io"
|
||||||
|
"owl-blogs/domain/model"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
"github.com/yuin/goldmark"
|
"github.com/yuin/goldmark"
|
||||||
|
@ -12,6 +13,11 @@ import (
|
||||||
"github.com/yuin/goldmark/renderer/html"
|
"github.com/yuin/goldmark/renderer/html"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type TemplateData struct {
|
||||||
|
Data interface{}
|
||||||
|
SiteConfig model.SiteConfig
|
||||||
|
}
|
||||||
|
|
||||||
//go:embed templates
|
//go:embed templates
|
||||||
var templates embed.FS
|
var templates embed.FS
|
||||||
|
|
||||||
|
@ -42,7 +48,10 @@ func RenderTemplateWithBase(w io.Writer, templateName string, data interface{})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = t.ExecuteTemplate(w, "base", data)
|
err = t.ExecuteTemplate(w, "base", TemplateData{
|
||||||
|
Data: data,
|
||||||
|
SiteConfig: model.SiteConfig{},
|
||||||
|
})
|
||||||
|
|
||||||
return err
|
return err
|
||||||
|
|
||||||
|
|
|
@ -3,15 +3,94 @@
|
||||||
<html lang='en'>
|
<html lang='en'>
|
||||||
<head>
|
<head>
|
||||||
<meta charset='utf-8'>
|
<meta charset='utf-8'>
|
||||||
<title>{{template "title" .}} - Owl Blog</title>
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<title>{{template "title" .Data}} - {{ .SiteConfig.Title }}</title>
|
||||||
|
<meta property="og:title" content="{{template "title" .Data}}" />
|
||||||
|
|
||||||
<link rel='stylesheet' href='/static/pico.min.css'>
|
<link rel='stylesheet' href='/static/pico.min.css'>
|
||||||
|
<style>
|
||||||
|
header {
|
||||||
|
background-color: {{.SiteConfig.HeaderColor}};
|
||||||
|
padding-bottom: 1rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
border-top: dashed 2px;
|
||||||
|
border-color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
float: left;
|
||||||
|
margin-right: 1rem;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row wrap;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-title {
|
||||||
|
order: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-profile {
|
||||||
|
order: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
hgroup h2 a { color: inherit; }
|
||||||
|
|
||||||
|
.photo-grid {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
padding: 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-grid-item {
|
||||||
|
flex: 1 0 25%;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-grid-item img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
aspect-ratio: 1 / 1 ;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="container">
|
<header>
|
||||||
<a href="/">Owl Blog</a>
|
<div class="container header h-card">
|
||||||
|
<hgroup class="header-title">
|
||||||
|
<h2><a class="p-name u-url" href="/">{{ .SiteConfig.Title }}</a></h2>
|
||||||
|
<h3 class="p-note">{{ .SiteConfig.SubTitle }}</h3>
|
||||||
|
</hgroup>
|
||||||
|
|
||||||
|
<div class="header-profile">
|
||||||
|
{{ if .SiteConfig.AvatarUrl }}
|
||||||
|
<img class="u-photo u-logo avatar" src="{{ .SiteConfig.AvatarUrl }}" alt="{{ .SiteConfig.Title }}" width="100" height="100" />
|
||||||
|
{{ end }}
|
||||||
|
<div style="float: right; list-style: none;">
|
||||||
|
{{ range $me := .SiteConfig.Me }}
|
||||||
|
<li><a href="{{$me.Url}}" rel="me">{{$me.Name}}</a>
|
||||||
|
</li>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="container">
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<main class="container">
|
<main class="container">
|
||||||
{{template "main" .}}
|
{{template "main" .Data}}
|
||||||
</main>
|
</main>
|
||||||
<footer class="container">
|
<footer class="container">
|
||||||
Powered by <a href='https://golang.org/'>Go</a>
|
Powered by <a href='https://golang.org/'>Go</a>
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
{{define "title"}}Editor{{end}}
|
||||||
|
|
||||||
|
{{define "main"}}
|
||||||
|
|
||||||
|
<a href="/editor">Back</a>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
{{.}}
|
||||||
|
|
||||||
|
{{end}}
|
|
@ -3,6 +3,7 @@ package web
|
||||||
import (
|
import (
|
||||||
"owl-blogs/app"
|
"owl-blogs/app"
|
||||||
"owl-blogs/domain/model"
|
"owl-blogs/domain/model"
|
||||||
|
"owl-blogs/render"
|
||||||
"owl-blogs/web/editor"
|
"owl-blogs/web/editor"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -49,7 +50,7 @@ func (h *EditorHandler) HandleGet(c *fiber.Ctx) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return c.SendString(htmlForm)
|
return render.RenderTemplateWithBase(c, "views/editor", htmlForm)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *EditorHandler) HandlePost(c *fiber.Ctx) error {
|
func (h *EditorHandler) HandlePost(c *fiber.Ctx) error {
|
||||||
|
|
Loading…
Reference in New Issue