feat: define links in separate json file
This commit is contained in:
@@ -20,6 +20,7 @@ func main() {
|
|||||||
mux.HandleFunc("/", handlers.Index)
|
mux.HandleFunc("/", handlers.Index)
|
||||||
mux.HandleFunc("/css/", handlers.CSS)
|
mux.HandleFunc("/css/", handlers.CSS)
|
||||||
mux.HandleFunc("/fonts/", handlers.Fonts)
|
mux.HandleFunc("/fonts/", handlers.Fonts)
|
||||||
|
mux.HandleFunc("/images/", handlers.Images)
|
||||||
|
|
||||||
loggingMux := middleware.Logging(mux)
|
loggingMux := middleware.Logging(mux)
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,22 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"href": "https://youtube.stevanfreeborn.com",
|
||||||
|
"icon": "fa-brands fa-square-youtube fa-lg",
|
||||||
|
"text": "YouTube"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "https://github.stevanfreeborn.com",
|
||||||
|
"icon": "fa-brands fa-square-github fa-lg",
|
||||||
|
"text": "GitHub"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "https://blog.stevanfreeborn.com",
|
||||||
|
"icon": "fa-solid fa-square-rss fa-lg",
|
||||||
|
"text": "Blog"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "https://linkedin.stevanfreeborn.com",
|
||||||
|
"icon": "fa-brands fa-square-linkedin fa-lg",
|
||||||
|
"text": "LinkedIn"
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -12,4 +12,8 @@ var (
|
|||||||
CSS embed.FS
|
CSS embed.FS
|
||||||
//go:embed "fonts/*"
|
//go:embed "fonts/*"
|
||||||
Fonts embed.FS
|
Fonts embed.FS
|
||||||
|
//go:embed "json/*"
|
||||||
|
JSON embed.FS
|
||||||
|
//go:embed "images/*"
|
||||||
|
Images embed.FS
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
integrity="sha512-2SwdPD6INVrV/lHTZbO2nodKhrnDdJK9/kg2XD1r9uGqPo1cUbujc+IYdlYdEErWNu69gVcYgdxlmVmzTWnetw=="
|
integrity="sha512-2SwdPD6INVrV/lHTZbO2nodKhrnDdJK9/kg2XD1r9uGqPo1cUbujc+IYdlYdEErWNu69gVcYgdxlmVmzTWnetw=="
|
||||||
crossorigin="anonymous" referrerpolicy="no-referrer" />
|
crossorigin="anonymous" referrerpolicy="no-referrer" />
|
||||||
<link href="css/style.css" rel="stylesheet">
|
<link href="css/style.css" rel="stylesheet">
|
||||||
|
<link rel="icon" type="image/x-icon" href="images/favicon.ico">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@@ -18,35 +19,20 @@
|
|||||||
alt="A man with short brown hair, a grey hoodie, and a white t-shirt smiles at the camera. He has a white earbud in his left ear. The background is a blurred office setting with warm lighting visible through glass partitions." />
|
alt="A man with short brown hair, a grey hoodie, and a white t-shirt smiles at the camera. He has a white earbud in his left ear. The background is a blurred office setting with warm lighting visible through glass partitions." />
|
||||||
<div class="bio">
|
<div class="bio">
|
||||||
<h1>Stevan Freeborn</h1>
|
<h1>Stevan Freeborn</h1>
|
||||||
<p>I'm a {{ .Age }}-year old dad of 2 who enjoys drinking coffee, lifting weights, and solving problems with code.</p>
|
<p>I'm a {{ .Age }}-year old dad of 2 who enjoys drinking coffee, lifting weights, and solving problems with code.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<nav class="links-container">
|
<nav class="links-container">
|
||||||
<ul>
|
<ul>
|
||||||
|
{{ range .Links }}
|
||||||
<li>
|
<li>
|
||||||
<a class="link" href="https://youtube.stevanfreeborn.com">
|
<a class="link" href="{{ .Href }}">
|
||||||
<i class="fa-brands fa-square-youtube fa-lg"></i>
|
<i class="{{ .Icon }}"></i>
|
||||||
<span>YouTube</span>
|
<span>{{ .Text }}</span>
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a class="link" href="https://github.stevanfreeborn.com">
|
|
||||||
<i class="fa-brands fa-square-github fa-lg"></i>
|
|
||||||
<span>GitHub</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a class="link" href="https://blog.stevanfreeborn.com">
|
|
||||||
<i class="fa-solid fa-square-rss fa-lg"></i>
|
|
||||||
<span>Blog</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a class="link" href="https://linkedin.stevanfreeborn.com">
|
|
||||||
<i class="fa-brands fa-square-linkedin fa-lg"></i>
|
|
||||||
<span>LinkedIn</span>
|
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
{{ end }}
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io/fs"
|
||||||
"math"
|
"math"
|
||||||
"net/http"
|
"net/http"
|
||||||
"text/template"
|
"text/template"
|
||||||
@@ -10,21 +12,24 @@ import (
|
|||||||
"github.com/StevanFreeborn/links.stevanfreeborn.com/internal/assets"
|
"github.com/StevanFreeborn/links.stevanfreeborn.com/internal/assets"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const LINKS_JSON_PATH = "json/links.json"
|
||||||
const DAYS_IN_YEAR = 365
|
const DAYS_IN_YEAR = 365
|
||||||
const HOURS_IN_DAY = 24
|
const HOURS_IN_DAY = 24
|
||||||
|
|
||||||
var birthday time.Time = time.Date(1993, time.April, 21, 0, 0, 0, 0, time.UTC)
|
var birthday time.Time = time.Date(1993, time.April, 21, 0, 0, 0, 0, time.UTC)
|
||||||
|
|
||||||
|
type Link struct {
|
||||||
|
Href string `json:"href"`
|
||||||
|
Icon string `json:"icon"`
|
||||||
|
Text string `json:"text"`
|
||||||
|
}
|
||||||
|
|
||||||
type IndexViewModel struct {
|
type IndexViewModel struct {
|
||||||
Age float64
|
Age float64
|
||||||
|
Links []Link
|
||||||
}
|
}
|
||||||
|
|
||||||
func Index(w http.ResponseWriter, r *http.Request) {
|
func Index(w http.ResponseWriter, r *http.Request) {
|
||||||
// TODO: Links coming in external JSON data
|
|
||||||
// this could be static file in the repo
|
|
||||||
// or it could be fetched
|
|
||||||
// { link: "link", text: "text", "icon": "icon" }
|
|
||||||
|
|
||||||
t, err := template.ParseFS(assets.Templates, "templates/index.gohtml")
|
t, err := template.ParseFS(assets.Templates, "templates/index.gohtml")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -32,10 +37,25 @@ func Index(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
linksFile, err := assets.JSON.Open(LINKS_JSON_PATH)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Unable to load links", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
links, err := readLinksFromJSON(linksFile)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Unable to parse links", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
age := time.Since(birthday).Hours() / HOURS_IN_DAY / DAYS_IN_YEAR
|
age := time.Since(birthday).Hours() / HOURS_IN_DAY / DAYS_IN_YEAR
|
||||||
|
|
||||||
viewModel := IndexViewModel{
|
viewModel := IndexViewModel{
|
||||||
Age: math.Floor(age),
|
Age: math.Floor(age),
|
||||||
|
Links: links,
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Execute(w, viewModel)
|
t.Execute(w, viewModel)
|
||||||
@@ -48,3 +68,22 @@ func CSS(w http.ResponseWriter, r *http.Request) {
|
|||||||
func Fonts(w http.ResponseWriter, r *http.Request) {
|
func Fonts(w http.ResponseWriter, r *http.Request) {
|
||||||
http.ServeFileFS(w, r, assets.Fonts, r.URL.Path)
|
http.ServeFileFS(w, r, assets.Fonts, r.URL.Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Images(w http.ResponseWriter, r *http.Request) {
|
||||||
|
http.ServeFileFS(w, r, assets.Images, r.URL.Path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func readLinksFromJSON(file fs.File) ([]Link, error) {
|
||||||
|
defer file.Close()
|
||||||
|
var links []Link
|
||||||
|
|
||||||
|
decoder := json.NewDecoder(file)
|
||||||
|
|
||||||
|
err := decoder.Decode(&links)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return links, nil
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user