12 Commits

Author SHA1 Message Date
Mathis HERRIOT
2450977e61 chore(versioning): bump package versions to 0.1.0 across all modules
Some checks failed
CI/CD Pipeline / Valider backend (push) Failing after 1m12s
CI/CD Pipeline / Valider documentation (push) Successful in 1m41s
CI/CD Pipeline / Valider frontend (push) Successful in 1m43s
CI/CD Pipeline / Déploiement en Production (push) Has been skipped
2026-01-20 12:00:35 +01:00
Mathis HERRIOT
afc18b555a chore(versioning): improve version script with enhanced validation and refactored command handling 2026-01-20 11:57:24 +01:00
Mathis HERRIOT
9699127739 feat(docs): add details on S3-compatible storage and email notifications system
Some checks failed
CI/CD Pipeline / Valider backend (push) Failing after 1m0s
CI/CD Pipeline / Valider documentation (push) Successful in 1m39s
CI/CD Pipeline / Valider frontend (push) Successful in 1m33s
CI/CD Pipeline / Déploiement en Production (push) Has been skipped
2026-01-20 11:50:29 +01:00
Mathis HERRIOT
938d8bde7b feat(docs): extend database schema documentation with new fields avatar_url, bio, and slug 2026-01-20 11:50:18 +01:00
Mathis HERRIOT
65c7096f46 feat(docs): update API reference with new endpoints and extended parameter details 2026-01-20 11:49:47 +01:00
Mathis HERRIOT
57c00ad4d1 chore(ci): remove deprecated deploy workflow and update CI pipeline with production deployment steps 2026-01-20 11:21:23 +01:00
Mathis HERRIOT
39618f7708 chore(versioning): bump package versions to 0.0.1 across all modules
Some checks failed
Deploy to Production / Validate Build & Lint (backend) (push) Failing after 1m12s
Deploy to Production / Validate Build & Lint (documentation) (push) Successful in 1m46s
Deploy to Production / Validate Build & Lint (frontend) (push) Successful in 1m46s
Deploy to Production / Deploy to Production (push) Has been skipped
2026-01-20 10:53:11 +01:00
Mathis HERRIOT
e84e4a5a9d chore(ci): add new workflow for linting and testing components 2026-01-20 10:52:58 +01:00
Mathis HERRIOT
e74973a9d0 chore(ci): update deploy workflow to include tag-based triggers and conditional testing steps 2026-01-20 10:52:48 +01:00
Mathis HERRIOT
9233c1bf89 feat(versioning): add support for SemVer increment commands (PATCH, MINOR, MAJOR) in version script 2026-01-20 10:52:41 +01:00
Mathis HERRIOT
88c7f45a2c chore(ci): remove unused backend and lint workflows to clean up repository 2026-01-20 10:52:27 +01:00
Mathis HERRIOT
9af72156f5 chore: remove unused .output.txt file to clean up repository 2026-01-20 10:51:48 +01:00
12 changed files with 185 additions and 361 deletions

View File

@@ -1,36 +0,0 @@
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

View File

