tychat-contracts 1.4.4 → 1.4.6

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.
@@ -0,0 +1,25 @@
1
+ name: Publish packages
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ push:
6
+ tags:
7
+ - 'v*.*.*'
8
+
9
+ jobs:
10
+ build:
11
+ runs-on: ubuntu-latest
12
+ permissions:
13
+ contents: read
14
+ id-token: write
15
+ steps:
16
+ - uses: actions/checkout@v5
17
+ - uses: actions/setup-node@v4
18
+ with:
19
+ node-version: '20'
20
+ registry-url: 'https://registry.npmjs.org'
21
+ cache: npm
22
+ - run: npm ci
23
+ - run: npm publish --provenance --access public
24
+ env:
25
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
@@ -11,4 +11,8 @@ export declare const TOPIC_ANALYTICS_DASHBOARD = "analytics.dashboard";
11
11
  export declare const TOPIC_ANALYTICS_TIMESERIES = "analytics.timeseries";
12
12
  /** Topic for requesting event details with pagination. */
13
13
  export declare const TOPIC_ANALYTICS_EVENTS_LIST = "analytics.events.list";
14
+ /** Topic for ingesting tenant user-action audit rows (dedicated table). */
15
+ export declare const TOPIC_ANALYTICS_TENANT_AUDIT_INGEST = "analytics.tenant_audit.ingest";
16
+ /** Topic for paginated tenant audit log listing. */
17
+ export declare const TOPIC_ANALYTICS_TENANT_AUDIT_LIST = "analytics.tenant_audit.list";
14
18
  //# sourceMappingURL=analytics-kafka-topics.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"analytics-kafka-topics.d.ts","sourceRoot":"","sources":["../../src/analytics/analytics-kafka-topics.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,kEAAkE;AAClE,eAAO,MAAM,qBAAqB,oBAAoB,CAAC;AAEvD,sEAAsE;AACtE,eAAO,MAAM,sBAAsB,qBAAqB,CAAC;AAEzD,oEAAoE;AACpE,eAAO,MAAM,yBAAyB,wBAAwB,CAAC;AAE/D,8DAA8D;AAC9D,eAAO,MAAM,0BAA0B,yBAAyB,CAAC;AAEjE,0DAA0D;AAC1D,eAAO,MAAM,2BAA2B,0BAA0B,CAAC"}
