backend

package
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 17, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Overview

Package backend is the seam that lets every zn command and the terminal UI work against a vault on this machine or one behind a self-hosted ZenNotes server: the same operations, bound either to a root on disk or to a server over HTTP.

Index

Constants

View Source
const RemoteTokenEnv = "ZENNOTES_REMOTE_TOKEN"

RemoteTokenEnv carries a server token for CI and headless use.

Variables

This section is empty.

Functions

func LooksLikeServerURL

func LooksLikeServerURL(value string) bool

LooksLikeServerURL is true for a bare URL rather than a profile name: it has a scheme, or looks like `host:port`.

func RememberTarget

func RememberTarget(t Target) error

RememberTarget makes a target zn's default for next time, saving a path or URL that was typed rather than picked from the list.

func ResolveAuthToken

func ResolveAuthToken(flagToken, profileToken string) string

ResolveAuthToken picks the token, loudest first: an explicit `--token`, then the environment, then whatever the saved profile carries.

func ResolveAuthTokenFor

func ResolveAuthTokenFor(baseURL, flagToken, profileToken string) string

ResolveAuthTokenFor is ResolveAuthToken with zn's own credential store in the chain: flag, environment, the token `zn connect` saved for the URL, then the desktop profile's token.

func ResolveAuthTokenForSource added in v0.2.0

func ResolveAuthTokenForSource(baseURL, flagToken, profileToken, source string) string

ResolveAuthTokenForSource preserves the desktop CLI's token precedence in app mode; terminal mode may additionally use credentials saved by zn connect.

func ResolveWorkspaceSource added in v0.2.0

func ResolveWorkspaceSource(source string) (string, error)

ResolveWorkspaceSource returns app or terminal, applying the environment default.

Types

type AddCommentInput

type AddCommentInput struct {
	Path       string
	Body       string
	AnchorText string
	Author     string
}

AddCommentInput starts a thread.

type Backend

type Backend interface {
	Kind() Kind
	// Label names the vault in output and errors.
	Label() string
	// Root is the vault directory on disk; empty for a server.
	Root() string

	Describe(ctx context.Context) (Description, error)
	ListNotes(ctx context.Context) ([]vault.NoteMeta, error)
	ListAssets(ctx context.Context) ([]vault.AssetMeta, error)
	// ReadAsset returns an attachment's bytes by vault-relative path.
	ReadAsset(ctx context.Context, rel string) ([]byte, error)
	ListFolders(ctx context.Context) ([]vault.FolderEntry, error)
	ReadNote(ctx context.Context, rel string) (vault.NoteContent, error)
	WriteNote(ctx context.Context, rel, body string) (vault.NoteMeta, error)
	CreateNote(ctx context.Context, folder vault.NoteFolder, title, subpath string, body *string) (vault.NoteMeta, error)
	AppendToNote(ctx context.Context, rel, text string) (vault.NoteMeta, error)
	PrependToNote(ctx context.Context, rel, text string) (vault.NoteMeta, error)
	RenameNote(ctx context.Context, rel, nextTitle string) (vault.NoteMeta, error)
	MoveNote(ctx context.Context, rel string, folder vault.NoteFolder, subpath string) (vault.NoteMeta, error)
	ArchiveNote(ctx context.Context, rel string) (vault.NoteMeta, error)
	UnarchiveNote(ctx context.Context, rel string) (vault.NoteMeta, error)
	MoveToTrash(ctx context.Context, rel string) (vault.NoteMeta, error)
	RestoreFromTrash(ctx context.Context, rel string) (vault.NoteMeta, error)
	DuplicateNote(ctx context.Context, rel string) (vault.NoteMeta, error)
	DeleteNote(ctx context.Context, rel string) error
	EmptyTrash(ctx context.Context) error
	InsertAtLine(ctx context.Context, rel string, lineNumber int, text string) (vault.NoteMeta, error)
	ReplaceInNote(ctx context.Context, rel, find, replace string, all bool) (vault.NoteMeta, int, error)
	CreateFolder(ctx context.Context, folder vault.NoteFolder, subpath string) error
	RenameFolder(ctx context.Context, folder vault.NoteFolder, oldSubpath, newSubpath string) (string, error)
	DeleteFolder(ctx context.Context, folder vault.NoteFolder, subpath string) error
	SearchText(ctx context.Context, query string, limit int) ([]vault.TextSearchMatch, error)
	Backlinks(ctx context.Context, rel string) ([]vault.NoteMeta, error)
	ScanTasks(ctx context.Context, opts vault.ParseTasksOptions) ([]vault.Task, error)
	ScanTasksForPath(ctx context.Context, rel string, opts vault.ParseTasksOptions) ([]vault.Task, error)
	ToggleTask(ctx context.Context, taskID string, dialect vault.TaskDialect) (*vault.Task, error)
	// DatabaseOps composes the CSV database operations over this backend's
	// file IO, so `zn base` writes the identical on-disk format everywhere.
	DatabaseOps() *database.Ops

	// ListComments and WriteComments are a note's comment sidecar as stored;
	// WriteComments replaces the list.
	ListComments(ctx context.Context, rel string) ([]vault.NoteComment, error)
	WriteComments(ctx context.Context, rel string, comments []vault.NoteComment) ([]vault.NoteComment, error)
	ListTemplates(ctx context.Context) ([]vault.CustomTemplateFile, error)
	WriteTemplate(ctx context.Context, input vault.WriteTemplateInput) (vault.CustomTemplateFile, error)
	DeleteTemplate(ctx context.Context, sourcePath string) error
	VaultSettings(ctx context.Context) (vault.VaultSettings, error)
	UpdateVaultSettings(ctx context.Context, patch func(raw map[string]any)) error
}

