Documentation
¶
Overview ¶
Package users hosts the `olares-cli settings users ...` subtree.
common.go centralizes the per-area Doer / output plumbing in the same shape as cli/cmd/ctl/settings/me/common.go. We deliberately don't share the helpers across packages because the per-area transports may need per-area types in later phases (e.g. settings/apps will need ListResult awareness, settings/backup will need a different base path), and a duplicated 100-line common.go per package is a much smaller cost than teasing the shared abstraction out before we know what stays common.
All `users` reads we ship here are gated server-side: app-service's /app-service/v1/users handler runs as the cluster controller and lists every user; user-service's /api/users/v2 wrapper applies a role-based filter so non-privileged callers only see themselves. We standardize on v2 for `users list` so the CLI degrades gracefully — a normal user gets a 1-row table instead of a 403, which matches the SPA's UX.
Package users implements the `olares-cli settings users` subtree.
Backed by user-service's bfl/users.controller.ts which itself proxies BFL's /api/users surface; see plan.md's section "1. Users" for the authoritative API map. Each settings area lives in its own Go package (rather than one flat `settings` package) so per-area types, parsers, and printers don't collide across the 13 sub-trees.
The umbrella exposes a `me` alias that delegates to the shared whoami helper in cmd/ctl/profile, matching plan.md's "two entry points, same implementation" rule.
Index ¶
- func NewCreateCommand(f *cmdutil.Factory) *cobra.Command
- func NewDeleteCommand(f *cmdutil.Factory) *cobra.Command
- func NewGetCommand(f *cmdutil.Factory) *cobra.Command
- func NewListCommand(f *cmdutil.Factory) *cobra.Command
- func NewMeCommand(f *cmdutil.Factory) *cobra.Command
- func NewUsersCommand(f *cmdutil.Factory) *cobra.Command
- type Doer
- type Format
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func NewCreateCommand ¶
NewCreateCommand implements `settings users create`.
func NewDeleteCommand ¶
NewDeleteCommand implements `settings users delete`.
func NewGetCommand ¶
`olares-cli settings users get <username>`
Wraps user-service's GET /api/users/:username. user-service's bfl/users.controller.ts:95 explicitly returns `data.data` from the upstream axios response — the upstream is app-service /app-service/v1/users/<name>, which writes a single UserInfo struct directly to the response body (see framework/app-service/.../ handler_user.go:422 handleUser).
Depending on whether NestJS' global response interceptor is active, the wire body shows up either as the raw UserInfo object or wrapped in an envelope `{code:200, data:{...}}`. decodeObjectResult (cli/cmd/ctl/settings/users/common.go) probes for a top-level `code` field and unwraps `data` accordingly, falling back to raw-body decode when no envelope is present, so we stay forward-compatible with whichever shape user-service settles on.
Role: admin floor. app-service does not gate handleUser server-side (any authenticated user gets a successful response for any username), but the SPA's "Users" page is admin-only — non-admin users have no UI entry into per-user inspection. We mirror that here so the CLI surface matches the SPA. A 404 still wins when the username doesn't exist on the server.
func NewListCommand ¶
`olares-cli settings users list`
Wraps user-service's GET /api/users/v2 — chosen over /api/users so non-privileged callers see themselves rather than a 403. Server flow (users.controller.ts:71-93):
- proxy app-service /app-service/v1/users (ListResult with all users)
- resolve currentUser by olaresId.split('@')[0]
- if currentUser is owner/admin → return as-is else → return only currentUser
Wire shape (after step 3, what NestJS sends back):
{ code: 200, data: [UserInfo, ...], totals: N }
We unwrap manually here because the BFL envelope helper in settings/me/common.go expects code=0 with a message field. App-service uses code=200 and no message.
Role: admin floor. The SPA only surfaces "Users" in the Settings menu for admin/owner (apps/.../stores/settings/admin.ts:menus); we mirror that here so a non-admin caller gets a refresh-and-retry hint up front rather than a single self-only row that quietly hides the rest of the instance. The server-side v2 endpoint still gracefully degrades, so passing the gate (or running with an empty role cache) yields the same shape the SPA would.
func NewMeCommand ¶
NewMeCommand: `olares-cli settings users me [--refresh] [-o table|json]`
Canonical "Settings -> Users -> me" entry point — a verb under the users area rather than a sibling of users / appearance / apps. Same behavior as `olares-cli profile whoami` and `olares-cli settings me whoami`: both are aliases that flow through pkg/whoami.Run, so they share output shape, cache write semantics, and the --refresh flag.
We deliberately ship all three at once (rather than picking a winner) because each entry point matches a different mental model:
- profile whoami → "where am I logged in / which profile is active" — same family as profile list, profile use.
- settings users me → docs.olares.com/manual/olares/settings/ UI mapping; you found Users in the SPA menu, the SPA shows you at the top.
- settings me whoami → tucked under the Person dropdown in the SPA; CLI-friendly self-service tree.
All three call the same code; the help text disambiguates which is which.
func NewUsersCommand ¶
NewUsersCommand returns the `settings users` parent: list and inspect Olares users, create/delete (HTTP-backed, same routes as Termipass), plus the `me` whoami shortcut. set-password / set-limits remain out of scope until wired here.
Types ¶
type Doer ¶
type Doer interface {
DoJSON(ctx context.Context, method, path string, body, out interface{}) error
}
Doer is the smallest contract the verbs need from the underlying HTTP client; *whoami.HTTPClient satisfies it (and has the desktop-ingress 401/403 reformatting we want), tests can supply a fake.