Compare commits

10 Commits

15 changed files with 10414 additions and 5 deletions
+1 -1
View File
@@ -61,7 +61,7 @@ PUBLIC_URL="http://localhost:8055"
# you need to pass to the database instance. # you need to pass to the database instance.
DB_CLIENT="sqlite3" DB_CLIENT="sqlite3"
DB_FILENAME="data.db" DB_FILENAME="./database/data.db"
+169 -1
View File
@@ -1 +1,169 @@
# api.konstitisyon.la # api.konstitisyon.nu
Backend Directus pour konstitisyon.nu, une plateforme de rédaction constitutionnelle collaborative. Ce dépôt contient la configuration Directus et le schéma de la base de données.
## Architecture
### Technologies
- **Backend**: Directus 11.x
- **Base de données**: SQLite
- **Authentification**: JWT via Directus
### Structure du projet
```
api.konstitisyon.nu/
├── extensions/ # Extensions Directus personnalisées
├── uploads/ # Fichiers uploadés
├── data.db # Base de données SQLite
└── .env # Configuration de l'environnement
```
## Modèle de données
### Schéma de la base de données
```mermaid
erDiagram
TITRES ||--o{ ARTICLES : contient
TITRES ||--o{ COMMENTAIRES : discute
TITRES {
uuid id PK
string status
uuid user_created FK
datetime date_created
text contenu
integer numero
}
ARTICLES {
uuid id PK
string status
uuid user_created FK
datetime date_created
text contenu
integer numero
uuid titre FK
}
COMMENTAIRES {
uuid id PK
string status
uuid user_created FK
datetime date_created
text contenu
uuid titre FK
}
VOTES {
uuid id PK
uuid user_created FK
datetime date_created
datetime date_updated
uuid content_version_id FK
integer vote
}
DIRECTUS_VERSIONS ||--o{ VOTES : evalue
DIRECTUS_USERS ||--o{ VOTES : cree
DIRECTUS_USERS ||--o{ COMMENTAIRES : ecrit
DIRECTUS_USERS ||--o{ ARTICLES : redige
DIRECTUS_USERS ||--o{ TITRES : redige
```
### Collections Directus
#### Collections principales
- **Titres**
- Sections principales de la constitution
- Numérotés et versionnés
- Champs : numero, contenu, status
- **Articles**
- Contenus détaillés sous chaque titre
- Liés à un titre parent
- Champs : numero, contenu, titre (FK), status
#### Collections participatives
- **Commentaires**
- Discussions sur les titres
- Liés à un titre spécifique
- Champs : contenu, titre (FK), user_created
- **Votes**
- Évaluation des versions de contenu
- Valeurs : +1 ou -1
- Champs : content_version_id (FK), vote, user_created
## Installation
### Prérequis
- Node.js 16+
- npm ou yarn
- SQLite 3
### Configuration
Copier le fichier `.env.sample` vers un fichier `.env` :
```bash
cp .env.sample .env
```
### Démarrage
```bash
# Installation des dépendances
npm install
# Démarrage du serveur
npx directus start
```
## API
### Points d'entrée principaux
#### Titres
- `GET /items/titres` : Liste des titres
- `GET /items/titres/:id` : Détails d'un titre
- `POST /items/titres` : Créer un titre
- `PATCH /items/titres/:id` : Modifier un titre
#### Articles
- `GET /items/articles` : Liste des articles
- `GET /items/articles/:id` : Détails d'un article
- `POST /items/articles` : Créer un article
- `PATCH /items/articles/:id` : Modifier un article
#### Commentaires
- `GET /items/commentaires` : Liste des commentaires
- `GET /items/commentaires/:id` : Détails d'un commentaire
- `POST /items/commentaires` : Créer un commentaire
#### Votes
- `GET /items/votes` : Liste des votes
- `GET /items/votes/:id` : Détails d'un vote
- `POST /items/votes` : Créer un vote
- `PATCH /items/votes/:id` : Modifier un vote
### Authentification
- `POST /auth/login` : Connexion utilisateur
- `POST /auth/refresh` : Rafraîchissement du token
- `POST /auth/logout` : Déconnexion
### Base de données
Le projet utilise une base de données SQLite. Un fichier `data.sample.db` est fourni avec :
- La structure complète de la base de données
- Un compte administrateur par défaut :
- Email : admin@example.com
- Mot de passe : admin
Pour démarrer un nouveau projet :
1. Copier `data.sample.db` vers `data.db`
- `cp data.sample.db data.db`
2. Mettre à jour le fichier `.env` pour pointer vers `data.db`
3. Démarrer le serveur
## License
Ce projet est sous licence AGPL-3. Cette licence garantit que le code reste libre et que toute modification doit être partagée avec la communauté. Voir le fichier `LICENSE` pour plus de détails.
## Contact
Pour toute question ou suggestion concernant le projet, n'hésitez pas à ouvrir une issue sur Codeberg.
Binary file not shown.
+10
View File
@@ -0,0 +1,10 @@
services:
directus:
image: directus/directus:11.14.0
ports:
- 8055:8055
volumes:
- ./database:/directus/database
- ./uploads:/directus/uploads
- ./extensions:/directus/extensions
env_file: ".env"
@@ -0,0 +1,3 @@
.DS_Store
node_modules
dist
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,29 @@
{
"name": "disallow-votes",
"description": "Interdit les votes sur des versions ayant plus de 3 jours",
"icon": "extension",
"version": "1.0.0",
"keywords": [
"directus",
"directus-extension",
"directus-extension-hook"
],
"type": "module",
"files": [
"dist"
],
"directus:extension": {
"type": "hook",
"path": "dist/index.js",
"source": "src/index.js",
"host": "^10.10.0"
},
"scripts": {
"build": "directus-extension build",
"dev": "directus-extension build -w --no-minify",
"link": "directus-extension link"
},
"devDependencies": {
"@directus/extensions-sdk": "12.1.4"
}
}
@@ -0,0 +1,82 @@
export default ({filter}, {services}) => {
const checkVersionValidity = async (versionId, database, schema) => {
if (!versionId) {
throw new Error('Lidentifiant de la version est manquant.')
}
const version = await database('directus_versions')
.select('date_created')
.where({id: versionId })
.first()
if (!version) {
throw new Error('Version non trouvée.')
}
// Check if version is older than 3 days
const createdAt = new Date(version.date_created)
const threeDaysAgo = new Date(Date.now() - 3 * 24 * 60 * 60 * 1000)
if (createdAt < threeDaysAgo) {
throw new Error(
'Le vote nest plus possible après 3 jours de la création de la version.'
)
}
// Check if version is outdated (has been superseded by a promoted version)
try {
const {VersionsService} = services
const versionsService = new VersionsService({schema, knex: database})
const comparison = await versionsService.compare(versionId)
if (comparison && comparison.outdated === true) {
throw new Error(
'Le vote nest plus possible sur une version obsolète.'
)
}
} catch (error) {
// If it's our custom error, rethrow it
if (error.message === 'Le vote nest plus possible sur une version obsolète.') {
throw error
}
// Otherwise, log and continue (don't block vote if comparison fails)
console.warn('Could not check version outdated status:', error.message)
}
}
filter('items.create', async (input, {collection}, {database, schema}) => {
if (collection === 'votes') {
await checkVersionValidity(input.content_version_id, database, schema)
}
return input
})
filter('items.update', async (input, {collection}, {database, schema}) => {
if (collection === 'votes') {
await checkVersionValidity(input.content_version_id, database, schema)
}
return input
})
filter('items.delete', async (input, {collection}, {database, schema}) => {
if (collection === 'votes') {
const voteId = input[0]
if (!voteId) {
throw new Error('Lidentifiant du vote est manquant.')
}
const vote = await database('votes')
.select('content_version_id')
.where({id: voteId })
.first()
if (!vote) {
throw new Error('Vote non trouvé.')
}
await checkVersionValidity(vote.content_version_id, database, schema)
}
return input
})
}
@@ -0,0 +1,2 @@
EMAIL_NEW_USER=
DIRECTUS_URL="http://0.0.0.0:8055"
@@ -0,0 +1,3 @@
.DS_Store
node_modules
dist
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,29 @@
{
"name": "directus-extension-new-user",
"description": "Envoie un e-mail lors de linscription dun utilisateur",
"icon": "extension",
"version": "1.0.0",
"keywords": [
"directus",
"directus-extension",
"directus-extension-hook"
],
"type": "module",
"files": [
"dist"
],
"directus:extension": {
"type": "hook",
"path": "dist/index.js",
"source": "src/index.js",
"host": "^10.10.0"
},
"scripts": {
"build": "directus-extension build",
"dev": "directus-extension build -w --no-minify",
"link": "directus-extension link"
},
"devDependencies": {
"@directus/extensions-sdk": "12.1.4"
}
}
@@ -0,0 +1,55 @@
export default ({filter}, {services, env}) => {
const checkEmailExists = async (email, database) => {
const user = await database('directus_users')
.select('id')
.where({email})
.first()
return user !== undefined
}
filter('users.create', async (input, {schema}, {database}) => {
if (!services.MailService) {
console.error('Le service MailService est manquant.')
return input
}
const adminEmail = env.EMAIL_NEW_USER
const directusURL = env.DIRECTUS_URL || 'http://0.0.0.0:8055'
if (!adminEmail) {
console.error('La variable EMAIL_NEW_USER est manquante.')
return input
}
const emailExists = await checkEmailExists(input.email, database)
if (emailExists) {
console.error('Ladresse e-mail est déjà utilisée.')
return input
}
try {
const mailService = new services.MailService({schema})
mailService.send({
to: adminEmail,
subject: `Nouvel utilisateur : ${input.email}`,
text: `Un nouvel utilisateur a été créé :\n Nom: ${input.first_name || 'N/A'} Email: ${input.email || 'N/A'}\n Pour valider => ${directusURL}/admin/users`,
html: `
<p>Un nouvel utilisateur a été créé :</p>
<ul>
<li><strong>Nom:</strong> ${input.first_name || 'N/A'} </li>
<li><strong>Email:</strong> ${input.email || 'N/A'}</li>
</ul>
<p>Pour valider, <a href="${directusURL}/admin/users" target="_blank"><strong>cliquez ici</strong></a> ou sur ce lien => <a href="${directusURL}/admin/users" target="_blank">${directusURL}/admin/users</a></p>
`,
})
console.log('Email envoyé avec succès à', adminEmail)
} catch (err) {
console.error("Erreur lors de lenvoi de le-mail via MailService:", err)
}
return input
})
}
+2 -2
View File
@@ -1,11 +1,11 @@
{ {
"name": "api.konstitisyon.la", "name": "api.konstitisyon.nu",
"version": "1.0.0", "version": "1.0.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "api.konstitisyon.la", "name": "api.konstitisyon.nu",
"version": "1.0.0", "version": "1.0.0",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
+1 -1
View File
@@ -1,5 +1,5 @@
{ {
"name": "api.konstitisyon.la", "name": "api.konstitisyon.nu",
"version": "1.0.0", "version": "1.0.0",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {