Files
Job-Info/SVELTE_DEVELOPMENT_SYSTEM.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

392 lines
11 KiB
Markdown

# Svelte + Microsoft OAuth Development System
**Comprehensive Methodology, Standards, and Reference Tools**
---
## PHASE 1: RESEARCH & ARCHITECTURE (IN PROGRESS)
### 1. Technology Stack (Monica-Enforced Standards)
**Frontend Runtime:** Svelte (with SvelteKit for routing/build)
**Backend:** Bun + Hono (unchanged - proven working)
**Build Tool:** Vite (integrated with SvelteKit)
**Language:** TypeScript (both frontend and backend)
**Styling:** TailwindCSS + PostCSS
**Storage:** IndexedDB (device storage), PocketBase (token persistence)
**Auth:** Microsoft OAuth2 directly + Service Worker credential management
---
## PART A: MICROSOFT OAUTH2 ARCHITECTURE
### A1. Overall Flow
```
User clicks "Sign In"
→ Service Worker intercepts Microsoft OAuth redirect URL
→ Service Worker extracts auth code
→ Backend (Hono) exchanges code for tokens using Service Worker credentials
→ Backend stores refresh token in PocketBase under user collection
→ Backend returns access token to frontend
→ Frontend stores short-lived access token in memory/sessionStorage
→ Service Worker manages token refresh on expiration
```
### A2. Why This Approach
- **Direct OAuth with Microsoft:** No PocketBase auth, eliminates middle-layer complexity
- **Service Worker credentials:** Never expose client secret to frontend, server handles all token exchanges
- **PocketBase for token storage:** Persistent, synced, secure backend storage of refresh tokens
- **Device storage (IndexedDB):** User data (jobs, cache) stored locally without external service
- **Token refresh automation:** Service Worker checks token expiry and refreshes proactively
### A3. Key Endpoints Needed
```
POST /api/auth/authorize
- Accepts: { code, state }
- Returns: { accessToken, expiresIn, user }
- Action: Exchanges OAuth code for tokens via service worker credentials
POST /api/auth/refresh
- Accepts: { }
- Returns: { accessToken, expiresIn }
- Action: Uses stored refresh token to get new access token
GET /api/auth/status
- Returns: { isAuthenticated, user, tokenExpiry }
POST /api/auth/logout
- Clears refresh token from PocketBase
```
### A4. Microsoft Graph API Integration
- Endpoint: `https://graph.microsoft.com/v1.0/me/drive/root/children`
- Token required: accessToken in Authorization header
- Caching strategy: IndexedDB + device storage for file listings
- Refresh strategy: Access token → 1 hour expiry, Service Worker refreshes automatically
---
## PART B: SVELTE ARCHITECTURE
### B1. Project Structure
```
frontend/
src/
routes/
+page.svelte (home/jobs list)
signin/
+page.svelte (Microsoft OAuth login)
folder/
[id]/
+page.svelte (folder view)
components/
JobCard.svelte
FileListItem.svelte
SearchBar.svelte
stores/
auth.ts (writable stores for user, tokens)
jobs.ts (derived store for job data)
files.ts (derived store for file listings)
services/
oauth.ts (OAuth2 flow handler)
graph.ts (Microsoft Graph API calls)
storage.ts (IndexedDB operations)
utils/
constants.ts
app.svelte (layout wrapper)
svelte.config.js
vite.config.ts
tailwind.config.ts
```
### B2. Svelte Stores Strategy
```typescript
// auth.ts - Global auth state
export const user = writable<User | null>(null);
export const accessToken = writable<string | null>(null);
export const isAuthenticated = derived([user], ([$user]) => !!$user);
// jobs.ts - Derived from IndexedDB cache
export const jobs = writable<Job[]>([]);
export const jobsLoading = writable(false);
// files.ts - Derived from Microsoft Graph
export const files = writable<MicrosoftFile[]>([]);
export const filesLoading = writable(false);
```
### B3. Service Worker Role
```
Purpose: Token refresh automation + OAuth redirect interception
Tasks:
- Listens for token expiry (via postMessage from frontend)
- Automatically calls POST /api/auth/refresh before expiry
- Updates accessToken in frontend via postMessage
- Handles Microsoft OAuth redirect URL capture (if needed)
- Persists tokens in sessionStorage (never exposed to main thread)
```
---
## PART C: DEVICE STORAGE STRATEGY (IndexedDB)
### C1. Database Schema
```javascript
// Database: "jobinfo-app"
// Stores:
// 1. jobs
// Key: jobId (string)
// Value: { id, title, company, postedDate, ... }
// Index: "company", "postedDate"
// 2. fileCache
// Key: folderId (string)
// Value: { folderId, files: [...], lastRefreshed: timestamp }
// Index: "lastRefreshed"
// 3. userCache
// Key: "current"
// Value: { user object from Microsoft }
// 4. tokenMetadata (not storing tokens, just metadata)
// Key: "current"
// Value: { expiresAt: timestamp, refreshedAt: timestamp }
```
### C2. IndexedDB Helper Functions
```typescript
// storage.ts
export async function saveJobs(jobs: Job[]): Promise<void>
export async function getJobs(): Promise<Job[]>
export async function saveFileCache(folderId: string, files: MicrosoftFile[]): Promise<void>
export async function getFileCache(folderId: string): Promise<MicrosoftFile[] | null>
export async function clearExpiredCache(): Promise<void>
```
---
## PART D: BACKEND (HONO) UPDATES
### D1. New Routes
```typescript
// backend/auth-routes.ts
app.post('/auth/authorize', async (c) => {
// 1. Validate request { code, state }
// 2. Exchange code for tokens using Microsoft OAuth credentials
// 3. Store refresh token in PocketBase
// 4. Return accessToken to frontend
})
app.post('/auth/refresh', async (c) => {
// 1. Get user from auth header
// 2. Retrieve refresh token from PocketBase
// 3. Exchange for new access token
// 4. Return new access token
})
app.get('/auth/status', async (c) => {
// Return current auth status
})
```
### D2. Environment Variables Required
```
MICROSOFT_CLIENT_ID=
MICROSOFT_CLIENT_SECRET=
MICROSOFT_TENANT_ID=
MICROSOFT_REDIRECT_URI=http://localhost:5173/auth/callback
POCKETBASE_URL=https://pocketbase.ccllc.pro
POCKETBASE_ADMIN_EMAIL=
POCKETBASE_ADMIN_PASSWORD=
```
---
## PART E: DEVELOPMENT RULES (NON-NEGOTIABLE)
### RULE E1: Code Documentation Standard
Every file, every function, every store must have:
```typescript
/**
* MODULE: [Name]
* PURPOSE: [What it does]
* DEPENDENCIES: [External services, stores, APIs]
* PERSISTENCE: [How/where data is stored]
* NOTES: [Any gotchas, edge cases, security considerations]
*/
```
### RULE E2: Component Structure
Every Svelte component must have:
```svelte
<!--
COMPONENT: ComponentName
PURPOSE: [What it renders]
PROPS: [Detailed prop documentation]
EVENTS: [Events this component dispatches]
STATE: [Local reactive state]
-->
<script lang="ts">
// Props with detailed comments
// Reactive declarations
// Lifecycle hooks
// Event handlers
</script>
<style>
/* TailwindCSS classes only, no custom CSS unless documented as necessary */
</style>
```
### RULE E3: Store Pattern
Every Svelte store follows this pattern:
```typescript
/**
* STORE: storeName
* PURPOSE: [What data it holds]
* UPDATES: [How and when it's updated]
* SUBSCRIPTIONS: [Components that use it]
*/
export const storeName = writable<Type>(initialValue);
// Every update must be wrapped in a clearly named function:
export async function updateStoreName(newValue: Type): Promise<void> {
// Detailed comment explaining the update
storeName.set(newValue);
}
```
### RULE E4: API Call Pattern
Every API call to backend must follow:
```typescript
/**
* API: /endpoint
* METHOD: GET/POST/etc
* HEADERS REQUIRED: [Auth, Content-Type, etc]
* REQUEST BODY: { ... with detailed types }
* RESPONSE: { ... with detailed types }
* ERRORS: [What can go wrong and how we handle it]
* RETRY STRATEGY: [Is this retried? When?]
*/
export async function apiCallName(params: Type): Promise<ResponseType> {
const token = get(accessToken);
if (!token) throw new Error('Not authenticated');
try {
const response = await fetch(`/api/endpoint`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
},
body: JSON.stringify(params),
});
if (!response.ok) throw new Error(`API error: ${response.status}`);
return response.json();
} catch (error) {
console.error('API call failed:', error);
throw error;
}
}
```
### RULE E5: Error Handling Standard
No silent failures. Every promise, every fetch, every async operation:
```typescript
try {
// Operation
} catch (error) {
// 1. Log error with context
console.error('Context: what were we trying to do', error);
// 2. Determine if recoverable
if (isRecoverable(error)) {
// Attempt recovery
} else {
// Notify user or escalate
}
}
```
### RULE E6: Token Handling
- **Never store tokens in localStorage** (vulnerable to XSS)
- **Access token:** In memory only (lost on refresh, that's intentional)
- **Refresh token:** Only on backend in PocketBase
- **Service Worker:** Can read from sessionStorage only, never localStorage
- **Token expiry check:** Frontend checks before each API call, Service Worker proactively refreshes
### RULE E7: Code Quality Standards
- Every TypeScript file has strict: true in tsconfig
- No `any` types without explicit comment explaining why
- No console.log in production code (use proper logging)
- All async operations have error handling
- All state changes are intentional and logged in stores
- No magic numbers or strings (use CONST_DEFINED_AT_TOP)
---
## PART F: DEVELOPMENT CHECKLIST
### Pre-Build Checklist
- [ ] All imports are TypeScript (.ts, not .js)
- [ ] All stores have update functions with documentation
- [ ] All API calls have error handling
- [ ] All tokens are handled per RULE E6
- [ ] All components follow component structure in RULE E2
- [ ] No hardcoded URLs (use constants)
- [ ] No console.log statements
- [ ] All environment variables are documented
### Testing Checklist
- [ ] Login flow works (OAuth → token storage → API calls)
- [ ] Token refresh works (Service Worker updates token)
- [ ] File listing loads from Microsoft Graph
- [ ] File listing cached in IndexedDB
- [ ] Search/filter works from cached files
- [ ] Logout clears tokens and cache
- [ ] Offline mode works (uses cached data)
---
## PART G: SESSION LOG LOCATION
All work logged to: `/home/admin/Job-Info-Test/logs/SVELTE_SESSION_LOG.txt`
Format:
```
[2026-01-19 10:00] Component/Store Created: ComponentName
- PURPOSE: [What it does]
- IMPLEMENTATION: [Key decisions]
- DEPENDENCIES: [What it depends on]
- STATUS: Completed / In Progress / Blocked
[2026-01-19 10:15] Fix: [Issue resolved]
- PROBLEM: [What was broken]
- DIAGNOSIS: [How we found the issue]
- SOLUTION: [What we changed]
- TESTING: [How we verified it works]
- STATUS: Verified working
```
---
## NEXT STEPS
1. ✅ Create this document (DONE)
2. ⏳ Research Svelte + SvelteKit best practices
3. ⏳ Research Microsoft OAuth2 + Service Worker patterns
4. ⏳ Create backend OAuth routes
5. ⏳ Create Svelte project structure
6. ⏳ Create auth store and OAuth service
7. ⏳ Create IndexedDB service
8. ⏳ Create Microsoft Graph service
9. ⏳ Build UI components
10. ⏳ Integrate and test end-to-end
---
**Status: FOUNDATION DOCUMENT CREATED - Ready for Research Phase**