new
commit
daf390bf6c
|
@ -0,0 +1 @@
|
||||||
|
config.json
|
|
@ -0,0 +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%;
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 488 B |
Binary file not shown.
After Width: | Height: | Size: 9.4 KiB |
Binary file not shown.
After Width: | Height: | Size: 60 KiB |
|
@ -0,0 +1,117 @@
|
||||||
|
<!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>
|
||||||
|
<h1>
|
||||||
|
some system information
|
||||||
|
</h1>
|
||||||
|
<ul>
|
||||||
|
<li>unix timestamp of page rendering - <strong>{{ .renderingTimestamp }}</strong></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 lifelong obsession
|
||||||
|
</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>
|
||||||
|
this site is written in Go without using frameworks in few hours, 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>
|
|
@ -0,0 +1,25 @@
|
||||||
|
package cache
|
||||||
|
|
||||||
|
import "sync"
|
||||||
|
|
||||||
|
type Cache struct {
|
||||||
|
Data map[string]any
|
||||||
|
Mu sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func Init() *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()
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
For server start (author's note to self):
|
||||||
|
|
||||||
|
sudo systemctl stop server.service
|
||||||
|
go build main.go
|
||||||
|
sudo systemctl start server.service
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
AssetsPath string
|
||||||
|
TemplatesPath string
|
||||||
|
TemplatesExt string
|
||||||
|
LocalIP string
|
||||||
|
ServerIP string
|
||||||
|
Port string
|
||||||
|
}
|
||||||
|
|
||||||
|
func Init() *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
|
||||||
|
}
|
|
@ -0,0 +1,176 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"log"
|
||||||
|
"main/cache"
|
||||||
|
"main/config"
|
||||||
|
"main/tools"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// App представляет основное состояние приложения
|
||||||
|
type App struct {
|
||||||
|
Templates *template.Template // Шаблоны страниц
|
||||||
|
Config *config.Config // Конфиг
|
||||||
|
StartTime int64 // Время запуска
|
||||||
|
Cache *cache.Cache // Кэш (отрендеренные странички)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var app *App
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Инициализация приложения
|
||||||
|
if app, err = AppInit("config.json"); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Настройка маршрутов и запуск
|
||||||
|
if app.SetupRouterAndRun() != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Запускает сервер на указанном IP и порту
|
||||||
|
func RunServer(ip, port string, router http.Handler) {
|
||||||
|
addr := ip + port
|
||||||
|
log.Println("Run on", addr)
|
||||||
|
log.Fatal(http.ListenAndServe(addr, router))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Инициализирует приложение
|
||||||
|
func AppInit(configPath string) (*App, error) {
|
||||||
|
|
||||||
|
a := &App{
|
||||||
|
StartTime: time.Now().Unix(),
|
||||||
|
Config: config.Init(),
|
||||||
|
Cache: cache.Init(),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Загрузка конфига
|
||||||
|
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) SetupRouterAndRun() error {
|
||||||
|
// Настройка маршрутов
|
||||||
|
router := a.SetupRouter()
|
||||||
|
|
||||||
|
// Запуск сервера
|
||||||
|
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 (a *App) SetupRouter() *http.ServeMux {
|
||||||
|
router := http.NewServeMux()
|
||||||
|
|
||||||
|
// Обработка статических файлов с кэшированием
|
||||||
|
router.Handle(a.Config.AssetsPath, a.StaticHandler())
|
||||||
|
|
||||||
|
// Обработка главной страницы
|
||||||
|
router.Handle("/", a.MainPageHandler())
|
||||||
|
|
||||||
|
return router
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обработчик статических файлов с кэшированием
|
||||||
|
func (a *App) StaticHandler() http.HandlerFunc {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Add("Cache-Control", "public, max-age=31536000, immutable")
|
||||||
|
// Здесь используется встроенный файловый сервер Go (http.FileServer), который:
|
||||||
|
// Реализует интерфейс http.Handler (и поэтому имеет метод ServeHTTP)
|
||||||
|
// Автоматически обслуживает статические файлы из файловой системы
|
||||||
|
// Сам обрабатывает HTTP-запросы, определяет MIME-типы, отправляет правильные заголовки и т.д.
|
||||||
|
http.FileServer(http.Dir(".")).ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обработчик главной страницы
|
||||||
|
func (a *App) MainPageHandler() http.HandlerFunc {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var err error
|
||||||
|
tmplName := "main" + a.Config.TemplatesExt
|
||||||
|
|
||||||
|
// Страничка рендерится только если ее нет в кэше
|
||||||
|
pageData, ok := a.Cache.Get(tmplName)
|
||||||
|
if !ok {
|
||||||
|
context := map[string]any{
|
||||||
|
"version": a.StartTime,
|
||||||
|
"renderingTimestamp": time.Now().Unix(),
|
||||||
|
}
|
||||||
|
pageData, err = a.RenderPage(tmplName, context)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
a.Cache.Set(tmplName, pageData)
|
||||||
|
}
|
||||||
|
|
||||||
|
a.SendPage(w, pageData.([]byte))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Загрузка шаблонов
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Рендерит шаблон в срез байт
|
||||||
|
func (a *App) RenderPage(tmplName string, context any) ([]byte, error) {
|
||||||
|
var pageData bytes.Buffer
|
||||||
|
|
||||||
|
if err := a.Templates.ExecuteTemplate(&pageData, tmplName, context); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return pageData.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Отправляет страницу
|
||||||
|
func (a *App) SendPage(w http.ResponseWriter, data []byte) {
|
||||||
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
w.Write(data)
|
||||||
|
}
|
|
@ -0,0 +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 // Адрес не найден
|
||||||
|
}
|
Loading…
Reference in New Issue