@@ -1,13 +1,18 @@
name: Deploy to Production # Pipeline CI/CD pour Gitea Actions (Forgejo)
# Compatible avec GitHub Actions pour la portabilité
name: CI/CD Pipeline
on: on:
push: push:
branches: branches:
- main - '**'
tags:
- 'v*'
pull_request:
jobs: jobs:
validate: validate:
name: Validate Build & Lint name: Valider ${{ matrix.component }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
@@ -16,23 +21,23 @@ jobs:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Install pnpm - name: Installer pnpm
uses: pnpm/action-setup@v4 uses: pnpm/action-setup@v4
with: with:
version: 9 version: 9
- name: Setup Node.js - name: Configurer Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version: 20 node-version: 20
- name: Get pnpm store directory - name: Obtenir le chemin du store pnpm
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: Setup pnpm cache - name: Configurer le cache pnpm
uses: actions/cache@v4 uses: actions/cache@v4
with: with:
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
@@ -40,26 +45,43 @@ jobs:
restore-keys: | restore-keys: |
${{ runner.os }}-pnpm-store- ${{ runner.os }}-pnpm-store-
- name: Install dependencies - name: Installer les dépendances
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: Deploy to Production name: Déploiement en 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: Deploy with Docker Compose - name: Vérifier l'environnement Docker
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:

View File

@@ -1,43 +0,0 @@
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

View File

@@ -1,225 +0,0 @@
{
"name": "@memegoat/source",
"version": "0.0.1",
"description": "",
"scripts": {
"build": "pnpm run build:back && pnpm run build:front && pnpm run build:docs",
"build:front": "pnpm run -F @memegoat/frontend build",
"build:back": "pnpm run -F @memegoat/backend build",
"build:docs": "pnpm run -F @memegoat/documentation build",
"lint": "pnpm run lint:back && pnpm run lint:front && pnpm run lint:docs",
"lint:back": "pnpm run -F @memegoat/backend lint",
"lint:front": "pnpm run -F @memegoat/frontend lint",
"lint:docs": "pnpm run -F @memegoat/documentation lint",
"test": "pnpm run test:back && pnpm run test:front",
"test:back": "pnpm run -F @memegoat/backend test",
"test:front": "pnpm run -F @memegoat/frontend test",
"format": "pnpm run format:back && pnpm run format:front && pnpm run format:docs",
"format:back": "pnpm run -F @memegoat/backend format",
"format:front": "pnpm run -F @memegoat/frontend format",
"format:docs": "pnpm run -F @memegoat/documentation format",
"upgrade": "pnpm dlx taze minor"
},
"keywords": [],
"author": {
"name": "Mathis HERRIOT",
"email": "mherriot.pro@proton.me",
"role": "Author"
},
"license": "AGPL-3.0-only",
"devDependencies": {
"@biomejs/biome": "2.3.11"
}
}
{
"name": "@memegoat/backend",
"version": "0.0.1",
"description": "",
"author": "",
"private": true,
"license": "UNLICENSED",
"files": [
"dist",
".migrations",
"drizzle.config.ts"
],
"scripts": {
"build": "nest build",
"lint": "biome check",
"lint:write": "biome check --write",
"format": "biome format --write",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json",
"db:generate": "drizzle-kit generate",
"db:migrate": "drizzle-kit migrate",
"db:studio": "drizzle-kit studio"
},
"dependencies": {
"@nestjs-modules/mailer": "^2.0.2",
"@nestjs/cache-manager": "^3.1.0",
"@nestjs/common": "^11.0.1",
"@nestjs/config": "^4.0.2",
"@nestjs/core": "^11.0.1",
"@nestjs/mapped-types": "^2.1.0",
"@nestjs/platform-express": "^11.0.1",
"@nestjs/schedule": "^6.1.0",
"@nestjs/throttler": "^6.5.0",
"@noble/post-quantum": "^0.5.4",
"@node-rs/argon2": "^2.0.2",
"@sentry/nestjs": "^10.32.1",
"@sentry/profiling-node": "^10.32.1",
"cache-manager": "^7.2.7",
"cache-manager-redis-yet": "^5.1.5",
"clamscan": "^2.4.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.3",
"dotenv": "^17.2.3",
"drizzle-orm": "^0.45.1",
"fluent-ffmpeg": "^2.1.3",
"helmet": "^8.1.0",
"iron-session": "^8.0.4",
"jose": "^6.1.3",
"minio": "^8.0.6",
"nodemailer": "^7.0.12",
"otplib": "^12.0.1",
"pg": "^8.16.3",
"qrcode": "^1.5.4",
"reflect-metadata": "^0.2.2",
"rxjs": "^7.8.1",
"sharp": "^0.34.5",
"uuid": "^13.0.0",
"zod": "^4.3.5",
"drizzle-kit": "^0.31.8"
},
"devDependencies": {
"@nestjs/cli": "^11.0.0",
"globals": "^16.0.0",
"jest": "^30.0.0",
"source-map-support": "^0.5.21",
"supertest": "^7.0.0",
"ts-jest": "^29.2.5",
"ts-loader": "^9.5.2",
"ts-node": "^10.9.2",
"tsconfig-paths": "^4.2.0",
"tsx": "^4.21.0",
"typescript": "^5.7.3",
"typescript-eslint": "^8.20.0",
"@nestjs/schematics": "^11.0.0",
"@nestjs/testing": "^11.0.1",
"@types/express": "^5.0.0",
"@types/fluent-ffmpeg": "^2.1.28",
"@types/jest": "^30.0.0",
"@types/multer": "^2.0.0",
"@types/node": "^22.10.7",
"@types/nodemailer": "^7.0.4",
"@types/pg": "^8.16.0",
"@types/qrcode": "^1.5.6",
"@types/sharp": "^0.32.0",
"@types/supertest": "^6.0.2",
"@types/uuid": "^11.0.0",
"drizzle-kit": "^0.31.8"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node",
"transformIgnorePatterns": [
"node_modules/(?!(.pnpm/)?(jose|@noble|uuid)/)"
],
"transform": {
"^.+\\.(t|j)sx?$": "ts-jest"
},
"moduleNameMapper": {
"^@noble/post-quantum/(.*)$": "<rootDir>/../node_modules/@noble/post-quantum/$1",
"^@noble/hashes/(.*)$": "<rootDir>/../node_modules/@noble/hashes/$1"
}
}
}
{
"name": "@memegoat/frontend",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "biome check",
"format": "biome format --write"
},
"dependencies": {
"@hookform/resolvers": "^5.2.2",
"@radix-ui/react-accordion": "^1.2.12",
"@radix-ui/react-alert-dialog": "^1.1.15",
"@radix-ui/react-aspect-ratio": "^1.1.8",
"@radix-ui/react-avatar": "^1.1.11",
"@radix-ui/react-checkbox": "^1.3.3",
"@radix-ui/react-collapsible": "^1.1.12",
"@radix-ui/react-context-menu": "^2.2.16",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-hover-card": "^1.1.15",
"@radix-ui/react-label": "^2.1.8",
"@radix-ui/react-menubar": "^1.1.16",
"@radix-ui/react-navigation-menu": "^1.2.14",
"@radix-ui/react-popover": "^1.1.15",
"@radix-ui/react-progress": "^1.1.8",
"@radix-ui/react-radio-group": "^1.3.8",
"@radix-ui/react-scroll-area": "^1.2.10",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-separator": "^1.1.8",
"@radix-ui/react-slider": "^1.3.6",
"@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-switch": "^1.2.6",
"@radix-ui/react-tabs": "^1.1.13",
"@radix-ui/react-toggle": "^1.1.10",
"@radix-ui/react-toggle-group": "^1.1.11",
"@radix-ui/react-tooltip": "^1.2.8",
"axios": "^1.13.2",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
"date-fns": "^4.1.0",
"embla-carousel-react": "^8.6.0",
"input-otp": "^1.4.2",
"lucide-react": "^0.562.0",
"next": "16.1.1",
"next-themes": "^0.4.6",
"react": "19.2.3",
"react-day-picker": "^9.13.0",
"react-dom": "19.2.3",
"react-hook-form": "^7.71.1",
"react-resizable-panels": "^4.4.1",
"recharts": "2.15.4",
"sonner": "^2.0.7",
"tailwind-merge": "^3.4.0",
"vaul": "^1.1.2",
"zod": "^4.3.5"
},
"devDependencies": {
"@biomejs/biome": "2.3.11",
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"babel-plugin-react-compiler": "1.0.0",
"tailwindcss": "^4",
"tw-animate-css": "^1.4.0",
"typescript": "^5"
}
}

View File

@@ -1,6 +1,6 @@
{ {
"name": "@memegoat/backend", "name": "@memegoat/backend",
"version": "0.0.0", "version": "0.1.0",
"description": "", "description": "",
"author": "", "author": "",
"private": true, "private": true,

View File

@@ -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. 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.
@@ -89,7 +94,22 @@ 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">
@@ -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. - `POST /users/me/2fa/disable` : Désactive avec jeton.
</Accordion> </Accordion>
<Accordion title="Administration (GET /users/admin)"> <Accordion title="Administration (Admin uniquement)">
Liste tous les utilisateurs. Réservé aux administrateurs. - `GET /users/admin` : Liste tous les utilisateurs (avec pagination `limit`, `offset`).
**Params :** `limit`, `offset`. - `DELETE /users/:uuid` : Supprime définitivement un utilisateur par son UUID.
</Accordion> </Accordion>
</Accordions> </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). 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) - `tag` (string) : Filtrer par tag.
- `category` (slug ou id) - `category` (slug ou id) : Filtrer par catégorie.
- `author` (username) - `author` (username) : Filtrer par auteur.
- `query` (titre) - `query` (titre) : Recherche textuelle.
- `favoritesOnly` (bool) - `favoritesOnly` (bool) : Ne montrer que les favoris de l'utilisateur connecté.
- `userId` (uuid) : Filtrer les contenus d'un utilisateur spécifique.
</Accordion> </Accordion>
<Accordion title="GET /contents/:idOrSlug"> <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. 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. Upload un fichier avec traitement automatique par le serveur.
**Type :** `multipart/form-data` **Type :** `multipart/form-data`
**Champs :** **Champs :**
@@ -145,6 +173,11 @@ 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>
@@ -152,6 +185,10 @@ 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
@@ -159,19 +196,23 @@ 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)">
- `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. - `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 (Modérateurs). - `GET /reports` : Liste des signalements (Pagination `limit`, `offset`). **Admin/Modérateurs**.
- `PATCH /reports/:id/status` : Gère le workflow. - `PATCH /reports/:id/status` : Change le statut (`pending`, `resolved`, `dismissed`). **Admin/Modérateurs**.
</Accordion> </Accordion>
</Accordions> </Accordions>
@@ -185,7 +226,23 @@ 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 populaires ou récents. - `GET /tags` : Recherche de tags.
**Params :** `query`, `sort`, `limit`. - **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> </Accordion>
</Accordions> </Accordions>

