add gemlog

This commit is contained in:
Travis Shears 2025-10-05 17:01:13 +02:00
parent 93cdcef885
commit 5c94a3820a
10 changed files with 145 additions and 30 deletions

3
.env
View file

@ -1,3 +0,0 @@
export POCKET_BASE_PW=ifk2HEbM
export POCKET_BASE_HOST=http://aemos:5000
export POCKET_BASE_USER=micro_blog_fetchers

2
.gitignore vendored
View file

@ -1,3 +1,5 @@
keys/ keys/
gemini-server gemini-server
gemini_site gemini_site
.env

1
go.mod
View file

@ -3,5 +3,6 @@ module gemini_site
go 1.25.0 go 1.25.0
require ( require (
git.travisshears.com/travisshears/gemlog-cli v1.0.1
github.com/kulak/gemini v1.2.2 github.com/kulak/gemini v1.2.2
) )

5
go.sum
View file

@ -1,3 +1,5 @@
git.travisshears.com/travisshears/gemlog-cli v1.0.1 h1:0AcFwrnukwcWHuePEROG3WDSGbcE8WoFvJTQxrTaszc=
git.travisshears.com/travisshears/gemlog-cli v1.0.1/go.mod h1:N6l94N174EhDOIHU0/RlJ0PWrxB0BMa0W6LcpgAtvCE=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/kulak/gemini v1.2.2 h1:wPFOAFFdOf9ZaHcpMwTq1xYUWxmyV3h0uQl0OXCGa+A= github.com/kulak/gemini v1.2.2 h1:wPFOAFFdOf9ZaHcpMwTq1xYUWxmyV3h0uQl0OXCGa+A=
@ -8,5 +10,6 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

102
internal/gemlog/gemlog.go Normal file
View file

@ -0,0 +1,102 @@
package gemlog
import (
"fmt"
"log/slog"
"os"
"strconv"
"strings"
gemlog "git.travisshears.com/travisshears/gemlog-cli/gemlog"
gemini "github.com/kulak/gemini"
)
type Gemlog struct {
config *gemlog.Config
}
func NewGemlog() *Gemlog {
port, err := strconv.Atoi(os.Getenv("COUCH_DB_PORT"))
if err != nil {
slog.Error("Failed to parse COUCH_DB_PORT", "error", err)
os.Exit(1)
}
config := gemlog.Config{
CouchDB: gemlog.CouchDBConfig{
Host: os.Getenv("COUCHDB_HOST"),
Port: port,
User: os.Getenv("COUCHDB_USER"),
Password: os.Getenv("COUCHDB_PASSWORD"),
},
}
g := &Gemlog{
config: &config,
}
return g
}
func (g *Gemlog) HandleRequest(w gemini.ResponseWriter, req *gemini.Request) {
path := req.URL.Path
switch {
case path == "/gemlog" || path == "/gemlog/":
g.serveIndex(w, req)
case strings.HasPrefix(path, "/gemlog/post/"):
slug := strings.TrimPrefix(path, "/gemlog/post/")
g.servePost(w, req, slug)
default:
w.WriteStatusMsg(gemini.StatusNotFound, "Page not found")
}
}
func (g *Gemlog) serveIndex(w gemini.ResponseWriter, req *gemini.Request) {
w.WriteStatusMsg(gemini.StatusSuccess, "text/gemini")
var content strings.Builder
page, err := os.ReadFile("./pages/gemlog.gmi")
if err != nil {
slog.Error("Problem reading gemlog page", "error", err)
return
}
content.Write(page)
content.WriteString("\n")
posts, err := gemlog.ListGemLogs(g.config)
if err != nil {
content.WriteString("Error fetching posts: " + err.Error() + "\n\n")
}
if len(posts) == 0 {
content.WriteString("No posts found.\n\n")
} else {
content.WriteString("## Posts\n\n")
for _, post := range posts {
content.WriteString(fmt.Sprintf("=> /gemlog/post/%s %s - %s\n", post.Slug, post.Date.Format("2006-01-02"), post.Title))
}
}
content.WriteString("## Nav\n\n")
// content.WriteString(fmt.Sprintf("=> /microblog/page/%d Next page\n", pageNum+1))
content.WriteString("=> / Back to home\n")
w.WriteBody([]byte(content.String()))
}
func (g *Gemlog) servePost(w gemini.ResponseWriter, req *gemini.Request, slug string) {
var content strings.Builder
w.WriteStatusMsg(gemini.StatusSuccess, "text/gemini")
post, err := gemlog.ReadGemlogEntryBySlug(g.config, slug)
if err != nil {
content.WriteString("Error fetching gemlog post: " + err.Error() + "\n\n")
}
content.WriteString(fmt.Sprintf("# %s\n", post.Title))
content.WriteString(fmt.Sprintf("posted: %s\n", post.Date.Format("2006-01-02")))
content.WriteString(post.Gemtxt)
content.WriteString("## Nav\n\n")
content.WriteString("=> /gemlog Back to gemlog list\n")
content.WriteString("=> / Back to home\n")
w.WriteBody([]byte(content.String()))
}

