This commit is contained in:
2024-01-17 12:02:03 -07:00
parent 161cc95538
commit f8ce4e3b48
43 changed files with 1614 additions and 21 deletions

31
lib/email.go Normal file
View File

@ -0,0 +1,31 @@
package lib
import (
"fmt"
"os"
"github.com/resendlabs/resend-go"
)
var client *resend.Client
// init function
func init() {
client = resend.NewClient(os.Getenv("RESEND_API_KEY"))
}
func SendEmail(to_email string, from_email string, from_name string, html string, subject string) {
params := &resend.SendEmailRequest{
From: from_name + "<" + from_email + ">",
To: []string{to_email},
Html: html,
Subject: subject,
}
sent, err := client.Emails.Send(params)
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Println(sent.Id)
}

59
lib/markdown.go Normal file
View File

@ -0,0 +1,59 @@
package lib
import (
"bufio"
"bytes"
"errors"
"fmt"
"os"
"strings"
"github.com/yuin/goldmark"
"gopkg.in/yaml.v2"
)
func ExtractFrontMatter(file os.DirEntry, dir string) (CardLink, error) {
f, err := os.Open(dir + file.Name())
if err != nil {
return CardLink{}, fmt.Errorf("failed to open file: %w", err)
}
defer f.Close()
scanner := bufio.NewScanner(f)
var lines []string
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
if err := scanner.Err(); err != nil {
return CardLink{}, fmt.Errorf("failed to read file: %w", err)
}
content := strings.Join(lines, "\n")
splitContent := strings.SplitN(content, "---", 3)
if len(splitContent) < 3 {
return CardLink{}, fmt.Errorf("invalid file format: %s", file.Name())
}
frontMatter := CardLink{}
if err := yaml.Unmarshal([]byte(splitContent[1]), &frontMatter); err != nil {
return CardLink{}, fmt.Errorf("failed to unmarshal frontmatter: %w", err)
}
md := goldmark.New(goldmark.WithExtensions())
var buf bytes.Buffer
if err := md.Convert([]byte(splitContent[2]), &buf); err != nil {
return CardLink{}, fmt.Errorf("failed to convert markdown: %w", err)
}
return frontMatter, nil
}
func SplitFrontmatter(md []byte) (frontmatter []byte, content []byte, err error) {
parts := bytes.SplitN(md, []byte("---"), 3)
if len(parts) < 3 {
return nil, nil, errors.New("invalid or missing frontmatter")
}
return parts[1], parts[2], nil
}

57
lib/markdown_test.go Normal file
View File

@ -0,0 +1,57 @@
package lib_test
import (
"io/fs"
"log"
"os"
"path/filepath"
"testing"
"github.com/alecthomas/assert/v2"
"goth.stack/lib"
)
func TestExtractFrontMatter(t *testing.T) {
// Create a temporary file with some front matter
tmpfile, err := os.CreateTemp("../content", "example.*.md")
println(tmpfile.Name())
if err != nil {
log.Fatal(err)
}
defer os.Remove(tmpfile.Name()) // clean up
text := `---
name: "Test Title"
description: "Test Description"
---
# Test Content
`
if _, err := tmpfile.Write([]byte(text)); err != nil {
log.Fatal(err)
}
if err := tmpfile.Close(); err != nil {
log.Fatal(err)
}
// Get the directory entry for the temporary file
dirEntry, err := os.ReadDir(filepath.Dir(tmpfile.Name()))
if err != nil {
log.Fatal(err)
}
var tmpFileEntry fs.DirEntry
for _, entry := range dirEntry {
if entry.Name() == filepath.Base(tmpfile.Name()) {
tmpFileEntry = entry
break
}
}
// Now we can test ExtractFrontMatter
frontMatter, err := lib.ExtractFrontMatter(tmpFileEntry, "../content/")
assert.NoError(t, err)
assert.Equal(t, "Test Title", frontMatter.Name)
assert.Equal(t, "Test Description", frontMatter.Description)
}

48
lib/redis.go Normal file
View File

@ -0,0 +1,48 @@
package lib
import (
"context"
"log"
"os"
"github.com/joho/godotenv"
"github.com/redis/go-redis/v9"
)
var ctx = context.Background()
var RedisClient *redis.Client
func NewClient() *redis.Client {
godotenv.Load(".env")
redis_host := os.Getenv("REDIS_HOST")
redis_password := os.Getenv("REDIS_PASSWORD")
log.Printf("Connecting to Redis at %s", redis_host)
return redis.NewClient(&redis.Options{
Addr: redis_host,
Password: redis_password,
DB: 0,
})
}
func Publish(client *redis.Client, channel string, message string) error {
if client == nil {
client = NewClient()
}
return client.Publish(ctx, channel, message).Err()
}
func Subscribe(client *redis.Client, channel string) (*redis.PubSub, string) {
if client == nil {
client = NewClient()
}
pubsub := client.Subscribe(ctx, channel)
_, err := pubsub.Receive(ctx)
if err != nil {
log.Fatalf("Error receiving subscription: %v", err)
}
return pubsub, channel
}

27
lib/redis_test.go Normal file
View File

@ -0,0 +1,27 @@
package lib_test
import (
"testing"
"github.com/go-redis/redismock/v9"
"github.com/stretchr/testify/assert"
"goth.stack/lib"
)
func TestPublish(t *testing.T) {
db, mock := redismock.NewClientMock()
mock.ExpectPublish("mychannel", "mymessage").SetVal(1)
err := lib.Publish(db, "mychannel", "mymessage")
assert.NoError(t, err)
assert.NoError(t, mock.ExpectationsWereMet())
}
// Then you can check the channel name in your test
func TestSubscribe(t *testing.T) {
db, _ := redismock.NewClientMock()
pubsub, channel := lib.Subscribe(db, "mychannel")
assert.NotNil(t, pubsub)
assert.Equal(t, "mychannel", channel)
}

20
lib/sse.go Normal file
View File

@ -0,0 +1,20 @@
package lib
func SendSSE(channel string, message string) error {
// Create a channel to receive an error from the goroutine
errCh := make(chan error, 1)
// Use a goroutine to send the message asynchronously
go func() {
err := Publish(RedisClient, channel, message)
errCh <- err // Send the error to the channel
}()
// Wait for the goroutine to finish and check for errors
err := <-errCh
if err != nil {
return err
}
return nil
}

37
lib/types.go Normal file
View File

@ -0,0 +1,37 @@
package lib
import (
"html/template"
)
type IconLink struct {
Name string
Href string
Icon template.HTML
}
type CardLink struct {
Name string
Href string
Description string
Date string
Tags []string
Internal bool
}
type Post struct {
Content template.HTML
Name string
Date string
Tags []string
}
type FrontMatter struct {
Name string
Date string
Tags []string
}
type PubSubMessage struct {
Channel string `json:"channel"`
Data string `json:"data"`
}