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

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,15 +6,16 @@ import (
) )
type Config struct { type Config struct {
AssetsPath string PostsDir string
TemplatesPath string AssetsDir string
TemplatesExt string TemplatesDir string
LocalIP string TemplatesExt string
LocalPort string LocalIP string
ServerIP string LocalPort string
ServerPort string ServerIP string
ServerDomain string ServerPort string
Port string ServerDomain string
Port string
} }
func ConfigInit() *Config { func ConfigInit() *Config {

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)
}