Imrpove migrations

This commit is contained in:
julien committed 2026-11-02 19:32:48 +01:00
1 parent f386499d2f
commit 4f28a59c7e
7 files changed
+187 -88

No files matched your search

+27 -7
View File
@@ -3,8 +3,11 @@
## À faire ## À faire
- limiter le spam sur `/contact/send` - limiter le spam sur `/contact/send`
- développer les comptes utilisateurs et leur interface - développer l'inscription des utilisateurs et la vérification des adresses électroniques
- afficher les recettes et dépenses sur la page d'accueil - développer les demandes d'adhésion et leur examen par le Noyau
- développer l'espace personnel des adhérents
- afficher les recettes et les dépenses sur la page d'accueil
- gérer les accès aux services selon la situation des adhérents
## Développement ## Développement
@@ -24,12 +27,22 @@ SMTP_PASSWORD=...
Générer `SESSION_SECRET` avec `openssl rand -hex 64`. Générer `SESSION_SECRET` avec `openssl rand -hex 64`.
Ne pas versionner le fichier `.env`. Ne pas versionner le fichier `.env`.
Appliquer les migrations : Créer ou mettre à jour la base de données :
```sh ```sh
bundle exec ruby bin/migrate bundle exec ruby bin/migrate
``` ```
Lors de la première installation, créer le compte du premier administrateur :
```sh
bundle exec ruby bin/initialize-admin
```
Cette commande est à exécuter une seule fois. Elle crée un compte vérifié et
lui attribue les droits techniques d'administration. Elle ne crée pas de
demande d'adhésion.
Démarrer le serveur de développement : Démarrer le serveur de développement :
```sh ```sh
@@ -44,12 +57,19 @@ bundle exec irb -r dotenv/load -r ./netig.rb
## Production ## Production
Docker Compose fournit les variables d'environnement via `env_file` Docker Compose fournit les variables d'environnement via `env_file` et définit
et définit `RACK_ENV=production`. `RACK_ENV=production`.
Exécuter les migrations dans le conteneur applicatif avant de Exécuter les migrations dans le conteneur applicatif avant de démarrer la
démarrer la nouvelle version : nouvelle version :
```sh ```sh
bundle exec ruby bin/migrate bundle exec ruby bin/migrate
``` ```
Lors de la première installation, créer le premier administrateur depuis le
conteneur applicatif :
```sh
bundle exec ruby bin/initialize-admin
```
+10
View File
@@ -42,6 +42,12 @@ abort "Opération annulée." unless %w[o oui].include?(
console.gets&.strip&.downcase console.gets&.strip&.downcase
) )
verified_status_id = DB[:account_statuses]
.where(name: "Verified")
.get(:id)
abort "Statut Verified introuvable." unless verified_status_id
auth = Rodauth.lib do auth = Rodauth.lib do
db DB db DB
enable :create_account enable :create_account
@@ -57,6 +63,10 @@ DB.transaction do
account_id = auth.account_id_for_login(login: email) account_id = auth.account_id_for_login(login: email)
DB[:accounts]
.where(id: account_id)
.update(status_id: verified_status_id)
DB[:members].insert( DB[:members].insert(
account_id: account_id, account_id: account_id,
kind: "individual", kind: "individual",
+52 -3
View File
@@ -1,19 +1,68 @@
Sequel.migration do Sequel.migration do
up do up do
create_table(:account_statuses) do
Integer :id, primary_key: true
String :name, null: false, unique: true
end
from(:account_statuses).import(
[:id, :name],
[
[1, "Unverified"],
[2, "Verified"],
[3, "Closed"]
]
)
db = self
create_table(:accounts) do create_table(:accounts) do
primary_key :id, type: :Bignum primary_key :id, type: :Bignum
foreign_key :status_id, :account_statuses,
null: false,
default: 1
String :email, null: false String :email, null: false
index :email, unique: true if db.supports_partial_indexes?
index :email, unique: true, where: { status_id: [1, 2] }
else
index :email, unique: true
end
end end
create_table(:account_password_hashes) do create_table(:account_password_hashes) do
foreign_key :id, :accounts, primary_key: true, type: :Bignum foreign_key :id, :accounts,
primary_key: true,
type: :Bignum
String :password_hash, null: false String :password_hash, null: false
end end
create_table(:account_verification_keys) do
foreign_key :id, :accounts,
primary_key: true,
type: :Bignum
String :key, null: false
DateTime :requested_at,
null: false,
default: Sequel::CURRENT_TIMESTAMP
DateTime :email_last_sent,
null: false,
default: Sequel::CURRENT_TIMESTAMP
end
end end
down do down do
drop_table(:account_password_hashes, :accounts) drop_table(
:account_verification_keys,
:account_password_hashes,
:accounts,
:account_statuses
)
end end
end end
+2
View File
@@ -12,6 +12,8 @@ Sequel.migration do
String :last_name String :last_name
String :organization_name String :organization_name
TrueClass :admin, null: false, default: false
index :account_id, unique: true index :account_id, unique: true
constraint :members_kind_valid, constraint :members_kind_valid,
-13
View File
@@ -1,13 +0,0 @@
Sequel.migration do
up do
alter_table(:members) do
add_column :admin, TrueClass, null: false, default: false
end
end
down do
alter_table(:members) do
drop_column :admin
end
end
end
+56
View File
@@ -0,0 +1,56 @@
Sequel.migration do
up do
create_table(:memberships) do
primary_key :id, type: :Bignum
foreign_key :member_id, :members,
type: :Bignum,
null: false
String :status, null: false, default: "pending"
DateTime :requested_at,
null: false,
default: Sequel::CURRENT_TIMESTAMP
DateTime :decided_at
foreign_key :recorded_by, :members,
type: :Bignum
DateTime :ended_at
String :end_reason
index :status
# Un seul dossier en attente ou une seule adhésion
# en cours par adhérent.
index :member_id,
unique: true,
where: { status: ["pending", "accepted"], ended_at: nil }
constraint :memberships_status_valid,
Sequel.lit("status IN ('pending', 'accepted', 'rejected')")
constraint :memberships_decision_consistent,
Sequel.lit(
"(status = 'pending' AND decided_at IS NULL " \
"AND recorded_by IS NULL) " \
"OR (status IN ('accepted', 'rejected') " \
"AND decided_at IS NOT NULL AND recorded_by IS NOT NULL)"
)
constraint :memberships_end_consistent,
Sequel.lit(
"(ended_at IS NULL AND end_reason IS NULL) " \
"OR (status = 'accepted' AND ended_at IS NOT NULL " \
"AND end_reason IN ('resignation', 'death', " \
"'dissolution', 'exclusion'))"
)
end
end
down do
drop_table(:memberships)
end
end
+40 -65
View File
@@ -28,30 +28,26 @@ Ses principes sont :
- le partage ouvert des savoirs et des compétences informatiques. - le partage ouvert des savoirs et des compétences informatiques.
L'association est sans but lucratif. Ses services sont proposés sans L'association est sans but lucratif. Ses services sont proposés sans
facturation liée à leur utilisation. Ses ressources, notamment les facturation liée à leur utilisation. Les dons volontaires contribuent au
cotisations des membres et les dons volontaires, servent à financer et financement des infrastructures et à la pérennité des services. L’association
pérenniser ses infrastructures. Aucun bénéfice n’est distribué aux membres. invite les membres et les utilisateurs qui le peuvent à la soutenir
financièrement, selon leurs moyens. Cette participation est importante pour
assurer la continuité des services. Les dons sont libres, sans contrepartie
et ne conditionnent ni l’adhésion, ni les droits des membres, ni l’accès aux
services. Aucun bénéfice n’est distribué aux membres.
## Article 3 — Adhésion ## Article 3 — Adhésion
Peut adhérer toute personne physique ou morale acceptant les statuts et Peut adhérer toute personne physique ou morale acceptant les statuts et
les principes de l’association. Le Noyau examine les demandes. les principes de l’association. Le Noyau examine les demandes et décide
de l’admission.
L’Assemblée générale (ci-après « AG ») fixe la cotisation annuelle. Après L’adhésion prend effet à la date de son acceptation et est valable sans
acceptation et paiement, l’adhésion dure un an et se renouvelle à chaque limitation de durée. Aucune cotisation ni aucun don n’est exigé pour adhérer,
échéance si la cotisation est payée. Un paiement anticipé ou effectué conserver la qualité de membre ou exercer les droits qui y sont attachés.
pendant le délai de relance ne change pas l’échéance.
En cas d’impayé, une relance laisse trente jours pour payer. L’adhésion L’adhésion prend fin par démission notifiée, décès, dissolution de la
est maintenue pendant ce délai, mais le membre n’est plus à jour de sa personne morale ou exclusion (article 7).
cotisation : il ne peut pas voter en AG et ses accès aux services réservés
aux membres à jour peuvent être suspendus par le Noyau ou le responsable
du service. À défaut de paiement à l’expiration du délai, l’adhésion prend fin.
L’adhésion prend également fin par démission notifiée, décès, dissolution
de la personne morale ou exclusion (article 7). La démission n’annule pas
l'obligation de régler les cotisations en retard ni celle due pour l’année
en cours.
## Article 4 — Assemblée générale ## Article 4 — Assemblée générale
@@ -89,22 +85,21 @@ modifications statutaires et à la dissolution.
### Voix et procurations ### Voix et procurations
Chaque membre à jour de sa cotisation, personne physique ou morale, Chaque membre, personne physique ou morale, dispose d’une voix délibérative.
dispose d’une voix. La voix d’une personne morale est exercée par son La voix d’une personne morale est exercée par son représentant habilité.
représentant habilité. Une personne physique également membre à titre Une personne physique également membre à titre personnel qui représente
personnel qui représente une ou plusieurs personnes morales membres une ou plusieurs personnes morales membres exerce une voix en son nom et
exerce une voix en son nom et une voix pour chacune d’elles. une voix pour chacune d’elles.
Tout membre à jour peut se faire représenter par un membre personne Tout membre peut se faire représenter par un membre personne physique.
physique à jour. Une personne morale donne procuration par son Une personne morale donne procuration par son représentant habilité.
représentant habilité. Une même personne peut détenir au plus deux Une même personne peut détenir au plus deux procurations, en plus des voix
procurations, en plus des voix qu’elle exerce en son nom ou comme qu’elle exerce en son nom ou comme représentant d’une ou plusieurs personnes
représentant d’une ou plusieurs personnes morales. morales.
Les voix délibératives sont celles des membres à jour de leur cotisation. Chaque voix délibérative compte séparément pour le calcul des seuils et des
Chaque voix délibérative compte séparément pour le calcul des seuils et quorums prévus par les statuts ; les fractions sont arrondies à l’entier
des quorums prévus par les statuts ; les fractions sont arrondies à supérieur.
l’entier supérieur.
### Décisions ### Décisions
@@ -224,9 +219,9 @@ principes de l’article 2 ni modifier les statuts.
## Article 6 — Ressources et comptabilité ## Article 6 — Ressources et comptabilité
Les ressources comprennent les cotisations, dons, subventions, recettes Les ressources comprennent les dons volontaires, les subventions, les
d’activités compatibles avec l’objet et les principes de l’association recettes d’activités compatibles avec l’objet et les principes de
et toute autre ressource autorisée par la loi. l’association et toute autre ressource autorisée par la loi.
Les fonctions sont bénévoles. Les frais engagés pour l’association peuvent Les fonctions sont bénévoles. Les frais engagés pour l’association peuvent
être remboursés sur justificatifs, selon les modalités du Noyau. Nul ne être remboursés sur justificatifs, selon les modalités du Noyau. Nul ne
@@ -318,14 +313,14 @@ substantielle impose une nouvelle AG et fait repartir le délai de
trente jours. trente jours.
La première AG ne délibère que si deux tiers au moins des voix La première AG ne délibère que si deux tiers au moins des voix
délibératives des membres à jour sont présentes ou représentées. À délibératives sont présentes ou représentées. À défaut de quorum, une
défaut de quorum, une seconde AG est convoquée par le Noyau ou, si la seconde AG est convoquée par le Noyau ou, si la première AG a été
première AG a été convoquée par des membres, par ces mêmes membres. convoquée par des membres, par ces mêmes membres. Sa convocation est
Sa convocation est envoyée dans les quinze jours suivant le constat envoyée dans les quinze jours suivant le constat de l’absence de quorum.
de l’absence de quorum. Elle se tient au moins quinze jours après Elle se tient au moins quinze jours après l’envoi de sa convocation.
l’envoi de sa convocation. Elle reprend le même ordre du jour et le même Elle reprend le même ordre du jour et le même projet, et ne délibère
projet, et ne délibère valablement que si un tiers au moins des voix valablement que si un tiers au moins des voix délibératives est présent
délibératives des membres à jour est présent ou représenté. ou représenté.
Si le quorum de la seconde AG n’est pas atteint, le projet n’est pas Si le quorum de la seconde AG n’est pas atteint, le projet n’est pas
adopté. Il peut être soumis à une nouvelle procédure dans les conditions adopté. Il peut être soumis à une nouvelle procédure dans les conditions
@@ -334,31 +329,11 @@ du présent article.
La modification ou la dissolution est adoptée à la majorité des deux La modification ou la dissolution est adoptée à la majorité des deux
tiers des suffrages exprimés. Toutefois, toute modification statutaire tiers des suffrages exprimés. Toutefois, toute modification statutaire
ayant pour effet d’augmenter les engagements des membres requiert ayant pour effet d’augmenter les engagements des membres requiert
l’accord unanime de tous les membres, y compris ceux qui ne sont pas l’accord unanime de tous les membres. Cet accord peut être recueilli
à jour de leur cotisation. Cet accord peut être recueilli par écrit par écrit en dehors de l’AG.
en dehors de l’AG.
En cas de dissolution, l’AG désigne un ou plusieurs liquidateurs et En cas de dissolution, l’AG désigne un ou plusieurs liquidateurs et
attribue l’actif net, après règlement des dettes, à un organisme à but attribue l’actif net, après règlement des dettes, à un organisme à but
non lucratif poursuivant un objet compatible avec celui de NETig. non lucratif poursuivant un objet compatible avec celui de NETig.
Aucun actif n’est distribué aux membres. Aucun actif n’est distribué aux membres.
---
**Statuts adoptés par l’Assemblée générale constitutive.**
Fait à __________________________, le ____ / ____ / ______
**Signatures**
Nom : __________________________________________
Signature : _____________________________________
Nom : __________________________________________
Signature : _____________________________________
Nom : __________________________________________
Signature : _____________________________________