mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 08:31:08 -05:00
- Implement tests for RideLocation and CompanyHeadquarters models to verify functionality and data integrity. - Create a manual trigger test script for trending content calculation endpoint, including authentication and unauthorized access tests. - Develop a manufacturer sync test to ensure ride manufacturers are correctly associated with ride models. - Add tests for ParkLocation model, including coordinate setting and distance calculations between parks. - Implement a RoadTripService test suite covering geocoding, route calculation, park discovery, and error handling. - Create a unified map service test script to validate map functionality, API endpoints, and performance metrics.
601 lines
18 KiB
Markdown
601 lines
18 KiB
Markdown
# 🏗️ ThrillWiki Nuxt Frontend - Architecture Decisions
|
|
|
|
**Status:** ✅ COMPLETE
|
|
**Last Updated:** 2025-01-27 19:58 UTC
|
|
**Dependencies:** requirements.md
|
|
**Blocks:** All implementation phases
|
|
|
|
## 🎯 Architecture Overview
|
|
|
|
### System Architecture
|
|
```
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ ThrillWiki System │
|
|
├─────────────────────────────────────────────────────────────┤
|
|
│ Frontend (Nuxt 3) │ Backend (Django) │
|
|
│ ┌─────────────────────┐ │ ┌─────────────────────┐ │
|
|
│ │ Pages & Components │ │ │ REST API (/api/v1/) │ │
|
|
│ │ ├─ Parks │ │ │ ├─ Authentication │ │
|
|
│ │ ├─ Rides │ │ │ ├─ Parks CRUD │ │
|
|
│ │ ├─ Auth │ │ │ ├─ Rides CRUD │ │
|
|
│ │ └─ Admin │ │ │ ├─ Photos │ │
|
|
│ └─────────────────────┘ │ │ └─ Moderation │ │
|
|
│ ┌─────────────────────┐ │ └─────────────────────┘ │
|
|
│ │ Composables │ │ ┌─────────────────────┐ │
|
|
│ │ ├─ useAuth │◄───┼──┤ JWT Authentication │ │
|
|
│ │ ├─ useApi │◄───┼──┤ Token Management │ │
|
|
│ │ ├─ useParks │◄───┼──┤ CORS Configuration │ │
|
|
│ │ └─ useRides │ │ └─────────────────────┘ │
|
|
│ └─────────────────────┘ │ │
|
|
│ ┌─────────────────────┐ │ ┌─────────────────────┐ │
|
|
│ │ Component Library │ │ │ Database (PostgreSQL)│ │
|
|
│ │ ├─ UI Components │ │ │ ├─ Parks │ │
|
|
│ │ ├─ Forms │ │ │ ├─ Rides │ │
|
|
│ │ ├─ Navigation │ │ │ ├─ Users │ │
|
|
│ │ └─ Modals │ │ │ └─ Photos │ │
|
|
│ └─────────────────────┘ │ └─────────────────────┘ │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
### Technology Stack Decisions
|
|
|
|
#### Frontend Framework: Nuxt 3
|
|
**Decision:** Use Nuxt 3 with Vue 3 Composition API
|
|
**Rationale:**
|
|
- **Server-Side Rendering:** Better SEO and initial load performance
|
|
- **File-based Routing:** Intuitive page organization
|
|
- **Auto-imports:** Reduced boilerplate code
|
|
- **Built-in Optimization:** Image optimization, code splitting, etc.
|
|
- **TypeScript Support:** First-class TypeScript integration
|
|
- **Ecosystem:** Rich ecosystem with modules and plugins
|
|
|
|
**Alternatives Considered:**
|
|
- **Next.js:** Rejected due to React requirement
|
|
- **SvelteKit:** Rejected due to smaller ecosystem
|
|
- **Vite + Vue:** Rejected due to lack of SSR out-of-the-box
|
|
|
|
#### State Management: Pinia
|
|
**Decision:** Use Pinia for global state management
|
|
**Rationale:**
|
|
- **Vue 3 Native:** Built specifically for Vue 3
|
|
- **TypeScript Support:** Excellent TypeScript integration
|
|
- **DevTools:** Great debugging experience
|
|
- **Modular:** Easy to organize stores by feature
|
|
- **Performance:** Optimized for Vue 3 reactivity
|
|
|
|
**Alternatives Considered:**
|
|
- **Vuex:** Rejected due to Vue 3 compatibility issues
|
|
- **Composables Only:** Rejected for complex state management needs
|
|
|
|
#### Component Library: TBD (User Choice Required)
|
|
**Status:** ⏳ PENDING USER DECISION
|
|
**Options Analyzed:**
|
|
|
|
##### Option 1: Nuxt UI (Recommended)
|
|
```typescript
|
|
// Installation
|
|
npm install @nuxt/ui
|
|
|
|
// Configuration
|
|
export default defineNuxtConfig({
|
|
modules: ['@nuxt/ui'],
|
|
ui: {
|
|
global: true,
|
|
icons: ['heroicons']
|
|
}
|
|
})
|
|
```
|
|
|
|
**Pros:**
|
|
- Built specifically for Nuxt 3
|
|
- Tailwind CSS integration
|
|
- Headless UI foundation (accessibility)
|
|
- TypeScript support
|
|
- Modern design system
|
|
- Tree-shakable
|
|
|
|
**Cons:**
|
|
- Newer library (less mature)
|
|
- Smaller component set
|
|
- Limited complex components
|
|
|
|
##### Option 2: Vuetify
|
|
```typescript
|
|
// Installation
|
|
npm install vuetify @mdi/font
|
|
|
|
// Configuration
|
|
import { createVuetify } from 'vuetify'
|
|
export default defineNuxtPlugin(() => {
|
|
const vuetify = createVuetify({
|
|
theme: { defaultTheme: 'light' }
|
|
})
|
|
return { provide: { vuetify } }
|
|
})
|
|
```
|
|
|
|
**Pros:**
|
|
- Mature, battle-tested
|
|
- Comprehensive component set
|
|
- Material Design system
|
|
- Strong community
|
|
- Good documentation
|
|
|
|
**Cons:**
|
|
- Large bundle size
|
|
- Material Design constraints
|
|
- Vue 3 support still evolving
|
|
- Less customizable
|
|
|
|
##### Option 3: PrimeVue
|
|
```typescript
|
|
// Installation
|
|
npm install primevue primeicons
|
|
|
|
// Configuration
|
|
import PrimeVue from 'primevue/config'
|
|
export default defineNuxtPlugin((nuxtApp) => {
|
|
nuxtApp.vueApp.use(PrimeVue)
|
|
})
|
|
```
|
|
|
|
**Pros:**
|
|
- Enterprise-focused
|
|
- Comprehensive components
|
|
- Good TypeScript support
|
|
- Professional themes
|
|
- Accessibility features
|
|
|
|
**Cons:**
|
|
- Commercial themes cost
|
|
- Learning curve
|
|
- Less modern design
|
|
- Larger bundle size
|
|
|
|
#### Authentication: JWT with Refresh Tokens
|
|
**Decision:** Implement JWT authentication with refresh token mechanism
|
|
**Rationale:**
|
|
- **Stateless:** No server-side session storage required
|
|
- **Scalable:** Works well with multiple frontend instances
|
|
- **Secure:** Short-lived access tokens with refresh mechanism
|
|
- **Standard:** Industry standard for API authentication
|
|
|
|
**Implementation Strategy:**
|
|
```typescript
|
|
// composables/useAuth.ts
|
|
export const useAuth = () => {
|
|
const accessToken = useCookie('access_token', {
|
|
httpOnly: true,
|
|
secure: true,
|
|
sameSite: 'strict',
|
|
maxAge: 15 * 60 // 15 minutes
|
|
})
|
|
|
|
const refreshToken = useCookie('refresh_token', {
|
|
httpOnly: true,
|
|
secure: true,
|
|
sameSite: 'strict',
|
|
maxAge: 7 * 24 * 60 * 60 // 7 days
|
|
})
|
|
|
|
const refreshAccessToken = async () => {
|
|
// Auto-refresh logic
|
|
}
|
|
|
|
return {
|
|
login, logout, refreshAccessToken,
|
|
isAuthenticated: computed(() => !!accessToken.value)
|
|
}
|
|
}
|
|
```
|
|
|
|
#### API Integration: Custom Composables with $fetch
|
|
**Decision:** Use Nuxt's built-in $fetch with custom composables
|
|
**Rationale:**
|
|
- **Built-in:** No additional HTTP client needed
|
|
- **SSR Compatible:** Works seamlessly with server-side rendering
|
|
- **Type Safe:** Full TypeScript support
|
|
- **Caching:** Built-in request caching
|
|
- **Error Handling:** Consistent error handling patterns
|
|
|
|
**Implementation Pattern:**
|
|
```typescript
|
|
// composables/useApi.ts
|
|
export const useApi = () => {
|
|
const { $fetch } = useNuxtApp()
|
|
const { accessToken } = useAuth()
|
|
|
|
const apiCall = async (endpoint: string, options: any = {}) => {
|
|
return await $fetch(`/api/v1${endpoint}`, {
|
|
...options,
|
|
headers: {
|
|
'Authorization': `Bearer ${accessToken.value}`,
|
|
...options.headers
|
|
}
|
|
})
|
|
}
|
|
|
|
return { apiCall }
|
|
}
|
|
```
|
|
|
|
### Project Structure Decisions
|
|
|
|
#### Directory Structure
|
|
```
|
|
frontend/
|
|
├── assets/ # Static assets (images, fonts, etc.)
|
|
├── components/ # Vue components
|
|
│ ├── ui/ # UI library components
|
|
│ ├── layout/ # Layout components
|
|
│ ├── forms/ # Form components
|
|
│ └── features/ # Feature-specific components
|
|
│ ├── parks/ # Park-related components
|
|
│ ├── rides/ # Ride-related components
|
|
│ ├── auth/ # Authentication components
|
|
│ └── admin/ # Admin/moderation components
|
|
├── composables/ # Vue composables
|
|
│ ├── useAuth.ts # Authentication logic
|
|
│ ├── useApi.ts # API integration
|
|
│ ├── useParks.ts # Parks data management
|
|
│ ├── useRides.ts # Rides data management
|
|
│ └── useModeration.ts # Moderation logic
|
|
├── layouts/ # Nuxt layouts
|
|
│ ├── default.vue # Default layout
|
|
│ ├── auth.vue # Authentication layout
|
|
│ └── admin.vue # Admin layout
|
|
├── middleware/ # Route middleware
|
|
│ ├── auth.ts # Authentication middleware
|
|
│ └── admin.ts # Admin access middleware
|
|
├── pages/ # File-based routing
|
|
│ ├── index.vue # Homepage
|
|
│ ├── parks/ # Parks pages
|
|
│ ├── rides/ # Rides pages
|
|
│ ├── auth/ # Authentication pages
|
|
│ └── admin/ # Admin pages
|
|
├── plugins/ # Nuxt plugins
|
|
│ ├── api.client.ts # API configuration
|
|
│ └── context7.client.ts # Context7 integration
|
|
├── stores/ # Pinia stores
|
|
│ ├── auth.ts # Authentication store
|
|
│ ├── parks.ts # Parks store
|
|
│ └── ui.ts # UI state store
|
|
├── types/ # TypeScript type definitions
|
|
│ ├── api.ts # API response types
|
|
│ ├── auth.ts # Authentication types
|
|
│ └── components.ts # Component prop types
|
|
├── utils/ # Utility functions
|
|
│ ├── validation.ts # Form validation
|
|
│ ├── formatting.ts # Data formatting
|
|
│ └── constants.ts # Application constants
|
|
├── nuxt.config.ts # Nuxt configuration
|
|
├── package.json # Dependencies
|
|
└── tsconfig.json # TypeScript configuration
|
|
```
|
|
|
|
### Development Environment Decisions
|
|
|
|
#### Package Manager: npm
|
|
**Decision:** Use npm for package management
|
|
**Rationale:**
|
|
- **Consistency:** Matches existing project setup
|
|
- **Reliability:** Stable and well-supported
|
|
- **Lock File:** package-lock.json for reproducible builds
|
|
- **CI/CD:** Easy integration with deployment pipelines
|
|
|
|
#### Development Server Configuration
|
|
```typescript
|
|
// nuxt.config.ts
|
|
export default defineNuxtConfig({
|
|
devtools: { enabled: true },
|
|
|
|
// Development server configuration
|
|
devServer: {
|
|
port: 3000,
|
|
host: '0.0.0.0'
|
|
},
|
|
|
|
// Proxy API calls to Django backend
|
|
nitro: {
|
|
devProxy: {
|
|
'/api': {
|
|
target: 'http://localhost:8000',
|
|
changeOrigin: true
|
|
}
|
|
}
|
|
},
|
|
|
|
// Runtime configuration
|
|
runtimeConfig: {
|
|
public: {
|
|
apiBase: process.env.NUXT_PUBLIC_API_BASE || 'http://localhost:8000/api/v1'
|
|
}
|
|
}
|
|
})
|
|
```
|
|
|
|
#### Environment Variables
|
|
```bash
|
|
# .env
|
|
NUXT_PUBLIC_API_BASE=http://localhost:8000/api/v1
|
|
NUXT_SECRET_JWT_SECRET=your-jwt-secret
|
|
NUXT_PUBLIC_APP_NAME=ThrillWiki
|
|
NUXT_PUBLIC_APP_VERSION=1.0.0
|
|
```
|
|
|
|
### Performance Optimization Decisions
|
|
|
|
#### Code Splitting Strategy
|
|
**Decision:** Implement route-based and component-based code splitting
|
|
**Implementation:**
|
|
```typescript
|
|
// Lazy load heavy components
|
|
const PhotoGallery = defineAsyncComponent(() => import('~/components/PhotoGallery.vue'))
|
|
|
|
// Route-based splitting (automatic with Nuxt)
|
|
// pages/admin/ - Admin bundle
|
|
// pages/parks/ - Parks bundle
|
|
// pages/rides/ - Rides bundle
|
|
```
|
|
|
|
#### Image Optimization
|
|
**Decision:** Use Nuxt Image module for automatic optimization
|
|
**Configuration:**
|
|
```typescript
|
|
// nuxt.config.ts
|
|
export default defineNuxtConfig({
|
|
modules: ['@nuxt/image'],
|
|
image: {
|
|
provider: 'ipx',
|
|
quality: 80,
|
|
format: ['webp', 'avif', 'jpg'],
|
|
screens: {
|
|
xs: 320,
|
|
sm: 640,
|
|
md: 768,
|
|
lg: 1024,
|
|
xl: 1280
|
|
}
|
|
}
|
|
})
|
|
```
|
|
|
|
#### Caching Strategy
|
|
**Decision:** Multi-layer caching approach
|
|
**Layers:**
|
|
1. **Browser Cache:** Static assets with long cache times
|
|
2. **API Cache:** Response caching with TTL
|
|
3. **Component Cache:** Expensive component computations
|
|
4. **Route Cache:** Static route pre-generation
|
|
|
|
```typescript
|
|
// API caching example
|
|
export const useParks = () => {
|
|
const { data: parks } = useLazyFetch('/api/v1/parks/', {
|
|
key: 'parks-list',
|
|
server: true,
|
|
default: () => [],
|
|
transform: (data: any) => data.results || data
|
|
})
|
|
|
|
return { parks }
|
|
}
|
|
```
|
|
|
|
### Security Decisions
|
|
|
|
#### Token Storage
|
|
**Decision:** Use HTTP-only cookies for token storage
|
|
**Rationale:**
|
|
- **XSS Protection:** Tokens not accessible via JavaScript
|
|
- **CSRF Protection:** SameSite cookie attribute
|
|
- **Automatic Handling:** Browser handles cookie management
|
|
|
|
#### Input Validation
|
|
**Decision:** Client-side validation with server-side verification
|
|
**Implementation:**
|
|
```typescript
|
|
// utils/validation.ts
|
|
import { z } from 'zod'
|
|
|
|
export const parkSchema = z.object({
|
|
name: z.string().min(1).max(100),
|
|
location: z.string().min(1),
|
|
operator: z.string().optional(),
|
|
status: z.enum(['OPERATING', 'CLOSED', 'UNDER_CONSTRUCTION'])
|
|
})
|
|
|
|
export type ParkInput = z.infer<typeof parkSchema>
|
|
```
|
|
|
|
#### CORS Configuration
|
|
**Decision:** Strict CORS policy for production
|
|
**Django Configuration:**
|
|
```python
|
|
# backend/config/settings/production.py
|
|
CORS_ALLOWED_ORIGINS = [
|
|
"https://thrillwiki.com",
|
|
"https://www.thrillwiki.com"
|
|
]
|
|
|
|
CORS_ALLOW_CREDENTIALS = True
|
|
CORS_ALLOW_ALL_ORIGINS = False # Never true in production
|
|
```
|
|
|
|
### Testing Strategy Decisions
|
|
|
|
#### Testing Framework: Vitest
|
|
**Decision:** Use Vitest for unit and component testing
|
|
**Rationale:**
|
|
- **Vite Integration:** Fast test execution
|
|
- **Vue Support:** Excellent Vue component testing
|
|
- **TypeScript:** Native TypeScript support
|
|
- **Jest Compatible:** Familiar API for developers
|
|
|
|
#### E2E Testing: Playwright
|
|
**Decision:** Use Playwright for end-to-end testing
|
|
**Rationale:**
|
|
- **Cross-browser:** Chrome, Firefox, Safari support
|
|
- **Mobile Testing:** Mobile browser simulation
|
|
- **Reliable:** Stable test execution
|
|
- **Modern:** Built for modern web applications
|
|
|
|
#### Testing Configuration
|
|
```typescript
|
|
// vitest.config.ts
|
|
import { defineConfig } from 'vitest/config'
|
|
import vue from '@vitejs/plugin-vue'
|
|
|
|
export default defineConfig({
|
|
plugins: [vue()],
|
|
test: {
|
|
environment: 'happy-dom',
|
|
coverage: {
|
|
reporter: ['text', 'json', 'html'],
|
|
threshold: {
|
|
global: {
|
|
branches: 80,
|
|
functions: 80,
|
|
lines: 80,
|
|
statements: 80
|
|
}
|
|
}
|
|
}
|
|
}
|
|
})
|
|
```
|
|
|
|
### Deployment Decisions
|
|
|
|
#### Build Strategy
|
|
**Decision:** Hybrid rendering with static generation for public pages
|
|
**Configuration:**
|
|
```typescript
|
|
// nuxt.config.ts
|
|
export default defineNuxtConfig({
|
|
nitro: {
|
|
prerender: {
|
|
routes: [
|
|
'/',
|
|
'/parks',
|
|
'/rides',
|
|
'/about',
|
|
'/privacy',
|
|
'/terms'
|
|
]
|
|
}
|
|
},
|
|
|
|
// Route rules for different rendering strategies
|
|
routeRules: {
|
|
'/': { prerender: true },
|
|
'/parks': { prerender: true },
|
|
'/rides': { prerender: true },
|
|
'/admin/**': { ssr: false }, // SPA mode for admin
|
|
'/auth/**': { ssr: false } // SPA mode for auth
|
|
}
|
|
})
|
|
```
|
|
|
|
#### Docker Configuration
|
|
**Decision:** Multi-stage Docker build for production
|
|
**Dockerfile:**
|
|
```dockerfile
|
|
# Build stage
|
|
FROM node:18-alpine AS builder
|
|
WORKDIR /app
|
|
COPY package*.json ./
|
|
RUN npm ci --only=production
|
|
COPY . .
|
|
RUN npm run build
|
|
|
|
# Production stage
|
|
FROM node:18-alpine AS production
|
|
WORKDIR /app
|
|
COPY --from=builder /app/.output ./
|
|
EXPOSE 3000
|
|
CMD ["node", "server/index.mjs"]
|
|
```
|
|
|
|
### Context7 Integration Decisions
|
|
|
|
#### Documentation Strategy
|
|
**Decision:** Integrate Context7 for automatic documentation generation
|
|
**Implementation:**
|
|
```typescript
|
|
// plugins/context7.client.ts
|
|
export default defineNuxtPlugin(() => {
|
|
if (process.dev) {
|
|
// Initialize Context7 connection
|
|
const context7 = new Context7Client({
|
|
endpoint: 'http://localhost:8080',
|
|
project: 'thrillwiki-frontend'
|
|
})
|
|
|
|
// Auto-document API calls
|
|
context7.trackApiCalls()
|
|
|
|
// Document component usage
|
|
context7.trackComponentUsage()
|
|
}
|
|
})
|
|
```
|
|
|
|
#### Knowledge Preservation
|
|
**Decision:** Structured documentation with progress tracking
|
|
**Features:**
|
|
- Automatic API endpoint documentation
|
|
- Component usage tracking
|
|
- Implementation decision logging
|
|
- Progress milestone tracking
|
|
- LLM handoff preparation
|
|
|
|
---
|
|
|
|
## 🔧 Implementation Priorities
|
|
|
|
### Phase 1: Foundation (Week 1)
|
|
1. **Project Setup:** Initialize Nuxt 3 with TypeScript
|
|
2. **Component Library:** Integrate chosen UI library
|
|
3. **Authentication:** Implement JWT auth system
|
|
4. **API Integration:** Set up Django backend communication
|
|
5. **Basic Layout:** Create header, footer, navigation
|
|
|
|
### Phase 2: Core Features (Week 2)
|
|
1. **Parks System:** Listing, detail, search functionality
|
|
2. **Rides System:** Listing, detail, filtering
|
|
3. **User Profiles:** Profile management and settings
|
|
4. **Photo System:** Upload, display, basic moderation
|
|
|
|
### Phase 3: Advanced Features (Week 3)
|
|
1. **Submission System:** Content submission workflow
|
|
2. **Moderation Interface:** Admin tools and queues
|
|
3. **Advanced Search:** Filters, autocomplete, suggestions
|
|
4. **Maps Integration:** Location visualization
|
|
|
|
### Phase 4: Polish & Deployment (Week 4)
|
|
1. **Performance Optimization:** Bundle size, loading times
|
|
2. **Testing:** Comprehensive test suite
|
|
3. **Documentation:** Complete user and developer docs
|
|
4. **Deployment:** Production setup and monitoring
|
|
|
|
---
|
|
|
|
## 🚨 Critical Dependencies
|
|
|
|
### Immediate Blockers
|
|
1. **Component Library Choice:** Must be decided before implementation
|
|
2. **Django JWT Setup:** Backend enhancement required
|
|
3. **Development Environment:** CORS and proxy configuration
|
|
|
|
### Technical Dependencies
|
|
1. **Node.js 18+:** Required for Nuxt 3
|
|
2. **Django Backend:** Must be running for development
|
|
3. **PostgreSQL:** Database must be accessible
|
|
4. **Context7:** Integration approach needs clarification
|
|
|
|
---
|
|
|
|
**Next Document:** `component-library-analysis.md` - Detailed analysis of chosen library
|
|
**Status:** Ready for component library decision and implementation start
|