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

trying claude code so,

byarielm.fyi aca6bc26 794e5fd8

verified
Changed files
+234 -1
+1 -1
.gitignore
··· 8 8 public-jwk.json 9 9 test-data/ 10 10 11 - # Deciduous database (local) 11 + .claude/ 12 12 .deciduous/
+233
CLAUDE.md
··· 1 + # CLAUDE.md 2 + 3 + This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. 4 + 1 5 # Project Instructions 2 6 3 7 ## Decision Graph Workflow ··· 195 199 deciduous trace link <session_id> <node_id> # Manual linking 196 200 deciduous trace prune --days 30 # Cleanup old traces 197 201 ``` 202 + 203 + ## Project Overview 204 + 205 + ATlast is a web application that helps users find their followed accounts from other social platforms (TikTok, Instagram, Twitter/X) on Bluesky/AT Protocol. Users upload their data export files, which are parsed for usernames, then searched in the AT Protocol network. 206 + 207 + ## Development Commands 208 + 209 + ### Local Development 210 + ```bash 211 + # Mock mode (frontend only, no backend/OAuth/database) 212 + npm run dev:mock 213 + 214 + # Full mode (with backend, OAuth, database) 215 + npm run dev:full # or npm run dev 216 + 217 + # Build for production 218 + npm run build 219 + 220 + # Initialize local database 221 + npm run init-db 222 + 223 + # Generate encryption keys for OAuth 224 + npm run generate-key 225 + ``` 226 + 227 + ### Environment Configuration 228 + 229 + Two development modes supported: 230 + 231 + **Mock Mode** (.env.mock): 232 + - Set `VITE_LOCAL_MOCK=true` 233 + - No database or OAuth required 234 + - Uses MockApiAdapter for fake data 235 + 236 + **Full Mode** (.env): 237 + - Set `VITE_LOCAL_MOCK=false` 238 + - Requires PostgreSQL (local or Neon) 239 + - Requires OAuth keys and configuration 240 + - Must use `http://127.0.0.1:8888` (NOT localhost) for OAuth to work 241 + - See CONTRIBUTING.md for detailed setup 242 + 243 + ## Architecture Overview 244 + 245 + ### Frontend (React + TypeScript + Vite) 246 + 247 + **Core Structure:** 248 + - `src/pages/` - Page components (Login, Upload, Results, etc.) 249 + - `src/components/` - Reusable UI components 250 + - `src/lib/parsers/` - File parsing logic for different platforms 251 + - `src/lib/api/` - API client with adapter pattern (Real vs Mock) 252 + - `src/contexts/` - React contexts (SettingsContext for theme/preferences) 253 + - `src/hooks/` - Custom React hooks 254 + 255 + **API Client Pattern:** 256 + The app uses an adapter pattern for the API layer: 257 + - `src/lib/api/client.ts` - Factory that returns Real or Mock adapter based on ENV.IS_LOCAL_MOCK 258 + - `src/lib/api/adapters/RealApiAdapter.ts` - Calls Netlify Functions 259 + - `src/lib/api/adapters/MockApiAdapter.ts` - Returns fake data for frontend development 260 + - `src/lib/api/IApiClient.ts` - Interface both adapters implement 261 + 262 + **Platform Parsers:** 263 + - `src/lib/parsers/platformDefinitions.ts` - Defines parsing rules for each platform (Instagram, TikTok, etc.) 264 + - `src/lib/parsers/fileExtractor.ts` - Handles ZIP file uploads and extracts usernames 265 + - `src/lib/parsers/parserLogic.ts` - Implements extraction logic (HTML regex, JSON path traversal, TEXT regex) 266 + 267 + Each platform has multiple ParseRule entries defining: 268 + - `zipPath` - Location inside ZIP archive 269 + - `format` - "HTML" | "TEXT" | "JSON" 270 + - `rule` - Regex pattern string or JSON key path array 271 + 272 + ### Backend (Netlify Functions) 273 + 274 + **Function Structure:** 275 + ``` 276 + netlify/functions/ 277 + ├── core/ 278 + │ ├── middleware/ # Auth, error handling, session security 279 + │ ├── types/ # Shared types 280 + │ ├── errors/ # Custom error classes 281 + │ └── config/ # Configuration 282 + ├── infrastructure/ 283 + │ ├── oauth/ # OAuth client factory, stores 284 + │ ├── database/ # Database connection, service layer 285 + │ ├── cache/ # Caching utilities 286 + │ └── lexicons/ # AT Protocol lexicons 287 + ├── services/ # Business logic (SessionService, FollowService, etc.) 288 + ├── repositories/ # Data access layer 289 + └── utils/ # Shared utilities 290 + ``` 291 + 292 + **Key Functions:** 293 + - `oauth-start.ts` / `oauth-callback.ts` - AT Protocol OAuth flow 294 + - `session.ts` - Session management and validation 295 + - `batch-search-actors.ts` - Searches multiple usernames on Bluesky (includes ranking algorithm) 296 + - `check-follow-status.ts` - Checks if user follows specific DIDs 297 + - `batch-follow-users.ts` - Bulk follow operations 298 + - `save-results.ts` - Persists search results to database 299 + - `get-uploads.ts` - Retrieves user's upload history 300 + 301 + **Authentication Pattern:** 302 + All protected endpoints use: 303 + 1. `withAuthErrorHandling()` middleware wrapper 304 + 2. `AuthenticatedHandler` type (provides `context.sessionId`, `context.did`, `context.event`) 305 + 3. `SessionService.getAgentForSession()` to get authenticated AT Protocol agent 306 + 307 + **Database:** 308 + - PostgreSQL via Neon serverless 309 + - Accessed through `DatabaseService` (infrastructure/database/) 310 + - Repositories pattern for data access (repositories/) 311 + 312 + **OAuth:** 313 + - AT Protocol OAuth using `@atproto/oauth-client-node` 314 + - OAuth client factory creates client with session/state stores 315 + - Private key (ES256) stored in `OAUTH_PRIVATE_KEY` env var 316 + - Public JWK served at `/jwks` endpoint 317 + - Must use `127.0.0.1` (not localhost) for local OAuth redirects 318 + 319 + ### UI/Styling 320 + 321 + **Tailwind CSS with dual theme support:** 322 + - Light mode: purple/cyan color scheme 323 + - Dark mode: cyan/purple inverted 324 + - All components use `dark:` variant classes 325 + 326 + **Color System (from CONTRIBUTING.md):** 327 + - Text: purple-950/cyan-50 (primary), purple-750/cyan-250 (secondary) 328 + - Borders: cyan-500/purple-500 with opacity variants 329 + - Buttons: orange-600 (primary CTA), slate-600/700 (secondary) 330 + - Backgrounds: white/slate-900 (primary), purple-50/slate-900 (secondary) 331 + - Selected states: cyan-50 border-cyan-500 / purple-950/30 border-purple-500 332 + - Accents: orange-500/amber-500 (badges, progress) 333 + 334 + **Key Patterns:** 335 + - Mobile-first responsive design 336 + - List virtualization with `@tanstack/react-virtual` for large result sets 337 + - Code splitting and lazy loading for pages 338 + - Error boundaries throughout the app 339 + - Loading skeletons for async operations 340 + 341 + ### Search & Matching Algorithm 342 + 343 + **Username Matching (batch-search-actors.ts):** 344 + The search uses a scoring system (0-100): 345 + 1. Exact handle match (before first dot): 100 346 + 2. Exact full handle match: 90 347 + 3. Exact display name match: 80 348 + 4. Partial handle match (contains): 60 349 + 5. Partial full handle match: 50 350 + 6. Partial display name match: 40 351 + 7. Reverse partial match: 30 352 + 353 + All comparisons use normalized strings (lowercase, no special chars). 354 + Returns top 5 ranked results per username. 355 + 356 + **Result Enrichment:** 357 + - Fetches profiles in batches of 25 for post/follower counts 358 + - Checks follow status using custom lexicons (default: `app.bsky.graph.follow`) 359 + - Attaches follow status to each actor result 360 + 361 + ## Key Technical Details 362 + 363 + ### AT Protocol Integration 364 + - Uses `@atproto/api` for Bluesky API interactions 365 + - Uses `@atcute/identity-resolver` for DID resolution 366 + - Supports custom lexicons for experimental features 367 + - OAuth flow follows AT Protocol OAuth spec 368 + 369 + ### File Upload Flow 370 + 1. User uploads ZIP file (Instagram/TikTok data export) 371 + 2. `fileExtractor.ts` reads ZIP in browser (using JSZip) 372 + 3. Matches file paths to platform rules from `platformDefinitions.ts` 373 + 4. `parserLogic.ts` extracts usernames (regex for HTML/TEXT, path traversal for JSON) 374 + 5. Deduplicates and returns username list 375 + 6. Frontend calls `/batch-search-actors` with username batches (max 50) 376 + 7. Results stored in database via `/save-results` 377 + 378 + ### Session Management 379 + - Session IDs stored in httpOnly cookies 380 + - Sessions linked to encrypted OAuth state stores 381 + - Session validation middleware checks database and expiry 382 + - Sessions tied to specific DIDs (user accounts) 383 + 384 + ### Deployment 385 + - Hosted on Netlify 386 + - Static frontend built with Vite 387 + - Serverless functions for backend 388 + - Database on Neon (PostgreSQL) 389 + - OAuth redirects configured in netlify.toml 390 + 391 + ## Adding New Features 392 + 393 + ### Adding a New Social Platform 394 + 1. Add parsing rules to `src/lib/parsers/platformDefinitions.ts`: 395 + ```typescript 396 + export const PLATFORM_RULES: Record<string, ParseRule[]> = { 397 + newplatform: [ 398 + { 399 + zipPath: "path/in/zip/file.json", 400 + format: "JSON", 401 + rule: ["key", "path", "to", "username"], 402 + }, 403 + ], 404 + }; 405 + ``` 406 + 2. Test with real data export from that platform 407 + 3. Update UI in platform selection components 408 + 409 + ### Adding a New API Endpoint 410 + 1. Create function in `netlify/functions/your-endpoint.ts` 411 + 2. Use `withAuthErrorHandling()` for protected endpoints 412 + 3. Implement `AuthenticatedHandler` type 413 + 4. Add method to `IApiClient.ts` interface 414 + 5. Implement in both `RealApiAdapter.ts` and `MockApiAdapter.ts` 415 + 6. Use via `apiClient.yourMethod()` in components 416 + 417 + ### Adding Database Models 418 + 1. Create migration script in `scripts/` 419 + 2. Run against local database 420 + 3. Update repository in `netlify/functions/repositories/` 421 + 4. Update DatabaseService if needed 422 + 423 + ## Important Notes 424 + 425 + - **OAuth Localhost Issue**: Must use `127.0.0.1:8888` not `localhost:8888` for local OAuth to work 426 + - **Batch Limits**: Search endpoint limited to 50 usernames per request 427 + - **Profile Fetching**: Batched in groups of 25 to avoid rate limits 428 + - **Normalization**: All username comparisons use lowercase + special char removal 429 + - **Security**: CSP headers configured in netlify.toml, session security middleware prevents CSRF 430 + - **Error Handling**: Custom error classes (ValidationError, AuthenticationError, etc.) with proper HTTP status codes