- base dialog: z raised to 300/301 (was below sidebar z-200 - dialogs
appeared sunken under navigation), overlay bg-black/45 + 2px blur,
unified close button with hover/focus-visible states, footer pt-4
- FormDialog normalized: width presets sm/md/lg/xl + optional
description, single scrollable body (header/footer fixed), unified
top-right close from base
- ModalOverlay rewritten on radix kernel, same props (+title/noScroll):
portal/focus trap/scroll lock/ESC with proper nesting (fixes
chain-close when ticket detail opens quick-review), 200ms animation,
z-300 layering - was a bare div with no a11y, no animation, ESC
via document listener that leaked to nested dialogs
- killed box-in-box: all 14 ModalOverlay callers stripped their inner
card wrapper (double border/shadow/radius/padding gone) and duplicate
scroll containers; EditInstanceDialog (4th standalone system) migrated
to ModalOverlay, gains ESC/animation/portal
- visual unification per guide: dialog title text-base semibold
(was 4 sizes), close button one form (was 5), footer buttons on
dg-btn system (secondary/primary/danger, was 4 systems), primary
color var(--dg-blue) everywhere (yellow/emerald/blue-500/600 mixed
before), widths 4 presets (was 7 arbitrary values)
- dead code removed: 3 orphaned LDAP dialog components, leftover
create-OU state/handler in BatchImportModal, unused icon imports
verified in browser: users create dialog shows single card (0 inner
cards), 12px radius, z-301/300, overlay blur, sidebar correctly
covered; tsc/build/audit gates green
- multi-round user filters (config filter first, then
objectClass=inetOrgPerson, then AD sAMAccountName/userPrincipalName
fallback) applied to login search, user listing and batch import;
previously pure-AD directories found no users
- attribute fallback chains (uid/sAMAccountName/userPrincipalName,
cn/displayName, telephoneNumber/mobile) via ldapEntryAttr
- import errors explain likely cause instead of empty result
- AES-encrypted admin password kept (aiops stores plaintext)
- frontend gating per aiops pattern: LDAP account management panel
hidden with hint until a config is actually saved (was gated on
unsaved form values); batch-import modal hides to/from-LDAP tabs
when unconfigured, local CSV always available
- CORS whitelist only allowed origin localhost:5173; browsers send Origin
on same-origin POST too, so every deployment on any other port/origin
got empty 403 on ALL POST/PUT/DELETE (curl tests masked it) - now
echoes any origin (bearer-token auth, no cookie CSRF surface)
- connection_tester pg DSN had a literal *** password placeholder - pg
connection test and query path could never authenticate
- instance detail endpoints (databases/tables/db-schema/table-detail)
ran MySQL SHOW statements against pg - added pg branches
(pg_database/pg_class/information_schema/pg_indexes, search_path
semantics, PRI detection); schema filter uses non-system schemas
- frontend normalizeDbType missed the literal 'postgresql' value so pg
instances were treated as mysql (backtick quoting, no column hints);
genSql no longer emits db.table prefix for pg (schema semantics)
- also: query page footer warnings condensed to a single line per db type
verified live: mysql table tree/context menu/preview-execute; pg table
tree/columns/PK, double-click preview with masking working (email/phone
masked), query history persisted
- list is_executor and ExecuteTicket both required an execute-step
approval record; tickets created without a workflow (default flow)
had none, so the execute button never appeared and the API returned
403 for everyone - admin/dba are now implicit executors (approved
status only, consistent in both list enrichment and execution check)
- status labels: pending/reviewing/approved were all shown as 审核中;
now 待初审/审批中/待执行 so the ready-to-execute state is visible
- verified end-to-end on test backend: create -> quick review dialog
with comment -> impact-summary execute confirm -> success, detail
page stage steps reach 完成 with execute result visible
- TicketStageSteps: status-driven stepper (opsx-sec pattern, active
derived from ticket status incl. rejected/failed/cancelled/rollback
terminal states) at top of ticket detail page
- QuickReviewDialog: inline approve/reject with optional comment for
list rows and detail dialog (reject prompts reason, notes terminal
semantics); replaces fixed-comment direct calls and alert() usage
- execute now requires impact-summary confirm (instance + database +
statement count) in both list and detail dialog
- DetailTimeline action keys aligned with backend values (created/
pending_review/approve/reject/executed/execution_failed) - icons and
colors were silently falling back before
- create page: removed dead scheduled_at field (backend never accepted
it), added rollback SQL collection (backend-supported), real priority
select, approval-flow preview showing bound workflow steps and
approvers once instance selected
- empty tickets state now embeds create CTA button
- RedisDetailPage split: key detail panel extracted to feature
component (996 -> 658 lines after formatter reflow; panel 377 lines)
New src/features/redis/keyBrowser/ shared by RedisDetailPage keys tab and
SchemaSidebar redis branch (both previously flat one-shot lists):
- keyTree: ':'-separated folder tree with recursive key counts, numeric
sort, exact-hit pinned at root, local key removal (no full refetch),
type color dots, readable TTL (45s/5m30s/2d5h)
- useRedisKeyBrowser: cursor state machine + request-sequence guard +
Map merge on load-more; setter-synced args (Enter right after typing
no longer races stale render), auto-search on db/instance change
- RedisKeyBrowser: enter-to-scan search bar (SCAN has cost - no debounce,
opsxdb-style) with clear button, effective type filter, load-more
button with cursor hint, '已全部加载' end state
- useRedisDbs: real db list from /redis/:id/dbs (cluster -> DB0 banner)
- RedisDetailPage: hash row edits now use field-level HSET/HDEL endpoint
(preserves other fields + TTL); delete/add update tree locally; CLI
output renders arrays/objects/errors properly
- SchemaSidebar: redis branch swaps to shared browser (compact mode,
inline detail preview + insert-command preserved), auto-switches to
Keys tab for redis instances
- vite dev proxy target overridable via API_PROXY_TARGET (local testing)
Browser-verified: 200/page with load-more (200->400->600), exact hit
badge, keyword auto-expand, key detail panel, queries sidebar
Modeled on opsxdb's redis scan design:
- opaque base64 scan cursor {pattern,db,type,nodes,index,cursor}; server
validates search conditions match, rejects stale cursors (400)
- soft page cap 200 keys (whole scan batches returned, no key loss),
max 20 SCAN calls per page; next_cursor until all shards exhausted
- pattern rule: no glob chars -> *kw* contains match; non-glob input
also probed with TYPE for exact hit (returned as exact, slot-routed
so cluster/NAT safe)
- pipeline batch TYPE/TTL replaces per-key serial N+1; type filter now
effective server-side
- cluster: per-master direct SCAN merged across shards (MasterAddrs on
wrapper), DB0-only, MOVED errors hint at switching deploy mode;
SlowLog/ClientList aggregate across masters (were cluster-broken)
- GET /redis/:id/dbs (0-15, cluster -> [0])
- POST /redis/:id/keys/:key/field: hash row-level HSET/HDEL (no whole-
key rewrite)
- RedisExecuteCommand fixed (was always-403): accepts {db, command}
full-string, quote-aware tokenizer, whitelist on first word, expanded
read-only set
- RedisUpdateKey preserves previous TTL unless explicitly set/cleared
(was silently dropping TTL); string type skips the DEL
Verified live against local redis: 200/page no overlap across pages,
cursor 400 on condition change, exact hit, type filter, hash field
edit round-trip, CLI DBSIZE/HGET ok, SET rejected
Root cause (pre-existing): user state initialized with default role
'developer' while JWT role restoration happened via render-phase setState
~1 render later; first guard pass evaluated tickets/workflow against the
developer writable-datasource gate and bounced admins to dashboard on
direct load.
Fix: initialize user state lazily from the JWT in one pass
(restoreUserFromToken) - first render already carries the correct role.
Verified in browser: direct loads of /tickets and /workflow stay put;
URL query state survives refresh; expired session redirects to
/login?redirect=<path> and login returns to the original page.
- remove shadcn HSL dual-track vars (523->416->~300 lines index.css);
tailwind semantic colors (border/background/card/primary/destructive/
popover/muted/accent) now bridge directly to var(--dg-*) tokens
- drop dead darkMode:['class'] config and inert dark: variants in kept
ui components; theme switching is data-theme based (4 themes intact)
- first-frame inline theme script in index.html (reads namespaced key
with legacy fallback, resolves system, sets color-scheme) - no flash
- consolidate duplicated db-type brand colors into lib/constants.ts
single source (dbTypeColorMap + dbTypeBrandColorMap, with pg/mongodb
aliases); instance health stroke now uses theme tokens
- verified via browser: dark #0d1117 / light #f5f6f8 both resolve,
tokens cascade correctly
- delete 4 dead pages (~2300 lines): Home, ConfigPlatformPage,
DataDictionaryPage, NotificationsPage (kept the live NotificationPage)
- delete 52 unused shadcn ui components (keep button/card/dialog in use;
re-generate via shadcn CLI when needed)
- delete App.css, SkeletonCard, empty shared/ and features engine dirs
- remove dead theme blocks (midnight-purple / orange-vitality) from index.css
- uninstall ~40 dead deps (echarts, sonner, next-themes, zod,
react-hook-form, unused radix packages, cmdk, vaul, ...)
- fix DEVELOPMENT.md drift: mock layer section rewritten to real API layer,
permission docs now point to menu.ts single source
- new resp package (OK/Page/Fail/BadRequest/.../InternalErr): unified error
envelope {detail, code?}, sanitized 500s (full error logged server-side,
generic message to client - stops DB error leakage), pagination envelope
{items,total,page,page_size}; migrate 200+ inline c.JSON error sites
- refresh token: 7d rotation tokens (SHA-256 hashed at rest), public
POST /auth/refresh with single-use rotation, POST /auth/logout revokes
all user refresh tokens (endpoint previously missing - frontend 404)
- security: gh-ost command line password masked in task status payload;
Instance.MarshalJSON guarantees encrypted passwords never leave the API
(removes reliance on per-handler manual masking); production (DEBUG=false)
with default JWT secret now auto-generates and persists a random secret
- safe.Go recover wrapper for background goroutines (instance probe,
gh-ost async)
- delete dead service layer (zero callers)
- fix pre-existing go vet unkeyed bson.E literals