feat: perf optimisation
Some checks failed
Deploy with Docker Compose / deploy (push) Failing after 2s

This commit is contained in:
2026-02-27 16:23:05 +01:00
parent bcfd602353
commit 0c3a54c62c
20 changed files with 883 additions and 489 deletions

View File

@@ -0,0 +1,65 @@
<!-- Context: project-intelligence/business | Priority: high | Version: 1.0 | Updated: 2026-02-27 -->
# Business Domain
**Purpose**: Business context, problems solved, and value created for the comic/manga reader app.
**Last Updated**: 2026-02-27
## Quick Reference
- **Update When**: Business direction changes, new features shipped
- **Audience**: Developers needing context, stakeholders
## Project Identity
```
Project Name: Stripstream
Tagline: Modern web reader for digital comics and manga
Problem: Need a responsive, feature-rich web interface for reading comics from Komga servers
Solution: Next.js PWA that syncs with Komga API, supports offline reading, multiple reading modes
```
## Target Users
| Segment | Who | Needs | Pain Points |
|---------|-----|-------|--------------|
| Comic/Manga Readers | Users with Komga servers | Read comics in browser | Komga web UI is limited |
| Mobile Readers | iPad/Android users | Offline reading, touch gestures | No native mobile app |
| Library Organizers | Users with large collections | Search, filter, track progress | Hard to manage |
## Value Proposition
**For Users**:
- Read comics anywhere via responsive PWA
- Offline reading with local storage
- Multiple viewing modes (single/double page, RTL, scroll)
- Progress sync with Komga server
- Light/Dark mode, language support (EN/FR)
**For Business**:
- Open source showcase
- Demonstrates Next.js + Komga integration patterns
## Key Features
- **Sync**: Read progress, series/books lists with Komga
- **Reader**: RTL, double/single page, zoom, thumbnails, fullscreen
- **Offline**: PWA with local book downloads
- **UI**: Dark/light, responsive, loading/error states
- **Lists**: Pagination, search, mark read/unread, favorites
- **Settings**: Cache TTL, Komga config, display preferences
## Tech Stack Context
- Integrates with **Komga** (comic server API)
- Uses **MongoDB** for local caching/preferences
- **NextAuth** for authentication (session-based)
## Success Metrics
| Metric | Target |
|--------|--------|
| Page load | <2s |
| Reader responsiveness | 60fps |
| PWA install rate | Track via analytics |
## Constraints
- Requires Komga server (not standalone)
- Mobile storage limits for offline books
## Related Files
- `technical-domain.md` - Tech stack and code patterns
- `business-tech-bridge.md` - Business-technical mapping

View File

@@ -0,0 +1,65 @@
<!-- Context: project-intelligence/bridge | Priority: medium | Version: 1.0 | Updated: 2026-02-27 -->
# Business-Tech Bridge
**Purpose**: Map business concepts to technical implementation.
**Last Updated**: 2026-02-27
## Quick Reference
- **Update When**: New features that bridge business and tech
- **Audience**: Developers, product
---
## Business → Technical Mapping
| Business Concept | Technical Implementation |
|-----------------|------------------------|
| Read comics | `BookService.getBook()`, PhotoswipeReader component |
| Sync progress | Komga API calls in `ReadProgressService` |
| Offline reading | Service Worker + IndexedDB via `ClientOfflineBookService` |
| User preferences | MongoDB + `PreferencesService` |
| Library management | `LibraryService`, `SeriesService` |
| Authentication | NextAuth v5 + `AuthServerService` |
---
## User Flows → API Routes
| User Action | API Route | Service |
|-------------|-----------|---------|
| View home | `GET /api/komga/home` | `HomeService` |
| Browse series | `GET /api/komga/libraries/:id/series` | `SeriesService` |
| Read book | `GET /api/komga/books/:id` | `BookService` |
| Update progress | `POST /api/komga/books/:id/read-progress` | `BookService` |
| Download book | `GET /api/komga/images/books/:id/pages/:n` | `ImageService` |
---
## Components → Services
| UI Component | Service Layer |
|--------------|---------------|
| HomeContent | HomeService |
| SeriesGrid | SeriesService |
| BookCover | BookService |
| PhotoswipeReader | ImageService |
| FavoritesButton | FavoriteService |
| SettingsPanel | PreferencesService |
---
## Data Flow
```
User Request → API Route → Service → Komga API / MongoDB → Response
Logger (Pino)
```
---
## Related Files
- `technical-domain.md` - Tech stack and code patterns
- `business-domain.md` - Business context
- `decisions-log.md` - Architecture decisions

View File

