This commit is contained in:
Tijl 2024-08-19 22:32:43 +02:00
commit dff92ce496
Signed by: tijl
GPG Key ID: DAE24BFCD722F053
26 changed files with 2480 additions and 0 deletions

29
.air.toml Normal file
View File

@ -0,0 +1,29 @@
root = "."
tmp_dir = ".air-tmp"
[build]
bin = "./.air-tmp/main"
cmd = "just build && go build -o ./.air-tmp/main cmd/server/main.go"
delay = 1000
exclude_dir = ["node_modules"]
exclude_unchanged = false
follow_symlink = false
include_ext = ["go", "html"]
kill_delay = "0s"
log = "build-errors.log"
send_interrupt = false
stop_on_error = true
[color]
build = "yellow"
main = "magenta"
runner = "green"
watcher = "cyan"
[log]
time = false
[misc]
clean_on_exit = true

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
node_modules/
tijl.dev
static/js/interactive.js
static/css/styles.css

32
cmd/server/main.go Normal file
View File

@ -0,0 +1,32 @@
package main
import (
"log"
"git.tijl.dev/tijl/tijl.dev/internal/assets"
"git.tijl.dev/tijl/tijl.dev/internal/i18n"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/template/html/v2"
)
func main() {
assets.LoadSVGs()
i18n.LoadTranslations()
engine := html.New("/home/tijl/projects/tijldev-next/web", ".html")
engine.AddFunc("icon", assets.Svg)
engine.AddFunc("translate", i18n.TranslateFunc())
app := fiber.New(fiber.Config{
Views: engine,
})
app.Get("/", func(c *fiber.Ctx) error {
return c.Render("pages/index", fiber.Map{})
})
app.Static("/static", "./static")
log.Fatal(app.Listen(":3000"))
}

24
go.mod Normal file
View File

@ -0,0 +1,24 @@
module git.tijl.dev/tijl/tijl.dev
go 1.22.5
require (
github.com/gofiber/fiber/v2 v2.52.5
github.com/gofiber/template/html/v2 v2.1.2
)
require (
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/gofiber/template v1.8.3 // indirect
github.com/gofiber/utils v1.1.0 // indirect
github.com/google/uuid v1.5.0 // indirect
github.com/klauspost/compress v1.17.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.51.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect
golang.org/x/sys v0.15.0 // indirect
)

41
go.sum Normal file
View File

@ -0,0 +1,41 @@
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gofiber/fiber/v2 v2.52.5 h1:tWoP1MJQjGEe4GB5TUGOi7P2E0ZMMRx5ZTG4rT+yGMo=
github.com/gofiber/fiber/v2 v2.52.5/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
github.com/gofiber/template v1.8.3 h1:hzHdvMwMo/T2kouz2pPCA0zGiLCeMnoGsQZBTSYgZxc=
github.com/gofiber/template v1.8.3/go.mod h1:bs/2n0pSNPOkRa5VJ8zTIvedcI/lEYxzV3+YPXdBvq8=
github.com/gofiber/template/html/v2 v2.1.2 h1:wkK/mYJ3nIhongTkG3t0QgV4ADdgOYJYVSAF2AHnh8Y=
github.com/gofiber/template/html/v2 v2.1.2/go.mod h1:E98Z/FzvpaSib06aWEgYk6GXNf3ctoyaJH8yW5ay5ak=
github.com/gofiber/utils v1.1.0 h1:vdEBpn7AzIUJRhe+CiTOJdUcTg4Q9RK+pEa0KPbLdrM=
github.com/gofiber/utils v1.1.0/go.mod h1:poZpsnhBykfnY1Mc0KeEa6mSHrS3dV0+oBWyeQmb2e0=
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM=
github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA=
github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g=
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

40
internal/assets/svg.go Normal file
View File

@ -0,0 +1,40 @@
package assets
import (
"html/template"
"log"
"os"
"path/filepath"
"strings"
)
var SVGData map[string]string
func LoadSVGs() {
SVGData = make(map[string]string)
dir := "static/assets"
files, err := filepath.Glob(filepath.Join(dir, "*.svg"))
if err != nil {
log.Fatalf("Error loading SVG files: %v", err)
}
for _, file := range files {
data, err := os.ReadFile(file)
if err != nil {
log.Printf("Error reading SVG file %s: %v", file, err)
continue
}
filename := filepath.Base(file)
key := strings.TrimSuffix(filename, filepath.Ext(filename))
SVGData[key] = string(data)
}
}
func Svg(name string) template.HTML {
if svg, ok := SVGData[name]; ok {
return template.HTML(svg)
}
return ""
}

71
internal/i18n/i18n.go Normal file
View File

