Files
Job-Info/SVELTE_BUILD_PLAN.md
T
aewing 42ff3e8f33
Build & Deploy / Test & Lint (push) Has been cancelled
Build & Deploy / Security Scan (push) Has been cancelled
Code Quality / Code Quality Checks (push) Has been cancelled
Code Quality / Dependency Vulnerabilities (push) Has been cancelled
Code Quality / Performance Testing (push) Has been cancelled
Build & Deploy / Build Docker Images (push) Has been cancelled
Build & Deploy / Deploy to Kubernetes (push) Has been cancelled
Add Mode6Test: copy of Mode5Test running on port 3000
2026-01-20 13:43:29 +00:00

520 lines
14 KiB
Markdown

# Svelte Project Initialization & Build Plan
## Overview
This is the complete plan for building a fresh Svelte + SvelteKit application with:
- Direct Microsoft OAuth2 authentication (no PocketBase auth)
- On-device storage (IndexedDB) for jobs and files
- Service Worker for background token refresh
- Comprehensive error handling and logging
**Timeline:** ~3-4 days of focused development
**Complexity:** Medium-High (OAuth integration requires precision)
**Risk:** Low (following proven patterns, well-tested OAuth flow)
---
## PHASE 1: Project Setup (Day 1, ~2 hours)
### Step 1.1: Create SvelteKit Project
```bash
npm create svelte@latest frontend
cd frontend
npm install
```
**Configuration needed:**
- TypeScript: YES
- Vitest: YES
- Playwright: NO (for now)
- Tailwind: YES
- Prettier: YES
### Step 1.2: Install Dependencies
```bash
npm install --save-dev \
@sveltejs/adapter-node \
typescript \
tailwindcss postcss autoprefixer
npm install \
svelte-spa-router \
idb \
crypto-js
```
### Step 1.3: Configure Environment Variables
Create `.env.local`:
```
VITE_PUBLIC_MICROSOFT_CLIENT_ID=your-client-id
VITE_PUBLIC_MICROSOFT_TENANT_ID=common
VITE_PUBLIC_MICROSOFT_REDIRECT_URI=http://localhost:5173/auth/callback
```
**NOTE:** Client ID is PUBLIC (exposed to frontend). Client SECRET stays on backend only.
### Step 1.4: Create Directory Structure
```
frontend/src/
├── routes/
│ ├── +layout.svelte # Root layout
│ ├── +page.svelte # Home (requires auth)
│ ├── signin/
│ │ └── +page.svelte # OAuth login page
│ ├── auth/
│ │ └── callback/
│ │ └── +page.svelte # OAuth callback handler
│ └── [folder]/
│ └── +page.svelte # Folder view
├── components/
│ ├── JobCard.svelte
│ ├── JobList.svelte
│ ├── SearchBar.svelte
│ ├── Navigation.svelte
│ ├── ErrorBoundary.svelte
│ └── FileViewer.svelte
├── stores/
│ ├── auth.ts # Auth state management
│ ├── jobs.ts # Jobs state management
│ └── ui.ts # UI state (modals, notifications)
├── services/
│ ├── oauth.ts # OAuth2 flow logic
│ ├── graph.ts # Microsoft Graph API calls
│ ├── storage.ts # IndexedDB operations
│ └── errorHandler.ts # Centralized error handling
├── utils/
│ ├── constants.ts
│ ├── types.ts
│ └── helpers.ts
├── lib/
│ └── db.ts # IndexedDB schema & operations
├── app.svelte # Root component
├── app.css # Global styles
└── service-worker.ts # Background token refresh
```
### Step 1.5: Create TypeScript Configuration
Update `tsconfig.json`:
```json
{
"compilerOptions": {
"strict": true,
"skipLibCheck": true,
"esModuleInterop": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true
}
}
```
**Status: ✅ Complete**
---
## PHASE 2: Core Infrastructure (Day 1-2, ~4 hours)
### Step 2.1: Implement IndexedDB Service
**File:** `src/lib/db.ts`
```typescript
/**
* IndexedDB database service
*
* Database name: "jobinfo-app"
* Stores:
* - jobs: { id: jobId, data: Job object }
* - fileCache: { id: folderId, files: FileList, cached_at: timestamp }
* - userProfile: { id: "current", data: User object }
*/
export async function initDatabase(): Promise<IDBDatabase> {
// Open/create database
// Create stores if don't exist
// Return database reference
}
export async function getJobs(): Promise<Job[]> {
// Query jobs store from IndexedDB
// Return array of Job objects
}
export async function saveJobs(jobs: Job[]): Promise<void> {
// Store jobs array in IndexedDB
}
export async function getFileCache(folderId: string): Promise<File[] | null> {
// Get cached file list for folder
// Check if cache expired (> 1 hour)
}
export async function saveFileCache(folderId: string, files: File[]): Promise<void> {
// Store file list with timestamp
}
export async function clearCache(): Promise<void> {
// Clear all IndexedDB data (logout)
}
```
**Testing:** Create test to verify database operations work
### Step 2.2: Implement OAuth Service
**File:** `src/services/oauth.ts`
**Functions:**
- `generatePKCE()` - Generate code challenge/verifier
- `initiateOAuthFlow()` - Redirect user to Microsoft login
- `handleOAuthCallback(code, state, codeVerifier)` - Exchange code for token
- `setTokens(accessToken, refreshToken, expiresIn)` - Store tokens securely
**Key points:**
- Never console.log tokens
- Store verification in sessionStorage (lost on close)
- Return only accessToken to frontend
### Step 2.3: Implement Auth Stores
**File:** `src/stores/auth.ts`
**Exports:**
- `user` (writable) - Current user object
- `accessToken` (writable) - Current access token
- `tokenExpiry` (writable) - When token expires
- `isAuthenticated` (derived) - true if user & valid token
- `setAuthUser(...)` - Update stores after login
- `refreshAccessToken()` - Get new token
- `clearAuth()` - Logout
**Testing:** Test store updates and derived values
### Step 2.4: Implement Graph Service
**File:** `src/services/graph.ts`
**Functions:**
- `listFiles(folderId)` - GET /me/drive/items/{id}/children
- `getFile(fileId)` - GET /me/drive/items/{id}
- `searchFiles(query)` - Search in OneDrive
**Error handling:** Catch 401 (invalid token), refresh and retry
**Status: ✅ Complete**
---
## PHASE 3: User Interface Components (Day 2, ~3 hours)
### Step 3.1: Create Navigation Component
**File:** `src/components/Navigation.svelte`
Shows:
- User name (if authenticated)
- Sign Out button
- Logo and title
### Step 3.2: Create JobCard Component
**File:** `src/components/JobCard.svelte`
Props: `job: Job`
Displays: Title, company, date, preview
Events: Click to view details
### Step 3.3: Create JobList Component
**File:** `src/components/JobList.svelte`
Features:
- Subscribe to jobs store
- Display loading state
- Handle "no jobs" case
- Render JobCard for each
### Step 3.4: Create SearchBar Component
**File:** `src/components/SearchBar.svelte`
Features:
- Input field
- Dispatch search event
- Debounce input (300ms)
### Step 3.5: Create ErrorBoundary Component
**File:** `src/components/ErrorBoundary.svelte`
Features:
- Catch errors from child components
- Display error message
- Show retry button
- Log to console (development only)
**Status: ✅ Complete**
---
## PHASE 4: Routes & Pages (Day 2-3, ~2 hours)
### Step 4.1: Create Root Layout
**File:** `src/routes/+layout.svelte`
Features:
- Load Navigation
- Initialize jobs from IndexedDB on mount
- Show error boundary
- Render slot for pages
### Step 4.2: Create Home Page
**File:** `src/routes/+page.svelte`
Features:
- Guard: redirect to /signin if not authenticated
- Display JobList component
- Show search bar
- Handle filter options
### Step 4.3: Create Sign-In Page
**File:** `src/routes/signin/+page.svelte`
Features:
- Guard: redirect to / if authenticated
- Big "Sign in with Microsoft" button
- Call `initiateOAuthFlow()` on click
- Loading state during redirect
### Step 4.4: Create OAuth Callback Handler
**File:** `src/routes/auth/callback/+page.svelte`
Flow:
1. Extract `code` and `state` from URL params
2. Retrieve `codeVerifier` from sessionStorage
3. POST to `/api/auth/authorize` with { code, state, codeVerifier }
4. Receive { accessToken, expiresIn, user }
5. Call `setAuthUser(user, accessToken, expiresIn)`
6. Redirect to `/` (home)
**Error handling:**
- If code missing → show error, link to retry
- If state mismatch → CSRF error, redirect to /signin
- If backend error → show error message, retry button
### Step 4.5: Create Folder View Page
**File:** `src/routes/[folder]/+page.svelte`
Features:
- Load files from Graph API for folder
- Cache in IndexedDB
- Display FileCard for each
- Handle navigation to subfolders
**Status: ✅ Complete**
---
## PHASE 5: Service Worker (Day 3, ~1.5 hours)
### Step 5.1: Create Service Worker Registration
**File:** `src/app.svelte`
On mount:
- Register service worker
- Listen for `TOKEN_REFRESHED` messages
- Update accessToken store when received
### Step 5.2: Implement Service Worker
**File:** `src/service-worker.ts`
Features:
- Every 60 seconds: Check token expiry
- If expiring in < 5 min: POST /api/auth/refresh
- On success: postMessage to all clients with new token
- On error: postMessage with error, trigger re-auth
**No token storage in SW:** Token expiry tracked by frontend, SW just refreshes
**Status: ✅ Complete**
---
## PHASE 6: Backend Routes (Day 3, ~2 hours)
### Step 6.1: Create POST /api/auth/authorize
**File:** `backend/src/routes/auth.ts`
Features:
- Accept { code, state, codeVerifier }
- Validate state (CSRF protection)
- Exchange code for token with Microsoft
- Get user info from Graph
- Store refresh token in PocketBase
- Return { accessToken, expiresIn, user }
### Step 6.2: Create POST /api/auth/refresh
**File:** `backend/src/routes/auth.ts`
Features:
- Accept Authorization header (Bearer {userId})
- Look up user in PocketBase
- Exchange refresh token for new access token
- Update refresh token in PocketBase (if new one provided)
- Return { accessToken, expiresIn }
### Step 6.3: Create POST /api/auth/logout
**File:** `backend/src/routes/auth.ts`
Features:
- Clear refresh token from PocketBase
- Invalidate any cached tokens
**Status: ✅ Complete**
---
## PHASE 7: Error Handling & Logging (Day 3, ~1.5 hours)
### Step 7.1: Centralized Error Handler
**File:** `src/services/errorHandler.ts`
Features:
- Log all errors with context
- Different handling for:
- Network errors
- Auth errors (401, 403)
- Validation errors
- System errors
- User-friendly error messages
- Retry logic where applicable
### Step 7.2: Session Logging
**File:** `logs/SVELTE_SESSION_LOG.txt`
Log format:
```
[2026-01-20 10:00] Component Created: JobCard
- PURPOSE: Display individual job listing
- FEATURES: Click handler, styling
- DEPENDENCIES: Job type, TailwindCSS
- STATUS: Completed
[2026-01-20 10:15] Fix: Auth token refresh timing
- PROBLEM: Token expired before refresh triggered
- DIAGNOSIS: Timer set to 5 min before expiry, but clock skew
- SOLUTION: Added 10 second buffer
- TESTING: Verified token refresh happens before 401 errors
- STATUS: Verified working
```
**Status: ✅ Complete**
---
## PHASE 8: Testing & Validation (Day 4, ~2 hours)
### Test Checklist
**Auth Flow:**
- [ ] User clicks "Sign in with Microsoft"
- [ ] Redirected to Microsoft login
- [ ] Correct scopes requested
- [ ] Callback URL correct
- [ ] Authorization code received
- [ ] Code exchanged for token
- [ ] User profile loaded
- [ ] Redirected to home page
- [ ] User name displayed in header
- [ ] Jobs load from device storage
**Token Refresh:**
- [ ] Token set with correct expiry
- [ ] Service Worker checks token every 60s
- [ ] Refresh triggered 5 min before expiry
- [ ] New token received from backend
- [ ] Token updated in stores
- [ ] No 401 errors during normal usage
**Device Storage:**
- [ ] Jobs load from IndexedDB on startup
- [ ] New files cached after fetched
- [ ] Cache used instead of API when available
- [ ] Cache cleared on logout
- [ ] Multiple folders cached separately
**Error Handling:**
- [ ] Network error shows user message
- [ ] Auth error triggers re-login
- [ ] Expired token forces refresh
- [ ] Refresh failure forces full re-auth
- [ ] Missing data handled gracefully
**Browser Compatibility:**
- [ ] Works in Chrome, Firefox, Safari, Edge
- [ ] Works on mobile browsers
- [ ] Service Worker works in all
- [ ] IndexedDB available in all
---
## PHASE 9: Deployment Preparation (Day 4, ~1 hour)
### Step 9.1: Environment Setup
Create production `.env`:
```
VITE_PUBLIC_MICROSOFT_CLIENT_ID=production-client-id
VITE_PUBLIC_MICROSOFT_TENANT_ID=organization-id
VITE_PUBLIC_MICROSOFT_REDIRECT_URI=https://yourdomain.com/auth/callback
```
### Step 9.2: Build Configuration
- Set up build optimization
- Configure adapter for deployment
- Verify TypeScript strict mode
- Test production build locally
### Step 9.3: Azure App Registration
- Register application in Azure portal
- Configure redirect URIs (both local and production)
- Set client secret (backend only)
- Configure API permissions
- Grant consent (admin)
**Status: ✅ Complete**
---
## Development Rules (Non-Negotiable)
1. **Every file has documentation** - PURPOSE, DEPENDENCIES, NOTES
2. **No tokens in console.log** - Ever
3. **All async has error handling** - Try/catch required
4. **Types on everything** - No `any` without comment
5. **Stores updated explicitly** - No random setState calls
6. **APIs tested locally** - Before production
7. **SessionStorage, never localStorage for tokens** - Security rule
8. **Comments on why, not what** - Code explains itself
9. **Console errors show to user** - Don't hide failures
10. **Every change logged** - Session log updated
---
## Success Criteria
When complete, the application:
- ✅ Users can sign in with Microsoft account
- ✅ OAuth tokens managed securely (backend, never frontend)
- ✅ Jobs load instantly from device storage
- ✅ New jobs loaded from Microsoft Graph progressively
- ✅ Token refresh happens automatically in background
- ✅ No user sees 401 errors (refresh catches them)
- ✅ Logout clears all tokens and data
- ✅ All errors shown to user clearly
- ✅ Session log tracks all decisions
- ✅ TypeScript strict mode passes
- ✅ No console errors or warnings
---
## Ready to Begin?
This plan is ready. All reference documents created:
- SVELTE_DEVELOPMENT_SYSTEM.md - Architecture & rules
- OAUTH2_REFERENCE_GUIDE.md - OAuth patterns
- SVELTE_PATTERNS_REFERENCE.md - Component patterns
- This file - Build plan
**Next step:** Confirm you're ready, and I'll begin Phase 1 (Project setup).