Documentation
¶
Overview ¶
Package webapp serves the bdrive web server: a browsable web view of synced files (file tree reconstructed from the journals, rendered markdown, downloads), browser uploads, and — in hub mode — the sync API that lets storage-blind client devices sync whole projects through this server.
Two modes:
- single-volume: Source is set (a DirSource for a plain folder, or a RemoteSource in tests); the classic viewer.
- hub: Root + Projects are set; the server hosts many projects, each a volume stored under <root>/<project-id>/ in the object store, managed by a file-backed project registry.
The client — browser or syncing device — is deliberately told nothing about the storage: no remote URL, bucket, or credentials ever appear in an API response.
Index ¶
- Constants
- func RenderMarkdown(src []byte) (string, error)
- type AuthProvider
- type BuiltinAuth
- type DeviceInfo
- type DeviceRegistry
- type DirSource
- type DirectUploader
- type FileInfo
- type HistoryEntry
- type Identity
- type Mailer
- type Node
- type Project
- type ProjectDB
- type RemoteSource
- func (r *RemoteSource) Commit(ctx context.Context, p, blob string, size int64) error
- func (r *RemoteSource) Files(ctx context.Context) (map[string]FileInfo, error)
- func (r *RemoteSource) HasBlob(ctx context.Context, blob string) (bool, error)
- func (r *RemoteSource) Open(ctx context.Context, _ string, fi FileInfo) (io.ReadCloser, error)
- func (r *RemoteSource) SignBlobPut(ctx context.Context, blob string, size int64, ttl time.Duration) (*remote.SignedPut, error)
- func (r *RemoteSource) Upload(ctx context.Context, p string, src io.Reader, _ int64) error
- type Server
- type Share
- type ShareDB
- type Source
- type UploadConfig
- type Uploader
- type User
Constants ¶
const DefaultUploadTTL = 15 * time.Minute
DefaultUploadTTL is used when UploadConfig.TTL is unset: long enough for a slow upload, short enough that a leaked URL goes stale quickly.
Variables ¶
This section is empty.
Functions ¶
func RenderMarkdown ¶
RenderMarkdown converts markdown to HTML (GFM + wikilinks). Raw HTML in the source is escaped by goldmark's safe default.
Types ¶
type AuthProvider ¶
type AuthProvider interface {
// CLILoginPath is the page `bdrive login` opens in a browser. The CLI
// appends ?redirect=http://127.0.0.1:<port>/callback&state=<nonce>.
CLILoginPath() string
// Authenticate resolves the request's Bearer token or session cookie.
Authenticate(r *http.Request) (User, bool)
// Register mounts the provider's own pages and endpoints (/auth/*,
// /api/auth/*) on the server mux.
Register(mux *http.ServeMux)
}
AuthProvider is the seam between the server and an identity system.
type BuiltinAuth ¶
type BuiltinAuth struct {
AllowSignup bool
Mail *Mailer // nil → reset links go to the server log
// contains filtered or unexported fields
}
BuiltinAuth is the open-source identity provider: email + password + name accounts and long-lived device tokens, persisted in one JSON file (loaded at open, rewritten atomically on every change — same discipline as the project registry). It owns the /auth/* pages the browser sees and the /api/auth/* endpoints the CLI uses.
func OpenBuiltinAuth ¶
func OpenBuiltinAuth(path string, allowSignup bool, mail *Mailer) (*BuiltinAuth, error)
OpenBuiltinAuth loads (or starts) the account registry at path.
func (*BuiltinAuth) Authenticate ¶
func (a *BuiltinAuth) Authenticate(r *http.Request) (User, bool)
func (*BuiltinAuth) CLILoginPath ¶
func (a *BuiltinAuth) CLILoginPath() string
func (*BuiltinAuth) Register ¶
func (a *BuiltinAuth) Register(mux *http.ServeMux)
type DeviceInfo ¶
type DeviceInfo struct {
ID string `json:"id"`
Name string `json:"name,omitempty"`
OS string `json:"os,omitempty"`
User string `json:"user,omitempty"` // account email last seen using this device
IP string `json:"ip,omitempty"` // as observed by the server
LastSeen time.Time `json:"last_seen"`
}
DeviceInfo is what the server knows about one syncing device: self-reported name/OS (headers sent by the client), plus what the server itself observed (public IP of the last push, last activity, the signed-in account). History joins ops against this registry, so IPs are real — as the server saw them — and ops stay small.
type DeviceRegistry ¶
type DeviceRegistry struct {
// contains filtered or unexported fields
}
DeviceRegistry is the file-backed device table (devices.json), same load-at-open / atomic-rewrite discipline as the project registry.
func OpenDeviceRegistry ¶
func OpenDeviceRegistry(path string) (*DeviceRegistry, error)
func (*DeviceRegistry) Get ¶
func (r *DeviceRegistry) Get(id string) (DeviceInfo, bool)
func (*DeviceRegistry) Observe ¶
func (r *DeviceRegistry) Observe(d DeviceInfo)
Observe merges what a request revealed about a device. Disk writes are throttled: identity changes persist immediately, bare last-seen bumps at most once a minute.
type DirSource ¶
type DirSource struct {
Root string
}
DirSource serves a plain local folder straight from disk — no bdrive remote or volume needed. Meant for debugging the webapp (and as a quick local markdown browser): the tree reflects the folder live, provenance is just file mtimes, and content streams from the filesystem.
type DirectUploader ¶
type DirectUploader interface {
Uploader
SignBlobPut(ctx context.Context, blob string, size int64, ttl time.Duration) (*remote.SignedPut, error)
HasBlob(ctx context.Context, blob string) (bool, error)
Commit(ctx context.Context, path, blob string, size int64) error
}
DirectUploader is additionally implemented by sources whose storage can accept presigned direct uploads.
type FileInfo ¶
FileInfo is the resolved state of one path: content identity (Blob doubles as the ETag), plus provenance where the source knows it.
type HistoryEntry ¶
type HistoryEntry struct {
Time string `json:"time"`
Kind string `json:"kind"` // put | delete
Path string `json:"path"`
Size int64 `json:"size,omitempty"`
Blob string `json:"blob,omitempty"` // sha256; fetch via the blob endpoint
User string `json:"user,omitempty"`
UserName string `json:"user_name,omitempty"`
Author string `json:"author,omitempty"` // offline/git fallback identity
Device DeviceInfo `json:"device"`
Note string `json:"note,omitempty"`
}
HistoryEntry is one change as the history API reports it.
type Identity ¶
type Identity struct {
ID, Name, Author string
}
Identity is the device identity uploads are journaled under.
type Mailer ¶
type Mailer struct {
Host string // e.g. smtp.gmail.com
Port int // e.g. 587 (STARTTLS)
User string
Pass string
From string // e.g. drive@example.com
}
Mailer sends plain-text mail over SMTP — the lowest-common-denominator transport a self-hoster can point at anything (Gmail app password, SES, Mailgun, a local relay). No SDK, stdlib only. A nil Mailer reports itself as unconfigured so callers can fall back to logging the message.
type Node ¶
type Node struct {
Name string `json:"name"`
Path string `json:"path"`
Dir bool `json:"dir"`
Size int64 `json:"size,omitempty"`
Time time.Time `json:"time,omitzero"`
Author string `json:"author,omitempty"`
Device string `json:"device,omitempty"`
Children []*Node `json:"children,omitempty"`
}
Node is one entry of the file tree returned by the tree endpoint.
type Project ¶
type Project struct {
ID string `json:"id"`
Name string `json:"name"`
Created time.Time `json:"created"`
}
Project is one synced project hosted by this server. Its storage lives under <root>/<id>/ in the object store; the id is permanent, the name is a renameable label.
type ProjectDB ¶
type ProjectDB struct {
// contains filtered or unexported fields
}
ProjectDB is the server's project registry, persisted as a JSON file that is loaded on open and rewritten atomically on every change.
func OpenProjectDB ¶
OpenProjectDB loads the registry at path; a missing file is an empty registry.
func (*ProjectDB) GetOrCreate ¶
GetOrCreate returns the project with the given name, creating it (with a fresh id) if none exists. Names are matched exactly.
type RemoteSource ¶
type RemoteSource struct {
Backend remote.Backend
// Device identifies this server in ops it journals for uploads. Required
// for uploads; irrelevant for reading.
Device Identity
// contains filtered or unexported fields
}
RemoteSource reads a beardrive remote: it fetches every journal and folds the ops into the current volume state (same total order as journal.Replay, but keeping author/device/time of the winning op per path). With Device set it also accepts uploads, journaled under that identity.
func (*RemoteSource) Commit ¶
Commit appends a put op for path→blob to this server's own journal. It refuses if the blob is not in the store yet (a peer must never see an op whose content is missing). Only this server writes this journal key, so the read-modify-write below has a single writer; upmu serializes it across concurrent requests.
func (*RemoteSource) Open ¶
func (r *RemoteSource) Open(ctx context.Context, _ string, fi FileInfo) (io.ReadCloser, error)
type Server ¶
type Server struct {
// Single-volume mode: serve exactly this source.
Source Source
Volume string // display only
// Hub mode (when Root is set): many projects on one storage root.
Root remote.Backend
Projects *ProjectDB
// Device identifies this server in ops it journals for browser uploads.
Device Identity
Refresh time.Duration
Upload UploadConfig
// Auth, when set, gates the whole API behind sign-in. Nil means the
// historical trusted-network behavior: no accounts, everyone welcome.
Auth AuthProvider
// Devices, when set, records what the server observes about syncing
// devices (name, OS, public IP, last activity) for history.
Devices *DeviceRegistry
Shares *ShareDB
// contains filtered or unexported fields
}
Server renders volumes as a website and, in hub mode, brokers sync for client devices.
type ShareDB ¶
type ShareDB struct {
// contains filtered or unexported fields
}
ShareDB is the file-backed share registry (shares.json), same discipline as the project registry.
func OpenShareDB ¶
func (*ShareDB) Create ¶
Create returns a share for (project, path), reusing an existing live one so repeated shares of the same file hand out the same URL.
type Source ¶
type Source interface {
Files(ctx context.Context) (map[string]FileInfo, error)
Open(ctx context.Context, path string, fi FileInfo) (io.ReadCloser, error)
}
Source supplies the file set and content of one volume. Implementations: RemoteSource (a beardrive remote) and DirSource (a plain local folder).
type UploadConfig ¶
type UploadConfig struct {
Enabled bool
// TTL bounds the lifetime of presigned direct-upload URLs.
TTL time.Duration
}
UploadConfig controls whether and how clients may write.