https://go.dev/doc/modules/layout This also enables us to later import just the db interaction part say to the gemini capsule backend go project.
206 lines
5.8 KiB
Go
206 lines
5.8 KiB
Go
package gemlog
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"log/slog"
|
|
"net/http"
|
|
"time"
|
|
)
|
|
|
|
func genBasicAuthHeader(user, password string) string {
|
|
auth := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", user, password)))
|
|
return fmt.Sprintf("Basic %s", auth)
|
|
}
|
|
|
|
func ListGemLogs(config *Config) ([]GemlogListEntry, error) {
|
|
url := fmt.Sprintf("%s:%d/gemlog/_design/gemlog-cli/_view/list", config.CouchDB.Host, config.CouchDB.Port)
|
|
req, err := http.NewRequest("GET", url, nil)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create request: %w", err)
|
|
}
|
|
|
|
req.Header.Add("authorization", genBasicAuthHeader(config.CouchDB.User, config.CouchDB.Password))
|
|
|
|
res, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to send request: %w", err)
|
|
}
|
|
defer res.Body.Close()
|
|
|
|
body, err := io.ReadAll(res.Body)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to read response body: %w", err)
|
|
}
|
|
|
|
if res.StatusCode < 200 || res.StatusCode >= 300 {
|
|
return nil, fmt.Errorf("unexpected status code %d: %s", res.StatusCode, string(body))
|
|
}
|
|
|
|
// Parse CouchDB response
|
|
var couchResponse struct {
|
|
TotalRows int `json:"total_rows"`
|
|
Offset int `json:"offset"`
|
|
Rows []struct {
|
|
ID string `json:"id"`
|
|
Key string `json:"key"`
|
|
Value struct {
|
|
Title string `json:"title"`
|
|
Slug string `json:"slug"`
|
|
Date time.Time `json:"date"`
|
|
Rev string `json:"rev"`
|
|
ID string `json:"id"`
|
|
} `json:"value"`
|
|
} `json:"rows"`
|
|
}
|
|
|
|
if err := json.Unmarshal(body, &couchResponse); err != nil {
|
|
return nil, fmt.Errorf("failed to parse response: %w", err)
|
|
}
|
|
|
|
// Extract entries from response
|
|
entries := make([]GemlogListEntry, 0, len(couchResponse.Rows))
|
|
for _, row := range couchResponse.Rows {
|
|
entry := GemlogListEntry{
|
|
ID: row.ID,
|
|
Title: row.Value.Title,
|
|
Slug: row.Value.Slug,
|
|
Date: row.Value.Date,
|
|
Rev: row.Value.Rev,
|
|
}
|
|
entries = append(entries, entry)
|
|
}
|
|
|
|
slog.Info("Found gemlogs", "count", len(entries))
|
|
return entries, nil
|
|
}
|
|
|
|
func ReadGemlogEntry(config *Config, id string) (GemlogEntry, error) {
|
|
url := fmt.Sprintf("%s:%d/gemlog/%s", config.CouchDB.Host, config.CouchDB.Port, id)
|
|
req, err := http.NewRequest("GET", url, nil)
|
|
if err != nil {
|
|
return GemlogEntry{}, fmt.Errorf("failed to create request: %w", err)
|
|
}
|
|
|
|
req.Header.Add("authorization", genBasicAuthHeader(config.CouchDB.User, config.CouchDB.Password))
|
|
req.Header.Add("content-type", "application/json")
|
|
|
|
res, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
return GemlogEntry{}, fmt.Errorf("failed to send request: %w", err)
|
|
}
|
|
defer res.Body.Close()
|
|
|
|
body, err := io.ReadAll(res.Body)
|
|
if err != nil {
|
|
return GemlogEntry{}, fmt.Errorf("failed to read response body: %w", err)
|
|
}
|
|
|
|
if res.StatusCode < 200 || res.StatusCode >= 300 {
|
|
return GemlogEntry{}, fmt.Errorf("unexpected status code %d: %s", res.StatusCode, string(body))
|
|
}
|
|
|
|
var rawData struct {
|
|
Title string `json:"title"`
|
|
GemText string `json:"gemtxt"`
|
|
Slug string `json:"slug"`
|
|
Date time.Time `json:"date"`
|
|
}
|
|
|
|
if err := json.Unmarshal(body, &rawData); err != nil {
|
|
return GemlogEntry{}, fmt.Errorf("failed to parse response: %w", err)
|
|
}
|
|
|
|
return GemlogEntry{
|
|
Title: rawData.Title,
|
|
Slug: rawData.Slug,
|
|
Date: rawData.Date,
|
|
Gemtxt: rawData.GemText,
|
|
Tags: make([]string, 0),
|
|
}, nil
|
|
}
|
|
|
|
func DeleteGemlogEntry(config *Config, id string, rev string) error {
|
|
url := fmt.Sprintf("%s:%d/gemlog/%s?rev=%s", config.CouchDB.Host, config.CouchDB.Port, id, rev)
|
|
req, err := http.NewRequest("DELETE", url, nil)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create request: %w", err)
|
|
}
|
|
|
|
req.Header.Add("authorization", genBasicAuthHeader(config.CouchDB.User, config.CouchDB.Password))
|
|
req.Header.Add("content-type", "application/json")
|
|
|
|
res, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to send request: %w", err)
|
|
}
|
|
defer res.Body.Close()
|
|
|
|
body, err := io.ReadAll(res.Body)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to read response body: %w", err)
|
|
}
|
|
|
|
if res.StatusCode < 200 || res.StatusCode >= 300 {
|
|
return fmt.Errorf("unexpected status code %d: %s", res.StatusCode, string(body))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func SaveGemlogEntry(config *Config, entry *GemlogEntry) error {
|
|
url := fmt.Sprintf("%s:%d/gemlog/", config.CouchDB.Host, config.CouchDB.Port)
|
|
|
|
// Marshal the entry struct to JSON
|
|
jsonData, err := json.Marshal(entry)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to marshal entry: %w", err)
|
|
}
|
|
|
|
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create request: %w", err)
|
|
}
|
|
|
|
req.Header.Add("authorization", genBasicAuthHeader(config.CouchDB.User, config.CouchDB.Password))
|
|
req.Header.Add("content-type", "application/json")
|
|
|
|
res, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to send request: %w", err)
|
|
}
|
|
defer res.Body.Close()
|
|
|
|
body, err := io.ReadAll(res.Body)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to read response body: %w", err)
|
|
}
|
|
|
|
if res.StatusCode < 200 || res.StatusCode >= 300 {
|
|
return fmt.Errorf("unexpected status code %d: %s", res.StatusCode, string(body))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func CheckDBConnection(config *Config) error {
|
|
url := fmt.Sprintf("%s:%d/gemlog/", config.CouchDB.Host, config.CouchDB.Port)
|
|
req, err := http.NewRequest("GET", url, nil)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create request: %w", err)
|
|
}
|
|
req.Header.Add("authorization", genBasicAuthHeader(config.CouchDB.User, config.CouchDB.Password))
|
|
res, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to send request: %w", err)
|
|
}
|
|
defer res.Body.Close()
|
|
|
|
if res.StatusCode == 200 {
|
|
return nil
|
|
}
|
|
return fmt.Errorf("unexpected status code %d", res.StatusCode)
|
|
}
|