@ -0,0 +1,71 @@
package i18n
import (
"encoding/json"
"github.com/gofiber/fiber/v2"
"log"
"os"
"path/filepath"
)
var translations map[string]map[string]string
const DefaultLang = "en"
// LoadTranslations loads translations from JSON files
func LoadTranslations() {
translations = make(map[string]map[string]string)
dir := "locales"
files, err := filepath.Glob(filepath.Join(dir, "*.json"))
if err != nil {
log.Fatalf("Error loading language files: %v", err)
}
for _, file := range files {
lang := filepath.Base(file)
lang = lang[:len(lang)-len(filepath.Ext(lang))]
file, err := os.Open(file)
if err != nil {
log.Printf("Error opening translation file %s: %v", file, err)
continue
}
defer file.Close()
var messages map[string]string
if err := json.NewDecoder(file).Decode(&messages); err != nil {
log.Printf("Error decoding translation file %s: %v", file, err)
continue
}
translations[lang] = messages
}
}
// Translate returns the translated message for the given key and context
func Translate(c *fiber.Ctx, key string) string {
lang := c.Locals("lang").(string)
if messages, ok := translations[lang]; ok {
if message, ok := messages[key]; ok {
return message
}
}
if messages, ok := translations[DefaultLang]; ok {
if message, ok := messages[key]; ok {
return message
}
}
return key
}
// TranslateFunc returns a function for use in templates
func TranslateFunc() func(*fiber.Ctx, string) string {
return func(c *fiber.Ctx, key string) string {
return Translate(c, key)
}
}
// GetTranslations returns the translations map
func GetTranslations() map[string]map[string]string {
return translations
}

View File

@ -0,0 +1,21 @@
package middleware
import (
"git.tijl.dev/tijl/tijl.dev/internal/i18n"
"github.com/gofiber/fiber/v2"
)
func LanguageMiddleware() fiber.Handler {
return func(c *fiber.Ctx) error {
lang := c.Cookies("lang")
if lang == "" {
lang = i18n.DefaultLang
}
if _, exists := i18n.GetTranslations()[lang]; !exists {
lang = i18n.DefaultLang
}
c.Locals("lang", lang)
return c.Next()
}
}

14
justfile Normal file
View File

@ -0,0 +1,14 @@
dev:
vite
npm-build-css:
npm run build:css
npm-build:
npm run build
go-build:
go build cmd/server/main.go -o tijl.dev
build: npm-build-css npm-build go-build

8
locales/en.json Normal file
View File

@ -0,0 +1,8 @@
{
"home": "Home",
"blog": "Blog",
"language": "Language",
"login": "Login",
"english": "English",
"dutch": "Dutch"
}

8
locales/nl.json Normal file
View File

@ -0,0 +1,8 @@
{
"home": "Home",
"blog": "Blog",
"language": "Taal",
"login": "Login",
"english": "Engels",
"dutch": "Nederlands"
}

BIN
main Executable file

Binary file not shown.

1955
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

15
package.json Normal file
View File

@ -0,0 +1,15 @@
{
"name": "tijldev-next",
"scripts": {
"dev": "vite",
"build": "vite build",
"build:css": "tailwindcss -i ./web/styles.css -o ./static/css/styles.css --minify"
},
"devDependencies": {
"@tailwindcss/typography": "^0.5.14",
"daisyui": "^4.12.10",
"tailwindcss": "^3.4.10",
"typescript": "^5.0.0",
"vite": "^4.0.0"
}
}

6
static/assets/blog.svg Normal file
View File

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round"
d="M11 5.882V19.24a1.76 1.76 0 01-3.417.592l-2.147-6.15M18 13a3 3 0 100-6M5.436 13.683A4.001 4.001 0 017 6h1.832c4.1 0 7.625-1.234 9.168-3v14c-1.543-1.766-5.067-3-9.168-3H7a3.988 3.988 0 01-1.564-.317z" />
</svg>

After

Width:  |  Height:  |  Size: 595 B

5
static/assets/home.svg Normal file
View File

@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round"
d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
</svg>

After

Width:  |  Height:  |  Size: 540 B

View File

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round"
d="M3 5h12M9 3v2m1.048 9.5A18.022 18.022 0 016.412 9m6.088 9h7M11 21l5-10 5 10M12.751 5C11.783 10.77 8.07 15.61 3 18.129" />
</svg>

After

Width:  |  Height:  |  Size: 554 B

6
static/assets/login.svg Normal file
View File

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round"
d="M5.121 17.804A13.937 13.937 0 0112 16c2.5 0 4.847.655 6.879 1.804M15 10a3 3 0 11-6 0 3 3 0 016 0zm6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>

After

Width:  |  Height:  |  Size: 526 B

Binary file not shown.

34
tailwind.config.js Normal file
View File

@ -0,0 +1,34 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./web/**/*.{html,js,ts}"],
//purge: ["./templates/**/*.html"],
darkMode: false,
theme: {
extend: {},
},
variants: {},
plugins: [require("@tailwindcss/typography"), require("daisyui")],
daisyui: {
logs: false,
themes: [
{
ttservices: {
primary: "#f7931a",
secondary: "#804df3", // #804df3
accent: "#66c4fa", // #66c4fa
neutral: "#f7931a", // #212121
"base-100": "#181818", // previously 171717
"base-200": "#151515", // previously 151515
"base-300": "#101010", // previously 121212
info: "#0ea5e9",
success: "#22c55e",
warning: "#FBBD23",
error: "#ef4444",
"--rounded-btn": "0.75rem",
//"--btn-text-case": "uppercase",
},
},
],
},
};