Backend is the set of operations every command is written against.

func New

func New(target Target, opts Options) (Backend, error)

New binds a target to a backend.

type CommentThreadView

type CommentThreadView struct {
	CommentView
	AnchorText string        `json:"anchorText"`
	Line       int           `json:"line"`
	Resolved   bool          `json:"resolved"`
	ResolvedAt *int64        `json:"resolvedAt"`
	Replies    []CommentView `json:"replies"`
}

CommentThreadView is a top-level comment with its replies.

func AddComment

AddComment starts a thread, anchored to text from the note when given.

func ListCommentThreads

func ListCommentThreads(ctx context.Context, b Backend, rel string, includeResolved bool) ([]CommentThreadView, error)

ListCommentThreads reads a note's threads, unresolved ones unless includeResolved.

func ReplyToComment

func ReplyToComment(ctx context.Context, b Backend, in ReplyInput) (CommentThreadView, error)

ReplyToComment answers a thread; id may be the thread or any reply in it.

func ResolveComment

func ResolveComment(ctx context.Context, b Backend, rel, id string, resolved bool) (CommentThreadView, error)

ResolveComment marks a thread resolved, or reopens it.

type CommentView

type CommentView struct {
	ID        string  `json:"id"`
	Author    *string `json:"author"`
	Body      string  `json:"body"`
	CreatedAt int64   `json:"createdAt"`
	UpdatedAt int64   `json:"updatedAt"`
}

CommentView is one comment or reply as tools and commands present it.

type Description

type Description struct {
	Kind Kind
	// Root is the local vault directory.
	Root string
	// BaseURL and Name describe a server; VaultPath and VaultName are the
	// vault it serves, as it reports them.
	BaseURL              string
	Name                 string
	VaultPath            string
	VaultName            string
	PrimaryNotesLocation vault.PrimaryNotesLocation
	// AuthConfigured says whether this process holds a token at all.
	AuthConfigured bool
}

Description is where a vault lives and how it is laid out.

type Kind

type Kind string

Kind says where a vault lives.

const (
	KindLocal  Kind = "local"
	KindRemote Kind = "remote"
)

type Local

type Local struct {
	// contains filtered or unexported fields
}

Local is a vault on this machine: every method is the vault engine bound to one root.

func (*Local) AppendToNote

func (l *Local) AppendToNote(_ context.Context, rel, text string) (vault.NoteMeta, error)

func (*Local) ArchiveNote

func (l *Local) ArchiveNote(_ context.Context, rel string) (vault.NoteMeta, error)
func (l *Local) Backlinks(_ context.Context, rel string) ([]vault.NoteMeta, error)

func (*Local) CreateFolder

func (l *Local) CreateFolder(_ context.Context, folder vault.NoteFolder, subpath string) error

func (*Local) CreateNote

func (l *Local) CreateNote(_ context.Context, folder vault.NoteFolder, title, subpath string, body *string) (vault.NoteMeta, error)

func (*Local) DatabaseOps

func (l *Local) DatabaseOps() *database.Ops

