@carlonicora/nestjs-neo4jsonapi 8.5.0 → 8.6.0
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/dist/bootstrap/app.module.factory.d.ts.map +1 -1
- package/dist/bootstrap/app.module.factory.js +1 -0
- package/dist/bootstrap/app.module.factory.js.map +1 -1
- package/dist/bootstrap/bootstrap.options.d.ts +14 -0
- package/dist/bootstrap/bootstrap.options.d.ts.map +1 -1
- package/dist/config/enums/queue.id.d.ts +1 -0
- package/dist/config/enums/queue.id.d.ts.map +1 -1
- package/dist/config/enums/queue.id.js +1 -0
- package/dist/config/enums/queue.id.js.map +1 -1
- package/dist/core/email/templates/en/accountDeleted.hbs +23 -0
- package/dist/core/email/templates/en/accountDeletionRequested.hbs +28 -0
- package/dist/foundations/account-deletion/account-deletion.config.d.ts +18 -0
- package/dist/foundations/account-deletion/account-deletion.config.d.ts.map +1 -0
- package/dist/foundations/account-deletion/account-deletion.config.js +6 -0
- package/dist/foundations/account-deletion/account-deletion.config.js.map +1 -0
- package/dist/foundations/account-deletion/account-deletion.constants.d.ts +36 -0
- package/dist/foundations/account-deletion/account-deletion.constants.d.ts.map +1 -0
- package/dist/foundations/account-deletion/account-deletion.constants.js +41 -0
- package/dist/foundations/account-deletion/account-deletion.constants.js.map +1 -0
- package/dist/foundations/account-deletion/account-deletion.module.d.ts +31 -0
- package/dist/foundations/account-deletion/account-deletion.module.d.ts.map +1 -0
- package/dist/foundations/account-deletion/account-deletion.module.js +98 -0
- package/dist/foundations/account-deletion/account-deletion.module.js.map +1 -0
- package/dist/foundations/account-deletion/controllers/account-deletion.controller.d.ts +28 -0
- package/dist/foundations/account-deletion/controllers/account-deletion.controller.d.ts.map +1 -0
- package/dist/foundations/account-deletion/controllers/account-deletion.controller.js +132 -0
- package/dist/foundations/account-deletion/controllers/account-deletion.controller.js.map +1 -0
- package/dist/foundations/account-deletion/cron/account-deletion.cron.d.ts +20 -0
- package/dist/foundations/account-deletion/cron/account-deletion.cron.d.ts.map +1 -0
- package/dist/foundations/account-deletion/cron/account-deletion.cron.js +78 -0
- package/dist/foundations/account-deletion/cron/account-deletion.cron.js.map +1 -0
- package/dist/foundations/account-deletion/dtos/account-deletion.keep.post.dto.d.ts +14 -0
- package/dist/foundations/account-deletion/dtos/account-deletion.keep.post.dto.d.ts.map +1 -0
- package/dist/foundations/account-deletion/dtos/account-deletion.keep.post.dto.js +51 -0
- package/dist/foundations/account-deletion/dtos/account-deletion.keep.post.dto.js.map +1 -0
- package/dist/foundations/account-deletion/dtos/account-deletion.post.dto.d.ts +14 -0
- package/dist/foundations/account-deletion/dtos/account-deletion.post.dto.d.ts.map +1 -0
- package/dist/foundations/account-deletion/dtos/account-deletion.post.dto.js +51 -0
- package/dist/foundations/account-deletion/dtos/account-deletion.post.dto.js.map +1 -0
- package/dist/foundations/account-deletion/entities/account-deletion.d.ts +20 -0
- package/dist/foundations/account-deletion/entities/account-deletion.d.ts.map +1 -0
- package/dist/foundations/account-deletion/entities/account-deletion.js +21 -0
- package/dist/foundations/account-deletion/entities/account-deletion.js.map +1 -0
- package/dist/foundations/account-deletion/entities/account-deletion.meta.d.ts +3 -0
- package/dist/foundations/account-deletion/entities/account-deletion.meta.d.ts.map +1 -0
- package/dist/foundations/account-deletion/entities/account-deletion.meta.js +10 -0
- package/dist/foundations/account-deletion/entities/account-deletion.meta.js.map +1 -0
- package/dist/foundations/account-deletion/index.d.ts +15 -0
- package/dist/foundations/account-deletion/index.d.ts.map +1 -0
- package/dist/foundations/account-deletion/index.js +31 -0
- package/dist/foundations/account-deletion/index.js.map +1 -0
- package/dist/foundations/account-deletion/interfaces/account-deletion-handler.interface.d.ts +29 -0
- package/dist/foundations/account-deletion/interfaces/account-deletion-handler.interface.d.ts.map +1 -0
- package/dist/foundations/account-deletion/interfaces/account-deletion-handler.interface.js +5 -0
- package/dist/foundations/account-deletion/interfaces/account-deletion-handler.interface.js.map +1 -0
- package/dist/foundations/account-deletion/interfaces/account-deletion-login-hook.interface.d.ts +12 -0
- package/dist/foundations/account-deletion/interfaces/account-deletion-login-hook.interface.d.ts.map +1 -0
- package/dist/foundations/account-deletion/interfaces/account-deletion-login-hook.interface.js +5 -0
- package/dist/foundations/account-deletion/interfaces/account-deletion-login-hook.interface.js.map +1 -0
- package/dist/foundations/account-deletion/processors/account-deletion.processor.d.ts +28 -0
- package/dist/foundations/account-deletion/processors/account-deletion.processor.d.ts.map +1 -0
- package/dist/foundations/account-deletion/processors/account-deletion.processor.js +97 -0
- package/dist/foundations/account-deletion/processors/account-deletion.processor.js.map +1 -0
- package/dist/foundations/account-deletion/repositories/account-deletion.repository.d.ts +85 -0
- package/dist/foundations/account-deletion/repositories/account-deletion.repository.d.ts.map +1 -0
- package/dist/foundations/account-deletion/repositories/account-deletion.repository.js +186 -0
- package/dist/foundations/account-deletion/repositories/account-deletion.repository.js.map +1 -0
- package/dist/foundations/account-deletion/repositories/account-deletion.wipe.repository.d.ts +77 -0
- package/dist/foundations/account-deletion/repositories/account-deletion.wipe.repository.d.ts.map +1 -0
- package/dist/foundations/account-deletion/repositories/account-deletion.wipe.repository.js +243 -0
- package/dist/foundations/account-deletion/repositories/account-deletion.wipe.repository.js.map +1 -0
- package/dist/foundations/account-deletion/services/account-deletion.service.d.ts +105 -0
- package/dist/foundations/account-deletion/services/account-deletion.service.d.ts.map +1 -0
- package/dist/foundations/account-deletion/services/account-deletion.service.js +300 -0
- package/dist/foundations/account-deletion/services/account-deletion.service.js.map +1 -0
- package/dist/foundations/account-deletion/services/account-wipe.service.d.ts +64 -0
- package/dist/foundations/account-deletion/services/account-wipe.service.d.ts.map +1 -0
- package/dist/foundations/account-deletion/services/account-wipe.service.js +134 -0
- package/dist/foundations/account-deletion/services/account-wipe.service.js.map +1 -0
- package/dist/foundations/account-deletion/services/full-company-deletion.service.d.ts +88 -0
- package/dist/foundations/account-deletion/services/full-company-deletion.service.d.ts.map +1 -0
- package/dist/foundations/account-deletion/services/full-company-deletion.service.js +184 -0
- package/dist/foundations/account-deletion/services/full-company-deletion.service.js.map +1 -0
- package/dist/foundations/account-deletion/utils/deliverable-email.d.ts +8 -0
- package/dist/foundations/account-deletion/utils/deliverable-email.d.ts.map +1 -0
- package/dist/foundations/account-deletion/utils/deliverable-email.js +20 -0
- package/dist/foundations/account-deletion/utils/deliverable-email.js.map +1 -0
- package/dist/foundations/auth/entities/auth.d.ts +1 -0
- package/dist/foundations/auth/entities/auth.d.ts.map +1 -1
- package/dist/foundations/auth/entities/auth.js +4 -0
- package/dist/foundations/auth/entities/auth.js.map +1 -1
- package/dist/foundations/auth/repositories/auth.repository.d.ts +2 -0
- package/dist/foundations/auth/repositories/auth.repository.d.ts.map +1 -1
- package/dist/foundations/auth/repositories/auth.repository.js +4 -0
- package/dist/foundations/auth/repositories/auth.repository.js.map +1 -1
- package/dist/foundations/auth/services/auth.service.d.ts +3 -1
- package/dist/foundations/auth/services/auth.service.d.ts.map +1 -1
- package/dist/foundations/auth/services/auth.service.js +11 -2
- package/dist/foundations/auth/services/auth.service.js.map +1 -1
- package/dist/foundations/foundations.modules.d.ts +7 -0
- package/dist/foundations/foundations.modules.d.ts.map +1 -1
- package/dist/foundations/foundations.modules.js +5 -0
- package/dist/foundations/foundations.modules.js.map +1 -1
- package/dist/foundations/index.d.ts +1 -0
- package/dist/foundations/index.d.ts.map +1 -1
- package/dist/foundations/index.js +1 -0
- package/dist/foundations/index.js.map +1 -1
- package/dist/foundations/user/entities/user.d.ts +3 -0
- package/dist/foundations/user/entities/user.d.ts.map +1 -1
- package/dist/foundations/user/entities/user.js +4 -0
- package/dist/foundations/user/entities/user.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { ClsService } from "nestjs-cls";
|
|
2
|
+
import { Neo4jService } from "../../../core/neo4j/services/neo4j.service";
|
|
3
|
+
import { AbstractRepository } from "../../../core/neo4j/abstracts/abstract.repository";
|
|
4
|
+
import { SecurityService } from "../../../core/security/services/security.service";
|
|
5
|
+
import { User, UserDescriptor } from "../../user/entities/user";
|
|
6
|
+
/**
|
|
7
|
+
* Reads and writes of the account-deletion request on the User node.
|
|
8
|
+
*
|
|
9
|
+
* Owner decision OD-1 (Carlo, 2026-10-25): this repository reads a User and its
|
|
10
|
+
* Memberships by explicit id with no company predicate, for exactly six callers
|
|
11
|
+
* (self preview/request, admin preview/request on another user, the public keep
|
|
12
|
+
* link, the daily cron, the login hook and the worker processor), and runs three
|
|
13
|
+
* hand-written writes by user id (setRequest, clearRequest, deleteSessions).
|
|
14
|
+
* A user deleting their account is not scoped to one company: a placeholder
|
|
15
|
+
* player belongs to the GM's company, an administrator acts on any user, and the
|
|
16
|
+
* keep link and the cron run with no session at all. Every query therefore runs
|
|
17
|
+
* in a fresh CLS context (`ifNested: "override"`), so `initQuery` binds no
|
|
18
|
+
* caller company or user. Every id is a query parameter. User nodes come back
|
|
19
|
+
* through readOne/readMany with the User serialiser; only findMembershipSummary
|
|
20
|
+
* returns a scalar row.
|
|
21
|
+
*/
|
|
22
|
+
export declare class AccountDeletionRepository extends AbstractRepository<User, typeof UserDescriptor.relationships> {
|
|
23
|
+
protected readonly descriptor: import("../../..").EntityDescriptor<User, {
|
|
24
|
+
role: {
|
|
25
|
+
model: import("../../..").DataMeta;
|
|
26
|
+
direction: "out";
|
|
27
|
+
relationship: string;
|
|
28
|
+
cardinality: "many";
|
|
29
|
+
dtoKey: string;
|
|
30
|
+
readOnly: true;
|
|
31
|
+
};
|
|
32
|
+
company: {
|
|
33
|
+
model: import("../../..").DataModelInterface<import("../..").Company>;
|
|
34
|
+
direction: "out";
|
|
35
|
+
relationship: string;
|
|
36
|
+
cardinality: "one";
|
|
37
|
+
};
|
|
38
|
+
module: {
|
|
39
|
+
model: import("../../..").DataMeta;
|
|
40
|
+
direction: "out";
|
|
41
|
+
relationship: string;
|
|
42
|
+
cardinality: "many";
|
|
43
|
+
dtoKey: string;
|
|
44
|
+
};
|
|
45
|
+
}>;
|
|
46
|
+
constructor(neo4j: Neo4jService, securityService: SecurityService, clsService: ClsService);
|
|
47
|
+
/** Runs the callback in an empty CLS context: no caller company or user is bound (OD-1). */
|
|
48
|
+
private _unscoped;
|
|
49
|
+
private _userReturn;
|
|
50
|
+
findTargetUser(params: {
|
|
51
|
+
userId: string;
|
|
52
|
+
}): Promise<User | null>;
|
|
53
|
+
findByUndoTokenHash(params: {
|
|
54
|
+
undoTokenHash: string;
|
|
55
|
+
}): Promise<User | null>;
|
|
56
|
+
/** Users whose grace period is over. Worker cron only. */
|
|
57
|
+
findDue(params: {
|
|
58
|
+
now: string;
|
|
59
|
+
}): Promise<User[]>;
|
|
60
|
+
/**
|
|
61
|
+
* The company of the user's Membership (preferring the company the user also
|
|
62
|
+
* BELONGS_TO) and how many Memberships that company has. A placeholder user
|
|
63
|
+
* (no Membership) gets `{ companyId: null, memberCount: 0 }`.
|
|
64
|
+
*/
|
|
65
|
+
findMembershipSummary(params: {
|
|
66
|
+
userId: string;
|
|
67
|
+
}): Promise<{
|
|
68
|
+
companyId: string | null;
|
|
69
|
+
memberCount: number;
|
|
70
|
+
}>;
|
|
71
|
+
setRequest(params: {
|
|
72
|
+
userId: string;
|
|
73
|
+
requestedAt: string;
|
|
74
|
+
scheduledAt: string;
|
|
75
|
+
undoTokenHash: string | null;
|
|
76
|
+
}): Promise<void>;
|
|
77
|
+
clearRequest(params: {
|
|
78
|
+
userId: string;
|
|
79
|
+
}): Promise<void>;
|
|
80
|
+
/** Logs the user out everywhere: every Auth and AuthCode, and every OAuth token revoked. */
|
|
81
|
+
deleteSessions(params: {
|
|
82
|
+
userId: string;
|
|
83
|
+
}): Promise<void>;
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=account-deletion.repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"account-deletion.repository.d.ts","sourceRoot":"","sources":["../../../../src/foundations/account-deletion/repositories/account-deletion.repository.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,4CAA4C,CAAC;AAC1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,mDAAmD,CAAC;AACvF,OAAO,EAAE,eAAe,EAAE,MAAM,kDAAkD,CAAC;AAGnF,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAGhE;;;;;;;;;;;;;;;GAeG;AACH,qBACa,yBAA0B,SAAQ,kBAAkB,CAAC,IAAI,EAAE,OAAO,cAAc,CAAC,aAAa,CAAC;IAC1G,SAAS,CAAC,QAAQ,CAAC,UAAU;;;;;;;;;;;;;;;;;;;;;;OAAkB;gBAEnC,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,UAAU,EAAE,UAAU;IAIzF,4FAA4F;IAC5F,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,WAAW;IAOb,cAAc,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAahE,mBAAmB,CAAC,MAAM,EAAE;QAAE,aAAa,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAalF,0DAA0D;IACpD,OAAO,CAAC,MAAM,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAevD;;;;OAIG;IACG,qBAAqB,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IA6B7G,UAAU,CAAC,MAAM,EAAE;QACvB,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,MAAM,CAAC;QACpB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;KAC9B,GAAG,OAAO,CAAC,IAAI,CAAC;IAsBX,YAAY,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAc7D,4FAA4F;IACtF,cAAc,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAwBhE"}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.AccountDeletionRepository = void 0;
|
|
13
|
+
const common_1 = require("@nestjs/common");
|
|
14
|
+
const nestjs_cls_1 = require("nestjs-cls");
|
|
15
|
+
const neo4j_service_1 = require("../../../core/neo4j/services/neo4j.service");
|
|
16
|
+
const abstract_repository_1 = require("../../../core/neo4j/abstracts/abstract.repository");
|
|
17
|
+
const security_service_1 = require("../../../core/security/services/security.service");
|
|
18
|
+
const company_meta_1 = require("../../company/entities/company.meta");
|
|
19
|
+
const revoke_user_oauth_tokens_query_1 = require("../../oauth/queries/revoke-user-oauth-tokens.query");
|
|
20
|
+
const user_1 = require("../../user/entities/user");
|
|
21
|
+
const user_meta_1 = require("../../user/entities/user.meta");
|
|
22
|
+
/**
|
|
23
|
+
* Reads and writes of the account-deletion request on the User node.
|
|
24
|
+
*
|
|
25
|
+
* Owner decision OD-1 (Carlo, 2026-10-25): this repository reads a User and its
|
|
26
|
+
* Memberships by explicit id with no company predicate, for exactly six callers
|
|
27
|
+
* (self preview/request, admin preview/request on another user, the public keep
|
|
28
|
+
* link, the daily cron, the login hook and the worker processor), and runs three
|
|
29
|
+
* hand-written writes by user id (setRequest, clearRequest, deleteSessions).
|
|
30
|
+
* A user deleting their account is not scoped to one company: a placeholder
|
|
31
|
+
* player belongs to the GM's company, an administrator acts on any user, and the
|
|
32
|
+
* keep link and the cron run with no session at all. Every query therefore runs
|
|
33
|
+
* in a fresh CLS context (`ifNested: "override"`), so `initQuery` binds no
|
|
34
|
+
* caller company or user. Every id is a query parameter. User nodes come back
|
|
35
|
+
* through readOne/readMany with the User serialiser; only findMembershipSummary
|
|
36
|
+
* returns a scalar row.
|
|
37
|
+
*/
|
|
38
|
+
let AccountDeletionRepository = class AccountDeletionRepository extends abstract_repository_1.AbstractRepository {
|
|
39
|
+
constructor(neo4j, securityService, clsService) {
|
|
40
|
+
super(neo4j, securityService, clsService);
|
|
41
|
+
this.descriptor = user_1.UserDescriptor;
|
|
42
|
+
}
|
|
43
|
+
/** Runs the callback in an empty CLS context: no caller company or user is bound (OD-1). */
|
|
44
|
+
_unscoped(callback) {
|
|
45
|
+
return this.clsService.run({ ifNested: "override" }, callback);
|
|
46
|
+
}
|
|
47
|
+
_userReturn() {
|
|
48
|
+
return `
|
|
49
|
+
OPTIONAL MATCH (${user_meta_1.userMeta.nodeName})-[:BELONGS_TO]->(${user_meta_1.userMeta.nodeName}_${company_meta_1.companyMeta.nodeName}:${company_meta_1.companyMeta.labelName})
|
|
50
|
+
RETURN ${user_meta_1.userMeta.nodeName}, ${user_meta_1.userMeta.nodeName}_${company_meta_1.companyMeta.nodeName}
|
|
51
|
+
`;
|
|
52
|
+
}
|
|
53
|
+
async findTargetUser(params) {
|
|
54
|
+
return this._unscoped(async () => {
|
|
55
|
+
const query = this.neo4j.initQuery({ serialiser: user_1.UserDescriptor.model });
|
|
56
|
+
query.queryParams = { ...query.queryParams, userId: params.userId };
|
|
57
|
+
// nja-lint-ignore OD-1
|
|
58
|
+
query.query += `
|
|
59
|
+
MATCH (${user_meta_1.userMeta.nodeName}:${user_meta_1.userMeta.labelName} {id: $userId})
|
|
60
|
+
${this._userReturn()}
|
|
61
|
+
`;
|
|
62
|
+
return this.neo4j.readOne(query);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
async findByUndoTokenHash(params) {
|
|
66
|
+
return this._unscoped(async () => {
|
|
67
|
+
const query = this.neo4j.initQuery({ serialiser: user_1.UserDescriptor.model });
|
|
68
|
+
query.queryParams = { ...query.queryParams, undoTokenHash: params.undoTokenHash };
|
|
69
|
+
// nja-lint-ignore OD-1
|
|
70
|
+
query.query += `
|
|
71
|
+
MATCH (${user_meta_1.userMeta.nodeName}:${user_meta_1.userMeta.labelName} {deletionUndoTokenHash: $undoTokenHash})
|
|
72
|
+
${this._userReturn()}
|
|
73
|
+
`;
|
|
74
|
+
return this.neo4j.readOne(query);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
/** Users whose grace period is over. Worker cron only. */
|
|
78
|
+
async findDue(params) {
|
|
79
|
+
return this._unscoped(async () => {
|
|
80
|
+
const query = this.neo4j.initQuery({ serialiser: user_1.UserDescriptor.model, fetchAll: true });
|
|
81
|
+
query.queryParams = { ...query.queryParams, now: params.now };
|
|
82
|
+
// nja-lint-ignore OD-1
|
|
83
|
+
query.query += `
|
|
84
|
+
MATCH (${user_meta_1.userMeta.nodeName}:${user_meta_1.userMeta.labelName})
|
|
85
|
+
WHERE ${user_meta_1.userMeta.nodeName}.deletionScheduledAt IS NOT NULL
|
|
86
|
+
AND ${user_meta_1.userMeta.nodeName}.deletionScheduledAt <= datetime($now)
|
|
87
|
+
${this._userReturn()}
|
|
88
|
+
`;
|
|
89
|
+
return this.neo4j.readMany(query);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* The company of the user's Membership (preferring the company the user also
|
|
94
|
+
* BELONGS_TO) and how many Memberships that company has. A placeholder user
|
|
95
|
+
* (no Membership) gets `{ companyId: null, memberCount: 0 }`.
|
|
96
|
+
*/
|
|
97
|
+
async findMembershipSummary(params) {
|
|
98
|
+
return this._unscoped(async () => {
|
|
99
|
+
// nja-lint-ignore OD-1
|
|
100
|
+
const result = await this.neo4j.read(`
|
|
101
|
+
MATCH (user:User {id: $userId})
|
|
102
|
+
OPTIONAL MATCH (user)-[:HAS_MEMBERSHIP]->(:Membership)-[:IN_COMPANY]->(company:Company)
|
|
103
|
+
WITH user, company
|
|
104
|
+
ORDER BY CASE WHEN company IS NOT NULL AND EXISTS { (user)-[:BELONGS_TO]->(company) } THEN 0 ELSE 1 END
|
|
105
|
+
WITH collect(company)[0] AS company
|
|
106
|
+
OPTIONAL MATCH (member:Membership)-[:IN_COMPANY]->(company)
|
|
107
|
+
RETURN company.id AS companyId, count(DISTINCT member) AS memberCount
|
|
108
|
+
`, { userId: params.userId });
|
|
109
|
+
// nja-lint-ignore OD-1 (scalar row: company id and a count, no entity node)
|
|
110
|
+
const record = result.records?.[0];
|
|
111
|
+
if (!record)
|
|
112
|
+
return { companyId: null, memberCount: 0 };
|
|
113
|
+
const companyId = record.get("companyId");
|
|
114
|
+
const memberCount = record.get("memberCount");
|
|
115
|
+
return {
|
|
116
|
+
companyId: companyId ?? null,
|
|
117
|
+
memberCount: typeof memberCount?.toNumber === "function" ? memberCount.toNumber() : Number(memberCount ?? 0),
|
|
118
|
+
};
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
async setRequest(params) {
|
|
122
|
+
await this._unscoped(async () => {
|
|
123
|
+
const query = this.neo4j.initQuery();
|
|
124
|
+
query.queryParams = {
|
|
125
|
+
...query.queryParams,
|
|
126
|
+
userId: params.userId,
|
|
127
|
+
requestedAt: params.requestedAt,
|
|
128
|
+
scheduledAt: params.scheduledAt,
|
|
129
|
+
undoTokenHash: params.undoTokenHash,
|
|
130
|
+
};
|
|
131
|
+
// nja-lint-ignore OD-1
|
|
132
|
+
query.query += `
|
|
133
|
+
MATCH (user:User {id: $userId})
|
|
134
|
+
SET user.deletionRequestedAt = datetime($requestedAt),
|
|
135
|
+
user.deletionScheduledAt = datetime($scheduledAt),
|
|
136
|
+
user.deletionUndoTokenHash = $undoTokenHash,
|
|
137
|
+
user.updatedAt = datetime()
|
|
138
|
+
`;
|
|
139
|
+
await this.neo4j.writeOne(query);
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
async clearRequest(params) {
|
|
143
|
+
await this._unscoped(async () => {
|
|
144
|
+
const query = this.neo4j.initQuery();
|
|
145
|
+
query.queryParams = { ...query.queryParams, userId: params.userId };
|
|
146
|
+
// nja-lint-ignore OD-1
|
|
147
|
+
query.query += `
|
|
148
|
+
MATCH (user:User {id: $userId})
|
|
149
|
+
REMOVE user.deletionRequestedAt, user.deletionScheduledAt, user.deletionUndoTokenHash
|
|
150
|
+
SET user.updatedAt = datetime()
|
|
151
|
+
`;
|
|
152
|
+
await this.neo4j.writeOne(query);
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
/** Logs the user out everywhere: every Auth and AuthCode, and every OAuth token revoked. */
|
|
156
|
+
async deleteSessions(params) {
|
|
157
|
+
await this._unscoped(async () => {
|
|
158
|
+
const query = this.neo4j.initQuery();
|
|
159
|
+
query.queryParams = { ...query.queryParams, userId: params.userId };
|
|
160
|
+
// nja-lint-ignore OD-1
|
|
161
|
+
query.query += `
|
|
162
|
+
MATCH (user:User {id: $userId})
|
|
163
|
+
CALL {
|
|
164
|
+
WITH user
|
|
165
|
+
MATCH (user)-[:HAS_AUTH]->(:Auth)-[:HAS_AUTH_CODE]->(code:AuthCode)
|
|
166
|
+
DETACH DELETE code
|
|
167
|
+
}
|
|
168
|
+
CALL {
|
|
169
|
+
WITH user
|
|
170
|
+
MATCH (user)-[:HAS_AUTH]->(auth:Auth)
|
|
171
|
+
DETACH DELETE auth
|
|
172
|
+
}
|
|
173
|
+
WITH user
|
|
174
|
+
${(0, revoke_user_oauth_tokens_query_1.revokeUserOAuthTokensClause)("user")}
|
|
175
|
+
RETURN user.id AS userId
|
|
176
|
+
`;
|
|
177
|
+
await this.neo4j.writeOne(query);
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
exports.AccountDeletionRepository = AccountDeletionRepository;
|
|
182
|
+
exports.AccountDeletionRepository = AccountDeletionRepository = __decorate([
|
|
183
|
+
(0, common_1.Injectable)(),
|
|
184
|
+
__metadata("design:paramtypes", [neo4j_service_1.Neo4jService, security_service_1.SecurityService, nestjs_cls_1.ClsService])
|
|
185
|
+
], AccountDeletionRepository);
|
|
186
|
+
//# sourceMappingURL=account-deletion.repository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"account-deletion.repository.js","sourceRoot":"","sources":["../../../../src/foundations/account-deletion/repositories/account-deletion.repository.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { ClsService } from "nestjs-cls";
|
|
2
|
+
import { AbstractRepository } from "../../../core/neo4j/abstracts/abstract.repository";
|
|
3
|
+
import { Neo4jService } from "../../../core/neo4j/services/neo4j.service";
|
|
4
|
+
import { SecurityService } from "../../../core/security/services/security.service";
|
|
5
|
+
import { User, UserDescriptor } from "../../user/entities/user";
|
|
6
|
+
export declare class AccountDeletionWipeRepository extends AbstractRepository<User, typeof UserDescriptor.relationships> {
|
|
7
|
+
protected readonly descriptor: import("../../..").EntityDescriptor<User, {
|
|
8
|
+
role: {
|
|
9
|
+
model: import("../../..").DataMeta;
|
|
10
|
+
direction: "out";
|
|
11
|
+
relationship: string;
|
|
12
|
+
cardinality: "many";
|
|
13
|
+
dtoKey: string;
|
|
14
|
+
readOnly: true;
|
|
15
|
+
};
|
|
16
|
+
company: {
|
|
17
|
+
model: import("../../..").DataModelInterface<import("../..").Company>;
|
|
18
|
+
direction: "out";
|
|
19
|
+
relationship: string;
|
|
20
|
+
cardinality: "one";
|
|
21
|
+
};
|
|
22
|
+
module: {
|
|
23
|
+
model: import("../../..").DataMeta;
|
|
24
|
+
direction: "out";
|
|
25
|
+
relationship: string;
|
|
26
|
+
cardinality: "many";
|
|
27
|
+
dtoKey: string;
|
|
28
|
+
};
|
|
29
|
+
}>;
|
|
30
|
+
constructor(neo4j: Neo4jService, securityService: SecurityService, clsService: ClsService);
|
|
31
|
+
deleteUserLinkedNodes(params: {
|
|
32
|
+
userId: string;
|
|
33
|
+
batchSize: number;
|
|
34
|
+
}): Promise<void>;
|
|
35
|
+
detachUserFromForeignNodes(params: {
|
|
36
|
+
userId: string;
|
|
37
|
+
batchSize: number;
|
|
38
|
+
}): Promise<void>;
|
|
39
|
+
deleteEmailKeyed(params: {
|
|
40
|
+
email: string;
|
|
41
|
+
userId: string;
|
|
42
|
+
batchSize: number;
|
|
43
|
+
}): Promise<void>;
|
|
44
|
+
deleteStripeNodes(params: {
|
|
45
|
+
companyId: string;
|
|
46
|
+
batchSize: number;
|
|
47
|
+
}): Promise<void>;
|
|
48
|
+
deleteCompanyNodes(params: {
|
|
49
|
+
companyId: string;
|
|
50
|
+
batchSize: number;
|
|
51
|
+
}): Promise<void>;
|
|
52
|
+
deleteDisconnectedKnowledge(params: {
|
|
53
|
+
batchSize: number;
|
|
54
|
+
}): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* Deletes the Company node and its Memberships in one transaction (a company has a handful
|
|
57
|
+
* of memberships; an orphaned Membership would read as a platform membership). Remaining
|
|
58
|
+
* edges, referrals included, go with the Company.
|
|
59
|
+
*/
|
|
60
|
+
deleteCompanyShell(params: {
|
|
61
|
+
companyId: string;
|
|
62
|
+
}): Promise<void>;
|
|
63
|
+
/** Hard-deletes the User node. Last step of an account wipe. */
|
|
64
|
+
deleteUserNode(params: {
|
|
65
|
+
userId: string;
|
|
66
|
+
}): Promise<void>;
|
|
67
|
+
/** Ids of the users of a company (legacy BELONGS_TO edge or a Membership), members first. */
|
|
68
|
+
findCompanyUserIds(params: {
|
|
69
|
+
companyId: string;
|
|
70
|
+
}): Promise<string[]>;
|
|
71
|
+
private _companyDelete;
|
|
72
|
+
/** Seeds a query with only the parameters it needs (never the CLS company / user). */
|
|
73
|
+
private _wipeQuery;
|
|
74
|
+
/** Runs one batch per transaction until a batch deletes nothing. */
|
|
75
|
+
private _deleteInBatches;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=account-deletion.wipe.repository.d.ts.map
|
package/dist/foundations/account-deletion/repositories/account-deletion.wipe.repository.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"account-deletion.wipe.repository.d.ts","sourceRoot":"","sources":["../../../../src/foundations/account-deletion/repositories/account-deletion.wipe.repository.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,kBAAkB,EAAE,MAAM,mDAAmD,CAAC;AACvF,OAAO,EAAE,YAAY,EAAE,MAAM,4CAA4C,CAAC;AAC1E,OAAO,EAAE,eAAe,EAAE,MAAM,kDAAkD,CAAC;AACnF,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AA8EhE,qBACa,6BAA8B,SAAQ,kBAAkB,CAAC,IAAI,EAAE,OAAO,cAAc,CAAC,aAAa,CAAC;IAC9G,SAAS,CAAC,QAAQ,CAAC,UAAU;;;;;;;;;;;;;;;;;;;;;;OAAkB;gBAEnC,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,UAAU,EAAE,UAAU;IAInF,qBAAqB,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBnF,0BAA0B,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAcxF,gBAAgB,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB7F,iBAAiB,CAAC,MAAM,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAMlF,kBAAkB,CAAC,MAAM,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAMnF,2BAA2B,CAAC,MAAM,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB/E;;;;OAIG;IACG,kBAAkB,CAAC,MAAM,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IActE,gEAAgE;IAC1D,cAAc,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAW/D,6FAA6F;IACvF,kBAAkB,CAAC,MAAM,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAyB1E,OAAO,CAAC,cAAc;IAatB,sFAAsF;IACtF,OAAO,CAAC,UAAU;IAYlB,oEAAoE;YACtD,gBAAgB;CAa/B"}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.AccountDeletionWipeRepository = void 0;
|
|
13
|
+
// Only caller: AccountDeletionProcessor (through AccountWipeService and FullCompanyDeletionService), never an HTTP request.
|
|
14
|
+
//
|
|
15
|
+
// Owner-approved exception (Carlo, 2026-10-25, recorded in the account-deletion plan):
|
|
16
|
+
// - The wipe deletes nodes of every label that hang off one user or one company, across
|
|
17
|
+
// companies, so it matches by an explicit user / company id instead of buildDefaultMatch().
|
|
18
|
+
// - initQuery() is not used: in the processor the CLS carries companyId and userId, and
|
|
19
|
+
// initQuery() would prepend a currentUser-BELONGS_TO-company match that stops matching as
|
|
20
|
+
// soon as the company or the user is gone, turning every later step into a silent no-op.
|
|
21
|
+
// - Bulk deletes read their own scalar "deleted" count, which readOne()/readMany() cannot
|
|
22
|
+
// map, so they run through executeInTransaction (writeOne returns null without a serialiser).
|
|
23
|
+
// - Every id is a query parameter. Every bulk delete runs LIMIT $batchSize (a neo4j Integer,
|
|
24
|
+
// WIPE_BATCH_SIZE = 1000) per transaction and loops until a batch deletes 0. The patterns
|
|
25
|
+
// interpolated into the query text are constants of this file, never request input.
|
|
26
|
+
const common_1 = require("@nestjs/common");
|
|
27
|
+
const neo4j_driver_1 = require("neo4j-driver");
|
|
28
|
+
const nestjs_cls_1 = require("nestjs-cls");
|
|
29
|
+
const abstract_repository_1 = require("../../../core/neo4j/abstracts/abstract.repository");
|
|
30
|
+
const neo4j_service_1 = require("../../../core/neo4j/services/neo4j.service");
|
|
31
|
+
const security_service_1 = require("../../../core/security/services/security.service");
|
|
32
|
+
const user_1 = require("../../user/entities/user");
|
|
33
|
+
/**
|
|
34
|
+
* Nodes owned by the user, deleted with the account. Each pattern binds the node to delete
|
|
35
|
+
* as `n`; `u` is the user. Order matters: children before the node they hang off.
|
|
36
|
+
*/
|
|
37
|
+
const USER_LINKED_PATTERNS = [
|
|
38
|
+
"(u)-[:HAS_AUTH]->(:Auth)-[:HAS_AUTH_CODE]->(n)",
|
|
39
|
+
"(u)-[:HAS_AUTH]->(n)",
|
|
40
|
+
"(u)-[:HAS_TOTP_AUTHENTICATOR]->(n)",
|
|
41
|
+
"(u)-[:HAS_BACKUP_CODE]->(n)",
|
|
42
|
+
"(u)-[:HAS_TWO_FACTOR_CONFIG]->(n)",
|
|
43
|
+
"(u)-[:HAS_PENDING_TWO_FACTOR]->(n)",
|
|
44
|
+
"(u)-[:HAS_PASSKEY]->(n)",
|
|
45
|
+
"(u)-[:OWNS_CLIENT]->(:OAuthClient)-[:ISSUED_ACCESS_TOKEN]->(:OAuthAccessToken)-[:LINKED_REFRESH]->(n)",
|
|
46
|
+
"(u)-[:OWNS_CLIENT]->(:OAuthClient)-[:ISSUED_ACCESS_TOKEN|ISSUED_CODE]->(n)",
|
|
47
|
+
"(u)-[:OWNS_CLIENT]->(n)",
|
|
48
|
+
"(u)-[:AUTHORIZED_CODE]->(n)",
|
|
49
|
+
"(u)-[:HAS_ACCESS_TOKEN]->(:OAuthAccessToken)-[:LINKED_REFRESH]->(n)",
|
|
50
|
+
"(u)-[:HAS_ACCESS_TOKEN]->(n)",
|
|
51
|
+
"(n:OAuthRefreshToken {userId: u.id})",
|
|
52
|
+
"(n:OAuthAccessToken {userId: u.id})",
|
|
53
|
+
"(u)-[:HAS_DISCORD]->(n)",
|
|
54
|
+
"(u)-[:HAS_GOOGLE]->(n)",
|
|
55
|
+
"(u)-[:HAS_PUSH]->(n)",
|
|
56
|
+
"(n:Notification)-[:TRIGGERED_FOR]->(u)",
|
|
57
|
+
"(u)-[:PERFORMED]->(n)",
|
|
58
|
+
"(n:AnalyticsPageView)-[:IN_SESSION]->(:AnalyticsSession)-[:BY_USER]->(u)",
|
|
59
|
+
"(n:AnalyticsSession)-[:BY_USER]->(u)",
|
|
60
|
+
"(u)-[:HAS_MEMBERSHIP]->(n:Membership)",
|
|
61
|
+
];
|
|
62
|
+
/** Edges from the user to nodes that belong to someone else: the edge goes, the node stays. */
|
|
63
|
+
const FOREIGN_EDGE_TYPES = "TRIGGERED_BY|CREATED_BY|CREATED|PUBLISHED|AUTHORED";
|
|
64
|
+
/** Email-keyed nodes. `$email` and `$userId` are parameters. */
|
|
65
|
+
const EMAIL_KEYED_MATCHES = [
|
|
66
|
+
"MATCH (n:Waitlist) WHERE ($email <> '' AND toLower(n.email) = toLower($email)) OR n.userId = $userId",
|
|
67
|
+
"MATCH (n:EmailAutomationEnrollment) WHERE $email <> '' AND toLower(n.email) = toLower($email)",
|
|
68
|
+
"MATCH (n:EmailOptOut) WHERE $email <> '' AND toLower(n.email) = toLower($email)",
|
|
69
|
+
];
|
|
70
|
+
/**
|
|
71
|
+
* Stripe nodes of the company's customer, children first. StripeInvoice has no Company edge
|
|
72
|
+
* (it hangs off the customer and the subscription), so the company sweep alone would orphan it.
|
|
73
|
+
*/
|
|
74
|
+
const STRIPE_PATTERNS = [
|
|
75
|
+
"(company)<-[:BELONGS_TO]-(:StripeCustomer)<-[:BELONGS_TO]-(n:StripeInvoice)",
|
|
76
|
+
"(company)<-[:BELONGS_TO]-(:StripeCustomer)<-[:BELONGS_TO]-(:StripeSubscription)<-[:FOR_SUBSCRIPTION]-(n:StripeInvoice)",
|
|
77
|
+
"(company)<-[:BELONGS_TO]-(:StripeCustomer)<-[:BELONGS_TO]-(:StripeSubscription)<-[:BELONGS_TO]-(n:StripeUsageRecord)",
|
|
78
|
+
"(company)<-[:BELONGS_TO]-(:StripeCustomer)--(n:StripePaymentMethod)",
|
|
79
|
+
"(company)<-[:BELONGS_TO]-(:StripeCustomer)<-[:BELONGS_TO]-(n:StripeSubscription)",
|
|
80
|
+
"(company)<-[:BELONGS_TO]-(n:StripeCustomer)",
|
|
81
|
+
];
|
|
82
|
+
/**
|
|
83
|
+
* Company nodes. Users are never swept here: placeholders are deleted one by one with their
|
|
84
|
+
* user-linked nodes, and members keep their node (the account-deletion flow handles them).
|
|
85
|
+
* Memberships are not swept here either: they go with the Company in deleteCompanyShell, so a
|
|
86
|
+
* crash half way leaves the member still recognised as the company's only member on re-run.
|
|
87
|
+
*/
|
|
88
|
+
const COMPANY_PATTERNS = [
|
|
89
|
+
"(company)<-[:BELONGS_TO]-(owner)-[:HAS_CHUNK]->(n:Chunk) WHERE NOT owner:User",
|
|
90
|
+
"(company)<-[:BELONGS_TO]-(n) WHERE NOT n:User AND NOT n:Company",
|
|
91
|
+
];
|
|
92
|
+
/**
|
|
93
|
+
* Shared knowledge left with nothing pointing at it. AtomicFacts are shared across companies
|
|
94
|
+
* (md5 id), so only facts no chunk links any more go; KeyConcepts go only when nothing at all
|
|
95
|
+
* (fact, community, key-concept relationship) is linked to them.
|
|
96
|
+
*/
|
|
97
|
+
const DISCONNECTED_KNOWLEDGE_MATCHES = [
|
|
98
|
+
"MATCH (n:AtomicFact) WHERE NOT (n)<-[:HAS_ATOMIC_FACT]-() // nja-lint-ignore wipe-exception",
|
|
99
|
+
"MATCH (n:KeyConcept) WHERE NOT (n)--() // nja-lint-ignore wipe-exception",
|
|
100
|
+
];
|
|
101
|
+
let AccountDeletionWipeRepository = class AccountDeletionWipeRepository extends abstract_repository_1.AbstractRepository {
|
|
102
|
+
constructor(neo4j, securityService, clsService) {
|
|
103
|
+
super(neo4j, securityService, clsService);
|
|
104
|
+
this.descriptor = user_1.UserDescriptor;
|
|
105
|
+
}
|
|
106
|
+
async deleteUserLinkedNodes(params) {
|
|
107
|
+
for (const pattern of USER_LINKED_PATTERNS) {
|
|
108
|
+
await this._deleteInBatches(this._wipeQuery({ userId: params.userId, batchSize: params.batchSize }, `
|
|
109
|
+
MATCH (u:User {id: $userId}) // nja-lint-ignore wipe-exception
|
|
110
|
+
MATCH ${pattern}
|
|
111
|
+
WITH DISTINCT n LIMIT $batchSize
|
|
112
|
+
DETACH DELETE n
|
|
113
|
+
RETURN count(*) AS deleted
|
|
114
|
+
`));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
async detachUserFromForeignNodes(params) {
|
|
118
|
+
await this._deleteInBatches(this._wipeQuery({ userId: params.userId, batchSize: params.batchSize }, `
|
|
119
|
+
MATCH (u:User {id: $userId})-[r:${FOREIGN_EDGE_TYPES}]-() // nja-lint-ignore wipe-exception
|
|
120
|
+
WITH r LIMIT $batchSize
|
|
121
|
+
DELETE r
|
|
122
|
+
RETURN count(*) AS deleted
|
|
123
|
+
`));
|
|
124
|
+
}
|
|
125
|
+
async deleteEmailKeyed(params) {
|
|
126
|
+
for (const match of EMAIL_KEYED_MATCHES) {
|
|
127
|
+
await this._deleteInBatches(this._wipeQuery({ email: params.email ?? "", userId: params.userId, batchSize: params.batchSize }, `
|
|
128
|
+
${match} // nja-lint-ignore wipe-exception
|
|
129
|
+
WITH n LIMIT $batchSize
|
|
130
|
+
DETACH DELETE n
|
|
131
|
+
RETURN count(*) AS deleted
|
|
132
|
+
`));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
async deleteStripeNodes(params) {
|
|
136
|
+
for (const pattern of STRIPE_PATTERNS) {
|
|
137
|
+
await this._deleteInBatches(this._companyDelete(params, pattern));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
async deleteCompanyNodes(params) {
|
|
141
|
+
for (const pattern of COMPANY_PATTERNS) {
|
|
142
|
+
await this._deleteInBatches(this._companyDelete(params, pattern));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
async deleteDisconnectedKnowledge(params) {
|
|
146
|
+
for (const match of DISCONNECTED_KNOWLEDGE_MATCHES) {
|
|
147
|
+
await this._deleteInBatches(this._wipeQuery({ batchSize: params.batchSize }, `
|
|
148
|
+
${match}
|
|
149
|
+
WITH n LIMIT $batchSize
|
|
150
|
+
DETACH DELETE n
|
|
151
|
+
RETURN count(*) AS deleted
|
|
152
|
+
`));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Deletes the Company node and its Memberships in one transaction (a company has a handful
|
|
157
|
+
* of memberships; an orphaned Membership would read as a platform membership). Remaining
|
|
158
|
+
* edges, referrals included, go with the Company.
|
|
159
|
+
*/
|
|
160
|
+
async deleteCompanyShell(params) {
|
|
161
|
+
const { query, queryParams } = this._wipeQuery({ companyId: params.companyId }, `
|
|
162
|
+
MATCH (company:Company {id: $companyId}) // nja-lint-ignore wipe-exception
|
|
163
|
+
OPTIONAL MATCH (company)<-[:IN_COMPANY]-(membership:Membership)
|
|
164
|
+
DETACH DELETE membership
|
|
165
|
+
WITH DISTINCT company
|
|
166
|
+
DETACH DELETE company
|
|
167
|
+
`);
|
|
168
|
+
await this.neo4j.executeInTransaction([{ query, params: queryParams }]);
|
|
169
|
+
}
|
|
170
|
+
/** Hard-deletes the User node. Last step of an account wipe. */
|
|
171
|
+
async deleteUserNode(params) {
|
|
172
|
+
const { query, queryParams } = this._wipeQuery({ userId: params.userId }, `
|
|
173
|
+
MATCH (u:User {id: $userId}) // nja-lint-ignore wipe-exception
|
|
174
|
+
DETACH DELETE u
|
|
175
|
+
`);
|
|
176
|
+
await this.neo4j.executeInTransaction([{ query, params: queryParams }]);
|
|
177
|
+
}
|
|
178
|
+
/** Ids of the users of a company (legacy BELONGS_TO edge or a Membership), members first. */
|
|
179
|
+
async findCompanyUserIds(params) {
|
|
180
|
+
const { query, queryParams } = this._wipeQuery({ companyId: params.companyId }, `
|
|
181
|
+
MATCH (company:Company {id: $companyId}) // nja-lint-ignore wipe-exception
|
|
182
|
+
CALL {
|
|
183
|
+
WITH company
|
|
184
|
+
MATCH (u:User)-[:BELONGS_TO]->(company)
|
|
185
|
+
RETURN u
|
|
186
|
+
UNION
|
|
187
|
+
WITH company
|
|
188
|
+
MATCH (u:User)-[:HAS_MEMBERSHIP]->(:Membership)-[:IN_COMPANY]->(company)
|
|
189
|
+
RETURN u
|
|
190
|
+
}
|
|
191
|
+
WITH DISTINCT company, u
|
|
192
|
+
RETURN u.id AS id,
|
|
193
|
+
EXISTS { (u)-[:HAS_MEMBERSHIP]->(:Membership)-[:IN_COMPANY]->(company) } AS isMember
|
|
194
|
+
ORDER BY isMember DESC, id ASC
|
|
195
|
+
`);
|
|
196
|
+
// nja-lint-ignore wipe-exception
|
|
197
|
+
const result = await this.neo4j.read(query, queryParams);
|
|
198
|
+
return result.records.map((record) => String(record.get("id")));
|
|
199
|
+
}
|
|
200
|
+
_companyDelete(params, pattern) {
|
|
201
|
+
return this._wipeQuery({ companyId: params.companyId, batchSize: params.batchSize }, `
|
|
202
|
+
MATCH (company:Company {id: $companyId}) // nja-lint-ignore wipe-exception
|
|
203
|
+
MATCH ${pattern}
|
|
204
|
+
WITH DISTINCT n LIMIT $batchSize
|
|
205
|
+
DETACH DELETE n
|
|
206
|
+
RETURN count(*) AS deleted
|
|
207
|
+
`);
|
|
208
|
+
}
|
|
209
|
+
/** Seeds a query with only the parameters it needs (never the CLS company / user). */
|
|
210
|
+
_wipeQuery(params, query) {
|
|
211
|
+
const queryParams = {};
|
|
212
|
+
if (params.userId !== undefined)
|
|
213
|
+
queryParams.userId = params.userId;
|
|
214
|
+
if (params.companyId !== undefined)
|
|
215
|
+
queryParams.companyId = params.companyId;
|
|
216
|
+
if (params.email !== undefined)
|
|
217
|
+
queryParams.email = params.email;
|
|
218
|
+
if (params.batchSize !== undefined)
|
|
219
|
+
queryParams.batchSize = (0, neo4j_driver_1.int)(params.batchSize);
|
|
220
|
+
return { query, queryParams };
|
|
221
|
+
}
|
|
222
|
+
/** Runs one batch per transaction until a batch deletes nothing. */
|
|
223
|
+
async _deleteInBatches(wipeQuery) {
|
|
224
|
+
let total = 0;
|
|
225
|
+
for (;;) {
|
|
226
|
+
// nja-lint-ignore wipe-exception
|
|
227
|
+
const [result] = await this.neo4j.executeInTransaction([
|
|
228
|
+
{ query: wipeQuery.query, params: wipeQuery.queryParams },
|
|
229
|
+
]);
|
|
230
|
+
const raw = result?.records?.[0]?.get("deleted");
|
|
231
|
+
const deleted = (0, neo4j_driver_1.isInt)(raw) ? raw.toNumber() : Number(raw ?? 0);
|
|
232
|
+
if (!deleted)
|
|
233
|
+
return total;
|
|
234
|
+
total += deleted;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
exports.AccountDeletionWipeRepository = AccountDeletionWipeRepository;
|
|
239
|
+
exports.AccountDeletionWipeRepository = AccountDeletionWipeRepository = __decorate([
|
|
240
|
+
(0, common_1.Injectable)(),
|
|
241
|
+
__metadata("design:paramtypes", [neo4j_service_1.Neo4jService, security_service_1.SecurityService, nestjs_cls_1.ClsService])
|
|
242
|
+
], AccountDeletionWipeRepository);
|
|
243
|
+
//# sourceMappingURL=account-deletion.wipe.repository.js.map
|
package/dist/foundations/account-deletion/repositories/account-deletion.wipe.repository.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"account-deletion.wipe.repository.js","sourceRoot":"","sources":["../../../../src/foundations/account-deletion/repositories/account-deletion.wipe.repository.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,4HAA4H;AAC5H,EAAE;AACF,uFAAuF;AACvF,wFAAwF;AACxF,8FAA8F;AAC9F,wFAAwF;AACxF,4FAA4F;AAC5F,2FAA2F;AAC3F,0FAA0F;AAC1F,gGAAgG;AAChG,6FAA6F;AAC7F,4FAA4F;AAC5F,sFAAsF;AACtF,2CAA4C;AAC5C,+CAA0C;AAC1C,2CAAwC;AACxC,2FAAuF;AACvF,8EAA0E;AAC1E,uFAAmF;AACnF,mDAAgE;AAIhE;;;GAGG;AACH,MAAM,oBAAoB,GAAa;IACrC,gDAAgD;IAChD,sBAAsB;IACtB,oCAAoC;IACpC,6BAA6B;IAC7B,mCAAmC;IACnC,oCAAoC;IACpC,yBAAyB;IACzB,uGAAuG;IACvG,4EAA4E;IAC5E,yBAAyB;IACzB,6BAA6B;IAC7B,qEAAqE;IACrE,8BAA8B;IAC9B,sCAAsC;IACtC,qCAAqC;IACrC,yBAAyB;IACzB,wBAAwB;IACxB,sBAAsB;IACtB,wCAAwC;IACxC,uBAAuB;IACvB,0EAA0E;IAC1E,sCAAsC;IACtC,uCAAuC;CACxC,CAAC;AAEF,+FAA+F;AAC/F,MAAM,kBAAkB,GAAG,oDAAoD,CAAC;AAEhF,gEAAgE;AAChE,MAAM,mBAAmB,GAAa;IACpC,sGAAsG;IACtG,+FAA+F;IAC/F,iFAAiF;CAClF,CAAC;AAEF;;;GAGG;AACH,MAAM,eAAe,GAAa;IAChC,6EAA6E;IAC7E,wHAAwH;IACxH,sHAAsH;IACtH,qEAAqE;IACrE,kFAAkF;IAClF,6CAA6C;CAC9C,CAAC;AAEF;;;;;GAKG;AACH,MAAM,gBAAgB,GAAa;IACjC,+EAA+E;IAC/E,iEAAiE;CAClE,CAAC;AAEF;;;;GAIG;AACH,MAAM,8BAA8B,GAAa;IAC/C,6FAA6F;IAC7F,0EAA0E;CAC3E,CAAC;AAGK,IAAM,6BAA6B,GAAnC,MAAM,6BAA8B,SAAQ,wCAA6D;IAG9G,YAAY,KAAmB,EAAE,eAAgC,EAAE,UAAsB;QACvF,KAAK,CAAC,KAAK,EAAE,eAAe,EAAE,UAAU,CAAC,CAAC;QAHzB,eAAU,GAAG,qBAAc,CAAC;IAI/C,CAAC;IAED,KAAK,CAAC,qBAAqB,CAAC,MAA6C;QACvE,KAAK,MAAM,OAAO,IAAI,oBAAoB,EAAE,CAAC;YAC3C,MAAM,IAAI,CAAC,gBAAgB,CACzB,IAAI,CAAC,UAAU,CACb,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,EACtD;;kBAEQ,OAAO;;;;SAIhB,CACA,CACF,CAAC;QACJ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,0BAA0B,CAAC,MAA6C;QAC5E,MAAM,IAAI,CAAC,gBAAgB,CACzB,IAAI,CAAC,UAAU,CACb,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,EACtD;0CACkC,kBAAkB;;;;OAIrD,CACA,CACF,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAC,MAA4D;QACjF,KAAK,MAAM,KAAK,IAAI,mBAAmB,EAAE,CAAC;YACxC,MAAM,IAAI,CAAC,gBAAgB,CACzB,IAAI,CAAC,UAAU,CACb,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,EACjF;YACE,KAAK;;;;SAIR,CACA,CACF,CAAC;QACJ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,iBAAiB,CAAC,MAAgD;QACtE,KAAK,MAAM,OAAO,IAAI,eAAe,EAAE,CAAC;YACtC,MAAM,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;QACpE,CAAC;IACH,CAAC;IAED,KAAK,CAAC,kBAAkB,CAAC,MAAgD;QACvE,KAAK,MAAM,OAAO,IAAI,gBAAgB,EAAE,CAAC;YACvC,MAAM,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;QACpE,CAAC;IACH,CAAC;IAED,KAAK,CAAC,2BAA2B,CAAC,MAA6B;QAC7D,KAAK,MAAM,KAAK,IAAI,8BAA8B,EAAE,CAAC;YACnD,MAAM,IAAI,CAAC,gBAAgB,CACzB,IAAI,CAAC,UAAU,CACb,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,EAC/B;YACE,KAAK;;;;SAIR,CACA,CACF,CAAC;QACJ,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,kBAAkB,CAAC,MAA6B;QACpD,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,IAAI,CAAC,UAAU,CAC5C,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,EAC/B;;;;;;KAMD,CACA,CAAC;QACF,MAAM,IAAI,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC;IAC1E,CAAC;IAED,gEAAgE;IAChE,KAAK,CAAC,cAAc,CAAC,MAA0B;QAC7C,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,IAAI,CAAC,UAAU,CAC5C,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,EACzB;;;KAGD,CACA,CAAC;QACF,MAAM,IAAI,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC;IAC1E,CAAC;IAED,6FAA6F;IAC7F,KAAK,CAAC,kBAAkB,CAAC,MAA6B;QACpD,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,IAAI,CAAC,UAAU,CAC5C,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,EAC/B;;;;;;;;;;;;;;;KAeD,CACA,CAAC;QACF,iCAAiC;QACjC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;QACzD,OAAQ,MAAM,CAAC,OAAiB,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC7E,CAAC;IAEO,cAAc,CAAC,MAAgD,EAAE,OAAe;QACtF,OAAO,IAAI,CAAC,UAAU,CACpB,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,EAC5D;;cAEQ,OAAO;;;;KAIhB,CACA,CAAC;IACJ,CAAC;IAED,sFAAsF;IAC9E,UAAU,CAChB,MAAmF,EACnF,KAAa;QAEb,MAAM,WAAW,GAA4B,EAAE,CAAC;QAChD,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS;YAAE,WAAW,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;QACpE,IAAI,MAAM,CAAC,SAAS,KAAK,SAAS;YAAE,WAAW,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;QAC7E,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS;YAAE,WAAW,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;QACjE,IAAI,MAAM,CAAC,SAAS,KAAK,SAAS;YAAE,WAAW,CAAC,SAAS,GAAG,IAAA,kBAAG,EAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAClF,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;IAChC,CAAC;IAED,oEAAoE;IAC5D,KAAK,CAAC,gBAAgB,CAAC,SAAoB;QACjD,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,SAAS,CAAC;YACR,iCAAiC;YACjC,MAAM,CAAC,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,oBAAoB,CAAC;gBACrD,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC,WAAW,EAAE;aAC1D,CAAC,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC;YACjD,MAAM,OAAO,GAAG,IAAA,oBAAK,EAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;YAC/D,IAAI,CAAC,OAAO;gBAAE,OAAO,KAAK,CAAC;YAC3B,KAAK,IAAI,OAAO,CAAC;QACnB,CAAC;IACH,CAAC;CACF,CAAA;AAnLY,sEAA6B;wCAA7B,6BAA6B;IADzC,IAAA,mBAAU,GAAE;qCAIQ,4BAAY,EAAmB,kCAAe,EAAc,uBAAU;GAH9E,6BAA6B,CAmLzC"}
|