128 lines
3.7 KiB
Go
128 lines
3.7 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/niklasfasching/go-org/org"
|
|
)
|
|
|
|
// Post represents a blog post with metadata and content.
|
|
type Post struct {
|
|
Title string // Title of the post
|
|
Slug string // URL-friendly identifier for the post
|
|
Tags []string // Tags associated with the post
|
|
Description string // Brief description of the post
|
|
Date time.Time // Date when the post was published
|
|
DateStr string // Date when the post was published (YYYY-MM-DD)
|
|
Timestamp int64 // Unix timestamp of the publication date
|
|
Path string // File path to the original .org file
|
|
PathHtml string // URL path to the rendered HTML file
|
|
Content *org.Document // Parsed content of the post
|
|
ReadTime uint8 // Estimated reading time in minutes
|
|
Hero string // URL path to the hero image for the post
|
|
}
|
|
|
|
// listPosts reads the posts directory and returns a slice of Post structs.
|
|
// It filters out non-.org files, parses each .org file, and sorts the posts by date in descending order.
|
|
// Returns the slice of posts and any error encountered during the process.
|
|
func listPosts() ([]Post, error) {
|
|
entries, err := os.ReadDir("posts")
|
|
if err != nil {
|
|
fmt.Println("Error reading directory:", err)
|
|
return nil, err
|
|
}
|
|
|
|
entries = filter(entries, func(e os.DirEntry) bool { return filepath.Ext(e.Name()) == ".org" })
|
|
|
|
var posts []Post
|
|
for _, entry := range entries {
|
|
filePath := filepath.Join("posts", entry.Name())
|
|
|
|
post, err := parseOrg(filePath)
|
|
if err != nil {
|
|
log.Println("[!] Unable to parse ", filePath)
|
|
} else {
|
|
posts = append(posts, post)
|
|
}
|
|
}
|
|
|
|
sort.Slice(posts, func(i, j int) bool {
|
|
return posts[i].Timestamp > posts[j].Timestamp
|
|
})
|
|
|
|
return posts, nil
|
|
}
|
|
|
|
// handleImages processes image and video links in the org document.
|
|
// It updates the URL of the link to point to the media directory.
|
|
// Parameters:
|
|
// - protocol: The protocol of the link (e.g., "file", "http").
|
|
// - description: The description of the link.
|
|
// - link: The URL of the link.
|
|
//
|
|
// Returns:
|
|
// - The processed link node.
|
|
func handleImages(protocol string, description []org.Node, link string) org.Node {
|
|
linked := org.RegularLink{protocol, description, link, false}
|
|
if linked.Kind() == "image" || linked.Kind() == "video" {
|
|
linked.URL = path.Join("/medias/", linked.URL)
|
|
}
|
|
return linked
|
|
}
|
|
|
|
// parseOrg parses an org file and returns a Post struct.
|
|
// It reads the file, extracts metadata, and calculates the reading time.
|
|
// Parameters:
|
|
// - filePath: The path to the org file.
|
|
//
|
|
// Returns:
|
|
// - The parsed Post struct.
|
|
// - Any error encountered during the process.
|
|
func parseOrg(filePath string) (Post, error) {
|
|
file, err := os.Open(filePath)
|
|
if err != nil {
|
|
log.Fatal("Error reading file")
|
|
return Post{}, err
|
|
}
|
|
|
|
config := org.New()
|
|
config.ResolveLink = handleImages
|
|
|
|
orgData := config.Parse(file, filePath)
|
|
|
|
title := orgData.Get("TITLE")
|
|
description := orgData.Get("DESCRIPTION")
|
|
dateStr := strings.Split(orgData.Get("DATE"), "T")[0]
|
|
slug := orgData.Get("SLUG")
|
|
tags := strings.Split(orgData.Get("TAGS"), ", ")
|
|
hero := path.Join("/medias", orgData.Get("HERO"))
|
|
|
|
date, _ := time.Parse("2006-01-02", dateStr)
|
|
ts := date.Unix()
|
|
|
|
raw, _ := os.ReadFile(filePath)
|
|
readTime := len(strings.Split(string(raw), " ")) / 200
|
|
|
|
return Post{
|
|
Title: title,
|
|
Slug: slug,
|
|
Tags: tags,
|
|
Description: description,
|
|
Date: date,
|
|
DateStr: date.Format("2006-01-02"),
|
|
Timestamp: ts,
|
|
Path: filePath,
|
|
PathHtml: "/posts/" + slug + ".html",
|
|
Content: orgData,
|
|
ReadTime: uint8(readTime),
|
|
Hero: hero,
|
|
}, nil
|
|
}
|