func (*Local) DeleteFolder

func (l *Local) DeleteFolder(_ context.Context, folder vault.NoteFolder, subpath string) error

func (*Local) DeleteNote

func (l *Local) DeleteNote(_ context.Context, rel string) error

func (*Local) DeleteTemplate

func (l *Local) DeleteTemplate(_ context.Context, sourcePath string) error

func (*Local) Describe

func (l *Local) Describe(context.Context) (Description, error)

func (*Local) DuplicateNote

func (l *Local) DuplicateNote(_ context.Context, rel string) (vault.NoteMeta, error)

func (*Local) EmptyTrash

func (l *Local) EmptyTrash(context.Context) error

func (*Local) InsertAtLine

func (l *Local) InsertAtLine(_ context.Context, rel string, lineNumber int, text string) (vault.NoteMeta, error)

func (*Local) Kind

func (l *Local) Kind() Kind

func (*Local) Label

func (l *Local) Label() string

func (*Local) ListAssets

func (l *Local) ListAssets(context.Context) ([]vault.AssetMeta, error)

func (*Local) ListComments

func (l *Local) ListComments(_ context.Context, rel string) ([]vault.NoteComment, error)

func (*Local) ListFolders

func (l *Local) ListFolders(context.Context) ([]vault.FolderEntry, error)

func (*Local) ListNotes

func (l *Local) ListNotes(context.Context) ([]vault.NoteMeta, error)

func (*Local) ListTemplates

func (l *Local) ListTemplates(context.Context) ([]vault.CustomTemplateFile, error)

func (*Local) MoveNote

func (l *Local) MoveNote(_ context.Context, rel string, folder vault.NoteFolder, subpath string) (vault.NoteMeta, error)

func (*Local) MoveToTrash

func (l *Local) MoveToTrash(_ context.Context, rel string) (vault.NoteMeta, error)

func (*Local) PrependToNote

func (l *Local) PrependToNote(_ context.Context, rel, text string) (vault.NoteMeta, error)

func (*Local) ReadAsset

func (l *Local) ReadAsset(_ context.Context, rel string) ([]byte, error)

func (*Local) ReadNote

func (l *Local) ReadNote(_ context.Context, rel string) (vault.NoteContent, error)

func (*Local) RenameFolder

func (l *Local) RenameFolder(_ context.Context, folder vault.NoteFolder, oldSubpath, newSubpath string) (string, error)

func (*Local) RenameNote

func (l *Local) RenameNote(_ context.Context, rel, nextTitle string) (vault.NoteMeta, error)

func (*Local) ReplaceInNote

func (l *Local) ReplaceInNote(_ context.Context, rel, find, replace string, all bool) (vault.NoteMeta, int, error)

func (*Local) RestoreFromTrash

func (l *Local) RestoreFromTrash(_ context.Context, rel string) (vault.NoteMeta, error)

func (*Local) Root

func (l *Local) Root() string

func (*Local) ScanTasks

func (l *Local) ScanTasks(_ context.Context, opts vault.ParseTasksOptions) ([]vault.Task, error)

func (*Local) ScanTasksForPath

func (l *Local) ScanTasksForPath(_ context.Context, rel string, opts vault.ParseTasksOptions) ([]vault.Task, error)

func (*Local) SearchText

func (l *Local) SearchText(_ context.Context, query string, limit int) ([]vault.TextSearchMatch, error)

func (*Local) ToggleTask

func (l *Local) ToggleTask(_ context.Context, taskID string, dialect vault.TaskDialect) (*vault.Task, error)

func (*Local) UnarchiveNote

func (l *Local) UnarchiveNote(_ context.Context, rel string) (vault.NoteMeta, error)

func (*Local) UpdateVaultSettings

func (l *Local) UpdateVaultSettings(_ context.Context, patch func(raw map[string]any)) error

func (*Local) Vault

func (l *Local) Vault() *vault.Vault

Vault exposes the engine for callers that need local-only operations.

func (*Local) VaultSettings

func (l *Local) VaultSettings(context.Context) (vault.VaultSettings, error)

func (*Local) WriteComments

func (l *Local) WriteComments(_ context.Context, rel string, comments []vault.NoteComment) ([]vault.NoteComment, error)

func (*Local) WriteNote

func (l *Local) WriteNote(_ context.Context, rel, body string) (vault.NoteMeta, error)

func (*Local) WriteTemplate

