From c1901320e0c7c4011725e3b42e5631b00119927a Mon Sep 17 00:00:00 2001
From: serr <sc7227484@gmail.com>
Date: Mon, 7 Apr 2025 22:17:55 +0300
Subject: [PATCH] small change

---
 .gitignore                       |   2 +-
 assets/css/styles.css            | 112 ++++++-------
 main.go                          | 146 ++++++++---------
 mvc/controllers/middlewares.go   |  86 +++++-----
 mvc/controllers/pages/main.go    | 164 +++++++++----------
 mvc/controllers/pages/main_ru.go |  68 ++++----
 mvc/controllers/static.go        |  28 ++--
 mvc/models/app.go                | 130 +++++++--------
 mvc/models/cache.go              |  50 +++---
 mvc/models/config.go             |  64 ++++----
 mvc/models/pages/main.go         |  54 +++----
 mvc/models/pages/main_ru.go      |  54 +++----
 mvc/views/pages/main.gohtml      | 248 ++++++++++++++---------------
 mvc/views/pages/main_ru.gohtml   | 262 +++++++++++++++----------------
 tools/inet.go                    |  62 ++++----
 tools/logs.go                    |  58 +++----
 16 files changed, 794 insertions(+), 794 deletions(-)

diff --git a/.gitignore b/.gitignore
index 618f0c4..a35805a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,2 @@
-config.json
+config.json
 restart.sh
