feat(server): initialize loop

This commit is contained in:
Kristian Borgwarth 2026-03-22 14:43:54 +01:00
parent 3eeeb9966e
commit 1c8e19b83f
7 changed files with 109 additions and 17 deletions

View file

@ -5,12 +5,14 @@ import (
"log" "log"
"path/filepath" "path/filepath"
migrate "github.com/KristianJBorgwarth/dendrite.daemon/persistence" "github.com/KristianJBorgwarth/dendrite.daemon/persistence"
"github.com/KristianJBorgwarth/dendrite.daemon/core"
_ "modernc.org/sqlite" _ "modernc.org/sqlite"
) )
func main() { func main() {
server := core.NewServer()
server.Run()
} }
func runMigration() { func runMigration() {
@ -24,7 +26,7 @@ func runMigration() {
migrationsDir := filepath.Join("persistence", "migrations") migrationsDir := filepath.Join("persistence", "migrations")
if err := migrate.Run(db, migrationsDir); err != nil { if err := persistence.ApplyMigrations(db, migrationsDir); err != nil {
log.Fatal(err) log.Fatal(err)
} }

View file

@ -1,2 +0,0 @@
// Package commands provides the commands interface for the application.
package commands

View file

@ -1 +0,0 @@
package comms

2
core/handlers/doc.go Normal file
View file

@ -0,0 +1,2 @@
// Package handlers contains commands for handling incoming requests
package handlers

47
core/handlers/init.go Normal file
View file

@ -0,0 +1,47 @@
package handlers
import (
"encoding/json"
"github.com/KristianJBorgwarth/dendrite.daemon/config"
)
type initializeParams struct {
VaultPath string `json:"vaultPath"`
TemplateDir string `json:"templateDir"`
ScratchNote struct {
Dir string `json:"dir"`
TemplateName string `json:"templateName"`
} `json:"scratchNote"`
DailyNote struct {
Dir string `json:"dir"`
TemplateName string `json:"templateName"`
FilenameFormat string `json:"filenameFormat"`
} `json:"dailyNote"`
}
func Initialize(raw json.RawMessage) (*config.Config, error) {
var params initializeParams
if err := json.Unmarshal(raw, &params); err != nil {
return nil, err
}
cfg := &config.Config{
VaultPath: params.VaultPath,
TemplateDir: params.TemplateDir,
ScratchNote: config.ScratchConfig{
Dir: params.ScratchNote.Dir,
TemplateName: params.ScratchNote.TemplateName,
},
DailyNote: config.DailyConfig{
Dir: params.DailyNote.Dir,
TemplateName: params.DailyNote.TemplateName,
FilenameFormat: params.DailyNote.FilenameFormat,
},
}
cfg.SetDefaults()
return cfg, nil
}

View file

@ -1,5 +1,5 @@
// Package server contains core functionality for dendrite server // Package core contains core functionality for dendrite server
package server package core
import ( import (
"bufio" "bufio"
@ -10,6 +10,9 @@ import (
"os" "os"
"strconv" "strconv"
"strings" "strings"
"github.com/KristianJBorgwarth/dendrite.daemon/config"
"github.com/KristianJBorgwarth/dendrite.daemon/core/handlers"
"github.com/KristianJBorgwarth/dendrite.daemon/core/models" "github.com/KristianJBorgwarth/dendrite.daemon/core/models"
) )
@ -17,6 +20,7 @@ type Server struct {
in *bufio.Reader in *bufio.Reader
out io.Writer out io.Writer
log *slog.Logger log *slog.Logger
config *config.Config
} }
func NewServer() *Server { func NewServer() *Server {
@ -27,6 +31,31 @@ func NewServer() *Server {
} }
} }
func (s *Server) writeResponse(id int, result any, err error) {
resp := types.Response{
Jsonrpc: "2.0",
ID: id,
}
if err != nil {
resp.Error = map[string]any{
"code": -1,
"message": err.Error(),
}
} else {
resp.Result = result
}
data, marshalErr := json.Marshal(resp)
if marshalErr != nil {
s.log.Error("failed to marshal response", "error", marshalErr)
return
}
fmt.Fprintf(s.out, "Content-Length: %d\r\n\r\n", len(data))
s.out.Write(data)
}
// Run starts the server loop, continuously reading and processing incoming messages // Run starts the server loop, continuously reading and processing incoming messages
func (s *Server) Run() error { func (s *Server) Run() error {
for { for {
@ -70,7 +99,7 @@ func (s *Server) readContentLength() (int, error) {
break break
} }
if after, ok :=strings.CutPrefix(line, "Content-Length:"); ok { if after, ok := strings.CutPrefix(line, "Content-Length:"); ok {
val := strings.TrimSpace(after) val := strings.TrimSpace(after)
return strconv.Atoi(val) return strconv.Atoi(val)
} }
@ -80,9 +109,24 @@ func (s *Server) readContentLength() (int, error) {
func (s *Server) handleRequest(req types.Request) { func (s *Server) handleRequest(req types.Request) {
s.log.Info("handling request", "method", req.Method, "id", req.ID) s.log.Info("handling request", "method", req.Method, "id", req.ID)
var (
result any
err error
)
switch req.Method {
case "initialize":
s.config, err = handlers.Initialize(req.Params)
result = map[string]any{"status": "ok"}
default:
err = fmt.Errorf("unknown method: %s", req.Method)
}
s.writeResponse(req.ID, result, err)
} }
func (s *Server) handleNotification(notif types.Notification) { func (s *Server) handleNotification(notif types.Notification) {
s.log.Info("handling notification", "method", notif.Method) s.log.Info("handling notification", "method", notif.Method)
} }

View file

@ -1,5 +1,5 @@
// Package migrate handles database schema migrations // Package persistence handles database schema migrations
package migrate package persistence
import ( import (
"database/sql" "database/sql"
@ -8,7 +8,7 @@ import (
"sort" "sort"
) )
func Run(db *sql.DB, dir string) error { func ApplyMigrations(db *sql.DB, dir string) error {
_, err := db.Exec(`CREATE TABLE IF NOT EXISTS schema_migrations (version TEXT PRIMARY KEY);`) _, err := db.Exec(`CREATE TABLE IF NOT EXISTS schema_migrations (version TEXT PRIMARY KEY);`)
if err != nil { if err != nil {
return err return err