type Options

type Options struct {
	// SyncTitleHeading is the desktop's "Sync title heading on rename"
	// preference, applied by the local backend.
	SyncTitleHeading bool
}

Options tune a backend.

type Remote

type Remote struct {
	// contains filtered or unexported fields
}

Remote is a vault behind a self-hosted server, reached over the same HTTP API the desktop and web clients use. Where the server has no route for something (there is no task-toggle route, and note creation takes no body), the operation is composed from the routes that do exist.

func (*Remote) AppendToNote

func (r *Remote) AppendToNote(ctx context.Context, rel, text string) (vault.NoteMeta, error)

func (*Remote) ArchiveNote

func (r *Remote) ArchiveNote(ctx context.Context, rel string) (vault.NoteMeta, error)
func (r *Remote) Backlinks(ctx context.Context, rel string) ([]vault.NoteMeta, error)

func (*Remote) Client

func (r *Remote) Client() *remote.Client

Client exposes the HTTP client for callers that need server-only routes.

func (*Remote) CreateFolder

func (r *Remote) CreateFolder(ctx context.Context, folder vault.NoteFolder, subpath string) error

func (*Remote) CreateNote

func (r *Remote) CreateNote(ctx context.Context, folder vault.NoteFolder, title, subpath string, body *string) (vault.NoteMeta, error)

CreateNote takes two round trips: the server's create route takes no body, so a note with content is created and then written.

func (*Remote) DatabaseOps

func (r *Remote) DatabaseOps() *database.Ops

DatabaseOps reads through /notes/read, which serves any vault file; a 404 means absent, anything else is a real failure (the absence reader settles servers that answer 500 for both).

func (*Remote) DeleteFolder

func (r *Remote) DeleteFolder(ctx context.Context, folder vault.NoteFolder, subpath string) error

func (*Remote) DeleteNote

func (r *Remote) DeleteNote(ctx context.Context, rel string) error

func (*Remote) DeleteTemplate

func (r *Remote) DeleteTemplate(ctx context.Context, sourcePath string) error

func (*Remote) Describe

func (r *Remote) Describe(ctx context.Context) (Description, error)

Describe takes two reads: the vault the server is serving and its layout settings. A 401 here is the first thing an unauthenticated session hits.

func (*Remote) DuplicateNote

func (r *Remote) DuplicateNote(ctx context.Context, rel string) (vault.NoteMeta, error)

func (*Remote) EmptyTrash

func (r *Remote) EmptyTrash(ctx context.Context) error

func (*Remote) InsertAtLine

func (r *Remote) InsertAtLine(ctx context.Context, rel string, lineNumber int, text string) (vault.NoteMeta, error)

func (*Remote) Kind

func (r *Remote) Kind() Kind

func (*Remote) Label

func (r *Remote) Label() string

func (*Remote) ListAssets

func (r *Remote) ListAssets(ctx context.Context) ([]vault.AssetMeta, error)

func (*Remote) ListComments

func (r *Remote) ListComments(ctx context.Context, rel string) ([]vault.NoteComment, error)

func (*Remote) ListFolders

func (r *Remote) ListFolders(ctx context.Context) ([]vault.FolderEntry, error)

func (*Remote) ListNotes

func (r *Remote) ListNotes(ctx context.Context) ([]vault.NoteMeta, error)

func (*Remote) ListTemplates

func (r *Remote) ListTemplates(ctx context.Context) ([]vault.CustomTemplateFile, error)

func (*Remote) MoveNote

func (r *Remote) MoveNote(ctx context.Context, rel string, folder vault.NoteFolder, subpath string) (vault.NoteMeta, error)

func (*Remote) MoveToTrash

func (r *Remote) MoveToTrash(ctx context.Context, rel string) (vault.NoteMeta, error)

func (*Remote) PrependToNote

func (r *Remote) PrependToNote(ctx context.Context, rel, text string) (vault.NoteMeta, error)

func (*Remote) ReadAsset

func (r *Remote) ReadAsset(ctx context.Context, rel string) ([]byte, error)

func (*Remote) ReadNote

func (r *Remote) ReadNote(ctx context.Context, rel string) (vault.NoteContent, error)

func (*Remote) RenameFolder

func (r *Remote) RenameFolder(ctx context.Context, folder vault.NoteFolder, oldSubpath, newSubpath string) (string, error)

func (*Remote) RenameNote

