Add React dashboard and performance profiling capabilities#34
Add React dashboard and performance profiling capabilities#34
Conversation
- Replace HTML templates with modern React/Preact dashboard using TypeScript - Add comprehensive performance profiling with CPU/memory tracking, flame graphs, and bottleneck detection - Implement OpenTelemetry tracing integration - Add profiling and tracing example applications - Embed static assets in Go binary for zero-dependency deployment
There was a problem hiding this comment.
Sorry @doganarif, your pull request is larger than the review limit of 150000 diff characters
There was a problem hiding this comment.
40 issues found across 78 files
Prompt for AI agents (all 40 issues)
Understand the root cause of the following 40 issues and fix them.
<file name="internal/dashboard/static/index.html">
<violation number="1" location="internal/dashboard/static/index.html:7">
Absolute asset path breaks when dashboard is mounted under a subpath; use a relative path so assets load under the dashboard prefix.</violation>
<violation number="2" location="internal/dashboard/static/index.html:24">
Absolute script path will bypass the dashboard mount and fail to load under non-root mounting; use a relative path.</violation>
</file>
<file name="internal/middleware/tracer.go">
<violation number="1" location="internal/middleware/tracer.go:65">
Nested StartTrace incorrectly uses the parent of the current trace, preventing proper child nesting and dropping first-level children under root.</violation>
</file>
<file name="cmd/examples/tracing/main.go">
<violation number="1" location="cmd/examples/tracing/main.go:51">
Possible nil pointer dereference: tracer.RecordCustom is called without checking tracer != nil.</violation>
</file>
<file name="internal/middleware/profiling.go">
<violation number="1" location="internal/middleware/profiling.go:60">
Starting runtime CPU profiling per request is process-wide and unsafe under concurrent traffic; it may conflict and skew metrics.</violation>
<violation number="2" location="internal/middleware/profiling.go:67">
Unbounded io.ReadAll on request body with ignored error can cause OOM and misses read failures; limit and handle errors.</violation>
<violation number="3" location="internal/middleware/profiling.go:69">
Sensitive data risk: storing full request body without redaction or size limits.</violation>
<violation number="4" location="internal/middleware/profiling.go:163">
profilingResponseWriter drops optional http interfaces (Flusher/Hijacker/Pusher), potentially breaking streaming, SSE, and WebSockets.</violation>
</file>
<file name="internal/dashboard/ui/src/components/FlameGraph.tsx">
<violation number="1" location="internal/dashboard/ui/src/components/FlameGraph.tsx:73">
Avoid setting innerHTML with untrusted data; this allows XSS. Construct the tooltip with textContent or sanitized HTML.</violation>
<violation number="2" location="internal/dashboard/ui/src/components/FlameGraph.tsx:79">
Use clientX/clientY for positioning when the tooltip uses position: fixed; pageX/pageY lead to incorrect offsets on scroll.</violation>
</file>
<file name="internal/dashboard/ui/src/App.tsx">
<violation number="1" location="internal/dashboard/ui/src/App.tsx:58">
useEffect uses filters in its callback but has an empty dependency array, causing stale filter application on live updates.</violation>
</file>
<file name="internal/profiling/flamegraph.go">
<violation number="1" location="internal/profiling/flamegraph.go:161">
Hotspot detection never recurses from root because node.Value == 0 triggers an early return, yielding empty results.</violation>
</file>
<file name="internal/dashboard/handler.go">
<violation number="1" location="internal/dashboard/handler.go:32">
Error from fs.Sub is ignored; staticFS may be nil and later Open will panic. Handle the error or provide a safe fallback.</violation>
</file>
<file name="internal/dashboard/ui/src/components/RequestComparison.tsx">
<violation number="1" location="internal/dashboard/ui/src/components/RequestComparison.tsx:97">
Objects (including JSX elements) are JSON-stringified in cells, causing incorrect rendering (e.g., Status row). Render JSX elements as-is when detected.</violation>
</file>
<file name="internal/dashboard/ui/postcss.config.js">
<violation number="1" location="internal/dashboard/ui/postcss.config.js:4">
PostCSS config references autoprefixer, but the package is not installed, likely causing the Tailwind build to fail. Install autoprefixer (devDependency) or remove the plugin from the config.</violation>
</file>
<file name="internal/dashboard/ui/src/components/ExportImport.tsx">
<violation number="1" location="internal/dashboard/ui/src/components/ExportImport.tsx:65">
CSV export does not escape internal quotes or sanitize leading formula characters, risking malformed CSV and potential CSV injection in spreadsheet apps.</violation>
</file>
<file name="internal/model/request.go">
<violation number="1" location="internal/model/request.go:25">
Adding PerformanceMetrics with a BSON tag will persist raw CPU/heap profile bytes from Metrics to MongoDB, inflating storage and impacting performance. Consider excluding these fields from BSON encoding in Metrics or avoid persisting PerformanceMetrics entirely.</violation>
</file>
<file name="internal/profiling/sqlhook.go">
<violation number="1" location="internal/profiling/sqlhook.go:79">
Nil check missing before calling profiler; calling RecordSQLQuery on a nil *Profiler will panic.</violation>
<violation number="2" location="internal/profiling/sqlhook.go:187">
Using context.Background here prevents SQL metrics from being recorded; store and reuse the prepare context or pass a request-specific ctx.</violation>
</file>
<file name="internal/dashboard/ui/tsconfig.json">
<violation number="1" location="internal/dashboard/ui/tsconfig.json:17">
Path alias for React should point to "preact/compat", not "@preact/compat". Use the official Preact compat subpath to ensure correct type/module resolution.</violation>
<violation number="2" location="internal/dashboard/ui/tsconfig.json:18">
Path alias for react-dom should point to "preact/compat", not "@preact/compat". Align with Preact’s compat subpath for reliable resolution.</violation>
</file>
<file name="internal/dashboard/ui/src/components/RequestTrace.tsx">
<violation number="1" location="internal/dashboard/ui/src/components/RequestTrace.tsx:222">
Date parsing without validation can yield NaN when start_time/end_time are missing; this breaks sorting and position calculations in the timeline.</violation>
<violation number="2" location="internal/dashboard/ui/src/components/RequestTrace.tsx:237">
Sorting directly on potentially NaN startTime can result in undefined ordering; guard with fallbacks.</violation>
<violation number="3" location="internal/dashboard/ui/src/components/RequestTrace.tsx:247">
minTime derived from the first event can be NaN; compute the minimum across valid startTimes instead.</violation>
<violation number="4" location="internal/dashboard/ui/src/components/RequestTrace.tsx:248">
maxTime calculation can become NaN due to invalid times and truthiness; filter invalid values explicitly.</violation>
</file>
<file name="internal/dashboard/ui/src/components/EnvironmentInfo.tsx">
<violation number="1" location="internal/dashboard/ui/src/components/EnvironmentInfo.tsx:61">
Guard against division by zero when computing memoryPercentage to avoid NaN and invalid style/text rendering.</violation>
</file>
<file name="internal/dashboard/ui/src/lib/api.ts">
<violation number="1" location="internal/dashboard/ui/src/lib/api.ts:110">
URL parameters are not encoded; ids with special characters can break the request or inject extra parameters.</violation>
<violation number="2" location="internal/dashboard/ui/src/lib/api.ts:129">
Missing URL-encoding for requestId; special characters can break the metrics request.</violation>
<violation number="3" location="internal/dashboard/ui/src/lib/api.ts:135">
Missing URL-encoding for requestId; special characters can break the flame graph request.</violation>
</file>
<file name="cmd/examples/profiling/main.go">
<violation number="1" location="cmd/examples/profiling/main.go:321">
rows.Scan error is ignored; failures during scan will be silently dropped, risking incorrect results.</violation>
<violation number="2" location="cmd/examples/profiling/main.go:337">
rows.Scan error is ignored in products loop; failures during scan will be silently dropped.</violation>
<violation number="3" location="cmd/examples/profiling/main.go:345">
db.QueryRow().Scan error is ignored; on failure, order_count becomes misleading.</violation>
<violation number="4" location="cmd/examples/profiling/main.go:415">
db.QueryRow().Scan error is ignored; user_count may be incorrect on failure.</violation>
</file>
<file name="internal/dashboard/ui/src/components/RequestDrawer.tsx">
<violation number="1" location="internal/dashboard/ui/src/components/RequestDrawer.tsx:202">
Response size is calculated from string length (characters), not bytes; this misreports size for non-ASCII content.</violation>
</file>
<file name=".gitignore">
<violation number="1" location=".gitignore:40">
Example binary ignore is incomplete; tracing-example remains tracked. Use a wildcard to ignore all *-example binaries.</violation>
</file>
<file name="internal/profiling/profiler.go">
<violation number="1" location="internal/profiling/profiler.go:248">
Tracer retrieval uses a different context key than the one used to store it in middleware, so SQL tracing is never recorded.</violation>
</file>
<file name="cmd/examples/profiling/README.md">
<violation number="1" location="cmd/examples/profiling/README.md:235">
Retrieving the profiler via ctx.Value("profiler") is incorrect; the profiler is not stored in context, so this snippet will never succeed.</violation>
</file>
<file name="internal/dashboard/ui/src/components/ui/dialog.tsx">
<violation number="1" location="internal/dashboard/ui/src/components/ui/dialog.tsx:41">
DialogContent uses tailwindcss-animate utilities but tailwindcss-animate plugin is not configured; animations (fade/zoom/slide) will not work.</violation>
</file>
<file name="internal/dashboard/ui/src/components/ui/tooltip.tsx">
<violation number="1" location="internal/dashboard/ui/src/components/ui/tooltip.tsx:22">
Uses tailwindcss-animate-only utilities without configuring the plugin, so animations won't apply. Replace with classes defined in your Tailwind config (e.g., animate-fade-in) or add the plugin.</violation>
</file>
<file name="internal/dashboard/ui/src/components/ui/sheet.tsx">
<violation number="1" location="internal/dashboard/ui/src/components/ui/sheet.tsx:32">
Animate-in/out variants referenced here require tailwindcss-animate but the project lacks it; these classes will have no effect.</violation>
</file>
React with 👍 or 👎 to teach cubic. Mention @cubic-dev-ai to give feedback, ask questions, or re-run the review.
| <div id="app"> | ||
| <div class="loading">Loading GoVisual Dashboard...</div> | ||
| </div> | ||
| <script src="/dashboard.js"></script> |
There was a problem hiding this comment.
Absolute script path will bypass the dashboard mount and fail to load under non-root mounting; use a relative path.
Prompt for AI agents
Address the following comment on internal/dashboard/static/index.html at line 24:
<comment>Absolute script path will bypass the dashboard mount and fail to load under non-root mounting; use a relative path.</comment>
<file context>
@@ -0,0 +1,26 @@
+ <div id="app">
+ <div class="loading">Loading GoVisual Dashboard...</div>
+ </div>
+ <script src="/dashboard.js"></script>
+</body>
+</html>
</file context>
| <meta charset="UTF-8"> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
| <title>GoVisual Dashboard</title> | ||
| <link rel="stylesheet" href="/styles.css"> |
There was a problem hiding this comment.
Absolute asset path breaks when dashboard is mounted under a subpath; use a relative path so assets load under the dashboard prefix.
Prompt for AI agents
Address the following comment on internal/dashboard/static/index.html at line 7:
<comment>Absolute asset path breaks when dashboard is mounted under a subpath; use a relative path so assets load under the dashboard prefix.</comment>
<file context>
@@ -0,0 +1,26 @@
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>GoVisual Dashboard</title>
+ <link rel="stylesheet" href="/styles.css">
+ <style>
+ /* Critical CSS for initial load */
</file context>
| rt.currentPath = append(rt.currentPath, len(rt.Traces)-1) | ||
| } else { | ||
| // Nested trace | ||
| parent := rt.getParentTrace() |
There was a problem hiding this comment.
Nested StartTrace incorrectly uses the parent of the current trace, preventing proper child nesting and dropping first-level children under root.
Prompt for AI agents
Address the following comment on internal/middleware/tracer.go at line 65:
<comment>Nested StartTrace incorrectly uses the parent of the current trace, preventing proper child nesting and dropping first-level children under root.</comment>
<file context>
@@ -0,0 +1,285 @@
+ rt.currentPath = append(rt.currentPath, len(rt.Traces)-1)
+ } else {
+ // Nested trace
+ parent := rt.getParentTrace()
+ if parent != nil {
+ parent.Children = append(parent.Children, entry)
</file context>
|
|
||
| // Check for auth header | ||
| if auth := r.Header.Get("Authorization"); auth == "" { | ||
| tracer.RecordCustom("Auth Failed", map[string]interface{}{ |
There was a problem hiding this comment.
Possible nil pointer dereference: tracer.RecordCustom is called without checking tracer != nil.
Prompt for AI agents
Address the following comment on cmd/examples/tracing/main.go at line 51:
<comment>Possible nil pointer dereference: tracer.RecordCustom is called without checking tracer != nil.</comment>
<file context>
@@ -0,0 +1,276 @@
+
+ // Check for auth header
+ if auth := r.Header.Get("Authorization"); auth == "" {
+ tracer.RecordCustom("Auth Failed", map[string]interface{}{
+ "reason": "missing_header",
+ })
</file context>
| } | ||
|
|
||
| // profilingResponseWriter extends responseWriter with profiling capabilities | ||
| type profilingResponseWriter struct { |
There was a problem hiding this comment.
profilingResponseWriter drops optional http interfaces (Flusher/Hijacker/Pusher), potentially breaking streaming, SSE, and WebSockets.
Prompt for AI agents
Address the following comment on internal/middleware/profiling.go at line 163:
<comment>profilingResponseWriter drops optional http interfaces (Flusher/Hijacker/Pusher), potentially breaking streaming, SSE, and WebSockets.</comment>
<file context>
@@ -0,0 +1,246 @@
+}
+
+// profilingResponseWriter extends responseWriter with profiling capabilities
+type profilingResponseWriter struct {
+ responseWriter *responseWriter
+ profiler *profiling.Profiler
</file context>
|
|
||
| // 3. Database queries | ||
| var userCount int | ||
| db.QueryRow("SELECT COUNT(*) FROM users").Scan(&userCount) |
There was a problem hiding this comment.
db.QueryRow().Scan error is ignored; user_count may be incorrect on failure.
Prompt for AI agents
Address the following comment on cmd/examples/profiling/main.go at line 415:
<comment>db.QueryRow().Scan error is ignored; user_count may be incorrect on failure.</comment>
<file context>
@@ -0,0 +1,466 @@
+
+ // 3. Database queries
+ var userCount int
+ db.QueryRow("SELECT COUNT(*) FROM users").Scan(&userCount)
+ results["user_count"] = userCount
+
</file context>
| <DialogPrimitive.Content | ||
| ref={ref} | ||
| className={cn( | ||
| "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-slate-200 bg-white p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg dark:border-slate-800 dark:bg-slate-950", |
There was a problem hiding this comment.
DialogContent uses tailwindcss-animate utilities but tailwindcss-animate plugin is not configured; animations (fade/zoom/slide) will not work.
Prompt for AI agents
Address the following comment on internal/dashboard/ui/src/components/ui/dialog.tsx at line 41:
<comment>DialogContent uses tailwindcss-animate utilities but tailwindcss-animate plugin is not configured; animations (fade/zoom/slide) will not work.</comment>
<file context>
@@ -0,0 +1,122 @@
+ <DialogPrimitive.Content
+ ref={ref}
+ className={cn(
+ "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-slate-200 bg-white p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg dark:border-slate-800 dark:bg-slate-950",
+ className
+ )}
</file context>
| ref={ref} | ||
| sideOffset={sideOffset} | ||
| className={cn( | ||
| "z-50 overflow-hidden rounded-md border border-slate-200 bg-white px-3 py-1.5 text-sm text-slate-950 shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-tooltip-content-transform-origin] dark:border-slate-800 dark:bg-slate-950 dark:text-slate-50", |
There was a problem hiding this comment.
Uses tailwindcss-animate-only utilities without configuring the plugin, so animations won't apply. Replace with classes defined in your Tailwind config (e.g., animate-fade-in) or add the plugin.
Prompt for AI agents
Address the following comment on internal/dashboard/ui/src/components/ui/tooltip.tsx at line 22:
<comment>Uses tailwindcss-animate-only utilities without configuring the plugin, so animations won't apply. Replace with classes defined in your Tailwind config (e.g., animate-fade-in) or add the plugin.</comment>
<file context>
@@ -0,0 +1,30 @@
+ ref={ref}
+ sideOffset={sideOffset}
+ className={cn(
+ "z-50 overflow-hidden rounded-md border border-slate-200 bg-white px-3 py-1.5 text-sm text-slate-950 shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-tooltip-content-transform-origin] dark:border-slate-800 dark:bg-slate-950 dark:text-slate-50",
+ className
+ )}
</file context>
| SheetOverlay.displayName = SheetPrimitive.Overlay.displayName | ||
|
|
||
| const sheetVariants = cva( | ||
| "fixed z-50 gap-4 bg-white p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500 dark:bg-slate-950", |
There was a problem hiding this comment.
Animate-in/out variants referenced here require tailwindcss-animate but the project lacks it; these classes will have no effect.
Prompt for AI agents
Address the following comment on internal/dashboard/ui/src/components/ui/sheet.tsx at line 32:
<comment>Animate-in/out variants referenced here require tailwindcss-animate but the project lacks it; these classes will have no effect.</comment>
<file context>
@@ -0,0 +1,138 @@
+SheetOverlay.displayName = SheetPrimitive.Overlay.displayName
+
+const sheetVariants = cva(
+ "fixed z-50 gap-4 bg-white p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500 dark:bg-slate-950",
+ {
+ variants: {
</file context>
| <div>Value: ${d.value}</div> | ||
| `; | ||
| tooltipRef.current.style.display = "block"; | ||
| tooltipRef.current.style.left = event.pageX + 10 + "px"; |
There was a problem hiding this comment.
Use clientX/clientY for positioning when the tooltip uses position: fixed; pageX/pageY lead to incorrect offsets on scroll.
Prompt for AI agents
Address the following comment on internal/dashboard/ui/src/components/FlameGraph.tsx at line 79:
<comment>Use clientX/clientY for positioning when the tooltip uses position: fixed; pageX/pageY lead to incorrect offsets on scroll.</comment>
<file context>
@@ -0,0 +1,144 @@
+ <div>Value: ${d.value}</div>
+ `;
+ tooltipRef.current.style.display = "block";
+ tooltipRef.current.style.left = event.pageX + 10 + "px";
+ tooltipRef.current.style.top = event.pageY - 28 + "px";
+ }
</file context>
There was a problem hiding this comment.
40 issues found across 78 files
Prompt for AI agents (all 40 issues)
Understand the root cause of the following 40 issues and fix them.
<file name="internal/dashboard/static/index.html">
<violation number="1" location="internal/dashboard/static/index.html:7">
Absolute asset path breaks when dashboard is mounted under a subpath; use a relative path so assets load under the dashboard prefix.</violation>
<violation number="2" location="internal/dashboard/static/index.html:24">
Absolute script path will bypass the dashboard mount and fail to load under non-root mounting; use a relative path.</violation>
</file>
<file name="internal/middleware/tracer.go">
<violation number="1" location="internal/middleware/tracer.go:65">
Nested StartTrace incorrectly uses the parent of the current trace, preventing proper child nesting and dropping first-level children under root.</violation>
</file>
<file name="cmd/examples/tracing/main.go">
<violation number="1" location="cmd/examples/tracing/main.go:51">
Possible nil pointer dereference: tracer.RecordCustom is called without checking tracer != nil.</violation>
</file>
<file name="internal/middleware/profiling.go">
<violation number="1" location="internal/middleware/profiling.go:60">
Starting runtime CPU profiling per request is process-wide and unsafe under concurrent traffic; it may conflict and skew metrics.</violation>
<violation number="2" location="internal/middleware/profiling.go:67">
Unbounded io.ReadAll on request body with ignored error can cause OOM and misses read failures; limit and handle errors.</violation>
<violation number="3" location="internal/middleware/profiling.go:69">
Sensitive data risk: storing full request body without redaction or size limits.</violation>
<violation number="4" location="internal/middleware/profiling.go:163">
profilingResponseWriter drops optional http interfaces (Flusher/Hijacker/Pusher), potentially breaking streaming, SSE, and WebSockets.</violation>
</file>
<file name="internal/dashboard/ui/src/components/FlameGraph.tsx">
<violation number="1" location="internal/dashboard/ui/src/components/FlameGraph.tsx:73">
Avoid setting innerHTML with untrusted data; this allows XSS. Construct the tooltip with textContent or sanitized HTML.</violation>
<violation number="2" location="internal/dashboard/ui/src/components/FlameGraph.tsx:79">
Use clientX/clientY for positioning when the tooltip uses position: fixed; pageX/pageY lead to incorrect offsets on scroll.</violation>
</file>
<file name="internal/dashboard/ui/src/App.tsx">
<violation number="1" location="internal/dashboard/ui/src/App.tsx:58">
useEffect uses filters in its callback but has an empty dependency array, causing stale filter application on live updates.</violation>
</file>
<file name="internal/profiling/flamegraph.go">
<violation number="1" location="internal/profiling/flamegraph.go:161">
Hotspot detection never recurses from root because node.Value == 0 triggers an early return, yielding empty results.</violation>
</file>
<file name="internal/dashboard/handler.go">
<violation number="1" location="internal/dashboard/handler.go:32">
Error from fs.Sub is ignored; staticFS may be nil and later Open will panic. Handle the error or provide a safe fallback.</violation>
</file>
<file name="internal/dashboard/ui/src/components/RequestComparison.tsx">
<violation number="1" location="internal/dashboard/ui/src/components/RequestComparison.tsx:97">
Objects (including JSX elements) are JSON-stringified in cells, causing incorrect rendering (e.g., Status row). Render JSX elements as-is when detected.</violation>
</file>
<file name="internal/dashboard/ui/postcss.config.js">
<violation number="1" location="internal/dashboard/ui/postcss.config.js:4">
PostCSS config references autoprefixer, but the package is not installed, likely causing the Tailwind build to fail. Install autoprefixer (devDependency) or remove the plugin from the config.</violation>
</file>
<file name="internal/dashboard/ui/src/components/ExportImport.tsx">
<violation number="1" location="internal/dashboard/ui/src/components/ExportImport.tsx:65">
CSV export does not escape internal quotes or sanitize leading formula characters, risking malformed CSV and potential CSV injection in spreadsheet apps.</violation>
</file>
<file name="internal/model/request.go">
<violation number="1" location="internal/model/request.go:25">
Adding PerformanceMetrics with a BSON tag will persist raw CPU/heap profile bytes from Metrics to MongoDB, inflating storage and impacting performance. Consider excluding these fields from BSON encoding in Metrics or avoid persisting PerformanceMetrics entirely.</violation>
</file>
<file name="internal/profiling/sqlhook.go">
<violation number="1" location="internal/profiling/sqlhook.go:79">
Nil check missing before calling profiler; calling RecordSQLQuery on a nil *Profiler will panic.</violation>
<violation number="2" location="internal/profiling/sqlhook.go:187">
Using context.Background here prevents SQL metrics from being recorded; store and reuse the prepare context or pass a request-specific ctx.</violation>
</file>
<file name="internal/dashboard/ui/tsconfig.json">
<violation number="1" location="internal/dashboard/ui/tsconfig.json:17">
Path alias for React should point to "preact/compat", not "@preact/compat". Use the official Preact compat subpath to ensure correct type/module resolution.</violation>
<violation number="2" location="internal/dashboard/ui/tsconfig.json:18">
Path alias for react-dom should point to "preact/compat", not "@preact/compat". Align with Preact’s compat subpath for reliable resolution.</violation>
</file>
<file name="internal/dashboard/ui/src/components/RequestTrace.tsx">
<violation number="1" location="internal/dashboard/ui/src/components/RequestTrace.tsx:222">
Date parsing without validation can yield NaN when start_time/end_time are missing; this breaks sorting and position calculations in the timeline.</violation>
<violation number="2" location="internal/dashboard/ui/src/components/RequestTrace.tsx:237">
Sorting directly on potentially NaN startTime can result in undefined ordering; guard with fallbacks.</violation>
<violation number="3" location="internal/dashboard/ui/src/components/RequestTrace.tsx:247">
minTime derived from the first event can be NaN; compute the minimum across valid startTimes instead.</violation>
<violation number="4" location="internal/dashboard/ui/src/components/RequestTrace.tsx:248">
maxTime calculation can become NaN due to invalid times and truthiness; filter invalid values explicitly.</violation>
</file>
<file name="internal/dashboard/ui/src/components/EnvironmentInfo.tsx">
<violation number="1" location="internal/dashboard/ui/src/components/EnvironmentInfo.tsx:61">
Guard against division by zero when computing memoryPercentage to avoid NaN and invalid style/text rendering.</violation>
</file>
<file name="internal/dashboard/ui/src/lib/api.ts">
<violation number="1" location="internal/dashboard/ui/src/lib/api.ts:110">
URL parameters are not encoded; ids with special characters can break the request or inject extra parameters.</violation>
<violation number="2" location="internal/dashboard/ui/src/lib/api.ts:129">
Missing URL-encoding for requestId; special characters can break the metrics request.</violation>
<violation number="3" location="internal/dashboard/ui/src/lib/api.ts:135">
Missing URL-encoding for requestId; special characters can break the flame graph request.</violation>
</file>
<file name="cmd/examples/profiling/main.go">
<violation number="1" location="cmd/examples/profiling/main.go:321">
rows.Scan error is ignored; failures during scan will be silently dropped, risking incorrect results.</violation>
<violation number="2" location="cmd/examples/profiling/main.go:337">
rows.Scan error is ignored in products loop; failures during scan will be silently dropped.</violation>
<violation number="3" location="cmd/examples/profiling/main.go:345">
db.QueryRow().Scan error is ignored; on failure, order_count becomes misleading.</violation>
<violation number="4" location="cmd/examples/profiling/main.go:415">
db.QueryRow().Scan error is ignored; user_count may be incorrect on failure.</violation>
</file>
<file name="internal/dashboard/ui/src/components/RequestDrawer.tsx">
<violation number="1" location="internal/dashboard/ui/src/components/RequestDrawer.tsx:202">
Response size is calculated from string length (characters), not bytes; this misreports size for non-ASCII content.</violation>
</file>
<file name=".gitignore">
<violation number="1" location=".gitignore:40">
Example binary ignore is incomplete; tracing-example remains tracked. Use a wildcard to ignore all *-example binaries.</violation>
</file>
<file name="internal/profiling/profiler.go">
<violation number="1" location="internal/profiling/profiler.go:248">
Tracer retrieval uses a different context key than the one used to store it in middleware, so SQL tracing is never recorded.</violation>
</file>
<file name="cmd/examples/profiling/README.md">
<violation number="1" location="cmd/examples/profiling/README.md:235">
Retrieving the profiler via ctx.Value("profiler") is incorrect; the profiler is not stored in context, so this snippet will never succeed.</violation>
</file>
<file name="internal/dashboard/ui/src/components/ui/dialog.tsx">
<violation number="1" location="internal/dashboard/ui/src/components/ui/dialog.tsx:41">
DialogContent uses tailwindcss-animate utilities but tailwindcss-animate plugin is not configured; animations (fade/zoom/slide) will not work.</violation>
</file>
<file name="internal/dashboard/ui/src/components/ui/tooltip.tsx">
<violation number="1" location="internal/dashboard/ui/src/components/ui/tooltip.tsx:22">
Uses tailwindcss-animate-only utilities without configuring the plugin, so animations won't apply. Replace with classes defined in your Tailwind config (e.g., animate-fade-in) or add the plugin.</violation>
</file>
<file name="internal/dashboard/ui/src/components/ui/sheet.tsx">
<violation number="1" location="internal/dashboard/ui/src/components/ui/sheet.tsx:32">
Animate-in/out variants referenced here require tailwindcss-animate but the project lacks it; these classes will have no effect.</violation>
</file>
React with 👍 or 👎 to teach cubic. Mention @cubic-dev-ai to give feedback, ask questions, or re-run the review.
| <div id="app"> | ||
| <div class="loading">Loading GoVisual Dashboard...</div> | ||
| </div> | ||
| <script src="/dashboard.js"></script> |
There was a problem hiding this comment.
Absolute script path will bypass the dashboard mount and fail to load under non-root mounting; use a relative path.
Prompt for AI agents
Address the following comment on internal/dashboard/static/index.html at line 24:
<comment>Absolute script path will bypass the dashboard mount and fail to load under non-root mounting; use a relative path.</comment>
<file context>
@@ -0,0 +1,26 @@
+ <div id="app">
+ <div class="loading">Loading GoVisual Dashboard...</div>
+ </div>
+ <script src="/dashboard.js"></script>
+</body>
+</html>
</file context>
| <meta charset="UTF-8"> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
| <title>GoVisual Dashboard</title> | ||
| <link rel="stylesheet" href="/styles.css"> |
There was a problem hiding this comment.
Absolute asset path breaks when dashboard is mounted under a subpath; use a relative path so assets load under the dashboard prefix.
Prompt for AI agents
Address the following comment on internal/dashboard/static/index.html at line 7:
<comment>Absolute asset path breaks when dashboard is mounted under a subpath; use a relative path so assets load under the dashboard prefix.</comment>
<file context>
@@ -0,0 +1,26 @@
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>GoVisual Dashboard</title>
+ <link rel="stylesheet" href="/styles.css">
+ <style>
+ /* Critical CSS for initial load */
</file context>
| rt.currentPath = append(rt.currentPath, len(rt.Traces)-1) | ||
| } else { | ||
| // Nested trace | ||
| parent := rt.getParentTrace() |
There was a problem hiding this comment.
Nested StartTrace incorrectly uses the parent of the current trace, preventing proper child nesting and dropping first-level children under root.
Prompt for AI agents
Address the following comment on internal/middleware/tracer.go at line 65:
<comment>Nested StartTrace incorrectly uses the parent of the current trace, preventing proper child nesting and dropping first-level children under root.</comment>
<file context>
@@ -0,0 +1,285 @@
+ rt.currentPath = append(rt.currentPath, len(rt.Traces)-1)
+ } else {
+ // Nested trace
+ parent := rt.getParentTrace()
+ if parent != nil {
+ parent.Children = append(parent.Children, entry)
</file context>
|
|
||
| // Check for auth header | ||
| if auth := r.Header.Get("Authorization"); auth == "" { | ||
| tracer.RecordCustom("Auth Failed", map[string]interface{}{ |
There was a problem hiding this comment.
Possible nil pointer dereference: tracer.RecordCustom is called without checking tracer != nil.
Prompt for AI agents
Address the following comment on cmd/examples/tracing/main.go at line 51:
<comment>Possible nil pointer dereference: tracer.RecordCustom is called without checking tracer != nil.</comment>
<file context>
@@ -0,0 +1,276 @@
+
+ // Check for auth header
+ if auth := r.Header.Get("Authorization"); auth == "" {
+ tracer.RecordCustom("Auth Failed", map[string]interface{}{
+ "reason": "missing_header",
+ })
</file context>
| } | ||
|
|
||
| // profilingResponseWriter extends responseWriter with profiling capabilities | ||
| type profilingResponseWriter struct { |
There was a problem hiding this comment.
profilingResponseWriter drops optional http interfaces (Flusher/Hijacker/Pusher), potentially breaking streaming, SSE, and WebSockets.
Prompt for AI agents
Address the following comment on internal/middleware/profiling.go at line 163:
<comment>profilingResponseWriter drops optional http interfaces (Flusher/Hijacker/Pusher), potentially breaking streaming, SSE, and WebSockets.</comment>
<file context>
@@ -0,0 +1,246 @@
+}
+
+// profilingResponseWriter extends responseWriter with profiling capabilities
+type profilingResponseWriter struct {
+ responseWriter *responseWriter
+ profiler *profiling.Profiler
</file context>
|
|
||
| // 3. Database queries | ||
| var userCount int | ||
| db.QueryRow("SELECT COUNT(*) FROM users").Scan(&userCount) |
There was a problem hiding this comment.
db.QueryRow().Scan error is ignored; user_count may be incorrect on failure.
Prompt for AI agents
Address the following comment on cmd/examples/profiling/main.go at line 415:
<comment>db.QueryRow().Scan error is ignored; user_count may be incorrect on failure.</comment>
<file context>
@@ -0,0 +1,466 @@
+
+ // 3. Database queries
+ var userCount int
+ db.QueryRow("SELECT COUNT(*) FROM users").Scan(&userCount)
+ results["user_count"] = userCount
+
</file context>
| <DialogPrimitive.Content | ||
| ref={ref} | ||
| className={cn( | ||
| "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-slate-200 bg-white p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg dark:border-slate-800 dark:bg-slate-950", |
There was a problem hiding this comment.
DialogContent uses tailwindcss-animate utilities but tailwindcss-animate plugin is not configured; animations (fade/zoom/slide) will not work.
Prompt for AI agents
Address the following comment on internal/dashboard/ui/src/components/ui/dialog.tsx at line 41:
<comment>DialogContent uses tailwindcss-animate utilities but tailwindcss-animate plugin is not configured; animations (fade/zoom/slide) will not work.</comment>
<file context>
@@ -0,0 +1,122 @@
+ <DialogPrimitive.Content
+ ref={ref}
+ className={cn(
+ "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-slate-200 bg-white p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg dark:border-slate-800 dark:bg-slate-950",
+ className
+ )}
</file context>
| ref={ref} | ||
| sideOffset={sideOffset} | ||
| className={cn( | ||
| "z-50 overflow-hidden rounded-md border border-slate-200 bg-white px-3 py-1.5 text-sm text-slate-950 shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-tooltip-content-transform-origin] dark:border-slate-800 dark:bg-slate-950 dark:text-slate-50", |
There was a problem hiding this comment.
Uses tailwindcss-animate-only utilities without configuring the plugin, so animations won't apply. Replace with classes defined in your Tailwind config (e.g., animate-fade-in) or add the plugin.
Prompt for AI agents
Address the following comment on internal/dashboard/ui/src/components/ui/tooltip.tsx at line 22:
<comment>Uses tailwindcss-animate-only utilities without configuring the plugin, so animations won't apply. Replace with classes defined in your Tailwind config (e.g., animate-fade-in) or add the plugin.</comment>
<file context>
@@ -0,0 +1,30 @@
+ ref={ref}
+ sideOffset={sideOffset}
+ className={cn(
+ "z-50 overflow-hidden rounded-md border border-slate-200 bg-white px-3 py-1.5 text-sm text-slate-950 shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-tooltip-content-transform-origin] dark:border-slate-800 dark:bg-slate-950 dark:text-slate-50",
+ className
+ )}
</file context>
| SheetOverlay.displayName = SheetPrimitive.Overlay.displayName | ||
|
|
||
| const sheetVariants = cva( | ||
| "fixed z-50 gap-4 bg-white p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500 dark:bg-slate-950", |
There was a problem hiding this comment.
Animate-in/out variants referenced here require tailwindcss-animate but the project lacks it; these classes will have no effect.
Prompt for AI agents
Address the following comment on internal/dashboard/ui/src/components/ui/sheet.tsx at line 32:
<comment>Animate-in/out variants referenced here require tailwindcss-animate but the project lacks it; these classes will have no effect.</comment>
<file context>
@@ -0,0 +1,138 @@
+SheetOverlay.displayName = SheetPrimitive.Overlay.displayName
+
+const sheetVariants = cva(
+ "fixed z-50 gap-4 bg-white p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500 dark:bg-slate-950",
+ {
+ variants: {
</file context>
| <div>Value: ${d.value}</div> | ||
| `; | ||
| tooltipRef.current.style.display = "block"; | ||
| tooltipRef.current.style.left = event.pageX + 10 + "px"; |
There was a problem hiding this comment.
Use clientX/clientY for positioning when the tooltip uses position: fixed; pageX/pageY lead to incorrect offsets on scroll.
Prompt for AI agents
Address the following comment on internal/dashboard/ui/src/components/FlameGraph.tsx at line 79:
<comment>Use clientX/clientY for positioning when the tooltip uses position: fixed; pageX/pageY lead to incorrect offsets on scroll.</comment>
<file context>
@@ -0,0 +1,144 @@
+ <div>Value: ${d.value}</div>
+ `;
+ tooltipRef.current.style.display = "block";
+ tooltipRef.current.style.left = event.pageX + 10 + "px";
+ tooltipRef.current.style.top = event.pageY - 28 + "px";
+ }
</file context>
Overview
This PR modernizes GoVisual's dashboard with a complete rewrite to React/Preact and adds comprehensive performance profiling capabilities.
Key Changes
Dashboard Modernization
Performance Profiling Features
Tracing Integration
New Examples
Technical Details
Breaking Changes
Testing
All existing functionality preserved with enhanced visualization capabilities. New profiling features include comprehensive example applications for validation.
Summary by cubic
Rebuilt the dashboard as a fast React/Preact SPA with embedded assets, and added end-to-end performance profiling (CPU/memory, flame graphs, SQL/HTTP tracking) and tracing to help diagnose bottlenecks quickly.
New Features
Migration