24
vite.config.js Normal file
View File

@ -0,0 +1,24 @@
import { defineConfig } from "vite";
export default defineConfig({
plugins: [],
build: {
outDir: "static/js", // Output directory for compiled JS
rollupOptions: {
output: {
entryFileNames: "[name].js",
chunkFileNames: "[name].js",
assetFileNames: "[name].[ext]",
},
input: {
interactive: "web/lib/index.ts",
},
},
},
server: {
port: 3001,
proxy: {
"/api": "http://localhost:3000", // Proxy API requests to the Go server
},
},
});

26
web/layouts/base.html Normal file
View File

@ -0,0 +1,26 @@
<!doctype html>
<html lang="{{block " language" .}}en{{end}}">
<head>
<meta charset="utf-8" />
<link rel="stylesheet" href="static/css/styles.css" />
<!-- <link rel="icon" href="/favicon.png" /> -->
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#f28c18" />
<!-- <script defer data-api="/analytics/api/event" data-domain="tijl.dev" src="/analytics/js/script.js"></script> -->
<title>{{block "title" .}}{{end}}</title>
</head>
<body class="lg:w-4/5 xl:w-3/4 mx-auto">
<header>
{{block "nav" .}}{{end}}
</header>
<main class="mx-8 mt-4">
{{block "content" .}}{{end}}
</main>
<footer>
</footer>
</body>
</html>

0
web/lib/index.ts Normal file
View File

8
web/pages/index.html Normal file
View File

@ -0,0 +1,8 @@
{{ template "layouts/base" . }}
{{define "title"}}Home{{end}}
{{ define "content" }}
<h2>Welcome to My Go App</h2>
<p>This is the homepage.</p>
{{ end }}

76
web/partials/menu.html Normal file
View File

@ -0,0 +1,76 @@
{{define "nav"}}
<nav class="flex">
<div class="mt-4 mx-4 rounded-2xl shadow-2xl bg-base-300 navbar">
<div class="flex-1">
<a href="/" class="btn btn-ghost font-mono text-xl">tijl.dev</a>
</div>
<div class="flex-none">
<details class="dropdown dropdown-bottom dropdown-end">
<summary class="btn btn-ghost btn-square">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24"
stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round"
d="M5 12h.01M12 12h.01M19 12h.01M6 12a1 1 0 11-2 0 1 1 0 012 0zm7 0a1 1 0 11-2 0 1 1 0 012 0zm7 0a1 1 0 11-2 0 1 1 0 012 0z" />
</svg>
</summary>
<ul class="mt-3 z-[1] menu dropdown-content bg-base-300 rounded-box w-52 p-3 shadow-xl gap-1">
<li><a class="flex gap-4" href="/">
<span class="w-5 text-center">
{{ icon "home" }}
</span>
<span class="text-base">Home</span>
</a></li>
<li><a class="flex gap-4" href="/blog">
<span class="w-5 text-center">
{{ icon "blog" }}
</span>
<span class="text-base">Blog</span>
</a></li>
<li class="flex-none">
<details class="dropdown">
<summary><a class="flex gap-4">
<span class="w-5 text-center">
{{ icon "language" }}
</span>
<span class="text-base">Language</span>
</a>
</summary>
<ul class="menu dropdown-content z-[12] bg-base-200 rounded-box w-48 p-3 shadow-xl gap-1">
<li><a class="flex gap-4 active" href="/settings?lang=en">
<span class="w-5 h-5 text-center">
🇬🇧
</span>
<span class="text-base">
English
</span>
</a></li>
<li><a class="flex gap-4" href="/settings?lang=nl">
<span class="w-5 h-5 text-center">
🇳🇱
</span>
<span class="text-base">
Dutch
</span>
</a></li>
</ul>
</details>
</li>
<li><a class="active flex gap-4" href="/login">
<span class="w-5 text-center">
{{ icon "login" }}
</span>
<span class="text-base">Login</span>
</a>
</li>
</ul>
</details>
</div>
</div>
</nav>
{{end}}

26
web/styles.css Normal file
View File

@ -0,0 +1,26 @@
@import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities";
@layer base {
html {
font-family: "Cantarell", "CantarellVF", sans-serif;
}
.no-scrollbar::-webkit-scrollbar {
display: none;
}
.no-scrollbar {
-ms-overflow-style: none;
scrollbar-width: none;
}
}
@font-face {
font-family: 'CantarellVF';
src: url('/static/fonts/Cantarell-VF.woff2'), format('woff2');
font-weight: Regular;
font-style: normal;
font-display: swap;
}