facade
parent
b68851c862
commit
cb0358dbb4
50
main.go
50
main.go
|
@ -6,75 +6,57 @@ import (
|
||||||
"main/mvc/controllers"
|
"main/mvc/controllers"
|
||||||
"main/mvc/controllers/controllers_pages"
|
"main/mvc/controllers/controllers_pages"
|
||||||
"main/mvc/models"
|
"main/mvc/models"
|
||||||
"main/mvc/models/models_pages"
|
|
||||||
"main/tools"
|
"main/tools"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var err error
|
var err error
|
||||||
// Инициализация конфига
|
var app *models.App
|
||||||
if err := models.Cfg.Load("config.json"); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Загрузка постов
|
// Инициализация приложения
|
||||||
if err := models_pages.LoadPosts(models.Cfg.PostsDir); err != nil {
|
if app, err = models.InitApp(); err != nil {
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Инициализация приложения (обязательно после инициализации конфига)
|
|
||||||
if err := models.InitApp(); err != nil {
|
|
||||||
log.Fatal(err)
|
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 {
|
if ok, err := tools.IsIPInUse(app.Cfg.ServerIP); err != nil {
|
||||||
return err
|
log.Fatal(err)
|
||||||
} else if ok {
|
} else if ok {
|
||||||
runServer(models.Cfg.ServerIP, models.Cfg.ServerPort, router)
|
runServer(app.Cfg.ServerIP, app.Cfg.ServerPort, router)
|
||||||
} else {
|
} 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()
|
router := http.NewServeMux()
|
||||||
|
|
||||||
// Цепочка обработчиков, которые сработают до отдачи страницы юзеру
|
// Цепочка обработчиков, которые сработают до отдачи страницы юзеру
|
||||||
m := controllers.MiddlewaresChain
|
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)
|
postLink := string(key)
|
||||||
router.Handle(postLink, m(controllers_pages.PostPageHandler()))
|
router.Handle(postLink, m(controllers_pages.PostPageHandler(app)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
@ -18,7 +18,7 @@ func MainPageHandler() http.HandlerFunc {
|
||||||
// Количество запросов, обработанных сервером за 24ч
|
// Количество запросов, обработанных сервером за 24ч
|
||||||
if r.Method == "COUNT" {
|
if r.Method == "COUNT" {
|
||||||
var count []byte
|
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())
|
log.Printf("%s", err.Error())
|
||||||
}
|
}
|
||||||
sendCount(w, count)
|
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 {
|
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 {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
models.PagesCache.Set(models_pages.MainPageTmplName, pageData)
|
app.PagesCache.Set(models_pages.MainPageTmplName, pageData)
|
||||||
}
|
}
|
||||||
|
|
||||||
sendMainPage(w, pageData.([]byte))
|
sendMainPage(w, pageData.([]byte))
|
||||||
|
|
|
@ -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) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
// Страничка рендерится только если ее нет в кэше
|
// Страничка рендерится только если ее нет в кэше
|
||||||
pageData, ok := models.PagesCache.Get(models_pages.MainRuPageTmplName)
|
pageData, ok := app.PagesCache.Get(models_pages.MainRuPageTmplName)
|
||||||
if !ok {
|
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 {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
models.PagesCache.Set(models_pages.MainRuPageTmplName, pageData)
|
app.PagesCache.Set(models_pages.MainRuPageTmplName, pageData)
|
||||||
}
|
}
|
||||||
|
|
||||||
sendMainRuPage(w, pageData.([]byte))
|
sendMainRuPage(w, pageData.([]byte))
|
||||||
|
|
|
@ -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) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
var err error
|
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 {
|
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 {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
models.PagesCache.Set(models_pages.PostPageTmplName, pageData)
|
app.PagesCache.Set(models_pages.PostPageTmplName, pageData)
|
||||||
}
|
}
|
||||||
|
|
||||||
sendPostPage(w, pageData.([]byte))
|
sendPostPage(w, pageData.([]byte))
|
||||||
|
|
|
@ -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) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
// Страничка рендерится только если ее нет в кэше
|
// Страничка рендерится только если ее нет в кэше
|
||||||
pageData, ok := models.PagesCache.Get(models_pages.PostsPageTmplName)
|
pageData, ok := app.PagesCache.Get(models_pages.PostsPageTmplName)
|
||||||
if !ok {
|
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 {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
models.PagesCache.Set(models_pages.PostsPageTmplName, pageData)
|
app.PagesCache.Set(models_pages.PostsPageTmplName, pageData)
|
||||||
}
|
}
|
||||||
|
|
||||||
sendPostsPage(w, pageData.([]byte))
|
sendPostsPage(w, pageData.([]byte))
|
||||||
|
|
|
@ -2,55 +2,39 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"html/template"
|
"html/template"
|
||||||
"os"
|
"main/mvc/models/models_pages"
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// App хранит шаблоны и время запуска
|
type App struct {
|
||||||
type app struct {
|
Cfg *Config // Сонфиг
|
||||||
|
Posts models_pages.Posts // Посты
|
||||||
Templates *template.Template // Шаблоны страниц
|
Templates *template.Template // Шаблоны страниц
|
||||||
|
PagesCache *Cache // Кэш (отрендеренные странички)
|
||||||
Version int64 // Время запуска
|
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 {
|
if app.Templates, err = loadTemplates(app.Cfg.TemplatesDir, app.Cfg.TemplatesExt); err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
return nil
|
// Инициализация кэша
|
||||||
}
|
app.PagesCache = initCache()
|
||||||
|
return app, 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
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,23 +2,23 @@ package models
|
||||||
|
|
||||||
import "sync"
|
import "sync"
|
||||||
|
|
||||||
type cache struct {
|
type Cache struct {
|
||||||
Data map[string]any
|
Data map[string]any
|
||||||
Mu sync.RWMutex
|
Mu sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
func initCache() *Cache {
|
||||||
PagesCache = cache{Data: make(map[string]any)}
|
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()
|
c.Mu.RLock()
|
||||||
pageData, ok := c.Data[key]
|
pageData, ok := c.Data[key]
|
||||||
c.Mu.RUnlock()
|
c.Mu.RUnlock()
|
||||||
return pageData, ok
|
return pageData, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cache) Set(key string, data any) {
|
func (c *Cache) Set(key string, data any) {
|
||||||
c.Mu.Lock()
|
c.Mu.Lock()
|
||||||
c.Data[key] = data
|
c.Data[key] = data
|
||||||
c.Mu.Unlock()
|
c.Mu.Unlock()
|
||||||
|
|
|
@ -5,7 +5,11 @@ import (
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
type config struct {
|
const (
|
||||||
|
ConfigPath = "config.json"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
PostsDir string
|
PostsDir string
|
||||||
AssetsDir string
|
AssetsDir string
|
||||||
TemplatesDir string
|
TemplatesDir string
|
||||||
|
@ -18,18 +22,15 @@ type config struct {
|
||||||
Port string
|
Port string
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
func loadConfig(configPath string) (*Config, error) {
|
||||||
Cfg = &config{}
|
cfg := &Config{}
|
||||||
)
|
|
||||||
|
|
||||||
func (c *config) Load(configPath string) error {
|
|
||||||
configFile, err := os.ReadFile(configPath)
|
configFile, err := os.ReadFile(configPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
err = json.Unmarshal(configFile, c)
|
err = json.Unmarshal(configFile, cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
return nil
|
return cfg, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,17 +17,12 @@ const (
|
||||||
PostsPageTmplName = "posts.gohtml"
|
PostsPageTmplName = "posts.gohtml"
|
||||||
)
|
)
|
||||||
|
|
||||||
type posts map[PostLink]*Post
|
type Posts map[PostLink]*Post
|
||||||
|
|
||||||
var (
|
func LoadPosts(dir string) (Posts, error) {
|
||||||
allPosts = posts{}
|
|
||||||
)
|
|
||||||
|
|
||||||
func GetPosts() posts {
|
posts := Posts{}
|
||||||
return allPosts
|
|
||||||
}
|
|
||||||
|
|
||||||
func LoadPosts(dir string) error {
|
|
||||||
err := filepath.Walk(dir, func(path string, f os.FileInfo, err error) error {
|
err := filepath.Walk(dir, func(path string, f os.FileInfo, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -46,25 +41,25 @@ func LoadPosts(dir string) error {
|
||||||
|
|
||||||
html := tools.MdToHTML(md)
|
html := tools.MdToHTML(md)
|
||||||
link := fmt.Sprintf("/%s/", strings.TrimSuffix(filepath.Base(path), ".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
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != 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
|
var pageData bytes.Buffer
|
||||||
|
|
||||||
context := map[string]any{
|
context := map[string]any{
|
||||||
"version": version,
|
"version": version,
|
||||||
"renderingTimestamp": time.Now().Unix(),
|
"renderingTimestamp": time.Now().Unix(),
|
||||||
"posts": allPosts,
|
"posts": p,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := templates.ExecuteTemplate(&pageData, PostsPageTmplName, context); err != nil {
|
if err := templates.ExecuteTemplate(&pageData, PostsPageTmplName, context); err != nil {
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
Loading…
Reference in New Issue