Add Mode6Test: copy of Mode5Test running on port 3000
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
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
This commit is contained in:
@@ -0,0 +1,519 @@
|
||||
# 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).
|
||||
Reference in New Issue
Block a user