mirror of
https://github.com/hanzoai/base.git
synced 2026-10-21 03:41:50 +00:00
d582e283bee83dac838eed65ae607ffffef3fb90
2
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
069fcecb5e |
feat(admin): Twenty-grade editable record grid on @hanzo/ui (true-black) (#30)
* test+fix: clear pre-existing base test failures (iam /api→/v1, kube ns, pool timing) These failed on main already — surfaced once the zip-dep failure (#24) stopped masking the test run. None relate to zip; all are real drift/bugs: - iam userinfo path drift (5 failures): the client correctly requests the canonical /v1/iam/oauth/userinfo (HIP-0111), but the test fake-servers still registered the legacy /api/userinfo → 404. Updated iam_test.go (×4 + comment), platform_test.go, bootnode_test.go to the /v1 path. /api/get-user(s)/add-user are left as-is (client + tests agree there; a separate migration). - kube namespace bug (real): Client.New(defaultNamespace) let the in-cluster serviceaccount namespace file OVERRIDE an explicit namespace — so an ARC runner (arc-system) silently clobbered the caller's 'bootnode'. Now the pod namespace is adopted ONLY when the caller passed "". Explicit config wins. - db pool idle-eviction flake: IdleTimeout 50ms was shorter than the 3-pool setup loop on a loaded runner, so the sweeper evicted pool 0 before the Len()==3 assertion. Bumped to 300ms (+ wait 150→400ms). * build: goreleaser before-hook go mod download, not tidy (fixes release SECURITY ERROR) The release workflow runs GoReleaser on every PR; its before-hook ran `go mod tidy`, which RE-RESOLVES the module graph and trips the authenticated-git-only private module set (SECURITY ERROR / go.sum mismatch on the luxfi deps) — the Test and build steps pass because they verify against the committed go.sum via the runner's git auth instead of re-resolving. Release must never re-tidy (it mutates committed deps at release time). Swap to `go mod download`, which just populates the cache against the existing go.sum — same resolution path as the passing `go test ./...` step. * ci(release): GOPRIVATE + git-auth so goreleaser resolves luxfi from git The goreleaser go-mod hook fetched luxfi/keys from the public proxy (re-pushed bits) instead of authenticated git, so go.sum verification failed (SECURITY ERROR). Add GOPRIVATE + the same git insteadOf auth the Docker gate uses, at the job level, so test + the download hook + the builds all fetch hanzoai/* + luxfi/* from git — matching the committed go.sum. * feat(admin): @hanzo/ui true-black design system + buildable Vite baseline The ui-react admin was un-buildable WIP: package.json listed wouter/react-query while src used @tanstack/react-router + @luxfi/ui (Tamagui) with no Tailwind pipeline, a broken "/base" SDK import in all 19 routes, and a duplicate getRecord export. Nothing compiled. This lands a buildable baseline on the true-black @hanzo/ui shadcn design system. Verdict on @hanzo/ui consumability (settled empirically): the published @hanzo/ui@8 (gui/Tamagui, raw-TS source) and the @hanzo/ui barrel are NOT Vite-consumable — the barrel's index.mjs hard-imports next/image, next/link, cmdk, sonner, vaul, react-resizable-panels (unresolvable in Vite). @hanzo/ui@5 granular subpaths resolve but several components are coupled to bespoke theme classes. So we adopt @hanzo/ui's token table + true-black override and vendor the shadcn primitives (the pattern @hanzo/ui's own templates/vite-app uses). Verified in a browser: bodyBg #000, card #0a0a0a, hairline #171717, near-white primary, Basel + Geist Mono, .dark active. - package.json: real deps (@hanzo/[email protected], react-router, tailwind v3, radix, react-hook-form); drop wouter/@luxfi/ui - tailwind.config.cjs + postcss.config.cjs + src/index.css: shadcn HSL token scheme, true-black .dark (#000 canvas, gui surface/border ladder), Basel (self-hosted) + Geist Mono fonts - src/components/ui/{button,input,label}.tsx: vendored shadcn primitives on the shared tokens; src/lib/cn.ts - src/lib/api.ts: fix duplicate getRecord export (-> getRecordById), add getFullRecords paging + real /v1/realtime SSE client, export request - src/lib/base-client.ts: BaseClient object API over the /v1 fetch layer — unbreaks all 19 routes with zero route rewrites; base.ts drops broken "/base" - main.tsx: QueryClientProvider, import css, pin .dark, router basepath bound to BASE_ADMIN_UI_PATH (fixes /_/ mount routing); vite.config proxies /v1 - login.tsx converged onto @hanzo/ui primitives (first true-black route) - delete dead src/pages/* (old wouter generation, imported nowhere) - embed.go: correct the stale build comment (source is ./src, pnpm build) - rebuild committed dist/ (embedded by //go:embed) * feat(admin): Twenty-grade editable record grid on @hanzo/ui Replace the read-only records table with an inline-editable data grid — the heart of "edit everything". Cells own a display/edit split with a single commit/cancel protocol; the grid owns a two-tier keyboard cursor. - components/grid/RecordGrid: sortable columns (per-field sort toggle), sticky header, row selection + select-all, per-row actions (open/duplicate/delete), two-tier cursor — arrows move the active cell, Enter/F2/type-to-edit enters edit mode, Tab advances. True-black, hairline rows. - components/grid/EditableCell: display mode → floating Popover editor per type (text/number/date/textarea/json/select/relation); bool toggles in place; file/password/autodate render read-only. json validates on commit. - lib/fields: field-type → editor-kind mapping + display formatting + API value coercion, orthogonal to the grid. - routes/collections.$id.records: optimistic single-field persist (react-query onMutate cache patch + rollback on error), create (→ detail panel), duplicate, single + bulk delete behind a confirm Dialog, filter, pagination. - vendored primitives: checkbox, select, popover, dropdown-menu, dialog, textarea, badge (shadcn on the shared tokens); add the radix deps. - rebuild committed dist/ * feat(admin): record detail/edit panel on @hanzo/ui with per-type editors Rewrite the record editor on @hanzo/ui primitives + lib/fields. Each field renders the right editor by type — Input (text/number/date/url/email), Textarea (editor/json, json validated on submit), Select (single select), Checkbox (bool), file input (upload), relation as id csv. Auth collections get a password field; id + autodate render read-only. Controlled form state, optimistic invalidate, create/save/delete. True-black card, Basel type. Drops react-hook-form in favor of a small controlled form so @hanzo/ui Select/Checkbox bind cleanly. * feat(admin): converge root sidebar + collections list to true-black @hanzo/ui Phase 1 of the view convergence. The always-visible chrome and the entry point to the grid now match the true-black system. - __root: sidebar on bg-background/border-border with lucide icons + accent active state (was bg-neutral-950 + indigo) - collections: clickable collection rows -> /collections/$id/records, type/system badges, @hanzo/ui Input filter + Button delete (was raw table + indigo button) * fix(admin): flat route escapes so record grid/editor are reachable + safe default sort Two correctness fixes surfaced by end-to-end browser verification against a real Base server: 1. Routing: collections.$id[.records[.$recordId]] nested as TanStack layouts, but the parent components render content (not <Outlet/>), so /collections/$id/records rendered the collections list instead of the grid. Rename to flat escaped routes (collections_.$id_.records...) so each is a standalone route under root — URLs and all Link targets unchanged. 2. Default sort '-created' 400s on this fork's base collections (no `created` field). Default to '' (API insertion order, valid for any schema); sorting stays opt-in via column headers. Verified: grid renders true-black against /v1, inline text edit (floating editor) and bool toggle both persist to the real Base API (confirmed server-side). * docs(llm): admin @hanzo/ui rebuild — consumability verdict, IAM-native auth finding, phased plan --------- Co-authored-by: Hanzo Dev <[email protected]> |
||
|
|
7dcecb01c9 |
feat(bootnode): Go port foundation as a Base plugin (5 modules end-to-end) (#15)
Why: consolidate the Python bootnode backend (bootnode/api/, ~100 .py files)
onto Hanzo Base, leveraging Base's IAM client and per-org/per-user tenant
infrastructure instead of reimplementing auth, sessions, and multi-tenancy.
This is the structural foundation + the 5 most-important modules, not the whole
port.
What landed
- plugins/bootnode/: blockchain developer platform mounted under /v1.
Five modules ported end-to-end:
1. auth — multi-network OAuth2 callback (lux/pars/zoo/hanzo share the
lux-web3 IAM app; client id derived from redirect_uri) + bn_
project API keys (salted SHA-256, raw key shown once, verified
in constant time). Accepts IAM JWTs and pk-/sk-/hk- keys by
reusing github.com/hanzoai/base/iam — no IAM logic duplicated.
2. team — org/member CRUD scoped to the caller's project; invited emails
resolved against IAM (active) or held pending with an invite
token.
3. networks — applies bootno.de/v1 Network CRs (white-label brand, tier,
region, validator fleet). Replaces the Python's kubectl + raw
nginx-Ingress templating with a declarative CR for the
bootno.de operator to reconcile.
4. nodes — applies bootno.de/v1 NodeFleet CRs (CRD-driven cloud path;
the Python docker provider was a local-dev concern).
5. keys — applies bootno.de/v1 KMSSecret CRs by KMS path. NO plaintext
key material ever touches this service; the request and
response carry none, and a guard rejects any private-key field.
- plugins/bootnode/kube/: dependency-free Kubernetes REST client (net/http
server-side apply). No client-go, no CGO. In-cluster SA or KUBE_APISERVER.
- plugins/commerce/: typed Hanzo Commerce (Square billing) client behind a
Client interface. bootnode depends on the interface; commerce never depends
on bootnode/iam.
- 11 SQLAlchemy models -> Base collections (models/collections.go). No `users`
collection: IAM owns identity; bootnode references IAM user ids as text.
OrgCluster is the canonical org->k8s-cluster mapping.
- examples/base/main.go: platform plugin now runs PrincipalIsolation="sqlite"
(per-org + per-user encrypted SQLite); bootnode registered (BOOTNODE_ENABLED).
Modules pending (15, tracked in PR body): chat, zap, billing-http, bundler,
fleets, gas, infra, launch, lux, mpc, nfts, observability, rpc, tokens,
transfers, wallets, webhooks-http. (chains is also ported as a bonus 6th.)
Tests (20 functions, all green incl -race)
- auth: token classification, key gen/hash/verify (tamper + wrong-salt),
redirect->clientId derivation.
- kube: server-side-apply shape (PATCH + apply-patch+yaml + fieldManager),
error propagation, 404 get/delete idempotency.
- commerce: disabled no-op, get-or-create (create + existing), usage error
propagation, immediate cancel.
- workers: HMAC-signed delivery, non-2xx-is-failure, unreachable-is-failure.
- bootnode: full end-to-end against a fake IAM + fake apiserver — /me 401 then
200, project + bn_ key, team invite/list, Network + NodeFleet CR apply/get,
KMSSecret plaintext-rejection + apply + status, public chains. Plus
fail-fast on the insecure default salt against production IAM.
Verified: `go build ./...` exits 0; `go test -race ./plugins/bootnode/...
./plugins/commerce/...` passes; live binary serves /v1/chains (200) and gates
/v1/auth/me, /v1/networks (401); per-org SQLite isolation logged active; all
10 _bootnode_ collections created on boot.
Co-authored-by: zeekay <[email protected]>
|