Documentation
¶
Overview ¶
Package web is `dbc web`: the workbench in a browser.
It is a local tool for the person who started it. The server listens on loopback, opens the browser on a one-time login URL, and serves a page that runs statements through the same workspace package the TUI uses — so the run slot, the pinned session, cancel and history behave identically in both.
browser tab ──POST /api/v1/ws/:id/run──► handler ──► workspace.RunEditor
▲ │ refused? → 409 / 400 at once
│ ▼
│ go Job() ── runs, lands the outcome
└──── SSE /api/v1/ws/:id/events ◄──── deliver: "run" {status, hasResult}
│
└─► GET /api/v1/ws/:id/result → the table, rendered here
Built the way the author's other rweb apps are (gonotes, herdr-web): rweb for HTTP and SSE, element for server-side HTML, vanilla JS and CSS embedded with go:embed, serr for errors, nothing fetched from the network.
Index ¶
- Constants
- func GenerateSecret() (string, error)
- func StateFile() string
- type Options
- type SavedConn
- type Server
- type Store
- func (s *Store) Close() error
- func (s *Store) Conns() ([]SavedConn, error)
- func (s *Store) DeleteConn(name string) error
- func (s *Store) DeleteTab(id string) error
- func (s *Store) Layout() (map[string]string, error)
- func (s *Store) MoveLayout(moves map[string]string) error
- func (s *Store) Path() string
- func (s *Store) Persistent() bool
- func (s *Store) RetagTabs(from, to string) error
- func (s *Store) SaveConn(c SavedConn) error
- func (s *Store) SaveTab(t Tab) error
- func (s *Store) SetLayout(values map[string]string) error
- func (s *Store) Tabs() ([]Tab, error)
- type Tab
Constants ¶
const DefaultListen = "127.0.0.1:8450"
DefaultListen is where dbc web listens unless told otherwise: loopback, and a fixed port so a bookmark survives a restart (clear of gonotes' 8444 and herdr-web's 8421). When it is taken, a free port is used instead.
Variables ¶
This section is empty.
Functions ¶
func GenerateSecret ¶
GenerateSecret returns a random URL-safe secret (24 characters, 18 bytes).
Types ¶
type Options ¶
type Options struct {
// Listen is the address to bind. "" means DefaultListen, falling back
// to a free loopback port when 8450 is taken; an explicit address is
// used as given, and a busy one is an error.
Listen string
// Secret is the launch secret; "" generates one.
Secret string
// Store keeps tabs and layout; nil means a memory-only store.
Store *Store
// Conns is where connections added in the browser are kept — the
// saved-connections file every dbc reads (config.SavedFile); nil keeps
// them in memory. New does not merge the file into cfg: the caller has
// (config.LoadSaved), because every dbc command does it the same way.
Conns *config.SavedStore
// History is the query history every tab records into — the TUI's
// file, so a query run in either shows up in both. nil keeps it in
// memory.
History *userdata.History
// Ready, when set, is called once the server is listening, with the
// login URL (the secret in it) — to print it and open a browser.
Ready func(loginURL string)
// Logf receives the server's own messages (shutdown progress). nil
// discards them.
Logf func(format string, args ...any)
// ChatsDir is where assistant conversations are kept — the TUI's
// archive (userdata.ChatsDir), so a conversation had in either is
// offered in both. "" keeps them in memory only.
ChatsDir string
// ConsolesDir is where the SQL consoles live — the TUI's
// (userdata.ConsolesDir), so a query tab and the terminal share each
// database's running SQL files. "" turns consoles off: every tab keeps
// a buffer of its own (see consoles.go).
ConsolesDir string
// StartChat starts an assistant conversation; nil is ai.Start. Tests
// hand in a scripted agent (package aitest): the real one would need
// installing and signing in, and would spend Copilot requests.
StartChat func(ai.Agent, ai.Options) *ai.Chat
// BeginSignIn starts the agent's device-flow sign-in; nil is
// ai.BeginSignIn. Tests hand in a scripted server for the same reason.
BeginSignIn func(ai.Agent, string) (*ai.SignIn, error)
}
Options configure a Server.
type SavedConn ¶
type SavedConn struct {
Name string
Driver string
DSN string
AIRows bool // config.Connection.AIRows: the assistant may see result rows
Added time.Time
}
SavedConn is a row of the conns table, where dbc web kept connections added in the browser until they moved to connections.toml, which the TUI and headless runs can read beside a running dbc web (this file is locked by it). The table is now only read, once, by Server.moveStoreConns, which copies each row to config.SavedStore and deletes it here; the table stays in the schema so an older file opens, and so that read has something to query.
DSN is as typed — ${VAR} references unexpanded — which is how it moves.
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server is a running (or ready-to-run) dbc web.
func New ¶
New builds the server over the app's config and connection manager. It does no network IO; Run listens.
func (*Server) Run ¶
Run listens and serves until Ctrl+C (rweb handles SIGINT and SIGTERM itself and returns), then shuts down: every tab's run is stopped and its session released — rolling back what it left open — within a grace period.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store keeps what only the browser UI has: its query tabs (the editor buffer and the connection each one was on) and its layout (pane sizes). History and assistant conversations are not here — they stay in userdata, shared with the TUI, so a query run in either shows up in both — and neither are the connections added in the browser, which every dbc reads from connections.toml (config.SavedStore). Its conns table is where an older dbc web kept those; dbc web moves them out at startup (see SavedConn).
It is a bytdb file, ~/.config/dbc/web.bytdb. Two processes writing one bytdb WAL would corrupt it, so only one may hold it. bytdb (v0.18.0+) sees to that itself: opening the file takes an exclusive lock on its sidecar, web.bytdb.lock, held until the last handle closes, and a second opener gets bytdb.ErrLocked. The store once took that same sidecar lock itself (bytdb v0.16.0 had none); it must not now — the lock is not reentrant, so the store's lock would make bytdb refuse the store's own open. A second `dbc web` finds the file held and, rather than refuse to start, gets a memory-only Store (see OpenStore) and says so. The same fallback covers a machine with no home directory.
A memory-only Store has db == nil and keeps everything in the maps below, so callers never branch on which kind they hold.
func OpenStore ¶
OpenStore opens (creating it if needed) the store at path. path == "" returns a memory-only store and no error. On a failure — the file locked by another dbc web, an unwritable directory — it returns a memory-only store AND the error, so the caller can warn and carry on.
func (*Store) Close ¶
Close closes the file, and with it bytdb lets go of the lock. A memory-only store has nothing to close.
func (*Store) Conns ¶
Conns lists the conns table's rows (see SavedConn), oldest first — the order they were added, which moveStoreConns keeps in the file.
func (*Store) DeleteConn ¶
DeleteConn removes a conns table row, once moveStoreConns has moved it. A missing one is success.
func (*Store) DeleteTab ¶
DeleteTab forgets a saved tab. A missing one is success: the caller wanted it gone.
func (*Store) Layout ¶
Layout returns every saved layout value (pane sizes and the like), keyed by the name the page gave it. The values are opaque here: the page owns their meaning. (The one exception is above the store: a write of "tabs" or "plans", which list saved-tab keys, is merged with the keys other windows hold before it gets here — see mergeTabKeys in claims.go.)
func (*Store) MoveLayout ¶ added in v0.3.0
MoveLayout renames layout keys: each from key's value is stored under its to key, replacing whatever was there, and the from key is deleted. A from key with no saved value is skipped, so a missing one leaves its to key alone. It is how a connection rename carries the "tableSchema.<conn>" schema picks along (handleConnEdit) — including picks saved by a window that has since closed, which no page is left to move.
The moves are applied as one simultaneous rename: every from value is read, then every from key deleted, then every to key written. A rename onto a name under the old one ("a" → "a/b") makes chains — "a" → "a/b" and "a/b" → "a/b/b" in one call — that applied one by one in map order could read a value another move had just written.
One transaction for the lot, so a rename's picks move together or not at all. A delete, not a blank: the page's own move (connRenamed) can only blank, as SetLayout has no delete, but "" is itself a pick ("every schema") and would apply to a later connection given the old name.
func (*Store) Persistent ¶
Persistent reports whether the store writes to disk.
func (*Store) RetagTabs ¶
RetagTabs moves the saved query tabs on connection from to connection to — a rename of a connection added in the browser (handleConnEdit). A saved tab's conn is the connection it reconnects to when shown again; left on a name that no longer exists it would fall back to whatever the window has active, and the user would find a tab quietly on another database. (History and saved assistant chats keep the old name: they record what happened, under the name it had then.)
func (*Store) SaveConn ¶
SaveConn adds a row to the conns table. Nothing in dbc web writes the table any more (see SavedConn); this is how the tests make a store as an older dbc web left it. It does not replace a row by the same name.
type Tab ¶
type Tab struct {
ID string `json:"id"`
Title string `json:"title"`
Conn string `json:"conn"` // the connection it was on; "" before any
Buffer string `json:"buffer"` // the editor's text
Updated time.Time `json:"updated"`
// Console is the name of the console of Conn's database the tab shows
// (consoles.go); "" before it has one, or with consoles off. Its text
// is the console's file; Buffer keeps a copy, so a dbc from before
// consoles still opens the tab as it was.
Console string `json:"console"`
// Script makes it a script tab: the name of the Go script in
// scripts_dir it edits (scripts.go). Its text is the script's file,
// saved only when asked (Ctrl+S, or Run); Buffer stays "" and Conn is
// only where the tab was when it opened. "" for a query tab.
Script string `json:"script"`
}
Tab is one query tab as the browser left it.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package pages renders dbc web's HTML with element: the workbench shell, the result table the page swaps in after a run, and the sign-in notice.
|
Package pages renders dbc web's HTML with element: the workbench shell, the result table the page swaps in after a run, and the sign-in notice. |