@solidxai/core 0.1.12-beta.1 → 0.1.12-beta.3
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/constants/media-file-types.d.ts +5 -0
- package/dist/constants/media-file-types.d.ts.map +1 -0
- package/dist/constants/media-file-types.js +27 -0
- package/dist/constants/media-file-types.js.map +1 -0
- package/dist/controllers/media.controller.d.ts +14 -11
- package/dist/controllers/media.controller.d.ts.map +1 -1
- package/dist/controllers/media.controller.js +72 -33
- package/dist/controllers/media.controller.js.map +1 -1
- package/dist/dtos/create-chatter-message.dto.d.ts +1 -0
- package/dist/dtos/create-chatter-message.dto.d.ts.map +1 -1
- package/dist/dtos/create-chatter-message.dto.js +7 -1
- package/dist/dtos/create-chatter-message.dto.js.map +1 -1
- package/dist/dtos/create-media.dto.d.ts +1 -0
- package/dist/dtos/create-media.dto.d.ts.map +1 -1
- package/dist/dtos/create-media.dto.js +7 -1
- package/dist/dtos/create-media.dto.js.map +1 -1
- package/dist/dtos/post-chatter-message.dto.d.ts +1 -0
- package/dist/dtos/post-chatter-message.dto.d.ts.map +1 -1
- package/dist/dtos/post-chatter-message.dto.js +6 -1
- package/dist/dtos/post-chatter-message.dto.js.map +1 -1
- package/dist/dtos/update-chatter-message.dto.d.ts +1 -0
- package/dist/dtos/update-chatter-message.dto.d.ts.map +1 -1
- package/dist/dtos/update-chatter-message.dto.js +8 -1
- package/dist/dtos/update-chatter-message.dto.js.map +1 -1
- package/dist/dtos/update-chatter-note-message.dto.d.ts +1 -0
- package/dist/dtos/update-chatter-note-message.dto.d.ts.map +1 -1
- package/dist/dtos/update-chatter-note-message.dto.js +7 -1
- package/dist/dtos/update-chatter-note-message.dto.js.map +1 -1
- package/dist/dtos/update-media.dto.d.ts +1 -0
- package/dist/dtos/update-media.dto.d.ts.map +1 -1
- package/dist/dtos/update-media.dto.js +7 -1
- package/dist/dtos/update-media.dto.js.map +1 -1
- package/dist/entities/chatter-message.entity.d.ts +1 -0
- package/dist/entities/chatter-message.entity.d.ts.map +1 -1
- package/dist/entities/chatter-message.entity.js +5 -1
- package/dist/entities/chatter-message.entity.js.map +1 -1
- package/dist/entities/media.entity.d.ts +1 -0
- package/dist/entities/media.entity.d.ts.map +1 -1
- package/dist/entities/media.entity.js +9 -1
- package/dist/entities/media.entity.js.map +1 -1
- package/dist/enums/auth-type.enum.d.ts +2 -1
- package/dist/enums/auth-type.enum.d.ts.map +1 -1
- package/dist/enums/auth-type.enum.js +1 -0
- package/dist/enums/auth-type.enum.js.map +1 -1
- package/dist/guards/authentication.guard.d.ts +3 -1
- package/dist/guards/authentication.guard.d.ts.map +1 -1
- package/dist/guards/authentication.guard.js +5 -1
- package/dist/guards/authentication.guard.js.map +1 -1
- package/dist/guards/media-signed-url.guard.d.ts +9 -0
- package/dist/guards/media-signed-url.guard.d.ts.map +1 -0
- package/dist/guards/media-signed-url.guard.js +49 -0
- package/dist/guards/media-signed-url.guard.js.map +1 -0
- package/dist/helpers/field-crud-managers/MediaFieldCrudManager.d.ts.map +1 -1
- package/dist/helpers/field-crud-managers/MediaFieldCrudManager.js +10 -13
- package/dist/helpers/field-crud-managers/MediaFieldCrudManager.js.map +1 -1
- package/dist/interfaces/chatter-mention-notification.interface.d.ts +27 -0
- package/dist/interfaces/chatter-mention-notification.interface.d.ts.map +1 -0
- package/dist/interfaces/chatter-mention-notification.interface.js +3 -0
- package/dist/interfaces/chatter-mention-notification.interface.js.map +1 -0
- package/dist/jobs/chatter-mention-notification-email-queue-handler.service.d.ts +17 -0
- package/dist/jobs/chatter-mention-notification-email-queue-handler.service.d.ts.map +1 -0
- package/dist/jobs/chatter-mention-notification-email-queue-handler.service.js +106 -0
- package/dist/jobs/chatter-mention-notification-email-queue-handler.service.js.map +1 -0
- package/dist/jobs/database/chatter-mention-notification-email-publisher-database.service.d.ts +12 -0
- package/dist/jobs/database/chatter-mention-notification-email-publisher-database.service.d.ts.map +1 -0
- package/dist/jobs/database/chatter-mention-notification-email-publisher-database.service.js +39 -0
- package/dist/jobs/database/chatter-mention-notification-email-publisher-database.service.js.map +1 -0
- package/dist/jobs/database/chatter-mention-notification-email-queue-options-database.d.ts +8 -0
- package/dist/jobs/database/chatter-mention-notification-email-queue-options-database.d.ts.map +1 -0
- package/dist/jobs/database/chatter-mention-notification-email-queue-options-database.js +10 -0
- package/dist/jobs/database/chatter-mention-notification-email-queue-options-database.js.map +1 -0
- package/dist/jobs/database/chatter-mention-notification-email-subscriber-database.service.d.ts +20 -0
- package/dist/jobs/database/chatter-mention-notification-email-subscriber-database.service.d.ts.map +1 -0
- package/dist/jobs/database/chatter-mention-notification-email-subscriber-database.service.js +48 -0
- package/dist/jobs/database/chatter-mention-notification-email-subscriber-database.service.js.map +1 -0
- package/dist/jobs/rabbitmq/chatter-mention-notification-email-publisher.service.d.ts +12 -0
- package/dist/jobs/rabbitmq/chatter-mention-notification-email-publisher.service.d.ts.map +1 -0
- package/dist/jobs/rabbitmq/chatter-mention-notification-email-publisher.service.js +39 -0
- package/dist/jobs/rabbitmq/chatter-mention-notification-email-publisher.service.js.map +1 -0
- package/dist/jobs/rabbitmq/chatter-mention-notification-email-queue-options.d.ts +8 -0
- package/dist/jobs/rabbitmq/chatter-mention-notification-email-queue-options.d.ts.map +1 -0
- package/dist/jobs/rabbitmq/chatter-mention-notification-email-queue-options.js +10 -0
- package/dist/jobs/rabbitmq/chatter-mention-notification-email-queue-options.js.map +1 -0
- package/dist/jobs/rabbitmq/chatter-mention-notification-email-subscriber.service.d.ts +18 -0
- package/dist/jobs/rabbitmq/chatter-mention-notification-email-subscriber.service.d.ts.map +1 -0
- package/dist/jobs/rabbitmq/chatter-mention-notification-email-subscriber.service.js +45 -0
- package/dist/jobs/rabbitmq/chatter-mention-notification-email-subscriber.service.js.map +1 -0
- package/dist/jobs/redis/chatter-mention-notification-email-publisher-redis.service.d.ts +12 -0
- package/dist/jobs/redis/chatter-mention-notification-email-publisher-redis.service.d.ts.map +1 -0
- package/dist/jobs/redis/chatter-mention-notification-email-publisher-redis.service.js +39 -0
- package/dist/jobs/redis/chatter-mention-notification-email-publisher-redis.service.js.map +1 -0
- package/dist/jobs/redis/chatter-mention-notification-email-queue-options-redis.d.ts +8 -0
- package/dist/jobs/redis/chatter-mention-notification-email-queue-options-redis.d.ts.map +1 -0
- package/dist/jobs/redis/chatter-mention-notification-email-queue-options-redis.js +10 -0
- package/dist/jobs/redis/chatter-mention-notification-email-queue-options-redis.js.map +1 -0
- package/dist/jobs/redis/chatter-mention-notification-email-subscriber-redis.service.d.ts +18 -0
- package/dist/jobs/redis/chatter-mention-notification-email-subscriber-redis.service.d.ts.map +1 -0
- package/dist/jobs/redis/chatter-mention-notification-email-subscriber-redis.service.js +45 -0
- package/dist/jobs/redis/chatter-mention-notification-email-subscriber-redis.service.js.map +1 -0
- package/dist/repository/media.repository.d.ts.map +1 -1
- package/dist/repository/media.repository.js +2 -0
- package/dist/repository/media.repository.js.map +1 -1
- package/dist/seeders/seed-data/solid-core-metadata.json +109 -3
- package/dist/services/chatter-message.service.d.ts +7 -1
- package/dist/services/chatter-message.service.d.ts.map +1 -1
- package/dist/services/chatter-message.service.js +85 -4
- package/dist/services/chatter-message.service.js.map +1 -1
- package/dist/services/media-download-url.service.d.ts +21 -0
- package/dist/services/media-download-url.service.d.ts.map +1 -0
- package/dist/services/media-download-url.service.js +67 -0
- package/dist/services/media-download-url.service.js.map +1 -0
- package/dist/services/media-storage.utils.d.ts +27 -0
- package/dist/services/media-storage.utils.d.ts.map +1 -0
- package/dist/services/media-storage.utils.js +82 -0
- package/dist/services/media-storage.utils.js.map +1 -0
- package/dist/services/media.service.d.ts +20 -4
- package/dist/services/media.service.d.ts.map +1 -1
- package/dist/services/media.service.js +157 -55
- package/dist/services/media.service.js.map +1 -1
- package/dist/services/mediaStorageProviders/file-s3-storage-provider.d.ts +4 -4
- package/dist/services/mediaStorageProviders/file-s3-storage-provider.d.ts.map +1 -1
- package/dist/services/mediaStorageProviders/file-s3-storage-provider.js +37 -63
- package/dist/services/mediaStorageProviders/file-s3-storage-provider.js.map +1 -1
- package/dist/services/mediaStorageProviders/file-storage-provider.d.ts +5 -5
- package/dist/services/mediaStorageProviders/file-storage-provider.d.ts.map +1 -1
- package/dist/services/mediaStorageProviders/file-storage-provider.js +37 -38
- package/dist/services/mediaStorageProviders/file-storage-provider.js.map +1 -1
- package/dist/solid-core.module.d.ts.map +1 -1
- package/dist/solid-core.module.js +25 -1
- package/dist/solid-core.module.js.map +1 -1
- package/dist/subscribers/audit.subscriber.js +1 -1
- package/dist/subscribers/audit.subscriber.js.map +1 -1
- package/package.json +1 -1
- package/src/constants/media-file-types.ts +44 -0
- package/src/controllers/media.controller.ts +44 -24
- package/src/dtos/create-chatter-message.dto.ts +5 -0
- package/src/dtos/create-media.dto.ts +8 -3
- package/src/dtos/post-chatter-message.dto.ts +4 -0
- package/src/dtos/update-chatter-message.dto.ts +6 -0
- package/src/dtos/update-chatter-note-message.dto.ts +5 -0
- package/src/dtos/update-media.dto.ts +8 -3
- package/src/entities/chatter-message.entity.ts +3 -0
- package/src/entities/media.entity.ts +4 -1
- package/src/enums/auth-type.enum.ts +1 -0
- package/src/guards/authentication.guard.ts +3 -0
- package/src/guards/media-signed-url.guard.ts +41 -0
- package/src/helpers/field-crud-managers/MediaFieldCrudManager.ts +12 -24
- package/src/interfaces/chatter-mention-notification.interface.ts +29 -0
- package/src/jobs/chatter-mention-notification-email-queue-handler.service.ts +114 -0
- package/src/jobs/database/chatter-mention-notification-email-publisher-database.service.ts +23 -0
- package/src/jobs/database/chatter-mention-notification-email-queue-options-database.ts +9 -0
- package/src/jobs/database/chatter-mention-notification-email-subscriber-database.service.ts +32 -0
- package/src/jobs/rabbitmq/chatter-mention-notification-email-publisher.service.ts +23 -0
- package/src/jobs/rabbitmq/chatter-mention-notification-email-queue-options.ts +9 -0
- package/src/jobs/rabbitmq/chatter-mention-notification-email-subscriber.service.ts +30 -0
- package/src/jobs/redis/chatter-mention-notification-email-publisher-redis.service.ts +23 -0
- package/src/jobs/redis/chatter-mention-notification-email-queue-options-redis.ts +9 -0
- package/src/jobs/redis/chatter-mention-notification-email-subscriber-redis.service.ts +30 -0
- package/src/repository/media.repository.ts +2 -0
- package/src/seeders/seed-data/email-templates/chatter-mention-notification.handlebars.html +200 -0
- package/src/seeders/seed-data/solid-core-metadata.json +109 -3
- package/src/services/chatter-message.service.ts +97 -2
- package/src/services/media-download-url.service.ts +67 -0
- package/src/services/media-storage.utils.ts +79 -0
- package/src/services/media.service.ts +189 -59
- package/src/services/mediaStorageProviders/file-s3-storage-provider.ts +52 -76
- package/src/services/mediaStorageProviders/file-storage-provider.ts +44 -48
- package/src/solid-core.module.ts +35 -2
- package/src/subscribers/audit.subscriber.ts +1 -1
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="UTF-8" />
|
|
6
|
+
<title>You were mentioned in a note</title>
|
|
7
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
8
|
+
|
|
9
|
+
<style>
|
|
10
|
+
@import url("https://fonts.googleapis.com/css?family=Nunito+Sans:400,700&display=swap");
|
|
11
|
+
|
|
12
|
+
body {
|
|
13
|
+
margin: 0;
|
|
14
|
+
padding: 0;
|
|
15
|
+
background-color: #f4f4f7;
|
|
16
|
+
font-family: "Nunito Sans", Helvetica, Arial, sans-serif;
|
|
17
|
+
color: #333;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.email-wrapper {
|
|
21
|
+
max-width: 600px;
|
|
22
|
+
margin: 0 auto;
|
|
23
|
+
padding: 20px;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.logo {
|
|
27
|
+
text-align: center;
|
|
28
|
+
margin-bottom: 20px;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.logo img {
|
|
32
|
+
height: 50px;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.content-box {
|
|
36
|
+
background-color: #ffffff;
|
|
37
|
+
padding: 40px;
|
|
38
|
+
border-radius: 8px;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.title {
|
|
42
|
+
font-size: 22px;
|
|
43
|
+
margin-bottom: 10px;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.intro {
|
|
47
|
+
font-size: 16px;
|
|
48
|
+
line-height: 1.6;
|
|
49
|
+
margin: 0;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.note-box {
|
|
53
|
+
background-color: #f7f3ff;
|
|
54
|
+
border-left: 4px solid #722ED1;
|
|
55
|
+
padding: 16px;
|
|
56
|
+
border-radius: 5px;
|
|
57
|
+
margin: 24px 0;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.note-label {
|
|
61
|
+
margin: 0 0 8px;
|
|
62
|
+
font-size: 13px;
|
|
63
|
+
font-weight: 700;
|
|
64
|
+
color: #722ED1;
|
|
65
|
+
text-transform: uppercase;
|
|
66
|
+
letter-spacing: 0.04em;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.note-box p {
|
|
70
|
+
margin: 0;
|
|
71
|
+
font-size: 16px;
|
|
72
|
+
line-height: 1.6;
|
|
73
|
+
white-space: pre-wrap;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.note-body {
|
|
77
|
+
font-weight: 700;
|
|
78
|
+
font-style: italic;
|
|
79
|
+
color: #2f2450;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.record-summary {
|
|
83
|
+
background-color: #fbfbfd;
|
|
84
|
+
border: 1px solid #ececf1;
|
|
85
|
+
padding: 14px;
|
|
86
|
+
border-radius: 5px;
|
|
87
|
+
margin-bottom: 24px;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.record-summary p {
|
|
91
|
+
margin: 0 0 8px;
|
|
92
|
+
font-size: 14px;
|
|
93
|
+
line-height: 1.5;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.button-container {
|
|
97
|
+
text-align: center;
|
|
98
|
+
margin-bottom: 30px;
|
|
99
|
+
margin-top: 20px;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.button {
|
|
103
|
+
display: inline-block;
|
|
104
|
+
padding: 12px 24px;
|
|
105
|
+
background-color: #722ED1;
|
|
106
|
+
color: #ffffff;
|
|
107
|
+
text-decoration: none;
|
|
108
|
+
font-weight: bold;
|
|
109
|
+
border-radius: 5px;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.footer-text {
|
|
113
|
+
font-size: 16px;
|
|
114
|
+
line-height: 1.6;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.footer {
|
|
118
|
+
text-align: center;
|
|
119
|
+
font-size: 12px;
|
|
120
|
+
color: #6b6e76;
|
|
121
|
+
margin-top: 40px;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
@media only screen and (max-width: 600px) {
|
|
125
|
+
.email-wrapper {
|
|
126
|
+
width: 100% !important;
|
|
127
|
+
padding: 20px 0 !important;
|
|
128
|
+
margin: 0;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.content-box {
|
|
132
|
+
padding: 20px !important;
|
|
133
|
+
border-radius: 0;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.title {
|
|
137
|
+
font-size: 18px !important;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.button {
|
|
141
|
+
padding: 10px !important;
|
|
142
|
+
font-size: 14px;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
</style>
|
|
146
|
+
</head>
|
|
147
|
+
|
|
148
|
+
<body>
|
|
149
|
+
<div class="email-wrapper">
|
|
150
|
+
{{#if companyLogoUrl}}
|
|
151
|
+
<div class="logo">
|
|
152
|
+
<img src="{{companyLogoUrl}}" alt="Company Logo" />
|
|
153
|
+
</div>
|
|
154
|
+
{{/if}}
|
|
155
|
+
|
|
156
|
+
<div class="content-box">
|
|
157
|
+
<h2 class="title">Hi {{ mentionedUserDisplayName }},</h2>
|
|
158
|
+
<p class="intro">
|
|
159
|
+
<strong>{{ mentionedByDisplayName }}</strong> mentioned you in a note on
|
|
160
|
+
<strong>{{ entityDisplayName }}</strong>{{#if entityUserKey}} <strong>{{ entityUserKey }}</strong>{{/if}}.
|
|
161
|
+
</p>
|
|
162
|
+
|
|
163
|
+
<div class="note-box">
|
|
164
|
+
<p class="note-label" style="margin: 0 0 8px; font-size: 13px; font-weight: 700; color: #722ED1; text-transform: uppercase; letter-spacing: 0.04em;">
|
|
165
|
+
Note
|
|
166
|
+
</p>
|
|
167
|
+
<p class="note-body" style="margin: 0; font-size: 16px; line-height: 1.6; white-space: pre-wrap; font-weight: 700; font-style: italic; color: #2f2450;">
|
|
168
|
+
{{ noteBody }}
|
|
169
|
+
</p>
|
|
170
|
+
</div>
|
|
171
|
+
|
|
172
|
+
<div class="record-summary">
|
|
173
|
+
<p><strong>Record:</strong> {{ entityDisplayName }}{{#if entityUserKey}} - {{ entityUserKey }}{{/if}}</p>
|
|
174
|
+
<p><strong>Mentioned by:</strong> {{ mentionedByDisplayName }}{{#if mentionedByEmail}} ({{ mentionedByEmail }}){{/if}}</p>
|
|
175
|
+
</div>
|
|
176
|
+
|
|
177
|
+
<p class="intro">
|
|
178
|
+
To view the note and reply to {{ mentionedByDisplayName }}, open the record below.
|
|
179
|
+
</p>
|
|
180
|
+
|
|
181
|
+
<div class="button-container">
|
|
182
|
+
<a href="{{ entityFormUrl }}" class="button" target="_blank">
|
|
183
|
+
Open Record
|
|
184
|
+
</a>
|
|
185
|
+
</div>
|
|
186
|
+
|
|
187
|
+
<p class="intro">
|
|
188
|
+
Regards,
|
|
189
|
+
<br />
|
|
190
|
+
The {{ solidAppName }} Team.
|
|
191
|
+
</p>
|
|
192
|
+
</div>
|
|
193
|
+
|
|
194
|
+
<div class="footer">
|
|
195
|
+
© 2025 <a href="{{ solidAppWebsiteUrl }}" target="_blank">{{ solidAppName }}</a>. All rights reserved.
|
|
196
|
+
</div>
|
|
197
|
+
</div>
|
|
198
|
+
</body>
|
|
199
|
+
|
|
200
|
+
</html>
|
|
@@ -1704,6 +1704,18 @@
|
|
|
1704
1704
|
"columnName": "original_file_name",
|
|
1705
1705
|
"isSystem": true
|
|
1706
1706
|
},
|
|
1707
|
+
{
|
|
1708
|
+
"name": "isPublic",
|
|
1709
|
+
"displayName": "Is Public",
|
|
1710
|
+
"type": "boolean",
|
|
1711
|
+
"required": false,
|
|
1712
|
+
"unique": false,
|
|
1713
|
+
"index": false,
|
|
1714
|
+
"private": false,
|
|
1715
|
+
"encrypt": false,
|
|
1716
|
+
"columnName": "is_public",
|
|
1717
|
+
"isSystem": true
|
|
1718
|
+
},
|
|
1707
1719
|
{
|
|
1708
1720
|
"name": "modelMetadata",
|
|
1709
1721
|
"displayName": "Model Metadata",
|
|
@@ -3911,6 +3923,18 @@
|
|
|
3911
3923
|
"encrypt": false,
|
|
3912
3924
|
"isSystem": false
|
|
3913
3925
|
},
|
|
3926
|
+
{
|
|
3927
|
+
"name": "messageBodyMentions",
|
|
3928
|
+
"displayName": "Message Body Mentions",
|
|
3929
|
+
"type": "longText",
|
|
3930
|
+
"ormType": "text",
|
|
3931
|
+
"required": false,
|
|
3932
|
+
"unique": false,
|
|
3933
|
+
"index": false,
|
|
3934
|
+
"private": false,
|
|
3935
|
+
"encrypt": false,
|
|
3936
|
+
"isSystem": false
|
|
3937
|
+
},
|
|
3914
3938
|
{
|
|
3915
3939
|
"name": "coModelEntityId",
|
|
3916
3940
|
"displayName": "CoModel Entity Id",
|
|
@@ -5294,6 +5318,9 @@
|
|
|
5294
5318
|
"FieldMetadataController.getSelectionDynamicValues",
|
|
5295
5319
|
"FieldMetadataController.getSelectionDynamicValue",
|
|
5296
5320
|
"FieldMetadataController.findFieldDefaultMetaData",
|
|
5321
|
+
"MediaController.findMany",
|
|
5322
|
+
"MediaController.findOne",
|
|
5323
|
+
"MediaController.download",
|
|
5297
5324
|
"SavedFiltersController.delete",
|
|
5298
5325
|
"SavedFiltersController.deleteMany",
|
|
5299
5326
|
"SavedFiltersController.findOne",
|
|
@@ -8595,9 +8622,9 @@
|
|
|
8595
8622
|
"attrs": {
|
|
8596
8623
|
"name": "roles",
|
|
8597
8624
|
"widget": "checkbox",
|
|
8598
|
-
"inlineCreateAutoSave": "
|
|
8625
|
+
"inlineCreateAutoSave": "false",
|
|
8599
8626
|
"renderModeCheckboxPreprocessor": "MenuItemMetadataRolesFormFieldPreprocessor",
|
|
8600
|
-
"inlineCreate": "
|
|
8627
|
+
"inlineCreate": "false",
|
|
8601
8628
|
"inlineCreateLayout": {
|
|
8602
8629
|
"type": "form",
|
|
8603
8630
|
"attrs": {
|
|
@@ -8746,6 +8773,13 @@
|
|
|
8746
8773
|
"sortable": false
|
|
8747
8774
|
}
|
|
8748
8775
|
},
|
|
8776
|
+
{
|
|
8777
|
+
"type": "field",
|
|
8778
|
+
"attrs": {
|
|
8779
|
+
"name": "isPublic",
|
|
8780
|
+
"sortable": true
|
|
8781
|
+
}
|
|
8782
|
+
},
|
|
8749
8783
|
{
|
|
8750
8784
|
"type": "field",
|
|
8751
8785
|
"attrs": {
|
|
@@ -8841,6 +8875,14 @@
|
|
|
8841
8875
|
"isSearchable": true
|
|
8842
8876
|
}
|
|
8843
8877
|
},
|
|
8878
|
+
{
|
|
8879
|
+
"type": "field",
|
|
8880
|
+
"attrs": {
|
|
8881
|
+
"name": "isPublic",
|
|
8882
|
+
"label": "isPublic",
|
|
8883
|
+
"isSearchable": true
|
|
8884
|
+
}
|
|
8885
|
+
},
|
|
8844
8886
|
{
|
|
8845
8887
|
"type": "field",
|
|
8846
8888
|
"attrs": {
|
|
@@ -8909,6 +8951,12 @@
|
|
|
8909
8951
|
"name": "mediaStorageProviderMetadata"
|
|
8910
8952
|
}
|
|
8911
8953
|
},
|
|
8954
|
+
{
|
|
8955
|
+
"type": "field",
|
|
8956
|
+
"attrs": {
|
|
8957
|
+
"name": "isPublic"
|
|
8958
|
+
}
|
|
8959
|
+
},
|
|
8912
8960
|
{
|
|
8913
8961
|
"type": "field",
|
|
8914
8962
|
"attrs": {
|
|
@@ -14461,6 +14509,15 @@
|
|
|
14461
14509
|
"description": "Welcome email sent to users after signup.",
|
|
14462
14510
|
"active": true,
|
|
14463
14511
|
"type": "text"
|
|
14512
|
+
},
|
|
14513
|
+
{
|
|
14514
|
+
"name": "chatter-mention-notification",
|
|
14515
|
+
"displayName": "Default: Chatter Mention Notification",
|
|
14516
|
+
"body": "chatter-mention-notification.handlebars.html",
|
|
14517
|
+
"subject": "{{mentionedByDisplayName}} mentioned you in {{entityDisplayName}}",
|
|
14518
|
+
"description": "This template notifies users when they are mentioned in a custom chatter note.",
|
|
14519
|
+
"active": true,
|
|
14520
|
+
"type": "text"
|
|
14464
14521
|
}
|
|
14465
14522
|
],
|
|
14466
14523
|
"smsTemplates": [
|
|
@@ -14504,7 +14561,15 @@
|
|
|
14504
14561
|
"mediaStorageProviders": [
|
|
14505
14562
|
{
|
|
14506
14563
|
"name": "default-filesystem",
|
|
14507
|
-
"type": "filesystem"
|
|
14564
|
+
"type": "filesystem",
|
|
14565
|
+
"isPublic": true,
|
|
14566
|
+
"localPath": "media-files-storage"
|
|
14567
|
+
},
|
|
14568
|
+
{
|
|
14569
|
+
"name": "private-filesystem",
|
|
14570
|
+
"type": "filesystem",
|
|
14571
|
+
"isPublic": false,
|
|
14572
|
+
"localPath": "media-private-files-storage"
|
|
14508
14573
|
},
|
|
14509
14574
|
{
|
|
14510
14575
|
"name": "default-aws-s3",
|
|
@@ -14540,6 +14605,47 @@
|
|
|
14540
14605
|
}
|
|
14541
14606
|
}
|
|
14542
14607
|
}
|
|
14608
|
+
},
|
|
14609
|
+
{
|
|
14610
|
+
"name": "model:Media-role:Internal User-own",
|
|
14611
|
+
"description": "Show Media records owned by the current user",
|
|
14612
|
+
"roleUserKey": "Internal User",
|
|
14613
|
+
"modelMetadataUserKey": "media",
|
|
14614
|
+
"securityRuleConfig": {
|
|
14615
|
+
"filters": {
|
|
14616
|
+
"createdBy": {
|
|
14617
|
+
"$eq": "$activeUserId"
|
|
14618
|
+
}
|
|
14619
|
+
}
|
|
14620
|
+
}
|
|
14621
|
+
},
|
|
14622
|
+
{
|
|
14623
|
+
"name": "model:Media-role:Internal User-public",
|
|
14624
|
+
"description": "Show public Media records",
|
|
14625
|
+
"roleUserKey": "Internal User",
|
|
14626
|
+
"modelMetadataUserKey": "media",
|
|
14627
|
+
"securityRuleConfig": {
|
|
14628
|
+
"filters": {
|
|
14629
|
+
"mediaStorageProviderMetadata": {
|
|
14630
|
+
"isPublic": {
|
|
14631
|
+
"$eq": true
|
|
14632
|
+
}
|
|
14633
|
+
}
|
|
14634
|
+
}
|
|
14635
|
+
}
|
|
14636
|
+
},
|
|
14637
|
+
{
|
|
14638
|
+
"name": "model:Media-role:Admin",
|
|
14639
|
+
"description": "Show All Media records",
|
|
14640
|
+
"roleUserKey": "Admin",
|
|
14641
|
+
"modelMetadataUserKey": "media",
|
|
14642
|
+
"securityRuleConfig": {
|
|
14643
|
+
"filters": {
|
|
14644
|
+
"id": {
|
|
14645
|
+
"$ne": "0"
|
|
14646
|
+
}
|
|
14647
|
+
}
|
|
14648
|
+
}
|
|
14543
14649
|
}
|
|
14544
14650
|
],
|
|
14545
14651
|
"modelSequences": [],
|
|
@@ -11,6 +11,7 @@ import { PostChatterMessageDto } from 'src/dtos/post-chatter-message.dto';
|
|
|
11
11
|
import { UpdateChatterNoteMessageDto } from 'src/dtos/update-chatter-note-message.dto';
|
|
12
12
|
import { ModelMetadataHelperService } from 'src/helpers/model-metadata-helper.service';
|
|
13
13
|
import { lowerFirst } from 'src/helpers/string.helper';
|
|
14
|
+
import { ChatterMentionNotificationPayload } from 'src/interfaces/chatter-mention-notification.interface';
|
|
14
15
|
import { ChatterMessageDetailsRepository } from 'src/repository/chatter-message-details.repository';
|
|
15
16
|
import { ChatterMessageRepository } from 'src/repository/chatter-message.repository';
|
|
16
17
|
import { FieldMetadataRepository } from 'src/repository/field-metadata.repository';
|
|
@@ -24,6 +25,14 @@ import { getMediaStorageProvider } from './mediaStorageProviders';
|
|
|
24
25
|
import { RequestContextService } from './request-context.service';
|
|
25
26
|
import { Logger } from '@nestjs/common';
|
|
26
27
|
import { SolidIntrospectService } from './solid-introspect.service';
|
|
28
|
+
import { PublisherFactory } from './queues/publisher-factory.service';
|
|
29
|
+
|
|
30
|
+
interface ChatterMention {
|
|
31
|
+
username: string;
|
|
32
|
+
display_name?: string;
|
|
33
|
+
displayName?: string;
|
|
34
|
+
id?: string | number;
|
|
35
|
+
}
|
|
27
36
|
|
|
28
37
|
@Injectable()
|
|
29
38
|
export class ChatterMessageService extends CRUDService<ChatterMessage> {
|
|
@@ -47,6 +56,7 @@ export class ChatterMessageService extends CRUDService<ChatterMessage> {
|
|
|
47
56
|
private readonly modelMetadataRepo: ModelMetadataRepository,
|
|
48
57
|
readonly requestContextService: RequestContextService,
|
|
49
58
|
private readonly modelMetadataHelperService: ModelMetadataHelperService,
|
|
59
|
+
private readonly publisherFactory: PublisherFactory<ChatterMentionNotificationPayload>,
|
|
50
60
|
) {
|
|
51
61
|
super(entityManager, repo, 'chatterMessage', 'solid-core', moduleRef);
|
|
52
62
|
}
|
|
@@ -106,6 +116,78 @@ export class ChatterMessageService extends CRUDService<ChatterMessage> {
|
|
|
106
116
|
.filter(v => Number.isInteger(v) && v > 0);
|
|
107
117
|
}
|
|
108
118
|
|
|
119
|
+
private parseMessageBodyMentions(value?: string): ChatterMention[] {
|
|
120
|
+
if (!value || typeof value !== 'string') return [];
|
|
121
|
+
try {
|
|
122
|
+
const parsed = JSON.parse(value);
|
|
123
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
124
|
+
} catch {
|
|
125
|
+
return [];
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
private renderMentionTokens(messageBody: string, mentions: ChatterMention[]) {
|
|
130
|
+
return mentions.reduce((nextMessage, mention, index) => {
|
|
131
|
+
const username = mention.username;
|
|
132
|
+
if (!username) return nextMessage;
|
|
133
|
+
return nextMessage.replace(new RegExp(`\\{\\{\\s*${index}\\s*\\}\\}`, 'g'), `@${username}`);
|
|
134
|
+
}, messageBody || '');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
private async publishChatterMentionNotifications(message: ChatterMessage, model: any) {
|
|
138
|
+
if (message.messageType !== CHATTER_MESSAGE_TYPE.CUSTOM || message.messageSubType !== CHATTER_MESSAGE_SUBTYPE.NOTE) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const mentions = this.parseMessageBodyMentions(message.messageBodyMentions);
|
|
143
|
+
const mentionUserIds = mentions
|
|
144
|
+
.map(mention => Number(mention.id))
|
|
145
|
+
.filter(id => Number.isInteger(id) && id > 0);
|
|
146
|
+
const uniqueMentionUserIds = Array.from(new Set(mentionUserIds));
|
|
147
|
+
if (uniqueMentionUserIds.length === 0) return;
|
|
148
|
+
|
|
149
|
+
const activeUser = this.requestContextService.getActiveUser();
|
|
150
|
+
|
|
151
|
+
const payload: ChatterMentionNotificationPayload = {
|
|
152
|
+
templateName: 'chatter-mention-notification',
|
|
153
|
+
mentions: mentions.map(mention => ({
|
|
154
|
+
id: Number(mention.id) || undefined,
|
|
155
|
+
username: mention.username,
|
|
156
|
+
displayName: mention.display_name || mention.displayName || mention.username,
|
|
157
|
+
})),
|
|
158
|
+
actor: {
|
|
159
|
+
id: activeUser?.sub,
|
|
160
|
+
username: activeUser?.username,
|
|
161
|
+
email: activeUser?.email,
|
|
162
|
+
},
|
|
163
|
+
noteBody: this.renderMentionTokens(message.messageBody, mentions),
|
|
164
|
+
entity: {
|
|
165
|
+
id: message.coModelEntityId,
|
|
166
|
+
modelName: model?.singularName || message.coModelName,
|
|
167
|
+
moduleName: model?.module?.name,
|
|
168
|
+
displayName: model?.displayName || message.coModelName,
|
|
169
|
+
userKey: message.modelUserKey,
|
|
170
|
+
},
|
|
171
|
+
parentEntity: 'chatterMessage',
|
|
172
|
+
parentEntityId: message.id,
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
try {
|
|
176
|
+
await this.publisherFactory.publish(
|
|
177
|
+
{
|
|
178
|
+
payload,
|
|
179
|
+
parentEntity: 'chatterMessage',
|
|
180
|
+
parentEntityId: message.id,
|
|
181
|
+
retryCount: 3,
|
|
182
|
+
retryInterval: 5000,
|
|
183
|
+
},
|
|
184
|
+
'ChatterMentionNotificationEmailQueuePublisher',
|
|
185
|
+
);
|
|
186
|
+
} catch (error: any) {
|
|
187
|
+
this._logger.error(`Failed to publish chatter mention notification email job: ${error.message}`, error.stack);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
109
191
|
async markCompleted(id: number) {
|
|
110
192
|
const activeUser = this.requestContextService.getActiveUser();
|
|
111
193
|
if (!activeUser) {
|
|
@@ -146,10 +228,11 @@ export class ChatterMessageService extends CRUDService<ChatterMessage> {
|
|
|
146
228
|
|
|
147
229
|
const removeAttachmentIds = this.parseAttachmentIds(updateDto?.removeAttachmentIds);
|
|
148
230
|
const hasMessageBody = typeof updateDto?.messageBody === 'string';
|
|
231
|
+
const hasMessageBodyMentions = typeof updateDto?.messageBodyMentions === 'string';
|
|
149
232
|
const trimmedMessageBody = (updateDto?.messageBody ?? '').trim();
|
|
150
233
|
const hasNewFiles = Array.isArray(files) && files.length > 0;
|
|
151
234
|
|
|
152
|
-
if (!hasMessageBody && removeAttachmentIds.length === 0 && !hasNewFiles) {
|
|
235
|
+
if (!hasMessageBody && !hasMessageBodyMentions && removeAttachmentIds.length === 0 && !hasNewFiles) {
|
|
153
236
|
throw new BadRequestException('No note changes submitted.');
|
|
154
237
|
}
|
|
155
238
|
|
|
@@ -160,6 +243,13 @@ export class ChatterMessageService extends CRUDService<ChatterMessage> {
|
|
|
160
243
|
if (hasMessageBody) {
|
|
161
244
|
message.messageBody = trimmedMessageBody;
|
|
162
245
|
}
|
|
246
|
+
if (hasMessageBodyMentions) {
|
|
247
|
+
message.messageBodyMentions = updateDto.messageBodyMentions;
|
|
248
|
+
}
|
|
249
|
+
const model = await this.modelMetadataRepo.findOne({
|
|
250
|
+
where: { singularName: message.coModelName },
|
|
251
|
+
relations: { module: true }
|
|
252
|
+
});
|
|
163
253
|
message.updatedBy = activeUser.sub;
|
|
164
254
|
// Ensure updatedAt changes even for attachment-only edits.
|
|
165
255
|
message.updatedAt = new Date();
|
|
@@ -214,6 +304,8 @@ export class ChatterMessageService extends CRUDService<ChatterMessage> {
|
|
|
214
304
|
}
|
|
215
305
|
}
|
|
216
306
|
|
|
307
|
+
await this.publishChatterMentionNotifications(savedMessage, model);
|
|
308
|
+
|
|
217
309
|
return savedMessage;
|
|
218
310
|
}
|
|
219
311
|
|
|
@@ -226,13 +318,14 @@ export class ChatterMessageService extends CRUDService<ChatterMessage> {
|
|
|
226
318
|
chatterMessage.messageSubType = postDto.messageSubType || CHATTER_MESSAGE_SUBTYPE.CUSTOM;
|
|
227
319
|
chatterMessage.status = postDto.status ?? CHATTER_MESSAGE_STATUS.PENDING;
|
|
228
320
|
chatterMessage.messageBody = postDto.messageBody;
|
|
321
|
+
chatterMessage.messageBodyMentions = postDto.messageBodyMentions;
|
|
229
322
|
chatterMessage.coModelEntityId = postDto.coModelEntityId;
|
|
230
323
|
chatterMessage.coModelName = coModelName;
|
|
231
324
|
chatterMessage.modelUserKey = postDto.modelUserKey ?? null;
|
|
232
325
|
|
|
233
326
|
const model = await this.modelMetadataRepo.findOne({
|
|
234
327
|
where: { singularName: coModelName },
|
|
235
|
-
relations: { userKeyField: true }
|
|
328
|
+
relations: { userKeyField: true, module: true }
|
|
236
329
|
});
|
|
237
330
|
chatterMessage.modelDisplayName = model?.displayName ?? null;
|
|
238
331
|
|
|
@@ -262,6 +355,8 @@ export class ChatterMessageService extends CRUDService<ChatterMessage> {
|
|
|
262
355
|
}
|
|
263
356
|
}
|
|
264
357
|
|
|
358
|
+
await this.publishChatterMentionNotifications(savedMessage, model);
|
|
359
|
+
|
|
265
360
|
return savedMessage;
|
|
266
361
|
}
|
|
267
362
|
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Injectable, Logger } from '@nestjs/common';
|
|
2
|
+
import { ConfigService } from '@nestjs/config';
|
|
3
|
+
import { JwtService } from '@nestjs/jwt';
|
|
4
|
+
import { MediaStorageProviderType } from 'src/dtos/create-media-storage-provider-metadata.dto';
|
|
5
|
+
import { MediaStorageProviderMetadata } from 'src/entities/media-storage-provider-metadata.entity';
|
|
6
|
+
import { S3FileService } from 'src/services/file';
|
|
7
|
+
import { getEffectiveS3Region } from 'src/services/media-storage.utils';
|
|
8
|
+
import { SettingService } from 'src/services/setting.service';
|
|
9
|
+
import type { SolidCoreSetting } from 'src/services/settings/default-settings-provider.service';
|
|
10
|
+
|
|
11
|
+
export const MEDIA_DOWNLOAD_TOKEN_AUDIENCE = 'media-download';
|
|
12
|
+
const DEFAULT_SIGNED_URL_EXPIRY_MINUTES = 60;
|
|
13
|
+
|
|
14
|
+
export interface MediaDownloadTokenPayload {
|
|
15
|
+
mediaId: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@Injectable()
|
|
19
|
+
export class MediaDownloadUrlService {
|
|
20
|
+
private readonly logger = new Logger(MediaDownloadUrlService.name);
|
|
21
|
+
|
|
22
|
+
constructor(
|
|
23
|
+
private readonly jwtService: JwtService,
|
|
24
|
+
private readonly s3FileService: S3FileService,
|
|
25
|
+
private readonly settingService: SettingService,
|
|
26
|
+
private readonly configService: ConfigService,
|
|
27
|
+
) { }
|
|
28
|
+
|
|
29
|
+
async resolveDownloadUrl(mediaId: number, relativeUri: string, storageProvider?: MediaStorageProviderMetadata): Promise<string> {
|
|
30
|
+
const expiryMinutes = storageProvider?.signedUrlExpiry ?? DEFAULT_SIGNED_URL_EXPIRY_MINUTES;
|
|
31
|
+
|
|
32
|
+
if ((storageProvider?.type as MediaStorageProviderType) === MediaStorageProviderType.AwsS3) {
|
|
33
|
+
if (storageProvider?.bucketName) {
|
|
34
|
+
return this.s3FileService.getUrl(
|
|
35
|
+
`${storageProvider.bucketName}:${relativeUri}`,
|
|
36
|
+
{ region: getEffectiveS3Region(this.configService, storageProvider.region), expiresIn: expiryMinutes * 60 },
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
this.logger.warn(`Media id ${mediaId}: AwsS3 provider missing bucketName; falling back to app-hosted token URL`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Filesystem (or misconfigured S3) has no native presigned-URL mechanism, so mint our
|
|
43
|
+
// own short-lived token and embed it in the download URL - the token alone authorizes
|
|
44
|
+
// the request (see MediaSignedUrlGuard), so it works in a plain <img src> with no
|
|
45
|
+
// Authorization header needed.
|
|
46
|
+
const token = await this.mintDownloadToken(mediaId, expiryMinutes);
|
|
47
|
+
const downloadPath = `/api/media/${mediaId}/download?token=${token}`;
|
|
48
|
+
const baseUrl = this.settingService.getConfigValue<SolidCoreSetting>('baseUrl');
|
|
49
|
+
return `${String(baseUrl).replace(/\/+$/, '')}${downloadPath}`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async verifyDownloadToken(token: string): Promise<MediaDownloadTokenPayload> {
|
|
53
|
+
return this.jwtService.verifyAsync<MediaDownloadTokenPayload>(token, {
|
|
54
|
+
secret: this.settingService.getConfigValue<SolidCoreSetting>('secret'),
|
|
55
|
+
audience: MEDIA_DOWNLOAD_TOKEN_AUDIENCE,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private mintDownloadToken(mediaId: number, expiryMinutes: number): Promise<string> {
|
|
60
|
+
const payload: MediaDownloadTokenPayload = { mediaId };
|
|
61
|
+
return this.jwtService.signAsync(payload, {
|
|
62
|
+
secret: this.settingService.getConfigValue<SolidCoreSetting>('secret'),
|
|
63
|
+
audience: MEDIA_DOWNLOAD_TOKEN_AUDIENCE,
|
|
64
|
+
expiresIn: expiryMinutes * 60,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|