Compare commits
1 Commits
design
...
clientcach
Author | SHA1 | Date |
---|---|---|
|
975e7e8a4c |
|
@ -6,7 +6,8 @@ body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
background-color: #E6E6FA;
|
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPSc0MCcgaGVpZ2h0PSc0MCc+PHJlY3QgZmlsbD0nIzkxZWFlNCcgeD0nMCcgeT0nMCcgd2lkdGg9JzIwcHgnIGhlaWdodD0nMjBweCcvPjxyZWN0IGZpbGw9JyM4NmE4ZTcnIHg9JzAnIHk9JzIwJyB3aWR0aD0nMjBweCcgaGVpZ2h0PScyMHB4Jy8+PHJlY3QgZmlsbD0nIzkxZWFlNCcgeD0nMjAnIHk9JzIwJyB3aWR0aD0nMjBweCcgaGVpZ2h0PScyMHB4Jy8+PHJlY3QgZmlsbD0nIzg2YThlNycgeD0nMjAnIHk9JzAnIHdpZHRoPScyMHB4JyBoZWlnaHQ9JzIwcHgnLz48L3N2Zz4=");
|
||||||
|
background-repeat: repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
header, main, footer {
|
header, main, footer {
|
||||||
|
@ -32,7 +33,6 @@ div {
|
||||||
border: 1px solid;
|
border: 1px solid;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0 20px;
|
padding: 0 20px;
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
|
@ -42,17 +42,6 @@ h1 {
|
||||||
border-bottom: 1px solid;
|
border-bottom: 1px solid;
|
||||||
}
|
}
|
||||||
|
|
||||||
pre {
|
|
||||||
padding: 10px;
|
|
||||||
background: #E6E6FA;
|
|
||||||
border: 1px solid #e8e8e8;
|
|
||||||
overflow-x: auto;
|
|
||||||
white-space: pre;
|
|
||||||
word-wrap: normal;
|
|
||||||
width: 0;
|
|
||||||
min-width: calc(100% - 20px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.count {
|
.count {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
|
@ -38,25 +38,35 @@ func MainPageHandler(app *models.App) http.HandlerFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Страничка рендерится только если ее нет в кэше
|
// Страничка рендерится только если ее нет в кэше
|
||||||
pageData, ok := app.PagesCache.Get(models_pages.MainPageTmplName)
|
page, ok := app.PagesCache.Get(models_pages.MainPageTmplName)
|
||||||
if !ok {
|
if !ok {
|
||||||
pageData, err = models_pages.RenderMainPage(app.Templates, app.Version)
|
pageData, err := models_pages.RenderMainPage(app.Templates, app.Version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
app.PagesCache.Set(models_pages.MainPageTmplName, pageData)
|
ETag := models.GenerateETag(pageData)
|
||||||
|
page = &models.Page{Data: pageData, ETag: ETag}
|
||||||
|
app.PagesCache.Set(models_pages.MainPageTmplName, page)
|
||||||
}
|
}
|
||||||
|
|
||||||
sendMainPage(w, pageData)
|
sendMainPage(r, w, page)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Отправляет страницу
|
// Отправляет страницу
|
||||||
func sendMainPage(w http.ResponseWriter, data []byte) {
|
func sendMainPage(r *http.Request, w http.ResponseWriter, page *models.Page) {
|
||||||
|
|
||||||
|
w.Header().Set("Cache-Control", "public, max-age=31536000")
|
||||||
|
w.Header().Set("ETag", page.ETag)
|
||||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
w.Write(data)
|
if match := r.Header.Get("If-None-Match"); match == page.ETag {
|
||||||
|
w.WriteHeader(http.StatusNotModified)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write(page.Data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ответ на метод COUNT
|
// Ответ на метод COUNT
|
||||||
|
|
|
@ -10,8 +10,6 @@ import (
|
||||||
// Обработчик главной страницы
|
// Обработчик главной страницы
|
||||||
func PostPageHandler(app *models.App) http.HandlerFunc {
|
func PostPageHandler(app *models.App) http.HandlerFunc {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
var err error
|
|
||||||
|
|
||||||
postLink := r.URL.Path
|
postLink := r.URL.Path
|
||||||
|
|
||||||
// Ссылки на посты имеют вид postLink = /link/, а если прилетело что то типо /link/123123,
|
// Ссылки на посты имеют вид postLink = /link/, а если прилетело что то типо /link/123123,
|
||||||
|
@ -22,26 +20,33 @@ func PostPageHandler(app *models.App) http.HandlerFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Страничка рендерится только если ее нет в кэше
|
// Страничка рендерится только если ее нет в кэше
|
||||||
pageData, ok := app.PagesCache.Get(postLink)
|
page, ok := app.PagesCache.Get(postLink)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
||||||
post := app.Posts[models_pages.PostLink(postLink)]
|
post := app.Posts[models_pages.PostLink(postLink)]
|
||||||
|
pageData, err := models_pages.RenderPostPage(app.Templates, app.Version, post.Data)
|
||||||
pageData, err = models_pages.RenderPostPage(app.Templates, app.Version, post.Data)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
app.PagesCache.Set(postLink, pageData)
|
ETag := models.GenerateETag(pageData)
|
||||||
|
page = &models.Page{Data: pageData, ETag: ETag}
|
||||||
|
app.PagesCache.Set(postLink, page)
|
||||||
}
|
}
|
||||||
|
|
||||||
sendPostPage(w, pageData)
|
sendPostPage(r, w, page)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Отправляет страницу
|
// Отправляет страницу
|
||||||
func sendPostPage(w http.ResponseWriter, data []byte) {
|
func sendPostPage(r *http.Request, w http.ResponseWriter, page *models.Page) {
|
||||||
|
w.Header().Set("Cache-Control", "public, max-age=31536000")
|
||||||
|
w.Header().Set("ETag", page.ETag)
|
||||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
w.Write(data)
|
if match := r.Header.Get("If-None-Match"); match == page.ETag {
|
||||||
|
w.WriteHeader(http.StatusNotModified)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write(page.Data)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,26 +9,33 @@ import (
|
||||||
// Обработчик главной страницы
|
// Обработчик главной страницы
|
||||||
func PostsPageHandler(app *models.App) http.HandlerFunc {
|
func PostsPageHandler(app *models.App) http.HandlerFunc {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
var err error
|
|
||||||
|
|
||||||
// Страничка рендерится только если ее нет в кэше
|
// Страничка рендерится только если ее нет в кэше
|
||||||
pageData, ok := app.PagesCache.Get(models_pages.PostsPageTmplName)
|
page, ok := app.PagesCache.Get(models_pages.PostsPageTmplName)
|
||||||
if !ok {
|
if !ok {
|
||||||
pageData, err = app.Posts.RenderPostsPage(app.Templates, app.Version)
|
pageData, err := app.Posts.RenderPostsPage(app.Templates, app.Version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
app.PagesCache.Set(models_pages.PostsPageTmplName, pageData)
|
ETag := models.GenerateETag(pageData)
|
||||||
|
page = &models.Page{Data: pageData, ETag: ETag}
|
||||||
|
app.PagesCache.Set(models_pages.PostsPageTmplName, page)
|
||||||
}
|
}
|
||||||
|
|
||||||
sendPostsPage(w, pageData)
|
sendPostsPage(r, w, page)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Отправляет страницу
|
// Отправляет страницу
|
||||||
func sendPostsPage(w http.ResponseWriter, data []byte) {
|
func sendPostsPage(r *http.Request, w http.ResponseWriter, page *models.Page) {
|
||||||
|
w.Header().Set("Cache-Control", "public, max-age=31536000")
|
||||||
|
w.Header().Set("ETag", page.ETag)
|
||||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
w.Write(data)
|
if match := r.Header.Get("If-None-Match"); match == page.ETag {
|
||||||
|
w.WriteHeader(http.StatusNotModified)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write(page.Data)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,40 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import "sync"
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Page struct {
|
||||||
|
Data []byte
|
||||||
|
ETag string
|
||||||
|
}
|
||||||
|
|
||||||
type Cache struct {
|
type Cache struct {
|
||||||
Data map[string][]byte
|
Data map[string]*Page
|
||||||
Mu sync.RWMutex
|
Mu sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func initCache() *Cache {
|
func initCache() *Cache {
|
||||||
return &Cache{Data: make(map[string][]byte)}
|
return &Cache{Data: make(map[string]*Page)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) Get(key string) ([]byte, bool) {
|
func (c *Cache) Get(key string) (*Page, bool) {
|
||||||
c.Mu.RLock()
|
c.Mu.RLock()
|
||||||
pageData, ok := c.Data[key]
|
page, ok := c.Data[key]
|
||||||
c.Mu.RUnlock()
|
c.Mu.RUnlock()
|
||||||
return pageData, ok
|
return page, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) Set(key string, data []byte) {
|
func (c *Cache) Set(key string, page *Page) {
|
||||||
c.Mu.Lock()
|
c.Mu.Lock()
|
||||||
c.Data[key] = data
|
c.Data[key] = page
|
||||||
c.Mu.Unlock()
|
c.Mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GenerateETag(data []byte) string {
|
||||||
|
hash := md5.Sum(data)
|
||||||
|
return fmt.Sprintf(`"%s"`, hex.EncodeToString(hash[:]))
|
||||||
|
}
|
||||||
|
|
|
@ -5,25 +5,19 @@
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p>
|
<p>
|
||||||
and also you can subscribe to my <a href="https://t.me/lolistack" target="_blank">telegram channel</a> with pictures!
|
and also you can subscribe to my <a href="https://t.me/lolistack" target="_blank">telegram channel </a> with pictures!
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p>
|
<p>
|
||||||
<strong>some system information</strong>:
|
<strong>some system information</strong>:
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<ul>
|
||||||
> unix timestamp of page rendering: <code>{{ .renderingTimestamp }}</code><br/>
|
<li>unix timestamp of page rendering: <code>{{ .renderingTimestamp }}</code></li>
|
||||||
> <code>curl -X LIMINAL https://hikan.ru</code> - what do you know about liminal spaces<br/>
|
<li><code>curl -X LIMINAL https://hikan.ru</code> - what do you know about liminal spaces?</li>
|
||||||
> this site code repository - <a href="https://git.hikan.ru/serr/hikan.ru" target="_blank">git.hikan.ru/serr/hikan.ru</a><br/>
|
<li>this site code repository - <a href="https://git.hikan.ru/serr/hikan.ru" target="_blank">git.hikan.ru/serr/hikan.ru</a></li>
|
||||||
> 24-hour server request count (<code>curl -X COUNT https://hikan.ru</code>):
|
<li><code>curl -X COUNT https://hikan.ru</code> - 24-hour server request count</li>
|
||||||
</p>
|
</ul>
|
||||||
<p class="count">
|
|
||||||
<img src="/assets/pic/0.gif?v={{ .version }}">
|
|
||||||
<img src="/assets/pic/0.gif?v={{ .version }}">
|
|
||||||
<img src="/assets/pic/0.gif?v={{ .version }}">
|
|
||||||
<img src="/assets/pic/0.gif?v={{ .version }}">
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p>
|
<p>
|
||||||
|
@ -34,16 +28,4 @@
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
<script>
|
|
||||||
fetch('/', {method: 'COUNT'})
|
|
||||||
.then(r => r.text())
|
|
||||||
.then(num => num.trim())
|
|
||||||
.then(number => {
|
|
||||||
if (number.length > 0) {
|
|
||||||
document.querySelector('p.count').innerHTML =
|
|
||||||
[...number].map(d => `<img src="/assets/pic/${d}.gif?v={{ .version }}">`).join('');
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(console.error);
|
|
||||||
</script>
|
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
|
@ -12,10 +12,10 @@
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p>
|
<ul>
|
||||||
> <a href="/">main page</a><br/>
|
<li><a href="/">main page</a></li>
|
||||||
> <a href="/posts/">posts section</a>
|
<li><a href="/posts/">posts section</a></li>
|
||||||
</p>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
{{ end }}
|
{{ end }}
|
|
@ -36,34 +36,34 @@
|
||||||
<h1>
|
<h1>
|
||||||
things i love
|
things i love
|
||||||
</h1>
|
</h1>
|
||||||
<p>
|
<ul>
|
||||||
<strong>> coffee</strong>. i REALLY love coffee. almost any. and a lot of<br/>
|
<li><strong>coffee</strong>. i REALLY love coffee. almost any. and a lot of</li>
|
||||||
<strong>> movies and TV series</strong> (especially TV series). i watch something almost every day<br/>
|
<li><strong>movies and TV series</strong> (especially TV series). i watch something almost every day</li>
|
||||||
<strong>> true crime</strong>. i'm obsessed with serial killer cases, mysterious disappearances, unsolved murders - all that dark stuff<br/>
|
<li><strong>true crime</strong>. i'm obsessed with serial killer cases, mysterious disappearances, unsolved murders - all that dark stuff</li>
|
||||||
<strong>> russian underground rap</strong> like Slava KPSS, Zamay, MB Packet, Ovsyankin etc.<br/>
|
<li><strong>russian underground rap</strong> like Slava KPSS, Zamay, MB Packet, Ovsyankin etc.</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><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>
|
||||||
</p>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h1>
|
<h1>
|
||||||
projects
|
projects
|
||||||
</h1>
|
</h1>
|
||||||
<p>
|
<ul>
|
||||||
> <a href="https://git.hikan.ru/serr" target="_blank">git.hikan.ru/serr</a> - check my repos<br/>
|
<li><a href="https://git.hikan.ru/serr" target="_blank">git.hikan.ru/serr</a> - check my repos</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)<br/>
|
<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>
|
||||||
> <del>telegram bot with schedule for SPMI - <a href="https://t.me/gornischedule_bot" target="_blank">gornischedule_bot</a></del> (closed)<br/>
|
<li><del>telegram bot with schedule for SPMI - <a href="https://t.me/gornischedule_bot" target="_blank">gornischedule_bot</a></del> (closed)</li>
|
||||||
</p>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h1>
|
<h1>
|
||||||
nice links
|
nice links
|
||||||
</h1>
|
</h1>
|
||||||
<p>
|
<ul>
|
||||||
> <a href="https://mo.rijndael.cc/" target="_blank">Mo</a>, thx for design idea!<br/>
|
<li><a href="https://mo.rijndael.cc/" target="_blank">Mo</a>, thx for design idea!</li>
|
||||||
> huge collection of Xakep issues - <a href="https://図書館.きく.コム/" target="_blank">図書館.きく.コム</a><br/>
|
<li>huge collection of Xakep issues - <a href="https://図書館.きく.コム/" target="_blank">図書館.きく.コム</a></li>
|
||||||
> i love this website highlighting the Small Web - <a href="https://smallweb.cc/" target="_blank">smallweb</a><br/>
|
<li>i love this website highlighting the Small Web - <a href="https://smallweb.cc/" target="_blank">smallweb</a></li>
|
||||||
> very atmospheric forum about black metal - <a href="https://www.lycanthropia.net/" target="_blank">lycanthropia</a><br/>
|
<li>very atmospheric forum about black metal - <a href="https://www.lycanthropia.net/" target="_blank">lycanthropia</a></li>
|
||||||
</p>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
{{ template "footer" . }}
|
{{ template "footer" . }}
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
# Тест блоков с кодом!
|
|
||||||
|
|
||||||
Тестовый блок с кодом:
|
|
||||||
|
|
||||||
```
|
|
||||||
// Ответ на метод COUNT
|
|
||||||
#include <stdio.h>
|
|
||||||
int main() {
|
|
||||||
printf("very long striiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiing!");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
```
|
|
Loading…
Reference in New Issue