use new PocketBaseClient in microblog package

This commit is contained in:
Travis Shears 2025-09-28 09:21:17 +02:00
parent 2837293474
commit 506a12c06c
3 changed files with 119 additions and 115 deletions

View file

@ -1,6 +1,8 @@
package microblog package microblog
import ( import (
"gemini_site/internal/pocketbase"
gemini "github.com/kulak/gemini" gemini "github.com/kulak/gemini"
) )
@ -10,6 +12,6 @@ type Handler interface {
} }
// NewHandler creates a new microblog handler // NewHandler creates a new microblog handler
func NewHandler() Handler { func NewHandler(pbClient *pocketbase.PocketBaseClient) Handler {
return NewMicroBlog() return NewMicroBlog(pbClient)
} }

View file

@ -3,8 +3,8 @@ package microblog
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"sort" "gemini_site/internal/pocketbase"
"strings" "log/slog"
"time" "time"
gemini "github.com/kulak/gemini" gemini "github.com/kulak/gemini"
@ -48,149 +48,149 @@ type pbPost struct {
// MicroBlog manages blog posts // MicroBlog manages blog posts
type MicroBlog struct { type MicroBlog struct {
posts []Post pbClient *pocketbase.PocketBaseClient
} }
// NewMicroBlog creates a new microblog instance // NewMicroBlog creates a new microblog instance
func NewMicroBlog() *MicroBlog { func NewMicroBlog(pbClient *pocketbase.PocketBaseClient) *MicroBlog {
mb := &MicroBlog{ mb := &MicroBlog{
posts: make([]Post, 0), pbClient: pbClient,
} }
// Add some sample posts
mb.addSamplePosts()
return mb return mb
} }
// addSamplePosts adds some initial content // // Add some sample posts
func (mb *MicroBlog) addSamplePosts() { // mb.addSamplePosts()
samplePosts := []Post{
{
ID: "1",
Title: "Welcome to the Gemini Microblog",
Content: "This is the first post on our Gemini-powered microblog! It's simple, fast, and distraction-free.",
Author: "Admin",
Timestamp: time.Now().Add(-2 * time.Hour),
},
{
ID: "2",
Title: "The Beauty of Simplicity",
Content: "Gemini protocol encourages us to focus on content over presentation. This microblog embodies that philosophy.",
Author: "Admin",
Timestamp: time.Now().Add(-1 * time.Hour),
},
}
mb.posts = append(mb.posts, samplePosts...) // return mb
} // }
// addSamplePosts adds some initial content
// func (mb *MicroBlog) addSamplePosts() {
// samplePosts := []Post{
// {
// ID: "1",
// Title: "Welcome to the Gemini Microblog",
// Content: "This is the first post on our Gemini-powered microblog! It's simple, fast, and distraction-free.",
// Author: "Admin",
// Timestamp: time.Now().Add(-2 * time.Hour),
// },
// {
// ID: "2",
// Title: "The Beauty of Simplicity",
// Content: "Gemini protocol encourages us to focus on content over presentation. This microblog embodies that philosophy.",
// Author: "Admin",
// Timestamp: time.Now().Add(-1 * time.Hour),
// },
// }
// mb.posts = append(mb.posts, samplePosts...)
// }
// AddPost adds a new post to the blog // AddPost adds a new post to the blog
func (mb *MicroBlog) AddPost(title, content, author string) string { // func (mb *MicroBlog) AddPost(title, content, author string) string {
id := fmt.Sprintf("%d", time.Now().Unix()) // id := fmt.Sprintf("%d", time.Now().Unix())
post := Post{ // post := Post{
ID: id, // ID: id,
Title: title, // Title: title,
Content: content, // Content: content,
Author: author, // Author: author,
Timestamp: time.Now(), // Timestamp: time.Now(),
} // }
mb.posts = append(mb.posts, post) // mb.posts = append(mb.posts, post)
return id // return id
} // }
// GetPost retrieves a post by ID // GetPost retrieves a post by ID
func (mb *MicroBlog) GetPost(id string) (*Post, bool) { // func (mb *MicroBlog) GetPost(id string) (*Post, bool) {
for _, post := range mb.posts { // for _, post := range mb.posts {
if post.ID == id { // if post.ID == id {
return &post, true // return &post, true
} // }
} // }
return nil, false // return nil, false
} // }
// GetRecentPosts returns the most recent posts // GetRecentPosts returns the most recent posts
func (mb *MicroBlog) GetRecentPosts(limit int) []Post { func (mb *MicroBlog) GetRecentPosts(limit int) ([]post, error) {
// Sort posts by timestamp (newest first) res, err := mb.pbClient.GetList("micro_blog_posts", 1, limit, "-posted")
sortedPosts := make([]Post, len(mb.posts)) if err != nil {
copy(sortedPosts, mb.posts) return nil, fmt.Errorf("failed to fetch posts from pocketbase: %w", err)
sort.Slice(sortedPosts, func(i, j int) bool {
return sortedPosts[i].Timestamp.After(sortedPosts[j].Timestamp)
})
if limit > 0 && len(sortedPosts) > limit {
return sortedPosts[:limit]
} }
return sortedPosts var rawPosts []pbPost
if err := json.Unmarshal(res.Items, &rawPosts); err != nil {
return nil, fmt.Errorf("failed to decode response: %w", err)
}
slog.Info("Posts from pocketbase", "rawPosts", rawPosts)
return nil, fmt.Errorf("todo: %w", err)
} }
// HandleBlogRequest handles Gemini requests for the microblog // HandleBlogRequest handles Gemini requests for the microblog
func (mb *MicroBlog) HandleBlogRequest(w gemini.ResponseWriter, req *gemini.Request) { func (mb *MicroBlog) HandleBlogRequest(w gemini.ResponseWriter, req *gemini.Request) {
path := req.URL.Path // path := req.URL.Path
switch { // switch {
case path == "/microblog" || path == "/microblog/": // case path == "/microblog" || path == "/microblog/":
mb.serveBlogIndex(w, req) // mb.serveBlogIndex(w, req)
case strings.HasPrefix(path, "/microblog/post/"): // case strings.HasPrefix(path, "/microblog/post/"):
postID := strings.TrimPrefix(path, "/microblog/post/") // postID := strings.TrimPrefix(path, "/microblog/post/")
mb.servePost(w, req, postID) // mb.servePost(w, req, postID)
default: // default:
w.WriteStatusMsg(gemini.StatusNotFound, "Blog page not found") // w.WriteStatusMsg(gemini.StatusNotFound, "Blog page not found")
} // }
} }
// serveBlogIndex serves the main blog page with recent posts // serveBlogIndex serves the main blog page with recent posts
func (mb *MicroBlog) serveBlogIndex(w gemini.ResponseWriter, req *gemini.Request) { // func (mb *MicroBlog) serveBlogIndex(w gemini.ResponseWriter, req *gemini.Request) {
w.WriteStatusMsg(gemini.StatusSuccess, "text/gemini") // w.WriteStatusMsg(gemini.StatusSuccess, "text/gemini")
var content strings.Builder // var content strings.Builder
content.WriteString("# Gemini Microblog\n\n") // content.WriteString("# Gemini Microblog\n\n")
posts := mb.GetRecentPosts(10) // posts := mb.GetRecentPosts(10)
if len(posts) == 0 { // if len(posts) == 0 {
content.WriteString("No posts yet. Be the first to write something!\n\n") // content.WriteString("No posts yet. Be the first to write something!\n\n")
} else { // } else {
content.WriteString("## Recent Posts\n\n") // content.WriteString("## Recent Posts\n\n")
for _, post := range posts { // for _, post := range posts {
content.WriteString(fmt.Sprintf("=> /blog/post/%s %s\n", post.ID, post.Title)) // content.WriteString(fmt.Sprintf("=> /blog/post/%s %s\n", post.ID, post.Title))
content.WriteString(fmt.Sprintf(" By %s on %s\n\n", // content.WriteString(fmt.Sprintf(" By %s on %s\n\n",
post.Author, // post.Author,
post.Timestamp.Format("2006-01-02 15:04"))) // post.Timestamp.Format("2006-01-02 15:04")))
} // }
} // }
content.WriteString("## Actions\n\n") // content.WriteString("## Actions\n\n")
content.WriteString("=> /blog/new Write a new post\n") // content.WriteString("=> /blog/new Write a new post\n")
content.WriteString("=> / Back to home\n") // content.WriteString("=> / Back to home\n")
w.WriteBody([]byte(content.String())) // w.WriteBody([]byte(content.String()))
} // }
// servePost serves a single blog post // servePost serves a single blog post
func (mb *MicroBlog) servePost(w gemini.ResponseWriter, req *gemini.Request, postID string) { // func (mb *MicroBlog) servePost(w gemini.ResponseWriter, req *gemini.Request, postID string) {
post, found := mb.GetPost(postID) // post, found := mb.GetPost(postID)
if !found { // if !found {
w.WriteStatusMsg(gemini.StatusNotFound, "Post not found") // w.WriteStatusMsg(gemini.StatusNotFound, "Post not found")
return // return
} // }
w.WriteStatusMsg(gemini.StatusSuccess, "text/gemini") // w.WriteStatusMsg(gemini.StatusSuccess, "text/gemini")
var content strings.Builder // var content strings.Builder
content.WriteString(fmt.Sprintf("# %s\n\n", post.Title)) // content.WriteString(fmt.Sprintf("# %s\n\n", post.Title))
content.WriteString(fmt.Sprintf("By %s on %s\n\n", // content.WriteString(fmt.Sprintf("By %s on %s\n\n",
post.Author, // post.Author,
post.Timestamp.Format("2006-01-02 15:04"))) // post.Timestamp.Format("2006-01-02 15:04")))
content.WriteString("---\n\n") // content.WriteString("---\n\n")
content.WriteString(post.Content) // content.WriteString(post.Content)
content.WriteString("\n\n---\n\n") // content.WriteString("\n\n---\n\n")
content.WriteString("=> /blog Back to blog\n") // content.WriteString("=> /blog Back to blog\n")
content.WriteString("=> / Back to home\n") // content.WriteString("=> / Back to home\n")
w.WriteBody([]byte(content.String())) // w.WriteBody([]byte(content.String()))
} // }

View file

@ -4,15 +4,17 @@ import (
"fmt" "fmt"
"log" "log"
"gemini_site/internal/microblog"
"gemini_site/internal/pocketbase" "gemini_site/internal/pocketbase"
) )
func main() { func main() {
pbClient := pocketbase.NewPocketBaseClient() pbClient := pocketbase.NewPocketBaseClient()
res, err := pbClient.GetList("micro_blog_posts", 1, 10, "-posted") mb := microblog.NewMicroBlog(pbClient)
res, err := mb.GetRecentPosts(2)
// res, err := pbClient.GetList("micro_blog_posts", 1, 10, "-posted")
fmt.Println("Getting page 1 of microblog posts") fmt.Println("Getting page 1 of microblog posts")
// posts, err := client.GetPosts(1) // posts, err := client.GetPosts(1)
if err != nil { if err != nil {
log.Printf("Error getting posts: %v", err) log.Printf("Error getting posts: %v", err)
} else { } else {