rl-core-api 0.15.6 → 0.16.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.
Files changed (65) hide show
  1. package/dist/core/bootstrap/bootstrapCore.util.js +1 -1
  2. package/dist/core/config/env.schema.d.ts +7 -0
  3. package/dist/core/config/env.schema.js +4 -0
  4. package/dist/core/database/migrations/1789171200000-NotificationDismissal.d.ts +6 -0
  5. package/dist/core/database/migrations/1789171200000-NotificationDismissal.js +16 -0
  6. package/dist/core/database/migrations/index.js +2 -0
  7. package/dist/core/images/imageCleanup.service.d.ts +9 -0
  8. package/dist/core/images/imageCleanup.service.js +50 -0
  9. package/dist/core/images/imageFetch.service.d.ts +11 -0
  10. package/dist/core/images/imageFetch.service.js +190 -0
  11. package/dist/core/images/imageProcessing.service.d.ts +9 -0
  12. package/dist/core/images/imageProcessing.service.js +74 -0
  13. package/dist/core/images/imageProfile.interface.d.ts +40 -0
  14. package/dist/core/images/imageProfile.interface.js +29 -0
  15. package/dist/core/images/imageProfile.registry.d.ts +14 -0
  16. package/dist/core/images/imageProfile.registry.js +61 -0
  17. package/dist/core/images/imageStorage.service.d.ts +28 -0
  18. package/dist/core/images/imageStorage.service.js +151 -0
  19. package/dist/core/images/images.controller.d.ts +16 -0
  20. package/dist/core/images/images.controller.js +142 -0
  21. package/dist/core/images/images.module.d.ts +2 -0
  22. package/dist/core/images/images.module.js +35 -0
  23. package/dist/core/images/images.processor.d.ts +18 -0
  24. package/dist/core/images/images.processor.js +75 -0
  25. package/dist/core/images/images.service.d.ts +34 -0
  26. package/dist/core/images/images.service.js +137 -0
  27. package/dist/core/images/imagesApi.module.d.ts +2 -0
  28. package/dist/core/images/imagesApi.module.js +25 -0
  29. package/dist/core/images/imagesWorker.module.d.ts +2 -0
  30. package/dist/core/images/imagesWorker.module.js +22 -0
  31. package/dist/core/images/responses/imageAccepted.response.d.ts +4 -0
  32. package/dist/core/images/responses/imageAccepted.response.js +28 -0
  33. package/dist/core/images/responses/imageProfile.response.d.ts +10 -0
  34. package/dist/core/images/responses/imageProfile.response.js +55 -0
  35. package/dist/core/images/responses/imageStatus.response.d.ts +7 -0
  36. package/dist/core/images/responses/imageStatus.response.js +36 -0
  37. package/dist/core/images/uploadImageFromUrl.command.d.ts +3 -0
  38. package/dist/core/images/uploadImageFromUrl.command.js +23 -0
  39. package/dist/core/queue/job.producer.d.ts +1 -1
  40. package/dist/core/queue/job.producer.js +7 -1
  41. package/dist/core/queue/queue.constants.d.ts +4 -1
  42. package/dist/core/queue/queue.constants.js +5 -2
  43. package/dist/features/notifications/domain/notifications.service.d.ts +2 -0
  44. package/dist/features/notifications/domain/notifications.service.js +12 -0
  45. package/dist/features/notifications/infra/repositories/notification.repository.d.ts +1 -0
  46. package/dist/features/notifications/infra/repositories/notification.repository.js +8 -0
  47. package/dist/features/notifications/infra/repositories/notificationRead.repository.d.ts +1 -0
  48. package/dist/features/notifications/infra/repositories/notificationRead.repository.js +17 -0
  49. package/dist/features/notifications/infra/schema/notificationRead.schema.d.ts +1 -0
  50. package/dist/features/notifications/infra/schema/notificationRead.schema.js +4 -0
  51. package/dist/features/notifications/presentation/notifications.controller.d.ts +2 -0
  52. package/dist/features/notifications/presentation/notifications.controller.js +23 -0
  53. package/dist/features/users/domain/avatarImage.profile.d.ts +13 -0
  54. package/dist/features/users/domain/avatarImage.profile.js +30 -0
  55. package/dist/features/users/domain/index.d.ts +1 -1
  56. package/dist/features/users/domain/index.js +1 -1
  57. package/dist/features/users/presentation/users.controller.d.ts +6 -5
  58. package/dist/features/users/presentation/users.controller.js +28 -15
  59. package/dist/features/users/users.module.js +3 -1
  60. package/dist/index.d.ts +17 -1
  61. package/dist/index.js +43 -2
  62. package/dist/rlCore.module.js +5 -1
  63. package/package.json +1 -1
  64. package/dist/features/users/domain/avatar.service.d.ts +0 -5
  65. package/dist/features/users/domain/avatar.service.js +0 -61