@@ -0,0 +1,130 @@
<!-- Context: project-intelligence/decisions | Priority: medium | Version: 1.0 | Updated: 2026-02-27 -->
# Decisions Log
**Purpose**: Record architecture decisions with context and rationale.
**Last Updated**: 2026-02-27
## Quick Reference
- **Update When**: New architecture decisions
- **Audience**: Developers, architects
---
## ADR-001: Use Prisma with MongoDB
**Date**: 2024
**Status**: Accepted
**Context**: Need database for caching Komga responses and storing user preferences.
**Decision**: Use Prisma ORM with MongoDB adapter.
**Rationale**:
- Type-safe queries across the app
- Schema migration support
- Works well with MongoDB's flexible schema
**Alternatives Considered**:
- Mongoose: Less type-safe, manual schema management
- Raw MongoDB driver: No type safety, verbose
---
## ADR-002: Service Layer Pattern
**Date**: 2024
**Status**: Accepted
**Context**: API routes need business logic separated from HTTP handling.
**Decision**: Create service classes in `src/lib/services/` (BookService, SeriesService, etc.)
**Rationale**:
- Separation of concerns
- Testable business logic
- Reusable across API routes
**Example**:
```typescript
// API route (thin)
export async function GET(request: NextRequest, { params }) {
const book = await BookService.getBook(bookId);
return NextResponse.json(book);
}
// Service (business logic)
class BookService {
static async getBook(bookId: string) { ... }
}
```
---
## ADR-003: Custom AppError with Error Codes
**Date**: 2024
**Status**: Accepted
**Context**: Need consistent error handling across API.
**Decision**: Custom `AppError` class with error codes from `ERROR_CODES` constant.
**Rationale**:
- Consistent error format: `{ error: { code, name, message } }`
- Typed error codes for client handling
- Centralized error messages via `getErrorMessage()`
---
## ADR-004: Radix UI + Tailwind for Components
**Date**: 2024
**Status**: Accepted
**Context**: Need accessible UI components without fighting a component library.
**Decision**: Use Radix UI primitives with custom Tailwind styling.
**Rationale**:
- Radix provides accessible primitives
- Full control over styling via Tailwind
- Shadcn-like pattern (cva + cn)
---
## ADR-005: Client-Side Request Deduplication
**Date**: 2024
**Status**: Accepted
**Context**: Multiple components may request same data (e.g., home page with series, books, continue reading).
**Decision**: `RequestDeduplicationService` with React query-like deduplication.
**Rationale**:
- Reduces Komga API calls
- Consistent data across components
- Configurable TTL
---
## ADR-006: PWA with Offline Book Storage
**Date**: 2024
**Status**: Accepted
**Context**: Users want to read offline, especially on mobile.
**Decision**: Next PWA + Service Worker + IndexedDB for storing book blobs.
**Rationale**:
- Full offline capability
- Background sync when online
- Local storage limits on mobile
---
## Related Files
- `technical-domain.md` - Tech stack details
- `business-domain.md` - Business context

View File

@@ -0,0 +1,64 @@
<!-- Context: project-intelligence/living-notes | Priority: low | Version: 1.0 | Updated: 2026-02-27 -->
# Living Notes
**Purpose**: Development notes, TODOs, and temporary information.
**Last Updated**: 2026-02-27
## Quick Reference
- **Update When**: Adding dev notes, tracking issues
- **Audience**: Developers
---
## Current Focus
- Performance optimization (see PLAN_OPTIMISATION_PERFORMANCES.md)
- Reducing bundle size
- Image optimization
---
## Development Notes
### Service Layer
All business logic lives in `src/lib/services/`. API routes are thin wrappers.
### API Error Handling
Use `AppError` class from `@/utils/errors`. Always include error code from `ERROR_CODES`.
### Component Patterns
- UI components: `src/components/ui/` (Radix + Tailwind)
- Feature components: `src/components/*/` (by feature)
- Use `cva` for variant props
- Use `cn` from `@/lib/utils` for class merging
### Types
- Komga types: `src/types/komga/`
- App types: `src/types/`
### Database
- Prisma schema: `prisma/schema.prisma`
- MongoDB connection: `src/lib/prisma.ts`
---
## Known Issues
- Large libraries may be slow to load (pagination helps)
- Offline storage limited by device space
---
## Future Ideas
- [ ] Add more reader modes
- [ ] User collections/tags
- [ ] Reading statistics
- [ ] Better caching strategy
---
## Related Files
- `technical-domain.md` - Code patterns
- `decisions-log.md` - Architecture decisions

View File

@@ -0,0 +1,23 @@
<!-- Context: project-intelligence/navigation | Priority: critical | Version: 1.0 | Updated: 2026-02-27 -->
# Project Intelligence
Quick overview of project patterns and context files.
## Quick Routes
| File | Description | Priority |
|------|-------------|----------|
| [technical-domain.md](./technical-domain.md) | Tech stack, architecture, patterns | critical |
| [business-domain.md](./business-domain.md) | Business logic, domain model | high |
| [decisions-log.md](./decisions-log.md) | Architecture decisions | medium |
| [living-notes.md](./living-notes.md) | Development notes | low |
| [business-tech-bridge.md](./business-tech-bridge.md) | Business-technical mapping | medium |
## All Files Complete |
## Usage
- **AI Agents**: Read technical-domain.md for code patterns
- **New Developers**: Start with technical-domain.md + business-domain.md
- **Architecture**: Check decisions-log.md for rationale

