doc: split Architecture.md into per-topic pages
doc/Architecture.md was 436 lines and growing; this commit
extracts each non-trivial subject into its own page under
doc/architecture/ and reduces the root document to a table of
contents + transversal sections (vision, stack, admin rights).
New pages (under doc/architecture/):
- workflow-multi-parties.md : client / fournisseur /
coordinateur roles, sous-traitance, project states, B2B/B2C,
domaine musical production flow.
- domaine-musical.md : titres collaboratifs (formats, flux de
production, contraintes de licence).
- licences.md : LicenceModele, CC/ODbL seed, badge projet,
cycle de vie.
- domaines-activite.md : arbre des activites, Droit a la
racine, DomaineActivite model.
- dictionnaires-metier.md : regle d'heritage, DictionnaireMetier
+ TermeMetier, cycle de vie d'un terme. Absorbs the previous
doc/Dictionnaire.md draft.
- offres-frontmatter.md : ClasseFormulaire / ClasseDevis,
OffreFournisseur, Demande, parsing YamlDotNet (introduit dans
4034c399 Front matters). Absorbs the previous
doc/Formulaires-devis.md and doc/Demande.md fragments.
- postit-oidc.md : documentation du client desktop PostIt,
custom URI scheme (RFC 8252 §7.1), composants partages,
plateformes, UX observable, persistance et reprise au boot,
garanties testees.
Architecture.md (436 -> 66 lines) keeps the vision, the stack
overview, the admin rights section, and a TOC table pointing at
each detail page. The "A documenter ensuite" backlog is kept
at the end.
Cross-links are relative: from Architecture.md the links go
architecture/<page>.md; from inside doc/architecture/ they go
../Architecture.md or <sibling>.md.
This commit is contained in:
parent
4034c39905
commit
74de6aa1d1
11 changed files with 548 additions and 338 deletions
|
|
@ -9,103 +9,20 @@ Elle gère des **devis** (pas de facturation directe), et s'intègre
|
|||
dans des workflows collaboratifs pouvant aboutir à des livrables
|
||||
sous licence libre.
|
||||
|
||||
---
|
||||
## Pages de détail
|
||||
|
||||
## Workflow de mise en relation multi-parties
|
||||
Chaque sujet est détaillé dans sa propre page (sous
|
||||
`doc/architecture/`) ; cette racine tient lieu de sommaire.
|
||||
|
||||
### Initiateur
|
||||
|
||||
Le projet peut être initié par n'importe quelle partie :
|
||||
- Un **client** (particulier ou pro) qui exprime un besoin
|
||||
- Un **fournisseur** qui propose une offre ou monte un collectif
|
||||
- Un **tiers coordinateur** qui orchestre sans être client ni prestataire
|
||||
|
||||
### Rôles
|
||||
|
||||
| Rôle | Type de compte | Description |
|
||||
|---------------|--------------------|-----------------------------------------------------------|
|
||||
| Client | Pro ou particulier | Exprime le besoin, valide les devis, co-signe la licence |
|
||||
| Fournisseur | Pro | Répond aux besoins, peut sous-traiter |
|
||||
| Coordinateur | Pro ou particulier | Orchestre le projet sans relation de facturation directe |
|
||||
|
||||
### Sous-traitance
|
||||
|
||||
Un fournisseur peut faire appel à d'autres fournisseurs,
|
||||
**avec accord explicite du client**. Chaque sous-traitant :
|
||||
- Est visible dans le projet
|
||||
- Co-signe l'accord de licence
|
||||
- Peut recevoir un devis distinct
|
||||
|
||||
### B2B / B2C
|
||||
|
||||
La distinction est portée par le **type de compte** :
|
||||
- Compte **pro** : SIRET, TVA, facturation professionnelle
|
||||
- Compte **particulier** : usage personnel, sans obligations fiscales pro
|
||||
|
||||
Un projet peut mélanger les deux (ex: un particulier client,
|
||||
plusieurs prestataires pro) — Yavsc n'impose pas d'homogénéité.
|
||||
|
||||
### États d'un projet
|
||||
|
||||
```
|
||||
Initié → En recherche de parties → Devis en cours
|
||||
→ Accord de licence signé → En production
|
||||
→ Livré → Publié (si licence libre)
|
||||
```
|
||||
|
||||
### Contrainte clé
|
||||
|
||||
L'accord de licence est établi **avant** tout début de production,
|
||||
signé (ou validé) par toutes les parties : client, fournisseurs,
|
||||
et sous-traitants éventuels.
|
||||
|
||||
---
|
||||
|
||||
## Domaine musical — Titres collaboratifs
|
||||
|
||||
### Objectif
|
||||
|
||||
Permettre la production collaborative de titres musicaux sous licence
|
||||
libre, à partir de la mise en relation assurée par Yavsc.
|
||||
|
||||
### Formats supportés
|
||||
|
||||
- Audio (ex: WAV, FLAC, MP3)
|
||||
- Partition (ex: MusicXML, LilyPond, PDF)
|
||||
- MIDI
|
||||
|
||||
### Flux de production
|
||||
|
||||
1. Un client exprime un besoin musical
|
||||
2. Des prestataires répondent avec des devis
|
||||
3. Un **consensus est établi en amont** entre le client et les
|
||||
contributeurs sur la licence du livrable final
|
||||
4. La collaboration produit les fichiers
|
||||
5. Le titre est publié sous la licence choisie
|
||||
|
||||
---
|
||||
|
||||
## Licences
|
||||
|
||||
### Modèle `LicenceModele`
|
||||
|
||||
Géré par l'administration Yavsc. Chaque modèle porte :
|
||||
|
||||
- `EstLibre` (bool) — détermine le badge affiché sur le projet
|
||||
- Les conditions : attribution, partage à l'identique, usage commercial,
|
||||
modification
|
||||
- Une URL vers le texte officiel de la licence
|
||||
|
||||
### Seed initial
|
||||
|
||||
Licences Creative Commons préchargées : CC0, CC BY, CC BY-SA, CC BY-ND,
|
||||
CC BY-NC, CC BY-NC-SA, CC BY-NC-ND (toutes en version 4.0) + ODbL 1.0.
|
||||
|
||||
### Badge projet
|
||||
|
||||
Rendu CSS/HTML — vert si `EstLibre`, orange sinon.
|
||||
|
||||
---
|
||||
| Sujet | Page |
|
||||
|------------------------------------------------|-------------------------------------------------------------------|
|
||||
| Workflow multi-parties (client / fournisseur / coordinateur, sous-traitance, états) | [workflow-multi-parties.md](architecture/workflow-multi-parties.md) |
|
||||
| Domaine musical (titres collaboratifs) | [domaine-musical.md](architecture/domaine-musical.md) |
|
||||
| Licences (`LicenceModele`, seed CC/ODbL, badge)| [licences.md](architecture/licences.md) |
|
||||
| Domaines d'activité (arbre, `DomaineActivite`) | [domaines-activite.md](architecture/domaines-activite.md) |
|
||||
| Dictionnaires métier (héritage en arbre) | [dictionnaires-metier.md](architecture/dictionnaires-metier.md) |
|
||||
| Offre fournisseur + frontmatter (`ClasseFormulaire`, `ClasseDevis`, parsing) | [offres-frontmatter.md](architecture/offres-frontmatter.md) |
|
||||
| PostIt / OIDC (client desktop, custom URI scheme, silent refresh) | [postit-oidc.md](architecture/postit-oidc.md) |
|
||||
|
||||
## Stack technique
|
||||
|
||||
|
|
@ -114,14 +31,15 @@ Rendu CSS/HTML — vert si `EstLibre`, orange sinon.
|
|||
- **Base de données** : PostgreSQL (provider Npgsql)
|
||||
- **Frontend** : Razor views
|
||||
- **Parsing frontmatter** : YamlDotNet
|
||||
|
||||
---
|
||||
- **Client desktop** : Avalonia 12 (PostIt — détails dans
|
||||
[postit-oidc.md](architecture/postit-oidc.md))
|
||||
|
||||
## Droits de Yavsc
|
||||
|
||||
### Administration
|
||||
|
||||
Le groupe des administrateurs prend la charge de :
|
||||
|
||||
- la Gestion des licences
|
||||
- la Gestion des groupes d'utilisateurs (les modérateurs, en particulier)
|
||||
- la Gestion des projets
|
||||
|
|
@ -129,8 +47,9 @@ Le groupe des administrateurs prend la charge de :
|
|||
|
||||
### Gestion des utilisateurs et des groupes
|
||||
|
||||
En supposant que les certificats de letsencrypt sont au groupe `www-data`,
|
||||
on peut créer l'utilisateur `yavsc` avec les droits suivants :
|
||||
En supposant que les certificats de letsencrypt sont au groupe
|
||||
`www-data`, on peut créer l'utilisateur `yavsc` avec les droits
|
||||
suivants :
|
||||
|
||||
```bash
|
||||
sudo addgroup yavsc --system
|
||||
|
|
@ -138,169 +57,6 @@ sudo adduser --ingroup yavsc --add-extra-groups www-data \
|
|||
--disabled-password --system yavsc
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Domaines d'activité
|
||||
|
||||
### Hiérarchie
|
||||
|
||||
Les activités sont organisées en arbre. Chaque activité peut avoir
|
||||
une activité parente d'ordre plus général :
|
||||
|
||||
```
|
||||
Droit ← racine, ancêtre commun
|
||||
├── Musique
|
||||
│ ├── Jazz
|
||||
│ ├── Classique
|
||||
│ └── ...
|
||||
├── Graphisme
|
||||
├── BTP
|
||||
└── ...
|
||||
```
|
||||
|
||||
Le domaine **Droit** est positionné à la racine — *nul n'est censé
|
||||
ignorer la loi*. Sa position structurelle rend ses dictionnaires
|
||||
naturellement disponibles dans tous les projets, sans attribut spécial.
|
||||
|
||||
### Modèle `DomaineActivite`
|
||||
|
||||
```
|
||||
DomaineActivite
|
||||
- Id, Nom
|
||||
- ParentId (FK → DomaineActivite, nullable)
|
||||
```
|
||||
|
||||
Pas de booléen `EstTransversal` — la transversalité est une conséquence
|
||||
de la position dans l'arbre, pas un attribut explicite.
|
||||
|
||||
---
|
||||
|
||||
## Dictionnaires métier
|
||||
|
||||
### Principe
|
||||
|
||||
Les dictionnaires sont des **modèles publics** de termes métier,
|
||||
maintenus par les modérateurs et alimentés par les fournisseurs.
|
||||
Au moment de créer un projet, le dictionnaire est **copié** —
|
||||
c'est un instantané, pas une référence vivante.
|
||||
|
||||
### Règle de résolution
|
||||
|
||||
> Un projet hérite des dictionnaires de son activité **et de tous
|
||||
> ses ancêtres** jusqu'à la racine.
|
||||
|
||||
Ainsi, les termes juridiques sont toujours disponibles, quel que
|
||||
soit le domaine du projet.
|
||||
|
||||
### Modèle
|
||||
|
||||
```
|
||||
DictionnaireMetier
|
||||
- Id, Nom
|
||||
- DomaineActiviteId (FK)
|
||||
- Langue ← attribut du dictionnaire entier
|
||||
|
||||
TermeMetier
|
||||
- Id
|
||||
- DictionnaireMetierId (FK)
|
||||
- Mot
|
||||
- Definition
|
||||
- StatutValidation ← Proposé | Validé | Rejeté
|
||||
- ProposeParId (FK → ApplicationUser)
|
||||
- ValidéParId (FK → ApplicationUser, nullable)
|
||||
```
|
||||
|
||||
### Cycle de vie d'un terme
|
||||
|
||||
1. Un **fournisseur** propose un terme dans son domaine et sa langue
|
||||
2. Un **modérateur** valide → le terme intègre le dictionnaire public
|
||||
3. À la création d'un projet, le dictionnaire est **associé par copie**
|
||||
|
||||
---
|
||||
|
||||
## Offre fournisseur et modèle canonique de demande
|
||||
|
||||
### Principe
|
||||
|
||||
Un fournisseur décrit son offre en **Markdown**, avec un en-tête
|
||||
structuré délimité par `---` (frontmatter YAML standard) qui porte
|
||||
les métadonnées de formulaire et de devis. Le corps est libre.
|
||||
|
||||
```markdown
|
||||
---
|
||||
formulaire: Prestation Musicale
|
||||
devis: Arrangement Orchestral
|
||||
---
|
||||
|
||||
Je propose des arrangements pour orchestre de chambre,
|
||||
livraison sous 3 semaines, formats MusicXML et PDF...
|
||||
```
|
||||
|
||||
### `ClasseFormulaire` et `ClasseDevis`
|
||||
|
||||
Ces deux référentiels sont **définis côté serveur par les modérateurs**,
|
||||
pour servir de modèles de formulaires et de devis. Les fournisseurs
|
||||
s'y conforment — ils ne peuvent pas en créer de nouveaux.
|
||||
|
||||
### Modèle canonique nom/valeur
|
||||
|
||||
```
|
||||
ClasseFormulaire
|
||||
- Id, Nom
|
||||
- DomaineActiviteId (FK)
|
||||
└── ChampDemande
|
||||
- Nom (ex: "tempo", "tonalité", "durée")
|
||||
- TypeValeur (texte | entier | décimal | booléen | enum)
|
||||
- Obligatoire (bool)
|
||||
- ValeurDefaut
|
||||
|
||||
ClasseDevis
|
||||
- Id, Nom
|
||||
- DomaineActiviteId (FK)
|
||||
|
||||
OffreFournisseur
|
||||
- Id
|
||||
- FournisseurId (FK → ApplicationUser)
|
||||
- DomaineActiviteId (FK)
|
||||
- ContenuMarkdown ← corps libre de l'offre
|
||||
- ClasseFormulaireId (FK) ← extrait du frontmatter à la sauvegarde
|
||||
- ClasseDevisId (FK) ← extrait du frontmatter à la sauvegarde
|
||||
```
|
||||
|
||||
### Demande client
|
||||
|
||||
À partir de l'offre, la demande instancie les champs du formulaire :
|
||||
|
||||
```
|
||||
Demande
|
||||
- Id
|
||||
- OffreFournisseurId (FK)
|
||||
- ClientId (FK → ApplicationUser)
|
||||
└── ValeurChampDemande
|
||||
- ChampDemandeId (FK)
|
||||
- Valeur (string — sérialisé selon TypeValeur)
|
||||
```
|
||||
|
||||
### Règle d'héritage
|
||||
|
||||
Les `ClasseFormulaire` et `ClasseDevis` disponibles pour une activité
|
||||
incluent ceux de ses **ancêtres** dans l'arbre — cohérent avec la
|
||||
règle de résolution des dictionnaires.
|
||||
|
||||
### Parsing du frontmatter avec YamlDotNet
|
||||
|
||||
```csharp
|
||||
var parts = markdown.TrimStart()
|
||||
.Substring(3)
|
||||
.Split(new[] { "\n---" }, 2,
|
||||
StringSplitOptions.None);
|
||||
var yamlBlock = parts[0].Trim();
|
||||
var corps = parts[1].Trim();
|
||||
var meta = deserializer.Deserialize<FrontmatterOffreResult>(yamlBlock);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## À documenter ensuite
|
||||
|
||||
- Modèle `ProjetMusical`
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
Demande
|
||||
- Id
|
||||
- OffreFournisseurId (FK)
|
||||
- ClientId (FK → ApplicationUser)
|
||||
└── ValeurChampDemande
|
||||
- ChampDemandeId (FK)
|
||||
- Valeur (string — sérialisé selon TypeValeur)
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
# Dictionnaires métier
|
||||
|
||||
## Principe
|
||||
|
||||
Les dictionnaires sont des modèles publics de termes métier,
|
||||
maintenus par les modérateurs et alimentés par les fournisseurs.
|
||||
Au moment de créer un projet, le dictionnaire est copié —
|
||||
c'est un instantané, pas une référence vivante.
|
||||
|
||||
## Règle de résolution
|
||||
|
||||
Un projet hérite des dictionnaires de son activité et de tous
|
||||
ses ancêtres jusqu'à la racine.
|
||||
|
||||
Ainsi, les termes juridiques sont toujours disponibles, quel que
|
||||
soit le domaine du projet.
|
||||
|
||||
Modèle
|
||||
|
||||
|
||||
Le modèle de dictionnaire du contrat est le dictionnaire défini par l'activité ciblée par le client.
|
||||
|
||||
## Dictionnaires juridiques
|
||||
|
||||
Les dictionnaires juridiques sont des modèles publics de termes juridiques,
|
||||
maintenus par les modérateurs et alimentés par les modérateurs.
|
||||
|
||||
## Règle de résolution
|
||||
|
||||
Un projet hérite des dictionnaires de son activité et de tous
|
||||
ses ancêtres jusqu à la racine.
|
||||
|
||||
Ainsi, les termes juridiques sont toujours disponibles, quel que
|
||||
soit le domaine du projet.
|
||||
|
||||
Ce dictionnaire juridique est simplement fondamental, par construction, c'est la base de tous les dictionnaires.
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
|
||||
ClasseDevis
|
||||
- Id, Nom
|
||||
- DomaineActiviteId (FK)
|
||||
|
||||
OffreFournisseur
|
||||
- Id
|
||||
- FournisseurId (FK → ApplicationUser)
|
||||
- DomaineActiviteId (FK)
|
||||
- ContenuMarkdown ← corps libre de l'offre
|
||||
- ClasseFormulaireId (FK) ← extrait du frontmatter à la sauvegarde
|
||||
- ClasseDevisId (FK) ← extrait du frontmatter à la sauvegarde
|
||||
|
||||
ClasseFormulaire
|
||||
- Id, Nom
|
||||
- DomaineActiviteId (FK)
|
||||
└── ChampDemande
|
||||
- Nom (ex: "tempo", "tonalité", "durée")
|
||||
- TypeValeur (texte | entier | décimal | booléen | enum)
|
||||
- Obligatoire (bool)
|
||||
- ValeurDefaut
|
||||
|
||||
ClasseDevis
|
||||
- Id, Nom
|
||||
- DomaineActiviteId (FK)
|
||||
|
||||
OffreFournisseur
|
||||
- Id
|
||||
- FournisseurId (FK → ApplicationUser)
|
||||
- DomaineActiviteId (FK)
|
||||
- ContenuMarkdown ← corps libre de l'offre
|
||||
- ClasseFormulaireId (FK) ← extrait du frontmatter à la sauvegarde
|
||||
- ClasseDevisId (FK) ← extrait du frontmatter à la sauvegarde
|
||||
68
doc/architecture/dictionnaires-metier.md
Normal file
68
doc/architecture/dictionnaires-metier.md
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
# Dictionnaires métier
|
||||
|
||||
> **Récapitulatif** : Les dictionnaires sont des modèles publics
|
||||
> de termes métier, validés par les modérateurs et **copiés**
|
||||
> dans chaque projet (instantané, pas référence vivante).
|
||||
> L'héritage suit l'arbre des activités. Détail dans cette page,
|
||||
> racine de l'architecture : [Architecture.md](../Architecture.md).
|
||||
|
||||
## Principe
|
||||
|
||||
Les dictionnaires sont des **modèles publics** de termes métier,
|
||||
maintenus par les modérateurs et alimentés par les fournisseurs.
|
||||
Au moment de créer un projet, le dictionnaire est **copié** —
|
||||
c'est un instantané, pas une référence vivante.
|
||||
|
||||
> Note historique : une première ébauche de cette page existait
|
||||
> à `doc/Dictionnaire.md`. Elle a été absorbée ici.
|
||||
|
||||
## Règle de résolution
|
||||
|
||||
> Un projet hérite des dictionnaires de son activité **et de tous
|
||||
> ses ancêtres** jusqu'à la racine.
|
||||
|
||||
Ainsi, les termes juridiques sont toujours disponibles, quel que
|
||||
soit le domaine du projet.
|
||||
|
||||
Le modèle de dictionnaire du contrat est le dictionnaire défini
|
||||
par l'activité ciblée par le client.
|
||||
|
||||
## Dictionnaires juridiques
|
||||
|
||||
Les dictionnaires juridiques sont des modèles publics de termes
|
||||
juridiques, maintenus et alimentés par les modérateurs. Ils
|
||||
figurent à la racine de l'arbre (sous `Droit`) et profitent donc
|
||||
à toutes les activités par construction.
|
||||
|
||||
> Ce dictionnaire juridique est simplement fondamental, par
|
||||
> construction, c'est la base de tous les dictionnaires.
|
||||
|
||||
## Modèle
|
||||
|
||||
```
|
||||
DictionnaireMetier
|
||||
- Id, Nom
|
||||
- DomaineActiviteId (FK)
|
||||
- Langue ← attribut du dictionnaire entier
|
||||
|
||||
TermeMetier
|
||||
- Id
|
||||
- DictionnaireMetierId (FK)
|
||||
- Mot
|
||||
- Definition
|
||||
- StatutValidation ← Proposé | Validé | Rejeté
|
||||
- ProposeParId (FK → ApplicationUser)
|
||||
- ValidéParId (FK → ApplicationUser, nullable)
|
||||
```
|
||||
|
||||
## Cycle de vie d'un terme
|
||||
|
||||
1. Un **fournisseur** propose un terme dans son domaine et sa langue
|
||||
2. Un **modérateur** valide → le terme intègre le dictionnaire public
|
||||
3. À la création d'un projet, le dictionnaire est **associé par copie**
|
||||
|
||||
## Voir aussi
|
||||
|
||||
- [Architecture.md](../Architecture.md) — racine.
|
||||
- [domaines-activite.md](domaines-activite.md) — l'arbre des
|
||||
activités qui justifie la règle d'héritage.
|
||||
43
doc/architecture/domaine-musical.md
Normal file
43
doc/architecture/domaine-musical.md
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
# Domaine musical — Titres collaboratifs
|
||||
|
||||
> **Récapitulatif** : Yavsc permet de produire collaborativement
|
||||
> des titres musicaux sous licence libre, en s'appuyant sur la
|
||||
> mise en relation multi-parties. Détail dans cette page, racine
|
||||
> de l'architecture : [Architecture.md](../Architecture.md).
|
||||
|
||||
## Objectif
|
||||
|
||||
Permettre la production collaborative de titres musicaux sous
|
||||
licence libre, à partir de la mise en relation assurée par Yavsc.
|
||||
|
||||
## Formats supportés
|
||||
|
||||
- Audio (ex : WAV, FLAC, MP3)
|
||||
- Partition (ex : MusicXML, LilyPond, PDF)
|
||||
- MIDI
|
||||
|
||||
## Flux de production
|
||||
|
||||
1. Un client exprime un besoin musical
|
||||
2. Des prestataires répondent avec des devis
|
||||
3. Un **consensus est établi en amont** entre le client et les
|
||||
contributeurs sur la licence du livrable final
|
||||
4. La collaboration produit les fichiers
|
||||
5. Le titre est publié sous la licence choisie
|
||||
|
||||
## Contraintes spécifiques
|
||||
|
||||
- L'accord de licence sur le livrable musical est une instance
|
||||
du modèle `LicenceModele` détaillé dans
|
||||
[licences.md](licences.md).
|
||||
- L'arborescence d'activités musicales (`Musique → Jazz`,
|
||||
`Musique → Classique`, etc.) est décrite dans
|
||||
[domaines-activite.md](domaines-activite.md).
|
||||
|
||||
## Voir aussi
|
||||
|
||||
- [Architecture.md](../Architecture.md) — racine.
|
||||
- [workflow-multi-parties.md](workflow-multi-parties.md) — rôle
|
||||
client/fournisseur/coordinateur dans la production musicale.
|
||||
- [licences.md](licences.md) — modèle de licence applicable au
|
||||
livrable.
|
||||
59
doc/architecture/domaines-activite.md
Normal file
59
doc/architecture/domaines-activite.md
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
# Domaines d'activité
|
||||
|
||||
> **Récapitulatif** : Yavsc organise ses activités en arbre, avec
|
||||
> `Droit` à la racine pour rendre les termes juridiques
|
||||
> disponibles partout. Pas de booléen "transversal" : la
|
||||
> transversalité découle de la position dans l'arbre. Détail dans
|
||||
> cette page, racine de l'architecture : [Architecture.md](../Architecture.md).
|
||||
|
||||
## Hiérarchie
|
||||
|
||||
Les activités sont organisées en arbre. Chaque activité peut avoir
|
||||
une activité parente d'ordre plus général :
|
||||
|
||||
```
|
||||
Droit ← racine, ancêtre commun
|
||||
├── Musique
|
||||
│ ├── Jazz
|
||||
│ ├── Classique
|
||||
│ └── ...
|
||||
├── Graphisme
|
||||
├── BTP
|
||||
└── ...
|
||||
```
|
||||
|
||||
Le domaine **Droit** est positionné à la racine — *nul n'est
|
||||
censé ignorer la loi*. Sa position structurelle rend ses
|
||||
dictionnaires naturellement disponibles dans tous les projets,
|
||||
sans attribut spécial.
|
||||
|
||||
## Modèle `DomaineActivite`
|
||||
|
||||
```
|
||||
DomaineActivite
|
||||
- Id, Nom
|
||||
- ParentId (FK → DomaineActivite, nullable)
|
||||
```
|
||||
|
||||
Pas de booléen `EstTransversal` — la transversalité est une
|
||||
conséquence de la position dans l'arbre, pas un attribut
|
||||
explicite.
|
||||
|
||||
## Conséquences sur les autres référentiels
|
||||
|
||||
Cette arborescence irrigue :
|
||||
|
||||
- les **dictionnaires métier** (cf. [dictionnaires-metier.md](dictionnaires-metier.md)) :
|
||||
un projet hérite des dictionnaires de ses ancêtres.
|
||||
- les **classes de formulaire et de devis** (cf.
|
||||
[offres-frontmatter.md](offres-frontmatter.md)) : un fournisseur
|
||||
rattaché à `Musique → Jazz` a accès aux modèles de son
|
||||
activité et de ses ancêtres.
|
||||
|
||||
## Voir aussi
|
||||
|
||||
- [Architecture.md](../Architecture.md) — racine.
|
||||
- [dictionnaires-metier.md](dictionnaires-metier.md) — règle
|
||||
d'héritage en arbre.
|
||||
- [offres-frontmatter.md](offres-frontmatter.md) — modèle
|
||||
canonique nom/valeur pour formulaires et devis.
|
||||
42
doc/architecture/licences.md
Normal file
42
doc/architecture/licences.md
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
# Licences
|
||||
|
||||
> **Récapitulatif** : Yavsc référence des modèles de licence
|
||||
> (Creative Commons + ODbL) maintenus par l'administration. Chaque
|
||||
> projet signe l'accord avant toute production. Détail dans cette
|
||||
> page, racine de l'architecture : [Architecture.md](../Architecture.md).
|
||||
|
||||
## Modèle `LicenceModele`
|
||||
|
||||
Géré par l'administration Yavsc. Chaque modèle porte :
|
||||
|
||||
- `EstLibre` (bool) — détermine le badge affiché sur le projet
|
||||
- Les conditions : attribution, partage à l'identique, usage
|
||||
commercial, modification
|
||||
- Une URL vers le texte officiel de la licence
|
||||
|
||||
## Seed initial
|
||||
|
||||
Licences Creative Commons préchargées : CC0, CC BY, CC BY-SA,
|
||||
CC BY-ND, CC BY-NC, CC BY-NC-SA, CC BY-NC-ND (toutes en version
|
||||
4.0) + ODbL 1.0.
|
||||
|
||||
## Badge projet
|
||||
|
||||
Rendu CSS/HTML — vert si `EstLibre`, orange sinon.
|
||||
|
||||
## Cycle de vie
|
||||
|
||||
1. L'administration crée ou met à jour un `LicenceModele`.
|
||||
2. À la création d'un projet, le client choisit un modèle
|
||||
dans la liste des licences applicables à son activité.
|
||||
3. Le projet passe par l'état **Devis en cours** avant que
|
||||
l'accord ne soit signé par toutes les parties (cf.
|
||||
[workflow-multi-parties.md](workflow-multi-parties.md)).
|
||||
4. Une fois signé, l'état passe à **En production** ; le badge
|
||||
du projet reflète `EstLibre`.
|
||||
|
||||
## Voir aussi
|
||||
|
||||
- [Architecture.md](../Architecture.md) — racine.
|
||||
- [workflow-multi-parties.md](workflow-multi-parties.md) —
|
||||
contrainte clé : accord signé avant production.
|
||||
106
doc/architecture/offres-frontmatter.md
Normal file
106
doc/architecture/offres-frontmatter.md
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
# Offre fournisseur, formulaire, devis
|
||||
|
||||
> **Récapitulatif** : Le fournisseur rédige son offre en Markdown
|
||||
> avec un en-tête frontmatter YAML qui pointe vers un
|
||||
> `ClasseFormulaire` et un `ClasseDevis` définis par les
|
||||
> modérateurs. La demande du client instancie les champs du
|
||||
> formulaire. L'héritage suit l'arbre des activités. Détail dans
|
||||
> cette page, racine de l'architecture : [Architecture.md](../Architecture.md).
|
||||
|
||||
> Note historique : des fragments existaient à `doc/Formulaires-devis.md`
|
||||
> et `doc/Demande.md`. Ils sont absorbés ici.
|
||||
|
||||
## Principe
|
||||
|
||||
Un fournisseur décrit son offre en **Markdown**, avec un en-tête
|
||||
structuré délimité par `---` (frontmatter YAML standard) qui porte
|
||||
les métadonnées de formulaire et de devis. Le corps est libre.
|
||||
|
||||
```markdown
|
||||
---
|
||||
formulaire: Prestation Musicale
|
||||
devis: Arrangement Orchestral
|
||||
---
|
||||
|
||||
Je propose des arrangements pour orchestre de chambre,
|
||||
livraison sous 3 semaines, formats MusicXML et PDF...
|
||||
```
|
||||
|
||||
## `ClasseFormulaire` et `ClasseDevis`
|
||||
|
||||
Ces deux référentiels sont **définis côté serveur par les
|
||||
modérateurs**, pour servir de modèles de formulaires et de devis.
|
||||
Les fournisseurs s'y conforment — ils ne peuvent pas en créer
|
||||
de nouveaux.
|
||||
|
||||
## Modèle canonique nom/valeur
|
||||
|
||||
```
|
||||
ClasseFormulaire
|
||||
- Id, Nom
|
||||
- DomaineActiviteId (FK)
|
||||
└── ChampDemande
|
||||
- Nom (ex : "tempo", "tonalité", "durée")
|
||||
- TypeValeur (texte | entier | décimal | booléen | enum)
|
||||
- Obligatoire (bool)
|
||||
- ValeurDefaut
|
||||
|
||||
ClasseDevis
|
||||
- Id, Nom
|
||||
- DomaineActiviteId (FK)
|
||||
|
||||
OffreFournisseur
|
||||
- Id
|
||||
- FournisseurId (FK → ApplicationUser)
|
||||
- DomaineActiviteId (FK)
|
||||
- ContenuMarkdown ← corps libre de l'offre
|
||||
- ClasseFormulaireId (FK) ← extrait du frontmatter à la sauvegarde
|
||||
- ClasseDevisId (FK) ← extrait du frontmatter à la sauvegarde
|
||||
```
|
||||
|
||||
## Demande client
|
||||
|
||||
À partir de l'offre, la demande instancie les champs du formulaire :
|
||||
|
||||
```
|
||||
Demande
|
||||
- Id
|
||||
- OffreFournisseurId (FK)
|
||||
- ClientId (FK → ApplicationUser)
|
||||
└── ValeurChampDemande
|
||||
- ChampDemandeId (FK)
|
||||
- Valeur (string — sérialisé selon TypeValeur)
|
||||
```
|
||||
|
||||
## Règle d'héritage
|
||||
|
||||
Les `ClasseFormulaire` et `ClasseDevis` disponibles pour une
|
||||
activité incluent ceux de ses **ancêtres** dans l'arbre —
|
||||
cohérent avec la règle de résolution des dictionnaires
|
||||
([dictionnaires-metier.md](dictionnaires-metier.md)).
|
||||
|
||||
## Parsing du frontmatter avec YamlDotNet
|
||||
|
||||
Le parsing du frontmatter vit dans
|
||||
`src/Yavsc.Server/Services/FrontmatterParser.cs` (introduit dans
|
||||
le commit `4034c399 Front matters`). Squelette :
|
||||
|
||||
```csharp
|
||||
var parts = markdown.TrimStart()
|
||||
.Substring(3)
|
||||
.Split(new[] { "\n---" }, 2,
|
||||
StringSplitOptions.None);
|
||||
var yamlBlock = parts[0].Trim();
|
||||
var corps = parts[1].Trim();
|
||||
var meta = deserializer.Deserialize<FrontmatterOffreResult>(yamlBlock);
|
||||
```
|
||||
|
||||
Le résultat `FrontmatterOffreResult` est sérialisé en JSON pour
|
||||
alimenter `OffreFournisseur.ClasseFormulaireId` /
|
||||
`ClasseDevisId` à la sauvegarde.
|
||||
|
||||
## Voir aussi
|
||||
|
||||
- [Architecture.md](../Architecture.md) — racine.
|
||||
- [domaines-activite.md](domaines-activite.md) — arbre des
|
||||
activités et héritage.
|
||||
133
doc/architecture/postit-oidc.md
Normal file
133
doc/architecture/postit-oidc.md
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
# PostIt — Client desktop et authentification OIDC
|
||||
|
||||
> **Récapitulatif** : PostIt est un client desktop Avalonia 12
|
||||
> multi-cible (Desktop, Android, Browser/WASM) qui consomme l'API
|
||||
> Yavsc.Org. Authentification **public client + PKCE** via
|
||||
> **custom URI scheme** (RFC 8252 §7.1) — pas de loopback HTTP.
|
||||
> Détail dans cette page, racine de l'architecture : [Architecture.md](../Architecture.md).
|
||||
|
||||
## Rôle
|
||||
|
||||
PostIt est un client desktop (Avalonia 12, multi-cible : Desktop
|
||||
Linux/Windows, Android, Browser/WASM) qui consomme l'API
|
||||
Yavsc.Org pour les opérations CRUD du blog et d'autres
|
||||
ressources de l'utilisateur. C'est un **public client** : pas de
|
||||
secret, le secret est remplacé par PKCE.
|
||||
|
||||
## Flow d'authentification
|
||||
|
||||
L'authentification suit **RFC 8252 §7.1** (OAuth 2.0 for Native
|
||||
Apps — Custom URI Scheme Redirect), pas le pattern loopback
|
||||
historique. La séquence :
|
||||
|
||||
1. L'utilisateur clique **Se connecter** sur `LoginPage`.
|
||||
2. `YavscApiClient.LoginInteractiveAsync` demande à `OidcClient`
|
||||
de calculer l'authorize URL et de la passer à
|
||||
`CustomSchemeBrowser.InvokeAsync`.
|
||||
3. `CustomSchemeBrowser` ouvre le navigateur système sur l'authorize
|
||||
URL via `Process.Start(options.StartUrl) { UseShellExecute = true }`,
|
||||
puis **attend** sur un `TaskCompletionSource<string>` alimenté
|
||||
par un named pipe `PostIt.OidcCallback`.
|
||||
4. L'utilisateur s'authentifie sur l'IdP. Le navigateur redirige
|
||||
vers `postit://callback?code=***&state=…`.
|
||||
5. L'OS, qui a le scheme `postit://` enregistré, **lance une 2ᵉ
|
||||
instance** de PostIt avec l'URL en argument.
|
||||
6. La 2ᵉ instance lit `Environment.GetCommandLineArgs()` *avant*
|
||||
qu'Avalonia ne boote (`PostIt.Desktop.Program.Main`),
|
||||
détecte l'URL via `SchemeUrlDetector.FindCallbackUrl`, l'écrit
|
||||
sur le named pipe puis sort par `Environment.Exit(0)`. Aucune
|
||||
fenêtre n'est créée.
|
||||
7. L'instance vivante reçoit l'URL via le pipe, complète la TCS,
|
||||
`OidcClient` échange le code contre les tokens (access +
|
||||
refresh + id) et les persiste dans `~/.config/PostIt/tokens.json`.
|
||||
|
||||
## Pourquoi le scheme custom plutôt que loopback
|
||||
|
||||
Le loopback (`http://127.0.0.1:7890/callback`) oblige l'app à
|
||||
ouvrir un port TCP, à le publier comme redirect URI dans l'IdP,
|
||||
et à gérer la course "le navigateur revient-il à temps ?".
|
||||
Le scheme custom transfère la livraison du callback à l'OS :
|
||||
le navigateur tape sur une URL que l'OS sait router vers PostIt,
|
||||
pas vers un serveur HTTP.
|
||||
|
||||
## Composants partagés (`PostIt/`)
|
||||
|
||||
| Composant | Rôle |
|
||||
|---------------------------------|-------------------------------------------------------------------|
|
||||
| `Services/OidcLoginPhase` | Enum des étapes du flow : `Idle / Discovering / OpeningBrowser / AwaitingCallback / ExchangingCode / Success / Error` |
|
||||
| `Services/YavscApiClient` | Client HTTP de l'API Yavsc. Porte `LoginInteractiveAsync(IProgress<OidcLoginPhase>)` et `TrySilentLoginAsync`. Refresh silencieux sur 401 et sur access-token bientôt expiré. |
|
||||
| `Services/SingleInstance` | Named-pipe helper. `TryHandOffAsync` côté 2ᵉ instance, `StartServerAsync` côté instance vivante. |
|
||||
| `Services/CustomSchemeBrowser` | `IBrowser` OidcClient qui ouvre le système + attend le pipe. |
|
||||
| `Services/SchemeUrlDetector` | Détection pure, testable, du `postit://callback` dans argv. |
|
||||
| `Services/TokenStore` | Persiste `RefreshTokenRecord` (access/refresh/id + expiry). Mode 0600 sur POSIX. |
|
||||
| `ViewModels/SessionStatusViewModel` | VM du bandeau persistant Connecté/Déconnecté + Logout. |
|
||||
| `Views/SessionStatusBanner` | Bandeau affiché en haut de `MainWindow`, visible sur toutes les pages. |
|
||||
|
||||
## Plateformes (`PostIt.Desktop`, `PostIt.Android`, `PostIt.Browser`)
|
||||
|
||||
Chaque plateforme injecte son propre `IBrowser` via
|
||||
`Platform.CreateBrowser` :
|
||||
|
||||
- **Desktop** : `CustomSchemeBrowser` + scheme `postit://`.
|
||||
- **Android** : Chrome Custom Tabs via `AndroidSystemBrowser`,
|
||||
scheme `android://postit-signin` (déclaré comme
|
||||
`<activity-alias>` dans `AndroidManifest.xml`). MainActivity est
|
||||
`SingleTask` → `OnNewIntent` livre l'URL à `AndroidOidcCallbackSink`.
|
||||
- **Browser (WASM)** : pas de process distinct → N/A.
|
||||
|
||||
## UX observable
|
||||
|
||||
`LoginPage` affiche en continu la **phase** courante du flow
|
||||
(badge `PhaseLabel`) en plus du `StatusMessage` textuel :
|
||||
|
||||
| Phase | Signification |
|
||||
|--------------------|------------------------------------------------------------------|
|
||||
| `Discovering` | Fetch de `/.well-known/openid-configuration`. |
|
||||
| `OpeningBrowser` | Navigateur système ouvert, on attend que l'utilisateur revienne. |
|
||||
| `AwaitingCallback` | (idem `OpeningBrowser` aujourd'hui — couvre la fenêtre pipe). |
|
||||
| `ExchangingCode` | Trade du `code` contre les tokens à `/connect/token`. |
|
||||
| `Success` / `Error`| Issue du flow. |
|
||||
|
||||
Si le badge reste bloqué sur `OpeningBrowser`, **l'OS n'a jamais
|
||||
relancé PostIt avec l'URL `postit://callback`** : le scheme
|
||||
handler n'est pas enregistré correctement.
|
||||
|
||||
## Persistance et reprise au démarrage
|
||||
|
||||
Au boot, `App.OnFrameworkInitializationCompleted` :
|
||||
|
||||
1. Construit le `TokenStore` (~/.config/PostIt/tokens.json) et
|
||||
`YavscApiClient`. Ce dernier charge `_tokens = store.Load()`
|
||||
dans son constructeur.
|
||||
2. Ouvre `MainWindow` avec `HomePage` comme racine.
|
||||
3. Sur l'événement `Opened`, appelle `TrySilentLoginAsync` :
|
||||
- si l'access token a encore `> RefreshSkew` (60 s) de vie →
|
||||
`Success` immédiat, push `MainPage`.
|
||||
- sinon, tente un refresh via le refresh token. Si l'IdP
|
||||
accepte → push `MainPage`. Si l'IdP rejette (révocation,
|
||||
vol, expiration du refresh) → `_store.Clear()`, retour à
|
||||
`HomePage`.
|
||||
|
||||
Le bandeau `SessionStatusBanner` reflète `Api.HasValidSession`
|
||||
en continu et expose le bouton **Se déconnecter**, qui appelle
|
||||
`Api.LogoutAsync()` (purge du store) puis `PopToRootAsync`
|
||||
pour ramener sur `HomePage`.
|
||||
|
||||
## Garanties testées
|
||||
|
||||
- `LoginInteractiveAsync` émet `Discovering → OpeningBrowser →
|
||||
ExchangingCode → Success` (ou `Error` si pas de navigateur).
|
||||
- `TrySilentLoginAsync` retourne `false` si pas de bundle sur
|
||||
disque, `true` si l'access est encore valide, `true` après un
|
||||
refresh réussi.
|
||||
- `SchemeUrlDetector.FindCallbackUrl` reconnaît l'URL en argv,
|
||||
case-insensitive, refuse les faux positifs (`postit-…://`),
|
||||
retourne le premier match de manière déterministe.
|
||||
|
||||
Le chemin "refresh token rejeté → purge du store" est testé
|
||||
manuellement ; le stub `OidcStubAuthority` ne sait pas encore
|
||||
rejeter un refresh token précis — extension future.
|
||||
|
||||
## Voir aussi
|
||||
|
||||
- [Architecture.md](../Architecture.md) — racine.
|
||||
79
doc/architecture/workflow-multi-parties.md
Normal file
79
doc/architecture/workflow-multi-parties.md
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
# Workflow multi-parties
|
||||
|
||||
> **Récapitulatif** : Yavsc orchestre des projets multi-parties
|
||||
> (client, fournisseur, coordinateur) avec mise en relation,
|
||||
> sous-traitance et signature de licence en amont de la production.
|
||||
> Détail dans cette page, racine de l'architecture : [Architecture.md](../Architecture.md).
|
||||
|
||||
## Rôle de chaque partie
|
||||
|
||||
| Rôle | Type de compte | Description |
|
||||
|---------------|--------------------|-----------------------------------------------------------|
|
||||
| Client | Pro ou particulier | Exprime le besoin, valide les devis, co-signe la licence |
|
||||
| Fournisseur | Pro | Répond aux besoins, peut sous-traiter |
|
||||
| Coordinateur | Pro ou particulier | Orchestre le projet sans relation de facturation directe |
|
||||
|
||||
## Qui peut initier un projet
|
||||
|
||||
Le projet peut être initié par n'importe quelle partie :
|
||||
|
||||
- Un **client** (particulier ou pro) qui exprime un besoin
|
||||
- Un **fournisseur** qui propose une offre ou monte un collectif
|
||||
- Un **tiers coordinateur** qui orchestre sans être client ni prestataire
|
||||
|
||||
## Sous-traitance
|
||||
|
||||
Un fournisseur peut faire appel à d'autres fournisseurs,
|
||||
**avec accord explicite du client**. Chaque sous-traitant :
|
||||
|
||||
- Est visible dans le projet
|
||||
- Co-signe l'accord de licence
|
||||
- Peut recevoir un devis distinct
|
||||
|
||||
## Distinction B2B / B2C
|
||||
|
||||
La distinction est portée par le **type de compte** :
|
||||
|
||||
- Compte **pro** : SIRET, TVA, facturation professionnelle
|
||||
- Compte **particulier** : usage personnel, sans obligations fiscales pro
|
||||
|
||||
Un projet peut mélanger les deux (ex : un particulier client,
|
||||
plusieurs prestataires pro) — Yavsc n'impose pas d'homogénéité.
|
||||
|
||||
## États d'un projet
|
||||
|
||||
```
|
||||
Initié → En recherche de parties → Devis en cours
|
||||
→ Accord de licence signé → En production
|
||||
→ Livré → Publié (si licence libre)
|
||||
```
|
||||
|
||||
## Contrainte clé
|
||||
|
||||
L'accord de licence est établi **avant** tout début de production,
|
||||
signé (ou validé) par toutes les parties : client, fournisseurs,
|
||||
et sous-traitants éventuels.
|
||||
|
||||
## Domaine musical — Titres collaboratifs
|
||||
|
||||
Permet la production collaborative de titres musicaux sous licence
|
||||
libre, à partir de la mise en relation assurée par Yavsc.
|
||||
|
||||
### Formats supportés
|
||||
|
||||
- Audio (ex : WAV, FLAC, MP3)
|
||||
- Partition (ex : MusicXML, LilyPond, PDF)
|
||||
- MIDI
|
||||
|
||||
### Flux de production
|
||||
|
||||
1. Un client exprime un besoin musical
|
||||
2. Des prestataires répondent avec des devis
|
||||
3. Un **consensus est établi en amont** entre le client et les
|
||||
contributeurs sur la licence du livrable final
|
||||
4. La collaboration produit les fichiers
|
||||
5. Le titre est publié sous la licence choisie
|
||||
|
||||
## Voir aussi
|
||||
|
||||
- [Architecture.md](../Architecture.md) — racine.
|
||||
Loading…
Add table
Add a link
Reference in a new issue