@nodefony/mongoose 10.0.0-alpha.1
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/LICENSE +544 -0
- package/README.md +97 -0
- package/dist/_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorate.js +9 -0
- package/dist/_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorateMetadata.js +6 -0
- package/dist/index.js +90 -0
- package/dist/nodefony/config/config.js +57 -0
- package/dist/nodefony/config/defineModuleConfig.js +60 -0
- package/dist/nodefony/entity/sessionEntity.js +63 -0
- package/dist/nodefony/entity/tokenEntity.js +184 -0
- package/dist/nodefony/entity/userEntity.js +106 -0
- package/dist/nodefony/entity/webAuthnCredentialEntity.js +97 -0
- package/dist/nodefony/entity/webhookEndpointEntity.js +109 -0
- package/dist/nodefony/interfaces/IMongooseConfig.js +1 -0
- package/dist/nodefony/interfaces/index.js +1 -0
- package/dist/nodefony/registerStores.js +89 -0
- package/dist/nodefony/service/MongooseService.js +95 -0
- package/dist/nodefony/src/MongooseTokenStore.js +237 -0
- package/dist/nodefony/src/MongooseUserRepository.js +205 -0
- package/dist/nodefony/src/MongooseWebAuthnCredentialStore.js +144 -0
- package/dist/nodefony/src/MongooseWebhookStore.js +181 -0
- package/dist/nodefony/src/SessionStorage.js +241 -0
- package/dist/nodefony/src/mongoOrder.js +49 -0
- package/dist/nodefony/src/orm-core/MongooseOrm.js +440 -0
- package/dist/nodefony/src/orm-core/MongooseRepository.js +300 -0
- package/dist/nodefony/src/orm-core/MongooseTransaction.js +53 -0
- package/dist/nodefony/src/orm-core/index.js +4 -0
- package/dist/types/index.d.ts +74 -0
- package/dist/types/nodefony/config/config.d.ts +21 -0
- package/dist/types/nodefony/config/defineModuleConfig.d.ts +26 -0
- package/dist/types/nodefony/entity/sessionEntity.d.ts +42 -0
- package/dist/types/nodefony/entity/tokenEntity.d.ts +59 -0
- package/dist/types/nodefony/entity/userEntity.d.ts +54 -0
- package/dist/types/nodefony/entity/webAuthnCredentialEntity.d.ts +61 -0
- package/dist/types/nodefony/entity/webhookEndpointEntity.d.ts +62 -0
- package/dist/types/nodefony/interfaces/IMongooseConfig.d.ts +17 -0
- package/dist/types/nodefony/interfaces/index.d.ts +1 -0
- package/dist/types/nodefony/registerStores.d.ts +37 -0
- package/dist/types/nodefony/service/MongooseService.d.ts +48 -0
- package/dist/types/nodefony/src/MongooseTokenStore.d.ts +126 -0
- package/dist/types/nodefony/src/MongooseUserRepository.d.ts +82 -0
- package/dist/types/nodefony/src/MongooseWebAuthnCredentialStore.d.ts +52 -0
- package/dist/types/nodefony/src/MongooseWebhookStore.d.ts +72 -0
- package/dist/types/nodefony/src/SessionStorage.d.ts +63 -0
- package/dist/types/nodefony/src/mongoOrder.d.ts +40 -0
- package/dist/types/nodefony/src/orm-core/MongooseOrm.d.ts +132 -0
- package/dist/types/nodefony/src/orm-core/MongooseRepository.d.ts +51 -0
- package/dist/types/nodefony/src/orm-core/MongooseTransaction.d.ts +37 -0
- package/dist/types/nodefony/src/orm-core/index.d.ts +9 -0
- package/docs/configuration.md +776 -0
- package/docs/index.md +881 -0
- package/package.json +97 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { SchemaDefinition } from "mongoose";
|
|
2
|
+
import type { IEntity } from "@nodefony/orm-core";
|
|
3
|
+
/**
|
|
4
|
+
* Schéma Mongoose du **store de credentials WebAuthn** `@nodefony/security`
|
|
5
|
+
* (pendant documentaire de la table Drizzle) — implémentation NoSQL d'
|
|
6
|
+
* `IWebAuthnCredentialStore` (passkeys).
|
|
7
|
+
*
|
|
8
|
+
* ⚠️ **`_id` = clé naturelle (String), PAS un ObjectId auto** : le contrat
|
|
9
|
+
* `IRepository` traduit le critère `{ id }` en `{ _id }` (cf `MongooseRepository`).
|
|
10
|
+
* Comme l'`id` d'un credential est un **identifiant base64url fourni par
|
|
11
|
+
* l'authenticator** (pas généré par Mongo), on force `_id: String` → le
|
|
12
|
+
* credentialId EST la clé primaire. Le virtuel `id` (activé par `MongooseOrm`,
|
|
13
|
+
* `toObject:{virtuals:true}`) renvoie `String(_id)` = le credentialId.
|
|
14
|
+
*
|
|
15
|
+
* ⚠️ **Horodatages = `Number` (epoch ms), `timestamps:false`** : `IWebAuthnCredential`
|
|
16
|
+
* porte des `number` (`Date.now()`) et l'appelant fournit `createdAt`.
|
|
17
|
+
*
|
|
18
|
+
* Le store ne lit/écrit JAMAIS `IWebAuthnCredential` directement : il traduit via
|
|
19
|
+
* {@link WebAuthnCredentialRow} (forme plate, `nickname: string | null`) — le
|
|
20
|
+
* contrat porte un `nickname?` optionnel et des champs `readonly`, que la
|
|
21
|
+
* frontière de persistance normalise.
|
|
22
|
+
*/
|
|
23
|
+
export declare const webAuthnCredentialSchema: SchemaDefinition;
|
|
24
|
+
/**
|
|
25
|
+
* Forme **plate** d'une ligne de credentials renvoyée par le repository Mongoose —
|
|
26
|
+
* `id` = virtuel (= `_id` = credentialId), `nickname: string | null`. Le store
|
|
27
|
+
* mappe `Row ↔ IWebAuthnCredential` (`nickname?` omis si `null`).
|
|
28
|
+
*/
|
|
29
|
+
export interface WebAuthnCredentialRow {
|
|
30
|
+
id: string;
|
|
31
|
+
userId: string;
|
|
32
|
+
publicKey: string;
|
|
33
|
+
signCount: number;
|
|
34
|
+
transports: string[];
|
|
35
|
+
backupEligible: boolean;
|
|
36
|
+
backupState: boolean;
|
|
37
|
+
uvInitialized: boolean;
|
|
38
|
+
nickname: string | null;
|
|
39
|
+
createdAt: number;
|
|
40
|
+
lastUsedAt: number | null;
|
|
41
|
+
}
|
|
42
|
+
/** Nom logique de l'entité (clé de lookup `getRepository`). */
|
|
43
|
+
export declare const WEBAUTHN_CREDENTIAL_ENTITY = "webauthn_credential";
|
|
44
|
+
/**
|
|
45
|
+
* Construit le descripteur d'entité du store de credentials pour un ORM nommé.
|
|
46
|
+
*
|
|
47
|
+
* Le `connector` est **dynamique** (nom du connecteur de l'app, ex. `"nodefony"`) : le
|
|
48
|
+
* schéma est statique mais sa liaison à un ORM dépend de la config → pas d'`@entity`
|
|
49
|
+
* figé (parité `createTokenEntities`). `timestamps:false`. À enregistrer **avant**
|
|
50
|
+
* `orm.connect()`.
|
|
51
|
+
*
|
|
52
|
+
* @param orm - clé de l'ORM cible dans le `ormRegistry`.
|
|
53
|
+
*/
|
|
54
|
+
export declare function createWebAuthnCredentialEntity(connector: string): IEntity;
|
|
55
|
+
/**
|
|
56
|
+
* Enregistre l'entité du store de credentials dans le `entityRegistry` pour un
|
|
57
|
+
* ORM donné. À appeler **avant** `orm.connect()` (le modèle est compilé au connect).
|
|
58
|
+
*
|
|
59
|
+
* @param connector - nom de la connexion cible (clé du registre).
|
|
60
|
+
*/
|
|
61
|
+
export declare function registerWebAuthnCredentialEntity(connector: string): void;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { SchemaDefinition } from "mongoose";
|
|
2
|
+
import type { IEntity } from "@nodefony/orm-core";
|
|
3
|
+
/**
|
|
4
|
+
* Schéma Mongoose du **store d'endpoints webhook** `@nodefony/security` (P6.13,
|
|
5
|
+
* pendant documentaire de la table Drizzle) — implémentation NoSQL d'
|
|
6
|
+
* `IWebhookStore` (registre durable des destinations notifiées).
|
|
7
|
+
*
|
|
8
|
+
* ⚠️ **`_id` = clé naturelle (String), PAS un ObjectId auto** : l'`id` d'un
|
|
9
|
+
* endpoint est un identifiant `wh_<random>` **fourni** (pas généré par Mongo) →
|
|
10
|
+
* on force `_id: String` → l'id EST la clé primaire. Le contrat `IRepository`
|
|
11
|
+
* traduit le critère `{ id }` en `{ _id }` ; le virtuel `id` (activé par
|
|
12
|
+
* `MongooseOrm`, `toObject:{virtuals:true}`) renvoie `String(_id)` = l'id.
|
|
13
|
+
*
|
|
14
|
+
* ⚠️ **Horodatages = `Number` (epoch ms), `timestamps:false`** : `IWebhookEndpoint`
|
|
15
|
+
* porte des `number` (`Date.now()`) et l'appelant fournit `createdAt`/`updatedAt`.
|
|
16
|
+
*
|
|
17
|
+
* `events` = tableau de strings ; `metadata` = objet libre (`Object`/Mixed, idem
|
|
18
|
+
* `tokenEntity.metadata`). Le store réécrit ces champs en bloc (pas de mutation
|
|
19
|
+
* partielle in-place) → pas de souci de change-tracking Mixed.
|
|
20
|
+
*/
|
|
21
|
+
export declare const webhookEndpointSchema: SchemaDefinition;
|
|
22
|
+
/**
|
|
23
|
+
* Forme **plate** d'une ligne d'endpoint renvoyée par le repository Mongoose —
|
|
24
|
+
* `id` = virtuel (= `_id` = l'id `wh_…`), `events` mutable (le contrat porte
|
|
25
|
+
* `readonly string[]`). `MongooseWebhookStore` mappe `Row ↔ IWebhookEndpoint`.
|
|
26
|
+
*/
|
|
27
|
+
export interface WebhookEndpointRow {
|
|
28
|
+
id: string;
|
|
29
|
+
url: string;
|
|
30
|
+
secretEnc: string;
|
|
31
|
+
events: string[];
|
|
32
|
+
enabled: boolean;
|
|
33
|
+
description: string | null;
|
|
34
|
+
tenantId: string | null;
|
|
35
|
+
createdBy: string | null;
|
|
36
|
+
createdAt: number;
|
|
37
|
+
updatedAt: number;
|
|
38
|
+
lastDeliveryAt: number | null;
|
|
39
|
+
lastDeliveryStatus: number | null;
|
|
40
|
+
lastDeliveryError: string | null;
|
|
41
|
+
failureCount: number;
|
|
42
|
+
metadata: Record<string, unknown>;
|
|
43
|
+
}
|
|
44
|
+
/** Nom logique de l'entité (clé de lookup `getRepository`). */
|
|
45
|
+
export declare const WEBHOOK_ENDPOINT_ENTITY = "webhook_endpoint";
|
|
46
|
+
/**
|
|
47
|
+
* Construit le descripteur d'entité du store webhook pour un ORM nommé.
|
|
48
|
+
*
|
|
49
|
+
* Le `connector` est **dynamique** (nom du connecteur de l'app, ex. `"nodefony"`) : le
|
|
50
|
+
* schéma est statique mais sa liaison à un ORM dépend de la config → pas
|
|
51
|
+
* d'`@entity` figé. `timestamps:false`. À enregistrer **avant** `orm.connect()`.
|
|
52
|
+
*
|
|
53
|
+
* @param orm - clé de l'ORM cible dans le `ormRegistry`.
|
|
54
|
+
*/
|
|
55
|
+
export declare function createWebhookEndpointEntity(connector: string): IEntity;
|
|
56
|
+
/**
|
|
57
|
+
* Enregistre l'entité du store webhook dans le `entityRegistry` pour un ORM
|
|
58
|
+
* donné. À appeler **avant** `orm.connect()` (le modèle est compilé au connect).
|
|
59
|
+
*
|
|
60
|
+
* @param connector - nom de la connexion cible (clé du registre).
|
|
61
|
+
*/
|
|
62
|
+
export declare function registerWebhookEndpointEntity(connector: string): void;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { z } from "zod";
|
|
2
|
+
import type { mongooseConfigSchema } from "../config/config.js";
|
|
3
|
+
/**
|
|
4
|
+
* Configuration normalisée et gelée de `@nodefony/mongoose` (sortie du builder
|
|
5
|
+
* {@link defineMongooseConfig}, consommée par le `MongooseService`).
|
|
6
|
+
*
|
|
7
|
+
* Type **dérivé du schéma Zod** — NE PAS redéclarer les champs à la main (ils
|
|
8
|
+
* divergeraient silencieusement de la source de vérité `config/config.ts`).
|
|
9
|
+
*/
|
|
10
|
+
export type IMongooseConfig = z.infer<typeof mongooseConfigSchema>;
|
|
11
|
+
/**
|
|
12
|
+
* Entrée du builder `defineMongooseConfig` — tous les champs portant un défaut
|
|
13
|
+
* sont optionnels (l'app ne fournit que ce qu'elle surcharge dans `use()`).
|
|
14
|
+
*/
|
|
15
|
+
export type IMongooseConfigInput = z.input<typeof mongooseConfigSchema>;
|
|
16
|
+
/** Définition d'une connexion Mongoose nommée (sous-objet de `connectors`). */
|
|
17
|
+
export type IMongooseConnectorConfig = IMongooseConfig["connectors"][string];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type { IMongooseConfig, IMongooseConfigInput, IMongooseConnectorConfig, } from "./IMongooseConfig.js";
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AUTO-ENREGISTREMENT des backends framework portés par Mongoose — « charger le
|
|
3
|
+
* module = ses backends deviennent sélectionnables par simple nom » (convention-
|
|
4
|
+
* frère : `registerDrizzleFrameworkStores` de `@nodefony/drizzle`).
|
|
5
|
+
*
|
|
6
|
+
* Appelé par `Mongoose.onKernelRegister` (AVANT le connect de `onBoot` — les
|
|
7
|
+
* modèles sont compilés à la connexion). Pas de dialecte (NoSQL) : les schémas
|
|
8
|
+
* sont portables par construction.
|
|
9
|
+
*
|
|
10
|
+
* Couverture PARTIELLE assumée : session (auto via `@entity`), tokens, webauthn,
|
|
11
|
+
* webhooks. PAS d'implémentation mongoose pour l'audit ni l'idempotence — les
|
|
12
|
+
* sélectionner sur mongoose échoue franc (« store inconnu »), jamais en silence.
|
|
13
|
+
*
|
|
14
|
+
* Mêmes garde-fous que Drizzle : entité `has`-guarded (l'app garde la main),
|
|
15
|
+
* fabrique `get`-guarded (premier-arrivé-premier-servi).
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Connecteur conventionnel qui héberge le schéma framework (`"nodefony"` pour
|
|
19
|
+
* Mongoose, ≠ `"default"` de Drizzle — isole les entités homonymes dans le
|
|
20
|
+
* `entityRegistry` process-wide si les deux ORM cohabitent).
|
|
21
|
+
*/
|
|
22
|
+
export declare const FRAMEWORK_CONNECTOR = "nodefony";
|
|
23
|
+
/** Bilan de l'auto-enregistrement (loggé par le module — jamais silencieux). */
|
|
24
|
+
export interface IFrameworkStoresReport {
|
|
25
|
+
/** Entités déclarées par l'auto-register (modèles compilés au connect). */
|
|
26
|
+
registered: string[];
|
|
27
|
+
/** Entités déjà enregistrées par l'app (customisation respectée). */
|
|
28
|
+
appOwned: string[];
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Déclare les entités framework Mongoose (connecteur `nodefony`) et enregistre
|
|
32
|
+
* leurs fabriques de stores dans les registres de `@nodefony/security`.
|
|
33
|
+
* Idempotent (guards) — rejouable sans effet.
|
|
34
|
+
*
|
|
35
|
+
* @returns bilan à logger (registered / appOwned)
|
|
36
|
+
*/
|
|
37
|
+
export declare function registerMongooseFrameworkStores(): IFrameworkStoresReport;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { ConnectOptions } from "mongoose";
|
|
2
|
+
import { Service } from "nodefony";
|
|
3
|
+
import type { Module } from "nodefony";
|
|
4
|
+
import { MongooseOrm } from "../src/orm-core/MongooseOrm.js";
|
|
5
|
+
import type { IMongooseConnectorConfig } from "../interfaces/IMongooseConfig.js";
|
|
6
|
+
/**
|
|
7
|
+
* Service bootable du module `@nodefony/mongoose` (driver NoSQL).
|
|
8
|
+
*
|
|
9
|
+
* Au boot du kernel (`onBoot`), instancie un {@link MongooseOrm} (adapter
|
|
10
|
+
* orm-core) **par connecteur** déclaré dans la config et le connecte ; chaque
|
|
11
|
+
* ORM s'auto-enregistre dans le `ormRegistry`. Ferme proprement les connexions
|
|
12
|
+
* à `onTerminate`.
|
|
13
|
+
*
|
|
14
|
+
* Refonte de l'ancien `Mongoose extends Orm` (core legacy) : ce service ne
|
|
15
|
+
* dérive plus de la base ORM du core — il **orchestre** des adapters orm-core
|
|
16
|
+
* autonomes, exactement comme `DrizzleService`. Le core ne connaît plus l'ORM.
|
|
17
|
+
*/
|
|
18
|
+
declare class MongooseService extends Service {
|
|
19
|
+
#private;
|
|
20
|
+
module: Module;
|
|
21
|
+
constructor(module: Module);
|
|
22
|
+
/** Connecte tous les connecteurs déclarés en config (validée Zod). */
|
|
23
|
+
connectAll(): Promise<void>;
|
|
24
|
+
/** Assemble l'URI de connexion à partir de la config (`uri` ou composants). */
|
|
25
|
+
static buildUri(cfg: IMongooseConnectorConfig): string;
|
|
26
|
+
/**
|
|
27
|
+
* Options de connexion Mongoose d'un connecteur.
|
|
28
|
+
*
|
|
29
|
+
* `options` reste un fourre-tout transmis tel quel — Mongoose valide ses
|
|
30
|
+
* propres `ConnectOptions`, les re-modéliser en Zod serait une duplication
|
|
31
|
+
* qui dériverait. `autoIndex` fait exception, et une seule : il décide si les
|
|
32
|
+
* contraintes d'unicité sont construites au démarrage, ce qui mérite un champ
|
|
33
|
+
* typé, décrit, et visible dans la configuration d'un connecteur.
|
|
34
|
+
*
|
|
35
|
+
* Il **prime** donc sur une clé homonyme écrite dans `options` : entre deux
|
|
36
|
+
* canaux, celui qui est déclaré gagne — la même règle que les délais de
|
|
37
|
+
* connexion, où un choix explicite l'emporte sur un défaut.
|
|
38
|
+
*
|
|
39
|
+
* @param cfg - configuration validée du connecteur.
|
|
40
|
+
* @returns les options à passer à la connexion, ou `undefined` s'il n'y en a aucune.
|
|
41
|
+
*/
|
|
42
|
+
static buildConnectOptions(cfg: IMongooseConnectorConfig): ConnectOptions | undefined;
|
|
43
|
+
/** Ferme toutes les connexions. */
|
|
44
|
+
disconnectAll(): Promise<void>;
|
|
45
|
+
/** Retourne l'ORM Mongoose d'un connecteur (défaut : `"nodefony"`). */
|
|
46
|
+
getOrm(name?: string): MongooseOrm | undefined;
|
|
47
|
+
}
|
|
48
|
+
export default MongooseService;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import type { IRepository } from "@nodefony/orm-core";
|
|
2
|
+
import type { IPage } from "nodefony";
|
|
3
|
+
import type { IAccessTokenRecord, ITokenListQuery, ITokenStore, ITokenUsage, TokenRevokeReason } from "@nodefony/security";
|
|
4
|
+
import type { MongooseOrm } from "./orm-core/index.js";
|
|
5
|
+
import { type DeniedJtiRow, type SubjectRevocationRow } from "../entity/tokenEntity.js";
|
|
6
|
+
/**
|
|
7
|
+
* Store de jetons **Mongoose** (NoSQL) — implémentation d'{@link ITokenStore} au
|
|
8
|
+
* dessus de trois repositories `@nodefony/orm-core` (`access_token`, `denied_jti`,
|
|
9
|
+
* `subject_revocation`). Pendant documentaire de `DrizzleTokenStore`.
|
|
10
|
+
*
|
|
11
|
+
* **Approche B** : `@nodefony/security` n'est connu qu'en `import type` (0 dép
|
|
12
|
+
* runtime). C'est l'application qui enregistre la fabrique (`registerTokenStore`)
|
|
13
|
+
* et les entités (`registerTokenEntities(orm)` avant `orm.connect()`).
|
|
14
|
+
*
|
|
15
|
+
* **Spécificité Mongo** : la clé naturelle (`jti` / `subjectId`) est portée par
|
|
16
|
+
* `_id` (cf {@link tokenEntity}). Le contrat traduit `{ id }` → `{ _id }`, donc
|
|
17
|
+
* les lookups passent par le champ `id` ; les **écritures** posent explicitement
|
|
18
|
+
* `_id` (Mongo ne génère pas notre jti). Les reads sont normalisés (`id` ← `_id`)
|
|
19
|
+
* pour ne pas dépendre du virtuel. Le reste est identique au store Drizzle : `gc`
|
|
20
|
+
* via `$lte` (le type bracketing Mongo exclut les `null`), idempotence de `revoke`
|
|
21
|
+
* par read-then-write.
|
|
22
|
+
*/
|
|
23
|
+
export declare class MongooseTokenStore implements ITokenStore {
|
|
24
|
+
#private;
|
|
25
|
+
/**
|
|
26
|
+
* {@inheritDoc ITokenStore.sortableFields}
|
|
27
|
+
*
|
|
28
|
+
* Capacité pleine : le vocabulaire public entier est trié par Mongo, `id`
|
|
29
|
+
* compris — traduit en `_id` au moment de la requête.
|
|
30
|
+
*/
|
|
31
|
+
readonly sortableFields: readonly ["createdAt", "name", "subjectId", "id"];
|
|
32
|
+
/**
|
|
33
|
+
* @param records - repository de `access_token` (PAT + refresh).
|
|
34
|
+
* @param denied - repository de la denylist `denied_jti`.
|
|
35
|
+
* @param revocations - repository des seuils `subject_revocation`.
|
|
36
|
+
* @param now - horloge (epoch ms) injectable pour les tests.
|
|
37
|
+
* @param retentionRevokedMs - rétention d'un PAT révoqué sans `exp` avant purge.
|
|
38
|
+
*/
|
|
39
|
+
constructor(records: IRepository<IAccessTokenRecord>, denied: IRepository<DeniedJtiRow>, revocations: IRepository<SubjectRevocationRow>, now?: () => number, retentionRevokedMs?: number);
|
|
40
|
+
/**
|
|
41
|
+
* Construit le store depuis un {@link MongooseOrm} connecté. Les entités
|
|
42
|
+
* (`registerTokenEntities`) doivent avoir été enregistrées **avant** `connect()`.
|
|
43
|
+
*
|
|
44
|
+
* @param orm - ORM Mongoose connecté hébergeant les collections du store.
|
|
45
|
+
* @param now - horloge injectable (tests).
|
|
46
|
+
* @param retentionRevokedMs - rétention des PAT révoqués sans `exp`.
|
|
47
|
+
*/
|
|
48
|
+
static from(orm: MongooseOrm, now?: () => number, retentionRevokedMs?: number): MongooseTokenStore;
|
|
49
|
+
/**
|
|
50
|
+
* Insère ou remplace un record (PAT / refresh) — 1 round-trip, `upsert`
|
|
51
|
+
* atomique sur la PK plutôt qu'un `findOne` d'existence + `create`/`updateOne`
|
|
52
|
+
* (dont l'`await` laisse deux put concurrents du même id lire « absent » et
|
|
53
|
+
* insérer tous les deux → E11000 pour le perdant). `put` pose le record
|
|
54
|
+
* COMPLET → tout hors `id` est ré-appliqué au conflit.
|
|
55
|
+
*
|
|
56
|
+
* `id` en critère suffit à poser `_id` : Mongo ajoute les égalités du filtre
|
|
57
|
+
* au document inséré (cf `MongooseRepository.upsert`), plus besoin du `_id`
|
|
58
|
+
* explicite. Parité stricte avec l'adapter Drizzle.
|
|
59
|
+
*
|
|
60
|
+
* @param record - le record complet à persister.
|
|
61
|
+
*/
|
|
62
|
+
put(record: IAccessTokenRecord): Promise<void>;
|
|
63
|
+
findById(id: string): Promise<IAccessTokenRecord | null>;
|
|
64
|
+
findByHash(secretHash: string): Promise<IAccessTokenRecord | null>;
|
|
65
|
+
findBySubject(subjectId: string): Promise<IAccessTokenRecord[]>;
|
|
66
|
+
/** Tous les jetons (PAT + refresh) — vue d'administration cross-porteur. */
|
|
67
|
+
listAll(): Promise<IAccessTokenRecord[]>;
|
|
68
|
+
/**
|
|
69
|
+
* {@inheritDoc ITokenStore.listPage}
|
|
70
|
+
*
|
|
71
|
+
* `paginate()` d'orm-core (skip/limit + countDocuments) sur un filtre portable ;
|
|
72
|
+
* les `id` sont re-normalisés (`_id` → `id`) comme dans {@link listAll}.
|
|
73
|
+
*
|
|
74
|
+
* Le tri demandé est **traduit** avant de descendre : au repos, le jeton n'a
|
|
75
|
+
* pas de champ `id` (le `jti` EST le `_id`), et Mongo ne se plaint pas d'un tri
|
|
76
|
+
* sur un champ absent — il rend un ordre arbitraire. Sans traduction, un
|
|
77
|
+
* `?order=id` serait donc inerte ici et correct partout ailleurs.
|
|
78
|
+
*/
|
|
79
|
+
listPage(query: ITokenListQuery): Promise<IPage<IAccessTokenRecord>>;
|
|
80
|
+
/** {@inheritDoc ITokenStore.countTokens} */
|
|
81
|
+
countTokens(query: ITokenListQuery): Promise<number>;
|
|
82
|
+
markUsed(id: string, usage: ITokenUsage): Promise<void>;
|
|
83
|
+
/**
|
|
84
|
+
* Révoque un jeton — **idempotent** : la 1ʳᵉ date/raison est conservée.
|
|
85
|
+
*
|
|
86
|
+
* Le « pas encore révoqué » est dans le filtre (`revokedAt: { $null: true }`),
|
|
87
|
+
* pas dans un `if` JS après lecture : une seule instruction, donc deux
|
|
88
|
+
* révocations concurrentes ne se recouvrent plus (la 2ᵉ ne matche rien au lieu
|
|
89
|
+
* d'écraser la date/raison de la 1ʳᵉ). Parité stricte avec l'adapter Drizzle.
|
|
90
|
+
*
|
|
91
|
+
* @param id - identifiant du jeton.
|
|
92
|
+
* @param reason - motif, posé seulement à la 1ʳᵉ révocation.
|
|
93
|
+
*/
|
|
94
|
+
revoke(id: string, reason: TokenRevokeReason): Promise<void>;
|
|
95
|
+
/**
|
|
96
|
+
* Coupe toute une famille de refresh (détection de rejeu, RFC 9700) — les
|
|
97
|
+
* membres déjà révoqués gardent leur raison d'origine.
|
|
98
|
+
*
|
|
99
|
+
* Un seul `updateMany` filtré : atomique, et N+1 requêtes (1 find + 1 update
|
|
100
|
+
* par membre actif) tombent à 1.
|
|
101
|
+
*
|
|
102
|
+
* @param family - famille de refresh à couper.
|
|
103
|
+
* @param reason - motif appliqué aux membres encore actifs.
|
|
104
|
+
*/
|
|
105
|
+
revokeFamily(family: string, reason: TokenRevokeReason): Promise<void>;
|
|
106
|
+
denyJti(jti: string, expiresAt: number): Promise<void>;
|
|
107
|
+
isJtiDenied(jti: string): Promise<boolean>;
|
|
108
|
+
/**
|
|
109
|
+
* Pose le seuil de révocation en masse d'un porteur (« déconnecte-moi de
|
|
110
|
+
* partout ») : tout jeton émis avant `invalidBefore` est mort.
|
|
111
|
+
*
|
|
112
|
+
* **Monotone — le seuil ne recule JAMAIS**, y compris sous deux logouts
|
|
113
|
+
* simultanés : la comparaison vit dans la valeur écrite (`$max`, natif Mongo),
|
|
114
|
+
* pas dans un `if` JS après lecture. Une lecture suivie d'une écriture
|
|
115
|
+
* laisserait les deux appels voir le même état et écrire tous les deux — c'est
|
|
116
|
+
* le dernier qui resterait, même porteur d'un seuil plus ANCIEN, et **des
|
|
117
|
+
* jetons révoqués redeviendraient valides**. Parité stricte avec l'adapter
|
|
118
|
+
* Drizzle (`GREATEST`/`MAX` SQL).
|
|
119
|
+
*
|
|
120
|
+
* @param subjectId - porteur visé.
|
|
121
|
+
* @param invalidBefore - seuil (epoch ms) ; ignoré s'il est antérieur au seuil courant.
|
|
122
|
+
*/
|
|
123
|
+
revokeAllForSubject(subjectId: string, invalidBefore: number): Promise<void>;
|
|
124
|
+
getInvalidBefore(subjectId: string): Promise<number | null>;
|
|
125
|
+
gc(now?: number): Promise<number>;
|
|
126
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { ClientSession, Model } from "mongoose";
|
|
2
|
+
import type { IPasswordAuthenticatedUser, IUserListQuery, IUserRepository } from "@nodefony/user";
|
|
3
|
+
import type { IPage } from "nodefony";
|
|
4
|
+
import type { Criteria, IRepository, ITransaction, RepositoryReadOptions } from "@nodefony/orm-core";
|
|
5
|
+
import type { MongooseOrm } from "./orm-core/MongooseOrm.js";
|
|
6
|
+
import type { UserRow } from "../entity/userEntity.js";
|
|
7
|
+
/** Modèle Mongoose à document libre (boundary — comme `MongooseRepository`). */
|
|
8
|
+
type LooseModel = Model<Record<string, unknown>>;
|
|
9
|
+
/**
|
|
10
|
+
* Adapter Mongoose du contrat {@link IUserRepository} — persistance NoSQL de
|
|
11
|
+
* l'utilisateur (P5.8), pendant documentaire de `DrizzleUserRepository` (P5.9).
|
|
12
|
+
*
|
|
13
|
+
* Décore le repository portable (`IRepository<UserRow>` de {@link MongooseOrm}) de
|
|
14
|
+
* deux responsabilités propres à l'utilisateur :
|
|
15
|
+
* - **mapping document ↔ `BaseUser`** : les consommateurs reçoivent le comportement
|
|
16
|
+
* (`hasRole`/`isActive`/`isLocked`), pas un document nu ;
|
|
17
|
+
* - **finders métier** : `findByIdentifier` (lookup unique) et
|
|
18
|
+
* `findBySocialProvider` (scan du tableau `socialProviders` via `$elemMatch` —
|
|
19
|
+
* équivalent Mongo du `json_each` SQL de Drizzle, pattern Shadow User OAuth).
|
|
20
|
+
*
|
|
21
|
+
* Le credential (`password`) transite par cette frontière — attendu : le repository
|
|
22
|
+
* **est** la frontière de persistance du hash (cf `IUserRepository`).
|
|
23
|
+
*/
|
|
24
|
+
export declare class MongooseUserRepository implements IUserRepository {
|
|
25
|
+
#private;
|
|
26
|
+
/**
|
|
27
|
+
* Même vocabulaire public que les autres repositories — ici le tri part
|
|
28
|
+
* dans la requête, où il ne coûte qu'un index.
|
|
29
|
+
*/
|
|
30
|
+
readonly sortableFields: readonly ["identifier", "enabled", "createdAt", "updatedAt", "id"];
|
|
31
|
+
/**
|
|
32
|
+
* @param base - repository portable sur l'entité `User` (CRUD + criteria).
|
|
33
|
+
* @param model - modèle Mongoose natif `User` (pour le scan `$elemMatch`).
|
|
34
|
+
* @param session - session transactionnelle liée aux ops natives, ou `null`.
|
|
35
|
+
*/
|
|
36
|
+
constructor(base: IRepository<UserRow>, model: LooseModel, session?: ClientSession | null);
|
|
37
|
+
/**
|
|
38
|
+
* Construit le repository utilisateur depuis un {@link MongooseOrm} connecté.
|
|
39
|
+
* L'entité `User` doit avoir été enregistrée (cf `registerUserEntity`) **avant**
|
|
40
|
+
* `orm.connect()` (le modèle est compilé au connect).
|
|
41
|
+
*
|
|
42
|
+
* @param orm - ORM Mongoose connecté.
|
|
43
|
+
* @returns le repository utilisateur prêt à l'emploi.
|
|
44
|
+
*/
|
|
45
|
+
static from(orm: MongooseOrm): MongooseUserRepository;
|
|
46
|
+
find(criteria?: Criteria<IPasswordAuthenticatedUser>, options?: RepositoryReadOptions): Promise<IPasswordAuthenticatedUser[]>;
|
|
47
|
+
findOne(criteria: Criteria<IPasswordAuthenticatedUser>, options?: RepositoryReadOptions): Promise<IPasswordAuthenticatedUser | null>;
|
|
48
|
+
create(data: Partial<IPasswordAuthenticatedUser>): Promise<IPasswordAuthenticatedUser>;
|
|
49
|
+
updateOne(criteria: Criteria<IPasswordAuthenticatedUser>, data: Partial<IPasswordAuthenticatedUser>): Promise<IPasswordAuthenticatedUser | null>;
|
|
50
|
+
upsert(criteria: Criteria<IPasswordAuthenticatedUser>, update: Partial<IPasswordAuthenticatedUser>, insertOnly?: Partial<IPasswordAuthenticatedUser>): Promise<IPasswordAuthenticatedUser>;
|
|
51
|
+
createMany(data: Partial<IPasswordAuthenticatedUser>[]): Promise<IPasswordAuthenticatedUser[]>;
|
|
52
|
+
exists(criteria: Criteria<IPasswordAuthenticatedUser>): Promise<boolean>;
|
|
53
|
+
deleteOne(criteria: Criteria<IPasswordAuthenticatedUser>): Promise<boolean>;
|
|
54
|
+
findOneAndDelete(criteria: Criteria<IPasswordAuthenticatedUser>): Promise<IPasswordAuthenticatedUser | null>;
|
|
55
|
+
increment(criteria: Criteria<IPasswordAuthenticatedUser>, changes: Partial<Record<keyof IPasswordAuthenticatedUser, number>>): Promise<IPasswordAuthenticatedUser | null>;
|
|
56
|
+
updateMany(criteria: Criteria<IPasswordAuthenticatedUser>, data: Partial<IPasswordAuthenticatedUser>): Promise<number>;
|
|
57
|
+
delete(criteria: Criteria<IPasswordAuthenticatedUser>): Promise<number>;
|
|
58
|
+
count(criteria?: Criteria<IPasswordAuthenticatedUser>): Promise<number>;
|
|
59
|
+
countDistinct(field: keyof IPasswordAuthenticatedUser & string, criteria?: Criteria<IPasswordAuthenticatedUser>): Promise<number>;
|
|
60
|
+
withTransaction(tx: ITransaction): IUserRepository;
|
|
61
|
+
findByIdentifier(identifier: string): Promise<IPasswordAuthenticatedUser | null>;
|
|
62
|
+
/**
|
|
63
|
+
* Recherche par compte externe lié — scanne le tableau `socialProviders` via
|
|
64
|
+
* `$elemMatch` (1 requête, équivalent Mongo du `json_each` Drizzle). `null` si
|
|
65
|
+
* aucun lien. Liée à la session transactionnelle courante le cas échéant.
|
|
66
|
+
*/
|
|
67
|
+
findBySocialProvider(provider: string, providerId: string): Promise<IPasswordAuthenticatedUser | null>;
|
|
68
|
+
/**
|
|
69
|
+
* {@inheritDoc IUserRepository.listPage}
|
|
70
|
+
*
|
|
71
|
+
* Query native Mongo : `find(filter).sort().skip().limit(limit + 1)` — le store
|
|
72
|
+
* ne renvoie qu'une page (jamais de matérialisation complète). `roles: role` =
|
|
73
|
+
* containment de tableau natif, `$regex/i` = sous-chaîne insensible casse.
|
|
74
|
+
* `_id` en tiebreaker de tri (pagination offset déterministe).
|
|
75
|
+
*/
|
|
76
|
+
listPage(query: IUserListQuery): Promise<IPage<IPasswordAuthenticatedUser>>;
|
|
77
|
+
/** {@inheritDoc IUserRepository.countActiveAdmins} */
|
|
78
|
+
/** {@inheritDoc IUserRepository.countUsers} */
|
|
79
|
+
countUsers(query: IUserListQuery): Promise<number>;
|
|
80
|
+
countActiveAdmins(adminRole: string): Promise<number>;
|
|
81
|
+
}
|
|
82
|
+
export {};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type IRepository } from "@nodefony/orm-core";
|
|
2
|
+
import type { IPage } from "nodefony";
|
|
3
|
+
import type { IWebAuthnCredential, IWebAuthnCredentialStore, IWebAuthnCredentialSummary, IWebAuthnListQuery, WebAuthnAuthUpdate } from "@nodefony/security";
|
|
4
|
+
import type { MongooseOrm } from "./orm-core/index.js";
|
|
5
|
+
import { type WebAuthnCredentialRow } from "../entity/webAuthnCredentialEntity.js";
|
|
6
|
+
/**
|
|
7
|
+
* Store de credentials WebAuthn **Mongoose** (NoSQL) — implémentation d'
|
|
8
|
+
* {@link IWebAuthnCredentialStore} au-dessus d'un unique repository
|
|
9
|
+
* `@nodefony/orm-core` (`webauthn_credential`). Pendant documentaire de
|
|
10
|
+
* `DrizzleWebAuthnCredentialStore`.
|
|
11
|
+
*
|
|
12
|
+
* **Approche B** : `@nodefony/security` n'est connu qu'en `import type` (0 dép
|
|
13
|
+
* runtime). C'est l'application qui enregistre la fabrique
|
|
14
|
+
* (`registerWebAuthnStore("mongoose", …)`) et l'entité
|
|
15
|
+
* (`registerWebAuthnCredentialEntity(orm)` avant `orm.connect()`).
|
|
16
|
+
*
|
|
17
|
+
* **Spécificité Mongo** : la clé naturelle (credentialId) est portée par `_id`
|
|
18
|
+
* (cf {@link webAuthnCredentialSchema}). Le contrat traduit `{ id }` → `{ _id }`,
|
|
19
|
+
* donc les lookups passent par le champ `id` ; les **écritures** posent
|
|
20
|
+
* explicitement `_id` (Mongo ne génère pas notre credentialId). Le mapping
|
|
21
|
+
* `Row ↔ IWebAuthnCredential` normalise `nickname` (`null` → omis).
|
|
22
|
+
*/
|
|
23
|
+
export declare class MongooseWebAuthnCredentialStore implements IWebAuthnCredentialStore {
|
|
24
|
+
#private;
|
|
25
|
+
/** @param repo - repository de la collection `webauthn_credential`. */
|
|
26
|
+
constructor(repo: IRepository<WebAuthnCredentialRow>);
|
|
27
|
+
/**
|
|
28
|
+
* Construit le store depuis un {@link MongooseOrm} connecté. L'entité
|
|
29
|
+
* (`registerWebAuthnCredentialEntity`) doit avoir été enregistrée **avant**
|
|
30
|
+
* `connect()`.
|
|
31
|
+
*
|
|
32
|
+
* @param orm - ORM Mongoose connecté hébergeant la collection du store.
|
|
33
|
+
*/
|
|
34
|
+
static from(orm: MongooseOrm): MongooseWebAuthnCredentialStore;
|
|
35
|
+
findById(credentialId: string): Promise<IWebAuthnCredential | null>;
|
|
36
|
+
findByUser(userId: string): Promise<IWebAuthnCredential[]>;
|
|
37
|
+
/** `countDocuments` natif — jamais un `find().length` (le plafond ne charge rien). */
|
|
38
|
+
countByUser(userId: string): Promise<number>;
|
|
39
|
+
save(credential: IWebAuthnCredential): Promise<void>;
|
|
40
|
+
update(credentialId: string, patch: WebAuthnAuthUpdate): Promise<void>;
|
|
41
|
+
delete(credentialId: string): Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* {@inheritDoc IWebAuthnCredentialStore.listPage}
|
|
44
|
+
*
|
|
45
|
+
* Même helper `paginate()` que l'adapter SQL (`skip`/`limit` + `countDocuments`).
|
|
46
|
+
* La projection retire `publicKey` — elle ne franchit jamais la frontière du store.
|
|
47
|
+
* ⚠️ L'identité vient de `_id` (`#idOf`), pas du champ `id` de la row.
|
|
48
|
+
*/
|
|
49
|
+
listPage(query: IWebAuthnListQuery): Promise<IPage<IWebAuthnCredentialSummary>>;
|
|
50
|
+
/** {@inheritDoc IWebAuthnCredentialStore.countCredentials} */
|
|
51
|
+
countCredentials(query: IWebAuthnListQuery): Promise<number>;
|
|
52
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { IRepository } from "@nodefony/orm-core";
|
|
2
|
+
import type { IPage } from "nodefony";
|
|
3
|
+
import type { IWebhookEndpoint, IWebhookListQuery, IWebhookStore, WebhookEndpointUpdate } from "@nodefony/security";
|
|
4
|
+
import type { Model } from "mongoose";
|
|
5
|
+
import type { MongooseOrm } from "./orm-core/index.js";
|
|
6
|
+
/** Modèle Mongoose à document libre (boundary — comme `MongooseRepository`). */
|
|
7
|
+
type LooseModel = Model<Record<string, unknown>>;
|
|
8
|
+
import { type WebhookEndpointRow } from "../entity/webhookEndpointEntity.js";
|
|
9
|
+
/**
|
|
10
|
+
* Store d'endpoints webhook **Mongoose** (NoSQL) — implémentation d'
|
|
11
|
+
* {@link IWebhookStore} au-dessus d'un unique repository `@nodefony/orm-core`
|
|
12
|
+
* (`webhook_endpoint`). Pendant documentaire de `DrizzleWebhookStore` ; registre
|
|
13
|
+
* DURABLE des endpoints (survit au redémarrage, ≠ `MemoryWebhookStore`).
|
|
14
|
+
*
|
|
15
|
+
* **Approche B** : `@nodefony/security` n'est connu qu'en `import type` (0 dép
|
|
16
|
+
* runtime). C'est l'application qui enregistre la fabrique
|
|
17
|
+
* (`registerWebhookStore("mongoose", …)`) et l'entité
|
|
18
|
+
* (`registerWebhookEndpointEntity(orm)` avant `orm.connect()`).
|
|
19
|
+
*
|
|
20
|
+
* **Spécificité Mongo** : la clé naturelle (`wh_<random>`) est portée par `_id`
|
|
21
|
+
* (cf {@link webhookEndpointSchema}). Le contrat traduit `{ id }` → `{ _id }`,
|
|
22
|
+
* donc les lookups passent par `id` ; les **écritures** posent explicitement
|
|
23
|
+
* `_id` (Mongo ne génère pas notre id). Mapping `Row ↔ IWebhookEndpoint` :
|
|
24
|
+
* `IWebhookEndpoint` est déjà « plat tout `| null` », seuls les champs JSON
|
|
25
|
+
* `events`/`metadata` sont copiés défensivement.
|
|
26
|
+
*/
|
|
27
|
+
export declare class MongooseWebhookStore implements IWebhookStore {
|
|
28
|
+
#private;
|
|
29
|
+
/**
|
|
30
|
+
* {@inheritDoc IWebhookStore.sortableFields}
|
|
31
|
+
*
|
|
32
|
+
* Capacité pleine : le vocabulaire public entier est trié par Mongo, `id`
|
|
33
|
+
* compris — traduit en `_id` au moment de la requête.
|
|
34
|
+
*/
|
|
35
|
+
readonly sortableFields: readonly ["createdAt", "updatedAt", "url", "enabled", "failureCount", "id"];
|
|
36
|
+
/**
|
|
37
|
+
* @param repo - repository de la collection `webhook_endpoint`.
|
|
38
|
+
* @param model - modèle Mongoose natif, requis par le seul listing paginé
|
|
39
|
+
* (recherche `q` = `$or` sur deux champs, hors `Criteria` AND-only).
|
|
40
|
+
* `null` = `listPage` refuse plutôt que de tout charger en silence.
|
|
41
|
+
*/
|
|
42
|
+
constructor(repo: IRepository<WebhookEndpointRow>, model?: LooseModel | null);
|
|
43
|
+
/**
|
|
44
|
+
* Construit le store depuis un {@link MongooseOrm} connecté. L'entité
|
|
45
|
+
* (`registerWebhookEndpointEntity`) doit avoir été enregistrée **avant**
|
|
46
|
+
* `connect()`.
|
|
47
|
+
*
|
|
48
|
+
* @param orm - ORM Mongoose connecté hébergeant la collection du store.
|
|
49
|
+
*/
|
|
50
|
+
static from(orm: MongooseOrm): MongooseWebhookStore;
|
|
51
|
+
save(endpoint: IWebhookEndpoint): Promise<void>;
|
|
52
|
+
findById(id: string): Promise<IWebhookEndpoint | null>;
|
|
53
|
+
update(id: string, patch: WebhookEndpointUpdate): Promise<void>;
|
|
54
|
+
delete(id: string): Promise<void>;
|
|
55
|
+
listAll(): Promise<IWebhookEndpoint[]>;
|
|
56
|
+
/**
|
|
57
|
+
* {@inheritDoc IWebhookStore.listPage}
|
|
58
|
+
*
|
|
59
|
+
* Query native : `find(filter).sort(…).skip().limit(limit+1)` — une page,
|
|
60
|
+
* jamais la collection. Le `limit + 1` donne `hasNext` sans compter.
|
|
61
|
+
*
|
|
62
|
+
* Le tri demandé est **traduit** avant de descendre : au repos, l'endpoint n'a
|
|
63
|
+
* pas de champ `id` (l'identifiant EST le `_id`), et Mongo ne se plaint pas
|
|
64
|
+
* d'un tri sur un champ absent — il rend un ordre arbitraire. Sans traduction,
|
|
65
|
+
* un `?order=id` serait donc inerte ici et correct partout ailleurs. À défaut
|
|
66
|
+
* d'`order`, l'ordre par défaut `createdAt DESC, _id ASC` reste déterministe.
|
|
67
|
+
*/
|
|
68
|
+
listPage(query: IWebhookListQuery): Promise<IPage<IWebhookEndpoint>>;
|
|
69
|
+
/** {@inheritDoc IWebhookStore.countEndpoints} */
|
|
70
|
+
countEndpoints(query: IWebhookListQuery): Promise<number>;
|
|
71
|
+
}
|
|
72
|
+
export {};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { SessionsService } from "@nodefony/http";
|
|
2
|
+
import type { ISessionStorage, ISerializedSession, ISessionRecord, ISessionListFilter, ISessionListQuery } from "@nodefony/http";
|
|
3
|
+
import type { IPage } from "nodefony";
|
|
4
|
+
/**
|
|
5
|
+
* Stockage de session **Mongoose** (driver NoSQL), branché sur `@nodefony/orm-core`.
|
|
6
|
+
*
|
|
7
|
+
* Implémente le contrat {@link ISessionStorage} consommé par le `SessionsService`
|
|
8
|
+
* de `@nodefony/http` — store de session portable. Persiste via le repository
|
|
9
|
+
* orm-core de l'entité `session` (connecteur `nodefony`, modèle compilé au boot
|
|
10
|
+
* par `MongooseOrm`). Logique **identique** au store Drizzle (timestamps en ms,
|
|
11
|
+
* GC via l'opérateur riche portable `$lt`) — la portabilité du contrat orm-core.
|
|
12
|
+
*/
|
|
13
|
+
declare class SessionStorage implements ISessionStorage {
|
|
14
|
+
#private;
|
|
15
|
+
manager: SessionsService;
|
|
16
|
+
idleTimeoutS: number;
|
|
17
|
+
absoluteTimeoutS: number;
|
|
18
|
+
/**
|
|
19
|
+
* Même vocabulaire public que les autres backends — le tri part dans le
|
|
20
|
+
* `sort()` Mongo, où il ne coûte qu'un index.
|
|
21
|
+
*/
|
|
22
|
+
readonly sortableFields: readonly ["updatedAt", "createdAt", "user", "id"];
|
|
23
|
+
constructor(manager: SessionsService);
|
|
24
|
+
read(id: string): Promise<ISerializedSession>;
|
|
25
|
+
start(id: string): Promise<ISerializedSession>;
|
|
26
|
+
write(id: string, data: ISerializedSession): Promise<ISerializedSession>;
|
|
27
|
+
open(): Promise<number>;
|
|
28
|
+
close(): boolean;
|
|
29
|
+
destroy(id: string): Promise<boolean>;
|
|
30
|
+
gc(idleSeconds?: number, absoluteSeconds?: number): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Prolonge l'idle d'une session (timeout glissant) : `updateOne updatedAt = now`
|
|
33
|
+
* sur `session_id` — SANS réécrire le blob (touch NIST/OWASP). N'affecte pas
|
|
34
|
+
* `createdAt` (= borne absolute). ORM déconnecté → no-op ; ligne absente →
|
|
35
|
+
* no-op silencieux. Parité avec le store Drizzle.
|
|
36
|
+
*/
|
|
37
|
+
touch(id: string): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Énumération admin (capacité optionnelle d'`ISessionStorage`) — `find` projeté,
|
|
40
|
+
* filtrable par `user`. **Redaction par construction** : seuls `user`/`metaBag`/
|
|
41
|
+
* timestamps sortent de la base ; `Attributes`/`flashBag` (potentiellement
|
|
42
|
+
* sensibles) restent en base. ORM déconnecté → `[]`. Strictement identique au
|
|
43
|
+
* store Drizzle (parité du contrat orm-core).
|
|
44
|
+
*/
|
|
45
|
+
listAll(filter?: ISessionListFilter): Promise<ISessionRecord[]>;
|
|
46
|
+
/**
|
|
47
|
+
* Pagination **native** `skip/limit` + `countDocuments` (helper `paginate`
|
|
48
|
+
* orm-core) : une page = une requête bornée, quel que soit le volume en base.
|
|
49
|
+
* Ordre `updatedAt` DESC départagé par `session_id` — déterministe à horodatage
|
|
50
|
+
* égal. ORM déconnecté → page vide. Parité stricte avec le store Drizzle.
|
|
51
|
+
*/
|
|
52
|
+
listPage(query: ISessionListQuery): Promise<IPage<ISessionRecord>>;
|
|
53
|
+
/** `countDocuments` natif filtré — aucun document matérialisé. Déconnecté → 0. */
|
|
54
|
+
countSessions(query?: Partial<ISessionListQuery>): Promise<number>;
|
|
55
|
+
/**
|
|
56
|
+
* Utilisateurs distincts, agrégés côté serveur. Les sessions anonymes portent
|
|
57
|
+
* un `user` nul ou absent : l'agrégation les écarte, elles ne comptent donc
|
|
58
|
+
* pas pour un utilisateur. Déconnecté → 0 (même dégradation que
|
|
59
|
+
* {@link countSessions}).
|
|
60
|
+
*/
|
|
61
|
+
countDistinctUsers(query?: Partial<ISessionListQuery>): Promise<number>;
|
|
62
|
+
}
|
|
63
|
+
export default SessionStorage;
|