@liorian/sdk 0.31.0-alpha.61fe5c2 → 0.38.0-alpha.7aa2e5b
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/package.json +2 -2
- package/schemas/module.schema.json +1 -1
- package/src/application/service/auth-user.service.ts +114 -4
- package/src/application/service/config-module-api.service.ts +95 -0
- package/src/application/service/module-installation-api.service.ts +118 -0
- package/src/application/service/notifications-push.service.ts +14 -0
- package/src/application/service/organizations-api-service.ts +8 -0
- package/src/domain/config/app.config.ts +1 -0
- package/src/domain/entities/catalog.interface.ts +3 -0
- package/src/domain/entities/index.ts +1 -0
- package/src/domain/entities/module-configuration.interface.ts +68 -0
- package/src/domain/entities/module-installation.interface.ts +165 -0
- package/src/domain/entities/module.interface.ts +4 -0
- package/src/domain/enums/user-profile.enum.ts +4 -4
- package/src/infrastructure/hooks/index.ts +2 -0
- package/src/infrastructure/hooks/module-installation.ts +168 -0
- package/src/infrastructure/hooks/use-sso-login.ts +19 -0
- package/src/infrastructure/module-installer/declare-module-from-manifest.ts +62 -0
- package/src/infrastructure/module-installer/index.ts +4 -0
- package/src/infrastructure/module-installer/module-installer.service.ts +138 -0
- package/src/infrastructure/providers/auth.provider.tsx +66 -4
- package/src/infrastructure/providers/modules-guard.provider.tsx +43 -22
- package/src/infrastructure/providers/query-client.ts +40 -0
- package/src/infrastructure/providers/query.provider.tsx +3 -12
- package/src/infrastructure/stores/auth-user-connected.store.ts +17 -1
- package/src/infrastructure/stores/index.ts +1 -1
- package/src/infrastructure/stores/module-installation.store.ts +265 -0
- package/src/infrastructure/stores/module.store.ts +45 -24
- package/src/infrastructure/utilities/activity-lock.service.ts +7 -1
- package/src/infrastructure/utilities/api-service.ts +59 -23
- package/src/infrastructure/utilities/auth-logout.util.ts +7 -6
- package/src/infrastructure/utilities/index.tsx +3 -0
- package/src/infrastructure/utilities/jwt.util.ts +75 -0
- package/src/infrastructure/utilities/local-data-purge.util.ts +324 -0
- package/src/infrastructure/utilities/session-events.ts +41 -0
- package/src/infrastructure/utilities/sso-auth.util.ts +127 -15
- package/src/presentation/components/auth/sso-callback.view.tsx +28 -4
- package/src/presentation/components/auth-lock-screen.tsx +52 -4
- package/src/presentation/components/index.tsx +2 -0
- package/src/presentation/components/module-configuration/index.tsx +3 -0
- package/src/presentation/components/module-configuration/module-configuration.modal.tsx +48 -0
- package/src/presentation/components/module-configuration/module-configuration.provider.tsx +178 -0
- package/src/presentation/components/module-configuration/module-configuration.steps.tsx +112 -0
- package/src/presentation/components/module-installation/index.ts +7 -0
- package/src/presentation/components/module-installation/module-compatibility-badge.tsx +65 -0
- package/src/presentation/components/module-installation/module-install-progress.tsx +95 -0
- package/src/presentation/components/module-installation/module-permission-review.tsx +134 -0
- package/src/presentation/components/module-installation/module-state-badge.tsx +55 -0
- package/src/presentation/components/module-installation/module-version-selector.tsx +67 -0
- package/src/presentation/components/module-runtime/declarative/blocks/declarative-chart-block.tsx +124 -0
- package/src/presentation/components/module-runtime/declarative/blocks/declarative-datagrid-block.tsx +220 -0
- package/src/presentation/components/module-runtime/declarative/blocks/declarative-form-block.tsx +132 -0
- package/src/presentation/components/module-runtime/declarative/blocks/declarative-form-fields.tsx +213 -0
- package/src/presentation/components/module-runtime/declarative/blocks/declarative-kpi-block.tsx +41 -0
- package/src/presentation/components/module-runtime/declarative/blocks/declarative-settings-block.tsx +200 -0
- package/src/presentation/components/module-runtime/declarative/blocks/declarative-widgets-block.tsx +58 -0
- package/src/presentation/components/module-runtime/declarative/declarative-data.ts +83 -0
- package/src/presentation/components/module-runtime/declarative/declarative-engine.tsx +95 -0
- package/src/presentation/components/module-runtime/declarative/declarative-types.ts +159 -0
- package/src/presentation/components/module-runtime/declarative-host.tsx +63 -0
- package/src/presentation/components/module-runtime/iframe-host.tsx +54 -0
- package/src/presentation/components/module-runtime/use-module-route.ts +76 -0
- package/src/presentation/index.tsx +1 -0
- package/src/presentation/modals/components/ModalWrapper.tsx +14 -8
- package/src/presentation/modals/types/modal.type.ts +2 -0
- package/src/presentation/settings/index.tsx +2 -0
- package/src/presentation/settings/settings.tsx +173 -0
- package/src/presentation/ui/alert-dialog.tsx +11 -1
- package/src/presentation/ui/combobox.tsx +8 -2
- package/src/presentation/ui/dialog.tsx +21 -14
- package/src/presentation/ui/drawer.tsx +9 -2
- package/src/presentation/ui/portal-container.tsx +48 -0
- package/src/presentation/ui/sheet.tsx +21 -14
package/README.md
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
Design system, shell applicatif et socle partagé du monorepo **Liorian**.
|
|
4
4
|
|
|
5
5
|
Le package concentre l'ensemble des briques réutilisables utilisées par les hôtes frontend du
|
|
6
|
-
monorepo (`
|
|
7
|
-
`
|
|
6
|
+
monorepo (`apps/liorian-socle`, `apps/liorian-auth`, `apps/liorian-connect`,
|
|
7
|
+
`apps/liorian-console`, `apps/liorian-store`) ainsi que par les modules externes
|
|
8
8
|
déclarés par les applications : primitives UI (style shadcn), thème **Katon**, composants
|
|
9
9
|
composés (`DataGrid`, modales, sheets, lecteur multimédia…), providers, hooks, stores zustand,
|
|
10
10
|
routines, services API, domaines, enums et utilitaires.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@liorian/sdk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Core package for the
|
|
3
|
+
"version": "0.38.0-alpha.7aa2e5b",
|
|
4
|
+
"description": "Core package for the Liora (application shell, domain, infrastructure, UI design system).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"publishConfig": {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
3
|
"$id": "module-manifest.schema.json",
|
|
4
|
-
"$comment": "Schéma canonique du manifest.json des modules Liorian (@liorian/sdk). Référencé par les modules du socle (
|
|
4
|
+
"$comment": "Schéma canonique du manifest.json des modules Liorian (@liorian/sdk). Référencé par les modules du socle (apps/liorian-socle/src/modules/*) et les modules externes (apps/liorian-socle/library/modules/*).",
|
|
5
5
|
"title": "Module Manifest (Liorian)",
|
|
6
6
|
"description": "Manifeste d'installation et de déclaration d'un module Liorian. Ce fichier décrit l'identité du module, sa compatibilité (plateformes, versions socle/API), ses permissions, ses scopes OAuth, ses capacités Tauri et ses prérequis fonctionnels (requirements/optionalRequirements). Les dépendances npm d'exécution et de développement sont portées par le `package.json` du module. Il est la source de vérité des métadonnées techniques d'un module, consommée par le Module Store et l'outillage de distribution.",
|
|
7
7
|
"type": "object",
|
|
@@ -7,6 +7,10 @@ import {UserInterface} from "../../domain/entities/user.interface";
|
|
|
7
7
|
import {UserAuthResponseInterface} from "../../domain/entities/user-auth.interface";
|
|
8
8
|
import {OrganizationInterface} from "../../domain/entities/organization.interface";
|
|
9
9
|
import {AuthConfig} from "../../domain/config/auth.config";
|
|
10
|
+
import {ActivityLockService} from "../../infrastructure/utilities/activity-lock.service";
|
|
11
|
+
import {emitSessionExpired} from "../../infrastructure/utilities/session-events";
|
|
12
|
+
import {getTokenExpiration, isTokenExpired} from "../../infrastructure/utilities/jwt.util";
|
|
13
|
+
import {LocalDataPurgeService} from "../../infrastructure/utilities/local-data-purge.util";
|
|
10
14
|
|
|
11
15
|
const instance = new CacheStorageCapability<AuthCacheInterface>(AuthConfig.storages.sessionKey, CacheStorageType.LOCAL);
|
|
12
16
|
|
|
@@ -54,8 +58,35 @@ export class AuthUserService {
|
|
|
54
58
|
return instance.get('apiKey') ?? null;
|
|
55
59
|
}
|
|
56
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Timestamp d'expiration du jeton courant, ou `null` si le jeton est absent
|
|
63
|
+
* ou ne porte pas d'`exp` lisible. Permet de savoir *quand* la session
|
|
64
|
+
* expirera au lieu de le découvrir par une série de 401.
|
|
65
|
+
*/
|
|
66
|
+
static getTokenExpiration(): number | null {
|
|
67
|
+
return getTokenExpiration(this.getToken());
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Vrai si un jeton est présent **et** non expiré.
|
|
72
|
+
*
|
|
73
|
+
* L'expiration est évaluée localement sur la claim `exp` du JWT : sans
|
|
74
|
+
* cela, un jeton expiré depuis des heures reste considéré comme
|
|
75
|
+
* « connecté » jusqu'au prochain appel à l'API — l'application traverse
|
|
76
|
+
* alors une fenêtre où elle s'affiche connectée mais ne peut plus rien
|
|
77
|
+
* faire, et l'écran de connexion peut se retrouver dans un état
|
|
78
|
+
* incohérent.
|
|
79
|
+
*
|
|
80
|
+
* Un jeton non-JSON (opaque) n'est jamais considéré expiré ici : seule
|
|
81
|
+
* l'API peut trancher.
|
|
82
|
+
*/
|
|
83
|
+
static isTokenValid(): boolean {
|
|
84
|
+
const token = this.getToken();
|
|
85
|
+
return !!token && !isTokenExpired(token);
|
|
86
|
+
}
|
|
87
|
+
|
|
57
88
|
static isAuthenticated(): boolean {
|
|
58
|
-
return
|
|
89
|
+
return this.isTokenValid();
|
|
59
90
|
}
|
|
60
91
|
|
|
61
92
|
static async setUser(user: UserInterface) {
|
|
@@ -83,21 +114,100 @@ export class AuthUserService {
|
|
|
83
114
|
await instance.set('apiKey', apiKey).save();
|
|
84
115
|
}
|
|
85
116
|
|
|
117
|
+
/**
|
|
118
|
+
* Installe une session neuve (connexion directe ou échange du code OAuth).
|
|
119
|
+
*
|
|
120
|
+
* Effets de bord :
|
|
121
|
+
* - l'organisation courante et la clé d'API de la session précédente sont
|
|
122
|
+
* purgées : elles portent sur l'identité et le périmètre de l'ancienne
|
|
123
|
+
* session et seraient sinon renvoyées avec le **nouveau** jeton
|
|
124
|
+
* (`X-Organization-Id`, `X-API-KEY`), ce qui est à la fois inutile et
|
|
125
|
+
* faussé ;
|
|
126
|
+
* - le verrou d'inactivité, **persisté** dans `localStorage`, est levé.
|
|
127
|
+
* Sans cela, un verrou laissé actif survit à la reconnexion :
|
|
128
|
+
* `AuthProvider`/`AuthGuard` ré-affichent alors immédiatement l'écran de
|
|
129
|
+
* verrouillage et l'utilisateur reste bloqué sur l'écran de connexion
|
|
130
|
+
* malgré une session parfaitement valide.
|
|
131
|
+
*/
|
|
86
132
|
static async setSession(payload: UserAuthResponseInterface) {
|
|
133
|
+
// Purge préalable des traces du compte précédent : sans cela, les
|
|
134
|
+
// données locales (modules activés, panier, notifications lues,
|
|
135
|
+
// brouillons…) survivent et corrompent la session qui s'installe —
|
|
136
|
+
// éventuellement au profit d'un autre utilisateur sur le même
|
|
137
|
+
// navigateur. La purge est sélective (préférences d'appareil
|
|
138
|
+
// conservées) et best-effort : elle ne bloque jamais l'installation.
|
|
139
|
+
// Les secrets PKCE lus avant l'échange ont déjà été copiés en mémoire
|
|
140
|
+
// par l'appelant, leur suppression ici est donc sans danger.
|
|
141
|
+
try {
|
|
142
|
+
await LocalDataPurgeService.purge('new-session');
|
|
143
|
+
} catch {
|
|
144
|
+
// Best-effort : l'installation de la session prime.
|
|
145
|
+
}
|
|
146
|
+
|
|
87
147
|
const {user, token, device, organizations} = payload;
|
|
88
148
|
instance
|
|
89
149
|
.set('user', user)
|
|
90
150
|
.set('token', token)
|
|
91
151
|
.set('device', device)
|
|
92
|
-
.set('organizations', organizations)
|
|
152
|
+
.set('organizations', organizations)
|
|
153
|
+
.set('currentOrganization', null)
|
|
154
|
+
.set('apiKey', null);
|
|
93
155
|
|
|
94
156
|
await instance.save();
|
|
95
157
|
setCookie('token', token);
|
|
96
|
-
}
|
|
97
158
|
|
|
98
|
-
|
|
159
|
+
// S'authentifier, c'est déverrouiller : la nouvelle session prend le
|
|
160
|
+
// pas sur tout verrou d'inactivité résiduel.
|
|
161
|
+
ActivityLockService.unlock();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Purge **totale** des données locales rattachées au compte.
|
|
166
|
+
*
|
|
167
|
+
* Unique point de sortie de la session locale, emprunté par la déconnexion
|
|
168
|
+
* comme par l'expiration. Au-delà de la session chiffrée et du cookie
|
|
169
|
+
* `token`, elle délègue à {@link LocalDataPurgeService} l'effacement de
|
|
170
|
+
* toutes les autres traces du compte :
|
|
171
|
+
*
|
|
172
|
+
* - `localStorage` : verrou d'inactivité, modules activés, état
|
|
173
|
+
* d'installation, secrets PKCE OAuth, notifications lues, modules récents ;
|
|
174
|
+
* - `sessionStorage` : brouillon des formulaires en cours de saisie ;
|
|
175
|
+
* - cookies rattachés au compte ;
|
|
176
|
+
* - mémoire : cache React Query, magasins Zustand (via les hooks
|
|
177
|
+
* enregistrés par leurs auteurs).
|
|
178
|
+
*
|
|
179
|
+
* Sans cela, ces données survivent à la fin de session et réapparaissent à la
|
|
180
|
+
* connexion suivante — éventuellement au profit d'un autre utilisateur sur le
|
|
181
|
+
* même navigateur.
|
|
182
|
+
*/
|
|
183
|
+
static async clear(reason: string = "unknown") {
|
|
99
184
|
await instance.clear().save();
|
|
100
185
|
deleteCookie('token');
|
|
186
|
+
ActivityLockService.unlock();
|
|
187
|
+
await LocalDataPurgeService.purge(reason);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Point d'entrée unique d'invalidation de session.
|
|
192
|
+
*
|
|
193
|
+
* Purge la session locale puis notifie les abonnés (`AuthProvider`) afin
|
|
194
|
+
* que l'interface passe en écran de connexion. L'opération est
|
|
195
|
+
* **idempotente** : une rafale de 401 simultanées n'émet qu'un seul
|
|
196
|
+
* événement, et une application déjà déconnectée n'émet rien.
|
|
197
|
+
*
|
|
198
|
+
* @returns `true` si une session a effectivement été invalidée.
|
|
199
|
+
*/
|
|
200
|
+
static async invalidate(source: string = 'unknown'): Promise<boolean> {
|
|
201
|
+
// Attend le déchiffrement du stockage : sans cela, une 401 reçue avant
|
|
202
|
+
// la fin de l'hydratation verrait une session « absente » et
|
|
203
|
+
// n'émettrait aucune invalidation.
|
|
204
|
+
await this.ready();
|
|
205
|
+
|
|
206
|
+
if (!instance.get('token')) return false;
|
|
207
|
+
|
|
208
|
+
await this.clear(source);
|
|
209
|
+
emitSessionExpired({source});
|
|
210
|
+
return true;
|
|
101
211
|
}
|
|
102
212
|
|
|
103
213
|
static getCsrfToken(): string | null {
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import {ApiService} from "../../infrastructure/utilities/api-service";
|
|
2
|
+
import {FetchResponseInterface} from "../../domain/typing/response";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Service API du CRUD auto-généré des modules `CONFIGURATION`
|
|
6
|
+
* (T-504/S-028, FR-026) — cible `liorian-api-core` `/config-modules`.
|
|
7
|
+
*
|
|
8
|
+
* L'organisation est portée par l'en-tête `X-Organization-Id` (middleware
|
|
9
|
+
* `OrganizationApiKeyMiddleware`) ; le jeton proxy scopé d'un module iframe
|
|
10
|
+
* est attaché par la couche HTTP du socle (T-506).
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export interface ConfigModuleRecord {
|
|
14
|
+
id: string;
|
|
15
|
+
data: Record<string, any>;
|
|
16
|
+
createdAt: string | null;
|
|
17
|
+
updatedAt: string | null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface ConfigModuleListResponse {
|
|
21
|
+
rows: ConfigModuleRecord[];
|
|
22
|
+
total: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface ConfigModuleAggregatePayload {
|
|
26
|
+
fn?: "count" | "sum" | "mean";
|
|
27
|
+
/** Champ agrégé — omitir pour `__count`. */
|
|
28
|
+
field?: string;
|
|
29
|
+
/** Clé de regroupement (charts) — produit `rows: [{key, value}]`. */
|
|
30
|
+
groupBy?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface ConfigModuleAggregateScalar {
|
|
34
|
+
value: number;
|
|
35
|
+
field: string;
|
|
36
|
+
fn: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface ConfigModuleAggregateGrouped {
|
|
40
|
+
fn: string;
|
|
41
|
+
field: string;
|
|
42
|
+
groupBy: string;
|
|
43
|
+
rows: Array<{key: string; value: number}>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export type ConfigModuleAggregateResponse = ConfigModuleAggregateScalar | ConfigModuleAggregateGrouped;
|
|
47
|
+
|
|
48
|
+
export class ConfigModuleApiService extends ApiService {
|
|
49
|
+
|
|
50
|
+
/** Lister les enregistrements d'une ressource (`?search=`). */
|
|
51
|
+
static async list(moduleIdentifier: string, resource: string, options?: {search?: string}) {
|
|
52
|
+
return await this.get<FetchResponseInterface<ConfigModuleListResponse>>(
|
|
53
|
+
`/config-modules/${moduleIdentifier}/${resource}`,
|
|
54
|
+
options?.search ? {search: options.search} : undefined,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Obtenir un enregistrement. */
|
|
59
|
+
static async findOne(moduleIdentifier: string, resource: string, recordId: string) {
|
|
60
|
+
return await this.get<FetchResponseInterface<ConfigModuleRecord>>(
|
|
61
|
+
`/config-modules/${moduleIdentifier}/${resource}/${recordId}`,
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Créer un enregistrement. */
|
|
66
|
+
static async create(moduleIdentifier: string, resource: string, data: Record<string, any>) {
|
|
67
|
+
return await this.post<FetchResponseInterface<ConfigModuleRecord>>(
|
|
68
|
+
`/config-modules/${moduleIdentifier}/${resource}`,
|
|
69
|
+
data,
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Mettre à jour un enregistrement. */
|
|
74
|
+
static async update(moduleIdentifier: string, resource: string, recordId: string, data: Record<string, any>) {
|
|
75
|
+
return await this.put<FetchResponseInterface<ConfigModuleRecord>>(
|
|
76
|
+
`/config-modules/${moduleIdentifier}/${resource}/${recordId}`,
|
|
77
|
+
data,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Supprimer (soft-delete) un enregistrement. */
|
|
82
|
+
static async remove(moduleIdentifier: string, resource: string, recordId: string) {
|
|
83
|
+
return await this.delete<FetchResponseInterface<unknown>>(
|
|
84
|
+
`/config-modules/${moduleIdentifier}/${resource}/${recordId}`,
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Agrégat générique — `kpi` (scalaire) ou `chart` (groupé via `groupBy`). */
|
|
89
|
+
static async aggregate(moduleIdentifier: string, resource: string, payload: ConfigModuleAggregatePayload = {}) {
|
|
90
|
+
return await this.post<FetchResponseInterface<ConfigModuleAggregateResponse>>(
|
|
91
|
+
`/config-modules/${moduleIdentifier}/${resource}/_aggregate`,
|
|
92
|
+
payload,
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import {ApiService} from "../../infrastructure/utilities/api-service";
|
|
2
|
+
import {FetchResponseInterface} from "../../domain/typing/response";
|
|
3
|
+
import type {
|
|
4
|
+
InstallModulePayloadInterface,
|
|
5
|
+
ModuleAssetTokenInterface,
|
|
6
|
+
ModuleInstallationItemInterface,
|
|
7
|
+
ModuleRegistryItemInterface,
|
|
8
|
+
ModuleResolutionResponseInterface,
|
|
9
|
+
ResolveModulePayloadInterface,
|
|
10
|
+
SyncInstallationsPayloadInterface,
|
|
11
|
+
SyncInstallationsResultInterface,
|
|
12
|
+
} from "../../domain/entities/module-installation.interface";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Service API du domaine `module-installation` (T-403).
|
|
16
|
+
*
|
|
17
|
+
* Cible `liorian-api-core` (base `/module-installation`) : résolution,
|
|
18
|
+
* installation, cycle de vie, sync et jetons d'artefacts.
|
|
19
|
+
*/
|
|
20
|
+
export class ModuleInstallationApiService extends ApiService {
|
|
21
|
+
|
|
22
|
+
private static org(organizationId: string) {
|
|
23
|
+
return `/module-installation/organizations/${organizationId}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Étape 2 — Résoudre la version compatible (T-301). */
|
|
27
|
+
static async resolveModule(organizationId: string, payload: ResolveModulePayloadInterface) {
|
|
28
|
+
return await this.post<FetchResponseInterface<ModuleResolutionResponseInterface>>(
|
|
29
|
+
`${this.org(organizationId)}/resolve`,
|
|
30
|
+
payload,
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Étape 5 — Installer la version résolue (jeton mono-usage, ⚠️ AdminOrAbove). */
|
|
35
|
+
static async installModule(organizationId: string, payload: InstallModulePayloadInterface) {
|
|
36
|
+
return await this.post<FetchResponseInterface<ModuleInstallationItemInterface>>(
|
|
37
|
+
`${this.org(organizationId)}/installations`,
|
|
38
|
+
payload,
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Liste des installations (filtres `state`). */
|
|
43
|
+
static async listInstallations(organizationId: string, states?: string[]) {
|
|
44
|
+
const search = states && states.length ? `?state=${encodeURIComponent(states.join(','))}` : '';
|
|
45
|
+
return await this.get<FetchResponseInterface<ModuleInstallationItemInterface[]>>(
|
|
46
|
+
`${this.org(organizationId)}/installations${search}`,
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Détail d'une installation. */
|
|
51
|
+
static async getInstallation(organizationId: string, moduleIdentifier: string) {
|
|
52
|
+
return await this.get<FetchResponseInterface<ModuleInstallationItemInterface>>(
|
|
53
|
+
`${this.org(organizationId)}/installations/${moduleIdentifier}`,
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Registre pour le cache de rendu (ADR-006 : le core fait autorité). */
|
|
58
|
+
static async getRegistry(organizationId: string) {
|
|
59
|
+
return await this.get<FetchResponseInterface<ModuleRegistryItemInterface>>(
|
|
60
|
+
`${this.org(organizationId)}/installations/registry`,
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Activer un module installé (⚠️). */
|
|
65
|
+
static async enableInstallation(organizationId: string, moduleIdentifier: string) {
|
|
66
|
+
return await this.post<FetchResponseInterface<ModuleInstallationItemInterface>>(
|
|
67
|
+
`${this.org(organizationId)}/installations/${moduleIdentifier}/enable`,
|
|
68
|
+
{},
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Désactiver un module installé (⚠️). */
|
|
73
|
+
static async disableInstallation(organizationId: string, moduleIdentifier: string, reason?: string) {
|
|
74
|
+
return await this.post<FetchResponseInterface<ModuleInstallationItemInterface>>(
|
|
75
|
+
`${this.org(organizationId)}/installations/${moduleIdentifier}/disable`,
|
|
76
|
+
{reason},
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Rollback vers la version précédente (⚠️). */
|
|
81
|
+
static async rollbackInstallation(organizationId: string, moduleIdentifier: string, targetVersion?: string) {
|
|
82
|
+
return await this.post<FetchResponseInterface<ModuleInstallationItemInterface>>(
|
|
83
|
+
`${this.org(organizationId)}/installations/${moduleIdentifier}/rollback`,
|
|
84
|
+
{targetVersion},
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Désinstaller + purge disque (⚠️). */
|
|
89
|
+
static async uninstallInstallation(organizationId: string, moduleIdentifier: string) {
|
|
90
|
+
return await this.delete<FetchResponseInterface<unknown>>(
|
|
91
|
+
`${this.org(organizationId)}/installations/${moduleIdentifier}`,
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Revalidation hors-ligne → en ligne. */
|
|
96
|
+
static async validateInstallation(organizationId: string, moduleIdentifier: string) {
|
|
97
|
+
return await this.post<FetchResponseInterface<ModuleInstallationItemInterface>>(
|
|
98
|
+
`${this.org(organizationId)}/installations/${moduleIdentifier}/validate`,
|
|
99
|
+
{},
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Sync bidirectionnel (FR-023 : corrige V7). */
|
|
104
|
+
static async syncInstallations(organizationId: string, payload: SyncInstallationsPayloadInterface) {
|
|
105
|
+
return await this.post<FetchResponseInterface<SyncInstallationsResultInterface>>(
|
|
106
|
+
`${this.org(organizationId)}/installations/sync`,
|
|
107
|
+
payload,
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Jeton d'accès iframe (TTL 24 h, T-310). */
|
|
112
|
+
static async issueAssetToken(organizationId: string, moduleIdentifier: string, version: string, deviceId?: string) {
|
|
113
|
+
return await this.post<FetchResponseInterface<ModuleAssetTokenInterface>>(
|
|
114
|
+
`/module-installation/artifacts/${moduleIdentifier}/${version}/assets-token`,
|
|
115
|
+
{deviceId},
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import {AppConfig} from "../../domain/config/app.config";
|
|
4
4
|
import {ActivityTrackerService} from "../../infrastructure/utilities/activity-tracker.service";
|
|
5
|
+
import {LocalDataPurgeService} from "../../infrastructure/utilities/local-data-purge.util";
|
|
5
6
|
import {isTauri} from "../../infrastructure/utilities/tauri.util";
|
|
6
7
|
import {NotificationsApiService} from "./notifications-api-service";
|
|
7
8
|
import {NotificationsSocketService} from "./notifications-socket-service";
|
|
@@ -158,6 +159,17 @@ export class NotificationsPushService {
|
|
|
158
159
|
notifications.forEach((notification) => this.seenNotificationIds.add(notification.id));
|
|
159
160
|
}
|
|
160
161
|
|
|
162
|
+
/**
|
|
163
|
+
* Vide l'état local (notifications déjà vues) en fin de session.
|
|
164
|
+
*
|
|
165
|
+
* La clé `localStorage` est purgée par `LocalDataPurgeService` ; ce hook vide
|
|
166
|
+
* le `Set` en mémoire, qui se repeuplerait à la lecture — et se
|
|
167
|
+
* réenregistrerait — avec les notifications du compte suivant.
|
|
168
|
+
*/
|
|
169
|
+
static resetSeenIds(): void {
|
|
170
|
+
this.seenNotificationIds = new Set<string>();
|
|
171
|
+
}
|
|
172
|
+
|
|
161
173
|
protected static async poll(): Promise<void> {
|
|
162
174
|
try {
|
|
163
175
|
const notifications = await this.fetchUnreadNotifications();
|
|
@@ -297,3 +309,5 @@ export class NotificationsPushService {
|
|
|
297
309
|
}
|
|
298
310
|
|
|
299
311
|
}
|
|
312
|
+
|
|
313
|
+
LocalDataPurgeService.onMemoryPurge(() => NotificationsPushService.resetSeenIds());
|
|
@@ -85,6 +85,14 @@ export class OrganizationsApiService extends ApiService {
|
|
|
85
85
|
return await this.delete(`/organization-preferences/${id}`);
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
+
static async getConsoleConsent() {
|
|
89
|
+
return await this.get('/organization-consent');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
static async setConsoleConsent(granted: boolean) {
|
|
93
|
+
return await this.put('/organization-consent', {granted});
|
|
94
|
+
}
|
|
95
|
+
|
|
88
96
|
// Organization Document Types
|
|
89
97
|
static async createDocumentType(payload: any) {
|
|
90
98
|
return await this.post('/organization-document-types/', payload);
|
|
@@ -32,6 +32,7 @@ export const AppConfig = {
|
|
|
32
32
|
CORE_SOCKET_HOST: process.env.NEXT_PUBLIC_CORE_SOCKET_HOST as unknown as string,
|
|
33
33
|
CONNECT_API_HOST: process.env.NEXT_PUBLIC_CONNECT_API_HOST as unknown as string,
|
|
34
34
|
STORE_API_HOST: process.env.NEXT_PUBLIC_STORE_API_HOST as unknown as string,
|
|
35
|
+
CONSOLE_API_HOST: process.env.NEXT_PUBLIC_CONSOLE_API_HOST as unknown as string,
|
|
35
36
|
STORAGE_API_HOST: process.env.NEXT_PUBLIC_STORAGE_API_HOST as unknown as string,
|
|
36
37
|
|
|
37
38
|
API_TIMEOUT: process.env.NEXT_PUBLIC_API_TIMEOUT as unknown as number,
|
|
@@ -39,6 +39,7 @@ export interface CatalogModuleInterface {
|
|
|
39
39
|
isDefault: boolean;
|
|
40
40
|
type: CatalogModuleType;
|
|
41
41
|
external: boolean;
|
|
42
|
+
requiresActivationKey: boolean;
|
|
42
43
|
categoryId?: string | null;
|
|
43
44
|
category?: keyof typeof ModuleCategory | null;
|
|
44
45
|
editorId?: string | null;
|
|
@@ -58,6 +59,7 @@ export interface CreateCatalogModulePayloadInterface {
|
|
|
58
59
|
isDefault?: boolean;
|
|
59
60
|
type?: CatalogModuleType;
|
|
60
61
|
external?: boolean;
|
|
62
|
+
requiresActivationKey?: boolean;
|
|
61
63
|
categoryId?: string | null;
|
|
62
64
|
}
|
|
63
65
|
|
|
@@ -73,6 +75,7 @@ export interface UpdateCatalogModulePayloadInterface {
|
|
|
73
75
|
isDefault?: boolean;
|
|
74
76
|
type?: CatalogModuleType;
|
|
75
77
|
external?: boolean;
|
|
78
|
+
requiresActivationKey?: boolean;
|
|
76
79
|
categoryId?: string | null;
|
|
77
80
|
}
|
|
78
81
|
|
|
@@ -3,6 +3,7 @@ export * from "./activities.interface";
|
|
|
3
3
|
export * from "./media";
|
|
4
4
|
export * from "./catalog.interface";
|
|
5
5
|
export * from "./module-activation.interface";
|
|
6
|
+
export * from "./module-configuration.interface";
|
|
6
7
|
export * from "./module.interface";
|
|
7
8
|
export * from "./notification.interface";
|
|
8
9
|
export * from "./objectable.type";
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type React from "react";
|
|
2
|
+
import type {IconKey} from "../../presentation/icons/types";
|
|
3
|
+
import type {ModuleDeclarationInterface} from "./module.interface";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Données partagées entre les steps du modal `ModuleConfiguration`.
|
|
7
|
+
*/
|
|
8
|
+
export interface ModuleConfigurationDataInterface {
|
|
9
|
+
/** Clé d'activation saisie (uniquement si le module requiert une activation). */
|
|
10
|
+
serialKey?: string;
|
|
11
|
+
/** Valeurs libres collectées par les steps de configuration. */
|
|
12
|
+
settings: Record<string, any>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Props injectées à chaque composant de step déclaré par un module.
|
|
17
|
+
* Le développeur doit rendre réutilisable la section/le formulaire de la page
|
|
18
|
+
* de paramètres correspondante en acceptant ces props.
|
|
19
|
+
*/
|
|
20
|
+
export interface ModuleConfigurationStepPropsInterface<T extends Record<string, any> = Record<string, any>> {
|
|
21
|
+
/** Module en cours de configuration. */
|
|
22
|
+
module: ModuleDeclarationInterface;
|
|
23
|
+
/** Données courantes du stepper. */
|
|
24
|
+
data: Partial<T>;
|
|
25
|
+
/** Met à jour (merge) les données du stepper. */
|
|
26
|
+
updateData: (data: Partial<T>) => void;
|
|
27
|
+
/** Passe à l'étape suivante (déclenche les validateurs). */
|
|
28
|
+
next: () => void;
|
|
29
|
+
/** Revient à l'étape précédente. */
|
|
30
|
+
prev: () => void;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Déclaration d'une étape de configuration d'un module.
|
|
35
|
+
* Chaque étape correspond à une page/section du menu « Paramètres » du module.
|
|
36
|
+
*/
|
|
37
|
+
export interface ModuleConfigurationStepInterface<T extends Record<string, any> = Record<string, any>> {
|
|
38
|
+
/** Identifiant unique de l'étape. */
|
|
39
|
+
id: string;
|
|
40
|
+
/** Titre affiché dans le stepper. */
|
|
41
|
+
title: string;
|
|
42
|
+
/** Description optionnelle affichée sous le titre. */
|
|
43
|
+
description?: string;
|
|
44
|
+
/** Icône optionnelle. */
|
|
45
|
+
icon?: IconKey;
|
|
46
|
+
/** Si vrai, la navigation valide les champs HTML `required` de l'étape. */
|
|
47
|
+
required?: boolean;
|
|
48
|
+
/** Composant React réutilisable rendant la section/le formulaire. */
|
|
49
|
+
component: React.ComponentType<ModuleConfigurationStepPropsInterface<T>>;
|
|
50
|
+
/** Validateur optionnel de l'étape (peut rediriger vers une autre étape). */
|
|
51
|
+
validation?: (
|
|
52
|
+
data: Partial<T>,
|
|
53
|
+
updateData: (data: Partial<T>) => void,
|
|
54
|
+
) => Promise<number | string | { step: number | string; lockNavigation?: boolean } | void>;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Configuration initiale d'un module, exécutée avant sa première ouverture.
|
|
59
|
+
*/
|
|
60
|
+
export interface ModuleConfigurationInterface {
|
|
61
|
+
/**
|
|
62
|
+
* Si vrai, une étape d'activation par clé série est ajoutée en première position
|
|
63
|
+
* (également affichée tant que le module n'est pas activé pour l'organisation).
|
|
64
|
+
*/
|
|
65
|
+
requiresActivationKey?: boolean;
|
|
66
|
+
/** Étapes de configuration (une par page/section du menu Paramètres). */
|
|
67
|
+
steps?: ModuleConfigurationStepInterface[];
|
|
68
|
+
}
|