+1
-1
.gitignore
+1
-1
.gitignore
+233
CLAUDE.md
+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