Files
Portfolio-Game/docs/implementation-artifacts/1-1-initialisation-monorepo-infrastructure.md
skycel ec1ae92799 🎉 Init monorepo Nuxt 4 + Laravel 12 (Story 1.1)
Setup complet de l'infrastructure projet :
- Frontend Nuxt 4 (SSR, TypeScript, i18n, Pinia, TailwindCSS)
- Backend Laravel 12 API-only avec middleware X-API-Key et CORS
- Design tokens (sky-dark, sky-accent, sky-text) et polices (Merriweather, Inter)
- Documentation planning et implementation artifacts

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 02:08:56 +01:00

422 lines
14 KiB
Markdown

# Story 1.1: Initialisation du monorepo et infrastructure
Status: review
## Story
As a développeur,
I want un projet monorepo Nuxt 4 + Laravel 12 initialisé avec les configurations de base,
so that le développement peut commencer sur des fondations solides.
## Acceptance Criteria
1. **Given** un nouveau repository Git **When** le projet est initialisé **Then** la structure monorepo `frontend/` (Nuxt 4) + `api/` (Laravel 12) est en place
2. **And** Nuxt 4 est configuré avec SSR activé, TypeScript, et les modules `@nuxtjs/i18n`, `@nuxtjs/tailwindcss`, `@pinia/nuxt`, `nuxt/image`, `@nuxtjs/sitemap`
3. **And** Laravel 12 est configuré en mode API-only avec CORS autorisant le domaine frontend
4. **And** le middleware API Key (`X-API-Key`) est en place sur les routes API
5. **And** les fichiers `.env.example` existent pour frontend et backend
6. **And** TailwindCSS est configuré avec les design tokens (`sky-dark`, `sky-accent` #fa784f, `sky-text`)
7. **And** les polices sont définies (serif narrateur + sans-serif UI)
8. **And** le `.gitignore` est approprié pour les deux applications
## Tasks / Subtasks
- [x] **Task 1: Initialisation structure monorepo** (AC: #1)
- [x] Créer le dossier racine `skycel/` avec README.md
- [x] Configurer `.gitignore` global (node_modules, vendor, .env, etc.)
- [x] Initialiser Git repository
- [x] **Task 2: Setup Frontend Nuxt 4** (AC: #1, #2)
- [x] Exécuter `npx nuxi@latest init frontend`
- [x] Confirmer structure Nuxt 4 avec dossier `app/`
- [x] Activer TypeScript (déjà par défaut dans Nuxt 4)
- [x] Installer modules: `@nuxtjs/i18n`, `@nuxtjs/tailwindcss`, `@pinia/nuxt`, `@nuxt/image`, `@nuxtjs/sitemap`
- [x] Configurer `nuxt.config.ts` avec SSR activé
- [x] Créer `frontend/.env.example`
- [x] **Task 3: Setup Backend Laravel 12** (AC: #1, #3, #4)
- [x] Exécuter `composer create-project laravel/laravel api`
- [x] Configurer en mode API-only (supprimer views Blade inutiles)
- [x] Configurer CORS dans `config/cors.php` pour autoriser le domaine frontend
- [x] Créer middleware `VerifyApiKey` pour vérifier header `X-API-Key`
- [x] Enregistrer le middleware sur les routes API
- [x] Créer `api/.env.example`
- [x] **Task 4: Configuration TailwindCSS avec design tokens** (AC: #6)
- [x] Configurer `tailwind.config.js` avec thème custom
- [x] Définir tokens couleurs: `sky-dark` (noir→bleu), `sky-accent` (#fa784f), `sky-text` (blanc cassé)
- [x] Définir variantes hover/focus pour l'accent
- [x] Configurer purge pour production
- [x] **Task 5: Configuration des polices** (AC: #7)
- [x] Choisir police serif élégante pour narrateur/PNJ (ex: Merriweather, Lora, Playfair Display)
- [x] Choisir police sans-serif moderne pour UI (ex: Inter, Open Sans, Nunito)
- [x] Configurer les polices dans `tailwind.config.js` (fontFamily)
- [x] Importer les polices via Google Fonts ou fichiers locaux
- [x] **Task 6: Fichiers .env.example** (AC: #5)
- [x] `frontend/.env.example` avec: `NUXT_PUBLIC_API_URL`, `NUXT_PUBLIC_API_KEY`
- [x] `api/.env.example` avec: `APP_KEY`, `DB_*`, `API_KEY`, `CORS_ALLOWED_ORIGINS`
- [x] **Task 7: Validation finale** (AC: tous)
- [x] `cd frontend && npm run dev` fonctionne
- [x] `cd api && php artisan serve` fonctionne
- [x] Requête API avec header `X-API-Key` valide retourne 200
- [x] Requête API sans header retourne 401
- [x] Structure des dossiers conforme
## Dev Notes
### Architecture Monorepo
```
skycel/
├── frontend/ # Application Nuxt 4
│ ├── app/ # Code applicatif (structure Nuxt 4)
│ │ ├── pages/
│ │ ├── components/
│ │ ├── composables/
│ │ ├── stores/
│ │ ├── layouts/
│ │ ├── plugins/
│ │ ├── assets/
│ │ └── app.vue
│ ├── server/ # Server routes/API Nuxt (si besoin)
│ ├── public/
│ ├── i18n/
│ ├── nuxt.config.ts
│ ├── tailwind.config.js
│ ├── .env.example
│ └── package.json
├── api/ # Backend Laravel 12
│ ├── app/
│ │ ├── Http/
│ │ │ ├── Controllers/
│ │ │ ├── Middleware/
│ │ │ │ └── VerifyApiKey.php # CRÉER
│ │ │ ├── Requests/
│ │ │ └── Resources/
│ │ └── Models/
│ ├── database/
│ ├── routes/
│ │ └── api.php
│ ├── config/
│ │ └── cors.php # CONFIGURER
│ ├── bootstrap/
│ │ └── app.php # Enregistrer middleware
│ ├── .env.example
│ └── composer.json
├── docs/ # Documentation projet (existe déjà)
├── .gitignore
└── README.md
```
### Commandes d'initialisation
```bash
# Frontend Nuxt 4
npx nuxi@latest init frontend
cd frontend
npm install @nuxtjs/i18n @nuxtjs/tailwindcss @pinia/nuxt @nuxt/image @nuxtjs/sitemap pinia-plugin-persistedstate
# Backend Laravel 12
composer create-project laravel/laravel api
cd api
# Pas de packages supplémentaires pour cette story
```
### Configuration nuxt.config.ts
```typescript
// frontend/nuxt.config.ts
export default defineNuxtConfig({
devtools: { enabled: true },
// SSR activé (défaut)
ssr: true,
// Structure Nuxt 4
future: {
compatibilityVersion: 4,
},
modules: [
'@nuxtjs/i18n',
'@nuxtjs/tailwindcss',
'@pinia/nuxt',
'@nuxt/image',
'@nuxtjs/sitemap',
],
// i18n sera configuré en Story 1.3
i18n: {
locales: ['fr', 'en'],
defaultLocale: 'fr',
strategy: 'prefix_except_default',
},
// Transitions de page (configuré en Story 1.4)
app: {
pageTransition: { name: 'page', mode: 'out-in' },
},
runtimeConfig: {
public: {
apiUrl: process.env.NUXT_PUBLIC_API_URL || 'http://localhost:8000/api',
apiKey: process.env.NUXT_PUBLIC_API_KEY || '',
},
},
})
```
### Design Tokens TailwindCSS
```javascript
// frontend/tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./app/**/*.{vue,js,ts}',
'./components/**/*.{vue,js,ts}',
'./layouts/**/*.{vue,js,ts}',
'./pages/**/*.{vue,js,ts}',
],
theme: {
extend: {
colors: {
'sky-dark': {
DEFAULT: '#0a0e1a', // Noir tirant vers le bleu
50: '#1a1f2e',
100: '#151a28',
200: '#10141f',
300: '#0c1019',
400: '#080c14',
500: '#0a0e1a', // Base
600: '#060810',
700: '#04060c',
800: '#020408',
900: '#010204',
},
'sky-accent': {
DEFAULT: '#fa784f', // Orange chaud
hover: '#fb8c68',
active: '#f96436',
50: '#fff4f0',
100: '#ffe8e0',
200: '#ffd1c1',
300: '#ffb9a2',
400: '#fca283',
500: '#fa784f', // Base
600: '#e86940',
700: '#d65a31',
800: '#c44b22',
900: '#b23c13',
},
'sky-text': {
DEFAULT: '#f5f0e6', // Blanc cassé tirant vers jaune
muted: '#b8b3a8',
50: '#fdfcfa',
100: '#fbf9f5',
200: '#f7f3eb',
300: '#f5f0e6', // Base
400: '#e8e3d9',
500: '#dbd6cc',
600: '#cec9bf',
700: '#c1bcb2',
800: '#b4afa5',
900: '#a7a298',
},
},
fontFamily: {
// Police narrative (serif) - pour narrateur, PNJ, dialogues
'narrative': ['Merriweather', 'Georgia', 'serif'],
// Police UI (sans-serif) - pour interface, boutons, labels
'ui': ['Inter', 'system-ui', 'sans-serif'],
},
},
},
plugins: [],
}
```
### Middleware Laravel VerifyApiKey
```php
<?php
// api/app/Http/Middleware/VerifyApiKey.php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class VerifyApiKey
{
public function handle(Request $request, Closure $next): Response
{
$apiKey = $request->header('X-API-Key');
if (!$apiKey || $apiKey !== config('app.api_key')) {
return response()->json([
'error' => [
'code' => 'INVALID_API_KEY',
'message' => 'Invalid or missing API key',
]
], 401);
}
return $next($request);
}
}
```
### Configuration CORS Laravel
```php
<?php
// api/config/cors.php
return [
'paths' => ['api/*'],
'allowed_methods' => ['*'],
'allowed_origins' => explode(',', env('CORS_ALLOWED_ORIGINS', 'http://localhost:3000')),
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => false,
];
```
### Variables d'environnement
**frontend/.env.example:**
```env
# API Configuration
NUXT_PUBLIC_API_URL=http://localhost:8000/api
NUXT_PUBLIC_API_KEY=your-api-key-here
# Site URL (for sitemap, SEO)
NUXT_PUBLIC_SITE_URL=http://localhost:3000
```
**api/.env.example:**
```env
APP_NAME=Skycel
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost:8000
# Database
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=skycel
DB_USERNAME=root
DB_PASSWORD=
# API Security
API_KEY=your-api-key-here
# CORS
CORS_ALLOWED_ORIGINS=http://localhost:3000
```
### Project Structure Notes
- **Nuxt 4** utilise la nouvelle structure `app/` (pas `src/`)
- Les composants dans `app/components/` sont auto-importés
- Les composables dans `app/composables/` sont auto-importés
- Les stores Pinia dans `app/stores/` sont accessibles via auto-import
- Composants client-only: utiliser le suffixe `.client.vue` (pas de SSR)
### References
- [Source: docs/planning-artifacts/architecture.md#Starter-Template-Evaluation]
- [Source: docs/planning-artifacts/architecture.md#Structure-Monorepo]
- [Source: docs/planning-artifacts/architecture.md#Authentication-&-Security]
- [Source: docs/planning-artifacts/ux-design-specification.md#Color-System]
- [Source: docs/planning-artifacts/ux-design-specification.md#Typography-System]
- [Source: docs/planning-artifacts/epics.md#Story-1.1]
### Technical Requirements
| Requirement | Value | Source |
|-------------|-------|--------|
| Nuxt version | 4.x (latest) | Architecture |
| Laravel version | 12.x | Architecture |
| Node.js | 18+ | Nuxt 4 requirement |
| PHP | 8.2+ | Laravel 12 requirement |
| TypeScript | Enabled | Architecture |
| SSR | Enabled | Architecture, NFR5 |
### Libraries to Install
**Frontend (npm):**
| Package | Version | Purpose |
|---------|---------|---------|
| @nuxtjs/i18n | 8.x | Internationalisation |
| @nuxtjs/tailwindcss | 6.x | Styling |
| @pinia/nuxt | 0.5.x | State management |
| @nuxt/image | 1.x | Image optimization |
| @nuxtjs/sitemap | 5.x | SEO sitemap |
| pinia-plugin-persistedstate | 3.x | LocalStorage persistence |
**Backend (composer):**
- Aucun package supplémentaire pour cette story (Laravel de base suffit)
## Dev Agent Record
### Agent Model Used
Claude Opus 4.5 (claude-opus-4-5-20251101)
### Debug Log References
- TailwindCSS: Le module `@nuxtjs/tailwindcss` v6.14 provoquait une erreur PostCSS `Cannot use 'import.meta' outside a module` sur Node.js 18. Résolu en remplaçant le module par une configuration PostCSS directe dans `nuxt.config.ts`.
- PHP: Le PHP en PATH (8.0.3) est incompatible avec Laravel 12. Utilisation de PHP 8.2.29 disponible dans Laragon pour la création du projet et l'exécution.
- pinia-plugin-persistedstate: La v4 requiert pinia 3+, incompatible avec @pinia/nuxt 0.9.0 (pinia 2). Downgrade vers v3.2.
### Completion Notes List
- Structure monorepo `frontend/` + `api/` créée et fonctionnelle
- Nuxt 4 (3.17.5) configuré avec SSR, TypeScript, i18n, Pinia, @nuxt/image, sitemap
- TailwindCSS v3 configuré via PostCSS avec design tokens (sky-dark, sky-accent, sky-text)
- Polices Merriweather (narrative) et Inter (UI) importées via Google Fonts
- Laravel 12.50 installé en mode API-only avec CORS et middleware VerifyApiKey
- Middleware API Key vérifié : 401 sans clé, 200 avec clé valide
- Fichiers .env.example créés pour frontend et backend
### Change Log
| Date | Change | Author |
|------|--------|--------|
| 2026-02-03 | Story créée avec contexte complet | SM Agent |
| 2026-02-05 | Implémentation complète de toutes les tâches (Tasks 1-7) | Dev Agent (Claude Opus 4.5) |
### File List
**Nouveaux fichiers :**
- `README.md` - Documentation racine du monorepo
- `.gitignore` - Gitignore global
- `frontend/package.json` - Dependencies Nuxt 4
- `frontend/nuxt.config.ts` - Configuration Nuxt 4 avec SSR, modules, PostCSS
- `frontend/tsconfig.json` - Config TypeScript
- `frontend/tailwind.config.js` - Design tokens TailwindCSS
- `frontend/app/app.vue` - Composant racine Vue
- `frontend/app/pages/index.vue` - Page d'accueil placeholder
- `frontend/app/assets/css/main.css` - CSS global avec import polices
- `frontend/.env.example` - Variables d'environnement frontend
- `api/` - Projet Laravel 12 complet (via composer create-project)
- `api/app/Http/Middleware/VerifyApiKey.php` - Middleware authentification API Key
- `api/config/cors.php` - Configuration CORS
- `api/routes/api.php` - Routes API avec endpoint /health
**Fichiers modifiés :**
- `api/bootstrap/app.php` - Routing API-only, enregistrement middleware VerifyApiKey
- `api/config/app.php` - Ajout config api_key
- `api/.env.example` - Ajout APP_NAME=Skycel, DB config MySQL, API_KEY, CORS_ALLOWED_ORIGINS
- `api/.env` - Mêmes ajouts que .env.example avec valeurs dev
- `api/routes/web.php` - Vidé (mode API-only)