Initial Release

This commit is contained in:
2026-05-24 19:52:45 -07:00
commit 8242fdbbb3
181 changed files with 10119 additions and 0 deletions
+105
View File
@@ -0,0 +1,105 @@
package routes
import (
"bakonpancakz/homepage/env"
"bakonpancakz/homepage/include"
"bytes"
"compress/gzip"
"fmt"
"log"
"net/http"
"strings"
"sync"
"text/template"
)
var articleCache sync.Map
func PrepareBlogArticles(notFoundHandler HttpHandler) HttpHandler {
return func(w http.ResponseWriter, r *http.Request) {
for _, art := range env.Database.Articles {
if strings.Compare(art.Slug, r.PathValue("slug")) == 0 {
compress := strings.Contains(r.Header.Get("Accept-Encoding"), "gzip")
cacheKeyStandard := fmt.Sprintf("art_%s_%t", art.Slug, true)
cacheKeyCompress := fmt.Sprintf("art_%s_%t", art.Slug, false)
// Check Browser Cache
if r.Header.Get("If-None-Match") == env.VERSION {
w.WriteHeader(http.StatusNotModified)
return
}
// Check Template Cache
w.Header().Set("ETag", env.VERSION)
w.Header().Set("Content-Type", "text/html")
w.Header().Set("Cache-Control", "public, max-age=3600, must-revalidate")
if compress {
if v, ok := articleCache.Load(cacheKeyCompress); ok {
w.Header().Set("Content-Encoding", "gzip")
w.Write(v.([]byte))
return
}
} else {
if v, ok := articleCache.Load(cacheKeyStandard); ok {
w.Write(v.([]byte))
return
}
}
// Render Document
tmpl, err := template.ParseFS(
include.Templates,
include.PreparePath(include.Templates, "base.html"),
include.PreparePath(include.Templates, "blog_article.html"),
include.PreparePath(include.Templates, fmt.Sprint("articles/", art.BaseTemplate)),
include.PreparePath(include.Templates, "icons/logo_facebook.svg"),
include.PreparePath(include.Templates, "icons/logo_linkedin.svg"),
include.PreparePath(include.Templates, "icons/logo_x.svg"),
include.PreparePath(include.Templates, "icons/icon_top.svg"),
include.PreparePath(include.Templates, "icons/icon_home.svg"),
include.PreparePath(include.Templates, "icons/icon_share.svg"),
include.PreparePath(include.Templates, "icons/icon_stack.svg"),
)
if err != nil {
log.Println("[routes/blog] Template Error:", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
var b bytes.Buffer
if err = tmpl.ExecuteTemplate(&b, "base.html", map[string]any{
"Title": art.Title,
"Version": env.VERSION,
"Site": env.Database.Site,
"Article": art,
}); err != nil {
log.Println("[routes/blog] Template Error:", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
// Compress Document
var c bytes.Buffer
g := gzip.NewWriter(&c)
g.Write(b.Bytes())
g.Close()
if include.Embedded {
articleCache.Store(cacheKeyStandard, b.Bytes())
articleCache.Store(cacheKeyCompress, c.Bytes())
}
// Send Document
if compress {
w.Header().Set("Content-Encoding", "gzip")
w.Write(c.Bytes())
} else {
w.Write(b.Bytes())
}
return
}
}
notFoundHandler(w, r)
}
}
+33
View File
@@ -0,0 +1,33 @@
package routes
import (
"bakonpancakz/homepage/env"
"bakonpancakz/homepage/include"
"net/http"
)
func GET_Index(notFoundHandler HttpHandler) HttpHandler {
handler := ServeStaticTemplate(&ServeTemplateInfo{
FileSystem: include.Templates,
FilePaths: []string{"base.html", "homepage.html"},
Filename: "homepage.html",
Literals: map[string]any{
"Title": "Home",
"Version": env.VERSION,
"Site": env.Database.Site,
},
})
return func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" {
handler(w, r)
return
}
if r.Method == http.MethodGet {
notFoundHandler(w, r)
return
}
w.WriteHeader(http.StatusMethodNotAllowed)
}
}
+197
View File
@@ -0,0 +1,197 @@
package routes
import (
"bakonpancakz/homepage/include"
"bytes"
"compress/gzip"
"crypto/md5"
"fmt"
"io/fs"
"log"
"mime"
"net/http"
"os"
"path"
"strings"
"sync"
"text/template"
)
type HttpHandler func(w http.ResponseWriter, r *http.Request)
var hashTable sync.Map
type ServeTemplateInfo struct {
FileSystem fs.FS
FilePaths []string
Filename string
Literals map[string]any
StatusCode int
ContentType string
}
func prepareStaticTemplate(info *ServeTemplateInfo, ds *[]byte, dc *[]byte, dh *string) error {
// Render Document
tmpl, err := template.ParseFS(info.FileSystem, info.FilePaths...)
if err != nil {
return err
}
var b bytes.Buffer
if err := tmpl.ExecuteTemplate(&b, info.Filename, info.Literals); err != nil {
return err
}
// Compress Document
var c bytes.Buffer
g := gzip.NewWriter(&c)
g.Write(b.Bytes())
g.Close()
// Store Document
*ds = b.Bytes()
*dc = c.Bytes()
*dh = fmt.Sprintf("%X", md5.Sum(b.Bytes()))
return nil
}
func ServeStaticTemplate(info *ServeTemplateInfo) func(w http.ResponseWriter, r *http.Request) {
// Defaults
if info.StatusCode == 0 {
info.StatusCode = http.StatusOK
}
if info.ContentType == "" {
info.ContentType = "text/html"
}
for i, p := range info.FilePaths {
info.FilePaths[i] = include.PreparePath(info.FileSystem, p)
}
// Render Document
var (
documentStandard []byte
documentCompress []byte
documentHash string
)
if err := prepareStaticTemplate(info, &documentStandard, &documentCompress, &documentHash); err != nil {
log.Fatalln("[routes/template] Cannot Prepare Template:", err)
}
// Serve Function
return func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
if !include.Embedded {
if err := prepareStaticTemplate(info, &documentStandard, &documentCompress, &documentHash); err != nil {
log.Println("[routes/template] Refresh Error:", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
}
// Check Browser Cache
if r.Header.Get("If-None-Match") == documentHash {
w.WriteHeader(http.StatusNotModified)
return
}
// Send Document
w.Header().Set("ETag", documentHash)
w.Header().Set("Content-Type", info.ContentType)
w.Header().Set("Cache-Control", "public, max-age=3600, must-revalidate")
if strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
w.Header().Set("Content-Encoding", "gzip")
w.Write(documentCompress)
} else {
w.Write(documentStandard)
}
}
}
func ServeStaticFile(filepath string) HttpHandler {
var (
fileHash string
fileData []byte
fileMime string
)
// Prepare File
b, err := include.ReadFile(include.Public, filepath)
if err != nil {
log.Fatalln("[routes/static] Cannot Prepare File:", filepath, err)
}
fileMime = mime.TypeByExtension(path.Ext(filepath))
fileHash = fmt.Sprintf("%X", md5.Sum(b))
fileData = b
return func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
if r.Header.Get("If-None-Match") == fileHash {
w.WriteHeader(http.StatusNotModified)
return
}
w.Header().Add("ETag", fileHash)
w.Header().Add("X-Robots-Tag", "noindex")
w.Header().Add("Content-Type", fileMime)
w.Header().Add("Cache-Control", "public, max-age=604800, immutable")
w.Write(fileData)
}
}
func ServePublicFile(w http.ResponseWriter, r *http.Request, filepath string) {
if r.Method != http.MethodGet {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
// Check Cache Hash
if hash, ok := hashTable.Load(filepath); ok {
if r.Header.Get("If-None-Match") == hash.(string) {
w.WriteHeader(http.StatusNotModified)
return
}
}
// Read File Contents
filedata, err := include.ReadFile(include.Public, filepath)
if err != nil {
if os.IsNotExist(err) {
w.WriteHeader(http.StatusNotFound)
return
}
log.Println("[http] Read Asset Error:", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
// Generate Cache Hash
filehash := fmt.Sprintf("%X", md5.Sum(filedata))
hashTable.Store(filepath, filehash)
fileMime := mime.TypeByExtension(path.Ext(filepath))
// Send Contents
w.Header().Add("ETag", filehash)
w.Header().Add("X-Robots-Tag", "noindex")
w.Header().Add("Content-Type", fileMime)
w.Header().Add("Cache-Control", "public, max-age=604800, immutable")
if len(filedata) < (1<<20) &&
!strings.Contains(fileMime, "image") &&
!strings.Contains(fileMime, "audio") &&
!strings.Contains(fileMime, "video") &&
strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
w.Header().Set("Content-Encoding", "gzip")
g := gzip.NewWriter(w)
g.Write(filedata)
g.Close()
} else {
w.Write(filedata)
}
}