Skip to content

Latest commit

 

History

History
347 lines (280 loc) · 19.1 KB

CODEBASE_AUDIT.md

File metadata and controls

347 lines (280 loc) · 19.1 KB

DualMind UI — Codebase Audit

Generated: 2026-08-17 by automated analysis + manual review. Scope: Core DualMind_UI application (/home/ubuntu/DualMind_UI), excluding node_modules, old-html-backup, next-dualmind, admin-email-system.

This is a living document. Update it as issues are fixed.

1. Executive Summary

  • Type: Vanilla-JS single-page application with Cloudflare Worker proxy + Supabase client-side auth.
  • Size: ~22k logical non-comment code lines across 126 source files (excluding prototypes).
  • Build: Custom Node copy scripts; no bundler.
  • Test/CI: Broken out of the box (npm run lint glob fails, Playwright not installed).
  • Critical Runtime Bugs: login/auth-complete.js references undefined variables.
  • Security: Open CORS on worker, localStorage tokens, innerHTML usage in 64 places.
  • Architecture Risk: Multiple overlapping prototypes in the same repo; globals-heavy dependency graph.

2. Tooling Health

Check Result
npm install OK (218 packages)
npm audit 0 vulnerabilities after npm audit fix
npm run lint original FAIL — unquoted shell glob admin-email-system/**
npm run lint (quoted glob) 28 warnings, 0 errors
npm run test FAIL — @playwright/test not installed
npm run validate FAIL — depends on lint + test

3. Size & Language Breakdown

(From pygount, excluding prototypes)

Language Files Code Lines
CSS 18 8,485
JavaScript 39 7,059
HTML 17 3,353
Other 52 3,175
Total 126 ~22,072

4. Repository Structure Concerns

Orphaned/Prototype Code

File/Directory Risk
App.jsx React prototype; not imported by index.html
next-dualmind/ Entire Next.js prototype
old-html-backup/ Dead archive
arena-core.js Older arena class; not imported in prod
js/api-client.js Deprecated shim
js/auth.js, js/auth/api-service.js Legacy auth wrappers
build-deploy.js + wrangler-dist.toml Redundant build path

Duplicate UI Pages

  • login-modern.html vs login/index.html vs signup-modern.html / verify.html / auth-verify.html
  • Multiple auth entry points increase maintenance and fragment analytics.

5. Critical Bugs (Line-by-Line)

5.1 login/auth-complete.js — Runtime Crash

Undefined symbols (from ESLint):

  • emailInput lines 60, 63
  • setLoading lines 68, 79, 108, 123, 133, 158
  • supabaseClient lines 69, 109, 136
  • SITE_URL line 70

These will throw ReferenceError and break email/phone auth flows immediately.

5.2 config.js — process.env in Browser

Lines 119-120 attempt to read process.env, which does not exist in browsers. Fallbacks include a real Supabase project URL and partial anon key hardcoded in the repo. This makes local/back-end env substitution impossible.

5.3 worker.js — Open CORS

Lines 7-11 set Access-Control-Allow-Origin: * for every /api/* response. Any website can invoke your backend through a user's browser.

5.4 worker.js — Headers Leak + No Timeout

  • Line 40 forwards request.headers unfiltered to the backend.
  • fetch() to backend has no timeout or retry logic.
  • Backend errors (error.toString()) are returned to the client around line 61.

6. Code Quality Metrics

Most Complex Files

js/ui/app.js (2112 lines, 52 methods)

  • Classes: App
  • Key methods: constructor, waitForAuth, init, checkBackendAvailability, showBackendUnavailableBanner, hideBackendUnavailableBanner, fetchModels, prettifyModelName, getModelIdByName, setup, showOfflineIndicator, attachGlobalListeners, _openExportDropdown, handleNavigation, handleChatSubmit, renderChat, cancelStreams, runArenaDemo, runArenaApi, runDirectDemo, runDirectApi, showFloatingVoting, hideFloatingVoting, handleVoteSubmit, updateTurnVisibilityAfterVote, cleanModelName, resetTtsButton, stopTextToSpeech, handleTextToSpeech, syncUserWithBackend

components/chat/ChatView.js (1075 lines, 26 methods)

  • Classes: ChatView
  • Key methods: constructor, setupMarkdown, renderMarkdown, setState, appendTurn, clear, render, renderEmptyArena, renderArena, renderTurn, renderResponseCard, ensureResponseModal, openResponseModal, closeResponseModal, renderToggleButton, renderVoteBar, renderDirect, attach, attachModelSelectorListeners, updateDirectResponse, finishDirectResponse, updateResponse, finishResponse, attachScrollListener, attachResponseBodyScrollListeners, scrollToBottom

js/auth-modern.js (906 lines, 0 methods)

  • Key methods:

js/auth/supabase-auth.js (698 lines, 27 methods)

  • Classes: SupabaseAuthService
  • Key methods: constructor, init, signup, signupWithPhone, login, logout, resetPassword, signupWithEmailOtp, loginWithEmailOtp, verifyEmailOtp, sendSmsOtp, verifySmsOtp, updatePhone, updateProfile, changePassword, getAccessToken, isAuthenticated, getUser, getUserEmail, getUserName, getUserInitials, getUserId, getSession, _saveSession, _clearSession, _parseError, signInWithOAuth

components/Sidebar.js (692 lines, 29 methods)

  • Classes: Sidebar
  • Key methods: constructor, init, scheduleLoadThreads, loadThreads, render, renderRecentChats, escapeHtml, getUserInitials, getUserName, getUserEmail, updateRecentChats, attachThreadClickHandlers, attachEventListeners, attachActionHandlers, handleRenameThread, handleDeleteThread, handleResize, updateClasses, toggle, updateSidebarState, open, close, lockScroll, unlockScroll, enableFocusTrap, disableFocusTrap, handleNavClick, addRecentChat, getState

js/auth/api-service.js (611 lines, 16 methods)

  • Classes: DualMindAPIService
  • Key methods: constructor, chatNonStreaming, chatStreaming, dualChat, extractResponse, getModels, getMe, submitVote, getLeaderboard, getThreads, getThreadMessages, createThread, fetchThread, updateThreadVisibility, getFeatureFlag, healthCheck

js/feature-flags.js (427 lines, 0 methods)

  • Key methods:

components/SharedThreadView.js (368 lines, 16 methods)

  • Classes: SharedThreadView
  • Key methods: constructor, init, loadThread, getBaseUrl, render, renderLoading, renderError, renderHeader, _modeLabel, renderMeta, renderMessages, renderTurn, renderFooter, prettifyModelName, renderMarkdown, attach

js/api/core/HttpClient.js (356 lines, 14 methods)

  • Classes: HttpClient
  • Key methods: constructor, buildUrl, buildHeaders, log, getRetryDelay, isRetryable, safeJsonParse, request, get, post, put, patch, delete, sleep

js/auth-examples.js (355 lines, 6 methods)

  • Classes: ChatComponent
  • Key methods: constructor, init, render, setupEventListeners, sendMessage, loadMessages

components/ShareModal.js (337 lines, 12 methods)

  • Classes: ShareModal
  • Key methods: constructor, init, render, renderShareIcon, renderCopyIcon, getVisibilityHint, attach, open, close, updateVisibility, copyLink, rerender

components/ChatInput.js (323 lines, 21 methods)

  • Classes: ChatInput
  • Key methods: constructor, init, render, escapeHtml, renderLoader, renderAttachments, attachEventListeners, submit, clear, setLoading, handleAdd, handleImageUpload, processFiles, addAttachment, removeAttachment, updateAttachments, handleWebSearch, handleCodeMode, focus, setValue, autoResize

js/chatExport.js (306 lines, 10 methods)

  • Classes: ChatExport
  • Key methods: constructor, getFilename, _buildMetadata, toMarkdown, toJSON, toCSV, _csvEscape, toHTML, toPDF, download

js/resend-auth-email.js (305 lines, 0 methods)

  • Key methods:

worker.js (298 lines, 1 methods)

  • Key methods: fetch

innerHTML Usage

64 .innerHTML assignments were found across the code. High-risk locations:

  • arena-core.js:79 — this.arenaGrid.innerHTML = cards;
  • arena-core.js:103 — `body.innerHTML = ``
  • arena-core.js:111 — meta.innerHTML = '<i class="ri-check-line"></i>';
  • arena-core.js:116 — meta.innerHTML = '<i class="ri-loader-4-line"></i>';
  • arena-core.js:154 — badge.innerHTML = '<i class="ri-award-line"></i> Winner';
  • arena-core.js:201 — `this.arenaGrid.innerHTML = ``
  • arena-core.js:277 — this.arenaGrid.innerHTML = '';
  • login/auth-complete.js:118 — submitBtn.innerHTML = '<span>Verify Code</span><i class="ri-check-line"></i>';
  • login/auth-complete.js:192 — submitBtn.innerHTML = '<span>Verify Code</span><i class="ri-check-line"></i>';
  • login/auth-complete.js:194 — submitBtn.innerHTML = '<span>Send Code</span><i class="ri-smartphone-line"></i>';
  • login/auth-complete.js:197 — submitBtn.innerHTML = isLogin
  • components/ChatInput.js:26 — `this.container.innerHTML = ``
  • components/ChatInput.js:269 — preview.innerHTML = this.renderAttachments();
  • components/ShareModal.js:25 — overlay.innerHTML = this.render();
  • components/ShareModal.js:233 — overlay.innerHTML = this.render();
  • components/ShareModal.js:330 — overlay.innerHTML = this.render();
  • components/SharedThreadView.js:106 — this.container.innerHTML = this.renderLoading();
  • components/SharedThreadView.js:111 — this.container.innerHTML = this.renderError();
  • components/SharedThreadView.js:115 — `this.container.innerHTML = ``
  • components/Header.js:33 — `this.container.innerHTML = ``
  • components/Header.js:192 — if (modeIcon) modeIcon.innerHTML = modeData.icon('white');
  • components/Sidebar.js:78 — `this.container.innerHTML = ``
  • components/Sidebar.js:240 — listContainer.innerHTML = this.renderRecentChats();
  • components/CustomModal.js:22 — `root.innerHTML = ``
  • components/CustomModal.js:175 — `toast.innerHTML = ``
  • components/CustomModal.js:204 — if (contentEl) contentEl.innerHTML = content;
  • components/chat/ChatView.js:117 — temp.innerHTML = turnHtml;
  • components/chat/ChatView.js:152 — `this.container.innerHTML = ``
  • components/chat/ChatView.js:475 — `modal.innerHTML = ``
  • components/chat/ChatView.js:554 — `body.innerHTML = ``
  • components/chat/ChatView.js:563 — body.innerHTML = this.renderMarkdown(data.text || '') + (data.streaming ? '<span class="st
  • components/chat/ChatView.js:871 — el.innerHTML = this.renderMarkdown(fullText) + (streaming ? '<span class="stream-caret" ar
  • components/chat/ChatView.js:898 — el.innerHTML = this.renderMarkdown(fullText) + (streaming ? '<span class="stream-caret" ar
  • components/chat/ChatView.js:909 — modalBody.innerHTML = this.renderMarkdown(fullText) + (streaming ? '<span class="stream-ca
  • js/chatExport.js:260 — frame.innerHTML = this.toHTML();
  • js/update-password.js:29 — submitBtn.innerHTML = isLoading
  • js/auth-examples.js:251 — `this.container.innerHTML = ``
  • js/auth-examples.js:266 — `this.container.innerHTML = ``
  • js/auth-examples.js:325 — `messagesDiv.innerHTML = messages.map(msg => ``
  • js/auth-modern.js:233 — `confirmWrapper.innerHTML = ``
  • js/auth-modern.js:249 — `wrapper.innerHTML = ``
  • js/auth-modern.js:440 — if (btnIcon) btnIcon.innerHTML = '<div class="spinner"></div>';
  • js/auth-modern.js:445 — if (btnIcon) btnIcon.innerHTML = '<i class="ri-arrow-right-line"></i>';
  • js/auth-modern.js:542 — button.innerHTML = '<div class="spinner-small"></div> Connecting...';
  • js/auth-modern.js:552 — button.innerHTML = originalContent;
  • js/forgot-password.js:28 — submitBtn.innerHTML = isLoading
  • js/leaderboardPage.js:12 — `root.innerHTML = ``
  • js/leaderboardPage.js:50 — `root.innerHTML = ``
  • js/leaderboardPage.js:90 — `root.innerHTML = ``
  • js/ui/leaderboardModal.js:58 — `root.innerHTML = ``
  • js/ui/leaderboardModal.js:127 — `this._els.content.innerHTML = ``
  • js/ui/leaderboardModal.js:139 — `this._els.content.innerHTML = ``
  • js/ui/leaderboardModal.js:187 — `this._els.content.innerHTML = ``
  • js/ui/app.js:382 — `indicator.innerHTML = ``
  • js/ui/app.js:918 — if (leftEl) leftEl.innerHTML = '';
  • js/ui/app.js:919 — if (rightEl) rightEl.innerHTML = '';
  • js/ui/app.js:1114 — `container.innerHTML = ``
  • js/ui/app.js:1144 — container.innerHTML = '';
  • js/ui/app.js:1345 — buttonElement.innerHTML = originalContent;
  • js/ui/app.js:1410 — buttonElement.innerHTML = '⏳';
  • js/ui/app.js:1423 — buttonElement.innerHTML = '⏹';
  • js/ui/app.js:1458 — buttonElement.innerHTML = '⏹';
  • js/ui/app.js:1857 — iconEl.innerHTML = '<svg class="spinner" width="16" height="16" viewBox="0 0 24 24" fill="
  • js/ui/app.js:1876 — iconEl.innerHTML = originalIcon;

console.* Calls

255 console usages found. Many are intentional debug guards, but any remaining in production cause noise and logging cost.

  • build-deploy.js:39 — console.log(Skipping ${src} (not found));
  • build-deploy.js:55 — console.log(Copied: ${src});
  • build-deploy.js:59 — console.log('Building deployment package...\n');
  • build-deploy.js:67 — console.log('\n✅ Build complete! Files copied to dist/');
  • build-deploy.js:68 — console.log('Run: wrangler deploy --config wrangler-dist.toml');
  • build.js:83 — console.log('Frontend built to /dist successfully!');
  • performance-monitor.js:40 — console.log('Page Load Metrics:', this.metrics.pageLoad);
  • performance-monitor.js:72 — console.warn(Slow API call: ${url} took ${duration.toFixed(0)}ms);
  • performance-monitor.js:109 — console.warn('Performance Observer not supported:', e);
  • performance-monitor.js:223 — suggestions.forEach(s => console.log('•', s));
  • mock-api-server.js:62 — console.log('🔐 Login attempt:', req.body);
  • mock-api-server.js:93 — console.log('✅ Login successful for:', user.email);
  • mock-api-server.js:108 — console.log('📝 Signup attempt:', req.body);
  • mock-api-server.js:149 — console.log('✅ Signup successful for:', newUser.email);
  • mock-api-server.js:165 — console.log('📊 Leaderboard requested by:', req.user.email);
  • mock-api-server.js:175 — console.log('🗳️ Vote submitted by:', req.user.email, 'Data:', req.body);
  • mock-api-server.js:185 — console.log('💬 Chat request by:', req.user.email, 'Data:', req.body);
  • mock-api-server.js:248 — console.log(🚀 DualMind Mock API Server running on http://localhost:${PORT}`);`
  • mock-api-server.js:249 — console.log('📋 Available endpoints:');
  • mock-api-server.js:250 — console.log(' POST /api/auth/login');
  • mock-api-server.js:251 — console.log(' POST /api/auth/signup');
  • mock-api-server.js:252 — console.log(' GET /api/health');
  • mock-api-server.js:253 — console.log(' GET /api/arena/model-stats (requires auth)');
  • mock-api-server.js:254 — console.log(' POST /api/arena/vote (requires auth)');
  • mock-api-server.js:255 — console.log(' POST /api/arena/dualchat (requires auth)');
  • mock-api-server.js:256 — console.log('\n🔑 Test credentials:');
  • mock-api-server.js:257 — console.log(' Email: test@example.com');
  • mock-api-server.js:258 — console.log(' Password: password123');
  • mock-api-server.js:259 — console.log(' Email: admin@dualmind.com');
  • mock-api-server.js:260 — console.log(' Password: admin123');

... and 225 more.

Window Globals Assignments

37 window.* writes found. Key globals:

  • config.js:1 — window.DUALMIND_CONFIG
  • arena-core.js:291 — window.ArenaMode
  • performance-monitor.js:49 — window.fetch
  • performance-monitor.js:213 — window.performanceMonitor
  • login/js/supabase-init.js:2 — window.supabase
  • login/js/supabase-init.js:11 — window.supabaseClient
  • login/js/app-final.js:11 — if (typeof window.supabaseClient
  • login/js/app-final.js:167 — window.dualMindAuth
  • js/auth.js:72 — window.DUALMIND_AUTH_TOKEN
  • js/auth.js:188 — window.getSupabaseAccessToken
  • js/api-client.js:255 — window.APIClient
  • js/api-client.js:256 — window.createAPIClient
  • js/api-client.js:257 — window.getAPIClient
  • js/feature-flags.js:395 — window._DUALMIND_TESTER_ENERGY_BALANCE
  • js/apiInstance.js:27 — window.DualMindApiInstance
  • js/auth/api-service.js:610 — window.DualMindAPIService
  • js/auth/supabase-init.js:8 — window.DualMindAuthReady
  • js/auth/supabase-init.js:9 — window._resolveDualMindAuthReady
  • js/auth/supabase-init.js:18 — window._SUPABASE_AUTH_INITIALIZED
  • js/auth/supabase-init.js:49 — window._DUALMIND_AUTH
  • js/auth/supabase-init.js:63 — window.getAuth
  • js/auth/supabase-init.js:128 — window.DualMindAuth
  • js/auth/supabase-init.js:265 — window.auth
  • js/auth/supabase-auth.js:694 — window.SupabaseAuthService
  • js/auth/supabase-auth.js:695 — window.initializeSupabaseAuth
  • js/auth/supabase-auth.js:696 — window.getSupabaseAuthService
  • js/api/utils/authProvider.js:37 — if (typeof window.getSupabaseAccessToken
  • js/ui/app.js:68 — window._DUALMIND_API
  • js/ui/app.js:292 — window._DUALMIND_MODELS
  • js/ui/app.js:332 — window._APP
  • js/ui/app.js:373 — window._DUALMIND_APP_READY
  • js/ui/app.js:2108 — window.DualMindArena

7. Security Checklist

Item Status Notes
Input sanitization Partial DOMPurify used for AI markdown; thread titles escaped; but many innerHTML sites exist
Auth token storage Risky Supabase localStorage session; XSS exposure
CORS Risky Access-Control-Allow-Origin: * in worker
CSP header Missing No Content-Security-Policy
Secrets in repo Risky Supabase URL in config.js
API proxy filtering Missing Worker forwards headers unfiltered
Rate limiting Missing No worker or UI rate limiting

8. Performance Checklist

Item Status Notes
CSS bundling Not done 12 stylesheets loaded synchronously
JS bundling Not done ES modules + CDN scripts + inline scripts
Cache control Wrong Worker returns no-store for all statics from worker.js
Image optimization Manual assets/ PNG files not optimized/processed
Lazy loading Partial chatExport.js is lazy-loaded
DOM churn Risky ChatView full rerenders and appends

9. Testing & CI Status

  • No unit tests.
  • Playwright dependency missing.
  • GitHub Actions / CI not present.
  • commit_and_push.sh and commit_and_push.ps1 are manual wrappers that bypass review.

10. Startup Readiness Roadmap

Immediate (P0)

  1. Fix login/auth-complete.js undefined variables.
  2. Quote lint glob in package.json.
  3. Install Playwright or remove test scripts.
  4. Restrict worker.js CORS to allowed origins.

Short-term (P1)

  1. Remove orphan prototypes (App.jsx, next-dualmind/, old-html-backup/, etc.).
  2. Delete deprecated shims (js/api-client.js, js/auth.js, js/auth/api-service.js).
  3. Move Supabase URL/key to build-time env, remove from config.js.
  4. Add CSP + security headers in worker.
  5. Unify build/deploy scripts (build.js + wrangler.jsonc).

Medium-term (P2)

  1. Replace 12 CSS files with one built bundle.
  2. Add Jest/Vitest unit tests for API client and authProvider.
  3. Add GitHub Actions CI for lint, audit, test, deploy.
  4. Migrate auth tokens from localStorage to httpOnly cookies (requires backend support).
  5. Add feature flags behind remote config.

End of audit.