Help us shape the future of Go! Take the 2026 Go Developer Survey

web

package
v0.9.0 Latest Latest
Warning

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

Go to latest
Published: Oct 7, 2026 License: MIT Imports: 51 Imported by: 0

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

View Source
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

func GenerateSecret() (string, error)

GenerateSecret returns a random URL-safe secret (24 characters, 18 bytes).

func StateFile

func StateFile() string

StateFile is where the store lives, beside the history and the TUI's editor buffer; "" when no home directory is known.

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

func New(cfg *config.Config, mgr *db.Manager, opt Options) (*Server, error)

New builds the server over the app's config and connection manager. It does no network IO; Run listens.

func (*Server) LoginURL

func (s *Server) LoginURL() string

LoginURL is the URL that signs a browser in: open it once.

func (*Server) Run

func (s *Server) Run() error

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.

func (*Server) Shutdown

func (s *Server) Shutdown()

Shutdown stops every tab's work and releases its session. The listener is rweb's to close (it already has, when Run calls this).

func (*Server) URL

func (s *Server) URL() string

URL is the server's base URL, once listening.

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

func OpenStore(path string) (*Store, error)

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

func (s *Store) Close() error

Close closes the file, and with it bytdb lets go of the lock. A memory-only store has nothing to close.

func (*Store) Conns

func (s *Store) Conns() ([]SavedConn, error)

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

func (s *Store) DeleteConn(name string) error

DeleteConn removes a conns table row, once moveStoreConns has moved it. A missing one is success.

func (*Store) DeleteTab

func (s *Store) DeleteTab(id string) error

DeleteTab forgets a saved tab. A missing one is success: the caller wanted it gone.

func (*Store) Layout

func (s *Store) Layout() (map[string]string, error)

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

func (s *Store) MoveLayout(moves map[string]string) error

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) Path

func (s *Store) Path() string

Path is the store's file; "" for a memory-only store.

func (*Store) Persistent

func (s *Store) Persistent() bool

Persistent reports whether the store writes to disk.

func (*Store) RetagTabs

func (s *Store) RetagTabs(from, to string) error

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

func (s *Store) SaveConn(c SavedConn) error

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.

func (*Store) SaveTab

func (s *Store) SaveTab(t Tab) error

SaveTab writes a tab, replacing any saved under the same id.

func (*Store) SetLayout

func (s *Store) SetLayout(values map[string]string) error

SetLayout merges values into the layout: each key given is replaced, the rest are kept.

func (*Store) Tabs

func (s *Store) Tabs() ([]Tab, error)

Tabs lists the saved tabs, oldest id first.

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.

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.

Jump to

Keyboard shortcuts

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