@@ -0,0 +1,22 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.ImagesWorkerModule = void 0;
10
+ const common_1 = require("@nestjs/common");
11
+ const images_module_1 = require("./images.module");
12
+ const images_processor_1 = require("./images.processor");
13
+ const queue_constants_1 = require("../queue/queue.constants");
14
+ let ImagesWorkerModule = class ImagesWorkerModule {
15
+ };
16
+ exports.ImagesWorkerModule = ImagesWorkerModule;
17
+ exports.ImagesWorkerModule = ImagesWorkerModule = __decorate([
18
+ (0, common_1.Module)({
19
+ imports: [images_module_1.ImagesModule],
20
+ providers: (0, queue_constants_1.isRedisConfigured)() ? [images_processor_1.ImagesProcessor] : [],
21
+ })
22
+ ], ImagesWorkerModule);
@@ -0,0 +1,4 @@
1
+ export declare class ImageAcceptedResponse {
2
+ jobId: string | null;
3
+ path: string | null;
4
+ }
@@ -0,0 +1,28 @@
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.ImageAcceptedResponse = void 0;
13
+ const swagger_1 = require("@nestjs/swagger");
14
+ class ImageAcceptedResponse {
15
+ }
16
+ exports.ImageAcceptedResponse = ImageAcceptedResponse;
17
+ __decorate([
18
+ (0, swagger_1.ApiProperty)({ type: String, nullable: true, example: "312" }),
19
+ __metadata("design:type", Object)
20
+ ], ImageAcceptedResponse.prototype, "jobId", void 0);
21
+ __decorate([
22
+ (0, swagger_1.ApiProperty)({
23
+ type: String,
24
+ nullable: true,
25
+ example: "/uploads/funkos/pending/6f1b2c34-9d55-4a1e-8f0a-2b7c1d9e4f10.webp",
26
+ }),
27
+ __metadata("design:type", Object)
28
+ ], ImageAcceptedResponse.prototype, "path", void 0);
@@ -0,0 +1,10 @@
1
+ import { ImageFit, ImageFormat } from "../imageProfile.interface";
2
+ export declare class ImageProfileResponse {
3
+ name: string;
4
+ width: number;
5
+ height: number | null;
6
+ fit: ImageFit;
7
+ format: ImageFormat;
8
+ maxBytes: number;
9
+ accept: string[];
10
+ }
@@ -0,0 +1,55 @@
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.ImageProfileResponse = void 0;
13
+ const swagger_1 = require("@nestjs/swagger");
14
+ const imageProfile_interface_1 = require("../imageProfile.interface");
15
+ class ImageProfileResponse {
16
+ }
17
+ exports.ImageProfileResponse = ImageProfileResponse;
18
+ __decorate([
19
+ (0, swagger_1.ApiProperty)({ example: "funkos" }),
20
+ __metadata("design:type", String)
21
+ ], ImageProfileResponse.prototype, "name", void 0);
22
+ __decorate([
23
+ (0, swagger_1.ApiProperty)({ example: 400, description: "Lado máximo em pixels — é teto, não obrigação" }),
24
+ __metadata("design:type", Number)
25
+ ], ImageProfileResponse.prototype, "width", void 0);
26
+ __decorate([
27
+ (0, swagger_1.ApiPropertyOptional)({
28
+ type: Number,
29
+ nullable: true,
30
+ example: 400,
31
+ description: "Lado da caixa em que a imagem cabe. Com `inside`, a altura final segue a proporção da foto — este número é o teto, e é o que a tela usa para desenhar a área de envio",
32
+ }),
33
+ __metadata("design:type", Object)
34
+ ], ImageProfileResponse.prototype, "height", void 0);
35
+ __decorate([
36
+ (0, swagger_1.ApiProperty)({ enum: imageProfile_interface_1.ImageFit, example: imageProfile_interface_1.ImageFit.CONTAIN }),
37
+ __metadata("design:type", String)
38
+ ], ImageProfileResponse.prototype, "fit", void 0);
39
+ __decorate([
40
+ (0, swagger_1.ApiProperty)({ enum: imageProfile_interface_1.ImageFormat, example: imageProfile_interface_1.ImageFormat.WEBP }),
41
+ __metadata("design:type", String)
42
+ ], ImageProfileResponse.prototype, "format", void 0);
43
+ __decorate([
44
+ (0, swagger_1.ApiProperty)({ example: 8388608 }),
45
+ __metadata("design:type", Number)
46
+ ], ImageProfileResponse.prototype, "maxBytes", void 0);
47
+ __decorate([
48
+ (0, swagger_1.ApiProperty)({
49
+ type: String,
50
+ isArray: true,
51
+ example: ["image/jpeg", "image/png", "image/webp"],
52
+ description: "Vira o `accept` do seletor de arquivo",
53
+ }),
54
+ __metadata("design:type", Array)
55
+ ], ImageProfileResponse.prototype, "accept", void 0);
@@ -0,0 +1,7 @@
1
+ import { JobState } from "../../queue/job.interface";
2
+ export declare class ImageStatusResponse {
3
+ state: JobState;
4
+ percent: number;
5
+ path: string | null;
6
+ errorMessage: string | null;
7
+ }
@@ -0,0 +1,36 @@
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.ImageStatusResponse = void 0;
13
+ const swagger_1 = require("@nestjs/swagger");
14
+ class ImageStatusResponse {
15
+ }
16
+ exports.ImageStatusResponse = ImageStatusResponse;
17
+ __decorate([
18
+ (0, swagger_1.ApiProperty)({ example: "completed" }),
19
+ __metadata("design:type", String)
20
+ ], ImageStatusResponse.prototype, "state", void 0);
21
+ __decorate([
22
+ (0, swagger_1.ApiProperty)({ example: 100 }),
23
+ __metadata("design:type", Number)
24
+ ], ImageStatusResponse.prototype, "percent", void 0);
25
+ __decorate([
26
+ (0, swagger_1.ApiProperty)({
27
+ type: String,
28
+ nullable: true,
29
+ example: "/uploads/funkos/pending/6f1b2c34-9d55-4a1e-8f0a-2b7c1d9e4f10.webp",
30
+ }),
31
+ __metadata("design:type", Object)
32
+ ], ImageStatusResponse.prototype, "path", void 0);
33
+ __decorate([
34
+ (0, swagger_1.ApiProperty)({ type: String, nullable: true }),
35
+ __metadata("design:type", Object)
36
+ ], ImageStatusResponse.prototype, "errorMessage", void 0);
@@ -0,0 +1,3 @@
1
+ export declare class UploadImageFromUrlCommand {
2
+ url: string;
3
+ }
@@ -0,0 +1,23 @@
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.UploadImageFromUrlCommand = void 0;
13
+ const swagger_1 = require("@nestjs/swagger");
14
+ const class_validator_1 = require("class-validator");
15
+ class UploadImageFromUrlCommand {
16
+ }
17
+ exports.UploadImageFromUrlCommand = UploadImageFromUrlCommand;
18
+ __decorate([
19
+ (0, swagger_1.ApiProperty)({ example: "https://exemplo.com/ron.png" }),
20
+ (0, class_validator_1.IsUrl)({ protocols: ["http", "https"], require_protocol: true }),
21
+ (0, class_validator_1.MaxLength)(2048),
22
+ __metadata("design:type", String)
23
+ ], UploadImageFromUrlCommand.prototype, "url", void 0);
@@ -5,7 +5,7 @@ import { QueueName } from "./queue.constants";
5
5
  export declare class JobProducer implements OnApplicationBootstrap {
6
6
  private readonly logger;
7
7
  private readonly queues;
8
- constructor(jobs?: Queue<JobEnvelope>, exports?: Queue<JobEnvelope>);
8
+ constructor(jobs?: Queue<JobEnvelope>, exports?: Queue<JobEnvelope>, images?: Queue<JobEnvelope>);
9
9
  onApplicationBootstrap(): Promise<void>;
10
10
  isAvailable(queue?: QueueName): boolean;
11
11
  enqueue<TPayload>(name: string, payload: TPayload, options: EnqueueOptions): Promise<JobRef>;
@@ -28,7 +28,7 @@ const ENQUEUE_TIMEOUT_MS = 5000;
28
28
  const HEALTH_TIMEOUT_MS = 1500;
29
29
  const isStoredProgress = (value) => typeof value === "object" && value !== null && "percent" in value;
30
30
  let JobProducer = JobProducer_1 = class JobProducer {
31
- constructor(jobs, exports) {
31
+ constructor(jobs, exports, images) {
32
32
  this.logger = new common_1.Logger(JobProducer_1.name);
33
33
  this.queues = new Map();
34
34
  if (jobs) {
@@ -37,6 +37,9 @@ let JobProducer = JobProducer_1 = class JobProducer {
37
37
  if (exports) {
38
38
  this.queues.set(queue_constants_1.EXPORTS_QUEUE, exports);
39
39
  }
40
+ if (images) {
41
+ this.queues.set(queue_constants_1.IMAGES_QUEUE, images);
42
+ }
40
43
  }
41
44
  async onApplicationBootstrap() {
42
45
  if (!(0, queue_constants_1.isRedisConfigured)()) {
@@ -144,6 +147,9 @@ exports.JobProducer = JobProducer = JobProducer_1 = __decorate([
144
147
  __param(0, (0, bullmq_1.InjectQueue)(queue_constants_1.JOBS_QUEUE)),
145
148
  __param(1, (0, common_1.Optional)()),
146
149
  __param(1, (0, bullmq_1.InjectQueue)(queue_constants_1.EXPORTS_QUEUE)),
150
+ __param(2, (0, common_1.Optional)()),
151
+ __param(2, (0, bullmq_1.InjectQueue)(queue_constants_1.IMAGES_QUEUE)),
147
152
  __metadata("design:paramtypes", [bullmq_2.Queue,
153
+ bullmq_2.Queue,
148
154
  bullmq_2.Queue])
149
155
  ], JobProducer);
@@ -1,6 +1,7 @@
1
1
  export declare const JOBS_QUEUE = "jobs";
2
2
  export declare const EXPORTS_QUEUE = "exports";
3
- export declare const QUEUE_NAMES: readonly ["jobs", "exports"];
3
+ export declare const IMAGES_QUEUE = "images";
4
+ export declare const QUEUE_NAMES: readonly ["jobs", "exports", "images"];
4
5
  export type QueueName = (typeof QUEUE_NAMES)[number];
5
6
  export declare const isQueueName: (value: unknown) => value is QueueName;
6
7
  export declare const JOB_PROGRESS_EVENT = "job:progress";
@@ -10,6 +11,8 @@ export declare const JOB_CONCURRENCY = 5;
10
11
  export declare const JOB_ATTEMPTS = 3;
11
12
  export declare const EXPORT_CONCURRENCY = 2;
12
13
  export declare const EXPORT_LOCK_DURATION_MS = 300000;
14
+ export declare const IMAGE_CONCURRENCY = 4;
15
+ export declare const IMAGE_LOCK_DURATION_MS = 60000;
13
16
  export declare const JOB_BACKOFF_DELAY_MS = 30000;
14
17
  export declare const COMPLETED_RETENTION_SECONDS = 86400;
15
18
  export declare const FAILED_RETENTION_SECONDS = 14400;
@@ -1,11 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.queuePrefix = exports.isRedisConfigured = exports.FAILED_RETENTION_SECONDS = exports.COMPLETED_RETENTION_SECONDS = exports.JOB_BACKOFF_DELAY_MS = exports.EXPORT_LOCK_DURATION_MS = exports.EXPORT_CONCURRENCY = exports.JOB_ATTEMPTS = exports.JOB_CONCURRENCY = exports.JOB_FAILED_EVENT = exports.JOB_COMPLETED_EVENT = exports.JOB_PROGRESS_EVENT = exports.isQueueName = exports.QUEUE_NAMES = exports.EXPORTS_QUEUE = exports.JOBS_QUEUE = void 0;
3
+ exports.queuePrefix = exports.isRedisConfigured = exports.FAILED_RETENTION_SECONDS = exports.COMPLETED_RETENTION_SECONDS = exports.JOB_BACKOFF_DELAY_MS = exports.IMAGE_LOCK_DURATION_MS = exports.IMAGE_CONCURRENCY = exports.EXPORT_LOCK_DURATION_MS = exports.EXPORT_CONCURRENCY = exports.JOB_ATTEMPTS = exports.JOB_CONCURRENCY = exports.JOB_FAILED_EVENT = exports.JOB_COMPLETED_EVENT = exports.JOB_PROGRESS_EVENT = exports.isQueueName = exports.QUEUE_NAMES = exports.IMAGES_QUEUE = exports.EXPORTS_QUEUE = exports.JOBS_QUEUE = void 0;
4
4
  const dotenv_1 = require("dotenv");
5
5
  (0, dotenv_1.config)();
6
6
  exports.JOBS_QUEUE = "jobs";
7
7
  exports.EXPORTS_QUEUE = "exports";
8
- exports.QUEUE_NAMES = [exports.JOBS_QUEUE, exports.EXPORTS_QUEUE];
8
+ exports.IMAGES_QUEUE = "images";
9
+ exports.QUEUE_NAMES = [exports.JOBS_QUEUE, exports.EXPORTS_QUEUE, exports.IMAGES_QUEUE];
9
10
  const isQueueName = (value) => exports.QUEUE_NAMES.includes(value);
10
11
  exports.isQueueName = isQueueName;
11
12
  exports.JOB_PROGRESS_EVENT = "job:progress";
@@ -15,6 +16,8 @@ exports.JOB_CONCURRENCY = 5;
15
16
  exports.JOB_ATTEMPTS = 3;
16
17
  exports.EXPORT_CONCURRENCY = 2;
17
18
  exports.EXPORT_LOCK_DURATION_MS = 300_000;
19
+ exports.IMAGE_CONCURRENCY = 4;
20
+ exports.IMAGE_LOCK_DURATION_MS = 60_000;
18
21
  exports.JOB_BACKOFF_DELAY_MS = 30_000;
19
22
  exports.COMPLETED_RETENTION_SECONDS = 86_400;
20
23
  exports.FAILED_RETENTION_SECONDS = 14_400;
@@ -38,6 +38,8 @@ export declare class NotificationsService {
38
38
  findByUser(user: AuthenticatedUser, query: PaginationQuery): Promise<Paginated<UserNotification>>;
39
39
  countUnread(user: AuthenticatedUser): Promise<number>;
40
40
  markAsRead(id: string, user: AuthenticatedUser): Promise<void>;
41
+ dismiss(id: string, user: AuthenticatedUser): Promise<void>;
42
+ dismissRead(user: AuthenticatedUser): Promise<number>;
41
43
  markAllAsRead(user: AuthenticatedUser): Promise<void>;
42
44
  cleanupOldRead(retentionDays: number): Promise<number>;
43
45
  private visibilityOf;
@@ -60,6 +60,18 @@ let NotificationsService = class NotificationsService {
60
60
  }
61
61
  await this.reads.markAsRead([id], user.id);
62
62
  }
63
+ async dismiss(id, user) {
64
+ const visible = await this.notifications.isVisibleToUser(id, this.visibilityOf(user));
65
+ if (!visible) {
66
+ throw new common_1.NotFoundException("Notificação não encontrada");
67
+ }
68
+ await this.reads.dismiss([id], user.id);
69
+ }
70
+ async dismissRead(user) {
71
+ const ids = await this.notifications.findReadIds(this.visibilityOf(user));
72
+ await this.reads.dismiss(ids, user.id);
73
+ return ids.length;
74
+ }
63
75
  async markAllAsRead(user) {
64
76
  const ids = await this.notifications.findUnreadIds(this.visibilityOf(user));
65
77
  await this.reads.markAsRead(ids, user.id);
@@ -9,6 +9,7 @@ export declare class NotificationRepository extends Repository<Notification> {
9
9
  findVisibleToUser(params: VisibilityParams, page: number, limit: number): Promise<[Notification[], number]>;
10
10
  countUnread(params: VisibilityParams): Promise<number>;
11
11
  isVisibleToUser(notificationId: string, params: VisibilityParams): Promise<boolean>;
12
+ findReadIds(params: VisibilityParams): Promise<string[]>;
12
13
  findUnreadIds(params: VisibilityParams): Promise<string[]>;
13
14
  softDeleteReadByAllBefore(limit: Date): Promise<number>;
14
15
  private baseVisibleQuery;
@@ -69,6 +69,13 @@ let NotificationRepository = class NotificationRepository extends typeorm_1.Repo
69
69
  .getCount();
70
70
  return count > 0;
71
71
  }
72
+ async findReadIds(params) {
73
+ const rows = await this.baseVisibleQuery(params)
74
+ .andWhere("read.notification_id IS NOT NULL")
75
+ .select("notification.id", "id")
76
+ .getRawMany();
77
+ return rows.map((row) => row.id);
78
+ }
72
79
  async findUnreadIds(params) {
73
80
  const rows = await this.baseVisibleQuery(params)
74
81
  .andWhere("read.notification_id IS NULL")
@@ -92,6 +99,7 @@ let NotificationRepository = class NotificationRepository extends typeorm_1.Repo
92
99
  return this.createQueryBuilder("notification")
93
100
  .leftJoinAndSelect("notification.reads", "read", "read.user_id = :userId", { userId: params.userId })
94
101
  .where("notification.deletedAt IS NULL")
102
+ .andWhere("read.dismissed_at IS NULL")
95
103
  .andWhere(VISIBLE_TO_USER, {
96
104
  userScope: scopeType_enum_1.ScopeType.USER,
97
105
  roleScope: scopeType_enum_1.ScopeType.ROLE,
@@ -3,4 +3,5 @@ import { NotificationRead } from "../schema/notificationRead.schema";
3
3
  export declare class NotificationReadRepository extends Repository<NotificationRead> {
4
4
  constructor(dataSource: DataSource);
5
5
  markAsRead(notificationIds: string[], userId: string): Promise<void>;
6
+ dismiss(notificationIds: string[], userId: string): Promise<void>;
6
7
  }
@@ -32,6 +32,23 @@ let NotificationReadRepository = class NotificationReadRepository extends typeor
32
32
  .orIgnore()
33
33
  .execute();
34
34
  }
35
+ async dismiss(notificationIds, userId) {
36
+ if (notificationIds.length === 0) {
37
+ return;
38
+ }
39
+ const agora = new Date();
40
+ await this.createQueryBuilder()
41
+ .insert()
42
+ .into(notificationRead_schema_1.NotificationRead)
43
+ .values(notificationIds.map((notificationId) => ({
44
+ notificationId,
45
+ userId,
46
+ readAt: agora,
47
+ dismissedAt: agora,
48
+ })))
49
+ .orUpdate(["dismissed_at"], ["notification_id", "user_id"])
50
+ .execute();
51
+ }
35
52
  };
36
53
  exports.NotificationReadRepository = NotificationReadRepository;
37
54
  exports.NotificationReadRepository = NotificationReadRepository = __decorate([
@@ -3,5 +3,6 @@ export declare class NotificationRead {
3
3
  notificationId: string;
4
4
  userId: string;
5
5
  readAt: Date;
6
+ dismissedAt: Date | null;
6
7
  notification: Notification;
7
8
  }
@@ -27,6 +27,10 @@ __decorate([
27
27
  (0, typeorm_1.Column)({ name: "read_at", type: "datetime" }),
28
28
  __metadata("design:type", Date)
29
29
  ], NotificationRead.prototype, "readAt", void 0);
30
+ __decorate([
31
+ (0, typeorm_1.Column)({ name: "dismissed_at", type: "datetime", nullable: true }),
32
+ __metadata("design:type", Object)
33
+ ], NotificationRead.prototype, "dismissedAt", void 0);
30
34
  __decorate([
31
35
  (0, typeorm_1.ManyToOne)(() => notification_schema_1.Notification, { onDelete: "CASCADE" }),
32
36
  (0, typeorm_1.JoinColumn)({ name: "notification_id" }),
@@ -12,5 +12,7 @@ export declare class NotificationsController {
12
12
  unreadCount(user: AuthenticatedUser): Promise<UnreadCountResponse>;
13
13
  markAllAsRead(user: AuthenticatedUser): Promise<void>;
14
14
  markAsRead(id: string, user: AuthenticatedUser): Promise<void>;
15
+ dismissRead(user: AuthenticatedUser): Promise<void>;
16
+ dismiss(id: string, user: AuthenticatedUser): Promise<void>;
15
17
  create(command: CreateNotificationCommand, user: AuthenticatedUser): Promise<UserNotificationResponse>;
16
18
  }
@@ -40,6 +40,12 @@ let NotificationsController = class NotificationsController {
40
40
  async markAsRead(id, user) {
41
41
  await this.notifications.markAsRead(id, user);
42
42
  }
43
+ async dismissRead(user) {
44
+ await this.notifications.dismissRead(user);
45
+ }
46
+ async dismiss(id, user) {
47
+ await this.notifications.dismiss(id, user);
48
+ }
43
49
  create(command, user) {
44
50
  return this.publisher.publishAndPush({
45
51
  title: command.title,
@@ -88,6 +94,23 @@ __decorate([
88
94
  __metadata("design:paramtypes", [String, Object]),
89
95
  __metadata("design:returntype", Promise)
90
96
  ], NotificationsController.prototype, "markAsRead", null);
97
+ __decorate([
98
+ (0, common_1.Delete)("read"),
99
+ (0, common_1.HttpCode)(common_1.HttpStatus.NO_CONTENT),
100
+ __param(0, (0, currentUser_decorator_1.CurrentUser)()),
101
+ __metadata("design:type", Function),
102
+ __metadata("design:paramtypes", [Object]),
103
+ __metadata("design:returntype", Promise)
104
+ ], NotificationsController.prototype, "dismissRead", null);
105
+ __decorate([
106
+ (0, common_1.Delete)(":id"),
107
+ (0, common_1.HttpCode)(common_1.HttpStatus.NO_CONTENT),
108
+ __param(0, (0, common_1.Param)("id", common_1.ParseUUIDPipe)),
109
+ __param(1, (0, currentUser_decorator_1.CurrentUser)()),
110
+ __metadata("design:type", Function),
111
+ __metadata("design:paramtypes", [String, Object]),
112
+ __metadata("design:returntype", Promise)
113
+ ], NotificationsController.prototype, "dismiss", null);
91
114
  __decorate([
92
115
  (0, common_1.Post)(),
93
116
  (0, requirePermission_decorator_1.RequirePermission)("notifications:create:any"),
@@ -0,0 +1,13 @@
1
+ import { ImageFit, ImageFormat, ImageProfile } from "../../../core/images/imageProfile.interface";
2
+ export declare const AVATAR_MAX_BYTES: number;
3
+ export declare const AVATAR_PROFILE = "avatars";
4
+ export declare class AvatarImageProfile implements ImageProfile {
5
+ readonly name = "avatars";
6
+ readonly folder = "avatars";
7
+ readonly width = 256;
8
+ readonly height = 256;
9
+ readonly fit = ImageFit.COVER;
10
+ readonly format = ImageFormat.WEBP;
11
+ readonly quality = 85;
12
+ readonly maxBytes: number;
13
+ }
@@ -0,0 +1,30 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.AvatarImageProfile = exports.AVATAR_PROFILE = exports.AVATAR_MAX_BYTES = void 0;
10
+ const common_1 = require("@nestjs/common");
11
+ const imageProfile_interface_1 = require("../../../core/images/imageProfile.interface");
12
+ exports.AVATAR_MAX_BYTES = 5 * 1024 * 1024;
13
+ exports.AVATAR_PROFILE = "avatars";
14
+ let AvatarImageProfile = class AvatarImageProfile {
15
+ constructor() {
16
+ this.name = exports.AVATAR_PROFILE;
17
+ this.folder = "avatars";
18
+ this.width = 256;
19
+ this.height = 256;
20
+ this.fit = imageProfile_interface_1.ImageFit.COVER;
21
+ this.format = imageProfile_interface_1.ImageFormat.WEBP;
22
+ this.quality = 85;
23
+ this.maxBytes = exports.AVATAR_MAX_BYTES;
24
+ }
25
+ };
26
+ exports.AvatarImageProfile = AvatarImageProfile;
27
+ exports.AvatarImageProfile = AvatarImageProfile = __decorate([
28
+ (0, imageProfile_interface_1.ImageUploadProfile)(),
29
+ (0, common_1.Injectable)()
30
+ ], AvatarImageProfile);
@@ -1,2 +1,2 @@
1
- export * from "./avatar.service";
1
+ export * from "./avatarImage.profile";
2
2
  export * from "./users.service";
@@ -14,5 +14,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- __exportStar(require("./avatar.service"), exports);
17
+ __exportStar(require("./avatarImage.profile"), exports);
18
18
  __exportStar(require("./users.service"), exports);
@@ -1,7 +1,7 @@
1
1
  import { AuthenticatedUser } from "../../../core/auth/authenticatedUser.interface";
2
+ import { ImagesService } from "../../../core/images/images.service";
2
3
  import { FilterFieldResponse } from "../../../core/query/filterSchema.response";
3
4
  import { Paginated, PaginationQuery } from "../../../core/utils/pagination.util";
4
- import { AvatarService } from "../domain/avatar.service";
5
5
  import { UsersService } from "../domain/users.service";
6
6
  import { User } from "../infra/schema/user.schema";
7
7
  import { ChangePasswordCommand } from "./commands/changePassword.command";
@@ -10,13 +10,14 @@ import { UpdateProfileCommand } from "./commands/updateProfile.command";
10
10
  import { UpdateUserCommand } from "./commands/updateUser.command";
11
11
  export declare class UsersController {
12
12
  private readonly users;
13
- private readonly avatar;
14
- constructor(users: UsersService, avatar: AvatarService);
13
+ private readonly images;
14
+ constructor(users: UsersService, images: ImagesService);
15
15
  me(userId: string): Promise<User>;
16
16
  updateMe(userId: string, dto: UpdateProfileCommand): Promise<User>;
17
17
  changeMyPassword(userId: string, dto: ChangePasswordCommand): Promise<void>;
18
- uploadAvatar(userId: string, file: Express.Multer.File): Promise<User>;
19
- deleteAvatar(userId: string): Promise<void>;
18
+ uploadAvatar(user: AuthenticatedUser, file: Express.Multer.File): Promise<User>;
19
+ deleteAvatar(user: AuthenticatedUser): Promise<void>;
20
+ private discardAvatar;
20
21
  list(query: PaginationQuery, actor: AuthenticatedUser): Promise<Paginated<User>>;
21
22
  filterSchema(actor: AuthenticatedUser): FilterFieldResponse[];
22
23
  findOne(id: string, actor: AuthenticatedUser): Promise<User>;
@@ -21,10 +21,11 @@ const currentUser_decorator_1 = require("../../../core/auth/decorators/currentUs
21
21
  const requirePermission_decorator_1 = require("../../../core/auth/decorators/requirePermission.decorator");
22
22
  const permissions_guard_1 = require("../../../core/auth/guards/permissions.guard");
23
23
  const permissionScope_util_1 = require("../../../core/auth/permissionScope.util");
24
+ const images_service_1 = require("../../../core/images/images.service");
24
25
  const filterSchema_response_1 = require("../../../core/query/filterSchema.response");
25
26
  const pagination_util_1 = require("../../../core/utils/pagination.util");
26
27
  const auditable_decorator_1 = require("../../audit/presentation/decorators/auditable.decorator");
27
- const avatar_service_1 = require("../domain/avatar.service");
28
+ const avatarImage_profile_1 = require("../domain/avatarImage.profile");
28
29
  const users_service_1 = require("../domain/users.service");
29
30
  const changePassword_command_1 = require("./commands/changePassword.command");
30
31
  const createUser_command_1 = require("./commands/createUser.command");
@@ -33,9 +34,9 @@ const updateUser_command_1 = require("./commands/updateUser.command");
33
34
  const users_filters_1 = require("./filters/users.filters");
34
35
  const user_response_1 = require("./responses/user.response");
35
36
  let UsersController = class UsersController {
36
- constructor(users, avatar) {
37
+ constructor(users, images) {
37
38
  this.users = users;
38
- this.avatar = avatar;
39
+ this.images = images;
39
40
  }
40
41
  me(userId) {
41
42
  return this.users.findById(userId);
@@ -46,13 +47,25 @@ let UsersController = class UsersController {
46
47
  async changeMyPassword(userId, dto) {
47
48
  await this.users.changePassword(userId, dto.currentPassword, dto.newPassword);
48
49
  }
49
- async uploadAvatar(userId, file) {
50
- const path = await this.avatar.processAndStore(userId, file);
51
- return this.users.setAvatar(userId, path);
50
+ async uploadAvatar(user, file) {
51
+ const previous = (await this.users.findById(user.id)).avatar;
52
+ const path = await this.images.fromFileNow(user, avatarImage_profile_1.AVATAR_PROFILE, file);
53
+ const updated = await this.users.setAvatar(user.id, path);
54
+ await this.discardAvatar(user, previous);
55
+ return updated;
52
56
  }
53
- async deleteAvatar(userId) {
54
- await this.avatar.remove(userId);
55
- await this.users.removeAvatar(userId);
57
+ async deleteAvatar(user) {
58
+ const previous = (await this.users.findById(user.id)).avatar;
59
+ await this.users.removeAvatar(user.id);
60
+ await this.discardAvatar(user, previous);
61
+ }
62
+ async discardAvatar(user, path) {
63
+ if (!path) {
64
+ return;
65
+ }
66
+ await this.images
67
+ .remove(user, avatarImage_profile_1.AVATAR_PROFILE, path)
68
+ .catch(() => undefined);
56
69
  }
57
70
  list(query, actor) {
58
71
  return this.users.list(query, actor);
@@ -122,20 +135,20 @@ __decorate([
122
135
  (0, swagger_1.ApiOkResponse)({ type: user_response_1.UserResponse }),
123
136
  (0, common_1.UseInterceptors)((0, platform_express_1.FileInterceptor)("file", {
124
137
  storage: (0, multer_1.memoryStorage)(),
125
- limits: { fileSize: avatar_service_1.AVATAR_MAX_BYTES, files: 1 },
138
+ limits: { fileSize: avatarImage_profile_1.AVATAR_MAX_BYTES, files: 1 },
126
139
  })),
127
- __param(0, (0, currentUser_decorator_1.CurrentUser)("id")),
140
+ __param(0, (0, currentUser_decorator_1.CurrentUser)()),
128
141
  __param(1, (0, common_1.UploadedFile)()),
129
142
  __metadata("design:type", Function),
130
- __metadata("design:paramtypes", [String, Object]),
143
+ __metadata("design:paramtypes", [Object, Object]),
131
144
  __metadata("design:returntype", Promise)
132
145
  ], UsersController.prototype, "uploadAvatar", null);
133
146
  __decorate([
134
147
  (0, common_1.Delete)("me/avatar"),
135
148
  (0, common_1.HttpCode)(204),
136
- __param(0, (0, currentUser_decorator_1.CurrentUser)("id")),
149
+ __param(0, (0, currentUser_decorator_1.CurrentUser)()),
137
150
  __metadata("design:type", Function),
138
- __metadata("design:paramtypes", [String]),
151
+ __metadata("design:paramtypes", [Object]),
139
152
  __metadata("design:returntype", Promise)
140
153
  ], UsersController.prototype, "deleteAvatar", null);
141
154
  __decorate([
@@ -257,5 +270,5 @@ exports.UsersController = UsersController = __decorate([
257
270
  (0, swagger_1.ApiBearerAuth)(),
258
271
  (0, common_1.Controller)({ path: "users", version: "1" }),
259
272
  __metadata("design:paramtypes", [users_service_1.UsersService,
260
- avatar_service_1.AvatarService])
273
+ images_service_1.ImagesService])
261
274
  ], UsersController);