1
+ {"version":3,"file":"analytics-kafka-topics.d.ts","sourceRoot":"","sources":["../../src/analytics/analytics-kafka-topics.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,kEAAkE;AAClE,eAAO,MAAM,qBAAqB,oBAAoB,CAAC;AAEvD,sEAAsE;AACtE,eAAO,MAAM,sBAAsB,qBAAqB,CAAC;AAEzD,oEAAoE;AACpE,eAAO,MAAM,yBAAyB,wBAAwB,CAAC;AAE/D,8DAA8D;AAC9D,eAAO,MAAM,0BAA0B,yBAAyB,CAAC;AAEjE,0DAA0D;AAC1D,eAAO,MAAM,2BAA2B,0BAA0B,CAAC;AAEnE,2EAA2E;AAC3E,eAAO,MAAM,mCAAmC,kCAAkC,CAAC;AAEnF,oDAAoD;AACpD,eAAO,MAAM,iCAAiC,gCAAgC,CAAC"}
@@ -3,7 +3,7 @@
3
3
  * Kafka topic names used by the analytics service.
4
4
  */
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.TOPIC_ANALYTICS_EVENTS_LIST = exports.TOPIC_ANALYTICS_TIMESERIES = exports.TOPIC_ANALYTICS_DASHBOARD = exports.TOPIC_ANALYTICS_REPORT = exports.TOPIC_ANALYTICS_EVENT = void 0;
6
+ exports.TOPIC_ANALYTICS_TENANT_AUDIT_LIST = exports.TOPIC_ANALYTICS_TENANT_AUDIT_INGEST = exports.TOPIC_ANALYTICS_EVENTS_LIST = exports.TOPIC_ANALYTICS_TIMESERIES = exports.TOPIC_ANALYTICS_DASHBOARD = exports.TOPIC_ANALYTICS_REPORT = exports.TOPIC_ANALYTICS_EVENT = void 0;
7
7
  /** Topic for ingesting analytic events from all microservices. */
8
8
  exports.TOPIC_ANALYTICS_EVENT = 'analytics.event';
9
9
  /** Topic for requesting a report grouped by event type and period. */
@@ -14,3 +14,7 @@ exports.TOPIC_ANALYTICS_DASHBOARD = 'analytics.dashboard';
14
14
  exports.TOPIC_ANALYTICS_TIMESERIES = 'analytics.timeseries';
15
15
  /** Topic for requesting event details with pagination. */
16
16
  exports.TOPIC_ANALYTICS_EVENTS_LIST = 'analytics.events.list';
17
+ /** Topic for ingesting tenant user-action audit rows (dedicated table). */
18
+ exports.TOPIC_ANALYTICS_TENANT_AUDIT_INGEST = 'analytics.tenant_audit.ingest';
19
+ /** Topic for paginated tenant audit log listing. */
20
+ exports.TOPIC_ANALYTICS_TENANT_AUDIT_LIST = 'analytics.tenant_audit.list';
@@ -0,0 +1,23 @@
1
+ /** Outcome of the audited HTTP mutation. */
2
+ export declare const TENANT_AUDIT_OUTCOMES: readonly ["success", "failure"];
3
+ export type TenantAuditOutcome = (typeof TENANT_AUDIT_OUTCOMES)[number];
4
+ /**
5
+ * Payload for persisting a tenant-scoped user action audit row (HTTP mutation).
6
+ */
7
+ export declare class CreateTenantAuditLogDto {
8
+ eventId: string;
9
+ tenant: string;
10
+ actorType: string;
11
+ actorId: string;
12
+ action: string;
13
+ targetType?: string | null;
14
+ targetId?: string | null;
15
+ outcome: TenantAuditOutcome;
16
+ httpMethod: string;
17
+ httpPath: string;
18
+ httpStatus: number;
19
+ errorCode?: string | null;
20
+ metadata?: Record<string, unknown> | null;
21
+ occurredAt: string;
22
+ }
23
+ //# sourceMappingURL=create-tenant-audit-log.dto.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-tenant-audit-log.dto.d.ts","sourceRoot":"","sources":["../../src/analytics/create-tenant-audit-log.dto.ts"],"names":[],"mappings":"AAgBA,4CAA4C;AAC5C,eAAO,MAAM,qBAAqB,iCAAkC,CAAC;AACrE,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC;AAExE;;GAEG;AACH,qBAAa,uBAAuB;IAGlC,OAAO,EAAE,MAAM,CAAC;IAOhB,MAAM,EAAE,MAAM,CAAC;IAMf,SAAS,EAAE,MAAM,CAAC;IAMlB,OAAO,EAAE,MAAM,CAAC;IAShB,MAAM,EAAE,MAAM,CAAC;IAMf,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAM3B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAIzB,OAAO,EAAE,kBAAkB,CAAC;IAM5B,UAAU,EAAE,MAAM,CAAC;IAMnB,QAAQ,EAAE,MAAM,CAAC;IAMjB,UAAU,EAAE,MAAM,CAAC;IAMnB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAK1B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAI1C,UAAU,EAAE,MAAM,CAAC;CACpB"}
@@ -0,0 +1,131 @@
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.CreateTenantAuditLogDto = exports.TENANT_AUDIT_OUTCOMES = void 0;
13
+ const swagger_1 = require("@nestjs/swagger");
14
+ const class_validator_1 = require("class-validator");
15
+ /** Outcome of the audited HTTP mutation. */
16
+ exports.TENANT_AUDIT_OUTCOMES = ['success', 'failure'];
17
+ /**
18
+ * Payload for persisting a tenant-scoped user action audit row (HTTP mutation).
19
+ */
20
+ class CreateTenantAuditLogDto {
21
+ eventId;
22
+ tenant;
23
+ actorType;
24
+ actorId;
25
+ action;
26
+ targetType;
27
+ targetId;
28
+ outcome;
29
+ httpMethod;
30
+ httpPath;
31
+ httpStatus;
32
+ errorCode;
33
+ metadata;
34
+ occurredAt;
35
+ }
36
+ exports.CreateTenantAuditLogDto = CreateTenantAuditLogDto;
37
+ __decorate([
38
+ (0, swagger_1.ApiProperty)({ description: 'Unique id for idempotent insert', format: 'uuid' }),
39
+ (0, class_validator_1.IsUUID)(),
40
+ __metadata("design:type", String)
41
+ ], CreateTenantAuditLogDto.prototype, "eventId", void 0);
42
+ __decorate([
43
+ (0, swagger_1.ApiProperty)({ description: 'Tenant slug' }),
44
+ (0, class_validator_1.IsString)(),
45
+ (0, class_validator_1.IsNotEmpty)(),
46
+ (0, class_validator_1.MinLength)(1),
47
+ (0, class_validator_1.MaxLength)(255),
48
+ __metadata("design:type", String)
49
+ ], CreateTenantAuditLogDto.prototype, "tenant", void 0);
50
+ __decorate([
51
+ (0, swagger_1.ApiProperty)({ example: 'user', description: 'Actor category' }),
52
+ (0, class_validator_1.IsString)(),
53
+ (0, class_validator_1.IsNotEmpty)(),
54
+ (0, class_validator_1.MaxLength)(64),
55
+ __metadata("design:type", String)
56
+ ], CreateTenantAuditLogDto.prototype, "actorType", void 0);
57
+ __decorate([
58
+ (0, swagger_1.ApiProperty)({ description: 'Authenticated user id (sub)' }),
59
+ (0, class_validator_1.IsString)(),
60
+ (0, class_validator_1.IsNotEmpty)(),
61
+ (0, class_validator_1.MaxLength)(255),
62
+ __metadata("design:type", String)
63
+ ], CreateTenantAuditLogDto.prototype, "actorId", void 0);
64
+ __decorate([
65
+ (0, swagger_1.ApiProperty)({
66
+ example: 'http.mutation',
67
+ description: 'Deterministic action key (e.g. http.mutation)',
68
+ }),
69
+ (0, class_validator_1.IsString)(),
70
+ (0, class_validator_1.IsNotEmpty)(),
71
+ (0, class_validator_1.MaxLength)(128),
72
+ __metadata("design:type", String)
73
+ ], CreateTenantAuditLogDto.prototype, "action", void 0);
74
+ __decorate([
75
+ (0, swagger_1.ApiPropertyOptional)({ example: 'http' }),
76
+ (0, class_validator_1.IsOptional)(),
77
+ (0, class_validator_1.IsString)(),
78
+ (0, class_validator_1.MaxLength)(64),
79
+ __metadata("design:type", Object)
80
+ ], CreateTenantAuditLogDto.prototype, "targetType", void 0);
81
+ __decorate([
82
+ (0, swagger_1.ApiPropertyOptional)({ description: 'Route param id when present' }),
83
+ (0, class_validator_1.IsOptional)(),
84
+ (0, class_validator_1.IsString)(),
85
+ (0, class_validator_1.MaxLength)(255),
86
+ __metadata("design:type", Object)
87
+ ], CreateTenantAuditLogDto.prototype, "targetId", void 0);
88
+ __decorate([
89
+ (0, swagger_1.ApiProperty)({ enum: exports.TENANT_AUDIT_OUTCOMES }),
90
+ (0, class_validator_1.IsIn)([...exports.TENANT_AUDIT_OUTCOMES]),
91
+ __metadata("design:type", String)
92
+ ], CreateTenantAuditLogDto.prototype, "outcome", void 0);
93
+ __decorate([
94
+ (0, swagger_1.ApiProperty)({ example: 'POST' }),
95
+ (0, class_validator_1.IsString)(),
96
+ (0, class_validator_1.IsNotEmpty)(),
97
+ (0, class_validator_1.MaxLength)(16),
98
+ __metadata("design:type", String)
99
+ ], CreateTenantAuditLogDto.prototype, "httpMethod", void 0);
100
+ __decorate([
101
+ (0, swagger_1.ApiProperty)({ example: '/patients/123' }),
102
+ (0, class_validator_1.IsString)(),
103
+ (0, class_validator_1.IsNotEmpty)(),
104
+ (0, class_validator_1.MaxLength)(2048),
105
+ __metadata("design:type", String)
106
+ ], CreateTenantAuditLogDto.prototype, "httpPath", void 0);
107
+ __decorate([
108
+ (0, swagger_1.ApiProperty)({ example: 201 }),
109
+ (0, class_validator_1.IsInt)(),
110
+ (0, class_validator_1.Min)(100),
111
+ (0, class_validator_1.Max)(599),
112
+ __metadata("design:type", Number)
113
+ ], CreateTenantAuditLogDto.prototype, "httpStatus", void 0);
114
+ __decorate([
115
+ (0, swagger_1.ApiPropertyOptional)({ description: 'On failure, stable error code if available' }),
116
+ (0, class_validator_1.IsOptional)(),
117
+ (0, class_validator_1.IsString)(),
118
+ (0, class_validator_1.MaxLength)(128),
119
+ __metadata("design:type", Object)
120
+ ], CreateTenantAuditLogDto.prototype, "errorCode", void 0);
121
+ __decorate([
122
+ (0, swagger_1.ApiPropertyOptional)({ description: 'Minimal non-PII context' }),
123
+ (0, class_validator_1.IsOptional)(),
124
+ (0, class_validator_1.IsObject)(),
125
+ __metadata("design:type", Object)
126
+ ], CreateTenantAuditLogDto.prototype, "metadata", void 0);
127
+ __decorate([
128
+ (0, swagger_1.ApiProperty)({ description: 'When the action completed (UTC ISO-8601)' }),
129
+ (0, class_validator_1.IsISO8601)(),
130
+ __metadata("design:type", String)
131
+ ], CreateTenantAuditLogDto.prototype, "occurredAt", void 0);
@@ -1,6 +1,8 @@
1
1
  export * from './event-analytic.enum';
2
2
  export * from './followup-analytic-event-type.util';
3
3
  export * from './create-analytic-event.dto';
4
+ export * from './create-tenant-audit-log.dto';
5
+ export * from './list-tenant-audit-logs-query.dto';
4
6
  export * from './analytics-query.dto';
5
7
  export * from './analytics-kafka-topics';
6
8
  export * from './analytics-emitter.helper';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/analytics/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,qCAAqC,CAAC;AACpD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/analytics/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,qCAAqC,CAAC;AACpD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,oCAAoC,CAAC;AACnD,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC"}
@@ -17,6 +17,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./event-analytic.enum"), exports);
18
18
  __exportStar(require("./followup-analytic-event-type.util"), exports);
19
19
  __exportStar(require("./create-analytic-event.dto"), exports);
20
+ __exportStar(require("./create-tenant-audit-log.dto"), exports);
21
+ __exportStar(require("./list-tenant-audit-logs-query.dto"), exports);
20
22
  __exportStar(require("./analytics-query.dto"), exports);
21
23
  __exportStar(require("./analytics-kafka-topics"), exports);
22
24
  __exportStar(require("./analytics-emitter.helper"), exports);
@@ -0,0 +1,12 @@
1
+ import type { ParsedFilterDto } from '../filters/parsed-filter.dto';
2
+ /**
3
+ * Allowed tenant_audit_logs columns for filtering (camelCase API keys).
4
+ */
5
+ export declare const TENANT_AUDIT_LIST_FILTER_KEYS: readonly ["actorId", "action", "outcome", "httpMethod", "httpPath", "httpStatus", "occurredAt"];
6
+ export type TenantAuditListFilterKeyDto = (typeof TENANT_AUDIT_LIST_FILTER_KEYS)[number];
7
+ export declare class ListTenantAuditLogsQueryDto {
8
+ page?: number;
9
+ limit?: number;
10
+ filters?: ParsedFilterDto[];
11
+ }
12
+ //# sourceMappingURL=list-tenant-audit-logs-query.dto.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-tenant-audit-logs-query.dto.d.ts","sourceRoot":"","sources":["../../src/analytics/list-tenant-audit-logs-query.dto.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAEpE;;GAEG;AACH,eAAO,MAAM,6BAA6B,iGAQhC,CAAC;AACX,MAAM,MAAM,2BAA2B,GAAG,CAAC,OAAO,6BAA6B,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzF,qBAAa,2BAA2B;IAMtC,IAAI,CAAC,EAAE,MAAM,CAAC;IAQd,KAAK,CAAC,EAAE,MAAM,CAAC;IAUf,OAAO,CAAC,EAAE,eAAe,EAAE,CAAC;CAC7B"}
@@ -0,0 +1,60 @@
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.ListTenantAuditLogsQueryDto = exports.TENANT_AUDIT_LIST_FILTER_KEYS = void 0;
13
+ const swagger_1 = require("@nestjs/swagger");
14
+ const class_transformer_1 = require("class-transformer");
15
+ const class_validator_1 = require("class-validator");
16
+ /**
17
+ * Allowed tenant_audit_logs columns for filtering (camelCase API keys).
18
+ */
19
+ exports.TENANT_AUDIT_LIST_FILTER_KEYS = [
20
+ 'actorId',
21
+ 'action',
22
+ 'outcome',
23
+ 'httpMethod',
24
+ 'httpPath',
25
+ 'httpStatus',
26
+ 'occurredAt',
27
+ ];
28
+ class ListTenantAuditLogsQueryDto {
29
+ page;
30
+ limit;
31
+ filters;
32
+ }
33
+ exports.ListTenantAuditLogsQueryDto = ListTenantAuditLogsQueryDto;
34
+ __decorate([
35
+ (0, swagger_1.ApiPropertyOptional)({ example: 1, minimum: 1, default: 1 }),
36
+ (0, class_validator_1.IsOptional)(),
37
+ (0, class_transformer_1.Type)(() => Number),
38
+ (0, class_validator_1.IsInt)(),
39
+ (0, class_validator_1.Min)(1),
40
+ __metadata("design:type", Number)
41
+ ], ListTenantAuditLogsQueryDto.prototype, "page", void 0);
42
+ __decorate([
43
+ (0, swagger_1.ApiPropertyOptional)({ example: 20, minimum: 1, maximum: 100, default: 20 }),
44
+ (0, class_validator_1.IsOptional)(),
45
+ (0, class_transformer_1.Type)(() => Number),
46
+ (0, class_validator_1.IsInt)(),
47
+ (0, class_validator_1.Min)(1),
48
+ (0, class_validator_1.Max)(100),
49
+ __metadata("design:type", Number)
50
+ ], ListTenantAuditLogsQueryDto.prototype, "limit", void 0);
51
+ __decorate([
52
+ (0, swagger_1.ApiPropertyOptional)({
53
+ description: 'JSON string with an array of filter objects `{ key, op, value }`. ' +
54
+ 'Example: `[{"key":"outcome","op":"==","value":"success"}]`.',
55
+ type: String,
56
+ example: '[{"key":"outcome","op":"==","value":"success"}]',
57
+ }),
58
+ (0, class_validator_1.IsOptional)(),
59
+ __metadata("design:type", Array)
60
+ ], ListTenantAuditLogsQueryDto.prototype, "filters", void 0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tychat-contracts",
3
- "version": "1.4.4",
3
+ "version": "1.4.6",
4
4
  "description": "DTOs compartilhados com class-validator (API e microserviços)",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -16,3 +16,9 @@ export const TOPIC_ANALYTICS_TIMESERIES = 'analytics.timeseries';
16
16
 
17
17
  /** Topic for requesting event details with pagination. */
18
18
  export const TOPIC_ANALYTICS_EVENTS_LIST = 'analytics.events.list';
19
+
20
+ /** Topic for ingesting tenant user-action audit rows (dedicated table). */
21
+ export const TOPIC_ANALYTICS_TENANT_AUDIT_INGEST = 'analytics.tenant_audit.ingest';
22
+
23
+ /** Topic for paginated tenant audit log listing. */
24
+ export const TOPIC_ANALYTICS_TENANT_AUDIT_LIST = 'analytics.tenant_audit.list';
@@ -0,0 +1,105 @@
1
+ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
2
+ import {
3
+ IsIn,
4
+ IsInt,
5
+ IsISO8601,
6
+ IsNotEmpty,
7
+ IsObject,
8
+ IsOptional,
9
+ IsString,
10
+ IsUUID,
11
+ Max,
12
+ MaxLength,
13
+ Min,
14
+ MinLength,
15
+ } from 'class-validator';
16
+
17
+ /** Outcome of the audited HTTP mutation. */
18
+ export const TENANT_AUDIT_OUTCOMES = ['success', 'failure'] as const;
19
+ export type TenantAuditOutcome = (typeof TENANT_AUDIT_OUTCOMES)[number];
20
+
21
+ /**
22
+ * Payload for persisting a tenant-scoped user action audit row (HTTP mutation).
23
+ */
24
+ export class CreateTenantAuditLogDto {
25
+ @ApiProperty({ description: 'Unique id for idempotent insert', format: 'uuid' })
26
+ @IsUUID()
27
+ eventId: string;
28
+
29
+ @ApiProperty({ description: 'Tenant slug' })
30
+ @IsString()
31
+ @IsNotEmpty()
32
+ @MinLength(1)
33
+ @MaxLength(255)
34
+ tenant: string;
35
+
36
+ @ApiProperty({ example: 'user', description: 'Actor category' })
37
+ @IsString()
38
+ @IsNotEmpty()
39
+ @MaxLength(64)
40
+ actorType: string;
41
+
42
+ @ApiProperty({ description: 'Authenticated user id (sub)' })
43
+ @IsString()
44
+ @IsNotEmpty()
45
+ @MaxLength(255)
46
+ actorId: string;
47
+
48
+ @ApiProperty({
49
+ example: 'http.mutation',
50
+ description: 'Deterministic action key (e.g. http.mutation)',
51
+ })
52
+ @IsString()
53
+ @IsNotEmpty()
54
+ @MaxLength(128)
55
+ action: string;
56
+
57
+ @ApiPropertyOptional({ example: 'http' })
58
+ @IsOptional()
59
+ @IsString()
60
+ @MaxLength(64)
61
+ targetType?: string | null;
62
+
63
+ @ApiPropertyOptional({ description: 'Route param id when present' })
64
+ @IsOptional()
65
+ @IsString()
66
+ @MaxLength(255)
67
+ targetId?: string | null;
68
+
69
+ @ApiProperty({ enum: TENANT_AUDIT_OUTCOMES })
70
+ @IsIn([...TENANT_AUDIT_OUTCOMES])
71
+ outcome: TenantAuditOutcome;
72
+
73
+ @ApiProperty({ example: 'POST' })
74
+ @IsString()
75
+ @IsNotEmpty()
76
+ @MaxLength(16)
77
+ httpMethod: string;
78
+
79
+ @ApiProperty({ example: '/patients/123' })
80
+ @IsString()
81
+ @IsNotEmpty()
82
+ @MaxLength(2048)
83
+ httpPath: string;
84
+
85
+ @ApiProperty({ example: 201 })
86
+ @IsInt()
87
+ @Min(100)
88
+ @Max(599)
89
+ httpStatus: number;
90
+
91
+ @ApiPropertyOptional({ description: 'On failure, stable error code if available' })
92
+ @IsOptional()
93
+ @IsString()
94
+ @MaxLength(128)
95
+ errorCode?: string | null;
96
+
97
+ @ApiPropertyOptional({ description: 'Minimal non-PII context' })
98
+ @IsOptional()
99
+ @IsObject()
100
+ metadata?: Record<string, unknown> | null;
101
+
102
+ @ApiProperty({ description: 'When the action completed (UTC ISO-8601)' })
103
+ @IsISO8601()
104
+ occurredAt: string;
105
+ }
@@ -1,6 +1,8 @@
1
1
  export * from './event-analytic.enum';
2
2
  export * from './followup-analytic-event-type.util';
3
3
  export * from './create-analytic-event.dto';
4
+ export * from './create-tenant-audit-log.dto';
5
+ export * from './list-tenant-audit-logs-query.dto';
4
6
  export * from './analytics-query.dto';
5
7
  export * from './analytics-kafka-topics';
6
8
  export * from './analytics-emitter.helper';
@@ -0,0 +1,45 @@
1
+ import { ApiPropertyOptional } from '@nestjs/swagger';
2
+ import { Type } from 'class-transformer';
3
+ import { IsInt, IsOptional, Max, Min } from 'class-validator';
4
+ import type { ParsedFilterDto } from '../filters/parsed-filter.dto';
5
+
6
+ /**
7
+ * Allowed tenant_audit_logs columns for filtering (camelCase API keys).
8
+ */
9
+ export const TENANT_AUDIT_LIST_FILTER_KEYS = [
10
+ 'actorId',
11
+ 'action',
12
+ 'outcome',
13
+ 'httpMethod',
14
+ 'httpPath',
15
+ 'httpStatus',
16
+ 'occurredAt',
17
+ ] as const;
18
+ export type TenantAuditListFilterKeyDto = (typeof TENANT_AUDIT_LIST_FILTER_KEYS)[number];
19
+
20
+ export class ListTenantAuditLogsQueryDto {
21
+ @ApiPropertyOptional({ example: 1, minimum: 1, default: 1 })
22
+ @IsOptional()
23
+ @Type(() => Number)
24
+ @IsInt()
25
+ @Min(1)
26
+ page?: number;
27
+
28
+ @ApiPropertyOptional({ example: 20, minimum: 1, maximum: 100, default: 20 })
29
+ @IsOptional()
30
+ @Type(() => Number)
31
+ @IsInt()
32
+ @Min(1)
33
+ @Max(100)
34
+ limit?: number;
35
+
36
+ @ApiPropertyOptional({
37
+ description:
38
+ 'JSON string with an array of filter objects `{ key, op, value }`. ' +
39
+ 'Example: `[{"key":"outcome","op":"==","value":"success"}]`.',
40
+ type: String,
41
+ example: '[{"key":"outcome","op":"==","value":"success"}]',
42
+ })
43
+ @IsOptional()
44
+ filters?: ParsedFilterDto[];
45
+ }