View File

@@ -0,0 +1,154 @@
<!-- Context: project-intelligence/technical | Priority: critical | Version: 1.0 | Updated: 2026-02-27 -->
# Technical Domain
**Purpose**: Tech stack, architecture, development patterns for this project.
**Last Updated**: 2026-02-27
## Quick Reference
**Update Triggers**: Tech stack changes | New patterns | Architecture decisions
**Audience**: Developers, AI agents
## Primary Stack
| Layer | Technology | Version | Rationale |
|-------|-----------|---------|-----------|
| Framework | Next.js | 15.5.9 | App Router, Server Components |
| Language | TypeScript | 5.3.3 | Type safety |
| Database | MongoDB | - | Flexible schema for media metadata |
| ORM | Prisma | 6.17.1 | Type-safe DB queries |
| Styling | Tailwind CSS | 3.4.1 | Utility-first |
| UI Library | Radix UI | - | Accessible components |
| Animation | Framer Motion | 12.x | Declarative animations |
| Auth | NextAuth | v5 | Session management |
| Validation | Zod | 3.22.4 | Schema validation |
| Logger | Pino | 10.x | Structured logging |
## Project Structure
```
src/
├── app/ # Next.js App Router pages
├── components/ # React components (ui/, features/)
├── lib/ # Services, utils, config
│ └── services/ # Business logic (BookService, etc.)
├── hooks/ # Custom React hooks
├── types/ # TypeScript type definitions
├── utils/ # Helper functions
├── contexts/ # React contexts
├── constants/ # App constants
└── i18n/ # Internationalization
```
## Code Patterns
### API Endpoint
```typescript
import { NextResponse } from "next/server";
import { BookService } from "@/lib/services/book.service";
import { ERROR_CODES } from "@/constants/errorCodes";
import { getErrorMessage } from "@/utils/errors";
import { AppError } from "@/utils/errors";
import logger from "@/lib/logger";
export async function GET(request: NextRequest, { params }: { params: Promise<{ bookId: string }> }) {
try {
const bookId: string = (await params).bookId;
const data = await BookService.getBook(bookId);
return NextResponse.json(data);
} catch (error) {
logger.error({ err: error }, "API Books - Erreur:");
if (error instanceof AppError) {
const isNotFound = error.code === ERROR_CODES.BOOK.NOT_FOUND;
return NextResponse.json(
{ error: { code: error.code, name: "Error", message: getErrorMessage(error.code) } },
{ status: isNotFound ? 404 : 500 }
);
}
return NextResponse.json(
{ error: { code: ERROR_CODES.BOOK.NOT_FOUND, name: "Error", message: "Internal error" } },
{ status: 500 }
);
}
}
```
### Component with Variants
```typescript
import * as React from "react";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
const buttonVariants = cva("inline-flex items-center justify-center...", {
variants: {
variant: { default: "...", destructive: "...", outline: "..." },
size: { default: "...", sm: "...", lg: "..." },
},
defaultVariants: { variant: "default", size: "default" },
});
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
asChild?: boolean;
}
const Button = React.forwardRef<HTMLButtonProps, ButtonProps>(({ className, variant, size, asChild, ...props }, ref) => {
const Comp = asChild ? Slot : "button";
return <Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />;
});
Button.displayName = "Button";
export { Button, buttonVariants };
```
### Feature Component
```typescript
import type { KomgaBook } from "@/types/komga";
interface HomeContentProps {
data: HomeData;
}
export function HomeContent({ data }: HomeContentProps) {
return (
<div className="space-y-12">
{data.ongoing && <MediaRow titleKey="home.sections.continue" items={data.ongoing} />}
</div>
);
}
```
## Naming Conventions
| Type | Convention | Example |
|------|-----------|---------|
| Files | kebab-case | book-cover.tsx |
| Components | PascalCase | BookCover |
| Functions | camelCase | getBookById |
| Types | PascalCase | KomgaBook |
| Database | snake_case | read_progress |
| API Routes | kebab-case | /api/komga/books |
## Code Standards
- TypeScript strict mode enabled
- Zod for request/response validation
- Prisma for all database queries (type-safe)
- Server Components by default, Client Components when needed
- Custom AppError class with error codes
- Structured logging with Pino
- Error responses: `{ error: { code, name, message } }`
## Security Requirements
- Validate all user input with Zod
- Parameterized queries via Prisma (prevents SQL injection)
- Sanitize before rendering (React handles this)
- HTTPS only in production
- Auth via NextAuth v5
- Role-based access control (admin, user)
- API routes protected with session checks
## 📂 Codebase References
**API Routes**: `src/app/api/**/route.ts` - All API endpoints
**Services**: `src/lib/services/*.service.ts` - Business logic layer
**Components**: `src/components/ui/`, `src/components/*/` - UI and feature components
**Types**: `src/types/**` - TypeScript definitions
**Config**: package.json, tsconfig.json, prisma/schema.prisma
## Related Files
- business-domain.md - Business logic and domain model
- decisions-log.md - Architecture decisions