func (r *Remote) RenameNote(ctx context.Context, rel, nextTitle string) (vault.NoteMeta, error)

func (*Remote) ReplaceInNote

func (r *Remote) ReplaceInNote(ctx context.Context, rel, find, replace string, all bool) (vault.NoteMeta, int, error)

ReplaceInNote: no match means no write, so the receipt comes from the listing instead of a round trip that would re-save identical bytes.

func (*Remote) RestoreFromTrash

func (r *Remote) RestoreFromTrash(ctx context.Context, rel string) (vault.NoteMeta, error)

func (*Remote) Root

func (r *Remote) Root() string

func (*Remote) ScanTasks

func (r *Remote) ScanTasks(ctx context.Context, opts vault.ParseTasksOptions) ([]vault.Task, error)

The server parses with its own grammar; the dialect only shapes the toggle transform applied to the body it hands back.

func (*Remote) ScanTasksForPath

func (r *Remote) ScanTasksForPath(ctx context.Context, rel string, opts vault.ParseTasksOptions) ([]vault.Task, error)

func (*Remote) SearchText

func (r *Remote) SearchText(ctx context.Context, query string, limit int) ([]vault.TextSearchMatch, error)

SearchText: the server's search route has no limit parameter, so the cap is applied here. It runs the server's own engine, so ordering can differ from a local vault.

func (*Remote) ToggleTask

func (r *Remote) ToggleTask(ctx context.Context, taskID string, dialect vault.TaskDialect) (*vault.Task, error)

ToggleTask: no task-toggle endpoint exists, so the note is read, the same transform a local toggle applies is applied here, and the server re-parses the result.

func (*Remote) UnarchiveNote

func (r *Remote) UnarchiveNote(ctx context.Context, rel string) (vault.NoteMeta, error)

func (*Remote) UpdateVaultSettings

func (r *Remote) UpdateVaultSettings(ctx context.Context, patch func(raw map[string]any)) error

func (*Remote) VaultSettings

func (r *Remote) VaultSettings(ctx context.Context) (vault.VaultSettings, error)

func (*Remote) WriteComments

func (r *Remote) WriteComments(ctx context.Context, rel string, comments []vault.NoteComment) ([]vault.NoteComment, error)

func (*Remote) WriteNote

func (r *Remote) WriteNote(ctx context.Context, rel, body string) (vault.NoteMeta, error)

func (*Remote) WriteTemplate

func (r *Remote) WriteTemplate(ctx context.Context, input vault.WriteTemplateInput) (vault.CustomTemplateFile, error)

type ReplyInput

type ReplyInput struct {
	Path   string
	ID     string
	Body   string
	Author string
}

ReplyInput answers in a thread.

type Target

type Target struct {
	Kind Kind
	// Root is the local vault directory.
	Root string
	// Name is the saved server profile's name, "" for a bare URL.
	Name      string
	BaseURL   string
	AuthToken string
}

Target is which vault a command is about.

func ResolveDefaultTarget

func ResolveDefaultTarget(flagToken string) (Target, error)

ResolveDefaultTarget is the target when nothing named one: ZENNOTES_SERVER, then ZENNOTES_VAULT, then the selected source's default. Terminal mode prefers its saved default before falling back to the desktop workspace.

func ResolveServerTarget

func ResolveServerTarget(selector, flagToken string) (Target, error)

ResolveServerTarget resolves `--server <name|url>`.

func ResolveTarget

func ResolveTarget(vaultSelector, serverSelector, flagToken string) (Target, error)

ResolveTarget is the target for one invocation. `--server` wins over `--vault`; with neither, the environment and selected workspace source decide.

func ResolveTargetWithSource added in v0.2.0

func ResolveTargetWithSource(vaultSelector, serverSelector, flagToken, source string) (Target, error)

ResolveTargetWithSource keeps desktop commands attached to the desktop even when the terminal UI remembers a different workspace. Flags and environment vault overrides still win. Empty source reads ZENNOTES_WORKSPACE_SOURCE.

func ResolveVaultTarget

func ResolveVaultTarget(selector, flagToken string) (Target, error)

ResolveVaultTarget resolves `--vault <name|path>`: a local vault name, then a server profile name, then a directory path.

func TargetForWorkspace

func TargetForWorkspace(ws config.Workspaces, name, flagToken string) (Target, bool)

TargetForWorkspace resolves one of zn's own saved workspaces by name.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL