конфиг выделить изи апп

posts
serr 2025-04-10 21:25:50 +03:00
parent 13ca4c403b
commit 066a30ff7d
13 changed files with 188 additions and 53 deletions

2
go.mod
View File

@ -1,3 +1,5 @@
module main module main
go 1.23.2 go 1.23.2
require github.com/gomarkdown/markdown v0.0.0-20250311123330-531bef5e742b

2
go.sum Normal file
View File

@ -0,0 +1,2 @@
github.com/gomarkdown/markdown v0.0.0-20250311123330-531bef5e742b h1:EY/KpStFl60qA17CptGXhwfZ+k1sFNJIUNR8DdbcuUk=
github.com/gomarkdown/markdown v0.0.0-20250311123330-531bef5e742b/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=

View File

@ -6,6 +6,7 @@ import (
"main/mvc/controllers" "main/mvc/controllers"
controllers_pages "main/mvc/controllers/pages" controllers_pages "main/mvc/controllers/pages"
"main/mvc/models" "main/mvc/models"
models_pages "main/mvc/models/pages"
"main/tools" "main/tools"
"net/http" "net/http"
) )
@ -52,7 +53,7 @@ func setupRoutes(a *models.App) *http.ServeMux {
m := controllers.MiddlewaresChain m := controllers.MiddlewaresChain
// Обработка статических файлов // Обработка статических файлов
router.Handle(a.Config.AssetsPath, m(controllers.StaticHandler())) router.Handle(a.Config.AssetsDir, m(controllers.StaticHandler()))
// Главные странички // Главные странички
{ {
@ -62,6 +63,11 @@ func setupRoutes(a *models.App) *http.ServeMux {
router.Handle("/", m(controllers_pages.MainPageHandler(a))) router.Handle("/", m(controllers_pages.MainPageHandler(a)))
// Обработка страницы со списком постов // Обработка страницы со списком постов
router.Handle("/posts/", m(controllers_pages.PostsPageHandler(a))) router.Handle("/posts/", m(controllers_pages.PostsPageHandler(a)))
// Обработка страничек постов
for key := range models_pages.GetPosts() {
postLink := string(key)
router.Handle(postLink, m(controllers_pages.PostPageHandler(a)))
}
} }
return router return router

View File

@ -2,6 +2,7 @@ package controllers
import ( import (
"log" "log"
"main/mvc/models" "main/mvc/models"
models_pages "main/mvc/models/pages" models_pages "main/mvc/models/pages"
"main/tools" "main/tools"

View File

@ -0,0 +1,39 @@
package controllers
import (
"main/mvc/models"
models_pages "main/mvc/models/pages"
"net/http"
)
// Обработчик главной страницы
func PostPageHandler(a *models.App) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var err error
posts := models_pages.GetPosts()
// Страничка рендерится только если ее нет в кэше
pageData, ok := a.Cache.Get(models_pages.PostPageTmplName)
if !ok {
post := posts[models_pages.PostLink(r.URL.Path)]
pageData, err = models_pages.RenderPostPage(a.Templates, a.Version, post.Data)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
a.Cache.Set(models_pages.PostPageTmplName, pageData)
}
SendPostPage(w, pageData.([]byte))
})
}
// Отправляет страницу
func SendPostPage(w http.ResponseWriter, data []byte) {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.WriteHeader(http.StatusOK)
w.Write(data)
}

View File

@ -32,7 +32,7 @@ func AppInit(configPath string) (*App, error) {
} }
// Загрузка шаблонов // Загрузка шаблонов
if err := a.loadTemplates(a.Config.TemplatesPath, a.Config.TemplatesExt); err != nil { if err := a.loadTemplates(a.Config.TemplatesDir, a.Config.TemplatesExt); err != nil {
log.Fatal(err) log.Fatal(err)
} }

View File

@ -6,8 +6,9 @@ import (
) )
type Config struct { type Config struct {
AssetsPath string PostsDir string
TemplatesPath string AssetsDir string
TemplatesDir string
TemplatesExt string TemplatesExt string
LocalIP string LocalIP string
LocalPort string LocalPort string

View File

@ -1,36 +1,55 @@
package models package models
import ( import (
"bytes"
"html/template"
"time" "time"
) )
type PostName string type PostLink string
const (
// Имя соответствующего шаблона
PostPageTmplName = "post.gohtml"
)
type Post struct { type Post struct {
Name PostName // имя поста Link PostLink
Link string // ссылка на пост Preview template.HTML
Preview string // превью поста Data template.HTML
Data string // содержание CreateTimestamp int64
CreateTimestamp int64 // время создания
} }
// NewPost создает новый пост func RenderPostPage(templates *template.Template, version int64, data template.HTML) ([]byte, error) {
func NewPost(name, link, data string) *Post { var pageData bytes.Buffer
// preview - первые 500 символов содержания
var preview string context := map[string]any{
if len(data) > 500 { "version": version,
preview = data[:500] + "..." "renderingTimestamp": time.Now().Unix(),
} else { "data": data,
preview = data
} }
post := &Post{ if err := templates.ExecuteTemplate(&pageData, PostPageTmplName, context); err != nil {
Name: PostName(name), return nil, err
Link: link, }
Preview: preview,
Data: data, return pageData.Bytes(), nil
}
func NewPost(link string, data []byte) *Post {
previewBuf := make([]byte, 0, 503)
if len(data) > 500 {
previewBuf = append(previewBuf, data[:500]...)
previewBuf = append(previewBuf, '.', '.', '.')
} else {
previewBuf = append(previewBuf, data...)
}
return &Post{
Link: PostLink(link),
Preview: template.HTML(previewBuf),
Data: template.HTML(data),
CreateTimestamp: time.Now().Unix(), CreateTimestamp: time.Now().Unix(),
} }
return post
} }

View File

@ -2,7 +2,13 @@ package models
import ( import (
"bytes" "bytes"
"fmt"
"html/template" "html/template"
"io"
"log"
"main/tools"
"os"
"path/filepath"
"strings" "strings"
"time" "time"
) )
@ -12,28 +18,53 @@ const (
PostsPageTmplName = "posts.gohtml" PostsPageTmplName = "posts.gohtml"
) )
type Posts map[PostName]Post type Posts map[PostLink]*Post
var ( var (
posts = Posts{ posts = Posts{}
"post 1": *NewPost(
"post 1",
"/post-1/",
"full content 1 with more than 100 characters Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam euismod...",
),
"post 2": *NewPost(
"post 2",
"/post-2/",
"full content 2",
),
"post 3": *NewPost(
"post 3",
"/post-3/",
strings.Repeat("This is post 3 content. ", 30),
),
}
) )
func init() {
if err := LoadPosts("posts/"); err != nil {
log.Fatalf("%v", err)
}
}
func GetPosts() Posts {
return posts
}
func LoadPosts(dir string) error {
err := filepath.Walk(dir, func(path string, f os.FileInfo, err error) error {
if err != nil {
return err
}
if !f.IsDir() && strings.HasSuffix(f.Name(), ".md") {
file, err := os.Open(path)
if err != nil {
return err
}
md, err := io.ReadAll(file)
if err != nil {
return err
}
html := tools.MdToHTML(md)
link := fmt.Sprintf("/%s/", strings.TrimSuffix(filepath.Base(path), ".md"))
posts[PostLink(link)] = NewPost(link, html)
}
return nil
})
if err != nil {
return err
}
return nil
}
func RenderPostsPage(templates *template.Template, version int64) ([]byte, error) { func RenderPostsPage(templates *template.Template, version int64) ([]byte, error) {
var pageData bytes.Buffer var pageData bytes.Buffer

View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
{{ template "head" . }}
<body>
{{ template "header" . }}
<main>
<div>
{{ .data }}
</div>
</main>
{{ template "footer" . }}
</body>
</html>

View File

@ -7,9 +7,6 @@
{{ range $key, $post := .posts }} {{ range $key, $post := .posts }}
<div> <div>
<h1>
{{ $post.Name }}
</h1>
<p> <p>
{{ $post.Preview }} {{ $post.Preview }}
</p> </p>

2
posts/test.md Normal file
View File

@ -0,0 +1,2 @@
# Это тестовый пост
Этот пост был написан в файле формата .md

22
tools/parse.go Normal file
View File

@ -0,0 +1,22 @@
package tools
import (
"github.com/gomarkdown/markdown"
"github.com/gomarkdown/markdown/html"
"github.com/gomarkdown/markdown/parser"
)
// Принимает байты .md, отдает .html
func MdToHTML(md []byte) []byte {
// create markdown parser with extensions
extensions := parser.CommonExtensions | parser.NoEmptyLineBeforeBlock
p := parser.NewWithExtensions(extensions)
doc := p.Parse(md)
// create HTML renderer with extensions
htmlFlags := html.CommonFlags | html.HrefTargetBlank
opts := html.RendererOptions{Flags: htmlFlags}
renderer := html.NewRenderer(opts)
return markdown.Render(doc, renderer)
}