\ No newline at end of file
diff --git a/assets/css/styles.css b/assets/css/styles.css
index 61dada0..da2d645 100644
--- a/assets/css/styles.css
+++ b/assets/css/styles.css
@@ -1,57 +1,57 @@
-/* Design idea from here https://mo.rijndael.cc/ */
-
-body {
-    display: flex;
-    flex-wrap: wrap;
-    margin: 0;
-    align-items: flex-start;
-    padding: 10px;
-}
-
-header, main, footer {
-    margin: 5px;
-    display: flex;
-    flex-direction: column;
-    gap: 10px;
-}
-
-header, footer {
-    flex: 2;
-}
-
-main {
-    flex: 3;
-}
-
-header > div,
-footer > div,
-main > div {
-    box-shadow: 5px 5px 0 0 lightgrey;
-    box-sizing: border-box;
-    border: 1px solid;
-    width: 100%;
-    text-align: center;
-    color: black;
-    padding-left: 10px;
-    padding-right: 10px;
-}
-
-header > div > ul,
-footer > div > ul,
-main > div > ul {
-    text-align: left;
-}
-
-header > div > h1,
-footer > div > h1,
-main > div > h1 {
-    box-sizing: border-box;
-    border-top: 1px solid;
-    border-bottom: 1px solid;
-}
-
-@media (max-width: 1200px) {
-	header, footer, main {
-		flex: 1 100%;
-	}
+/* Design idea from here https://mo.rijndael.cc/ */
+
+body {
+    display: flex;
+    flex-wrap: wrap;
+    margin: 0;
+    align-items: flex-start;
+    padding: 10px;
+}
+
+header, main, footer {
+    margin: 5px;
+    display: flex;
+    flex-direction: column;
+    gap: 10px;
+}
+
+header, footer {
+    flex: 2;
+}
+
+main {
+    flex: 3;
+}
+
+header > div,
+footer > div,
+main > div {
+    box-shadow: 5px 5px 0 0 lightgrey;
+    box-sizing: border-box;
+    border: 1px solid;
+    width: 100%;
+    text-align: center;
+    color: black;
+    padding-left: 10px;
+    padding-right: 10px;
+}
+
+header > div > ul,
+footer > div > ul,
+main > div > ul {
+    text-align: left;
+}
+
+header > div > h1,
+footer > div > h1,
+main > div > h1 {
+    box-sizing: border-box;
+    border-top: 1px solid;
+    border-bottom: 1px solid;
+}
+
+@media (max-width: 1200px) {
+	header, footer, main {
+		flex: 1 100%;
+	}
 }
\ No newline at end of file
diff --git a/main.go b/main.go
index 93322a6..88421ad 100644
--- a/main.go
+++ b/main.go
@@ -1,73 +1,73 @@
-package main
-
-import (
-	"fmt"
-	"log"
-	"main/mvc/controllers"
-	controllers_pages "main/mvc/controllers/pages"
-	"main/mvc/models"
-	"main/tools"
-	"net/http"
-)
-
-func main() {
-	var app *models.App
-	var err error
-
-	// Инициализация приложения
-	if app, err = models.AppInit("config.json"); err != nil {
-		log.Fatal(err)
-	}
-
-	// Добавление префикса в виде домена сервера к записям в лог
-	log.SetPrefix(fmt.Sprintf("%s | ", app.Config.ServerDomain))
-
-	// Настройка маршрутов и запуск
-	if setupRoutesAndRun(app) != nil {
-		log.Fatal(err)
-	}
-}
-
-func setupRoutesAndRun(a *models.App) error {
-	// Настройка маршрутов
-	router := setupRoutes(a)
-
-	// Запуск сервера
-	if ok, err := tools.IsIPInUse(a.Config.ServerIP); err != nil {
-		return err
-	} else if ok {
-		runServer(a.Config.ServerIP, a.Config.Port, router)
-	} else {
-		runServer(a.Config.LocalIP, a.Config.Port, router)
-	}
-
-	return nil
-}
-
-// Настраивает маршруты
-func setupRoutes(a *models.App) *http.ServeMux {
-	router := http.NewServeMux()
-
-	// Цепочка обработчиков, которые сработают до отдачи страницы юзеру
-	m := controllers.MiddlewaresChain
-
-	// Обработка статических файлов
-	router.Handle(a.Config.AssetsPath, m(controllers.StaticHandler()))
-
-	// Странички
-	{
-		// Обработка главной страницы (русская версия)
-		router.Handle("/ru/", m(controllers_pages.MainRuPageHandler(a)))
-		// Обработка главной страницы
-		router.Handle("/", m(controllers_pages.MainPageHandler(a)))
-	}
-
-	return router
-}
-
-// Обертка над ListenAndServe, запускает сервер на указанном IP, PORT
-func runServer(ip, port string, router http.Handler) {
-	addr := ip + port
-	log.Println("Run on", addr)
-	log.Fatal(http.ListenAndServe(addr, router))
-}
+package main
+
+import (
+	"fmt"
+	"log"
+	"main/mvc/controllers"
+	controllers_pages "main/mvc/controllers/pages"
+	"main/mvc/models"
+	"main/tools"
+	"net/http"
+)
+
+func main() {
+	var app *models.App
+	var err error
+
+	// Инициализация приложения
+	if app, err = models.AppInit("config.json"); err != nil {
+		log.Fatal(err)
+	}
+
+	// Добавление префикса в виде домена сервера к записям в лог
+	log.SetPrefix(fmt.Sprintf("%s | ", app.Config.ServerDomain))
+
+	// Настройка маршрутов и запуск
+	if setupRoutesAndRun(app) != nil {
+		log.Fatal(err)
+	}
+}
+
+func setupRoutesAndRun(a *models.App) error {
+	// Настройка маршрутов
+	router := setupRoutes(a)
+
+	// Запуск сервера
+	if ok, err := tools.IsIPInUse(a.Config.ServerIP); err != nil {
+		return err
+	} else if ok {
+		runServer(a.Config.ServerIP, a.Config.Port, router)
+	} else {
+		runServer(a.Config.LocalIP, a.Config.Port, router)
+	}
+
+	return nil
+}
+
+// Настраивает маршруты
+func setupRoutes(a *models.App) *http.ServeMux {
+	router := http.NewServeMux()
+
+	// Цепочка обработчиков, которые сработают до отдачи страницы юзеру
+	m := controllers.MiddlewaresChain
+
+	// Обработка статических файлов
+	router.Handle(a.Config.AssetsPath, m(controllers.StaticHandler()))
+
+	// Странички
+	{
+		// Обработка главной страницы (русская версия)
+		router.Handle("/ru/", m(controllers_pages.MainRuPageHandler(a)))
+		// Обработка главной страницы
+		router.Handle("/", m(controllers_pages.MainPageHandler(a)))
+	}
+
+	return router
+}
+
+// Запускает сервер на указанном IP, PORT
+func runServer(ip, port string, router http.Handler) {
+	addr := ip + port
+	log.Println("Run on", addr)
+	log.Fatal(http.ListenAndServe(addr, router))
+}
diff --git a/mvc/controllers/middlewares.go b/mvc/controllers/middlewares.go
index 9f52b6f..64c79d3 100644
--- a/mvc/controllers/middlewares.go
+++ b/mvc/controllers/middlewares.go
@@ -1,43 +1,43 @@
-package controllers
-
-import (
-	"log"
-	"net/http"
-	"time"
-)
-
-type Middleware func(http.Handler) http.Handler
-
-var (
-	MiddlewaresChain = CreateMiddlewaresChain(
-		LoggingMiddleware,
-	)
-)
-
-/*
-Возвращает один middleware, который объединяет все переданные
-
-CreateMiddlewaresChain(m1, m2, m3)
-= func(next http.Handler) http.Handler { return m1(m2(m3(final))) }
-
-CreateMiddlewaresChain(LoggingMiddleware)
-= func(next http.Handler) http.Handler { return LoggingMiddleware(final) }
-*/
-func CreateMiddlewaresChain(middlewares ...Middleware) Middleware {
-	return func(final http.Handler) http.Handler {
-		for i := len(middlewares) - 1; i >= 0; i-- {
-			final = middlewares[i](final)
-		}
-		return final
-	}
-}
-
-func LoggingMiddleware(next http.Handler) http.Handler {
-	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		start := time.Now()
-
-		next.ServeHTTP(w, r)
-
-		log.Printf("%s %s %v", r.Method, r.URL.Path, time.Since(start))
-	})
-}
+package controllers
+
+import (
+	"log"
+	"net/http"
+	"time"
+)
+
+type Middleware func(http.Handler) http.Handler
+
+var (
+	MiddlewaresChain = CreateMiddlewaresChain(
+		LoggingMiddleware,
+	)
+)
+
+/*
+Возвращает один middleware, который объединяет все переданные
+
+CreateMiddlewaresChain(m1, m2, m3)
+= func(next http.Handler) http.Handler { return m1(m2(m3(final))) }
+
+CreateMiddlewaresChain(LoggingMiddleware)
+= func(next http.Handler) http.Handler { return LoggingMiddleware(final) }
+*/
+func CreateMiddlewaresChain(middlewares ...Middleware) Middleware {
+	return func(final http.Handler) http.Handler {
+		for i := len(middlewares) - 1; i >= 0; i-- {
+			final = middlewares[i](final)
+		}
+		return final
+	}
+}
+
+func LoggingMiddleware(next http.Handler) http.Handler {
+	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		start := time.Now()
+
+		next.ServeHTTP(w, r)
+
+		log.Printf("%s %s %v", r.Method, r.URL.Path, time.Since(start))
+	})
+}
diff --git a/mvc/controllers/pages/main.go b/mvc/controllers/pages/main.go
index 2a4f757..890a169 100644
--- a/mvc/controllers/pages/main.go
+++ b/mvc/controllers/pages/main.go
@@ -1,82 +1,82 @@
-package controllers
-
-import (
-	"log"
-	"main/mvc/models"
-	models_pages "main/mvc/models/pages"
-	"main/tools"
-	"net/http"
-)
-
-// Обработчик главной страницы
-func MainPageHandler(a *models.App) http.HandlerFunc {
-	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-
-		var err error
-
-		// Количество запросов, обработанных сервером за 24ч
-		if r.Method == "COUNT" {
-			var count []byte
-			if count, err = tools.GetJournalctlLogsCount("server", a.Config.ServerDomain, 24); err != nil {
-				log.Printf("%s", err.Error())
-			}
-			SendCount(w, count)
-			return
-		}
-
-		// Пасхалка
-		if r.Method == "LOVE" {
-			SendLove(w)
-			return
-		}
-
-		// Пасхалка 2
-		if r.Method == "LIMINAL" {
-			SendLiminal(w)
-			return
-		}
-
-		// Страничка рендерится только если ее нет в кэше
-		pageData, ok := a.Cache.Get(models_pages.MainPageTmplName)
-		if !ok {
-			pageData, err = models_pages.RenderMainPage(a.Templates, a.Version)
-			if err != nil {
-				http.Error(w, err.Error(), http.StatusInternalServerError)
-				return
-			}
-			a.Cache.Set(models_pages.MainPageTmplName, pageData)
-		}
-
-		SendMainPage(w, pageData.([]byte))
-	})
-}
-
-// Отправляет страницу
-func SendMainPage(w http.ResponseWriter, data []byte) {
-	w.Header().Set("Content-Type", "text/html; charset=utf-8")
-	w.WriteHeader(http.StatusOK)
-	w.Write(data)
-}
-
-// Ответ на метод COUNT
-func SendCount(w http.ResponseWriter, data []byte) {
-	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
-	w.WriteHeader(http.StatusOK)
-	w.Write(data)
-}
-
-// Ответ на метод LOVE
-func SendLove(w http.ResponseWriter) {
-	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
-	w.WriteHeader(http.StatusOK)
-	w.Write([]byte("13.01.2005\n"))
-}
-
-// Ответ на метод LIMINAL
-func SendLiminal(w http.ResponseWriter) {
-	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
-	w.WriteHeader(http.StatusOK)
-	text := "If you're not careful and you slip out of reality in the wrong place, you'll end up in the Backstage, where there's nothing but the stench of old damp carpet, yellow-colored madness, the endless unbearable hum of fluorescent lights, and roughly six hundred million square miles of randomly arranged empty rooms.\n"
-	w.Write([]byte(text))
-
-}
+package controllers
+
+import (
+	"log"
+	"main/mvc/models"
+	models_pages "main/mvc/models/pages"
+	"main/tools"
+	"net/http"
+)
+
+// Обработчик главной страницы
+func MainPageHandler(a *models.App) http.HandlerFunc {
+	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+
+		var err error
+
+		// Количество запросов, обработанных сервером за 24ч
+		if r.Method == "COUNT" {
+			var count []byte
+			if count, err = tools.GetJournalctlLogsCount("server", a.Config.ServerDomain, 24); err != nil {
+				log.Printf("%s", err.Error())
+			}
+			SendCount(w, count)
+			return
+		}
+
+		// Пасхалка
+		if r.Method == "LOVE" {
+			SendLove(w)
+			return
+		}
+
+		// Пасхалка 2
+		if r.Method == "LIMINAL" {
+			SendLiminal(w)
+			return
+		}
+
+		// Страничка рендерится только если ее нет в кэше
+		pageData, ok := a.Cache.Get(models_pages.MainPageTmplName)
+		if !ok {
+			pageData, err = models_pages.RenderMainPage(a.Templates, a.Version)
+			if err != nil {
+				http.Error(w, err.Error(), http.StatusInternalServerError)
+				return
+			}
+			a.Cache.Set(models_pages.MainPageTmplName, pageData)
+		}
+
+		SendMainPage(w, pageData.([]byte))
+	})
+}
+
+// Отправляет страницу
+func SendMainPage(w http.ResponseWriter, data []byte) {
+	w.Header().Set("Content-Type", "text/html; charset=utf-8")
+	w.WriteHeader(http.StatusOK)
+	w.Write(data)
+}
+
+// Ответ на метод COUNT
+func SendCount(w http.ResponseWriter, data []byte) {
+	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
+	w.WriteHeader(http.StatusOK)
+	w.Write(data)
+}
+
+// Ответ на метод LOVE
+func SendLove(w http.ResponseWriter) {
+	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
+	w.WriteHeader(http.StatusOK)
+	w.Write([]byte("13.01.2005\n"))
+}
+
+// Ответ на метод LIMINAL
+func SendLiminal(w http.ResponseWriter) {
+	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
+	w.WriteHeader(http.StatusOK)
+	text := "If you're not careful and you slip out of reality in the wrong place, you'll end up in the Backstage, where there's nothing but the stench of old damp carpet, yellow-colored madness, the endless unbearable hum of fluorescent lights, and roughly six hundred million square miles of randomly arranged empty rooms.\n"
+	w.Write([]byte(text))
+
+}
diff --git a/mvc/controllers/pages/main_ru.go b/mvc/controllers/pages/main_ru.go
index 6f67a13..b2a5da1 100644
--- a/mvc/controllers/pages/main_ru.go
+++ b/mvc/controllers/pages/main_ru.go
@@ -1,34 +1,34 @@
-package controllers
-
-import (
-	"main/mvc/models"
-	models_pages "main/mvc/models/pages"
-	"net/http"
-)
-
-// Обработчик главной страницы
-func MainRuPageHandler(a *models.App) http.HandlerFunc {
-	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		var err error
-
-		// Страничка рендерится только если ее нет в кэше
-		pageData, ok := a.Cache.Get(models_pages.MainRuPageTmplName)
-		if !ok {
-			pageData, err = models_pages.RenderMainRuPage(a.Templates, a.Version)
-			if err != nil {
-				http.Error(w, err.Error(), http.StatusInternalServerError)
-				return
-			}
-			a.Cache.Set(models_pages.MainRuPageTmplName, pageData)
-		}
-
-		SendMainPage(w, pageData.([]byte))
-	})
-}
-
-// Отправляет страницу
-func SendMainRuPage(w http.ResponseWriter, data []byte) {
-	w.Header().Set("Content-Type", "text/html; charset=utf-8")
-	w.WriteHeader(http.StatusOK)
-	w.Write(data)
-}
+package controllers
+
+import (
+	"main/mvc/models"
+	models_pages "main/mvc/models/pages"
+	"net/http"
+)
+
+// Обработчик главной страницы
+func MainRuPageHandler(a *models.App) http.HandlerFunc {
+	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		var err error
+
+		// Страничка рендерится только если ее нет в кэше
+		pageData, ok := a.Cache.Get(models_pages.MainRuPageTmplName)
+		if !ok {
+			pageData, err = models_pages.RenderMainRuPage(a.Templates, a.Version)
+			if err != nil {
+				http.Error(w, err.Error(), http.StatusInternalServerError)
+				return
+			}
+			a.Cache.Set(models_pages.MainRuPageTmplName, pageData)
+		}
+
+		SendMainPage(w, pageData.([]byte))
+	})
+}
+
+// Отправляет страницу
+func SendMainRuPage(w http.ResponseWriter, data []byte) {
+	w.Header().Set("Content-Type", "text/html; charset=utf-8")
+	w.WriteHeader(http.StatusOK)
+	w.Write(data)
+}
diff --git a/mvc/controllers/static.go b/mvc/controllers/static.go
index 28f346f..34c34ec 100644
--- a/mvc/controllers/static.go
+++ b/mvc/controllers/static.go
@@ -1,14 +1,14 @@
-package controllers
-
-import "net/http"
-
-// Обработчик статических файлов с кэшированием
-func StaticHandler() http.HandlerFunc {
-	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		// Здесь используется встроенный файловый сервер Go (http.FileServer), который:
-		// Реализует интерфейс http.Handler (и поэтому имеет метод ServeHTTP)
-		// Автоматически обслуживает статические файлы из файловой системы
-		// Сам обрабатывает HTTP-запросы, определяет MIME-типы, отправляет правильные заголовки и т.д.
-		http.FileServer(http.Dir(".")).ServeHTTP(w, r)
-	})
-}
+package controllers
+
+import "net/http"
+
+// Обработчик статических файлов с кэшированием
+func StaticHandler() http.HandlerFunc {
+	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		// Здесь используется встроенный файловый сервер Go (http.FileServer), который:
+		// Реализует интерфейс http.Handler (и поэтому имеет метод ServeHTTP)
+		// Автоматически обслуживает статические файлы из файловой системы
+		// Сам обрабатывает HTTP-запросы, определяет MIME-типы, отправляет правильные заголовки и т.д.
+		http.FileServer(http.Dir(".")).ServeHTTP(w, r)
+	})
+}
diff --git a/mvc/models/app.go b/mvc/models/app.go
index 7b5515d..6638f97 100644
--- a/mvc/models/app.go
+++ b/mvc/models/app.go
@@ -1,65 +1,65 @@
-package models
-
-import (
-	"html/template"
-	"log"
-	"os"
-	"path/filepath"
-	"strings"
-	"time"
-)
-
-// App хранит информацию о приложении
-type App struct {
-	Config    *Config            // Конфиг
-	Templates *template.Template // Шаблоны страниц
-	Cache     *Cache             // Кэш (отрендеренные странички)
-	Version   int64              // Время запуска
-}
-
-// Инициализирует приложение
-func AppInit(configPath string) (*App, error) {
-
-	a := &App{
-		Version: time.Now().Unix(),
-		Config:  ConfigInit(),
-		Cache:   CacheInit(),
-	}
-
-	// Загрузка конфига
-	if err := a.Config.Load(configPath); err != nil {
-		return nil, err
-	}
-
-	// Загрузка шаблонов
-	if err := a.loadTemplates(a.Config.TemplatesPath, a.Config.TemplatesExt); err != nil {
-		log.Fatal(err)
-	}
-
-	return a, 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
-}
+package models
+
+import (
+	"html/template"
+	"log"
+	"os"
+	"path/filepath"
+	"strings"
+	"time"
+)
+
+// App хранит информацию о приложении
+type App struct {
+	Config    *Config            // Конфиг
+	Templates *template.Template // Шаблоны страниц
+	Cache     *Cache             // Кэш (отрендеренные странички)
+	Version   int64              // Время запуска
+}
+
+// Инициализирует приложение
+func AppInit(configPath string) (*App, error) {
+
+	a := &App{
+		Version: time.Now().Unix(),
+		Config:  ConfigInit(),
+		Cache:   CacheInit(),
+	}
+
+	// Загрузка конфига
+	if err := a.Config.Load(configPath); err != nil {
+		return nil, err
+	}
+
+	// Загрузка шаблонов
+	if err := a.loadTemplates(a.Config.TemplatesPath, a.Config.TemplatesExt); err != nil {
+		log.Fatal(err)
+	}
+
+	return a, 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
+}
diff --git a/mvc/models/cache.go b/mvc/models/cache.go
index 5554aa6..585711b 100644
--- a/mvc/models/cache.go
+++ b/mvc/models/cache.go
@@ -1,25 +1,25 @@
-package models
-
-import "sync"
-
-type Cache struct {
-	Data map[string]any
-	Mu   sync.RWMutex
-}
-
-func CacheInit() *Cache {
-	return &Cache{Data: make(map[string]any)}
-}
-
-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) {
-	c.Mu.Lock()
-	c.Data[key] = data
-	c.Mu.Unlock()
-}
+package models
+
+import "sync"
+
+type Cache struct {
+	Data map[string]any
+	Mu   sync.RWMutex
+}
+
+func CacheInit() *Cache {
+	return &Cache{Data: make(map[string]any)}
+}
+
+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) {
+	c.Mu.Lock()
+	c.Data[key] = data
+	c.Mu.Unlock()
+}
diff --git a/mvc/models/config.go b/mvc/models/config.go
index ebfd160..31f5ba0 100644
--- a/mvc/models/config.go
+++ b/mvc/models/config.go
@@ -1,32 +1,32 @@
-package models
-
-import (
-	"encoding/json"
-	"os"
-)
-
-type Config struct {
-	AssetsPath    string
-	TemplatesPath string
-	TemplatesExt  string
-	LocalIP       string
-	ServerIP      string
-	ServerDomain  string
-	Port          string
-}
-
-func ConfigInit() *Config {
-	return &Config{}
-}
-
-func (c *Config) Load(configPath string) error {
-	configFile, err := os.ReadFile(configPath)
-	if err != nil {
-		return err
-	}
-	err = json.Unmarshal(configFile, c)
-	if err != nil {
-		return err
-	}
-	return nil
-}
+package models
+
+import (
+	"encoding/json"
+	"os"
+)
+
+type Config struct {
+	AssetsPath    string
+	TemplatesPath string
+	TemplatesExt  string
+	LocalIP       string
+	ServerIP      string
+	ServerDomain  string
+	Port          string
+}
+
+func ConfigInit() *Config {
+	return &Config{}
+}
+
+func (c *Config) Load(configPath string) error {
+	configFile, err := os.ReadFile(configPath)
+	if err != nil {
+		return err
+	}
+	err = json.Unmarshal(configFile, c)
+	if err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/mvc/models/pages/main.go b/mvc/models/pages/main.go
index fca61b6..f28ab1b 100644
--- a/mvc/models/pages/main.go
+++ b/mvc/models/pages/main.go
@@ -1,27 +1,27 @@
-package models
-
-import (
-	"bytes"
-	"html/template"
-	"time"
-)
-
-const (
-	// Имя соответствующего шаблона
-	MainPageTmplName = "main.gohtml"
-)
-
-func RenderMainPage(templates *template.Template, version int64) ([]byte, error) {
-	var pageData bytes.Buffer
-
-	context := map[string]any{
-		"version":            version,
-		"renderingTimestamp": time.Now().Unix(),
-	}
-
-	if err := templates.ExecuteTemplate(&pageData, MainPageTmplName, context); err != nil {
-		return nil, err
-	}
-
-	return pageData.Bytes(), nil
-}
+package models
+
+import (
+	"bytes"
+	"html/template"
+	"time"
+)
+
+const (
+	// Имя соответствующего шаблона
+	MainPageTmplName = "main.gohtml"
+)
+
+func RenderMainPage(templates *template.Template, version int64) ([]byte, error) {
+	var pageData bytes.Buffer
+
+	context := map[string]any{
+		"version":            version,
+		"renderingTimestamp": time.Now().Unix(),
+	}
+
+	if err := templates.ExecuteTemplate(&pageData, MainPageTmplName, context); err != nil {
+		return nil, err
+	}
+
+	return pageData.Bytes(), nil
+}
diff --git a/mvc/models/pages/main_ru.go b/mvc/models/pages/main_ru.go
index 8650fc5..80866ab 100644
--- a/mvc/models/pages/main_ru.go
+++ b/mvc/models/pages/main_ru.go
@@ -1,27 +1,27 @@
-package models
-
-import (
-	"bytes"
-	"html/template"
-	"time"
-)
-
-const (
-	// Имя соответствующего шаблона
-	MainRuPageTmplName = "main_ru.gohtml"
-)
-
-func RenderMainRuPage(templates *template.Template, version int64) ([]byte, error) {
-	var pageData bytes.Buffer
-
-	context := map[string]any{
-		"version":            version,
-		"renderingTimestamp": time.Now().Unix(),
-	}
-
-	if err := templates.ExecuteTemplate(&pageData, MainRuPageTmplName, context); err != nil {
-		return nil, err
-	}
-
-	return pageData.Bytes(), nil
-}
+package models
+
+import (
+	"bytes"
+	"html/template"
+	"time"
+)
+
+const (
+	// Имя соответствующего шаблона
+	MainRuPageTmplName = "main_ru.gohtml"
+)
+
+func RenderMainRuPage(templates *template.Template, version int64) ([]byte, error) {
+	var pageData bytes.Buffer
+
+	context := map[string]any{
+		"version":            version,
+		"renderingTimestamp": time.Now().Unix(),
+	}
+
+	if err := templates.ExecuteTemplate(&pageData, MainRuPageTmplName, context); err != nil {
+		return nil, err
+	}
+
+	return pageData.Bytes(), nil
+}
diff --git a/mvc/views/pages/main.gohtml b/mvc/views/pages/main.gohtml
index 1602c04..1eb6684 100644
--- a/mvc/views/pages/main.gohtml
+++ b/mvc/views/pages/main.gohtml
@@ -1,125 +1,125 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <title>hikan.ru</title>
-    <meta charset="UTF-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <link rel="shortcut icon" href="/assets/pic/favicon.webp?v={{ .version }}" type="image/x-icon">
-    <link rel="stylesheet" href="/assets/css/styles.css?v={{ .version }}" type="text/css">
-</head>
-<body>
-    <header>
-        <div>
-            <img src="/assets/pic/header.webp?v={{ .version }}" width="100%" height="100%">
-        </div>
-        <div>
-            <h1>
-                contacts
-            </h1>
-            <p>
-                you can message me on <a href="https://t.me/semaphoreslover" target="_blank">telegram</a> or <a href="https://mastodon.ml/@serr" target="_blank">mastodon</a>
-            </p>
-        </div>
-        <div>
-            <ul>
-                <li><a href="/ru">switch to ru version (AI translation)</a></li>
-            </ul>
-        </div>
-    </header>
-    <main>
-        <div>
-            <h1>
-                $whoami
-            </h1>
-            <p>
-                my name is serr (you can easily guess my real name if you speak Russian :d), and i didn't come up with that nickname, i just started being called it
-            </p>
-            <p>
-                i was born in 2003, i'm currently a cybersecurity major at university
-            </p>
-            <p>
-                <code>pronouns: he/him</code>
-            </p>
-        </div>
-        <div>
-            <h1>
-                what do i do?
-            </h1>
-            <p>
-                programming is my everything - my job, my hobby, my lifestyle
-            </p>
-            <p>
-                i love growing in all areas of programming - i am literally interested in everything: cybersecurity (chaotically breaking things, analyzing code, writing automated analyzers, and moving bytes back and forth), concurrency/multithreading, web development, low-level programming, cryptography and a lot more!
-            </p>
-            <p>
-                i like the idea of <a href="https://en.wikipedia.org/wiki/Symbolic_execution" target="_blank">symbolic</a>/concolic execution and virtual code execution in general
-            </p>
-        </div>
-        <div>
-            <h1>
-                things i love
-            </h1>
-            <ul>
-                <li><strong>coffee</strong>. i REALLY love coffee. almost any. and a lot of</li>
-                <li><strong>movies and TV series</strong> (especially TV series). i watch something almost every day</li>
-                <li><strong>true crime</strong>. i'm obsessed with serial killer cases, mysterious disappearances, unsolved murders - all that dark stuff</li>
-                <li><strong>russian underground rap</strong> like Slava KPSS, Zamay, MB Packet, Ovsyankin etc.</li>
-                <li><strong>simple and extensible code</strong>. i think if your code is overly complex, it means you are doing something wrong. most things are simpler than they seem</li>
-            </ul>
-        </div>
-        <div>
-            <h1>
-                projects
-            </h1>
-            <ul>
-                <li><a href="https://git.hikan.ru/serr" target="_blank">git.hikan.ru/serr</a> - check my repos</li>
-                <li><del>telegram bot with schedule for SPBPU - <a href="https://t.me/polysched_bot" target="_blank">polysched_bot</a></del> (transferred to a more proactive owner)</li>
-                <li><del>telegram bot with schedule for SPMI - <a href="https://t.me/gornischedule_bot" target="_blank">gornischedule_bot</a></del> (closed)</li>
-            </ul>
-        </div>
-        <div>
-            <h1>
-                nice links
-            </h1>
-            <ul>
-                <li><a href="https://mo.rijndael.cc/" target="_blank">Mo</a>, thx for design idea!</li>
-                <li>huge collection of Xakep issues -  <a href="https://図書館.きく.コム/" target="_blank">図書館.きく.コム</a></li>
-                <li>i love this website highlighting the Small Web - <a href="https://smallweb.cc/" target="_blank">smallweb</a></li>
-                <li>very atmospheric forum about black metal -  <a href="https://www.lycanthropia.net/" target="_blank">lycanthropia</a></li>
-            </ul>
-        </div>
-    </main>
-    <footer>
-        <div>
-            <img src="/assets/pic/footer.webp?v={{ .version }}" width="100%" height="100%">
-        </div>
-        <div>
-            <p>
-                and also you can subscribe to my Telegram channel with pictures!
-            </p>
-            <p>
-                <a href="https://t.me/lolistack" target="_blank">digital countryside</a>
-            </p>
-        </div>
-        <div>
-            <p>
-                _some system information_
-            </p>
-            <ul>
-                <li>unix timestamp of page rendering - <strong>{{ .renderingTimestamp }}</strong></li>
-                <li><code>curl -X COUNT https://hikan.ru</code> - 24-hour server request count</li>
-                <li><code>curl -X LIMINAL https://hikan.ru</code> - what do you know about liminal spaces?</li>
-                <li>this site code repository - <a href="https://git.hikan.ru/serr/hikan.ru" target="_blank">git.hikan.ru/serr/hikan.ru</a></li>
-            </ul>
-        </div>
-        <div>
-            <p>
-                this site is written in Go without using frameworks, hosting is <a href="https://htk.ge" target="_blank">hostetski</a>, domain bought for the price of a can of beer
-            </p>
-            <p>
-                <code>2024 - now</code>
-            </p>
-        </div>
-    </footer>
-</body>
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <title>hikan.ru</title>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <link rel="shortcut icon" href="/assets/pic/favicon.webp?v={{ .version }}" type="image/x-icon">
+    <link rel="stylesheet" href="/assets/css/styles.css?v={{ .version }}" type="text/css">
+</head>
+<body>
+    <header>
+        <div>
+            <img src="/assets/pic/header.webp?v={{ .version }}" width="100%" height="100%">
+        </div>
+        <div>
+            <h1>
+                contacts
+            </h1>
+            <p>
+                you can message me on <a href="https://t.me/semaphoreslover" target="_blank">telegram</a> or <a href="https://mastodon.ml/@serr" target="_blank">mastodon</a>
+            </p>
+        </div>
+        <div>
+            <ul>
+                <li><a href="/ru">switch to ru version (AI translation)</a></li>
+            </ul>
+        </div>
+    </header>
+    <main>
+        <div>
+            <h1>
+                $whoami
+            </h1>
+            <p>
+                my name is serr (you can easily guess my real name if you speak Russian :d), and i didn't come up with that nickname, i just started being called it
+            </p>
+            <p>
+                i was born in 2003, i'm currently a cybersecurity major at university
+            </p>
+            <p>
+                <code>pronouns: he/him</code>
+            </p>
+        </div>
+        <div>
+            <h1>
+                what do i do?
+            </h1>
+            <p>
+                programming is my everything - my job, my hobby, my lifestyle
+            </p>
+            <p>
+                i love growing in all areas of programming - i am literally interested in everything: cybersecurity (chaotically breaking things, analyzing code, writing automated analyzers, and moving bytes back and forth), concurrency/multithreading, web development, low-level programming, cryptography and a lot more!
+            </p>
+            <p>
+                i like the idea of <a href="https://en.wikipedia.org/wiki/Symbolic_execution" target="_blank">symbolic</a>/concolic execution and virtual code execution in general
+            </p>
+        </div>
+        <div>
+            <h1>
+                things i love
+            </h1>
+            <ul>
+                <li><strong>coffee</strong>. i REALLY love coffee. almost any. and a lot of</li>
+                <li><strong>movies and TV series</strong> (especially TV series). i watch something almost every day</li>
+                <li><strong>true crime</strong>. i'm obsessed with serial killer cases, mysterious disappearances, unsolved murders - all that dark stuff</li>
+                <li><strong>russian underground rap</strong> like Slava KPSS, Zamay, MB Packet, Ovsyankin etc.</li>
+                <li><strong>simple and extensible code</strong>. i think if your code is overly complex, it means you are doing something wrong. most things are simpler than they seem</li>
+            </ul>
+        </div>
+        <div>
+            <h1>
+                projects
+            </h1>
+            <ul>
+                <li><a href="https://git.hikan.ru/serr" target="_blank">git.hikan.ru/serr</a> - check my repos</li>
+                <li><del>telegram bot with schedule for SPBPU - <a href="https://t.me/polysched_bot" target="_blank">polysched_bot</a></del> (transferred to a more proactive owner)</li>
+                <li><del>telegram bot with schedule for SPMI - <a href="https://t.me/gornischedule_bot" target="_blank">gornischedule_bot</a></del> (closed)</li>
+            </ul>
+        </div>
+        <div>
+            <h1>
+                nice links
+            </h1>
+            <ul>
+                <li><a href="https://mo.rijndael.cc/" target="_blank">Mo</a>, thx for design idea!</li>
+                <li>huge collection of Xakep issues -  <a href="https://図書館.きく.コム/" target="_blank">図書館.きく.コム</a></li>
+                <li>i love this website highlighting the Small Web - <a href="https://smallweb.cc/" target="_blank">smallweb</a></li>
+                <li>very atmospheric forum about black metal -  <a href="https://www.lycanthropia.net/" target="_blank">lycanthropia</a></li>
+            </ul>
+        </div>
+    </main>
+    <footer>
+        <div>
+            <img src="/assets/pic/footer.webp?v={{ .version }}" width="100%" height="100%">
+        </div>
+        <div>
+            <p>
+                and also you can subscribe to my Telegram channel with pictures!
+            </p>
+            <p>
+                <a href="https://t.me/lolistack" target="_blank">digital countryside</a>
+            </p>
+        </div>
+        <div>
+            <p>
+                _some system information_
+            </p>
+            <ul>
+                <li>unix timestamp of page rendering - <strong>{{ .renderingTimestamp }}</strong></li>
+                <li><code>curl -X COUNT https://hikan.ru</code> - 24-hour server request count</li>
+                <li><code>curl -X LIMINAL https://hikan.ru</code> - what do you know about liminal spaces?</li>
+                <li>this site code repository - <a href="https://git.hikan.ru/serr/hikan.ru" target="_blank">git.hikan.ru/serr/hikan.ru</a></li>
+            </ul>
+        </div>
+        <div>
+            <p>
+                this site is written in Go without using frameworks, hosting is <a href="https://htk.ge" target="_blank">hostetski</a>, domain bought for the price of a can of beer
+            </p>
+            <p>
+                <code>2024 - now</code>
+            </p>
+        </div>
+    </footer>
+</body>
 </html>
\ No newline at end of file
diff --git a/mvc/views/pages/main_ru.gohtml b/mvc/views/pages/main_ru.gohtml
index ea0f0dd..b36985e 100644
--- a/mvc/views/pages/main_ru.gohtml
+++ b/mvc/views/pages/main_ru.gohtml
@@ -1,132 +1,132 @@
-<!DOCTYPE html>
-<html lang="ru">
-<head>
-    <title>hikan.ru</title>
-    <meta charset="UTF-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <link rel="shortcut icon" href="/assets/pic/favicon.webp?v={{ .version }}" type="image/x-icon">
-    <link rel="stylesheet" href="/assets/css/styles.css?v={{ .version }}" type="text/css">
-</head>
-<body>
-    <header>
-        <div>
-            <img src="/assets/pic/header.webp?v={{ .version }}" width="100%" height="100%">
-        </div>
-        <div>
-            <h1>
-                контакты
-            </h1>
-            <p>
-                вы можете написать мне в <a href="https://t.me/semaphoreslover" target="_blank">telegram</a> или <a href="https://mastodon.ml/@serr" target="_blank">mastodon</a>
-            </p>
-        </div>
-        <div>
-            <ul>
-                <li><a href="/">английская версия</a></li>
-            </ul>
-        </div>
-    </header>
-    <main>
-        <div>
-            <p>
-                <code style="color: #0E53FF">
-                    эту страничку на русский частично переводил DeepSeek, имейте в виду
-                </code>
-            </p>
-        </div>
-        <div>
-            <h1>
-                $whoami
-            </h1>
-            <p>
-                меня зовут serr (мое настоящее имя легко угадать, если вы говорите по-русски :d), и это не я придумал этот никнейм - просто меня стали так называть
-            </p>
-            <p>
-                я родился в 2003 году, сейчас учусь на специалиста по кибербезопасности
-            </p>
-            <p>
-                <code>местоимения: он/его</code>
-            </p>
-        </div>
-        <div>
-            <h1>
-                чем я занимаюсь?
-            </h1>
-            <p>
-                программирование - это моё всё: работа, хобби, стиль жизни
-            </p>
-            <p>
-                мне нравится развиваться во всех областях программирования - мне буквально интересно всё: кибербезопасность (хаотичный взлом вещей, анализ кода, написание автоматических анализаторов и перекладывание байтов туда-сюда), многопоточность, веб-разработка, низкоуровневое программирование, криптография и многое другое!
-            </p>
-            <p>
-                мне нравится идея <a href="https://en.wikipedia.org/wiki/Symbolic_execution" target="_blank">символьного</a>/конколического выполнения и виртуального выполнения кода в целом
-            </p>
-        </div>
-        <div>
-            <h1>
-                что я люблю
-            </h1>
-            <ul>
-                <li><strong>кофе</strong>. я ОЧЕНЬ люблю кофе. почти любой. и много</li>
-                <li><strong>фильмы и сериалы</strong> (особенно сериалы). я смотрю что-то почти каждый день</li>
-                <li><strong>true crime</strong>. я одержим делами о серийных убийцах, таинственных исчезновениях, нераскрытых убийствах - всем этим тёмным материалом</li>
-                <li><strong>русский андерграундный рэп</strong> типа Slava KPSS, Zamay, MB Packet, Ovsyankin и т.д.</li>
-                <li><strong>простой и расширяемый код</strong>. я считаю, что если ваш код слишком сложен, значит вы делаете что-то не так. большинство вещей проще, чем кажутся</li>
-            </ul>
-        </div>
-        <div>
-            <h1>
-                проекты
-            </h1>
-            <ul>
-                <li><a href="https://git.hikan.ru/serr" target="_blank">git.hikan.ru/serr</a> - мои репозитории</li>
-                <li><del>телеграм-бот с расписанием для СПбПУ - <a href="https://t.me/polysched_bot" target="_blank">polysched_bot</a></del> (передан более активному владельцу)</li>
-                <li><del>телеграм-бот с расписанием для Горного - <a href="https://t.me/gornischedule_bot" target="_blank">gornischedule_bot</a></del> (закрыт)</li>
-            </ul>
-        </div>
-        <div>
-            <h1>
-                интересные ссылки
-            </h1>
-            <ul>
-                <li><a href="https://mo.rijndael.cc/" target="_blank">Mo</a>, спасибо за идею дизайна!</li>
-                <li>огромная коллекция номеров Xakep - <a href="https://図書館.きく.コム/" target="_blank">図書館.きく.コム</a></li>
-                <li>мне нравится этот сайт о Small Web - <a href="https://smallweb.cc/" target="_blank">smallweb</a></li>
-                <li>очень атмосферный форум о блэк-метале - <a href="https://www.lycanthropia.net/" target="_blank">lycanthropia</a></li>
-            </ul>
-        </div>
-    </main>
-    <footer>
-        <div>
-            <img src="/assets/pic/footer.webp?v={{ .version }}" width="100%" height="100%">
-        </div>
-        <div>
-            <p>
-                а ещё можно подписаться на мой телеграм-канал с картинками!
-            </p>
-            <p>
-                <a href="https://t.me/lolistack" target="_blank">цифровая деревня</a>
-            </p>
-        </div>
-        <div>
-            <p>
-                _немного системной информации_
-            </p>
-            <ul>
-                <li>unix-время генерации страницы - <strong>{{ .renderingTimestamp }}</strong></li>
-                <li><code>curl -X COUNT https://hikan.ru</code> - количество завпросов, обработанных сервером за 24ч</li>
-                <li><code>curl -X LIMINAL https://hikan.ru</code> - что ты знаешь о лиминальных пространствах?</li>
-                <li>репозиторий с кодом этого сайта - <a href="https://git.hikan.ru/serr/hikan.ru" target="_blank">git.hikan.ru/serr/hikan.ru</a></li>
-            </ul>
-        </div>
-        <div>
-            <p>
-                этот сайт написан на Go без использования фреймворков, хостинг - <a href="https://htk.ge" target="_blank">hostetski</a>, домен куплен по цене банки пива
-            </p>
-            <p>
-                <code>2024 - настоящее время</code>
-            </p>
-        </div>
-    </footer>
-</body>
+<!DOCTYPE html>
+<html lang="ru">
+<head>
+    <title>hikan.ru</title>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <link rel="shortcut icon" href="/assets/pic/favicon.webp?v={{ .version }}" type="image/x-icon">
+    <link rel="stylesheet" href="/assets/css/styles.css?v={{ .version }}" type="text/css">
+</head>
+<body>
+    <header>
+        <div>
+            <img src="/assets/pic/header.webp?v={{ .version }}" width="100%" height="100%">
+        </div>
+        <div>
+            <h1>
+                контакты
+            </h1>
+            <p>
+                вы можете написать мне в <a href="https://t.me/semaphoreslover" target="_blank">telegram</a> или <a href="https://mastodon.ml/@serr" target="_blank">mastodon</a>
+            </p>
+        </div>
+        <div>
+            <ul>
+                <li><a href="/">английская версия</a></li>
+            </ul>
+        </div>
+    </header>
+    <main>
+        <div>
+            <p>
+                <code style="color: #0E53FF">
+                    эту страничку на русский частично переводил DeepSeek, имейте в виду
+                </code>
+            </p>
+        </div>
+        <div>
+            <h1>
+                $whoami
+            </h1>
+            <p>
+                меня зовут serr (мое настоящее имя легко угадать, если вы говорите по-русски :d), и это не я придумал этот никнейм - просто меня стали так называть
+            </p>
+            <p>
+                я родился в 2003 году, сейчас учусь на специалиста по кибербезопасности
+            </p>
+            <p>
+                <code>местоимения: он/его</code>
+            </p>
+        </div>
+        <div>
+            <h1>
+                чем я занимаюсь?
+            </h1>
+            <p>
+                программирование - это моё всё: работа, хобби, стиль жизни
+            </p>
+            <p>
+                мне нравится развиваться во всех областях программирования - мне буквально интересно всё: кибербезопасность (хаотичный взлом вещей, анализ кода, написание автоматических анализаторов и перекладывание байтов туда-сюда), многопоточность, веб-разработка, низкоуровневое программирование, криптография и многое другое!
+            </p>
+            <p>
+                мне нравится идея <a href="https://en.wikipedia.org/wiki/Symbolic_execution" target="_blank">символьного</a>/конколического выполнения и виртуального выполнения кода в целом
+            </p>
+        </div>
+        <div>
+            <h1>
+                что я люблю
+            </h1>
+            <ul>
+                <li><strong>кофе</strong>. я ОЧЕНЬ люблю кофе. почти любой. и много</li>
+                <li><strong>фильмы и сериалы</strong> (особенно сериалы). я смотрю что-то почти каждый день</li>
+                <li><strong>true crime</strong>. я одержим делами о серийных убийцах, таинственных исчезновениях, нераскрытых убийствах - всем этим тёмным материалом</li>
+                <li><strong>русский андерграундный рэп</strong> типа Slava KPSS, Zamay, MB Packet, Ovsyankin и т.д.</li>
+                <li><strong>простой и расширяемый код</strong>. я считаю, что если ваш код слишком сложен, значит вы делаете что-то не так. большинство вещей проще, чем кажутся</li>
+            </ul>
+        </div>
+        <div>
+            <h1>
+                проекты
+            </h1>
+            <ul>
+                <li><a href="https://git.hikan.ru/serr" target="_blank">git.hikan.ru/serr</a> - мои репозитории</li>
+                <li><del>телеграм-бот с расписанием для СПбПУ - <a href="https://t.me/polysched_bot" target="_blank">polysched_bot</a></del> (передан более активному владельцу)</li>
+                <li><del>телеграм-бот с расписанием для Горного - <a href="https://t.me/gornischedule_bot" target="_blank">gornischedule_bot</a></del> (закрыт)</li>
+            </ul>
+        </div>
+        <div>
+            <h1>
+                интересные ссылки
+            </h1>
+            <ul>
+                <li><a href="https://mo.rijndael.cc/" target="_blank">Mo</a>, спасибо за идею дизайна!</li>
+                <li>огромная коллекция номеров Xakep - <a href="https://図書館.きく.コム/" target="_blank">図書館.きく.コム</a></li>
+                <li>мне нравится этот сайт о Small Web - <a href="https://smallweb.cc/" target="_blank">smallweb</a></li>
+                <li>очень атмосферный форум о блэк-метале - <a href="https://www.lycanthropia.net/" target="_blank">lycanthropia</a></li>
+            </ul>
+        </div>
+    </main>
+    <footer>
+        <div>
+            <img src="/assets/pic/footer.webp?v={{ .version }}" width="100%" height="100%">
+        </div>
+        <div>
+            <p>
+                а ещё можно подписаться на мой телеграм-канал с картинками!
+            </p>
+            <p>
+                <a href="https://t.me/lolistack" target="_blank">цифровая деревня</a>
+            </p>
+        </div>
+        <div>
+            <p>
+                _немного системной информации_
+            </p>
+            <ul>
+                <li>unix-время генерации страницы - <strong>{{ .renderingTimestamp }}</strong></li>
+                <li><code>curl -X COUNT https://hikan.ru</code> - количество завпросов, обработанных сервером за 24ч</li>
+                <li><code>curl -X LIMINAL https://hikan.ru</code> - что ты знаешь о лиминальных пространствах?</li>
+                <li>репозиторий с кодом этого сайта - <a href="https://git.hikan.ru/serr/hikan.ru" target="_blank">git.hikan.ru/serr/hikan.ru</a></li>
+            </ul>
+        </div>
+        <div>
+            <p>
+                этот сайт написан на Go без использования фреймворков, хостинг - <a href="https://htk.ge" target="_blank">hostetski</a>, домен куплен по цене банки пива
+            </p>
+            <p>
+                <code>2024 - настоящее время</code>
+            </p>
+        </div>
+    </footer>
+</body>
 </html>
\ No newline at end of file
diff --git a/tools/inet.go b/tools/inet.go
index 9832117..0db43b3 100644
--- a/tools/inet.go
+++ b/tools/inet.go
@@ -1,31 +1,31 @@
-package tools
-
-import (
-	"net"
-	"strings"
-)
-
-// Проверяет есть ли айпи-адрес среди адресов сетевых интерфейсов
-func IsIPInUse(targetIP string) (bool, error) {
-	// Получаем список всех сетевых интерфейсов
-	interfaces, err := net.Interfaces()
-	if err != nil {
-		return false, err
-	}
-
-	for _, iface := range interfaces {
-		// Получаем адреса для каждого интерфейса
-		addrs, err := iface.Addrs()
-		if err != nil {
-			return false, err
-		}
-
-		for _, addr := range addrs {
-			if strings.Split(addr.String(), "/")[0] == targetIP {
-				return true, nil
-			}
-		}
-	}
-
-	return false, nil // Адрес не найден
-}
+package tools
+
+import (
+	"net"
+	"strings"
+)
+
+// Проверяет есть ли айпи-адрес среди адресов сетевых интерфейсов
+func IsIPInUse(targetIP string) (bool, error) {
+	// Получаем список всех сетевых интерфейсов
+	interfaces, err := net.Interfaces()
+	if err != nil {
+		return false, err
+	}
+
+	for _, iface := range interfaces {
+		// Получаем адреса для каждого интерфейса
+		addrs, err := iface.Addrs()
+		if err != nil {
+			return false, err
+		}
+
+		for _, addr := range addrs {
+			if strings.Split(addr.String(), "/")[0] == targetIP {
+				return true, nil
+			}
+		}
+	}
+
+	return false, nil // Адрес не найден
+}
diff --git a/tools/logs.go b/tools/logs.go
index e2b7276..378cd5c 100644
--- a/tools/logs.go
+++ b/tools/logs.go
@@ -1,29 +1,29 @@
-package tools
-
-import (
-	"bytes"
-	"fmt"
-	"os/exec"
-	"runtime"
-)
-
-func GetJournalctlLogsCount(serviceName, grepPattern string, hoursAgo int) ([]byte, error) {
-
-	if runtime.GOOS != "linux" {
-		return nil, fmt.Errorf("not a linux")
-	}
-
-	cmd := exec.Command("sh", "-c",
-		fmt.Sprintf("journalctl -u %s --since '%d hours ago' | grep -c '%s'",
-			serviceName, hoursAgo, grepPattern))
-
-	var output bytes.Buffer
-	cmd.Stdout = &output
-
-	err := cmd.Run()
-	if err != nil {
-		return nil, err
-	}
-
-	return output.Bytes(), nil
-}
+package tools
+
+import (
+	"bytes"
+	"fmt"
+	"os/exec"
+	"runtime"
+)
+
+func GetJournalctlLogsCount(serviceName, grepPattern string, hoursAgo int) ([]byte, error) {
+
+	if runtime.GOOS != "linux" {
+		return nil, fmt.Errorf("not a linux")
+	}
+
+	cmd := exec.Command("sh", "-c",
+		fmt.Sprintf("journalctl -u %s --since '%d hours ago' | grep -c '%s'",
+			serviceName, hoursAgo, grepPattern))
+
+	var output bytes.Buffer
+	cmd.Stdout = &output
+
+	err := cmd.Run()
+	if err != nil {
+		return nil, err
+	}
+
+	return output.Bytes(), nil
+}