feat(cfe): add cfe on save and rebuild (#45)

This commit is contained in:
Kristian 2026-05-30 23:33:30 +02:00 committed by GitHub
parent 87536b474a
commit 2f94782d33
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 98 additions and 27 deletions

View file

@ -26,18 +26,19 @@ func main() {
linkRepo := repositories.NewLinkRepository(persistence.NewReadContext()) linkRepo := repositories.NewLinkRepository(persistence.NewReadContext())
tagRepo := repositories.NewTagRepository(persistence.NewReadContext()) tagRepo := repositories.NewTagRepository(persistence.NewReadContext())
noteRepo := repositories.NewNoteRepository(persistence.NewReadContext()) noteRepo := repositories.NewNoteRepository(persistence.NewReadContext())
cfeRepo := repositories.NewCfeRepository(persistence.NewReadContext())
tagService := services.NewTagService(tagRepo) tagService := services.NewTagService(tagRepo)
linkService := services.NewLinkService(linkRepo) linkService := services.NewLinkService(linkRepo)
noteService := services.NewNoteService(tagRepo, linkRepo, noteRepo) noteService := services.NewNoteService(tagRepo, linkRepo, noteRepo, cfeRepo)
cfeSvc := services.NewCfeService(repositories.NewCfeRepository(persistence.NewReadContext())) cfeSvc := services.NewCfeService(cfeRepo)
idxr := services.NewIndexRebuilder(uow, noteRepo, linkRepo, tagRepo, indexRepo ) idxr := services.NewIndexRebuilder(uow, noteRepo, linkRepo, tagRepo, indexRepo, cfeRepo)
server.RegisterHandler("vault/init", vault.NewInitializeHandler(idxr, noteService)) server.RegisterHandler("vault/init", vault.NewInitializeHandler(idxr, noteService))
server.RegisterHandler("vault/rebuild", vault.NewRebuildIndexHandler(idxr)) server.RegisterHandler("vault/rebuild", vault.NewRebuildIndexHandler(idxr))
server.RegisterHandler("note/create", note.NewCreateNoteHandler(uow, tagService, noteRepo, cfeSvc)) server.RegisterHandler("note/create", note.NewCreateNoteHandler(uow, tagService, noteRepo, cfeSvc))
server.RegisterHandler("note/save", note.NewSaveNoteHandler(uow, noteRepo, tagService, noteService, linkService)) server.RegisterHandler("note/save", note.NewSaveNoteHandler(uow, noteRepo, tagService, noteService, linkService, cfeSvc))
server.RegisterHandler("note/delete", note.NewDeleteNoteHandler(uow, tagService, noteService)) server.RegisterHandler("note/delete", note.NewDeleteNoteHandler(uow, tagService, noteService))
server.RegisterHandler("note/goto", note.NewGotoNoteHandler(noteRepo)) server.RegisterHandler("note/goto", note.NewGotoNoteHandler(noteRepo))
server.RegisterHandler("note/backlinks", note.NewGetBackLinksHandler(linkRepo, noteRepo)) server.RegisterHandler("note/backlinks", note.NewGetBackLinksHandler(linkRepo, noteRepo))

View file

@ -20,6 +20,7 @@ type SaveNoteHandler struct {
tagService services.ITagService tagService services.ITagService
noteService services.INoteService noteService services.INoteService
linkService services.ILinkService linkService services.ILinkService
cfeSvc services.ICfeService
} }
func NewSaveNoteHandler( func NewSaveNoteHandler(
@ -28,8 +29,9 @@ func NewSaveNoteHandler(
ts services.ITagService, ts services.ITagService,
ns services.INoteService, ns services.INoteService,
ls services.ILinkService, ls services.ILinkService,
cfs services.ICfeService,
) *SaveNoteHandler { ) *SaveNoteHandler {
return &SaveNoteHandler{uow, nr, ts, ns, ls} return &SaveNoteHandler{uow, nr, ts, ns, ls, cfs}
} }
func (h *SaveNoteHandler) Handle(ctx context.Context, raw json.RawMessage) (any, error) { func (h *SaveNoteHandler) Handle(ctx context.Context, raw json.RawMessage) (any, error) {
@ -84,6 +86,10 @@ func (h *SaveNoteHandler) Handle(ctx context.Context, raw json.RawMessage) (any,
return nil, err return nil, err
} }
if err = h.cfeSvc.AddCfe(ctx, tx, note.ID(), file.FrontMatter.Custom); err != nil {
return nil, err
}
if err := h.uow.Commit(); err != nil { if err := h.uow.Commit(); err != nil {
return nil, err return nil, err
} }

View file

@ -1,6 +1,11 @@
package models package models
import filehandling "github.com/KristianJBorgwarth/dendrite.daemon/core/file_handling" import (
"errors"
"log/slog"
filehandling "github.com/KristianJBorgwarth/dendrite.daemon/core/file_handling"
)
func MapToLinkModel(noteID string, extractedLinks []*filehandling.ExtractedLink) []*Link { func MapToLinkModel(noteID string, extractedLinks []*filehandling.ExtractedLink) []*Link {
var links []*Link var links []*Link
@ -10,20 +15,23 @@ func MapToLinkModel(noteID string, extractedLinks []*filehandling.ExtractedLink)
return links return links
} }
func MapToCfe(noteID string, extractedCfe map[string]any) []*CustomFronMatter { func MapToCfe(noteID string, extractedCfe map[string]any) ([]*CustomFronMatter, error) {
var cfe []*CustomFronMatter var cfe []*CustomFronMatter
for key, value := range extractedCfe { for key, value := range extractedCfe {
if valueStr, ok := value.(string); ok { if valueStr, ok := value.(string); ok {
cfe = append(cfe, NewCustomFrontMatter(noteID, key, valueStr)) cfe = append(cfe, NewCustomFrontMatter(noteID, key, valueStr))
continue continue
} else if valueArr, ok := value.([]string); ok { } else if valueArr, ok := value.([]any); ok {
for _, v := range valueArr { for _, v := range valueArr {
cfe = append(cfe, NewCustomFrontMatter(noteID, key, v)) if s, ok := v.(string); ok {
cfe = append(cfe, NewCustomFrontMatter(noteID, key, s))
}
} }
continue continue
} else { } else {
cfe = append(cfe, NewCustomFrontMatter(noteID, key, "")) slog.Warn("Unsupported CFE value type, skipping", "key", key, "value", value)
return nil, errors.New("unsupported CFE value type")
} }
} }
return cfe return cfe, nil
} }

View file

@ -29,6 +29,9 @@ func (s *cfeService) AddCfe(
if len(cfe) == 0 { if len(cfe) == 0 {
return nil return nil
} }
cfeModels := models.MapToCfe(noteID, cfe) cfeModels, err := models.MapToCfe(noteID, cfe)
if err != nil {
return err
}
return s.cfeRepo.InsertRange(ctx, dbCtx, cfeModels) return s.cfeRepo.InsertRange(ctx, dbCtx, cfeModels)
} }

View file

@ -15,6 +15,14 @@ import (
"github.com/KristianJBorgwarth/dendrite.daemon/persistence/store" "github.com/KristianJBorgwarth/dendrite.daemon/persistence/store"
) )
type index struct {
notes []*models.Note
links []*models.Link
cfe []*models.CustomFronMatter
tags []*models.Tag
noteTags []*models.NoteTag
}
type IIndexRebuilder interface { type IIndexRebuilder interface {
RebuildIndex(ctx context.Context, vaultRoot string) error RebuildIndex(ctx context.Context, vaultRoot string) error
} }
@ -25,6 +33,7 @@ type indexRebuilder struct {
linkRepo repositories.ILinkRepository linkRepo repositories.ILinkRepository
tagRepo repositories.ITagRepository tagRepo repositories.ITagRepository
indexRepo repositories.IIndexRepository indexRepo repositories.IIndexRepository
cfeRepo repositories.ICfeRepository
} }
func NewIndexRebuilder( func NewIndexRebuilder(
@ -33,6 +42,7 @@ func NewIndexRebuilder(
linkRepo repositories.ILinkRepository, linkRepo repositories.ILinkRepository,
tagRepo repositories.ITagRepository, tagRepo repositories.ITagRepository,
indexRepo repositories.IIndexRepository, indexRepo repositories.IIndexRepository,
cfeRepo repositories.ICfeRepository,
) *indexRebuilder { ) *indexRebuilder {
return &indexRebuilder{ return &indexRebuilder{
uow: uow, uow: uow,
@ -40,6 +50,7 @@ func NewIndexRebuilder(
linkRepo: linkRepo, linkRepo: linkRepo,
tagRepo: tagRepo, tagRepo: tagRepo,
indexRepo: indexRepo, indexRepo: indexRepo,
cfeRepo: cfeRepo,
} }
} }
@ -54,13 +65,16 @@ func (r *indexRebuilder) RebuildIndex(ctx context.Context, vaultRoot string) err
return err return err
} }
notes, links, tags, noteTags := r.buildDBModels(files) index, err := r.buildDBModels(files)
if err != nil {
return err
}
if err = r.indexRepo.WipeIndex(ctx, dbctx); err != nil { if err = r.indexRepo.WipeIndex(ctx, dbctx); err != nil {
return err return err
} }
if err = r.buildIndex(ctx, dbctx, notes, links, tags, noteTags); err != nil { if err = r.buildIndex(ctx, dbctx, index); err != nil {
return err return err
} }
@ -74,24 +88,25 @@ func (r *indexRebuilder) RebuildIndex(ctx context.Context, vaultRoot string) err
func (r *indexRebuilder) buildIndex( func (r *indexRebuilder) buildIndex(
ctx context.Context, ctx context.Context,
dbctx persistence.IDbContext, dbctx persistence.IDbContext,
notes []*models.Note, index *index,
links []*models.Link,
tags []*models.Tag,
noteTags []*models.NoteTag,
) error { ) error {
if err := r.noteRepo.InsertRange(ctx, dbctx, notes); err != nil { if err := r.noteRepo.InsertRange(ctx, dbctx, index.notes); err != nil {
return err return err
} }
if err := r.linkRepo.InsertRange(ctx, dbctx, links); err != nil { if err := r.linkRepo.InsertRange(ctx, dbctx, index.links); err != nil {
return err return err
} }
if err := r.tagRepo.InsertRange(ctx, dbctx, tags); err != nil { if err := r.tagRepo.InsertRange(ctx, dbctx, index.tags); err != nil {
return err return err
} }
if err := r.tagRepo.InsertNoteTags(ctx, dbctx, noteTags); err != nil { if err := r.tagRepo.InsertNoteTags(ctx, dbctx, index.noteTags); err != nil {
return err
}
if err := r.cfeRepo.InsertRange(ctx, dbctx, index.cfe); err != nil {
return err return err
} }
@ -146,9 +161,10 @@ func (r *indexRebuilder) IsValidDirectory(path string) bool {
return true return true
} }
func (r *indexRebuilder) buildDBModels(files []*filehandling.File) ([]*models.Note, []*models.Link, []*models.Tag, []*models.NoteTag) { func (r *indexRebuilder) buildDBModels(files []*filehandling.File) (*index, error) {
var notes []*models.Note var notes []*models.Note
var links []*models.Link var links []*models.Link
var cfe []*models.CustomFronMatter
tagMap := make(map[string]*models.Tag) tagMap := make(map[string]*models.Tag)
var noteTags []*models.NoteTag var noteTags []*models.NoteTag
@ -160,6 +176,12 @@ func (r *indexRebuilder) buildDBModels(files []*filehandling.File) ([]*models.No
} }
noteTags = append(noteTags, models.CreateNoteTags(note.ID(), file.FrontMatter.Tags)...) noteTags = append(noteTags, models.CreateNoteTags(note.ID(), file.FrontMatter.Tags)...)
links = append(links, models.MapToLinkModel(note.ID(), file.ExtractedLinks)...) links = append(links, models.MapToLinkModel(note.ID(), file.ExtractedLinks)...)
mappedCfe, err := models.MapToCfe(note.ID(), file.FrontMatter.Custom)
if err != nil {
return nil, err
}
cfe = append(cfe, mappedCfe...)
} }
tags := make([]*models.Tag, 0, len(tagMap)) tags := make([]*models.Tag, 0, len(tagMap))
@ -167,5 +189,11 @@ func (r *indexRebuilder) buildDBModels(files []*filehandling.File) ([]*models.No
tags = append(tags, tag) tags = append(tags, tag)
} }
return notes, links, tags, noteTags return &index{
notes: notes,
links: links,
tags: tags,
noteTags: noteTags,
cfe: cfe,
}, nil
} }