View file

@ -0,0 +1,15 @@
package gemlog
import (
gemini "github.com/kulak/gemini"
)
// Handler interface for microblog functionality
type Handler interface {
HandleRequest(w gemini.ResponseWriter, req *gemini.Request)
}
// NewHandler creates a new microblog handler
func NewHandler() Handler {
return NewGemlog()
}

14
main.go
View file

@ -10,14 +10,16 @@ import (
"strings" "strings"
"time" "time"
"gemini_site/internal/microblog" gemlog "gemini_site/internal/gemlog"
microblog "gemini_site/internal/microblog"
"gemini_site/internal/pocketbase" "gemini_site/internal/pocketbase"
gemini "github.com/kulak/gemini" gemini "github.com/kulak/gemini"
) )
type MainHandler struct { type MainHandler struct {
blog microblog.Handler blog microblog.Handler
gemlog gemlog.Handler
} }
func (h MainHandler) ServeGemini(w gemini.ResponseWriter, req *gemini.Request) { func (h MainHandler) ServeGemini(w gemini.ResponseWriter, req *gemini.Request) {
@ -31,6 +33,11 @@ func (h MainHandler) ServeGemini(w gemini.ResponseWriter, req *gemini.Request) {
return return
} }
if strings.HasPrefix(req.URL.Path, "/gemlog") {
h.gemlog.HandleRequest(w, req)
return
}
switch req.URL.Path { switch req.URL.Path {
case "/": case "/":
gemini.ServeFileName("pages/home.gmi", "text/gemini")(w, req) gemini.ServeFileName("pages/home.gmi", "text/gemini")(w, req)
@ -105,7 +112,8 @@ func main() {
pbClient := pocketbase.NewPocketBaseClient() pbClient := pocketbase.NewPocketBaseClient()
handler := MainHandler{ handler := MainHandler{
blog: microblog.NewHandler(pbClient), blog: microblog.NewHandler(pbClient),
gemlog: gemlog.NewHandler(),
} }
err := gemini.ListenAndServe(host, cert, key, gemini.TrapPanic(handler.ServeGemini)) err := gemini.ListenAndServe(host, cert, key, gemini.TrapPanic(handler.ServeGemini))

9
pages/gemlog.gmi Normal file
View file

@ -0,0 +1,9 @@
# Gemlog
Welcome to my gemlog!
I'm still figureing out exactly what I'll be posting here as I already maintain a tech blog on my personal site. For now it will mostly be about my experience with the gemini protocal. Maybe some tutorials on how to self-host a capsule.
Abandoned capsules are a commmon sight here in gemspace. While this gemlog might go stale, the content on the micro-blog page is dynamic and as long as I'm posting to social media will have fresh content. Remember to check that page out as well.
=> /microblog Microblog

View file

@ -20,6 +20,7 @@ So far I've joined the following communities here in gemspace:
## Capsule Features ## Capsule Features
=> /gemlog Gemlog - Gemini exclusive blog
=> /microblog Microblog - Aggregation of all my microblog posts => /microblog Microblog - Aggregation of all my microblog posts
## Site updates ## Site updates

View file

@ -1,23 +0,0 @@
package main
import (
"fmt"
"log/slog"
"gemini_site/internal/microblog"
"gemini_site/internal/pocketbase"
)
func main() {
pbClient := pocketbase.NewPocketBaseClient()
mb := microblog.NewMicroBlog(pbClient)
res, err := mb.GetRecentPosts(10)
// res, err := pbClient.GetList("micro_blog_posts", 1, 10, "-posted")
fmt.Println("Getting page 1 of microblog posts, 10 posts")
// posts, err := client.GetPosts(1)
if err != nil {
slog.Error("Error getting posts", "error", err)
} else {
slog.Info("Got microblog posts", "posts", res)
}
}