View File

@@ -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." /> <Card title="Double Authentification" description="Support TOTP natif avec secret chiffré PGP pour une sécurité maximale." />
</Cards> </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.

View File

@@ -35,10 +35,13 @@ 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
} }
@@ -82,6 +85,8 @@ 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
@@ -100,6 +105,7 @@ 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
@@ -233,6 +239,8 @@ 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"
@@ -241,6 +249,7 @@ 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"

View File

@@ -1,6 +1,6 @@
{ {
"name": "@memegoat/documentation", "name": "@memegoat/documentation",
"version": "0.0.1", "version": "0.1.0",
"private": true, "private": true,
"scripts": { "scripts": {
"build": "next build", "build": "next build",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@memegoat/frontend", "name": "@memegoat/frontend",
"version": "0.0.0", "version": "0.1.0",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev",

View File

@@ -1,10 +1,13 @@
{ {
"name": "@memegoat/source", "name": "@memegoat/source",
"version": "0.0.0", "version": "0.1.0",
"description": "", "description": "",
"scripts": { "scripts": {
"version:get": "cmake -P version.cmake GET", "version:get": "cmake -P version.cmake GET",
"version:set": "cmake -P version.cmake SET", "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",

View File

@@ -1,6 +1,6 @@
# version.cmake - Script pour gérer la version SemVer de manière centralisée # version.cmake - Script pour gérer la version SemVer de manière centralisée
# Usage: cmake -P version.cmake [GET|SET] [new_version] # Usage: cmake -P version.cmake [GET|SET|PATCH|MINOR|MAJOR] [new_version]
set(PACKAGE_JSON_FILES set(PACKAGE_JSON_FILES
"${CMAKE_CURRENT_LIST_DIR}/package.json" "${CMAKE_CURRENT_LIST_DIR}/package.json"
@@ -15,6 +15,30 @@ function(get_current_version OUT_VAR)
set(${OUT_VAR} ${CURRENT_VERSION} PARENT_SCOPE) set(${OUT_VAR} ${CURRENT_VERSION} PARENT_SCOPE)
endfunction() endfunction()
# Fonction pour incrémenter la version SemVer
function(increment_version CURRENT_VERSION TYPE OUT_VAR)
if(CURRENT_VERSION MATCHES "^([0-9]+)\\.([0-9]+)\\.([0-9]+)")
set(MAJOR ${CMAKE_MATCH_1})
set(MINOR ${CMAKE_MATCH_2})
set(PATCH ${CMAKE_MATCH_3})
else()
message(FATAL_ERROR "Format de version invalide: ${CURRENT_VERSION}. Attendu: X.Y.Z")
endif()
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 # Fonction pour créer un tag git
function(create_git_tag VERSION) function(create_git_tag VERSION)
find_package(Git QUIET) find_package(Git QUIET)
@@ -49,36 +73,42 @@ function(set_new_version NEW_VERSION)
endif() endif()
endforeach() endforeach()
# Demander à l'utilisateur s'il veut tagger (ou le faire par défaut si spécifié) # Créer le tag git
create_git_tag(${NEW_VERSION}) create_git_tag(${NEW_VERSION})
endfunction() endfunction()
# Logique principale # Logique principale
set(ARG_OFFSET 0) if(CMAKE_SCRIPT_MODE_FILE STREQUAL CMAKE_CURRENT_LIST_FILE)
while(ARG_OFFSET LESS CMAKE_ARGC) set(ARG_OFFSET 0)
if("${CMAKE_ARGV${ARG_OFFSET}}" STREQUAL "-P") while(ARG_OFFSET LESS CMAKE_ARGC)
math(EXPR COMMAND_INDEX "${ARG_OFFSET} + 2") if("${CMAKE_ARGV${ARG_OFFSET}}" STREQUAL "-P")
math(EXPR VERSION_INDEX "${ARG_OFFSET} + 3") math(EXPR COMMAND_INDEX "${ARG_OFFSET} + 2")
break() 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() endif()
math(EXPR ARG_OFFSET "${ARG_OFFSET} + 1")
endwhile() set(COMMAND "${CMAKE_ARGV${COMMAND_INDEX}}")
if(NOT DEFINED COMMAND_INDEX OR COMMAND_INDEX GREATER_EQUAL CMAKE_ARGC) if("${COMMAND}" STREQUAL "GET")
message(FATAL_ERROR "Usage: cmake -P version.cmake [GET|SET] [new_version]") get_current_version(VERSION)
endif() message("${VERSION}")
elseif("${COMMAND}" STREQUAL "SET")
set(COMMAND "${CMAKE_ARGV${COMMAND_INDEX}}") if(VERSION_INDEX GREATER_EQUAL CMAKE_ARGC)
message(FATAL_ERROR "Veuillez spécifier la nouvelle version: cmake -P version.cmake SET 0.0.0")
if("${COMMAND}" STREQUAL "GET") endif()
get_current_version(VERSION) set(NEW_VERSION "${CMAKE_ARGV${VERSION_INDEX}}")
message("${VERSION}") set_new_version("${NEW_VERSION}")
elseif("${COMMAND}" STREQUAL "SET") elseif("${COMMAND}" MATCHES "^(PATCH|MINOR|MAJOR)$")
if(VERSION_INDEX GREATER_EQUAL CMAKE_ARGC) get_current_version(CURRENT_VERSION)
message(FATAL_ERROR "Veuillez spécifier la nouvelle version: cmake -P version.cmake SET 0.0.0") 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() endif()
set(NEW_VERSION "${CMAKE_ARGV${VERSION_INDEX}}")
set_new_version("${NEW_VERSION}")
else()
message(FATAL_ERROR "Commande inconnue: ${COMMAND}. Utilisez GET ou SET.")
endif() endif()