View file

@ -21,14 +21,16 @@ type noteService struct {
tagRepo repositories.ITagRepository tagRepo repositories.ITagRepository
linkRepo repositories.ILinkRepository linkRepo repositories.ILinkRepository
noteRepo repositories.INoteRepository noteRepo repositories.INoteRepository
cfeRepo repositories.ICfeRepository
} }
func NewNoteService( func NewNoteService(
tagRepo repositories.ITagRepository, tagRepo repositories.ITagRepository,
linkRepo repositories.ILinkRepository, linkRepo repositories.ILinkRepository,
noteRepo repositories.INoteRepository, noteRepo repositories.INoteRepository,
cfeRepo repositories.ICfeRepository,
) INoteService { ) INoteService {
return &noteService{tagRepo, linkRepo, noteRepo} return &noteService{tagRepo, linkRepo, noteRepo, cfeRepo}
} }
func (s *noteService) CreateNote(ctx context.Context, dbCtx persistence.IDbContext, path, title, slug string) (*models.Note, error) { func (s *noteService) CreateNote(ctx context.Context, dbCtx persistence.IDbContext, path, title, slug string) (*models.Note, error) {
@ -56,6 +58,10 @@ func (s *noteService) DeleteNoteMetaData(ctx context.Context, dbCtx persistence.
return err return err
} }
if err := s.cfeRepo.Delete(ctx, dbCtx, noteID); err != nil {
return err
}
return nil return nil
} }

