Compare commits
5 Commits
v0.1.0
...
081a6ecc65
| Author | SHA1 | Date | |
|---|---|---|---|
| 081a6ecc65 | |||
| 5dcee72844 | |||
| ec0d4b296b | |||
| 7a928df73c | |||
| a1c48bb792 |
36
.gitea/workflows/backend-tests.yml
Normal file
36
.gitea/workflows/backend-tests.yml
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
name: Backend Tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- 'backend/**'
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- 'backend/**'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
version: 9
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
- name: Get pnpm store directory
|
||||||
|
id: pnpm-cache
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "STORE_PATH=$(pnpm store path --silent)" >> "${GITEA_OUTPUT:-$GITHUB_OUTPUT}"
|
||||||
|
- uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
||||||
|
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-pnpm-store-
|
||||||
|
- name: Install dependencies
|
||||||
|
run: pnpm install --frozen-lockfile --prefer-offline
|
||||||
|
- name: Run Backend Tests
|
||||||
|
run: pnpm -F @memegoat/backend test
|
||||||
@@ -1,18 +1,13 @@
|
|||||||
# Pipeline CI/CD pour Gitea Actions (Forgejo)
|
name: Deploy to Production
|
||||||
# Compatible avec GitHub Actions pour la portabilité
|
|
||||||
name: CI/CD Pipeline
|
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- '**'
|
- prod
|
||||||
tags:
|
|
||||||
- 'v*'
|
|
||||||
pull_request:
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
validate:
|
validate:
|
||||||
name: Valider ${{ matrix.component }}
|
name: Validate Build & Lint
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
@@ -21,23 +16,23 @@ jobs:
|
|||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Installer pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@v4
|
||||||
with:
|
with:
|
||||||
version: 9
|
version: 9
|
||||||
|
|
||||||
- name: Configurer Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
|
|
||||||
- name: Obtenir le chemin du store pnpm
|
- name: Get pnpm store directory
|
||||||
id: pnpm-cache
|
id: pnpm-cache
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
echo "STORE_PATH=$(pnpm store path --silent)" >> "${GITEA_OUTPUT:-$GITHUB_OUTPUT}"
|
echo "STORE_PATH=$(pnpm store path --silent)" >> "${GITEA_OUTPUT:-$GITHUB_OUTPUT}"
|
||||||
|
|
||||||
- name: Configurer le cache pnpm
|
- name: Setup pnpm cache
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
||||||
@@ -45,43 +40,26 @@ jobs:
|
|||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-pnpm-store-
|
${{ runner.os }}-pnpm-store-
|
||||||
|
|
||||||
- name: Installer les dépendances
|
- name: Install dependencies
|
||||||
run: pnpm install --frozen-lockfile --prefer-offline
|
run: pnpm install --frozen-lockfile --prefer-offline
|
||||||
|
|
||||||
- name: Lint ${{ matrix.component }}
|
- name: Lint ${{ matrix.component }}
|
||||||
run: pnpm -F @memegoat/${{ matrix.component }} lint
|
run: pnpm -F @memegoat/${{ matrix.component }} lint
|
||||||
|
|
||||||
- name: Tester ${{ matrix.component }}
|
|
||||||
if: matrix.component == 'backend' || matrix.component == 'frontend'
|
|
||||||
run: |
|
|
||||||
if pnpm -F @memegoat/${{ matrix.component }} run | grep -q "test"; then
|
|
||||||
pnpm -F @memegoat/${{ matrix.component }} test
|
|
||||||
else
|
|
||||||
echo "Pas de script de test trouvé pour ${{ matrix.component }}, passage."
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Build ${{ matrix.component }}
|
- name: Build ${{ matrix.component }}
|
||||||
run: pnpm -F @memegoat/${{ matrix.component }} build
|
run: pnpm -F @memegoat/${{ matrix.component }} build
|
||||||
env:
|
env:
|
||||||
NEXT_PUBLIC_API_URL: ${{ secrets.NEXT_PUBLIC_API_URL }}
|
NEXT_PUBLIC_API_URL: ${{ secrets.NEXT_PUBLIC_API_URL }}
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
name: Déploiement en Production
|
name: Deploy to Production
|
||||||
needs: validate
|
needs: validate
|
||||||
# Déclenchement uniquement sur push sur main ou tag de version
|
|
||||||
# Gitea supporte le contexte 'github' pour la compatibilité
|
|
||||||
if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v'))
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Vérifier l'environnement Docker
|
- name: Deploy with Docker Compose
|
||||||
run: |
|
|
||||||
docker version
|
|
||||||
docker compose version
|
|
||||||
|
|
||||||
- name: Déployer avec Docker Compose
|
|
||||||
run: |
|
run: |
|
||||||
docker compose -f docker-compose.prod.yml up -d --build
|
docker compose -f docker-compose.prod.yml up -d --build
|
||||||
env:
|
env:
|
||||||
43
.gitea/workflows/lint.yml
Normal file
43
.gitea/workflows/lint.yml
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
name: Lint
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- 'frontend/**'
|
||||||
|
- 'backend/**'
|
||||||
|
- 'documentation/**'
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- 'frontend/**'
|
||||||
|
- 'backend/**'
|
||||||
|
- 'documentation/**'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
component: [backend, frontend, documentation]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
version: 9
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
- name: Get pnpm store directory
|
||||||
|
id: pnpm-cache
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "STORE_PATH=$(pnpm store path --silent)" >> "${GITEA_OUTPUT:-$GITHUB_OUTPUT}"
|
||||||
|
- uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
||||||
|
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-pnpm-store-
|
||||||
|
- name: Install dependencies
|
||||||
|
run: pnpm install --frozen-lockfile --prefer-offline
|
||||||
|
- name: Lint ${{ matrix.component }}
|
||||||
|
run: pnpm -F @memegoat/${{ matrix.component }} lint
|
||||||
50
ROADMAP.md
50
ROADMAP.md
@@ -1,50 +0,0 @@
|
|||||||
# 🐐 Memegoat - Roadmap & Critères de Production
|
|
||||||
|
|
||||||
Ce document définit les objectifs, les critères techniques et les fonctionnalités à atteindre pour que le projet Memegoat soit considéré comme prêt pour la production et conforme aux normes européennes (RGPD) et françaises.
|
|
||||||
|
|
||||||
## 1. 🏗️ Architecture & Infrastructure
|
|
||||||
- [x] Backend NestJS (TypeScript)
|
|
||||||
- [x] Base de données PostgreSQL avec Drizzle ORM
|
|
||||||
- [x] Stockage d'objets compatible S3 (MinIO)
|
|
||||||
- [x] Service d'Emailing (Nodemailer / SMTPS)
|
|
||||||
- [x] Documentation Technique & Référence API (`docs.memegoat.fr`)
|
|
||||||
- [x] Health Checks (`/health`)
|
|
||||||
- [x] Gestion des variables d'environnement (Validation avec Zod)
|
|
||||||
- [ ] CI/CD (Build, Lint, Test, Deploy)
|
|
||||||
|
|
||||||
## 2. 🔐 Sécurité & Authentification
|
|
||||||
- [x] Hachage des mots de passe (Argon2id)
|
|
||||||
- [x] Gestion des sessions robuste (JWT avec Refresh Token et Rotation)
|
|
||||||
- [x] RBAC (Role Based Access Control) fonctionnel
|
|
||||||
- [x] Système de Clés API (Hachées en base)
|
|
||||||
- [x] Double Authentification (2FA / TOTP)
|
|
||||||
- [x] Limitation de débit (Rate Limiting / Throttler)
|
|
||||||
- [x] Validation stricte des entrées (DTOs + ValidationPipe)
|
|
||||||
- [x] Protection contre les vulnérabilités OWASP (Helmet, CORS)
|
|
||||||
|
|
||||||
## 3. ⚖️ Conformité RGPD (EU & France)
|
|
||||||
- [x] Chiffrement natif des données personnelles (PII) via PGP (pgcrypto)
|
|
||||||
- [x] Hachage aveugle (Blind Indexing) pour l'email (recherche/unicité)
|
|
||||||
- [x] Journalisation d'audit complète (Audit Logs) pour les actions sensibles
|
|
||||||
- [x] Gestion du consentement (Versionnage CGU/Politique de Confidentialité)
|
|
||||||
- [x] Droit à l'effacement : Flux de suppression (Soft Delete -> Purge définitive)
|
|
||||||
- [x] Droit à la portabilité : Export des données utilisateur (JSON)
|
|
||||||
- [x] Purge automatique des données obsolètes (Signalements, Sessions expirées)
|
|
||||||
- [x] Anonymisation des adresses IP (Hachage) dans les logs
|
|
||||||
|
|
||||||
## 4. 🖼️ Fonctionnalités Coeur (Media & Galerie)
|
|
||||||
- [x] Exploration (Trends, Recent, Favoris)
|
|
||||||
- [x] Recherche par Tags, Catégories, Auteur, Texte
|
|
||||||
- [x] Gestion des Favoris
|
|
||||||
- [x] Upload sécurisé via S3 (URLs présignées)
|
|
||||||
- [x] Scan Antivirus (ClamAV) et traitement des médias (WebP, WebM, AVIF, AV1)
|
|
||||||
- [x] Limitation de la taille et des formats de fichiers entrants (Configurable)
|
|
||||||
- [x] Système de Signalement (Reports) et workflow de modération
|
|
||||||
- [ ] SEO : Metatags dynamiques et slugs sémantiques
|
|
||||||
|
|
||||||
## 5. ✅ Qualité & Robustesse
|
|
||||||
- [ ] Couverture de tests unitaires (Jest) > 80%
|
|
||||||
- [ ] Tests d'intégration et E2E
|
|
||||||
- [x] Gestion centralisée des erreurs (Filters NestJS)
|
|
||||||
- [ ] Monitoring et centralisation des logs (ex: Sentry, ELK/Loki)
|
|
||||||
- [ ] Performance : Cache (Redis) pour les tendances et recherches fréquentes
|
|
||||||
@@ -82,11 +82,6 @@ Cette page documente tous les points de terminaison disponibles sur l'API Memego
|
|||||||
Récupère les informations détaillées de l'utilisateur connecté. Requiert l'authentification.
|
Récupère les informations détaillées de l'utilisateur connecté. Requiert l'authentification.
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
|
||||||
<Accordion title="GET /users/public/:username">
|
|
||||||
Récupère le profil public d'un utilisateur par son nom d'utilisateur.
|
|
||||||
**Réponse :** `id`, `username`, `displayName`, `avatarUrl`, `createdAt`.
|
|
||||||
</Accordion>
|
|
||||||
|
|
||||||
<Accordion title="GET /users/me/export">
|
<Accordion title="GET /users/me/export">
|
||||||
Extrait l'intégralité des données de l'utilisateur au format JSON (Conformité RGPD).
|
Extrait l'intégralité des données de l'utilisateur au format JSON (Conformité RGPD).
|
||||||
Contient le profil, les contenus et les favoris.
|
Contient le profil, les contenus et les favoris.
|
||||||
@@ -94,22 +89,7 @@ Cette page documente tous les points de terminaison disponibles sur l'API Memego
|
|||||||
|
|
||||||
<Accordion title="PATCH /users/me">
|
<Accordion title="PATCH /users/me">
|
||||||
Met à jour les informations du profil.
|
Met à jour les informations du profil.
|
||||||
**Corps :**
|
|
||||||
- `displayName` (string)
|
- `displayName` (string)
|
||||||
- `bio` (string)
|
|
||||||
</Accordion>
|
|
||||||
|
|
||||||
<Accordion title="POST /users/me/avatar">
|
|
||||||
Met à jour l'avatar de l'utilisateur.
|
|
||||||
**Type :** `multipart/form-data`
|
|
||||||
**Champ :** `file` (Image)
|
|
||||||
</Accordion>
|
|
||||||
|
|
||||||
<Accordion title="PATCH /users/me/consent">
|
|
||||||
Met à jour les consentements légaux de l'utilisateur.
|
|
||||||
**Corps :**
|
|
||||||
- `termsVersion` (string)
|
|
||||||
- `privacyVersion` (string)
|
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
|
||||||
<Accordion title="DELETE /users/me">
|
<Accordion title="DELETE /users/me">
|
||||||
@@ -125,9 +105,9 @@ Cette page documente tous les points de terminaison disponibles sur l'API Memego
|
|||||||
- `POST /users/me/2fa/disable` : Désactive avec jeton.
|
- `POST /users/me/2fa/disable` : Désactive avec jeton.
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
|
||||||
<Accordion title="Administration (Admin uniquement)">
|
<Accordion title="Administration (GET /users/admin)">
|
||||||
- `GET /users/admin` : Liste tous les utilisateurs (avec pagination `limit`, `offset`).
|
Liste tous les utilisateurs. Réservé aux administrateurs.
|
||||||
- `DELETE /users/:uuid` : Supprime définitivement un utilisateur par son UUID.
|
**Params :** `limit`, `offset`.
|
||||||
</Accordion>
|
</Accordion>
|
||||||
</Accordions>
|
</Accordions>
|
||||||
|
|
||||||
@@ -138,15 +118,12 @@ Cette page documente tous les points de terminaison disponibles sur l'API Memego
|
|||||||
Recherche et filtre les contenus. Ces endpoints sont mis en cache (Redis + Navigateur).
|
Recherche et filtre les contenus. Ces endpoints sont mis en cache (Redis + Navigateur).
|
||||||
|
|
||||||
**Query Params :**
|
**Query Params :**
|
||||||
- `limit` (number) : Défaut 10.
|
|
||||||
- `offset` (number) : Défaut 0.
|
|
||||||
- `sort` : `trend` | `recent` (uniquement sur `/explore`)
|
- `sort` : `trend` | `recent` (uniquement sur `/explore`)
|
||||||
- `tag` (string) : Filtrer par tag.
|
- `tag` (string)
|
||||||
- `category` (slug ou id) : Filtrer par catégorie.
|
- `category` (slug ou id)
|
||||||
- `author` (username) : Filtrer par auteur.
|
- `author` (username)
|
||||||
- `query` (titre) : Recherche textuelle.
|
- `query` (titre)
|
||||||
- `favoritesOnly` (bool) : Ne montrer que les favoris de l'utilisateur connecté.
|
- `favoritesOnly` (bool)
|
||||||
- `userId` (uuid) : Filtrer les contenus d'un utilisateur spécifique.
|
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
|
||||||
<Accordion title="GET /contents/:idOrSlug">
|
<Accordion title="GET /contents/:idOrSlug">
|
||||||
@@ -156,13 +133,8 @@ Cette page documente tous les points de terminaison disponibles sur l'API Memego
|
|||||||
Si l'User-Agent correspond à un robot d'indexation (Googlebot, Twitterbot, etc.), l'API retourne un rendu HTML minimal contenant les méta-tags **OpenGraph** et **Twitter Cards** pour un partage optimal. Pour les autres clients, les données sont retournées en JSON.
|
Si l'User-Agent correspond à un robot d'indexation (Googlebot, Twitterbot, etc.), l'API retourne un rendu HTML minimal contenant les méta-tags **OpenGraph** et **Twitter Cards** pour un partage optimal. Pour les autres clients, les données sont retournées en JSON.
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
|
||||||
<Accordion title="POST /contents">
|
|
||||||
Crée une entrée de contenu (sans upload de fichier direct). Utile pour référencer des URLs externes.
|
|
||||||
**Corps :** `title`, `description`, `url`, `type`, `categoryId`, `tags`.
|
|
||||||
</Accordion>
|
|
||||||
|
|
||||||
<Accordion title="POST /contents/upload">
|
<Accordion title="POST /contents/upload">
|
||||||
Upload un fichier avec traitement automatique par le serveur.
|
Upload un fichier avec traitement automatique.
|
||||||
**Type :** `multipart/form-data`
|
**Type :** `multipart/form-data`
|
||||||
|
|
||||||
**Champs :**
|
**Champs :**
|
||||||
@@ -173,11 +145,6 @@ Cette page documente tous les points de terminaison disponibles sur l'API Memego
|
|||||||
- `tags`? : string[]
|
- `tags`? : string[]
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
|
||||||
<Accordion title="POST /contents/upload-url">
|
|
||||||
Génère une URL présignée pour un upload direct vers S3.
|
|
||||||
**Query Param :** `fileName` (string).
|
|
||||||
</Accordion>
|
|
||||||
|
|
||||||
<Accordion title="POST /contents/:id/view | /use">
|
<Accordion title="POST /contents/:id/view | /use">
|
||||||
Incrémente les statistiques de vue ou d'utilisation.
|
Incrémente les statistiques de vue ou d'utilisation.
|
||||||
</Accordion>
|
</Accordion>
|
||||||
@@ -185,10 +152,6 @@ Cette page documente tous les points de terminaison disponibles sur l'API Memego
|
|||||||
<Accordion title="DELETE /contents/:id">
|
<Accordion title="DELETE /contents/:id">
|
||||||
Supprime un contenu (Soft Delete). Doit être l'auteur.
|
Supprime un contenu (Soft Delete). Doit être l'auteur.
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
|
||||||
<Accordion title="DELETE /contents/:id/admin">
|
|
||||||
Supprime définitivement un contenu. **Réservé aux administrateurs.**
|
|
||||||
</Accordion>
|
|
||||||
</Accordions>
|
</Accordions>
|
||||||
|
|
||||||
### 📂 Catégories, ⭐ Favoris, 🚩 Signalements
|
### 📂 Catégories, ⭐ Favoris, 🚩 Signalements
|
||||||
@@ -196,23 +159,19 @@ Cette page documente tous les points de terminaison disponibles sur l'API Memego
|
|||||||
<Accordions>
|
<Accordions>
|
||||||
<Accordion title="Catégories (/categories)">
|
<Accordion title="Catégories (/categories)">
|
||||||
- `GET /categories` : Liste toutes les catégories.
|
- `GET /categories` : Liste toutes les catégories.
|
||||||
- `GET /categories/:id` : Détails d'une catégorie.
|
|
||||||
- `POST /categories` : Création (Admin uniquement).
|
- `POST /categories` : Création (Admin uniquement).
|
||||||
- `PATCH /categories/:id` : Mise à jour (Admin uniquement).
|
|
||||||
- `DELETE /categories/:id` : Suppression (Admin uniquement).
|
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
|
||||||
<Accordion title="Favoris (/favorites)">
|
<Accordion title="Favoris (/favorites)">
|
||||||
Requiert l'authentification.
|
- `GET /favorites` : Liste les favoris de l'utilisateur.
|
||||||
- `GET /favorites` : Liste les favoris de l'utilisateur (avec pagination `limit`, `offset`).
|
|
||||||
- `POST /favorites/:contentId` : Ajoute un favori.
|
- `POST /favorites/:contentId` : Ajoute un favori.
|
||||||
- `DELETE /favorites/:contentId` : Retire un favori.
|
- `DELETE /favorites/:contentId` : Retire un favori.
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
|
||||||
<Accordion title="Signalements (/reports)">
|
<Accordion title="Signalements (/reports)">
|
||||||
- `POST /reports` : Signale un contenu ou un tag.
|
- `POST /reports` : Signale un contenu ou un tag.
|
||||||
- `GET /reports` : Liste des signalements (Pagination `limit`, `offset`). **Admin/Modérateurs**.
|
- `GET /reports` : Liste (Modérateurs).
|
||||||
- `PATCH /reports/:id/status` : Change le statut (`pending`, `resolved`, `dismissed`). **Admin/Modérateurs**.
|
- `PATCH /reports/:id/status` : Gère le workflow.
|
||||||
</Accordion>
|
</Accordion>
|
||||||
</Accordions>
|
</Accordions>
|
||||||
|
|
||||||
@@ -226,23 +185,7 @@ Cette page documente tous les points de terminaison disponibles sur l'API Memego
|
|||||||
</Accordion>
|
</Accordion>
|
||||||
|
|
||||||
<Accordion title="Tags (/tags)">
|
<Accordion title="Tags (/tags)">
|
||||||
- `GET /tags` : Recherche de tags.
|
- `GET /tags` : Recherche de tags populaires ou récents.
|
||||||
- **Params :** `query` (recherche), `sort` (`popular` | `recent`), `limit`, `offset`.
|
**Params :** `query`, `sort`, `limit`.
|
||||||
</Accordion>
|
|
||||||
</Accordions>
|
|
||||||
|
|
||||||
### 🛠️ Système & Médias
|
|
||||||
|
|
||||||
<Accordions>
|
|
||||||
<Accordion title="Santé (/health)">
|
|
||||||
- `GET /health` : Vérifie l'état de l'API et de la connexion à la base de données.
|
|
||||||
</Accordion>
|
|
||||||
|
|
||||||
<Accordion title="Médias (/media)">
|
|
||||||
- `GET /media/*key` : Accès direct aux fichiers stockés sur S3. Supporte la mise en cache agressive.
|
|
||||||
</Accordion>
|
|
||||||
|
|
||||||
<Accordion title="Administration (/admin)">
|
|
||||||
- `GET /admin/stats` : Récupère les statistiques globales de la plateforme. **Admin uniquement**.
|
|
||||||
</Accordion>
|
</Accordion>
|
||||||
</Accordions>
|
</Accordions>
|
||||||
|
|||||||
@@ -20,13 +20,6 @@ Le système utilise plusieurs méthodes d'authentification sécurisées pour ré
|
|||||||
<Card title="Double Authentification" description="Support TOTP natif avec secret chiffré PGP pour une sécurité maximale." />
|
<Card title="Double Authentification" description="Support TOTP natif avec secret chiffré PGP pour une sécurité maximale." />
|
||||||
</Cards>
|
</Cards>
|
||||||
|
|
||||||
### Stockage & Médias (S3)
|
### Webhooks / Services Externes
|
||||||
|
|
||||||
Memegoat utilise une architecture de stockage d'objets compatible S3 (MinIO). Les interactions se font de deux manières :
|
Liste des intégrations tierces.
|
||||||
|
|
||||||
1. **Proxification Backend** : Pour l'accès public via `/media/*`.
|
|
||||||
2. **URLs Présignées** : Pour l'upload sécurisé direct depuis le client (via `/contents/upload-url`).
|
|
||||||
|
|
||||||
### Notifications (Mail)
|
|
||||||
|
|
||||||
Le système intègre un service d'envoi d'emails (SMTP) pour les notifications critiques et la gestion des comptes.
|
|
||||||
|
|||||||
@@ -35,13 +35,10 @@ erDiagram
|
|||||||
string username
|
string username
|
||||||
string email
|
string email
|
||||||
string display_name
|
string display_name
|
||||||
string avatar_url
|
|
||||||
string bio
|
|
||||||
string status
|
string status
|
||||||
}
|
}
|
||||||
CONTENT {
|
CONTENT {
|
||||||
string title
|
string title
|
||||||
string slug
|
|
||||||
string type
|
string type
|
||||||
string storage_key
|
string storage_key
|
||||||
}
|
}
|
||||||
@@ -85,8 +82,6 @@ erDiagram
|
|||||||
bytea email
|
bytea email
|
||||||
varchar email_hash
|
varchar email_hash
|
||||||
varchar display_name
|
varchar display_name
|
||||||
varchar avatar_url
|
|
||||||
varchar bio
|
|
||||||
varchar password_hash
|
varchar password_hash
|
||||||
user_status status
|
user_status status
|
||||||
bytea two_factor_secret
|
bytea two_factor_secret
|
||||||
@@ -105,7 +100,6 @@ erDiagram
|
|||||||
uuid category_id FK
|
uuid category_id FK
|
||||||
content_type type
|
content_type type
|
||||||
varchar title
|
varchar title
|
||||||
varchar slug
|
|
||||||
varchar storage_key
|
varchar storage_key
|
||||||
varchar mime_type
|
varchar mime_type
|
||||||
integer file_size
|
integer file_size
|
||||||
@@ -239,8 +233,6 @@ erDiagram
|
|||||||
varchar email_hash "UNIQUE, INDEXED"
|
varchar email_hash "UNIQUE, INDEXED"
|
||||||
varchar username "UNIQUE, NOT NULL"
|
varchar username "UNIQUE, NOT NULL"
|
||||||
varchar password_hash "NOT NULL"
|
varchar password_hash "NOT NULL"
|
||||||
varchar avatar_url "NULLABLE"
|
|
||||||
varchar bio "NULLABLE"
|
|
||||||
bytea two_factor_secret "ENCRYPTED"
|
bytea two_factor_secret "ENCRYPTED"
|
||||||
boolean is_two_factor_enabled "DEFAULT false"
|
boolean is_two_factor_enabled "DEFAULT false"
|
||||||
timestamp gdpr_accepted_at "NULLABLE"
|
timestamp gdpr_accepted_at "NULLABLE"
|
||||||
@@ -249,7 +241,6 @@ erDiagram
|
|||||||
contents {
|
contents {
|
||||||
uuid id "DEFAULT gen_random_uuid()"
|
uuid id "DEFAULT gen_random_uuid()"
|
||||||
uuid user_id "REFERENCES users(uuid)"
|
uuid user_id "REFERENCES users(uuid)"
|
||||||
varchar slug "UNIQUE, NOT NULL"
|
|
||||||
varchar storage_key "UNIQUE, NOT NULL"
|
varchar storage_key "UNIQUE, NOT NULL"
|
||||||
integer file_size "NOT NULL"
|
integer file_size "NOT NULL"
|
||||||
timestamp deleted_at "SOFT DELETE"
|
timestamp deleted_at "SOFT DELETE"
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
Réalisation du frontend :
|
|
||||||
|
|
||||||
# Exigences
|
|
||||||
|
|
||||||
- Responsive dans tout les formats tailwindcss
|
|
||||||
- Accessibilité A11Y
|
|
||||||
- Implémentation réel uniquement
|
|
||||||
- Site en français
|
|
||||||
- SEO parfaitement réalisé, robot.txt, sitemap.xml...
|
|
||||||
- Utilisation des composants shadcn/ui
|
|
||||||
- Réalisation d'une page d'erreur customisé
|
|
||||||
- Utilisation des fonctionalités de NextJS suivantes :
|
|
||||||
- Nested routes
|
|
||||||
- Dynamic routes
|
|
||||||
- Route groups
|
|
||||||
- Private folders
|
|
||||||
- Parralel and intercepted routes
|
|
||||||
- Prefetching pages
|
|
||||||
- Streaming pages
|
|
||||||
- Server and Client Components
|
|
||||||
- Cache Components
|
|
||||||
- Image optimization
|
|
||||||
- Incremental Static Regeneration
|
|
||||||
- Custom hooks
|
|
||||||
- Axios
|
|
||||||
|
|
||||||
Toute l'application est basé sur un système dashboard/sidebar intégrant le routing.
|
|
||||||
La page principale est la page de navigation du contennu.
|
|
||||||
En mode desktop nous retrouvons la sidebar à gauche, le contennu en scroll infini au milieu et les paramètres de recherche sur la droite.
|
|
||||||
En mode mobile la sidebar est replié, les paramètres de recherche sont représenté comme une icône de filtrage flotante en haut à droite
|
|
||||||
@@ -3,11 +3,6 @@
|
|||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"description": "",
|
"description": "",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"version:get": "cmake -P version.cmake GET",
|
|
||||||
"version:set": "cmake -P version.cmake SET",
|
|
||||||
"v:patch": "cmake -P version.cmake PATCH",
|
|
||||||
"v:minor": "cmake -P version.cmake MINOR",
|
|
||||||
"v:major": "cmake -P version.cmake MAJOR",
|
|
||||||
"build": "pnpm run build:back && pnpm run build:front && pnpm run build:docs",
|
"build": "pnpm run build:back && pnpm run build:front && pnpm run build:docs",
|
||||||
"build:front": "pnpm run -F @memegoat/frontend build",
|
"build:front": "pnpm run -F @memegoat/frontend build",
|
||||||
"build:back": "pnpm run -F @memegoat/backend build",
|
"build:back": "pnpm run -F @memegoat/backend build",
|
||||||
|
|||||||
109
version.cmake
109
version.cmake
@@ -1,109 +0,0 @@
|
|||||||
# version.cmake - Script pour gérer la version SemVer de manière centralisée
|
|
||||||
|
|
||||||
# Usage: cmake -P version.cmake [GET|SET|PATCH|MINOR|MAJOR] [new_version]
|
|
||||||
|
|
||||||
set(PACKAGE_JSON_FILES
|
|
||||||
"${CMAKE_CURRENT_LIST_DIR}/package.json"
|
|
||||||
"${CMAKE_CURRENT_LIST_DIR}/backend/package.json"
|
|
||||||
"${CMAKE_CURRENT_LIST_DIR}/frontend/package.json"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Fonction pour lire la version depuis le package.json racine
|
|
||||||
function(get_current_version OUT_VAR)
|
|
||||||
file(READ "${CMAKE_CURRENT_LIST_DIR}/package.json" ROOT_JSON)
|
|
||||||
string(JSON CURRENT_VERSION GET "${ROOT_JSON}" "version")
|
|
||||||
set(${OUT_VAR} ${CURRENT_VERSION} PARENT_SCOPE)
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
# Fonction pour incrémenter la version SemVer
|
|
||||||
function(increment_version CURRENT_VERSION TYPE OUT_VAR)
|
|
||||||
string(REPLACE "." ";" VERSION_LIST ${CURRENT_VERSION})
|
|
||||||
list(GET VERSION_LIST 0 MAJOR)
|
|
||||||
list(GET VERSION_LIST 1 MINOR)
|
|
||||||
list(GET VERSION_LIST 2 PATCH)
|
|
||||||
|
|
||||||
if("${TYPE}" STREQUAL "MAJOR")
|
|
||||||
math(EXPR MAJOR "${MAJOR} + 1")
|
|
||||||
set(MINOR 0)
|
|
||||||
set(PATCH 0)
|
|
||||||
elseif("${TYPE}" STREQUAL "MINOR")
|
|
||||||
math(EXPR MINOR "${MINOR} + 1")
|
|
||||||
set(PATCH 0)
|
|
||||||
elseif("${TYPE}" STREQUAL "PATCH")
|
|
||||||
math(EXPR PATCH "${PATCH} + 1")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(${OUT_VAR} "${MAJOR}.${MINOR}.${PATCH}" PARENT_SCOPE)
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
# Fonction pour créer un tag git
|
|
||||||
function(create_git_tag VERSION)
|
|
||||||
find_package(Git QUIET)
|
|
||||||
if(GIT_FOUND)
|
|
||||||
execute_process(
|
|
||||||
COMMAND ${GIT_EXECUTABLE} tag -a "v${VERSION}" -m "Release v${VERSION}"
|
|
||||||
WORKING_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}"
|
|
||||||
RESULT_VARIABLE TAG_RESULT
|
|
||||||
)
|
|
||||||
if(TAG_RESULT EQUAL 0)
|
|
||||||
message(STATUS "Tag v${VERSION} créé avec succès")
|
|
||||||
else()
|
|
||||||
message(WARNING "Échec de la création du tag v${VERSION}. Il existe peut-être déjà.")
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
message(WARNING "Git non trouvé, impossible de créer le tag.")
|
|
||||||
endif()
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
# Fonction pour mettre à jour la version dans tous les fichiers package.json
|
|
||||||
function(set_new_version NEW_VERSION)
|
|
||||||
foreach(JSON_FILE ${PACKAGE_JSON_FILES})
|
|
||||||
if(EXISTS "${JSON_FILE}")
|
|
||||||
message(STATUS "Mise à jour de ${JSON_FILE} vers la version ${NEW_VERSION}")
|
|
||||||
file(READ "${JSON_FILE}" CONTENT)
|
|
||||||
# Utilisation de string(JSON ...) pour modifier la version si disponible (CMake >= 3.19)
|
|
||||||
# Sinon on peut utiliser une regex simple pour package.json
|
|
||||||
string(REGEX REPLACE "\"version\": \"[^\"]+\"" "\"version\": \"${NEW_VERSION}\"" NEW_CONTENT "${CONTENT}")
|
|
||||||
file(WRITE "${JSON_FILE}" "${NEW_CONTENT}")
|
|
||||||
else()
|
|
||||||
message(WARNING "Fichier non trouvé: ${JSON_FILE}")
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
# Demander à l'utilisateur s'il veut tagger (ou le faire par défaut si spécifié)
|
|
||||||
create_git_tag(${NEW_VERSION})
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
# Logique principale
|
|
||||||
set(ARG_OFFSET 0)
|
|
||||||
while(ARG_OFFSET LESS CMAKE_ARGC)
|
|
||||||
if("${CMAKE_ARGV${ARG_OFFSET}}" STREQUAL "-P")
|
|
||||||
math(EXPR COMMAND_INDEX "${ARG_OFFSET} + 2")
|
|
||||||
math(EXPR VERSION_INDEX "${ARG_OFFSET} + 3")
|
|
||||||
break()
|
|
||||||
endif()
|
|
||||||
math(EXPR ARG_OFFSET "${ARG_OFFSET} + 1")
|
|
||||||
endwhile()
|
|
||||||
|
|
||||||
if(NOT DEFINED COMMAND_INDEX OR COMMAND_INDEX GREATER_EQUAL CMAKE_ARGC)
|
|
||||||
message(FATAL_ERROR "Usage: cmake -P version.cmake [GET|SET|PATCH|MINOR|MAJOR] [new_version]")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(COMMAND "${CMAKE_ARGV${COMMAND_INDEX}}")
|
|
||||||
|
|
||||||
if("${COMMAND}" STREQUAL "GET")
|
|
||||||
get_current_version(VERSION)
|
|
||||||
message("${VERSION}")
|
|
||||||
elseif("${COMMAND}" STREQUAL "SET")
|
|
||||||
if(VERSION_INDEX GREATER_EQUAL CMAKE_ARGC)
|
|
||||||
message(FATAL_ERROR "Veuillez spécifier la nouvelle version: cmake -P version.cmake SET 0.0.0")
|
|
||||||
endif()
|
|
||||||
set(NEW_VERSION "${CMAKE_ARGV${VERSION_INDEX}}")
|
|
||||||
set_new_version("${NEW_VERSION}")
|
|
||||||
elseif("${COMMAND}" MATCHES "^(PATCH|MINOR|MAJOR)$")
|
|
||||||
get_current_version(CURRENT_VERSION)
|
|
||||||
increment_version("${CURRENT_VERSION}" "${COMMAND}" NEW_VERSION)
|
|
||||||
set_new_version("${NEW_VERSION}")
|
|
||||||
else()
|
|
||||||
message(FATAL_ERROR "Commande inconnue: ${COMMAND}. Utilisez GET, SET, PATCH, MINOR ou MAJOR.")
|
|
||||||
endif()
|
|
||||||
Reference in New Issue
Block a user