ATlast — you'll never need to find your favorites on another platform again. Find your favs in the ATmosphere.
atproto

fix: add health check function for extension server detection

- Created /health function endpoint with CORS support
- Updated checkServerHealth to use function endpoint instead of root URL
- Fixes Firefox extension server detection with proper CORS headers

byarielm.fyi 15b67054 bb14ec5f

verified
Changed files
+94 -193
docs
packages
extension
src
functions
src
-165
EXTENSION_STATUS.md
··· 1 - # Extension Implementation Status 2 - 3 - ## Current State: ✅ COMPLETE - Ready for Production Testing 4 - 5 - ### What's Complete ✅ 6 - 7 - 1. **Environment Configuration** 8 - - Dev/prod builds with correct API URLs 9 - - Build: `npm run build` (dev) or `npm run build:prod` 10 - - Dev: `http://127.0.0.1:8888` 11 - - Prod: `https://atlast.byarielm.fyi` 12 - 13 - 2. **Server Health Check** 14 - - Extension checks if dev server is running (dev mode only) 15 - - Shows "Server offline" state with instructions 16 - - "Check Again" button to retry 17 - 18 - 3. **Authentication Flow** 19 - - Extension checks `/session` endpoint on init 20 - - Shows "Not logged in" state if no session 21 - - "Open ATlast" button to log in 22 - - "Check Again" to retry after login 23 - - **User must be logged in to ATlast BEFORE using extension** 24 - 25 - 4. **Upload Flow** (matches file upload) 26 - - Scan Twitter Following page 27 - - POST to `/extension-import` (requires auth) 28 - - Backend: 29 - - Gets DID from session 30 - - Creates `user_upload` entry 31 - - Saves to `source_accounts` table 32 - - Returns `uploadId` 33 - - Opens `/results?uploadId={id}` 34 - - Frontend searches and displays (same as file upload) 35 - 36 - 5. **CORS Permissions** 37 - - Extension has host_permissions for: 38 - - `http://127.0.0.1:8888/*` 39 - - `http://localhost:8888/*` 40 - - `https://atlast.byarielm.fyi/*` 41 - 42 - 6. **Cleanup Complete** 43 - - ❌ Removed `extension_imports` table 44 - - ❌ Removed `get-extension-import` function 45 - - ❌ Removed `ExtensionImport.tsx` page 46 - - ❌ Removed `/import/:id` route 47 - - ❌ Removed `utils/import-store.ts` 48 - 49 - ### What Needs Testing 🧪 50 - 51 - 1. **Full Flow Test** 52 - ```bash 53 - # 1. Start dev server 54 - npx netlify-cli dev --filter @atlast/web 55 - 56 - # 2. Build extension 57 - cd packages/extension 58 - npm run build 59 - 60 - # 3. Load extension in Chrome 61 - chrome://extensions/ → Load unpacked → packages/extension/dist/chrome/ 62 - 63 - # 4. Log in to ATlast 64 - Open http://127.0.0.1:8888 → Log in 65 - 66 - # 5. Go to Twitter 67 - https://twitter.com/justadev_atlast/following 68 - 69 - # 6. Open extension popup 70 - - Should show "Ready to scan Twitter/X" 71 - - Click "Start Scan" 72 - - Wait for completion 73 - - Click "Open in ATlast" 74 - - Should open /results?uploadId={id} 75 - - Results should load and search automatically 76 - ``` 77 - 78 - 2. **Error Cases to Test** 79 - - Not logged in → should show login prompt 80 - - Server offline → should show offline state 81 - - Empty results → should show appropriate message 82 - - Network errors → should handle gracefully 83 - 84 - ### Recently Completed (Dec 2024 - Jan 2025) 🎉 85 - 86 - **Extension Flow Fixes:** 87 - - ✅ NaN database error - Fixed missing `matchedUsers` parameter in `extension-import.ts` (node #287) 88 - - ✅ Database initialized successfully (node #288) 89 - - ✅ API response unwrapping - Fixed api-client to access ApiResponse.data field (nodes #290-295) 90 - - ✅ Loading screen during extension upload search (node #325) 91 - - ✅ Timezone fixes - All timestamp columns use TIMESTAMPTZ (node #326) 92 - - ✅ Vite dev server optimization - Pre-bundling dependencies for faster startup (node #327) 93 - 94 - **Decision Graph Documentation:** 95 - - ✅ Fixed 18 orphan nodes and linked to parent goals (nodes #328-331) 96 - - ✅ Improved decision graph workflow with lifecycle management (node #332) 97 - - ✅ Updated CLAUDE.md with node status transitions and common mistakes 98 - 99 - ### Current Status 📊 100 - 101 - **All extension bugs resolved!** The extension is fully functional and ready for production testing and deployment. 102 - 103 - ### Next Steps 📋 104 - 105 - 1. ✅ Build extension: `cd packages/extension && pnpm run build` 106 - 2. ✅ Reload extension in Chrome 107 - 3. ✅ Test login flow 108 - 4. ✅ Test scan and upload 109 - 5. ✅ Verify results page works 110 - 6. ✅ All bugs fixed 111 - 7. 🔜 Test production build: `pnpm run build:prod` 112 - 8. 🔜 Chrome Web Store submission 113 - 9. 🔜 Firefox Add-ons support and submission 114 - 115 - ### Architecture Notes 📝 116 - 117 - **Removed temporary import storage approach:** 118 - - Previously tried in-memory storage (doesn't work in serverless) 119 - - Then tried database storage with temp table (overkill) 120 - 121 - **Current approach:** 122 - - User logs in to ATlast FIRST 123 - - Extension requires authentication 124 - - Upload creates permanent records immediately 125 - - No temporary storage needed 126 - - Matches file upload behavior exactly 127 - 128 - **Why this is better:** 129 - - Simpler architecture 130 - - No temporary storage to expire 131 - - Proper user association from the start 132 - - Reuses existing upload/search infrastructure 133 - - Same flow as file uploads (consistency) 134 - 135 - ### Files Modified in Latest Refactor 136 - 137 - **Deleted:** 138 - - `packages/functions/src/get-extension-import.ts` 139 - - `packages/functions/src/utils/import-store.ts` 140 - - `packages/web/src/pages/ExtensionImport.tsx` 141 - 142 - **Modified:** 143 - - `packages/functions/src/extension-import.ts` - Now requires auth, creates upload 144 - - `packages/functions/src/infrastructure/database/DatabaseService.ts` - Removed extension_imports table 145 - - `packages/functions/src/core/types/database.types.ts` - Removed ExtensionImportRow 146 - - `packages/web/src/Router.tsx` - Removed /import/:id route 147 - - `packages/extension/src/popup/popup.ts` - Added session check, login state 148 - - `packages/extension/src/popup/popup.html` - Added not-logged-in state 149 - - `packages/extension/src/lib/api-client.ts` - Added checkSession(), credentials: 'include' 150 - 151 - ### Decision Graph Summary 152 - 153 - **Total nodes:** 332 nodes, 333 edges 154 - **Key decisions tracked:** 155 - - Environment configuration approach (#261-269) 156 - - Port 8888 conflict resolution (#270-274) 157 - - CORS permissions fix (#275-277) 158 - - Storage approach: in-memory → database → proper auth flow (#278-284) 159 - - Refactor and build (#285-286) 160 - - Bug fixes: NaN parameter error (#287), database initialization (#288) 161 - - API response unwrapping fix (#290-295) 162 - - Extension upload flow fixes (#296-327) 163 - - Decision graph integrity fixes (#328-332) 164 - 165 - **Live graph:** https://notactuallytreyanastasio.github.io/deciduous/
+16 -16
docs/git-history.json
··· 1 1 [ 2 2 { 3 - "hash": "603cf0a187850664336a12c9e5cbb49038906f53", 4 - "short_hash": "603cf0a", 3 + "hash": "be9f4207b61429400e44ccfda326595c1087235c", 4 + "short_hash": "be9f420", 5 5 "author": "Ariel M. Lighty", 6 - "date": "2025-12-27T22:42:43-05:00", 7 - "message": "fix: CORS for extension credentialed requests\n\nUpdated CORS headers to support credentials from Chrome extensions:\n- Added getCorsHeaders() to detect chrome-extension:// origins\n- Changed from wildcard Access-Control-Allow-Origin to specific origin\n- Added Access-Control-Allow-Credentials: true for credentialed requests\n- Updated session endpoint to pass event for CORS header detection", 8 - "files_changed": 4 6 + "date": "2025-12-28T19:21:29-05:00", 7 + "message": "feat: add Firefox support with webextension-polyfill\n\nImplemented cross-browser compatibility for the extension:\n\n- Installed webextension-polyfill for unified browser.* API\n- Replaced all chrome.* API calls with browser.* imports\n- Updated build system to output both chrome/ and firefox/ directories\n- Created Firefox-specific manifest with browser_specific_settings", 8 + "files_changed": 12 9 9 }, 10 10 { 11 11 "hash": "603cf0a187850664336a12c9e5cbb49038906f53", ··· 32 32 "files_changed": 9 33 33 }, 34 34 { 35 - "hash": "fe29bb3e5faa0151f63c14724f7509af669860de", 36 - "short_hash": "fe29bb3", 35 + "hash": "6ac877ee6d1990bdab4ef3b03ac59f4682afe0a5", 36 + "short_hash": "6ac877e", 37 37 "author": "Ariel M. Lighty", 38 - "date": "2025-12-27T16:02:10-05:00", 39 - "message": "docs: update all .md files to reflect current project status\n\nUpdated 4 markdown files with current state:\n\nEXTENSION_STATUS.md:\n- Changed status from DEBUGGING to COMPLETE\n- Updated decision graph count (295 → 332 nodes)\n- Added recently completed section (nodes 296-332)\n- Marked all extension bugs as resolved\n\nCONTRIBUTING.md:\n- Replaced npm with pnpm throughout\n- Added monorepo structure documentation\n- Updated development commands (netlify-cli dev --filter)\n- Added extension development workflow\n\nPLAN.md:\n- Updated status to Phase 1 COMPLETE\n- Added all recent fixes to completion list\n- Updated decision graph count to 332 nodes\n- Added changelog entries for latest work\n\npackages/extension/README.md:\n- Added prerequisites section (dev server + login required)\n- Updated build commands with dev/prod distinction\n- Added Step 0: Start ATlast Dev Server\n- Added common issues for auth and server states\n\nAll files now accurately reflect completion status and use pnpm.", 40 - "files_changed": 6 38 + "date": "2025-12-27T16:03:56-05:00", 39 + "message": "docs: update decision graph after markdown updates", 40 + "files_changed": 2 41 41 }, 42 42 { 43 43 "hash": "fe29bb3e5faa0151f63c14724f7509af669860de", ··· 160 160 "files_changed": 3 161 161 }, 162 162 { 163 - "hash": "32cdee3aeac7ef986df47e0fff786b5f7471e55b", 164 - "short_hash": "32cdee3", 163 + "hash": "ba29fd68872913ba0a587aa7f29f97b3d373a732", 164 + "short_hash": "ba29fd6", 165 165 "author": "Ariel M. Lighty", 166 166 "date": "2025-12-25T13:22:32-05:00", 167 167 "message": "configure Netlify dev for monorepo with --filter flag\n\nFixed Netlify CLI monorepo detection issue by using --filter flag:\n- Updated root package.json scripts to use 'npx netlify-cli dev --filter @atlast/web'\n- Updated netlify.toml [dev] section to use npm with --prefix for framework command\n- Added monorepo development instructions to CLAUDE.md\n- Documented Windows Git Bash compatibility issue with netlify command\n\nSolution: Use 'npx netlify-cli dev --filter @atlast/web' to bypass monorepo\nproject selection prompt and specify which workspace package to run.\n\nDev server now runs successfully at http://localhost:8888 with all backend\nfunctions loaded.", 168 - "files_changed": 4 168 + "files_changed": 5 169 169 }, 170 170 { 171 - "hash": "ba29fd68872913ba0a587aa7f29f97b3d373a732", 172 - "short_hash": "ba29fd6", 171 + "hash": "32cdee3aeac7ef986df47e0fff786b5f7471e55b", 172 + "short_hash": "32cdee3", 173 173 "author": "Ariel M. Lighty", 174 174 "date": "2025-12-25T13:22:32-05:00", 175 175 "message": "configure Netlify dev for monorepo with --filter flag\n\nFixed Netlify CLI monorepo detection issue by using --filter flag:\n- Updated root package.json scripts to use 'npx netlify-cli dev --filter @atlast/web'\n- Updated netlify.toml [dev] section to use npm with --prefix for framework command\n- Added monorepo development instructions to CLAUDE.md\n- Documented Windows Git Bash compatibility issue with netlify command\n\nSolution: Use 'npx netlify-cli dev --filter @atlast/web' to bypass monorepo\nproject selection prompt and specify which workspace package to run.\n\nDev server now runs successfully at http://localhost:8888 with all backend\nfunctions loaded.", 176 - "files_changed": 5 176 + "files_changed": 4 177 177 }, 178 178 { 179 179 "hash": "c3e7afad396d130791d801a85cbfc9643bcd6309",
+50 -6
docs/graph-data.json
··· 4142 4142 "node_type": "goal", 4143 4143 "title": "Fix Firefox extension server detection and login check", 4144 4144 "description": null, 4145 - "status": "pending", 4145 + "status": "completed", 4146 4146 "created_at": "2025-12-28T20:14:51.646204800-05:00", 4147 - "updated_at": "2025-12-28T20:14:51.646204800-05:00", 4147 + "updated_at": "2025-12-28T20:32:19.249555-05:00", 4148 4148 "metadata_json": "{\"branch\":\"master\",\"confidence\":85,\"prompt\":\"The extension works in chrome. In firefox, it's failing to detect that the dev server is running and open + logged in on firefox. There's no right-click to inspect on the popup either.\"}" 4149 4149 }, 4150 4150 { ··· 4241 4241 "node_type": "action", 4242 4242 "title": "Adding moz-extension:// origin detection to CORS handler for Firefox extension support", 4243 4243 "description": null, 4244 - "status": "pending", 4244 + "status": "completed", 4245 4245 "created_at": "2025-12-28T20:28:31.661326900-05:00", 4246 - "updated_at": "2025-12-28T20:28:31.661326900-05:00", 4246 + "updated_at": "2025-12-28T20:32:19.367968600-05:00", 4247 4247 "metadata_json": "{\"branch\":\"master\",\"confidence\":95}" 4248 4248 }, 4249 4249 { ··· 4252 4252 "node_type": "outcome", 4253 4253 "title": "Fixed Firefox extension CORS by adding moz-extension:// origin detection to response.utils.ts. Reverted netlify.toml changes as functions handle CORS correctly. User needs to restart dev server.", 4254 4254 "description": null, 4255 - "status": "pending", 4255 + "status": "completed", 4256 4256 "created_at": "2025-12-28T20:29:39.856303800-05:00", 4257 - "updated_at": "2025-12-28T20:29:39.856303800-05:00", 4257 + "updated_at": "2025-12-28T20:32:19.494690-05:00", 4258 + "metadata_json": "{\"branch\":\"master\",\"confidence\":95}" 4259 + }, 4260 + { 4261 + "id": 388, 4262 + "change_id": "0ada864e-be98-4a2f-a14e-ffd3eea9aaa9", 4263 + "node_type": "observation", 4264 + "title": "Health check uses HEAD request to root URL (Vite server), not a Netlify function. Doesn't get CORS headers from getCorsHeaders. Need dedicated health endpoint or change check to use existing function.", 4265 + "description": null, 4266 + "status": "pending", 4267 + "created_at": "2025-12-28T20:37:22.132717600-05:00", 4268 + "updated_at": "2025-12-28T20:37:22.132717600-05:00", 4269 + "metadata_json": "{\"branch\":\"master\",\"confidence\":95}" 4270 + }, 4271 + { 4272 + "id": 389, 4273 + "change_id": "f522d5b2-c325-4f34-9f27-b8ea5c50618d", 4274 + "node_type": "outcome", 4275 + "title": "Created /health function endpoint with CORS support. Updated checkServerHealth to use /.netlify/functions/health instead of root URL. Extension rebuilt successfully.", 4276 + "description": null, 4277 + "status": "pending", 4278 + "created_at": "2025-12-28T20:38:19.981309500-05:00", 4279 + "updated_at": "2025-12-28T20:38:19.981309500-05:00", 4258 4280 "metadata_json": "{\"branch\":\"master\",\"confidence\":95}" 4259 4281 } 4260 4282 ], ··· 8471 8493 "weight": 1.0, 8472 8494 "rationale": "Complete fix implemented", 8473 8495 "created_at": "2025-12-28T20:30:09.745415200-05:00" 8496 + }, 8497 + { 8498 + "id": 384, 8499 + "from_node_id": 387, 8500 + "to_node_id": 388, 8501 + "from_change_id": "cffdee0f-8535-4d88-83ed-fdf6101f7ac3", 8502 + "to_change_id": "0ada864e-be98-4a2f-a14e-ffd3eea9aaa9", 8503 + "edge_type": "leads_to", 8504 + "weight": 1.0, 8505 + "rationale": "New issue discovered in health check", 8506 + "created_at": "2025-12-28T20:37:24.355885500-05:00" 8507 + }, 8508 + { 8509 + "id": 385, 8510 + "from_node_id": 388, 8511 + "to_node_id": 389, 8512 + "from_change_id": "0ada864e-be98-4a2f-a14e-ffd3eea9aaa9", 8513 + "to_change_id": "f522d5b2-c325-4f34-9f27-b8ea5c50618d", 8514 + "edge_type": "leads_to", 8515 + "weight": 1.0, 8516 + "rationale": "Fix implemented", 8517 + "created_at": "2025-12-28T20:38:22.044029100-05:00" 8474 8518 } 8475 8519 ] 8476 8520 }
+7 -6
packages/extension/src/lib/api-client.ts
··· 78 78 */ 79 79 export async function checkServerHealth(): Promise<boolean> { 80 80 try { 81 - // Try to fetch the root URL with a short timeout 81 + // Try to fetch the health endpoint with a short timeout 82 82 const controller = new AbortController(); 83 83 const timeoutId = setTimeout(() => controller.abort(), 3000); 84 84 85 - const response = await fetch(ATLAST_API_URL, { 86 - method: 'HEAD', 87 - signal: controller.signal 85 + const response = await fetch(`${ATLAST_API_URL}/.netlify/functions/health`, { 86 + method: 'GET', 87 + signal: controller.signal, 88 + credentials: 'include', // Include for CORS 88 89 }); 89 90 90 91 clearTimeout(timeoutId); 91 92 92 - // Any response (even 404) means server is running 93 - return true; 93 + // Any successful response means server is running 94 + return response.ok; 94 95 } catch (error) { 95 96 console.error('[API Client] Server health check failed:', error); 96 97 return false;
+21
packages/functions/src/health.ts
··· 1 + import { SimpleHandler } from "./core/types/api.types"; 2 + import { successResponse } from "./utils"; 3 + import { withErrorHandling } from "./core/middleware"; 4 + 5 + /** 6 + * Health check endpoint 7 + * Returns 200 OK with server status 8 + */ 9 + const healthHandler: SimpleHandler = async (event) => { 10 + return successResponse( 11 + { 12 + status: "ok", 13 + timestamp: new Date().toISOString(), 14 + }, 15 + 200, 16 + {}, 17 + event 18 + ); 19 + }; 20 + 21 + export const handler = withErrorHandling(healthHandler);