From 99ace613cea10387786408936f9af1b6d7bd4cda Mon Sep 17 00:00:00 2001 From: Adam French Date: Mon, 12 Jan 2026 15:46:01 +0000 Subject: [PATCH] added obsidian note api --- backend/handlers/handle_notes.go | 19 ++++++++++ backend/handlers/store.go | 1 + backend/main.go | 8 +++- backend/services/notes.go | 65 ++++++++++++++++++++++++++++++++ 4 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 backend/handlers/handle_notes.go create mode 100644 backend/services/notes.go diff --git a/backend/handlers/handle_notes.go b/backend/handlers/handle_notes.go new file mode 100644 index 0000000..b40cfd1 --- /dev/null +++ b/backend/handlers/handle_notes.go @@ -0,0 +1,19 @@ +package handlers + +import ( + "net/http" + + "github.com/gin-gonic/gin" +) + +func (store *Store) GetNote(ctx *gin.Context) { + path := ctx.Param("path") + + note, err := store.Notes.GetNote(path) + if err != nil { + ctx.JSON(http.StatusNotFound, gin.H{"error": err.Error()}) + return + } + + ctx.JSON(http.StatusOK, note) +} diff --git a/backend/handlers/store.go b/backend/handlers/store.go index 5033741..bde8231 100644 --- a/backend/handlers/store.go +++ b/backend/handlers/store.go @@ -12,4 +12,5 @@ type Store struct { SpotifyAuth *spotifyauth.Authenticator SpotifyClient *spotify.Client Auth *services.Auth + Notes *services.Notes } diff --git a/backend/main.go b/backend/main.go index 896947c..47db51e 100644 --- a/backend/main.go +++ b/backend/main.go @@ -39,7 +39,11 @@ func main() { authConfig := services.AuthConfig{Secret: []byte(authSecret), Domain: domainName, RefreshTokenLifetime: refreshTokenLifetime, AccessTokenLifetime: accessTokenLifetime, Endpoint: backendEndpoint} auth := services.InitAuth(&authConfig) - store := handlers.Store{DB: db, SpotifyAuth: spotifyAuth, SpotifyClient: client, Auth: auth} + notesDir := os.Getenv("OBSIDIAN_DIR") + notesConfig := services.NotesConfig{Dir: notesDir} + notes := services.InitNotes(¬esConfig) + + store := handlers.Store{DB: db, SpotifyAuth: spotifyAuth, SpotifyClient: client, Auth: auth, Notes: notes} r := gin.Default() protected := r.Group("/", store.AuthMiddlewear) @@ -66,6 +70,8 @@ func main() { r.GET("/spotify/recent", store.RecentlyPlayed) // r.POST("/spotify", store.SendSong) + r.GET("/notes/*path", store.GetNote) + r.GET("/", func(c *gin.Context) { c.JSON(200, gin.H{"message": "Hello World"}) }) diff --git a/backend/services/notes.go b/backend/services/notes.go new file mode 100644 index 0000000..2ac4fd4 --- /dev/null +++ b/backend/services/notes.go @@ -0,0 +1,65 @@ +package services + +import ( + "errors" + "os" + "path/filepath" + "strings" + "time" +) + +type NotesConfig struct { + Dir string +} + +type Note struct { + Filename string `json:"title"` + Contents string `json:"contents"` + LastEdited time.Time `json:"last_edited"` +} + +type Notes struct { + Config NotesConfig +} + +func InitNotes(config *NotesConfig) *Notes { + return &Notes{ + Config: *config, + } +} + +var ErrPathTraversal = errors.New("invalid path") + +func (notes *Notes) GetNote(path string) (*Note, error) { + baseDir, err := filepath.Abs(notes.Config.Dir) + if err != nil { + return nil, err + } + + fullPath := filepath.Join(baseDir, path) + fullPath, err = filepath.Abs(fullPath) + if err != nil { + return nil, err + } + + // Enforce directory boundary + if !strings.HasPrefix(fullPath, baseDir+string(os.PathSeparator)) { + return nil, ErrPathTraversal + } + + data, err := os.ReadFile(fullPath) + if err != nil { + return nil, err + } + + info, err := os.Stat(fullPath) + if err != nil { + return nil, err + } + + return &Note{ + Filename: info.Name(), + Contents: string(data), + LastEdited: info.ModTime(), + }, nil +}