posts
serr 2025-04-11 11:12:38 +03:00
parent b68851c862
commit cb0358dbb4
10 changed files with 115 additions and 124 deletions

50
main.go
View File

@ -6,75 +6,57 @@ import (
"main/mvc/controllers"
"main/mvc/controllers/controllers_pages"
"main/mvc/models"
"main/mvc/models/models_pages"
"main/tools"
"net/http"
)
func main() {
var err error
// Инициализация конфига
if err := models.Cfg.Load("config.json"); err != nil {
log.Fatal(err)
}
var app *models.App
// Загрузка постов
if err := models_pages.LoadPosts(models.Cfg.PostsDir); err != nil {
log.Fatal(err)
}
// Инициализация приложения (обязательно после инициализации конфига)
if err := models.InitApp(); err != nil {
// Инициализация приложения
if app, err = models.InitApp(); err != nil {
log.Fatal(err)
}
// Добавление префикса в виде домена сервера к записям в лог
log.SetPrefix(fmt.Sprintf("%s | ", models.Cfg.ServerDomain))
log.SetPrefix(fmt.Sprintf("%s | ", app.Cfg.ServerDomain))
// Настройка маршрутов и запуск
if setupRoutesAndRun() != nil {
log.Fatal(err)
}
}
func setupRoutesAndRun() error {
// Настройка маршрутов
router := setupRoutes()
router := setupRoutes(app)
// Запуск сервера
if ok, err := tools.IsIPInUse(models.Cfg.ServerIP); err != nil {
return err
if ok, err := tools.IsIPInUse(app.Cfg.ServerIP); err != nil {
log.Fatal(err)
} else if ok {
runServer(models.Cfg.ServerIP, models.Cfg.ServerPort, router)
runServer(app.Cfg.ServerIP, app.Cfg.ServerPort, router)
} else {
runServer(models.Cfg.LocalIP, models.Cfg.LocalPort, router)
runServer(app.Cfg.LocalIP, app.Cfg.LocalPort, router)
}
return nil
}
// Настраивает маршруты
func setupRoutes() *http.ServeMux {
func setupRoutes(app *models.App) *http.ServeMux {
router := http.NewServeMux()
// Цепочка обработчиков, которые сработают до отдачи страницы юзеру
m := controllers.MiddlewaresChain
// Обработка статических файлов
router.Handle(models.Cfg.AssetsDir, m(controllers.StaticHandler()))
router.Handle(app.Cfg.AssetsDir, m(controllers.StaticHandler()))
// Главные странички
{
// Обработка главной страницы (русская версия)
router.Handle("/ru/", m(controllers_pages.MainRuPageHandler()))
router.Handle("/ru/", m(controllers_pages.MainRuPageHandler(app)))
// Обработка главной страницы
router.Handle("/", m(controllers_pages.MainPageHandler()))
router.Handle("/", m(controllers_pages.MainPageHandler(app)))
// Обработка страницы со списком постов
router.Handle("/posts/", m(controllers_pages.PostsPageHandler()))
router.Handle("/posts/", m(controllers_pages.PostsPageHandler(app)))
// Обработка страничек постов
for key := range models_pages.GetPosts() {
for key := range app.Posts {
postLink := string(key)
router.Handle(postLink, m(controllers_pages.PostPageHandler()))
router.Handle(postLink, m(controllers_pages.PostPageHandler(app)))
}
}

View File

@ -10,7 +10,7 @@ import (
)
// Обработчик главной страницы
func MainPageHandler() http.HandlerFunc {
func MainPageHandler(app *models.App) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var err error
@ -18,7 +18,7 @@ func MainPageHandler() http.HandlerFunc {
// Количество запросов, обработанных сервером за 24ч
if r.Method == "COUNT" {
var count []byte
if count, err = tools.GetJournalctlLogsCount("server", models.Cfg.ServerDomain, 24); err != nil {
if count, err = tools.GetJournalctlLogsCount("server", app.Cfg.ServerDomain, 24); err != nil {
log.Printf("%s", err.Error())
}
sendCount(w, count)
@ -38,14 +38,14 @@ func MainPageHandler() http.HandlerFunc {
}
// Страничка рендерится только если ее нет в кэше
pageData, ok := models.PagesCache.Get(models_pages.MainPageTmplName)
pageData, ok := app.PagesCache.Get(models_pages.MainPageTmplName)
if !ok {
pageData, err = models_pages.RenderMainPage(models.App.Templates, models.App.Version)
pageData, err = models_pages.RenderMainPage(app.Templates, app.Version)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
models.PagesCache.Set(models_pages.MainPageTmplName, pageData)
app.PagesCache.Set(models_pages.MainPageTmplName, pageData)
}
sendMainPage(w, pageData.([]byte))

View File

@ -7,19 +7,19 @@ import (
)
// Обработчик главной страницы
func MainRuPageHandler() http.HandlerFunc {
func MainRuPageHandler(app *models.App) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var err error
// Страничка рендерится только если ее нет в кэше
pageData, ok := models.PagesCache.Get(models_pages.MainRuPageTmplName)
pageData, ok := app.PagesCache.Get(models_pages.MainRuPageTmplName)
if !ok {
pageData, err = models_pages.RenderMainRuPage(models.App.Templates, models.App.Version)
pageData, err = models_pages.RenderMainRuPage(app.Templates, app.Version)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
models.PagesCache.Set(models_pages.MainRuPageTmplName, pageData)
app.PagesCache.Set(models_pages.MainRuPageTmplName, pageData)
}
sendMainRuPage(w, pageData.([]byte))

View File

@ -7,24 +7,22 @@ import (
)
// Обработчик главной страницы
func PostPageHandler() http.HandlerFunc {
func PostPageHandler(app *models.App) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var err error
posts := models_pages.GetPosts()
// Страничка рендерится только если ее нет в кэше
pageData, ok := models.PagesCache.Get(models_pages.PostPageTmplName)
pageData, ok := app.PagesCache.Get(models_pages.PostPageTmplName)
if !ok {
post := posts[models_pages.PostLink(r.URL.Path)]
post := app.Posts[models_pages.PostLink(r.URL.Path)]
pageData, err = models_pages.RenderPostPage(models.App.Templates, models.App.Version, post.Data)
pageData, err = models_pages.RenderPostPage(app.Templates, app.Version, post.Data)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
models.PagesCache.Set(models_pages.PostPageTmplName, pageData)
app.PagesCache.Set(models_pages.PostPageTmplName, pageData)
}
sendPostPage(w, pageData.([]byte))

View File

@ -7,19 +7,19 @@ import (
)
// Обработчик главной страницы
func PostsPageHandler() http.HandlerFunc {
func PostsPageHandler(app *models.App) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var err error
// Страничка рендерится только если ее нет в кэше
pageData, ok := models.PagesCache.Get(models_pages.PostsPageTmplName)
pageData, ok := app.PagesCache.Get(models_pages.PostsPageTmplName)
if !ok {
pageData, err = models_pages.RenderPostsPage(models.App.Templates, models.App.Version)
pageData, err = app.Posts.RenderPostsPage(app.Templates, app.Version)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
models.PagesCache.Set(models_pages.PostsPageTmplName, pageData)
app.PagesCache.Set(models_pages.PostsPageTmplName, pageData)
}
sendPostsPage(w, pageData.([]byte))

View File

@ -2,55 +2,39 @@ package models
import (
"html/template"
"os"
"path/filepath"
"strings"
"main/mvc/models/models_pages"
"time"
)
// App хранит шаблоны и время запуска
type app struct {
Templates *template.Template // Шаблоны страниц
Version int64 // Время запуска
type App struct {
Cfg *Config // Сонфиг
Posts models_pages.Posts // Посты
Templates *template.Template // Шаблоны страниц
PagesCache *Cache // Кэш (отрендеренные странички)
Version int64 // Время запуска
}
var (
App = &app{}
)
// Инициализирует приложение
func InitApp() error {
func InitApp() (*App, error) {
var err error
App.Version = time.Now().Unix()
app := &App{}
// Версия чтобы статика не кэшировалась
app.Version = time.Now().Unix()
// Загрузка конфига
if app.Cfg, err = loadConfig(ConfigPath); err != nil {
return nil, err
}
// Загрузка постов
if app.Posts, err = models_pages.LoadPosts(app.Cfg.PostsDir); err != nil {
return nil, err
}
// Загрузка шаблонов
if err := App.loadTemplates(Cfg.TemplatesDir, Cfg.TemplatesExt); err != nil {
return err
if app.Templates, err = loadTemplates(app.Cfg.TemplatesDir, app.Cfg.TemplatesExt); err != nil {
return nil, err
}
return nil
}
// Загрузка шаблонов
func (a *app) loadTemplates(templatesPath string, ext string) error {
tmpls := template.New("")
err := filepath.Walk(templatesPath, func(path string, f os.FileInfo, err error) error {
if err != nil {
return err
}
if !f.IsDir() && strings.HasSuffix(f.Name(), ext) {
_, err = tmpls.ParseFiles(path)
if err != nil {
return err
}
}
return nil
})
if err != nil {
return err
}
a.Templates = tmpls
return nil
// Инициализация кэша
app.PagesCache = initCache()
return app, nil
}

View File

@ -2,23 +2,23 @@ package models
import "sync"
type cache struct {
type Cache struct {
Data map[string]any
Mu sync.RWMutex
}
var (
PagesCache = cache{Data: make(map[string]any)}
)
func initCache() *Cache {
return &Cache{Data: make(map[string]any)}
}
func (c *cache) Get(key string) (any, bool) {
func (c *Cache) Get(key string) (any, bool) {
c.Mu.RLock()
pageData, ok := c.Data[key]
c.Mu.RUnlock()
return pageData, ok
}
func (c *cache) Set(key string, data any) {
func (c *Cache) Set(key string, data any) {
c.Mu.Lock()
c.Data[key] = data
c.Mu.Unlock()

View File

@ -5,7 +5,11 @@ import (
"os"
)
type config struct {
const (
ConfigPath = "config.json"
)
type Config struct {
PostsDir string
AssetsDir string
TemplatesDir string
@ -18,18 +22,15 @@ type config struct {
Port string
}
var (
Cfg = &config{}
)
func (c *config) Load(configPath string) error {
func loadConfig(configPath string) (*Config, error) {
cfg := &Config{}
configFile, err := os.ReadFile(configPath)
if err != nil {
return err
return nil, err
}
err = json.Unmarshal(configFile, c)
err = json.Unmarshal(configFile, cfg)
if err != nil {
return err
return nil, err
}
return nil
return cfg, nil
}

View File

@ -17,17 +17,12 @@ const (
PostsPageTmplName = "posts.gohtml"
)
type posts map[PostLink]*Post
type Posts map[PostLink]*Post
var (
allPosts = posts{}
)
func LoadPosts(dir string) (Posts, error) {
func GetPosts() posts {
return allPosts
}
posts := Posts{}
func LoadPosts(dir string) error {
err := filepath.Walk(dir, func(path string, f os.FileInfo, err error) error {
if err != nil {
return err
@ -46,25 +41,25 @@ func LoadPosts(dir string) error {
html := tools.MdToHTML(md)
link := fmt.Sprintf("/%s/", strings.TrimSuffix(filepath.Base(path), ".md"))
allPosts[PostLink(link)] = newPost(link, html)
posts[PostLink(link)] = newPost(link, html)
}
return nil
})
if err != nil {
return err
return nil, err
}
return nil
return posts, nil
}
func RenderPostsPage(templates *template.Template, version int64) ([]byte, error) {
func (p *Posts) RenderPostsPage(templates *template.Template, version int64) ([]byte, error) {
var pageData bytes.Buffer
context := map[string]any{
"version": version,
"renderingTimestamp": time.Now().Unix(),
"posts": allPosts,
"posts": p,
}
if err := templates.ExecuteTemplate(&pageData, PostsPageTmplName, context); err != nil {

31
mvc/models/templates.go Normal file
View File

@ -0,0 +1,31 @@
package models
import (
"html/template"
"os"
"path/filepath"
"strings"
)
func loadTemplates(templatesPath string, ext string) (*template.Template, error) {
tmpls := template.New("")
err := filepath.Walk(templatesPath, func(path string, f os.FileInfo, err error) error {
if err != nil {
return err
}
if !f.IsDir() && strings.HasSuffix(f.Name(), ext) {
_, err = tmpls.ParseFiles(path)
if err != nil {
return err
}
}
return nil
})
if err != nil {
return nil, err
}
return tmpls, nil
}