View file

@ -9,6 +9,7 @@ import (
type ICfeRepository interface { type ICfeRepository interface {
InsertRange(ctx context.Context, dbContext persistence.IDbContext, cfe []*models.CustomFronMatter) error InsertRange(ctx context.Context, dbContext persistence.IDbContext, cfe []*models.CustomFronMatter) error
Delete(ctx context.Context, dbContext persistence.IDbContext, noteID string) error
} }
type cfeRepository struct { type cfeRepository struct {
@ -26,7 +27,7 @@ func (r *cfeRepository) InsertRange(
) error { ) error {
statement, err := dbCtx.Prepare( statement, err := dbCtx.Prepare(
`INSERT INTO custom_frontmatter (note_id, key, value) `INSERT INTO custom_frontmatter (note_id, key, value)
VALUES (?, ?, ?) ON CONFLICT DO NOTHING;`,) VALUES (?, ?, ?) ON CONFLICT DO NOTHING;`)
if err != nil { if err != nil {
return err return err
} }
@ -39,3 +40,20 @@ func (r *cfeRepository) InsertRange(
return nil return nil
} }
func (r *cfeRepository) Delete(
ctx context.Context,
dbCtx persistence.IDbContext,
noteID string,
) error {
statement, err := dbCtx.Prepare(`DELETE FROM custom_frontmatter WHERE note_id = ?;`)
if err != nil {
return err
}
if _, err := statement.ExecContext(ctx, noteID); err != nil {
return err
}
return nil
}

View file

@ -22,7 +22,8 @@ func (r *indexRepository) WipeIndex(ctx context.Context, dbContext persistence.I
cmd := `DELETE FROM note; cmd := `DELETE FROM note;
DELETE FROM tag; DELETE FROM tag;
DELETE FROM note_tag; DELETE FROM note_tag;
DELETE FROM link;` DELETE FROM link;
DELETE FROM custom_frontmatter;`
_, err := dbContext.ExecContext(ctx, cmd) _, err := dbContext.ExecContext(ctx, cmd)
return err return err