small change
parent
341beefd9b
commit
c1901320e0
|
@ -1,2 +1,2 @@
|
|||
config.json
|
||||
config.json
|
||||
restart.sh
|
|
@ -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%;
|
||||
}
|
||||
}
|
146
main.go
146
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))
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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 // Адрес не найден
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue