Compare commits
4 Commits
39618f7708
...
v0.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9699127739
|
||
|
|
938d8bde7b
|
||
|
|
65c7096f46
|
||
|
|
57c00ad4d1
|
@@ -1,24 +1,18 @@
|
||||
name: CI (Lint & Test)
|
||||
# Pipeline CI/CD pour Gitea Actions (Forgejo)
|
||||
# Compatible avec GitHub Actions pour la portabilité
|
||||
name: CI/CD Pipeline
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- main
|
||||
tags-ignore:
|
||||
branches:
|
||||
- '**'
|
||||
tags:
|
||||
- 'v*'
|
||||
paths:
|
||||
- 'backend/**'
|
||||
- 'frontend/**'
|
||||
- 'documentation/**'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'backend/**'
|
||||
- 'frontend/**'
|
||||
- 'documentation/**'
|
||||
|
||||
jobs:
|
||||
validate:
|
||||
name: Validate ${{ matrix.component }}
|
||||
name: Valider ${{ matrix.component }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -27,23 +21,23 @@ jobs:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install pnpm
|
||||
- name: Installer pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9
|
||||
|
||||
- name: Setup Node.js
|
||||
- name: Configurer Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: Get pnpm store directory
|
||||
- name: Obtenir le chemin du store pnpm
|
||||
id: pnpm-cache
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> "${GITEA_OUTPUT:-$GITHUB_OUTPUT}"
|
||||
|
||||
- name: Setup pnpm cache
|
||||
- name: Configurer le cache pnpm
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
||||
@@ -51,17 +45,67 @@ jobs:
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
|
||||
- name: Install dependencies
|
||||
- name: Installer les dépendances
|
||||
run: pnpm install --frozen-lockfile --prefer-offline
|
||||
|
||||
- name: Lint ${{ matrix.component }}
|
||||
run: pnpm -F @memegoat/${{ matrix.component }} lint
|
||||
|
||||
- name: Test ${{ matrix.component }}
|
||||
- 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 "No test script found for ${{ matrix.component }}, skipping."
|
||||
echo "Pas de script de test trouvé pour ${{ matrix.component }}, passage."
|
||||
fi
|
||||
|
||||
- name: Build ${{ matrix.component }}
|
||||
run: pnpm -F @memegoat/${{ matrix.component }} build
|
||||
env:
|
||||
NEXT_PUBLIC_API_URL: ${{ secrets.NEXT_PUBLIC_API_URL }}
|
||||
|
||||
deploy:
|
||||
name: Déploiement en Production
|
||||
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
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Vérifier l'environnement Docker
|
||||
run: |
|
||||
docker version
|
||||
docker compose version
|
||||
|
||||
- name: Déployer avec Docker Compose
|
||||
run: |
|
||||
docker compose -f docker-compose.prod.yml up -d --build
|
||||
env:
|
||||
BACKEND_PORT: ${{ secrets.BACKEND_PORT }}
|
||||
FRONTEND_PORT: ${{ secrets.FRONTEND_PORT }}
|
||||
POSTGRES_HOST: ${{ secrets.POSTGRES_HOST }}
|
||||
POSTGRES_PORT: ${{ secrets.POSTGRES_PORT }}
|
||||
POSTGRES_USER: ${{ secrets.POSTGRES_USER }}
|
||||
POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }}
|
||||
POSTGRES_DB: ${{ secrets.POSTGRES_DB }}
|
||||
REDIS_HOST: ${{ secrets.REDIS_HOST }}
|
||||
REDIS_PORT: ${{ secrets.REDIS_PORT }}
|
||||
S3_ENDPOINT: ${{ secrets.S3_ENDPOINT }}
|
||||
S3_PORT: ${{ secrets.S3_PORT }}
|
||||
S3_ACCESS_KEY: ${{ secrets.S3_ACCESS_KEY }}
|
||||
S3_SECRET_KEY: ${{ secrets.S3_SECRET_KEY }}
|
||||
S3_BUCKET_NAME: ${{ secrets.S3_BUCKET_NAME }}
|
||||
JWT_SECRET: ${{ secrets.JWT_SECRET }}
|
||||
ENCRYPTION_KEY: ${{ secrets.ENCRYPTION_KEY }}
|
||||
PGP_ENCRYPTION_KEY: ${{ secrets.PGP_ENCRYPTION_KEY }}
|
||||
SESSION_PASSWORD: ${{ secrets.SESSION_PASSWORD }}
|
||||
MAIL_HOST: ${{ secrets.MAIL_HOST }}
|
||||
MAIL_PASS: ${{ secrets.MAIL_PASS }}
|
||||
MAIL_USER: ${{ secrets.MAIL_USER }}
|
||||
MAIL_FROM: ${{ secrets.MAIL_FROM }}
|
||||
DOMAIN_NAME: ${{ secrets.DOMAIN_NAME }}
|
||||
NEXT_PUBLIC_API_URL: ${{ secrets.NEXT_PUBLIC_API_URL }}
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
name: Deploy to Production
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
validate:
|
||||
name: Validate Build & Lint
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
component: [backend, frontend, documentation]
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9
|
||||
|
||||
- name: Setup Node.js
|
||||
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}"
|
||||
|
||||
- name: Setup pnpm cache
|
||||
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
|
||||
|
||||
- name: Test ${{ 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 "No test script found for ${{ matrix.component }}, skipping."
|
||||
fi
|
||||
|
||||
- name: Build ${{ matrix.component }}
|
||||
run: pnpm -F @memegoat/${{ matrix.component }} build
|
||||
env:
|
||||
NEXT_PUBLIC_API_URL: ${{ secrets.NEXT_PUBLIC_API_URL }}
|
||||
|
||||
deploy:
|
||||
name: Deploy to Production
|
||||
needs: validate
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Deploy with Docker Compose
|
||||
run: |
|
||||
docker compose -f docker-compose.prod.yml up -d --build
|
||||
env:
|
||||
BACKEND_PORT: ${{ secrets.BACKEND_PORT }}
|
||||
FRONTEND_PORT: ${{ secrets.FRONTEND_PORT }}
|
||||
POSTGRES_HOST: ${{ secrets.POSTGRES_HOST }}
|
||||
POSTGRES_PORT: ${{ secrets.POSTGRES_PORT }}
|
||||
POSTGRES_USER: ${{ secrets.POSTGRES_USER }}
|
||||
POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }}
|
||||
POSTGRES_DB: ${{ secrets.POSTGRES_DB }}
|
||||
REDIS_HOST: ${{ secrets.REDIS_HOST }}
|
||||
REDIS_PORT: ${{ secrets.REDIS_PORT }}
|
||||
S3_ENDPOINT: ${{ secrets.S3_ENDPOINT }}
|
||||
S3_PORT: ${{ secrets.S3_PORT }}
|
||||
S3_ACCESS_KEY: ${{ secrets.S3_ACCESS_KEY }}
|
||||
S3_SECRET_KEY: ${{ secrets.S3_SECRET_KEY }}
|
||||
S3_BUCKET_NAME: ${{ secrets.S3_BUCKET_NAME }}
|
||||
JWT_SECRET: ${{ secrets.JWT_SECRET }}
|
||||
ENCRYPTION_KEY: ${{ secrets.ENCRYPTION_KEY }}
|
||||
PGP_ENCRYPTION_KEY: ${{ secrets.PGP_ENCRYPTION_KEY }}
|
||||
SESSION_PASSWORD: ${{ secrets.SESSION_PASSWORD }}
|
||||
MAIL_HOST: ${{ secrets.MAIL_HOST }}
|
||||
MAIL_PASS: ${{ secrets.MAIL_PASS }}
|
||||
MAIL_USER: ${{ secrets.MAIL_USER }}
|
||||
MAIL_FROM: ${{ secrets.MAIL_FROM }}
|
||||
DOMAIN_NAME: ${{ secrets.DOMAIN_NAME }}
|
||||
NEXT_PUBLIC_API_URL: ${{ secrets.NEXT_PUBLIC_API_URL }}
|
||||
@@ -82,6 +82,11 @@ 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.
|
||||
</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">
|
||||
Extrait l'intégralité des données de l'utilisateur au format JSON (Conformité RGPD).
|
||||
Contient le profil, les contenus et les favoris.
|
||||
@@ -89,7 +94,22 @@ Cette page documente tous les points de terminaison disponibles sur l'API Memego
|
||||
|
||||
<Accordion title="PATCH /users/me">
|
||||
Met à jour les informations du profil.
|
||||
**Corps :**
|
||||
- `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 title="DELETE /users/me">
|
||||
@@ -105,9 +125,9 @@ Cette page documente tous les points de terminaison disponibles sur l'API Memego
|
||||
- `POST /users/me/2fa/disable` : Désactive avec jeton.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Administration (GET /users/admin)">
|
||||
Liste tous les utilisateurs. Réservé aux administrateurs.
|
||||
**Params :** `limit`, `offset`.
|
||||
<Accordion title="Administration (Admin uniquement)">
|
||||
- `GET /users/admin` : Liste tous les utilisateurs (avec pagination `limit`, `offset`).
|
||||
- `DELETE /users/:uuid` : Supprime définitivement un utilisateur par son UUID.
|
||||
</Accordion>
|
||||
</Accordions>
|
||||
|
||||
@@ -118,12 +138,15 @@ 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).
|
||||
|
||||
**Query Params :**
|
||||
- `limit` (number) : Défaut 10.
|
||||
- `offset` (number) : Défaut 0.
|
||||
- `sort` : `trend` | `recent` (uniquement sur `/explore`)
|
||||
- `tag` (string)
|
||||
- `category` (slug ou id)
|
||||
- `author` (username)
|
||||
- `query` (titre)
|
||||
- `favoritesOnly` (bool)
|
||||
- `tag` (string) : Filtrer par tag.
|
||||
- `category` (slug ou id) : Filtrer par catégorie.
|
||||
- `author` (username) : Filtrer par auteur.
|
||||
- `query` (titre) : Recherche textuelle.
|
||||
- `favoritesOnly` (bool) : Ne montrer que les favoris de l'utilisateur connecté.
|
||||
- `userId` (uuid) : Filtrer les contenus d'un utilisateur spécifique.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="GET /contents/:idOrSlug">
|
||||
@@ -133,8 +156,13 @@ 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.
|
||||
</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">
|
||||
Upload un fichier avec traitement automatique.
|
||||
Upload un fichier avec traitement automatique par le serveur.
|
||||
**Type :** `multipart/form-data`
|
||||
|
||||
**Champs :**
|
||||
@@ -145,6 +173,11 @@ Cette page documente tous les points de terminaison disponibles sur l'API Memego
|
||||
- `tags`? : string[]
|
||||
</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">
|
||||
Incrémente les statistiques de vue ou d'utilisation.
|
||||
</Accordion>
|
||||
@@ -152,6 +185,10 @@ Cette page documente tous les points de terminaison disponibles sur l'API Memego
|
||||
<Accordion title="DELETE /contents/:id">
|
||||
Supprime un contenu (Soft Delete). Doit être l'auteur.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="DELETE /contents/:id/admin">
|
||||
Supprime définitivement un contenu. **Réservé aux administrateurs.**
|
||||
</Accordion>
|
||||
</Accordions>
|
||||
|
||||
### 📂 Catégories, ⭐ Favoris, 🚩 Signalements
|
||||
@@ -159,19 +196,23 @@ Cette page documente tous les points de terminaison disponibles sur l'API Memego
|
||||
<Accordions>
|
||||
<Accordion title="Catégories (/categories)">
|
||||
- `GET /categories` : Liste toutes les catégories.
|
||||
- `GET /categories/:id` : Détails d'une catégorie.
|
||||
- `POST /categories` : Création (Admin uniquement).
|
||||
- `PATCH /categories/:id` : Mise à jour (Admin uniquement).
|
||||
- `DELETE /categories/:id` : Suppression (Admin uniquement).
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Favoris (/favorites)">
|
||||
- `GET /favorites` : Liste les favoris de l'utilisateur.
|
||||
Requiert l'authentification.
|
||||
- `GET /favorites` : Liste les favoris de l'utilisateur (avec pagination `limit`, `offset`).
|
||||
- `POST /favorites/:contentId` : Ajoute un favori.
|
||||
- `DELETE /favorites/:contentId` : Retire un favori.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Signalements (/reports)">
|
||||
- `POST /reports` : Signale un contenu ou un tag.
|
||||
- `GET /reports` : Liste (Modérateurs).
|
||||
- `PATCH /reports/:id/status` : Gère le workflow.
|
||||
- `GET /reports` : Liste des signalements (Pagination `limit`, `offset`). **Admin/Modérateurs**.
|
||||
- `PATCH /reports/:id/status` : Change le statut (`pending`, `resolved`, `dismissed`). **Admin/Modérateurs**.
|
||||
</Accordion>
|
||||
</Accordions>
|
||||
|
||||
@@ -185,7 +226,23 @@ Cette page documente tous les points de terminaison disponibles sur l'API Memego
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Tags (/tags)">
|
||||
- `GET /tags` : Recherche de tags populaires ou récents.
|
||||
**Params :** `query`, `sort`, `limit`.
|
||||
- `GET /tags` : Recherche de tags.
|
||||
- **Params :** `query` (recherche), `sort` (`popular` | `recent`), `limit`, `offset`.
|
||||
</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>
|
||||
</Accordions>
|
||||
|
||||
@@ -20,6 +20,13 @@ 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." />
|
||||
</Cards>
|
||||
|
||||
### Webhooks / Services Externes
|
||||
### Stockage & Médias (S3)
|
||||
|
||||
Liste des intégrations tierces.
|
||||
Memegoat utilise une architecture de stockage d'objets compatible S3 (MinIO). Les interactions se font de deux manières :
|
||||
|
||||
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,10 +35,13 @@ erDiagram
|
||||
string username
|
||||
string email
|
||||
string display_name
|
||||
string avatar_url
|
||||
string bio
|
||||
string status
|
||||
}
|
||||
CONTENT {
|
||||
string title
|
||||
string slug
|
||||
string type
|
||||
string storage_key
|
||||
}
|
||||
@@ -82,6 +85,8 @@ erDiagram
|
||||
bytea email
|
||||
varchar email_hash
|
||||
varchar display_name
|
||||
varchar avatar_url
|
||||
varchar bio
|
||||
varchar password_hash
|
||||
user_status status
|
||||
bytea two_factor_secret
|
||||
@@ -100,6 +105,7 @@ erDiagram
|
||||
uuid category_id FK
|
||||
content_type type
|
||||
varchar title
|
||||
varchar slug
|
||||
varchar storage_key
|
||||
varchar mime_type
|
||||
integer file_size
|
||||
@@ -233,6 +239,8 @@ erDiagram
|
||||
varchar email_hash "UNIQUE, INDEXED"
|
||||
varchar username "UNIQUE, NOT NULL"
|
||||
varchar password_hash "NOT NULL"
|
||||
varchar avatar_url "NULLABLE"
|
||||
varchar bio "NULLABLE"
|
||||
bytea two_factor_secret "ENCRYPTED"
|
||||
boolean is_two_factor_enabled "DEFAULT false"
|
||||
timestamp gdpr_accepted_at "NULLABLE"
|
||||
@@ -241,6 +249,7 @@ erDiagram
|
||||
contents {
|
||||
uuid id "DEFAULT gen_random_uuid()"
|
||||
uuid user_id "REFERENCES users(uuid)"
|
||||
varchar slug "UNIQUE, NOT NULL"
|
||||
varchar storage_key "UNIQUE, NOT NULL"
|
||||
integer file_size "NOT NULL"
|
||||
timestamp deleted_at "SOFT DELETE"
|
||||
|
||||
Reference in New Issue
Block a user