Artifact 4e264ef28c93ff278b590e9998e2c5b6c1ef717ded3956e1429ad8bb39257011:
- File
hub/users.go
— part of check-in
[67ea682587]
at
2026-05-04 18:34:31
on branch trunk
— feat(hub): new package for hub-fossil management and HTTP serving (#109)
Adds a top-level hub/ package that hosts an EdgeSync hub in-process: fossil hub repo bootstrap, embedded NATS server with JetStream, auto-port allocation, libfossil HTTP server (timeline UI + xfer endpoint), user management, and hub-side commits/reads. All libfossil and nats-server types are wrapped internally — the public surface exposes only stdlib types and types defined here.
Public API: - Config + NewHub + ServeHTTP + Stop - NATSURL / LeafUpstream / HTTPAddr accessors - AddUser / GetUser / HasUser / ListUsers / RemoveUser (User type) - Commit / Read / ReadAt (RevID, FileToCommit, CommitOpts types)
Auto port allocation: passing 0 for FossilHTTPPort, NATSClientPort, or NATSLeafPort yields three distinct bound ports. The HTTP listener is bound during NewHub so HTTPAddr() is immediately usable, even before ServeHTTP is called.
SQLite tuning (SetMaxOpenConns(1) + busy_timeout=30s) applied at hub bootstrap, matching the leaf agent's tuning.
Lives inside the EdgeSync root module (not a separate go.mod) so it ships in lockstep with the leaf agent. Can be promoted to its own module later if downstream consumers need to depend on it independently.
Closes #102 (user: [email protected], size: 1595) [annotate] [blame] [check-ins using]
package hub import ( "errors" "fmt" libfossil "github.com/danmestas/libfossil" ) // User describes a fossil user on the hub repo. type User struct { Login string Caps string } // AddUser creates a fossil user. Returns an error if Login is empty or the // user already exists. func (h *Hub) AddUser(u User) error { if u.Login == "" { return errors.New("hub: AddUser: Login is required") } if err := h.repo.CreateUser(libfossil.UserOpts{Login: u.Login, Caps: u.Caps}); err != nil { return fmt.Errorf("hub: add user %q: %w", u.Login, err) } return nil } // GetUser returns the user with the given login. Returns an error if no // such user exists. func (h *Hub) GetUser(login string) (User, error) { u, err := h.repo.GetUser(login) if err != nil { return User{}, fmt.Errorf("hub: get user %q: %w", login, err) } return User{Login: u.Login, Caps: u.Caps}, nil } // HasUser reports whether a user with the given login exists. func (h *Hub) HasUser(login string) bool { _, err := h.repo.GetUser(login) return err == nil } // ListUsers returns all users on the hub. func (h *Hub) ListUsers() ([]User, error) { users, err := h.repo.ListUsers() if err != nil { return nil, fmt.Errorf("hub: list users: %w", err) } out := make([]User, len(users)) for i, u := range users { out[i] = User{Login: u.Login, Caps: u.Caps} } return out, nil } // RemoveUser deletes the user with the given login. func (h *Hub) RemoveUser(login string) error { if err := h.repo.DeleteUser(login); err != nil { return fmt.Errorf("hub: remove user %q: %w", login, err) } return nil }