@happyvertical/smrt-messages 0.37.2 → 0.37.4
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/chunks/AccountCollection-BekDBDAm.js +210 -0
- package/dist/chunks/AccountCollection-BekDBDAm.js.map +1 -0
- package/dist/chunks/Email-Bv6cu8QX.js +282 -0
- package/dist/chunks/Email-Bv6cu8QX.js.map +1 -0
- package/dist/chunks/EmailFolder-DvZODbnW.js +153 -0
- package/dist/chunks/EmailFolder-DvZODbnW.js.map +1 -0
- package/dist/chunks/Message-CHTC1RuY.js +333 -0
- package/dist/chunks/Message-CHTC1RuY.js.map +1 -0
- package/dist/chunks/MessageCollection-DvG0YCfd.js +169 -0
- package/dist/chunks/MessageCollection-DvG0YCfd.js.map +1 -0
- package/dist/chunks/rolldown-runtime-D7D4PA-g.js +13 -0
- package/dist/index.js +1597 -3098
- package/dist/index.js.map +1 -1
- package/dist/manifest.json +2 -2
- package/dist/playground.js +154 -174
- package/dist/playground.js.map +1 -1
- package/dist/smrt-knowledge.json +11 -11
- package/dist/types.js +0 -2
- package/dist/ui.js +101 -101
- package/dist/ui.js.map +1 -1
- package/package.json +22 -22
- package/dist/types.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,3133 +1,1632 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
import { t as __exportAll } from "./chunks/rolldown-runtime-D7D4PA-g.js";
|
|
2
|
+
import { r as Account, t as AccountCollection } from "./chunks/AccountCollection-BekDBDAm.js";
|
|
3
|
+
import { t as Message } from "./chunks/Message-CHTC1RuY.js";
|
|
4
|
+
import { t as MessageCollection } from "./chunks/MessageCollection-DvG0YCfd.js";
|
|
5
|
+
import { t as Email } from "./chunks/Email-Bv6cu8QX.js";
|
|
6
|
+
import { t as EmailFolder } from "./chunks/EmailFolder-DvZODbnW.js";
|
|
7
|
+
import { ObjectRegistry, SmrtCollection, SmrtObject, foreignKey, smrt } from "@happyvertical/smrt-core";
|
|
8
|
+
import { TenantScoped, queryGlobal, queryWithGlobals, tenantId } from "@happyvertical/smrt-tenancy";
|
|
9
|
+
//#region src/__smrt-register__.ts
|
|
10
|
+
ObjectRegistry.registerPackageManifest(new URL("./manifest.json", "" + import.meta.url));
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region src/models/Attachment.ts
|
|
13
|
+
var __defProp$7 = Object.defineProperty;
|
|
14
|
+
var __getOwnPropDesc$7 = Object.getOwnPropertyDescriptor;
|
|
15
|
+
var __decorateClass$7 = (decorators, target, key, kind) => {
|
|
16
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$7(target, key) : target;
|
|
17
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
18
|
+
if (kind && result) __defProp$7(target, key, result);
|
|
19
|
+
return result;
|
|
15
20
|
};
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* Retrieve stored credentials
|
|
114
|
-
*/
|
|
115
|
-
async getCredentials() {
|
|
116
|
-
if (!this.credentialSecretId) {
|
|
117
|
-
return this.getSettings();
|
|
118
|
-
}
|
|
119
|
-
const { SecretService } = await import("@happyvertical/smrt-secrets");
|
|
120
|
-
const secretService = await SecretService.create({ db: this.db });
|
|
121
|
-
try {
|
|
122
|
-
const secret = await secretService.retrieve(this.credentialSecretId);
|
|
123
|
-
return JSON.parse(secret.value);
|
|
124
|
-
} catch {
|
|
125
|
-
return null;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
21
|
+
var Attachment = class extends SmrtObject {
|
|
22
|
+
tenantId = null;
|
|
23
|
+
messageId = "";
|
|
24
|
+
filename = "";
|
|
25
|
+
contentType = "";
|
|
26
|
+
size = 0;
|
|
27
|
+
contentId = "";
|
|
28
|
+
contentDisposition = "attachment";
|
|
29
|
+
filePath = "";
|
|
30
|
+
sourceUrl = "";
|
|
31
|
+
createdAt = /* @__PURE__ */ new Date();
|
|
32
|
+
constructor(options = {}) {
|
|
33
|
+
super(options);
|
|
34
|
+
if (options.tenantId !== void 0) this.tenantId = options.tenantId;
|
|
35
|
+
if (options.messageId !== void 0) this.messageId = options.messageId;
|
|
36
|
+
if (options.filename !== void 0) this.filename = options.filename;
|
|
37
|
+
if (options.contentType !== void 0) this.contentType = options.contentType;
|
|
38
|
+
if (options.size !== void 0) this.size = options.size;
|
|
39
|
+
if (options.contentId !== void 0) this.contentId = options.contentId;
|
|
40
|
+
if (options.contentDisposition !== void 0) this.contentDisposition = options.contentDisposition;
|
|
41
|
+
if (options.filePath !== void 0) this.filePath = options.filePath;
|
|
42
|
+
if (options.sourceUrl !== void 0) this.sourceUrl = options.sourceUrl;
|
|
43
|
+
if (options.createdAt) this.createdAt = options.createdAt;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Get the message this attachment belongs to
|
|
47
|
+
*/
|
|
48
|
+
async getMessage() {
|
|
49
|
+
if (!this.messageId) return null;
|
|
50
|
+
const { MessageCollection } = await import("./chunks/MessageCollection-DvG0YCfd.js").then((n) => n.n);
|
|
51
|
+
return await (await MessageCollection.create(this.options)).get({ id: this.messageId });
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Check if attachment is an image
|
|
55
|
+
*/
|
|
56
|
+
isImage() {
|
|
57
|
+
return this.contentType.startsWith("image/");
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Check if attachment is a PDF
|
|
61
|
+
*/
|
|
62
|
+
isPdf() {
|
|
63
|
+
return this.contentType === "application/pdf";
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Check if attachment is inline (embedded in message body)
|
|
67
|
+
*/
|
|
68
|
+
isInline() {
|
|
69
|
+
return this.contentDisposition === "inline";
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Get file extension from filename
|
|
73
|
+
*/
|
|
74
|
+
getExtension() {
|
|
75
|
+
if (!this.filename) return "";
|
|
76
|
+
const parts = this.filename.split(".");
|
|
77
|
+
return parts.length > 1 ? parts[parts.length - 1].toLowerCase() : "";
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Get human-readable file size
|
|
81
|
+
*/
|
|
82
|
+
getFormattedSize() {
|
|
83
|
+
const units = [
|
|
84
|
+
"B",
|
|
85
|
+
"KB",
|
|
86
|
+
"MB",
|
|
87
|
+
"GB"
|
|
88
|
+
];
|
|
89
|
+
let size = this.size;
|
|
90
|
+
let unitIndex = 0;
|
|
91
|
+
while (size >= 1024 && unitIndex < units.length - 1) {
|
|
92
|
+
size /= 1024;
|
|
93
|
+
unitIndex++;
|
|
94
|
+
}
|
|
95
|
+
return `${size.toFixed(1)} ${units[unitIndex]}`;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Check if file is stored externally
|
|
99
|
+
*/
|
|
100
|
+
hasExternalFile() {
|
|
101
|
+
return !!this.filePath;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Read file content (if stored externally)
|
|
105
|
+
*/
|
|
106
|
+
async readContent() {
|
|
107
|
+
if (!this.filePath) return null;
|
|
108
|
+
try {
|
|
109
|
+
const { getFilesystem } = await import("@happyvertical/files");
|
|
110
|
+
const data = await (await getFilesystem({ type: "local" })).read(this.filePath);
|
|
111
|
+
return data instanceof Buffer ? data : Buffer.from(data);
|
|
112
|
+
} catch {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
128
116
|
};
|
|
129
|
-
__decorateClass$
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
class
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
117
|
+
__decorateClass$7([tenantId({ nullable: true })], Attachment.prototype, "tenantId", 2);
|
|
118
|
+
__decorateClass$7([foreignKey("Message")], Attachment.prototype, "messageId", 2);
|
|
119
|
+
Attachment = __decorateClass$7([TenantScoped({ mode: "optional" }), smrt({
|
|
120
|
+
api: { include: [
|
|
121
|
+
"list",
|
|
122
|
+
"get",
|
|
123
|
+
"create",
|
|
124
|
+
"delete"
|
|
125
|
+
] },
|
|
126
|
+
mcp: { include: ["list", "get"] },
|
|
127
|
+
cli: true
|
|
128
|
+
})], Attachment);
|
|
129
|
+
//#endregion
|
|
130
|
+
//#region src/collections/AttachmentCollection.ts
|
|
131
|
+
var AttachmentCollection_exports = /* @__PURE__ */ __exportAll({ AttachmentCollection: () => AttachmentCollection });
|
|
132
|
+
var AttachmentCollection = class extends SmrtCollection {
|
|
133
|
+
static _itemClass = Attachment;
|
|
134
|
+
/**
|
|
135
|
+
* Get attachments for a message
|
|
136
|
+
*/
|
|
137
|
+
async getByMessage(messageId) {
|
|
138
|
+
return await this.list({ where: { messageId } });
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Get attachments by content type
|
|
142
|
+
*/
|
|
143
|
+
async getByContentType(contentType) {
|
|
144
|
+
return await this.list({ where: { contentType } });
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Get image attachments
|
|
148
|
+
*/
|
|
149
|
+
async getImages(messageId) {
|
|
150
|
+
return (messageId ? await this.getByMessage(messageId) : await this.list({})).filter((a) => a.isImage());
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Get PDF attachments
|
|
154
|
+
*/
|
|
155
|
+
async getPdfs(messageId) {
|
|
156
|
+
return (messageId ? await this.getByMessage(messageId) : await this.list({})).filter((a) => a.isPdf());
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Get inline attachments (embedded in message body)
|
|
160
|
+
*/
|
|
161
|
+
async getInline(messageId) {
|
|
162
|
+
return (await this.getByMessage(messageId)).filter((a) => a.isInline());
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Get regular attachments (not inline)
|
|
166
|
+
*/
|
|
167
|
+
async getRegular(messageId) {
|
|
168
|
+
return (await this.getByMessage(messageId)).filter((a) => !a.isInline());
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Get attachments with external files
|
|
172
|
+
*/
|
|
173
|
+
async getWithExternalFiles() {
|
|
174
|
+
return (await this.list({})).filter((a) => a.hasExternalFile());
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Get total size of attachments for a message
|
|
178
|
+
*/
|
|
179
|
+
async getTotalSize(messageId) {
|
|
180
|
+
return (await this.getByMessage(messageId)).reduce((sum, a) => sum + a.size, 0);
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Get largest attachments
|
|
184
|
+
*/
|
|
185
|
+
async getLargest(limit = 10) {
|
|
186
|
+
return (await this.list({})).sort((a, b) => b.size - a.size).slice(0, limit);
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Search attachments by filename
|
|
190
|
+
*/
|
|
191
|
+
async searchByFilename(query) {
|
|
192
|
+
const attachments = await this.list({});
|
|
193
|
+
const lowerQuery = query.toLowerCase();
|
|
194
|
+
return attachments.filter((a) => a.filename?.toLowerCase().includes(lowerQuery));
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Get attachments by extension
|
|
198
|
+
*/
|
|
199
|
+
async getByExtension(extension) {
|
|
200
|
+
const attachments = await this.list({});
|
|
201
|
+
const lowerExt = extension.toLowerCase().replace(/^\./, "");
|
|
202
|
+
return attachments.filter((a) => a.getExtension() === lowerExt);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Get attachment statistics
|
|
206
|
+
*/
|
|
207
|
+
async getStats() {
|
|
208
|
+
const attachments = await this.list({});
|
|
209
|
+
const byType = {};
|
|
210
|
+
for (const attachment of attachments) {
|
|
211
|
+
const type = attachment.contentType.split("/")[0] || "other";
|
|
212
|
+
byType[type] = (byType[type] || 0) + 1;
|
|
213
|
+
}
|
|
214
|
+
return {
|
|
215
|
+
total: attachments.length,
|
|
216
|
+
totalSize: attachments.reduce((sum, a) => sum + a.size, 0),
|
|
217
|
+
byType,
|
|
218
|
+
inline: attachments.filter((a) => a.isInline()).length,
|
|
219
|
+
regular: attachments.filter((a) => !a.isInline()).length
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Delete all attachments for a message
|
|
224
|
+
*/
|
|
225
|
+
async deleteByMessage(messageId) {
|
|
226
|
+
const attachments = await this.getByMessage(messageId);
|
|
227
|
+
let count = 0;
|
|
228
|
+
for (const attachment of attachments) {
|
|
229
|
+
await attachment.delete();
|
|
230
|
+
count++;
|
|
231
|
+
}
|
|
232
|
+
return count;
|
|
233
|
+
}
|
|
234
|
+
async findByTenant(tenantId) {
|
|
235
|
+
return this.list({ where: { tenantId } });
|
|
236
|
+
}
|
|
237
|
+
async findGlobal() {
|
|
238
|
+
return queryGlobal(this);
|
|
239
|
+
}
|
|
240
|
+
async findWithGlobals(tenantId) {
|
|
241
|
+
return queryWithGlobals(this, tenantId, "Attachment.findWithGlobals");
|
|
242
|
+
}
|
|
255
243
|
};
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
filePath = "";
|
|
266
|
-
// External file storage path
|
|
267
|
-
sourceUrl = "";
|
|
268
|
-
// For non-email attachments (media URLs, etc.)
|
|
269
|
-
// Timestamps
|
|
270
|
-
createdAt = /* @__PURE__ */ new Date();
|
|
271
|
-
constructor(options = {}) {
|
|
272
|
-
super(options);
|
|
273
|
-
if (options.tenantId !== void 0) this.tenantId = options.tenantId;
|
|
274
|
-
if (options.messageId !== void 0) this.messageId = options.messageId;
|
|
275
|
-
if (options.filename !== void 0) this.filename = options.filename;
|
|
276
|
-
if (options.contentType !== void 0)
|
|
277
|
-
this.contentType = options.contentType;
|
|
278
|
-
if (options.size !== void 0) this.size = options.size;
|
|
279
|
-
if (options.contentId !== void 0) this.contentId = options.contentId;
|
|
280
|
-
if (options.contentDisposition !== void 0)
|
|
281
|
-
this.contentDisposition = options.contentDisposition;
|
|
282
|
-
if (options.filePath !== void 0) this.filePath = options.filePath;
|
|
283
|
-
if (options.sourceUrl !== void 0) this.sourceUrl = options.sourceUrl;
|
|
284
|
-
if (options.createdAt) this.createdAt = options.createdAt;
|
|
285
|
-
}
|
|
286
|
-
/**
|
|
287
|
-
* Get the message this attachment belongs to
|
|
288
|
-
*/
|
|
289
|
-
async getMessage() {
|
|
290
|
-
if (!this.messageId) return null;
|
|
291
|
-
const { MessageCollection: MessageCollection2 } = await Promise.resolve().then(() => MessageCollection$1);
|
|
292
|
-
const collection = await MessageCollection2.create(this.options);
|
|
293
|
-
return await collection.get({ id: this.messageId });
|
|
294
|
-
}
|
|
295
|
-
/**
|
|
296
|
-
* Check if attachment is an image
|
|
297
|
-
*/
|
|
298
|
-
isImage() {
|
|
299
|
-
return this.contentType.startsWith("image/");
|
|
300
|
-
}
|
|
301
|
-
/**
|
|
302
|
-
* Check if attachment is a PDF
|
|
303
|
-
*/
|
|
304
|
-
isPdf() {
|
|
305
|
-
return this.contentType === "application/pdf";
|
|
306
|
-
}
|
|
307
|
-
/**
|
|
308
|
-
* Check if attachment is inline (embedded in message body)
|
|
309
|
-
*/
|
|
310
|
-
isInline() {
|
|
311
|
-
return this.contentDisposition === "inline";
|
|
312
|
-
}
|
|
313
|
-
/**
|
|
314
|
-
* Get file extension from filename
|
|
315
|
-
*/
|
|
316
|
-
getExtension() {
|
|
317
|
-
if (!this.filename) return "";
|
|
318
|
-
const parts = this.filename.split(".");
|
|
319
|
-
return parts.length > 1 ? parts[parts.length - 1].toLowerCase() : "";
|
|
320
|
-
}
|
|
321
|
-
/**
|
|
322
|
-
* Get human-readable file size
|
|
323
|
-
*/
|
|
324
|
-
getFormattedSize() {
|
|
325
|
-
const units = ["B", "KB", "MB", "GB"];
|
|
326
|
-
let size = this.size;
|
|
327
|
-
let unitIndex = 0;
|
|
328
|
-
while (size >= 1024 && unitIndex < units.length - 1) {
|
|
329
|
-
size /= 1024;
|
|
330
|
-
unitIndex++;
|
|
331
|
-
}
|
|
332
|
-
return `${size.toFixed(1)} ${units[unitIndex]}`;
|
|
333
|
-
}
|
|
334
|
-
/**
|
|
335
|
-
* Check if file is stored externally
|
|
336
|
-
*/
|
|
337
|
-
hasExternalFile() {
|
|
338
|
-
return !!this.filePath;
|
|
339
|
-
}
|
|
340
|
-
/**
|
|
341
|
-
* Read file content (if stored externally)
|
|
342
|
-
*/
|
|
343
|
-
async readContent() {
|
|
344
|
-
if (!this.filePath) return null;
|
|
345
|
-
try {
|
|
346
|
-
const { getFilesystem } = await import("@happyvertical/files");
|
|
347
|
-
const files = await getFilesystem({ type: "local" });
|
|
348
|
-
const data = await files.read(this.filePath);
|
|
349
|
-
return data instanceof Buffer ? data : Buffer.from(data);
|
|
350
|
-
} catch {
|
|
351
|
-
return null;
|
|
352
|
-
}
|
|
353
|
-
}
|
|
244
|
+
//#endregion
|
|
245
|
+
//#region src/models/EmailAccount.ts
|
|
246
|
+
var __defProp$6 = Object.defineProperty;
|
|
247
|
+
var __getOwnPropDesc$6 = Object.getOwnPropertyDescriptor;
|
|
248
|
+
var __decorateClass$6 = (decorators, target, key, kind) => {
|
|
249
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$6(target, key) : target;
|
|
250
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
251
|
+
if (kind && result) __defProp$6(target, key, result);
|
|
252
|
+
return result;
|
|
354
253
|
};
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
}
|
|
500
|
-
const
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
254
|
+
var EmailAccount = class extends Account {
|
|
255
|
+
email = "";
|
|
256
|
+
syncIntervalMinutes = 60;
|
|
257
|
+
constructor(options = {}) {
|
|
258
|
+
super(options);
|
|
259
|
+
if (options.email !== void 0) this.email = options.email;
|
|
260
|
+
if (options.providerType !== void 0) this.providerType = options.providerType;
|
|
261
|
+
if (options.syncIntervalMinutes !== void 0) this.syncIntervalMinutes = options.syncIntervalMinutes;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Options for child rows (folders/emails) created during sync: carries the DB
|
|
265
|
+
* connection + tenant context from this account, but strips this account's own
|
|
266
|
+
* identity fields. When this account was hydrated from the DB, `this.options`
|
|
267
|
+
* holds the account row's `id`/`slug`/`_skipLoad`; spreading those into a new
|
|
268
|
+
* child would make every synced row inherit the account's primary key and
|
|
269
|
+
* upsert over each other.
|
|
270
|
+
*/
|
|
271
|
+
childOptions() {
|
|
272
|
+
const rest = { ...this.options };
|
|
273
|
+
delete rest.id;
|
|
274
|
+
delete rest.slug;
|
|
275
|
+
delete rest._skipLoad;
|
|
276
|
+
return rest;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Create a sender for this email account
|
|
280
|
+
*/
|
|
281
|
+
async createSender() {
|
|
282
|
+
const client = await this.createClient();
|
|
283
|
+
await client.connect();
|
|
284
|
+
const { EmailSender } = await Promise.resolve().then(() => EmailSender_exports);
|
|
285
|
+
return new EmailSender(client, this);
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Create an EmailClient from stored settings
|
|
289
|
+
* Retrieves credentials from smrt-secrets if credentialSecretId is set
|
|
290
|
+
*/
|
|
291
|
+
async createClient() {
|
|
292
|
+
const { getEmailClient } = await import("@happyvertical/email");
|
|
293
|
+
let settings;
|
|
294
|
+
if (this.credentialSecretId) {
|
|
295
|
+
const { SecretService } = await import("@happyvertical/smrt-secrets");
|
|
296
|
+
const secret = await (await SecretService.create({ db: this.db })).retrieve(this.credentialSecretId);
|
|
297
|
+
settings = JSON.parse(secret.value);
|
|
298
|
+
} else settings = this.getSettings();
|
|
299
|
+
return await getEmailClient({
|
|
300
|
+
type: this.providerType,
|
|
301
|
+
...settings
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Sync emails from the email server to the database
|
|
306
|
+
*/
|
|
307
|
+
async syncFrom(options = {}) {
|
|
308
|
+
const startTime = Date.now();
|
|
309
|
+
const result = {
|
|
310
|
+
folders: [],
|
|
311
|
+
messagesProcessed: 0,
|
|
312
|
+
messagesDownloaded: 0,
|
|
313
|
+
messagesSkipped: 0,
|
|
314
|
+
errors: [],
|
|
315
|
+
duration: 0
|
|
316
|
+
};
|
|
317
|
+
try {
|
|
318
|
+
const client = await this.createClient();
|
|
319
|
+
await client.connect();
|
|
320
|
+
const foldersToSync = options.folders || ["INBOX"];
|
|
321
|
+
result.folders = foldersToSync;
|
|
322
|
+
const { EmailCollection } = await Promise.resolve().then(() => EmailCollection_exports);
|
|
323
|
+
const { EmailFolderCollection } = await Promise.resolve().then(() => EmailFolderCollection_exports);
|
|
324
|
+
const emailCollection = await EmailCollection.create(this.options);
|
|
325
|
+
const folderCollection = await EmailFolderCollection.create(this.options);
|
|
326
|
+
if (!this.id) throw new Error("EmailAccount.syncFrom requires a persisted account (missing id)");
|
|
327
|
+
const accountId = this.id;
|
|
328
|
+
for (const folderName of foldersToSync) try {
|
|
329
|
+
let folder = await folderCollection.getByPath(accountId, folderName);
|
|
330
|
+
if (!folder) {
|
|
331
|
+
const { EmailFolder } = await import("./chunks/EmailFolder-DvZODbnW.js").then((n) => n.n);
|
|
332
|
+
folder = new EmailFolder({
|
|
333
|
+
...this.childOptions(),
|
|
334
|
+
accountId,
|
|
335
|
+
name: folderName,
|
|
336
|
+
path: folderName
|
|
337
|
+
});
|
|
338
|
+
await folder.initialize();
|
|
339
|
+
await folder.save();
|
|
340
|
+
}
|
|
341
|
+
const fetchOptions = {
|
|
342
|
+
folder: folderName,
|
|
343
|
+
limit: options.batchSize || 100
|
|
344
|
+
};
|
|
345
|
+
if (options.since) fetchOptions.since = options.since;
|
|
346
|
+
if (options.before) fetchOptions.before = options.before;
|
|
347
|
+
const messages = await client.fetch(fetchOptions);
|
|
348
|
+
for (const msg of messages) {
|
|
349
|
+
result.messagesProcessed++;
|
|
350
|
+
try {
|
|
351
|
+
const existingEmail = await emailCollection.getByMessageId(accountId, msg.messageId || "");
|
|
352
|
+
if (existingEmail && !options.fullSync) {
|
|
353
|
+
result.messagesSkipped++;
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
const { Email } = await import("./chunks/Email-Bv6cu8QX.js").then((n) => n.n);
|
|
357
|
+
let email = existingEmail;
|
|
358
|
+
if (!email) {
|
|
359
|
+
email = new Email({
|
|
360
|
+
...this.childOptions(),
|
|
361
|
+
accountId
|
|
362
|
+
});
|
|
363
|
+
await email.initialize();
|
|
364
|
+
}
|
|
365
|
+
email.messageId = msg.messageId || "";
|
|
366
|
+
email.threadId = msg.threadId || "";
|
|
367
|
+
email.inReplyTo = msg.inReplyTo || "";
|
|
368
|
+
email.fromAddress = msg.from?.address || "";
|
|
369
|
+
email.fromName = msg.from?.name || "";
|
|
370
|
+
email.toAddresses = JSON.stringify(msg.to || []);
|
|
371
|
+
email.ccAddresses = JSON.stringify(msg.cc || []);
|
|
372
|
+
email.bccAddresses = JSON.stringify(msg.bcc || []);
|
|
373
|
+
email.replyToAddress = msg.replyTo?.address || "";
|
|
374
|
+
email.replyToName = msg.replyTo?.name || "";
|
|
375
|
+
email.subject = msg.subject || "";
|
|
376
|
+
email.date = msg.date || null;
|
|
377
|
+
email.textBody = msg.text || "";
|
|
378
|
+
email.htmlBody = msg.html || "";
|
|
379
|
+
email.body = msg.text || "";
|
|
380
|
+
email.folderId = folder.id ?? "";
|
|
381
|
+
email.folderPath = folderName;
|
|
382
|
+
email.labels = JSON.stringify(msg.labels || []);
|
|
383
|
+
email.flags = JSON.stringify(msg.flags || []);
|
|
384
|
+
email.hasAttachments = msg.attachments && msg.attachments.length > 0 || false;
|
|
385
|
+
email.size = msg.size || 0;
|
|
386
|
+
email.headers = JSON.stringify(msg.headers || {});
|
|
387
|
+
email.updatedAt = /* @__PURE__ */ new Date();
|
|
388
|
+
await email.save();
|
|
389
|
+
result.messagesDownloaded++;
|
|
390
|
+
if (options.onProgress) options.onProgress({
|
|
391
|
+
folder: folderName,
|
|
392
|
+
processed: result.messagesProcessed,
|
|
393
|
+
total: messages.length,
|
|
394
|
+
downloaded: result.messagesDownloaded,
|
|
395
|
+
skipped: result.messagesSkipped,
|
|
396
|
+
errors: result.errors.length
|
|
397
|
+
});
|
|
398
|
+
} catch (error) {
|
|
399
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
400
|
+
result.errors.push(err);
|
|
401
|
+
if (options.onError) options.onError(err, msg);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
const folderId = folder.id ?? "";
|
|
405
|
+
folder.messageCount = await emailCollection.countByFolder(folderId);
|
|
406
|
+
folder.unreadCount = await emailCollection.countUnreadByFolder(folderId);
|
|
407
|
+
await folder.save();
|
|
408
|
+
} catch (error) {
|
|
409
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
410
|
+
result.errors.push(err);
|
|
411
|
+
if (options.onError) options.onError(err);
|
|
412
|
+
}
|
|
413
|
+
this.lastSyncAt = /* @__PURE__ */ new Date();
|
|
414
|
+
this.updatedAt = /* @__PURE__ */ new Date();
|
|
415
|
+
await this.save();
|
|
416
|
+
await client.disconnect();
|
|
417
|
+
} catch (error) {
|
|
418
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
419
|
+
result.errors.push(err);
|
|
420
|
+
if (options.onError) options.onError(err);
|
|
421
|
+
}
|
|
422
|
+
result.duration = Date.now() - startTime;
|
|
423
|
+
return result;
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Get all folders for this account
|
|
427
|
+
*/
|
|
428
|
+
async getFolders() {
|
|
429
|
+
const { EmailFolderCollection } = await Promise.resolve().then(() => EmailFolderCollection_exports);
|
|
430
|
+
return await (await EmailFolderCollection.create(this.options)).list({ where: { accountId: this.id } });
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Get all emails for this account
|
|
434
|
+
*/
|
|
435
|
+
async getEmails(limit) {
|
|
436
|
+
const { EmailCollection } = await Promise.resolve().then(() => EmailCollection_exports);
|
|
437
|
+
const collection = await EmailCollection.create(this.options);
|
|
438
|
+
const options = { where: { accountId: this.id } };
|
|
439
|
+
if (limit) options.limit = limit;
|
|
440
|
+
return await collection.list(options);
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Get unread email count
|
|
444
|
+
*/
|
|
445
|
+
async getUnreadCount() {
|
|
446
|
+
const { EmailCollection } = await Promise.resolve().then(() => EmailCollection_exports);
|
|
447
|
+
return await (await EmailCollection.create(this.options)).countUnreadByAccount(this.id ?? "");
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Store credentials securely using smrt-secrets (email-specific)
|
|
451
|
+
*/
|
|
452
|
+
async setCredentials(credentials, options = {}) {
|
|
453
|
+
const { SecretService } = await import("@happyvertical/smrt-secrets");
|
|
454
|
+
const secretService = await SecretService.create({ db: this.db });
|
|
455
|
+
const secretName = `email-account-${this.id}`;
|
|
456
|
+
const secretValue = JSON.stringify(credentials);
|
|
457
|
+
if (this.credentialSecretId) await secretService.store(this.credentialSecretId, secretValue, {
|
|
458
|
+
description: options.description || `IMAP credentials for ${this.name}`,
|
|
459
|
+
category: options.category || "email"
|
|
460
|
+
});
|
|
461
|
+
else {
|
|
462
|
+
await secretService.store(secretName, secretValue, {
|
|
463
|
+
description: options.description || `IMAP credentials for ${this.name}`,
|
|
464
|
+
category: options.category || "email"
|
|
465
|
+
});
|
|
466
|
+
this.credentialSecretId = secretName;
|
|
467
|
+
this.updatedAt = /* @__PURE__ */ new Date();
|
|
468
|
+
await this.save();
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Sync all active email accounts (for job runner)
|
|
473
|
+
*
|
|
474
|
+
* NOTE: This is a class-wide operation, not per-instance. It syncs ALL active
|
|
475
|
+
* accounts, ignoring `this` instance. It exists as an instance method because
|
|
476
|
+
* the TaskRunner dispatches via `objectType: 'EmailAccount', method: 'syncAll'`
|
|
477
|
+
* which requires an instance method signature.
|
|
478
|
+
*/
|
|
479
|
+
async syncAll(args) {
|
|
480
|
+
const { EmailAccountCollection } = await Promise.resolve().then(() => EmailAccountCollection_exports);
|
|
481
|
+
return (await EmailAccountCollection.create({ db: this.db })).syncAll(args);
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Migrate from plain-text settings to secrets
|
|
485
|
+
*/
|
|
486
|
+
async migrateToSecrets() {
|
|
487
|
+
if (this.credentialSecretId) return;
|
|
488
|
+
const settings = this.getSettings();
|
|
489
|
+
if (Object.keys(settings).length === 0) return;
|
|
490
|
+
await this.setCredentials(settings, { description: `Migrated IMAP credentials for ${this.name}` });
|
|
491
|
+
}
|
|
513
492
|
};
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
return await collection.list({ where: { messageId: this.id } });
|
|
671
|
-
}
|
|
672
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
673
|
-
// Send Lifecycle
|
|
674
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
675
|
-
/**
|
|
676
|
-
* Send this message via its account's sender
|
|
677
|
-
*/
|
|
678
|
-
async send(options) {
|
|
679
|
-
if (this.sendStatus === "sending" || this.sendStatus === "sent") {
|
|
680
|
-
return {
|
|
681
|
-
success: false,
|
|
682
|
-
error: `Cannot send: message is already '${this.sendStatus}'`,
|
|
683
|
-
sentAt: /* @__PURE__ */ new Date()
|
|
684
|
-
};
|
|
685
|
-
}
|
|
686
|
-
const account = await this.getAccount();
|
|
687
|
-
if (!account) {
|
|
688
|
-
const result = {
|
|
689
|
-
success: false,
|
|
690
|
-
error: "No account associated with this message",
|
|
691
|
-
sentAt: /* @__PURE__ */ new Date()
|
|
692
|
-
};
|
|
693
|
-
this.sendStatus = "failed";
|
|
694
|
-
this.sendError = result.error ?? "";
|
|
695
|
-
this.updatedAt = /* @__PURE__ */ new Date();
|
|
696
|
-
await this.save();
|
|
697
|
-
return result;
|
|
698
|
-
}
|
|
699
|
-
const sender = await account.createSender();
|
|
700
|
-
const claimFromStatus = this.sendStatus;
|
|
701
|
-
if (this.isPersisted && this.id) {
|
|
702
|
-
const claim = await this.db.update(
|
|
703
|
-
this.tableName,
|
|
704
|
-
{ id: this.id, send_status: claimFromStatus },
|
|
705
|
-
{ send_status: "sending", updated_at: /* @__PURE__ */ new Date() }
|
|
706
|
-
);
|
|
707
|
-
if (!claim || claim.affected < 1) {
|
|
708
|
-
return {
|
|
709
|
-
success: false,
|
|
710
|
-
error: "Cannot send: message is already being sent",
|
|
711
|
-
sentAt: /* @__PURE__ */ new Date()
|
|
712
|
-
};
|
|
713
|
-
}
|
|
714
|
-
this.sendStatus = "sending";
|
|
715
|
-
this.updatedAt = /* @__PURE__ */ new Date();
|
|
716
|
-
} else {
|
|
717
|
-
this.sendStatus = "sending";
|
|
718
|
-
this.updatedAt = /* @__PURE__ */ new Date();
|
|
719
|
-
await this.save();
|
|
720
|
-
}
|
|
721
|
-
try {
|
|
722
|
-
const result = await sender.send(this, options);
|
|
723
|
-
if (result.success) {
|
|
724
|
-
this.sendStatus = "sent";
|
|
725
|
-
this.sentAt = result.sentAt;
|
|
726
|
-
this.sendError = "";
|
|
727
|
-
} else {
|
|
728
|
-
this.sendStatus = "failed";
|
|
729
|
-
this.sendError = result.error ?? "Send failed";
|
|
730
|
-
}
|
|
731
|
-
this.updatedAt = /* @__PURE__ */ new Date();
|
|
732
|
-
await this.save();
|
|
733
|
-
return result;
|
|
734
|
-
} catch (error) {
|
|
735
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
736
|
-
this.sendStatus = "failed";
|
|
737
|
-
this.sendError = errorMessage;
|
|
738
|
-
this.updatedAt = /* @__PURE__ */ new Date();
|
|
739
|
-
await this.save();
|
|
740
|
-
return {
|
|
741
|
-
success: false,
|
|
742
|
-
error: errorMessage,
|
|
743
|
-
sentAt: /* @__PURE__ */ new Date()
|
|
744
|
-
};
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
|
-
/**
|
|
748
|
-
* Retry sending a failed message
|
|
749
|
-
*/
|
|
750
|
-
async retrySend(options) {
|
|
751
|
-
if (this.sendStatus !== "failed") {
|
|
752
|
-
return {
|
|
753
|
-
success: false,
|
|
754
|
-
error: `Cannot retry: message status is '${this.sendStatus}', expected 'failed'`,
|
|
755
|
-
sentAt: /* @__PURE__ */ new Date()
|
|
756
|
-
};
|
|
757
|
-
}
|
|
758
|
-
if (this.retryCount >= this.maxRetries) {
|
|
759
|
-
return {
|
|
760
|
-
success: false,
|
|
761
|
-
error: `Retry budget exhausted (${this.retryCount}/${this.maxRetries})`,
|
|
762
|
-
sentAt: /* @__PURE__ */ new Date()
|
|
763
|
-
};
|
|
764
|
-
}
|
|
765
|
-
this.retryCount++;
|
|
766
|
-
this.updatedAt = /* @__PURE__ */ new Date();
|
|
767
|
-
await this.save();
|
|
768
|
-
return this.send(options);
|
|
769
|
-
}
|
|
770
|
-
/**
|
|
771
|
-
* Options for a derived draft (reply/forward) built from this message. Carries
|
|
772
|
-
* the DB connection + tenant context from `this.options`, but strips this
|
|
773
|
-
* message's own identity fields. When this message was hydrated from the DB,
|
|
774
|
-
* `this.options` holds the row's `id`/`slug`/`context`/`_skipLoad`; spreading
|
|
775
|
-
* those into a new draft would make `draft.save()` upsert onto the natural-key
|
|
776
|
-
* conflict columns (`slug`/`context`/`_meta_type`) and overwrite the ORIGINAL
|
|
777
|
-
* message instead of inserting a new row. See EmailAccount.childOptions().
|
|
778
|
-
*/
|
|
779
|
-
draftOptions() {
|
|
780
|
-
const rest = { ...this.options };
|
|
781
|
-
delete rest.id;
|
|
782
|
-
delete rest.slug;
|
|
783
|
-
delete rest.context;
|
|
784
|
-
delete rest._skipLoad;
|
|
785
|
-
return rest;
|
|
786
|
-
}
|
|
787
|
-
/**
|
|
788
|
-
* Create a reply to this message (returns unsaved draft)
|
|
789
|
-
*/
|
|
790
|
-
createReply(_options) {
|
|
791
|
-
const reply = new this.constructor({
|
|
792
|
-
...this.draftOptions(),
|
|
793
|
-
id: void 0,
|
|
794
|
-
accountId: this.accountId,
|
|
795
|
-
threadId: this.threadId || this.id || "",
|
|
796
|
-
subject: this.subject.startsWith("Re:") ? this.subject : `Re: ${this.subject}`,
|
|
797
|
-
toAddresses: JSON.stringify([
|
|
798
|
-
{ address: this.fromAddress, name: this.fromName }
|
|
799
|
-
]),
|
|
800
|
-
fromAddress: "",
|
|
801
|
-
fromName: "",
|
|
802
|
-
body: this.buildQuotedBody(),
|
|
803
|
-
inReplyToMessageId: this.id || "",
|
|
804
|
-
sendStatus: "draft",
|
|
805
|
-
isRead: true,
|
|
806
|
-
date: null,
|
|
807
|
-
createdAt: void 0,
|
|
808
|
-
updatedAt: void 0
|
|
809
|
-
});
|
|
810
|
-
return reply;
|
|
811
|
-
}
|
|
812
|
-
/**
|
|
813
|
-
* Create a forward of this message (returns unsaved draft)
|
|
814
|
-
*/
|
|
815
|
-
createForward() {
|
|
816
|
-
const forward = new this.constructor({
|
|
817
|
-
...this.draftOptions(),
|
|
818
|
-
id: void 0,
|
|
819
|
-
accountId: this.accountId,
|
|
820
|
-
threadId: "",
|
|
821
|
-
subject: this.subject.startsWith("Fwd:") ? this.subject : `Fwd: ${this.subject}`,
|
|
822
|
-
toAddresses: "[]",
|
|
823
|
-
fromAddress: "",
|
|
824
|
-
fromName: "",
|
|
825
|
-
body: this.buildQuotedBody(),
|
|
826
|
-
hasAttachments: this.hasAttachments,
|
|
827
|
-
inReplyToMessageId: "",
|
|
828
|
-
sendStatus: "draft",
|
|
829
|
-
isRead: true,
|
|
830
|
-
date: null,
|
|
831
|
-
createdAt: void 0,
|
|
832
|
-
updatedAt: void 0
|
|
833
|
-
});
|
|
834
|
-
return forward;
|
|
835
|
-
}
|
|
836
|
-
/**
|
|
837
|
-
* Build quoted body for reply/forward
|
|
838
|
-
*/
|
|
839
|
-
buildQuotedBody() {
|
|
840
|
-
const dateStr = this.date ? this.date.toLocaleString() : "unknown date";
|
|
841
|
-
const from = this.fromName ? `${this.fromName} <${this.fromAddress}>` : this.fromAddress;
|
|
842
|
-
const quotedLines = (this.body || "").split("\n").map((line) => `> ${line}`).join("\n");
|
|
843
|
-
return `
|
|
844
|
-
|
|
845
|
-
On ${dateStr}, ${from} wrote:
|
|
846
|
-
${quotedLines}`;
|
|
847
|
-
}
|
|
493
|
+
EmailAccount = __decorateClass$6([smrt({
|
|
494
|
+
tableStrategy: "sti",
|
|
495
|
+
api: { include: [
|
|
496
|
+
"list",
|
|
497
|
+
"get",
|
|
498
|
+
"create",
|
|
499
|
+
"update",
|
|
500
|
+
"delete"
|
|
501
|
+
] },
|
|
502
|
+
mcp: { include: ["list", "get"] },
|
|
503
|
+
cli: true
|
|
504
|
+
})], EmailAccount);
|
|
505
|
+
//#endregion
|
|
506
|
+
//#region src/collections/EmailAccountCollection.ts
|
|
507
|
+
var EmailAccountCollection_exports = /* @__PURE__ */ __exportAll({ EmailAccountCollection: () => EmailAccountCollection });
|
|
508
|
+
var EmailAccountCollection = class extends AccountCollection {
|
|
509
|
+
static _itemClass = EmailAccount;
|
|
510
|
+
/**
|
|
511
|
+
* Get account by email address
|
|
512
|
+
*/
|
|
513
|
+
async getByEmail(email) {
|
|
514
|
+
return (await this.list({ where: { email } }))[0] || null;
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* Get accounts by email provider type
|
|
518
|
+
*/
|
|
519
|
+
async getByEmailProviderType(providerType) {
|
|
520
|
+
return await this.list({ where: { providerType } });
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* Get active email accounts
|
|
524
|
+
*/
|
|
525
|
+
async getActive() {
|
|
526
|
+
return await this.list({ where: { isActive: true } });
|
|
527
|
+
}
|
|
528
|
+
/**
|
|
529
|
+
* Get inactive email accounts
|
|
530
|
+
*/
|
|
531
|
+
async getInactive() {
|
|
532
|
+
return await this.list({ where: { isActive: false } });
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Get accounts that need syncing
|
|
536
|
+
*/
|
|
537
|
+
async getNeedingSync(maxAgeMinutes = 60) {
|
|
538
|
+
const allAccounts = await this.getActive();
|
|
539
|
+
const cutoffTime = /* @__PURE__ */ new Date(Date.now() - maxAgeMinutes * 60 * 1e3);
|
|
540
|
+
return allAccounts.filter((account) => !account.lastSyncAt || account.lastSyncAt < cutoffTime);
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Search email accounts with filters.
|
|
544
|
+
* Alias: `search()` for backward compatibility.
|
|
545
|
+
*/
|
|
546
|
+
async search(query, filters) {
|
|
547
|
+
return this.searchEmailAccounts(query, filters);
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* Get accounts by email provider type.
|
|
551
|
+
* Alias: `getByProviderType()` for backward compatibility.
|
|
552
|
+
*/
|
|
553
|
+
async getByProviderType(providerType) {
|
|
554
|
+
return this.getByEmailProviderType(providerType);
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* Get email account statistics.
|
|
558
|
+
* Alias: `getStats()` for backward compatibility.
|
|
559
|
+
*/
|
|
560
|
+
async getStats() {
|
|
561
|
+
const stats = await this.getEmailStats();
|
|
562
|
+
return {
|
|
563
|
+
total: stats.total,
|
|
564
|
+
active: stats.active,
|
|
565
|
+
inactive: stats.inactive,
|
|
566
|
+
byType: stats.byProvider
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* Search email accounts with filters
|
|
571
|
+
*/
|
|
572
|
+
async searchEmailAccounts(query, filters) {
|
|
573
|
+
let accounts = await this.list({});
|
|
574
|
+
if (query) {
|
|
575
|
+
const lowerQuery = query.toLowerCase();
|
|
576
|
+
accounts = accounts.filter((a) => a.name?.toLowerCase().includes(lowerQuery) || a.email?.toLowerCase().includes(lowerQuery));
|
|
577
|
+
}
|
|
578
|
+
if (filters) {
|
|
579
|
+
if (filters.providerType) accounts = accounts.filter((a) => a.providerType === filters.providerType);
|
|
580
|
+
if (filters.email) {
|
|
581
|
+
const emailLower = filters.email.toLowerCase();
|
|
582
|
+
accounts = accounts.filter((a) => a.email?.toLowerCase().includes(emailLower));
|
|
583
|
+
}
|
|
584
|
+
if (filters.isActive !== void 0) accounts = accounts.filter((a) => a.isActive === filters.isActive);
|
|
585
|
+
}
|
|
586
|
+
return accounts;
|
|
587
|
+
}
|
|
588
|
+
/**
|
|
589
|
+
* Sync all active email accounts
|
|
590
|
+
*/
|
|
591
|
+
async syncAll(options) {
|
|
592
|
+
const results = /* @__PURE__ */ new Map();
|
|
593
|
+
const accounts = await this.getActive();
|
|
594
|
+
for (const account of accounts) {
|
|
595
|
+
const ea = account;
|
|
596
|
+
const accountId = ea.id ?? ea.email ?? "unknown";
|
|
597
|
+
try {
|
|
598
|
+
await ea.syncFrom(options);
|
|
599
|
+
results.set(accountId, { success: true });
|
|
600
|
+
} catch (error) {
|
|
601
|
+
results.set(accountId, {
|
|
602
|
+
success: false,
|
|
603
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
return results;
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Get total unread count across all email accounts
|
|
611
|
+
*/
|
|
612
|
+
async getTotalUnreadCount() {
|
|
613
|
+
const accounts = await this.getActive();
|
|
614
|
+
let total = 0;
|
|
615
|
+
for (const account of accounts) total += await account.getUnreadCount();
|
|
616
|
+
return total;
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Get email account statistics
|
|
620
|
+
*/
|
|
621
|
+
async getEmailStats() {
|
|
622
|
+
const accounts = await this.list({});
|
|
623
|
+
const byProvider = {
|
|
624
|
+
smtp: 0,
|
|
625
|
+
imap: 0,
|
|
626
|
+
pop3: 0,
|
|
627
|
+
gmail: 0
|
|
628
|
+
};
|
|
629
|
+
for (const account of accounts) {
|
|
630
|
+
const pt = account.providerType;
|
|
631
|
+
if (pt in byProvider) byProvider[pt]++;
|
|
632
|
+
}
|
|
633
|
+
return {
|
|
634
|
+
total: accounts.length,
|
|
635
|
+
active: accounts.filter((a) => a.isActive).length,
|
|
636
|
+
inactive: accounts.filter((a) => !a.isActive).length,
|
|
637
|
+
byProvider
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
async findByTenant(tenantId) {
|
|
641
|
+
return this.list({ where: { tenantId } });
|
|
642
|
+
}
|
|
643
|
+
async findGlobal() {
|
|
644
|
+
return queryGlobal(this);
|
|
645
|
+
}
|
|
646
|
+
async findWithGlobals(tenantId) {
|
|
647
|
+
return queryWithGlobals(this, tenantId, "EmailAccount.findWithGlobals");
|
|
648
|
+
}
|
|
848
649
|
};
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
(m) => m.subject?.toLowerCase().includes(lowerQuery) || m.body?.toLowerCase().includes(lowerQuery) || m.fromAddress?.toLowerCase().includes(lowerQuery) || m.fromName?.toLowerCase().includes(lowerQuery)
|
|
878
|
-
);
|
|
879
|
-
}
|
|
880
|
-
if (filters) {
|
|
881
|
-
if (filters.accountIds && filters.accountIds.length > 0) {
|
|
882
|
-
messages = messages.filter(
|
|
883
|
-
(m) => filters.accountIds?.includes(m.accountId)
|
|
884
|
-
);
|
|
885
|
-
}
|
|
886
|
-
if (filters.messageType) {
|
|
887
|
-
messages = messages.filter((m) => {
|
|
888
|
-
const metaType = m._meta_type || "";
|
|
889
|
-
return metaType.includes(filters.messageType);
|
|
890
|
-
});
|
|
891
|
-
}
|
|
892
|
-
if (filters.from) {
|
|
893
|
-
const fromLower = filters.from.toLowerCase();
|
|
894
|
-
messages = messages.filter(
|
|
895
|
-
(m) => m.fromAddress?.toLowerCase().includes(fromLower) || m.fromName?.toLowerCase().includes(fromLower)
|
|
896
|
-
);
|
|
897
|
-
}
|
|
898
|
-
if (filters.to) {
|
|
899
|
-
const toLower = filters.to.toLowerCase();
|
|
900
|
-
messages = messages.filter(
|
|
901
|
-
(m) => m.toAddresses?.toLowerCase().includes(toLower)
|
|
902
|
-
);
|
|
903
|
-
}
|
|
904
|
-
if (filters.isRead !== void 0) {
|
|
905
|
-
messages = messages.filter((m) => m.isRead === filters.isRead);
|
|
906
|
-
}
|
|
907
|
-
if (filters.isFlagged !== void 0) {
|
|
908
|
-
messages = messages.filter((m) => m.isFlagged === filters.isFlagged);
|
|
909
|
-
}
|
|
910
|
-
if (filters.sinceDate) {
|
|
911
|
-
messages = messages.filter(
|
|
912
|
-
(m) => m.date && m.date >= filters.sinceDate
|
|
913
|
-
);
|
|
914
|
-
}
|
|
915
|
-
if (filters.beforeDate) {
|
|
916
|
-
messages = messages.filter(
|
|
917
|
-
(m) => m.date && m.date < filters.beforeDate
|
|
918
|
-
);
|
|
919
|
-
}
|
|
920
|
-
if (filters.query) {
|
|
921
|
-
const q = filters.query.toLowerCase();
|
|
922
|
-
messages = messages.filter(
|
|
923
|
-
(m) => m.subject?.toLowerCase().includes(q) || m.body?.toLowerCase().includes(q)
|
|
924
|
-
);
|
|
925
|
-
}
|
|
926
|
-
}
|
|
927
|
-
return messages;
|
|
928
|
-
}
|
|
929
|
-
/**
|
|
930
|
-
* Get messages by multiple accounts
|
|
931
|
-
*/
|
|
932
|
-
async getByAccounts(accountIds) {
|
|
933
|
-
const allMessages = await this.list({});
|
|
934
|
-
return allMessages.filter((m) => accountIds.includes(m.accountId));
|
|
935
|
-
}
|
|
936
|
-
/**
|
|
937
|
-
* Get messages by STI type.
|
|
938
|
-
*
|
|
939
|
-
* Accepts either the full discriminator (e.g. "@happyvertical/smrt-messages:Email")
|
|
940
|
-
* or the short type name (e.g. "Email").
|
|
941
|
-
*/
|
|
942
|
-
async getByType(messageType) {
|
|
943
|
-
if (messageType.includes(":") || messageType.startsWith("@")) {
|
|
944
|
-
return await this.list({ where: { _meta_type: messageType } });
|
|
945
|
-
}
|
|
946
|
-
const allMessages = await this.list({});
|
|
947
|
-
return allMessages.filter((m) => {
|
|
948
|
-
const metaType = m._meta_type || "";
|
|
949
|
-
return metaType.endsWith(`:${messageType}`);
|
|
950
|
-
});
|
|
951
|
-
}
|
|
952
|
-
/**
|
|
953
|
-
* Get unread messages
|
|
954
|
-
*/
|
|
955
|
-
async getUnread(accountId) {
|
|
956
|
-
const where = { isRead: false };
|
|
957
|
-
if (accountId) {
|
|
958
|
-
where.accountId = accountId;
|
|
959
|
-
}
|
|
960
|
-
return await this.list({ where });
|
|
961
|
-
}
|
|
962
|
-
/**
|
|
963
|
-
* Get flagged messages
|
|
964
|
-
*/
|
|
965
|
-
async getFlagged(accountId) {
|
|
966
|
-
const where = { isFlagged: true };
|
|
967
|
-
if (accountId) {
|
|
968
|
-
where.accountId = accountId;
|
|
969
|
-
}
|
|
970
|
-
return await this.list({ where });
|
|
971
|
-
}
|
|
972
|
-
/**
|
|
973
|
-
* Get recent messages
|
|
974
|
-
*/
|
|
975
|
-
async getRecent(limit = 20, accountId) {
|
|
976
|
-
const allMessages = await this.list({
|
|
977
|
-
where: accountId ? { accountId } : void 0
|
|
978
|
-
});
|
|
979
|
-
return allMessages.sort((a, b) => {
|
|
980
|
-
const dateA = a.date?.getTime() || 0;
|
|
981
|
-
const dateB = b.date?.getTime() || 0;
|
|
982
|
-
return dateB - dateA;
|
|
983
|
-
}).slice(0, limit);
|
|
984
|
-
}
|
|
985
|
-
/**
|
|
986
|
-
* Get messages by thread
|
|
987
|
-
*/
|
|
988
|
-
async getByThread(threadId) {
|
|
989
|
-
return await this.list({ where: { threadId } });
|
|
990
|
-
}
|
|
991
|
-
/**
|
|
992
|
-
* Mark multiple messages as read
|
|
993
|
-
*/
|
|
994
|
-
async markAllRead(messageIds) {
|
|
995
|
-
for (const id of messageIds) {
|
|
996
|
-
const message = await this.get({ id });
|
|
997
|
-
if (message) {
|
|
998
|
-
await message.markRead();
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
1001
|
-
}
|
|
1002
|
-
/**
|
|
1003
|
-
* Get message statistics for an account
|
|
1004
|
-
*/
|
|
1005
|
-
async getAccountStats(accountId) {
|
|
1006
|
-
const messages = await this.list({ where: { accountId } });
|
|
1007
|
-
const byType = {};
|
|
1008
|
-
for (const msg of messages) {
|
|
1009
|
-
const metaType = msg._meta_type || "Unknown";
|
|
1010
|
-
const shortType = metaType.split(":").pop() || metaType;
|
|
1011
|
-
byType[shortType] = (byType[shortType] || 0) + 1;
|
|
1012
|
-
}
|
|
1013
|
-
return {
|
|
1014
|
-
total: messages.length,
|
|
1015
|
-
unread: messages.filter((m) => !m.isRead).length,
|
|
1016
|
-
flagged: messages.filter((m) => m.isFlagged).length,
|
|
1017
|
-
byType
|
|
1018
|
-
};
|
|
1019
|
-
}
|
|
1020
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1021
|
-
// Send / Draft Queries
|
|
1022
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1023
|
-
/**
|
|
1024
|
-
* Get draft messages
|
|
1025
|
-
*/
|
|
1026
|
-
async getDrafts(accountId) {
|
|
1027
|
-
const where = { sendStatus: "draft" };
|
|
1028
|
-
if (accountId) where.accountId = accountId;
|
|
1029
|
-
return await this.list({ where });
|
|
1030
|
-
}
|
|
1031
|
-
/**
|
|
1032
|
-
* Get sent messages
|
|
1033
|
-
*/
|
|
1034
|
-
async getSent(accountId) {
|
|
1035
|
-
const where = { sendStatus: "sent" };
|
|
1036
|
-
if (accountId) where.accountId = accountId;
|
|
1037
|
-
return await this.list({ where });
|
|
1038
|
-
}
|
|
1039
|
-
/**
|
|
1040
|
-
* Get scheduled messages
|
|
1041
|
-
*/
|
|
1042
|
-
async getScheduled(accountId) {
|
|
1043
|
-
const where = { sendStatus: "scheduled" };
|
|
1044
|
-
if (accountId) where.accountId = accountId;
|
|
1045
|
-
return await this.list({ where });
|
|
1046
|
-
}
|
|
1047
|
-
/**
|
|
1048
|
-
* Get messages that failed to send
|
|
1049
|
-
*/
|
|
1050
|
-
async getFailedSends(accountId) {
|
|
1051
|
-
const where = { sendStatus: "failed" };
|
|
1052
|
-
if (accountId) where.accountId = accountId;
|
|
1053
|
-
return await this.list({ where });
|
|
1054
|
-
}
|
|
1055
|
-
/**
|
|
1056
|
-
* Get outbox (pending + sending + scheduled)
|
|
1057
|
-
*/
|
|
1058
|
-
async getOutbox(accountId) {
|
|
1059
|
-
const allMessages = await this.list({
|
|
1060
|
-
where: accountId ? { accountId } : void 0
|
|
1061
|
-
});
|
|
1062
|
-
return allMessages.filter(
|
|
1063
|
-
(m) => m.sendStatus === "pending" || m.sendStatus === "sending" || m.sendStatus === "scheduled"
|
|
1064
|
-
);
|
|
1065
|
-
}
|
|
1066
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1067
|
-
// Tenant Helper Methods
|
|
1068
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1069
|
-
async findByTenant(tenantId2) {
|
|
1070
|
-
return this.list({ where: { tenantId: tenantId2 } });
|
|
1071
|
-
}
|
|
1072
|
-
// Message is the @TenantScoped STI base, so an explicit `tenant_id IS NULL`
|
|
1073
|
-
// filter via list() throws and unflagged raw SQL is blocked under an active
|
|
1074
|
-
// tenant context (#1596). Route through the raw helpers — no `_meta_type`
|
|
1075
|
-
// scope so the base collection still returns ALL message subtypes.
|
|
1076
|
-
async findGlobal() {
|
|
1077
|
-
return queryGlobal(this);
|
|
1078
|
-
}
|
|
1079
|
-
async findWithGlobals(tenantId2) {
|
|
1080
|
-
return queryWithGlobals(this, tenantId2, "Message.findWithGlobals");
|
|
1081
|
-
}
|
|
1082
|
-
}
|
|
1083
|
-
const MessageCollection$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
1084
|
-
__proto__: null,
|
|
1085
|
-
MessageCollection
|
|
1086
|
-
}, Symbol.toStringTag, { value: "Module" }));
|
|
1087
|
-
var __getOwnPropDesc$8 = Object.getOwnPropertyDescriptor;
|
|
1088
|
-
var __decorateClass$8 = (decorators, target, key, kind) => {
|
|
1089
|
-
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$8(target, key) : target;
|
|
1090
|
-
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
1091
|
-
if (decorator = decorators[i])
|
|
1092
|
-
result = decorator(result) || result;
|
|
1093
|
-
return result;
|
|
650
|
+
//#endregion
|
|
651
|
+
//#region src/models/EmailAttachment.ts
|
|
652
|
+
var EmailAttachment = class extends Attachment {
|
|
653
|
+
/**
|
|
654
|
+
* Legacy emailId field — maps to messageId
|
|
655
|
+
*/
|
|
656
|
+
get emailId() {
|
|
657
|
+
return this.messageId;
|
|
658
|
+
}
|
|
659
|
+
set emailId(value) {
|
|
660
|
+
this.messageId = value;
|
|
661
|
+
}
|
|
662
|
+
constructor(options = {}) {
|
|
663
|
+
const mappedOptions = {
|
|
664
|
+
...options,
|
|
665
|
+
messageId: options.emailId || options.messageId || ""
|
|
666
|
+
};
|
|
667
|
+
super(mappedOptions);
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* Get the email this attachment belongs to
|
|
671
|
+
* @deprecated Use getMessage() instead
|
|
672
|
+
*/
|
|
673
|
+
async getEmail() {
|
|
674
|
+
if (!this.messageId) return null;
|
|
675
|
+
const { EmailCollection } = await Promise.resolve().then(() => EmailCollection_exports);
|
|
676
|
+
return await (await EmailCollection.create(this.options)).get({ id: this.messageId });
|
|
677
|
+
}
|
|
1094
678
|
};
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
type: this.providerType,
|
|
1147
|
-
...settings
|
|
1148
|
-
});
|
|
1149
|
-
}
|
|
1150
|
-
/**
|
|
1151
|
-
* Sync emails from the email server to the database
|
|
1152
|
-
*/
|
|
1153
|
-
async syncFrom(options = {}) {
|
|
1154
|
-
const startTime = Date.now();
|
|
1155
|
-
const result = {
|
|
1156
|
-
folders: [],
|
|
1157
|
-
messagesProcessed: 0,
|
|
1158
|
-
messagesDownloaded: 0,
|
|
1159
|
-
messagesSkipped: 0,
|
|
1160
|
-
errors: [],
|
|
1161
|
-
duration: 0
|
|
1162
|
-
};
|
|
1163
|
-
try {
|
|
1164
|
-
const client = await this.createClient();
|
|
1165
|
-
await client.connect();
|
|
1166
|
-
const foldersToSync = options.folders || ["INBOX"];
|
|
1167
|
-
result.folders = foldersToSync;
|
|
1168
|
-
const { EmailCollection: EmailCollection2 } = await Promise.resolve().then(() => EmailCollection$1);
|
|
1169
|
-
const { EmailFolderCollection: EmailFolderCollection2 } = await Promise.resolve().then(() => EmailFolderCollection$1);
|
|
1170
|
-
const emailCollection = await EmailCollection2.create(this.options);
|
|
1171
|
-
const folderCollection = await EmailFolderCollection2.create(this.options);
|
|
1172
|
-
if (!this.id) {
|
|
1173
|
-
throw new Error(
|
|
1174
|
-
"EmailAccount.syncFrom requires a persisted account (missing id)"
|
|
1175
|
-
);
|
|
1176
|
-
}
|
|
1177
|
-
const accountId = this.id;
|
|
1178
|
-
for (const folderName of foldersToSync) {
|
|
1179
|
-
try {
|
|
1180
|
-
let folder = await folderCollection.getByPath(accountId, folderName);
|
|
1181
|
-
if (!folder) {
|
|
1182
|
-
const { EmailFolder: EmailFolder2 } = await Promise.resolve().then(() => EmailFolder$1);
|
|
1183
|
-
folder = new EmailFolder2({
|
|
1184
|
-
...this.childOptions(),
|
|
1185
|
-
accountId,
|
|
1186
|
-
name: folderName,
|
|
1187
|
-
path: folderName
|
|
1188
|
-
});
|
|
1189
|
-
await folder.initialize();
|
|
1190
|
-
await folder.save();
|
|
1191
|
-
}
|
|
1192
|
-
const fetchOptions = {
|
|
1193
|
-
folder: folderName,
|
|
1194
|
-
limit: options.batchSize || 100
|
|
1195
|
-
};
|
|
1196
|
-
if (options.since) {
|
|
1197
|
-
fetchOptions.since = options.since;
|
|
1198
|
-
}
|
|
1199
|
-
if (options.before) {
|
|
1200
|
-
fetchOptions.before = options.before;
|
|
1201
|
-
}
|
|
1202
|
-
const messages = await client.fetch(fetchOptions);
|
|
1203
|
-
for (const msg of messages) {
|
|
1204
|
-
result.messagesProcessed++;
|
|
1205
|
-
try {
|
|
1206
|
-
const existingEmail = await emailCollection.getByMessageId(
|
|
1207
|
-
accountId,
|
|
1208
|
-
msg.messageId || ""
|
|
1209
|
-
);
|
|
1210
|
-
if (existingEmail && !options.fullSync) {
|
|
1211
|
-
result.messagesSkipped++;
|
|
1212
|
-
continue;
|
|
1213
|
-
}
|
|
1214
|
-
const { Email: Email2 } = await Promise.resolve().then(() => Email$1);
|
|
1215
|
-
let email = existingEmail;
|
|
1216
|
-
if (!email) {
|
|
1217
|
-
email = new Email2({
|
|
1218
|
-
...this.childOptions(),
|
|
1219
|
-
accountId
|
|
1220
|
-
});
|
|
1221
|
-
await email.initialize();
|
|
1222
|
-
}
|
|
1223
|
-
email.messageId = msg.messageId || "";
|
|
1224
|
-
email.threadId = msg.threadId || "";
|
|
1225
|
-
email.inReplyTo = msg.inReplyTo || "";
|
|
1226
|
-
email.fromAddress = msg.from?.address || "";
|
|
1227
|
-
email.fromName = msg.from?.name || "";
|
|
1228
|
-
email.toAddresses = JSON.stringify(msg.to || []);
|
|
1229
|
-
email.ccAddresses = JSON.stringify(msg.cc || []);
|
|
1230
|
-
email.bccAddresses = JSON.stringify(msg.bcc || []);
|
|
1231
|
-
email.replyToAddress = msg.replyTo?.address || "";
|
|
1232
|
-
email.replyToName = msg.replyTo?.name || "";
|
|
1233
|
-
email.subject = msg.subject || "";
|
|
1234
|
-
email.date = msg.date || null;
|
|
1235
|
-
email.textBody = msg.text || "";
|
|
1236
|
-
email.htmlBody = msg.html || "";
|
|
1237
|
-
email.body = msg.text || "";
|
|
1238
|
-
email.folderId = folder.id ?? "";
|
|
1239
|
-
email.folderPath = folderName;
|
|
1240
|
-
email.labels = JSON.stringify(msg.labels || []);
|
|
1241
|
-
email.flags = JSON.stringify(msg.flags || []);
|
|
1242
|
-
email.hasAttachments = msg.attachments && msg.attachments.length > 0 || false;
|
|
1243
|
-
email.size = msg.size || 0;
|
|
1244
|
-
email.headers = JSON.stringify(msg.headers || {});
|
|
1245
|
-
email.updatedAt = /* @__PURE__ */ new Date();
|
|
1246
|
-
await email.save();
|
|
1247
|
-
result.messagesDownloaded++;
|
|
1248
|
-
if (options.onProgress) {
|
|
1249
|
-
options.onProgress({
|
|
1250
|
-
folder: folderName,
|
|
1251
|
-
processed: result.messagesProcessed,
|
|
1252
|
-
total: messages.length,
|
|
1253
|
-
downloaded: result.messagesDownloaded,
|
|
1254
|
-
skipped: result.messagesSkipped,
|
|
1255
|
-
errors: result.errors.length
|
|
1256
|
-
});
|
|
1257
|
-
}
|
|
1258
|
-
} catch (error) {
|
|
1259
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
1260
|
-
result.errors.push(err);
|
|
1261
|
-
if (options.onError) {
|
|
1262
|
-
options.onError(err, msg);
|
|
1263
|
-
}
|
|
1264
|
-
}
|
|
1265
|
-
}
|
|
1266
|
-
const folderId = folder.id ?? "";
|
|
1267
|
-
folder.messageCount = await emailCollection.countByFolder(folderId);
|
|
1268
|
-
folder.unreadCount = await emailCollection.countUnreadByFolder(folderId);
|
|
1269
|
-
await folder.save();
|
|
1270
|
-
} catch (error) {
|
|
1271
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
1272
|
-
result.errors.push(err);
|
|
1273
|
-
if (options.onError) {
|
|
1274
|
-
options.onError(err);
|
|
1275
|
-
}
|
|
1276
|
-
}
|
|
1277
|
-
}
|
|
1278
|
-
this.lastSyncAt = /* @__PURE__ */ new Date();
|
|
1279
|
-
this.updatedAt = /* @__PURE__ */ new Date();
|
|
1280
|
-
await this.save();
|
|
1281
|
-
await client.disconnect();
|
|
1282
|
-
} catch (error) {
|
|
1283
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
1284
|
-
result.errors.push(err);
|
|
1285
|
-
if (options.onError) {
|
|
1286
|
-
options.onError(err);
|
|
1287
|
-
}
|
|
1288
|
-
}
|
|
1289
|
-
result.duration = Date.now() - startTime;
|
|
1290
|
-
return result;
|
|
1291
|
-
}
|
|
1292
|
-
/**
|
|
1293
|
-
* Get all folders for this account
|
|
1294
|
-
*/
|
|
1295
|
-
async getFolders() {
|
|
1296
|
-
const { EmailFolderCollection: EmailFolderCollection2 } = await Promise.resolve().then(() => EmailFolderCollection$1);
|
|
1297
|
-
const collection = await EmailFolderCollection2.create(this.options);
|
|
1298
|
-
return await collection.list({ where: { accountId: this.id } });
|
|
1299
|
-
}
|
|
1300
|
-
/**
|
|
1301
|
-
* Get all emails for this account
|
|
1302
|
-
*/
|
|
1303
|
-
async getEmails(limit) {
|
|
1304
|
-
const { EmailCollection: EmailCollection2 } = await Promise.resolve().then(() => EmailCollection$1);
|
|
1305
|
-
const collection = await EmailCollection2.create(this.options);
|
|
1306
|
-
const options = {
|
|
1307
|
-
where: { accountId: this.id }
|
|
1308
|
-
};
|
|
1309
|
-
if (limit) {
|
|
1310
|
-
options.limit = limit;
|
|
1311
|
-
}
|
|
1312
|
-
return await collection.list(options);
|
|
1313
|
-
}
|
|
1314
|
-
/**
|
|
1315
|
-
* Get unread email count
|
|
1316
|
-
*/
|
|
1317
|
-
async getUnreadCount() {
|
|
1318
|
-
const { EmailCollection: EmailCollection2 } = await Promise.resolve().then(() => EmailCollection$1);
|
|
1319
|
-
const collection = await EmailCollection2.create(this.options);
|
|
1320
|
-
return await collection.countUnreadByAccount(this.id ?? "");
|
|
1321
|
-
}
|
|
1322
|
-
/**
|
|
1323
|
-
* Store credentials securely using smrt-secrets (email-specific)
|
|
1324
|
-
*/
|
|
1325
|
-
async setCredentials(credentials, options = {}) {
|
|
1326
|
-
const { SecretService } = await import("@happyvertical/smrt-secrets");
|
|
1327
|
-
const secretService = await SecretService.create({ db: this.db });
|
|
1328
|
-
const secretName = `email-account-${this.id}`;
|
|
1329
|
-
const secretValue = JSON.stringify(credentials);
|
|
1330
|
-
if (this.credentialSecretId) {
|
|
1331
|
-
await secretService.store(this.credentialSecretId, secretValue, {
|
|
1332
|
-
description: options.description || `IMAP credentials for ${this.name}`,
|
|
1333
|
-
category: options.category || "email"
|
|
1334
|
-
});
|
|
1335
|
-
} else {
|
|
1336
|
-
await secretService.store(secretName, secretValue, {
|
|
1337
|
-
description: options.description || `IMAP credentials for ${this.name}`,
|
|
1338
|
-
category: options.category || "email"
|
|
1339
|
-
});
|
|
1340
|
-
this.credentialSecretId = secretName;
|
|
1341
|
-
this.updatedAt = /* @__PURE__ */ new Date();
|
|
1342
|
-
await this.save();
|
|
1343
|
-
}
|
|
1344
|
-
}
|
|
1345
|
-
/**
|
|
1346
|
-
* Sync all active email accounts (for job runner)
|
|
1347
|
-
*
|
|
1348
|
-
* NOTE: This is a class-wide operation, not per-instance. It syncs ALL active
|
|
1349
|
-
* accounts, ignoring `this` instance. It exists as an instance method because
|
|
1350
|
-
* the TaskRunner dispatches via `objectType: 'EmailAccount', method: 'syncAll'`
|
|
1351
|
-
* which requires an instance method signature.
|
|
1352
|
-
*/
|
|
1353
|
-
async syncAll(args) {
|
|
1354
|
-
const { EmailAccountCollection: EmailAccountCollection2 } = await Promise.resolve().then(() => EmailAccountCollection$1);
|
|
1355
|
-
const collection = await EmailAccountCollection2.create({
|
|
1356
|
-
db: this.db
|
|
1357
|
-
});
|
|
1358
|
-
return collection.syncAll(args);
|
|
1359
|
-
}
|
|
1360
|
-
/**
|
|
1361
|
-
* Migrate from plain-text settings to secrets
|
|
1362
|
-
*/
|
|
1363
|
-
async migrateToSecrets() {
|
|
1364
|
-
if (this.credentialSecretId) {
|
|
1365
|
-
return;
|
|
1366
|
-
}
|
|
1367
|
-
const settings = this.getSettings();
|
|
1368
|
-
if (Object.keys(settings).length === 0) {
|
|
1369
|
-
return;
|
|
1370
|
-
}
|
|
1371
|
-
await this.setCredentials(settings, {
|
|
1372
|
-
description: `Migrated IMAP credentials for ${this.name}`
|
|
1373
|
-
});
|
|
1374
|
-
}
|
|
679
|
+
//#endregion
|
|
680
|
+
//#region src/collections/EmailAttachmentCollection.ts
|
|
681
|
+
var EmailAttachmentCollection = class extends AttachmentCollection {
|
|
682
|
+
static _itemClass = EmailAttachment;
|
|
683
|
+
/**
|
|
684
|
+
* Get attachments for an email
|
|
685
|
+
* @deprecated Use getByMessage() instead
|
|
686
|
+
*/
|
|
687
|
+
async getByEmail(emailId) {
|
|
688
|
+
return await this.getByMessage(emailId);
|
|
689
|
+
}
|
|
690
|
+
/**
|
|
691
|
+
* Get image attachments
|
|
692
|
+
*/
|
|
693
|
+
async getImages(emailId) {
|
|
694
|
+
return await super.getImages(emailId);
|
|
695
|
+
}
|
|
696
|
+
/**
|
|
697
|
+
* Get PDF attachments
|
|
698
|
+
*/
|
|
699
|
+
async getPdfs(emailId) {
|
|
700
|
+
return await super.getPdfs(emailId);
|
|
701
|
+
}
|
|
702
|
+
/**
|
|
703
|
+
* Get inline attachments
|
|
704
|
+
*/
|
|
705
|
+
async getInline(emailId) {
|
|
706
|
+
return await super.getInline(emailId);
|
|
707
|
+
}
|
|
708
|
+
/**
|
|
709
|
+
* Get regular attachments
|
|
710
|
+
*/
|
|
711
|
+
async getRegular(emailId) {
|
|
712
|
+
return await super.getRegular(emailId);
|
|
713
|
+
}
|
|
714
|
+
/**
|
|
715
|
+
* Delete all attachments for an email
|
|
716
|
+
* @deprecated Use deleteByMessage() instead
|
|
717
|
+
*/
|
|
718
|
+
async deleteByEmail(emailId) {
|
|
719
|
+
return await this.deleteByMessage(emailId);
|
|
720
|
+
}
|
|
721
|
+
async findByTenant(tenantId) {
|
|
722
|
+
return this.list({ where: { tenantId } });
|
|
723
|
+
}
|
|
724
|
+
async findGlobal() {
|
|
725
|
+
return queryGlobal(this);
|
|
726
|
+
}
|
|
727
|
+
async findWithGlobals(tenantId) {
|
|
728
|
+
return queryWithGlobals(this, tenantId, "EmailAttachment.findWithGlobals");
|
|
729
|
+
}
|
|
1375
730
|
};
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
tenantId2,
|
|
1550
|
-
"EmailAccount.findWithGlobals"
|
|
1551
|
-
);
|
|
1552
|
-
}
|
|
1553
|
-
}
|
|
1554
|
-
const EmailAccountCollection$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
1555
|
-
__proto__: null,
|
|
1556
|
-
EmailAccountCollection
|
|
1557
|
-
}, Symbol.toStringTag, { value: "Module" }));
|
|
1558
|
-
class EmailAttachment extends Attachment {
|
|
1559
|
-
/**
|
|
1560
|
-
* Legacy emailId field — maps to messageId
|
|
1561
|
-
*/
|
|
1562
|
-
get emailId() {
|
|
1563
|
-
return this.messageId;
|
|
1564
|
-
}
|
|
1565
|
-
set emailId(value) {
|
|
1566
|
-
this.messageId = value;
|
|
1567
|
-
}
|
|
1568
|
-
constructor(options = {}) {
|
|
1569
|
-
const mappedOptions = {
|
|
1570
|
-
...options,
|
|
1571
|
-
messageId: options.emailId || options.messageId || ""
|
|
1572
|
-
};
|
|
1573
|
-
super(mappedOptions);
|
|
1574
|
-
}
|
|
1575
|
-
/**
|
|
1576
|
-
* Get the email this attachment belongs to
|
|
1577
|
-
* @deprecated Use getMessage() instead
|
|
1578
|
-
*/
|
|
1579
|
-
async getEmail() {
|
|
1580
|
-
if (!this.messageId) return null;
|
|
1581
|
-
const { EmailCollection: EmailCollection2 } = await Promise.resolve().then(() => EmailCollection$1);
|
|
1582
|
-
const collection = await EmailCollection2.create(this.options);
|
|
1583
|
-
return await collection.get({ id: this.messageId });
|
|
1584
|
-
}
|
|
1585
|
-
}
|
|
1586
|
-
class EmailAttachmentCollection extends AttachmentCollection {
|
|
1587
|
-
static _itemClass = EmailAttachment;
|
|
1588
|
-
/**
|
|
1589
|
-
* Get attachments for an email
|
|
1590
|
-
* @deprecated Use getByMessage() instead
|
|
1591
|
-
*/
|
|
1592
|
-
async getByEmail(emailId) {
|
|
1593
|
-
return await this.getByMessage(emailId);
|
|
1594
|
-
}
|
|
1595
|
-
/**
|
|
1596
|
-
* Get image attachments
|
|
1597
|
-
*/
|
|
1598
|
-
async getImages(emailId) {
|
|
1599
|
-
return await super.getImages(emailId);
|
|
1600
|
-
}
|
|
1601
|
-
/**
|
|
1602
|
-
* Get PDF attachments
|
|
1603
|
-
*/
|
|
1604
|
-
async getPdfs(emailId) {
|
|
1605
|
-
return await super.getPdfs(emailId);
|
|
1606
|
-
}
|
|
1607
|
-
/**
|
|
1608
|
-
* Get inline attachments
|
|
1609
|
-
*/
|
|
1610
|
-
async getInline(emailId) {
|
|
1611
|
-
return await super.getInline(emailId);
|
|
1612
|
-
}
|
|
1613
|
-
/**
|
|
1614
|
-
* Get regular attachments
|
|
1615
|
-
*/
|
|
1616
|
-
async getRegular(emailId) {
|
|
1617
|
-
return await super.getRegular(emailId);
|
|
1618
|
-
}
|
|
1619
|
-
/**
|
|
1620
|
-
* Delete all attachments for an email
|
|
1621
|
-
* @deprecated Use deleteByMessage() instead
|
|
1622
|
-
*/
|
|
1623
|
-
async deleteByEmail(emailId) {
|
|
1624
|
-
return await this.deleteByMessage(emailId);
|
|
1625
|
-
}
|
|
1626
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1627
|
-
// Tenant Helper Methods
|
|
1628
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1629
|
-
async findByTenant(tenantId2) {
|
|
1630
|
-
return this.list({ where: { tenantId: tenantId2 } });
|
|
1631
|
-
}
|
|
1632
|
-
// EmailAttachment is a CTI subclass of the @TenantScoped Attachment (own
|
|
1633
|
-
// `email_attachments` table, no `_meta_type`) and inherits its recognition
|
|
1634
|
-
// (#1596); route global / cross-global lookups through the raw helpers.
|
|
1635
|
-
async findGlobal() {
|
|
1636
|
-
return queryGlobal(this);
|
|
1637
|
-
}
|
|
1638
|
-
async findWithGlobals(tenantId2) {
|
|
1639
|
-
return queryWithGlobals(
|
|
1640
|
-
this,
|
|
1641
|
-
tenantId2,
|
|
1642
|
-
"EmailAttachment.findWithGlobals"
|
|
1643
|
-
);
|
|
1644
|
-
}
|
|
1645
|
-
}
|
|
1646
|
-
var __getOwnPropDesc$7 = Object.getOwnPropertyDescriptor;
|
|
1647
|
-
var __decorateClass$7 = (decorators, target, key, kind) => {
|
|
1648
|
-
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$7(target, key) : target;
|
|
1649
|
-
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
1650
|
-
if (decorator = decorators[i])
|
|
1651
|
-
result = decorator(result) || result;
|
|
1652
|
-
return result;
|
|
731
|
+
//#endregion
|
|
732
|
+
//#region src/collections/EmailCollection.ts
|
|
733
|
+
var EmailCollection_exports = /* @__PURE__ */ __exportAll({ EmailCollection: () => EmailCollection });
|
|
734
|
+
var EmailCollection = class extends MessageCollection {
|
|
735
|
+
static _itemClass = Email;
|
|
736
|
+
/**
|
|
737
|
+
* Get email by RFC 822 Message-ID
|
|
738
|
+
*/
|
|
739
|
+
async getByMessageId(accountId, messageId) {
|
|
740
|
+
return (await this.list({
|
|
741
|
+
where: {
|
|
742
|
+
accountId,
|
|
743
|
+
messageId
|
|
744
|
+
},
|
|
745
|
+
limit: 1
|
|
746
|
+
}))[0] || null;
|
|
747
|
+
}
|
|
748
|
+
/**
|
|
749
|
+
* Get emails by account
|
|
750
|
+
*/
|
|
751
|
+
async getByAccount(accountId) {
|
|
752
|
+
return await this.list({ where: { accountId } });
|
|
753
|
+
}
|
|
754
|
+
/**
|
|
755
|
+
* Get emails by folder
|
|
756
|
+
*/
|
|
757
|
+
async getByFolder(folderId) {
|
|
758
|
+
return await this.list({ where: { folderId } });
|
|
759
|
+
}
|
|
760
|
+
/**
|
|
761
|
+
* Get emails by thread
|
|
762
|
+
*/
|
|
763
|
+
async getByThread(threadId) {
|
|
764
|
+
return await this.list({ where: { threadId } });
|
|
765
|
+
}
|
|
766
|
+
/**
|
|
767
|
+
* Get unread emails
|
|
768
|
+
*/
|
|
769
|
+
async getUnread(accountId) {
|
|
770
|
+
const where = { isRead: false };
|
|
771
|
+
if (accountId) where.accountId = accountId;
|
|
772
|
+
return await this.list({ where });
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* Get flagged emails
|
|
776
|
+
*/
|
|
777
|
+
async getFlagged(accountId) {
|
|
778
|
+
const where = { isFlagged: true };
|
|
779
|
+
if (accountId) where.accountId = accountId;
|
|
780
|
+
return await this.list({ where });
|
|
781
|
+
}
|
|
782
|
+
/**
|
|
783
|
+
* Get emails with attachments
|
|
784
|
+
*/
|
|
785
|
+
async getWithAttachments(accountId) {
|
|
786
|
+
const where = { hasAttachments: true };
|
|
787
|
+
if (accountId) where.accountId = accountId;
|
|
788
|
+
return await this.list({ where });
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* Get recent emails
|
|
792
|
+
*/
|
|
793
|
+
async getRecent(limit = 20, accountId) {
|
|
794
|
+
return (await this.list({ where: accountId ? { accountId } : void 0 })).sort((a, b) => {
|
|
795
|
+
const dateA = a.date?.getTime() || 0;
|
|
796
|
+
return (b.date?.getTime() || 0) - dateA;
|
|
797
|
+
}).slice(0, limit);
|
|
798
|
+
}
|
|
799
|
+
/**
|
|
800
|
+
* Count emails in a folder
|
|
801
|
+
*/
|
|
802
|
+
async countByFolder(folderId) {
|
|
803
|
+
return (await this.list({ where: { folderId } })).length;
|
|
804
|
+
}
|
|
805
|
+
/**
|
|
806
|
+
* Count unread emails in a folder
|
|
807
|
+
*/
|
|
808
|
+
async countUnreadByFolder(folderId) {
|
|
809
|
+
return (await this.list({ where: {
|
|
810
|
+
folderId,
|
|
811
|
+
isRead: false
|
|
812
|
+
} })).length;
|
|
813
|
+
}
|
|
814
|
+
/**
|
|
815
|
+
* Count unread emails for an account
|
|
816
|
+
*/
|
|
817
|
+
async countUnreadByAccount(accountId) {
|
|
818
|
+
return (await this.list({ where: {
|
|
819
|
+
accountId,
|
|
820
|
+
isRead: false
|
|
821
|
+
} })).length;
|
|
822
|
+
}
|
|
823
|
+
/**
|
|
824
|
+
* Search emails with email-specific filters.
|
|
825
|
+
* Alias: `search()` for backward compatibility.
|
|
826
|
+
*/
|
|
827
|
+
async search(query, filters) {
|
|
828
|
+
return this.searchEmails(query, filters);
|
|
829
|
+
}
|
|
830
|
+
/**
|
|
831
|
+
* Search emails with email-specific filters
|
|
832
|
+
*/
|
|
833
|
+
async searchEmails(query, filters) {
|
|
834
|
+
let emails = await this.list({});
|
|
835
|
+
if (query) {
|
|
836
|
+
const lowerQuery = query.toLowerCase();
|
|
837
|
+
emails = emails.filter((e) => e.subject?.toLowerCase().includes(lowerQuery) || e.textBody?.toLowerCase().includes(lowerQuery) || e.fromAddress?.toLowerCase().includes(lowerQuery) || e.fromName?.toLowerCase().includes(lowerQuery));
|
|
838
|
+
}
|
|
839
|
+
if (filters) {
|
|
840
|
+
if (filters.accountId) emails = emails.filter((e) => e.accountId === filters.accountId);
|
|
841
|
+
if (filters.folderId) emails = emails.filter((e) => e.folderId === filters.folderId);
|
|
842
|
+
if (filters.threadId) emails = emails.filter((e) => e.threadId === filters.threadId);
|
|
843
|
+
if (filters.from) {
|
|
844
|
+
const fromLower = filters.from.toLowerCase();
|
|
845
|
+
emails = emails.filter((e) => e.fromAddress?.toLowerCase().includes(fromLower) || e.fromName?.toLowerCase().includes(fromLower));
|
|
846
|
+
}
|
|
847
|
+
if (filters.to) {
|
|
848
|
+
const toLower = filters.to.toLowerCase();
|
|
849
|
+
emails = emails.filter((e) => e.toAddresses?.toLowerCase().includes(toLower));
|
|
850
|
+
}
|
|
851
|
+
if (filters.subject) {
|
|
852
|
+
const subjectLower = filters.subject.toLowerCase();
|
|
853
|
+
emails = emails.filter((e) => e.subject?.toLowerCase().includes(subjectLower));
|
|
854
|
+
}
|
|
855
|
+
if (filters.isRead !== void 0) emails = emails.filter((e) => e.isRead === filters.isRead);
|
|
856
|
+
if (filters.isFlagged !== void 0) emails = emails.filter((e) => e.isFlagged === filters.isFlagged);
|
|
857
|
+
if (filters.hasAttachments !== void 0) emails = emails.filter((e) => e.hasAttachments === filters.hasAttachments);
|
|
858
|
+
if (filters.sincDate) emails = emails.filter((e) => e.date && e.date >= filters.sincDate);
|
|
859
|
+
if (filters.beforeDate) emails = emails.filter((e) => e.date && e.date < filters.beforeDate);
|
|
860
|
+
}
|
|
861
|
+
return emails;
|
|
862
|
+
}
|
|
863
|
+
/**
|
|
864
|
+
* Mark all emails in a folder as read
|
|
865
|
+
*/
|
|
866
|
+
async markFolderRead(folderId) {
|
|
867
|
+
const folderEmails = (await this.getUnread()).filter((e) => e.folderId === folderId);
|
|
868
|
+
for (const email of folderEmails) await email.markRead();
|
|
869
|
+
}
|
|
870
|
+
/**
|
|
871
|
+
* Delete emails by folder
|
|
872
|
+
*/
|
|
873
|
+
async deleteByFolder(folderId) {
|
|
874
|
+
const emails = await this.getByFolder(folderId);
|
|
875
|
+
let count = 0;
|
|
876
|
+
for (const email of emails) {
|
|
877
|
+
await email.delete();
|
|
878
|
+
count++;
|
|
879
|
+
}
|
|
880
|
+
return count;
|
|
881
|
+
}
|
|
882
|
+
/**
|
|
883
|
+
* Get email statistics for an account
|
|
884
|
+
*/
|
|
885
|
+
async getAccountStats(accountId) {
|
|
886
|
+
const emails = await this.getByAccount(accountId);
|
|
887
|
+
return {
|
|
888
|
+
total: emails.length,
|
|
889
|
+
unread: emails.filter((e) => !e.isRead).length,
|
|
890
|
+
flagged: emails.filter((e) => e.isFlagged).length,
|
|
891
|
+
withAttachments: emails.filter((e) => e.hasAttachments).length,
|
|
892
|
+
byType: { Email: emails.length }
|
|
893
|
+
};
|
|
894
|
+
}
|
|
895
|
+
async findByTenant(tenantId) {
|
|
896
|
+
return this.list({ where: { tenantId } });
|
|
897
|
+
}
|
|
898
|
+
async findGlobal() {
|
|
899
|
+
return queryGlobal(this);
|
|
900
|
+
}
|
|
901
|
+
async findWithGlobals(tenantId) {
|
|
902
|
+
return queryWithGlobals(this, tenantId, "Email.findWithGlobals");
|
|
903
|
+
}
|
|
1653
904
|
};
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
return refs.split(/\s+/).filter(Boolean);
|
|
1824
|
-
}
|
|
1825
|
-
/**
|
|
1826
|
-
* Create a reply to this email with RFC 822 threading
|
|
1827
|
-
*/
|
|
1828
|
-
createReply(options) {
|
|
1829
|
-
const reply = new Email({
|
|
1830
|
-
...this.draftOptions(),
|
|
1831
|
-
id: void 0,
|
|
1832
|
-
accountId: this.accountId,
|
|
1833
|
-
threadId: this.threadId || this.id || void 0,
|
|
1834
|
-
subject: this.subject.startsWith("Re:") ? this.subject : `Re: ${this.subject}`,
|
|
1835
|
-
fromAddress: "",
|
|
1836
|
-
fromName: "",
|
|
1837
|
-
inReplyToMessageId: this.id || void 0,
|
|
1838
|
-
sendStatus: "draft",
|
|
1839
|
-
isRead: true,
|
|
1840
|
-
isDraft: true,
|
|
1841
|
-
date: null,
|
|
1842
|
-
createdAt: void 0,
|
|
1843
|
-
updatedAt: void 0,
|
|
1844
|
-
// RFC 822 threading
|
|
1845
|
-
inReplyTo: this.messageId,
|
|
1846
|
-
// To: original sender
|
|
1847
|
-
toAddresses: JSON.stringify([
|
|
1848
|
-
{ address: this.fromAddress, name: this.fromName }
|
|
1849
|
-
])
|
|
1850
|
-
});
|
|
1851
|
-
const refs = [...this.getReferences()];
|
|
1852
|
-
if (this.messageId && !refs.includes(this.messageId)) {
|
|
1853
|
-
refs.push(this.messageId);
|
|
1854
|
-
}
|
|
1855
|
-
reply.setHeaders({ references: refs.join(" ") });
|
|
1856
|
-
if (options?.replyAll) {
|
|
1857
|
-
const allRecipients = [
|
|
1858
|
-
...this.getToAddresses(),
|
|
1859
|
-
...this.getCcAddresses()
|
|
1860
|
-
];
|
|
1861
|
-
const seen = /* @__PURE__ */ new Set([this.fromAddress.toLowerCase()]);
|
|
1862
|
-
const ccAddresses = allRecipients.filter((r) => {
|
|
1863
|
-
const addr = r.address.toLowerCase();
|
|
1864
|
-
if (seen.has(addr)) return false;
|
|
1865
|
-
seen.add(addr);
|
|
1866
|
-
return true;
|
|
1867
|
-
});
|
|
1868
|
-
reply.ccAddresses = JSON.stringify(ccAddresses);
|
|
1869
|
-
}
|
|
1870
|
-
reply.body = this.buildQuotedBody();
|
|
1871
|
-
reply.textBody = reply.body;
|
|
1872
|
-
return reply;
|
|
1873
|
-
}
|
|
1874
|
-
/**
|
|
1875
|
-
* Create a forward of this email
|
|
1876
|
-
*/
|
|
1877
|
-
createForward() {
|
|
1878
|
-
const forwardBody = this.buildForwardBody();
|
|
1879
|
-
const forward = new Email({
|
|
1880
|
-
...this.draftOptions(),
|
|
1881
|
-
id: void 0,
|
|
1882
|
-
accountId: this.accountId,
|
|
1883
|
-
threadId: "",
|
|
1884
|
-
subject: this.subject.startsWith("Fwd:") ? this.subject : `Fwd: ${this.subject}`,
|
|
1885
|
-
toAddresses: "[]",
|
|
1886
|
-
ccAddresses: "[]",
|
|
1887
|
-
bccAddresses: "[]",
|
|
1888
|
-
fromAddress: "",
|
|
1889
|
-
fromName: "",
|
|
1890
|
-
body: forwardBody,
|
|
1891
|
-
textBody: forwardBody,
|
|
1892
|
-
hasAttachments: this.hasAttachments,
|
|
1893
|
-
inReplyToMessageId: "",
|
|
1894
|
-
inReplyTo: "",
|
|
1895
|
-
sendStatus: "draft",
|
|
1896
|
-
isDraft: true,
|
|
1897
|
-
isRead: true,
|
|
1898
|
-
date: null,
|
|
1899
|
-
createdAt: void 0,
|
|
1900
|
-
updatedAt: void 0
|
|
1901
|
-
});
|
|
1902
|
-
return forward;
|
|
1903
|
-
}
|
|
1904
|
-
/**
|
|
1905
|
-
* Build email-specific quoted body for replies
|
|
1906
|
-
*/
|
|
1907
|
-
buildQuotedBody() {
|
|
1908
|
-
const dateStr = this.date ? this.date.toLocaleString() : "unknown date";
|
|
1909
|
-
const from = this.fromName ? `${this.fromName} <${this.fromAddress}>` : this.fromAddress;
|
|
1910
|
-
const bodyText = this.textBody || this.body || "";
|
|
1911
|
-
const quotedLines = bodyText.split("\n").map((line) => `> ${line}`).join("\n");
|
|
1912
|
-
return `
|
|
1913
|
-
|
|
1914
|
-
On ${dateStr}, ${from} wrote:
|
|
1915
|
-
${quotedLines}`;
|
|
1916
|
-
}
|
|
1917
|
-
/**
|
|
1918
|
-
* Build forwarded message body
|
|
1919
|
-
*/
|
|
1920
|
-
buildForwardBody() {
|
|
1921
|
-
const dateStr = this.date ? this.date.toLocaleString() : "unknown date";
|
|
1922
|
-
const from = this.fromName ? `${this.fromName} <${this.fromAddress}>` : this.fromAddress;
|
|
1923
|
-
const toStr = this.getToAddresses().map((r) => r.name ? `${r.name} <${r.address}>` : r.address).join(", ");
|
|
1924
|
-
const bodyText = this.textBody || this.body || "";
|
|
1925
|
-
return [
|
|
1926
|
-
"",
|
|
1927
|
-
"",
|
|
1928
|
-
"---------- Forwarded message ----------",
|
|
1929
|
-
`From: ${from}`,
|
|
1930
|
-
`Date: ${dateStr}`,
|
|
1931
|
-
`Subject: ${this.subject}`,
|
|
1932
|
-
`To: ${toStr}`,
|
|
1933
|
-
"",
|
|
1934
|
-
bodyText
|
|
1935
|
-
].join("\n");
|
|
1936
|
-
}
|
|
905
|
+
//#endregion
|
|
906
|
+
//#region src/collections/EmailFolderCollection.ts
|
|
907
|
+
var EmailFolderCollection_exports = /* @__PURE__ */ __exportAll({ EmailFolderCollection: () => EmailFolderCollection });
|
|
908
|
+
var EmailFolderCollection = class extends SmrtCollection {
|
|
909
|
+
static _itemClass = EmailFolder;
|
|
910
|
+
/**
|
|
911
|
+
* Get folder by path for an account
|
|
912
|
+
*/
|
|
913
|
+
async getByPath(accountId, path) {
|
|
914
|
+
return (await this.list({ where: {
|
|
915
|
+
accountId,
|
|
916
|
+
path
|
|
917
|
+
} }))[0] || null;
|
|
918
|
+
}
|
|
919
|
+
/**
|
|
920
|
+
* Get folders by account
|
|
921
|
+
*/
|
|
922
|
+
async getByAccount(accountId) {
|
|
923
|
+
return await this.list({ where: { accountId } });
|
|
924
|
+
}
|
|
925
|
+
/**
|
|
926
|
+
* Get inbox folder for an account
|
|
927
|
+
*/
|
|
928
|
+
async getInbox(accountId) {
|
|
929
|
+
return (await this.getByAccount(accountId)).find((f) => f.isInbox()) || null;
|
|
930
|
+
}
|
|
931
|
+
/**
|
|
932
|
+
* Get sent folder for an account
|
|
933
|
+
*/
|
|
934
|
+
async getSent(accountId) {
|
|
935
|
+
return (await this.getByAccount(accountId)).find((f) => f.isSent()) || null;
|
|
936
|
+
}
|
|
937
|
+
/**
|
|
938
|
+
* Get drafts folder for an account
|
|
939
|
+
*/
|
|
940
|
+
async getDrafts(accountId) {
|
|
941
|
+
return (await this.getByAccount(accountId)).find((f) => f.isDrafts()) || null;
|
|
942
|
+
}
|
|
943
|
+
/**
|
|
944
|
+
* Get trash folder for an account
|
|
945
|
+
*/
|
|
946
|
+
async getTrash(accountId) {
|
|
947
|
+
return (await this.getByAccount(accountId)).find((f) => f.isTrash()) || null;
|
|
948
|
+
}
|
|
949
|
+
/**
|
|
950
|
+
* Get spam folder for an account
|
|
951
|
+
*/
|
|
952
|
+
async getSpam(accountId) {
|
|
953
|
+
return (await this.getByAccount(accountId)).find((f) => f.isSpam()) || null;
|
|
954
|
+
}
|
|
955
|
+
/**
|
|
956
|
+
* Get system folders for an account
|
|
957
|
+
*/
|
|
958
|
+
async getSystemFolders(accountId) {
|
|
959
|
+
return (await this.getByAccount(accountId)).filter((f) => f.isSystemFolder());
|
|
960
|
+
}
|
|
961
|
+
/**
|
|
962
|
+
* Get user-created folders for an account
|
|
963
|
+
*/
|
|
964
|
+
async getUserFolders(accountId) {
|
|
965
|
+
return (await this.getByAccount(accountId)).filter((f) => !f.isSystemFolder());
|
|
966
|
+
}
|
|
967
|
+
/**
|
|
968
|
+
* Get subscribed folders
|
|
969
|
+
*/
|
|
970
|
+
async getSubscribed(accountId) {
|
|
971
|
+
const where = { subscribed: true };
|
|
972
|
+
if (accountId) where.accountId = accountId;
|
|
973
|
+
return await this.list({ where });
|
|
974
|
+
}
|
|
975
|
+
/**
|
|
976
|
+
* Get folders with unread messages
|
|
977
|
+
*/
|
|
978
|
+
async getWithUnread(accountId) {
|
|
979
|
+
return (accountId ? await this.getByAccount(accountId) : await this.list({})).filter((f) => f.unreadCount > 0);
|
|
980
|
+
}
|
|
981
|
+
/**
|
|
982
|
+
* Search folders with filters
|
|
983
|
+
*/
|
|
984
|
+
async search(query, filters) {
|
|
985
|
+
let folders = await this.list({});
|
|
986
|
+
if (query) {
|
|
987
|
+
const lowerQuery = query.toLowerCase();
|
|
988
|
+
folders = folders.filter((f) => f.name?.toLowerCase().includes(lowerQuery) || f.path?.toLowerCase().includes(lowerQuery));
|
|
989
|
+
}
|
|
990
|
+
if (filters) {
|
|
991
|
+
if (filters.accountId) folders = folders.filter((f) => f.accountId === filters.accountId);
|
|
992
|
+
if (filters.specialUse) folders = folders.filter((f) => f.specialUse === filters.specialUse);
|
|
993
|
+
if (filters.subscribed !== void 0) folders = folders.filter((f) => f.subscribed === filters.subscribed);
|
|
994
|
+
}
|
|
995
|
+
return folders;
|
|
996
|
+
}
|
|
997
|
+
/**
|
|
998
|
+
* Refresh counts for all folders in an account
|
|
999
|
+
*/
|
|
1000
|
+
async refreshAllCounts(accountId) {
|
|
1001
|
+
const folders = await this.getByAccount(accountId);
|
|
1002
|
+
for (const folder of folders) await folder.refreshCounts();
|
|
1003
|
+
}
|
|
1004
|
+
/**
|
|
1005
|
+
* Get folder statistics for an account
|
|
1006
|
+
*/
|
|
1007
|
+
async getAccountStats(accountId) {
|
|
1008
|
+
const folders = await this.getByAccount(accountId);
|
|
1009
|
+
return {
|
|
1010
|
+
totalFolders: folders.length,
|
|
1011
|
+
totalMessages: folders.reduce((sum, f) => sum + f.messageCount, 0),
|
|
1012
|
+
totalUnread: folders.reduce((sum, f) => sum + f.unreadCount, 0),
|
|
1013
|
+
systemFolders: folders.filter((f) => f.isSystemFolder()).length,
|
|
1014
|
+
userFolders: folders.filter((f) => !f.isSystemFolder()).length
|
|
1015
|
+
};
|
|
1016
|
+
}
|
|
1017
|
+
/**
|
|
1018
|
+
* Create standard system folders for an account
|
|
1019
|
+
*/
|
|
1020
|
+
async createSystemFolders(accountId) {
|
|
1021
|
+
for (const folderData of [
|
|
1022
|
+
{
|
|
1023
|
+
name: "INBOX",
|
|
1024
|
+
path: "INBOX",
|
|
1025
|
+
specialUse: "\\Inbox"
|
|
1026
|
+
},
|
|
1027
|
+
{
|
|
1028
|
+
name: "Sent",
|
|
1029
|
+
path: "Sent",
|
|
1030
|
+
specialUse: "\\Sent"
|
|
1031
|
+
},
|
|
1032
|
+
{
|
|
1033
|
+
name: "Drafts",
|
|
1034
|
+
path: "Drafts",
|
|
1035
|
+
specialUse: "\\Drafts"
|
|
1036
|
+
},
|
|
1037
|
+
{
|
|
1038
|
+
name: "Trash",
|
|
1039
|
+
path: "Trash",
|
|
1040
|
+
specialUse: "\\Trash"
|
|
1041
|
+
},
|
|
1042
|
+
{
|
|
1043
|
+
name: "Spam",
|
|
1044
|
+
path: "Spam",
|
|
1045
|
+
specialUse: "\\Junk"
|
|
1046
|
+
}
|
|
1047
|
+
]) if (!await this.getByPath(accountId, folderData.path)) await (await this.create({
|
|
1048
|
+
accountId,
|
|
1049
|
+
...folderData
|
|
1050
|
+
})).save();
|
|
1051
|
+
}
|
|
1052
|
+
/**
|
|
1053
|
+
* Find all email folders belonging to a specific tenant
|
|
1054
|
+
*/
|
|
1055
|
+
async findByTenant(tenantId) {
|
|
1056
|
+
return this.list({ where: { tenantId } });
|
|
1057
|
+
}
|
|
1058
|
+
/**
|
|
1059
|
+
* Find all global email folders (no tenant).
|
|
1060
|
+
*
|
|
1061
|
+
* EmailFolder is @TenantScoped (CTI, own `email_folders` table). Under an
|
|
1062
|
+
* active tenant context list({ tenantId: null }) throws and unflagged raw SQL
|
|
1063
|
+
* is blocked (#1596); route through the raw helpers.
|
|
1064
|
+
*/
|
|
1065
|
+
async findGlobal() {
|
|
1066
|
+
return queryGlobal(this);
|
|
1067
|
+
}
|
|
1068
|
+
/**
|
|
1069
|
+
* Find email folders for a tenant including global folders.
|
|
1070
|
+
*/
|
|
1071
|
+
async findWithGlobals(tenantId) {
|
|
1072
|
+
return queryWithGlobals(this, tenantId, "EmailFolder.findWithGlobals");
|
|
1073
|
+
}
|
|
1937
1074
|
};
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
api: { include: ["list", "get", "create", "update", "delete"] },
|
|
1942
|
-
mcp: { include: ["list", "get"] },
|
|
1943
|
-
cli: true
|
|
1944
|
-
})
|
|
1945
|
-
], Email);
|
|
1946
|
-
const Email$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
1947
|
-
__proto__: null,
|
|
1948
|
-
get Email() {
|
|
1949
|
-
return Email;
|
|
1950
|
-
}
|
|
1951
|
-
}, Symbol.toStringTag, { value: "Module" }));
|
|
1952
|
-
class EmailCollection extends MessageCollection {
|
|
1953
|
-
static _itemClass = Email;
|
|
1954
|
-
/**
|
|
1955
|
-
* Get email by RFC 822 Message-ID
|
|
1956
|
-
*/
|
|
1957
|
-
async getByMessageId(accountId, messageId) {
|
|
1958
|
-
const emails = await this.list({
|
|
1959
|
-
where: { accountId, messageId },
|
|
1960
|
-
limit: 1
|
|
1961
|
-
});
|
|
1962
|
-
return emails[0] || null;
|
|
1963
|
-
}
|
|
1964
|
-
/**
|
|
1965
|
-
* Get emails by account
|
|
1966
|
-
*/
|
|
1967
|
-
async getByAccount(accountId) {
|
|
1968
|
-
return await this.list({ where: { accountId } });
|
|
1969
|
-
}
|
|
1970
|
-
/**
|
|
1971
|
-
* Get emails by folder
|
|
1972
|
-
*/
|
|
1973
|
-
async getByFolder(folderId) {
|
|
1974
|
-
return await this.list({ where: { folderId } });
|
|
1975
|
-
}
|
|
1976
|
-
/**
|
|
1977
|
-
* Get emails by thread
|
|
1978
|
-
*/
|
|
1979
|
-
async getByThread(threadId) {
|
|
1980
|
-
return await this.list({ where: { threadId } });
|
|
1981
|
-
}
|
|
1982
|
-
/**
|
|
1983
|
-
* Get unread emails
|
|
1984
|
-
*/
|
|
1985
|
-
async getUnread(accountId) {
|
|
1986
|
-
const where = { isRead: false };
|
|
1987
|
-
if (accountId) {
|
|
1988
|
-
where.accountId = accountId;
|
|
1989
|
-
}
|
|
1990
|
-
return await this.list({ where });
|
|
1991
|
-
}
|
|
1992
|
-
/**
|
|
1993
|
-
* Get flagged emails
|
|
1994
|
-
*/
|
|
1995
|
-
async getFlagged(accountId) {
|
|
1996
|
-
const where = { isFlagged: true };
|
|
1997
|
-
if (accountId) {
|
|
1998
|
-
where.accountId = accountId;
|
|
1999
|
-
}
|
|
2000
|
-
return await this.list({ where });
|
|
2001
|
-
}
|
|
2002
|
-
/**
|
|
2003
|
-
* Get emails with attachments
|
|
2004
|
-
*/
|
|
2005
|
-
async getWithAttachments(accountId) {
|
|
2006
|
-
const where = { hasAttachments: true };
|
|
2007
|
-
if (accountId) {
|
|
2008
|
-
where.accountId = accountId;
|
|
2009
|
-
}
|
|
2010
|
-
return await this.list({ where });
|
|
2011
|
-
}
|
|
2012
|
-
/**
|
|
2013
|
-
* Get recent emails
|
|
2014
|
-
*/
|
|
2015
|
-
async getRecent(limit = 20, accountId) {
|
|
2016
|
-
const allEmails = await this.list({
|
|
2017
|
-
where: accountId ? { accountId } : void 0
|
|
2018
|
-
});
|
|
2019
|
-
return allEmails.sort((a, b) => {
|
|
2020
|
-
const dateA = a.date?.getTime() || 0;
|
|
2021
|
-
const dateB = b.date?.getTime() || 0;
|
|
2022
|
-
return dateB - dateA;
|
|
2023
|
-
}).slice(0, limit);
|
|
2024
|
-
}
|
|
2025
|
-
/**
|
|
2026
|
-
* Count emails in a folder
|
|
2027
|
-
*/
|
|
2028
|
-
async countByFolder(folderId) {
|
|
2029
|
-
const emails = await this.list({ where: { folderId } });
|
|
2030
|
-
return emails.length;
|
|
2031
|
-
}
|
|
2032
|
-
/**
|
|
2033
|
-
* Count unread emails in a folder
|
|
2034
|
-
*/
|
|
2035
|
-
async countUnreadByFolder(folderId) {
|
|
2036
|
-
const emails = await this.list({ where: { folderId, isRead: false } });
|
|
2037
|
-
return emails.length;
|
|
2038
|
-
}
|
|
2039
|
-
/**
|
|
2040
|
-
* Count unread emails for an account
|
|
2041
|
-
*/
|
|
2042
|
-
async countUnreadByAccount(accountId) {
|
|
2043
|
-
const emails = await this.list({ where: { accountId, isRead: false } });
|
|
2044
|
-
return emails.length;
|
|
2045
|
-
}
|
|
2046
|
-
/**
|
|
2047
|
-
* Search emails with email-specific filters.
|
|
2048
|
-
* Alias: `search()` for backward compatibility.
|
|
2049
|
-
*/
|
|
2050
|
-
async search(query, filters) {
|
|
2051
|
-
return this.searchEmails(query, filters);
|
|
2052
|
-
}
|
|
2053
|
-
/**
|
|
2054
|
-
* Search emails with email-specific filters
|
|
2055
|
-
*/
|
|
2056
|
-
async searchEmails(query, filters) {
|
|
2057
|
-
let emails = await this.list({});
|
|
2058
|
-
if (query) {
|
|
2059
|
-
const lowerQuery = query.toLowerCase();
|
|
2060
|
-
emails = emails.filter(
|
|
2061
|
-
(e) => e.subject?.toLowerCase().includes(lowerQuery) || e.textBody?.toLowerCase().includes(lowerQuery) || e.fromAddress?.toLowerCase().includes(lowerQuery) || e.fromName?.toLowerCase().includes(lowerQuery)
|
|
2062
|
-
);
|
|
2063
|
-
}
|
|
2064
|
-
if (filters) {
|
|
2065
|
-
if (filters.accountId) {
|
|
2066
|
-
emails = emails.filter((e) => e.accountId === filters.accountId);
|
|
2067
|
-
}
|
|
2068
|
-
if (filters.folderId) {
|
|
2069
|
-
emails = emails.filter((e) => e.folderId === filters.folderId);
|
|
2070
|
-
}
|
|
2071
|
-
if (filters.threadId) {
|
|
2072
|
-
emails = emails.filter((e) => e.threadId === filters.threadId);
|
|
2073
|
-
}
|
|
2074
|
-
if (filters.from) {
|
|
2075
|
-
const fromLower = filters.from.toLowerCase();
|
|
2076
|
-
emails = emails.filter(
|
|
2077
|
-
(e) => e.fromAddress?.toLowerCase().includes(fromLower) || e.fromName?.toLowerCase().includes(fromLower)
|
|
2078
|
-
);
|
|
2079
|
-
}
|
|
2080
|
-
if (filters.to) {
|
|
2081
|
-
const toLower = filters.to.toLowerCase();
|
|
2082
|
-
emails = emails.filter(
|
|
2083
|
-
(e) => e.toAddresses?.toLowerCase().includes(toLower)
|
|
2084
|
-
);
|
|
2085
|
-
}
|
|
2086
|
-
if (filters.subject) {
|
|
2087
|
-
const subjectLower = filters.subject.toLowerCase();
|
|
2088
|
-
emails = emails.filter(
|
|
2089
|
-
(e) => e.subject?.toLowerCase().includes(subjectLower)
|
|
2090
|
-
);
|
|
2091
|
-
}
|
|
2092
|
-
if (filters.isRead !== void 0) {
|
|
2093
|
-
emails = emails.filter((e) => e.isRead === filters.isRead);
|
|
2094
|
-
}
|
|
2095
|
-
if (filters.isFlagged !== void 0) {
|
|
2096
|
-
emails = emails.filter((e) => e.isFlagged === filters.isFlagged);
|
|
2097
|
-
}
|
|
2098
|
-
if (filters.hasAttachments !== void 0) {
|
|
2099
|
-
emails = emails.filter(
|
|
2100
|
-
(e) => e.hasAttachments === filters.hasAttachments
|
|
2101
|
-
);
|
|
2102
|
-
}
|
|
2103
|
-
if (filters.sincDate) {
|
|
2104
|
-
emails = emails.filter(
|
|
2105
|
-
(e) => e.date && e.date >= filters.sincDate
|
|
2106
|
-
);
|
|
2107
|
-
}
|
|
2108
|
-
if (filters.beforeDate) {
|
|
2109
|
-
emails = emails.filter(
|
|
2110
|
-
(e) => e.date && e.date < filters.beforeDate
|
|
2111
|
-
);
|
|
2112
|
-
}
|
|
2113
|
-
}
|
|
2114
|
-
return emails;
|
|
2115
|
-
}
|
|
2116
|
-
/**
|
|
2117
|
-
* Mark all emails in a folder as read
|
|
2118
|
-
*/
|
|
2119
|
-
async markFolderRead(folderId) {
|
|
2120
|
-
const emails = await this.getUnread();
|
|
2121
|
-
const folderEmails = emails.filter((e) => e.folderId === folderId);
|
|
2122
|
-
for (const email of folderEmails) {
|
|
2123
|
-
await email.markRead();
|
|
2124
|
-
}
|
|
2125
|
-
}
|
|
2126
|
-
/**
|
|
2127
|
-
* Delete emails by folder
|
|
2128
|
-
*/
|
|
2129
|
-
async deleteByFolder(folderId) {
|
|
2130
|
-
const emails = await this.getByFolder(folderId);
|
|
2131
|
-
let count = 0;
|
|
2132
|
-
for (const email of emails) {
|
|
2133
|
-
await email.delete();
|
|
2134
|
-
count++;
|
|
2135
|
-
}
|
|
2136
|
-
return count;
|
|
2137
|
-
}
|
|
2138
|
-
/**
|
|
2139
|
-
* Get email statistics for an account
|
|
2140
|
-
*/
|
|
2141
|
-
async getAccountStats(accountId) {
|
|
2142
|
-
const emails = await this.getByAccount(accountId);
|
|
2143
|
-
return {
|
|
2144
|
-
total: emails.length,
|
|
2145
|
-
unread: emails.filter((e) => !e.isRead).length,
|
|
2146
|
-
flagged: emails.filter((e) => e.isFlagged).length,
|
|
2147
|
-
withAttachments: emails.filter((e) => e.hasAttachments).length,
|
|
2148
|
-
byType: { Email: emails.length }
|
|
2149
|
-
};
|
|
2150
|
-
}
|
|
2151
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
2152
|
-
// Tenant Helper Methods
|
|
2153
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
2154
|
-
async findByTenant(tenantId2) {
|
|
2155
|
-
return this.list({ where: { tenantId: tenantId2 } });
|
|
2156
|
-
}
|
|
2157
|
-
// Now that Email inherits Message's @TenantScoped recognition (#1596), an
|
|
2158
|
-
// explicit `tenant_id IS NULL` filter via list() throws and unflagged raw SQL
|
|
2159
|
-
// is blocked under an active tenant context — so route global / cross-global
|
|
2160
|
-
// lookups through the shared raw helpers. They auto-scope to the Email
|
|
2161
|
-
// `_meta_type` (via getStiChildMetaType) so the shared `messages` table never
|
|
2162
|
-
// returns sibling Message subtypes. (#1600)
|
|
2163
|
-
async findGlobal() {
|
|
2164
|
-
return queryGlobal(this);
|
|
2165
|
-
}
|
|
2166
|
-
async findWithGlobals(tenantId2) {
|
|
2167
|
-
return queryWithGlobals(this, tenantId2, "Email.findWithGlobals");
|
|
2168
|
-
}
|
|
2169
|
-
}
|
|
2170
|
-
const EmailCollection$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
2171
|
-
__proto__: null,
|
|
2172
|
-
EmailCollection
|
|
2173
|
-
}, Symbol.toStringTag, { value: "Module" }));
|
|
2174
|
-
var __defProp = Object.defineProperty;
|
|
2175
|
-
var __getOwnPropDesc$6 = Object.getOwnPropertyDescriptor;
|
|
2176
|
-
var __decorateClass$6 = (decorators, target, key, kind) => {
|
|
2177
|
-
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$6(target, key) : target;
|
|
2178
|
-
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
2179
|
-
if (decorator = decorators[i])
|
|
2180
|
-
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
2181
|
-
if (kind && result) __defProp(target, key, result);
|
|
2182
|
-
return result;
|
|
2183
|
-
};
|
|
2184
|
-
let EmailFolder = class extends SmrtObject {
|
|
2185
|
-
tenantId = null;
|
|
2186
|
-
accountId = "";
|
|
2187
|
-
name = "";
|
|
2188
|
-
path = "";
|
|
2189
|
-
delimiter = "/";
|
|
2190
|
-
specialUse = "";
|
|
2191
|
-
// '\\Inbox', '\\Sent', '\\Drafts', etc.
|
|
2192
|
-
messageCount = 0;
|
|
2193
|
-
unreadCount = 0;
|
|
2194
|
-
subscribed = true;
|
|
2195
|
-
// Timestamps
|
|
2196
|
-
createdAt = /* @__PURE__ */ new Date();
|
|
2197
|
-
updatedAt = /* @__PURE__ */ new Date();
|
|
2198
|
-
constructor(options = {}) {
|
|
2199
|
-
super(options);
|
|
2200
|
-
if (options.tenantId !== void 0) this.tenantId = options.tenantId;
|
|
2201
|
-
if (options.accountId !== void 0) this.accountId = options.accountId;
|
|
2202
|
-
if (options.name !== void 0) this.name = options.name;
|
|
2203
|
-
if (options.path !== void 0) this.path = options.path;
|
|
2204
|
-
if (options.delimiter !== void 0) this.delimiter = options.delimiter;
|
|
2205
|
-
if (options.specialUse !== void 0) this.specialUse = options.specialUse;
|
|
2206
|
-
if (options.messageCount !== void 0)
|
|
2207
|
-
this.messageCount = options.messageCount;
|
|
2208
|
-
if (options.unreadCount !== void 0)
|
|
2209
|
-
this.unreadCount = options.unreadCount;
|
|
2210
|
-
if (options.subscribed !== void 0) this.subscribed = options.subscribed;
|
|
2211
|
-
if (options.createdAt) this.createdAt = options.createdAt;
|
|
2212
|
-
if (options.updatedAt) this.updatedAt = options.updatedAt;
|
|
2213
|
-
}
|
|
2214
|
-
/**
|
|
2215
|
-
* Get the email account
|
|
2216
|
-
*/
|
|
2217
|
-
async getAccount() {
|
|
2218
|
-
if (!this.accountId) return null;
|
|
2219
|
-
const { EmailAccountCollection: EmailAccountCollection2 } = await Promise.resolve().then(() => EmailAccountCollection$1);
|
|
2220
|
-
const collection = await EmailAccountCollection2.create(this.options);
|
|
2221
|
-
return await collection.get({ id: this.accountId });
|
|
2222
|
-
}
|
|
2223
|
-
/**
|
|
2224
|
-
* Get all emails in this folder
|
|
2225
|
-
*/
|
|
2226
|
-
async getEmails(limit) {
|
|
2227
|
-
const { EmailCollection: EmailCollection2 } = await Promise.resolve().then(() => EmailCollection$1);
|
|
2228
|
-
const collection = await EmailCollection2.create(this.options);
|
|
2229
|
-
const options = {
|
|
2230
|
-
where: { folderId: this.id }
|
|
2231
|
-
};
|
|
2232
|
-
if (limit) {
|
|
2233
|
-
options.limit = limit;
|
|
2234
|
-
}
|
|
2235
|
-
return await collection.list(options);
|
|
2236
|
-
}
|
|
2237
|
-
/**
|
|
2238
|
-
* Get unread emails in this folder
|
|
2239
|
-
*/
|
|
2240
|
-
async getUnreadEmails(limit) {
|
|
2241
|
-
const { EmailCollection: EmailCollection2 } = await Promise.resolve().then(() => EmailCollection$1);
|
|
2242
|
-
const collection = await EmailCollection2.create(this.options);
|
|
2243
|
-
const options = {
|
|
2244
|
-
where: { folderId: this.id, isRead: false }
|
|
2245
|
-
};
|
|
2246
|
-
if (limit) {
|
|
2247
|
-
options.limit = limit;
|
|
2248
|
-
}
|
|
2249
|
-
return await collection.list(options);
|
|
2250
|
-
}
|
|
2251
|
-
/**
|
|
2252
|
-
* Update message counts from database
|
|
2253
|
-
*/
|
|
2254
|
-
async refreshCounts() {
|
|
2255
|
-
if (!this.id) {
|
|
2256
|
-
throw new Error(
|
|
2257
|
-
"EmailFolder.refreshCounts requires a persisted folder (missing id)"
|
|
2258
|
-
);
|
|
2259
|
-
}
|
|
2260
|
-
const folderId = this.id;
|
|
2261
|
-
const { EmailCollection: EmailCollection2 } = await Promise.resolve().then(() => EmailCollection$1);
|
|
2262
|
-
const collection = await EmailCollection2.create(this.options);
|
|
2263
|
-
this.messageCount = await collection.countByFolder(folderId);
|
|
2264
|
-
this.unreadCount = await collection.countUnreadByFolder(folderId);
|
|
2265
|
-
this.updatedAt = /* @__PURE__ */ new Date();
|
|
2266
|
-
await this.save();
|
|
2267
|
-
}
|
|
2268
|
-
/**
|
|
2269
|
-
* Check if this is the inbox folder
|
|
2270
|
-
*/
|
|
2271
|
-
isInbox() {
|
|
2272
|
-
return this.specialUse === "\\Inbox" || this.path.toLowerCase() === "inbox";
|
|
2273
|
-
}
|
|
2274
|
-
/**
|
|
2275
|
-
* Check if this is the sent folder
|
|
2276
|
-
*/
|
|
2277
|
-
isSent() {
|
|
2278
|
-
return this.specialUse === "\\Sent" || this.path.toLowerCase() === "sent";
|
|
2279
|
-
}
|
|
2280
|
-
/**
|
|
2281
|
-
* Check if this is the drafts folder
|
|
2282
|
-
*/
|
|
2283
|
-
isDrafts() {
|
|
2284
|
-
return this.specialUse === "\\Drafts" || this.path.toLowerCase() === "drafts";
|
|
2285
|
-
}
|
|
2286
|
-
/**
|
|
2287
|
-
* Check if this is the trash folder
|
|
2288
|
-
*/
|
|
2289
|
-
isTrash() {
|
|
2290
|
-
return this.specialUse === "\\Trash" || this.path.toLowerCase() === "trash";
|
|
2291
|
-
}
|
|
2292
|
-
/**
|
|
2293
|
-
* Check if this is the spam/junk folder
|
|
2294
|
-
*/
|
|
2295
|
-
isSpam() {
|
|
2296
|
-
return this.specialUse === "\\Junk" || this.path.toLowerCase() === "spam" || this.path.toLowerCase() === "junk";
|
|
2297
|
-
}
|
|
2298
|
-
/**
|
|
2299
|
-
* Check if this is a system folder
|
|
2300
|
-
*/
|
|
2301
|
-
isSystemFolder() {
|
|
2302
|
-
return !!this.specialUse;
|
|
2303
|
-
}
|
|
2304
|
-
/**
|
|
2305
|
-
* Subscribe to folder
|
|
2306
|
-
*/
|
|
2307
|
-
async subscribe() {
|
|
2308
|
-
this.subscribed = true;
|
|
2309
|
-
this.updatedAt = /* @__PURE__ */ new Date();
|
|
2310
|
-
await this.save();
|
|
2311
|
-
}
|
|
2312
|
-
/**
|
|
2313
|
-
* Unsubscribe from folder
|
|
2314
|
-
*/
|
|
2315
|
-
async unsubscribe() {
|
|
2316
|
-
this.subscribed = false;
|
|
2317
|
-
this.updatedAt = /* @__PURE__ */ new Date();
|
|
2318
|
-
await this.save();
|
|
2319
|
-
}
|
|
2320
|
-
};
|
|
2321
|
-
__decorateClass$6([
|
|
2322
|
-
tenantId({ nullable: true })
|
|
2323
|
-
], EmailFolder.prototype, "tenantId", 2);
|
|
2324
|
-
__decorateClass$6([
|
|
2325
|
-
foreignKey("Account")
|
|
2326
|
-
], EmailFolder.prototype, "accountId", 2);
|
|
2327
|
-
EmailFolder = __decorateClass$6([
|
|
2328
|
-
TenantScoped({ mode: "optional" }),
|
|
2329
|
-
smrt({
|
|
2330
|
-
api: { include: ["list", "get", "create", "update", "delete"] },
|
|
2331
|
-
mcp: { include: ["list", "get"] },
|
|
2332
|
-
cli: true
|
|
2333
|
-
})
|
|
2334
|
-
], EmailFolder);
|
|
2335
|
-
const EmailFolder$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
2336
|
-
__proto__: null,
|
|
2337
|
-
get EmailFolder() {
|
|
2338
|
-
return EmailFolder;
|
|
2339
|
-
}
|
|
2340
|
-
}, Symbol.toStringTag, { value: "Module" }));
|
|
2341
|
-
class EmailFolderCollection extends SmrtCollection {
|
|
2342
|
-
static _itemClass = EmailFolder;
|
|
2343
|
-
/**
|
|
2344
|
-
* Get folder by path for an account
|
|
2345
|
-
*/
|
|
2346
|
-
async getByPath(accountId, path) {
|
|
2347
|
-
const folders = await this.list({ where: { accountId, path } });
|
|
2348
|
-
return folders[0] || null;
|
|
2349
|
-
}
|
|
2350
|
-
/**
|
|
2351
|
-
* Get folders by account
|
|
2352
|
-
*/
|
|
2353
|
-
async getByAccount(accountId) {
|
|
2354
|
-
return await this.list({ where: { accountId } });
|
|
2355
|
-
}
|
|
2356
|
-
/**
|
|
2357
|
-
* Get inbox folder for an account
|
|
2358
|
-
*/
|
|
2359
|
-
async getInbox(accountId) {
|
|
2360
|
-
const folders = await this.getByAccount(accountId);
|
|
2361
|
-
return folders.find((f) => f.isInbox()) || null;
|
|
2362
|
-
}
|
|
2363
|
-
/**
|
|
2364
|
-
* Get sent folder for an account
|
|
2365
|
-
*/
|
|
2366
|
-
async getSent(accountId) {
|
|
2367
|
-
const folders = await this.getByAccount(accountId);
|
|
2368
|
-
return folders.find((f) => f.isSent()) || null;
|
|
2369
|
-
}
|
|
2370
|
-
/**
|
|
2371
|
-
* Get drafts folder for an account
|
|
2372
|
-
*/
|
|
2373
|
-
async getDrafts(accountId) {
|
|
2374
|
-
const folders = await this.getByAccount(accountId);
|
|
2375
|
-
return folders.find((f) => f.isDrafts()) || null;
|
|
2376
|
-
}
|
|
2377
|
-
/**
|
|
2378
|
-
* Get trash folder for an account
|
|
2379
|
-
*/
|
|
2380
|
-
async getTrash(accountId) {
|
|
2381
|
-
const folders = await this.getByAccount(accountId);
|
|
2382
|
-
return folders.find((f) => f.isTrash()) || null;
|
|
2383
|
-
}
|
|
2384
|
-
/**
|
|
2385
|
-
* Get spam folder for an account
|
|
2386
|
-
*/
|
|
2387
|
-
async getSpam(accountId) {
|
|
2388
|
-
const folders = await this.getByAccount(accountId);
|
|
2389
|
-
return folders.find((f) => f.isSpam()) || null;
|
|
2390
|
-
}
|
|
2391
|
-
/**
|
|
2392
|
-
* Get system folders for an account
|
|
2393
|
-
*/
|
|
2394
|
-
async getSystemFolders(accountId) {
|
|
2395
|
-
const folders = await this.getByAccount(accountId);
|
|
2396
|
-
return folders.filter((f) => f.isSystemFolder());
|
|
2397
|
-
}
|
|
2398
|
-
/**
|
|
2399
|
-
* Get user-created folders for an account
|
|
2400
|
-
*/
|
|
2401
|
-
async getUserFolders(accountId) {
|
|
2402
|
-
const folders = await this.getByAccount(accountId);
|
|
2403
|
-
return folders.filter((f) => !f.isSystemFolder());
|
|
2404
|
-
}
|
|
2405
|
-
/**
|
|
2406
|
-
* Get subscribed folders
|
|
2407
|
-
*/
|
|
2408
|
-
async getSubscribed(accountId) {
|
|
2409
|
-
const where = { subscribed: true };
|
|
2410
|
-
if (accountId) {
|
|
2411
|
-
where.accountId = accountId;
|
|
2412
|
-
}
|
|
2413
|
-
return await this.list({ where });
|
|
2414
|
-
}
|
|
2415
|
-
/**
|
|
2416
|
-
* Get folders with unread messages
|
|
2417
|
-
*/
|
|
2418
|
-
async getWithUnread(accountId) {
|
|
2419
|
-
const folders = accountId ? await this.getByAccount(accountId) : await this.list({});
|
|
2420
|
-
return folders.filter((f) => f.unreadCount > 0);
|
|
2421
|
-
}
|
|
2422
|
-
/**
|
|
2423
|
-
* Search folders with filters
|
|
2424
|
-
*/
|
|
2425
|
-
async search(query, filters) {
|
|
2426
|
-
let folders = await this.list({});
|
|
2427
|
-
if (query) {
|
|
2428
|
-
const lowerQuery = query.toLowerCase();
|
|
2429
|
-
folders = folders.filter(
|
|
2430
|
-
(f) => f.name?.toLowerCase().includes(lowerQuery) || f.path?.toLowerCase().includes(lowerQuery)
|
|
2431
|
-
);
|
|
2432
|
-
}
|
|
2433
|
-
if (filters) {
|
|
2434
|
-
if (filters.accountId) {
|
|
2435
|
-
folders = folders.filter((f) => f.accountId === filters.accountId);
|
|
2436
|
-
}
|
|
2437
|
-
if (filters.specialUse) {
|
|
2438
|
-
folders = folders.filter((f) => f.specialUse === filters.specialUse);
|
|
2439
|
-
}
|
|
2440
|
-
if (filters.subscribed !== void 0) {
|
|
2441
|
-
folders = folders.filter((f) => f.subscribed === filters.subscribed);
|
|
2442
|
-
}
|
|
2443
|
-
}
|
|
2444
|
-
return folders;
|
|
2445
|
-
}
|
|
2446
|
-
/**
|
|
2447
|
-
* Refresh counts for all folders in an account
|
|
2448
|
-
*/
|
|
2449
|
-
async refreshAllCounts(accountId) {
|
|
2450
|
-
const folders = await this.getByAccount(accountId);
|
|
2451
|
-
for (const folder of folders) {
|
|
2452
|
-
await folder.refreshCounts();
|
|
2453
|
-
}
|
|
2454
|
-
}
|
|
2455
|
-
/**
|
|
2456
|
-
* Get folder statistics for an account
|
|
2457
|
-
*/
|
|
2458
|
-
async getAccountStats(accountId) {
|
|
2459
|
-
const folders = await this.getByAccount(accountId);
|
|
2460
|
-
return {
|
|
2461
|
-
totalFolders: folders.length,
|
|
2462
|
-
totalMessages: folders.reduce((sum, f) => sum + f.messageCount, 0),
|
|
2463
|
-
totalUnread: folders.reduce((sum, f) => sum + f.unreadCount, 0),
|
|
2464
|
-
systemFolders: folders.filter((f) => f.isSystemFolder()).length,
|
|
2465
|
-
userFolders: folders.filter((f) => !f.isSystemFolder()).length
|
|
2466
|
-
};
|
|
2467
|
-
}
|
|
2468
|
-
/**
|
|
2469
|
-
* Create standard system folders for an account
|
|
2470
|
-
*/
|
|
2471
|
-
async createSystemFolders(accountId) {
|
|
2472
|
-
const standardFolders = [
|
|
2473
|
-
{ name: "INBOX", path: "INBOX", specialUse: "\\Inbox" },
|
|
2474
|
-
{ name: "Sent", path: "Sent", specialUse: "\\Sent" },
|
|
2475
|
-
{ name: "Drafts", path: "Drafts", specialUse: "\\Drafts" },
|
|
2476
|
-
{ name: "Trash", path: "Trash", specialUse: "\\Trash" },
|
|
2477
|
-
{ name: "Spam", path: "Spam", specialUse: "\\Junk" }
|
|
2478
|
-
];
|
|
2479
|
-
for (const folderData of standardFolders) {
|
|
2480
|
-
const existing = await this.getByPath(accountId, folderData.path);
|
|
2481
|
-
if (!existing) {
|
|
2482
|
-
const folder = await this.create({
|
|
2483
|
-
accountId,
|
|
2484
|
-
...folderData
|
|
2485
|
-
});
|
|
2486
|
-
await folder.save();
|
|
2487
|
-
}
|
|
2488
|
-
}
|
|
2489
|
-
}
|
|
2490
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
2491
|
-
// Tenant Helper Methods
|
|
2492
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
2493
|
-
/**
|
|
2494
|
-
* Find all email folders belonging to a specific tenant
|
|
2495
|
-
*/
|
|
2496
|
-
async findByTenant(tenantId2) {
|
|
2497
|
-
return this.list({ where: { tenantId: tenantId2 } });
|
|
2498
|
-
}
|
|
2499
|
-
/**
|
|
2500
|
-
* Find all global email folders (no tenant).
|
|
2501
|
-
*
|
|
2502
|
-
* EmailFolder is @TenantScoped (CTI, own `email_folders` table). Under an
|
|
2503
|
-
* active tenant context list({ tenantId: null }) throws and unflagged raw SQL
|
|
2504
|
-
* is blocked (#1596); route through the raw helpers.
|
|
2505
|
-
*/
|
|
2506
|
-
async findGlobal() {
|
|
2507
|
-
return queryGlobal(this);
|
|
2508
|
-
}
|
|
2509
|
-
/**
|
|
2510
|
-
* Find email folders for a tenant including global folders.
|
|
2511
|
-
*/
|
|
2512
|
-
async findWithGlobals(tenantId2) {
|
|
2513
|
-
return queryWithGlobals(
|
|
2514
|
-
this,
|
|
2515
|
-
tenantId2,
|
|
2516
|
-
"EmailFolder.findWithGlobals"
|
|
2517
|
-
);
|
|
2518
|
-
}
|
|
2519
|
-
}
|
|
2520
|
-
const EmailFolderCollection$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
2521
|
-
__proto__: null,
|
|
2522
|
-
EmailFolderCollection
|
|
2523
|
-
}, Symbol.toStringTag, { value: "Module" }));
|
|
1075
|
+
//#endregion
|
|
1076
|
+
//#region src/models/Blacklist.ts
|
|
1077
|
+
var __defProp$5 = Object.defineProperty;
|
|
2524
1078
|
var __getOwnPropDesc$5 = Object.getOwnPropertyDescriptor;
|
|
2525
1079
|
var __decorateClass$5 = (decorators, target, key, kind) => {
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
return result;
|
|
1080
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$5(target, key) : target;
|
|
1081
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
1082
|
+
if (kind && result) __defProp$5(target, key, result);
|
|
1083
|
+
return result;
|
|
2531
1084
|
};
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
}
|
|
2563
|
-
try {
|
|
2564
|
-
const regex = new RegExp(pattern, "i");
|
|
2565
|
-
return regex.test(normalizedEmail);
|
|
2566
|
-
} catch {
|
|
2567
|
-
return false;
|
|
2568
|
-
}
|
|
2569
|
-
}
|
|
2570
|
-
default:
|
|
2571
|
-
return false;
|
|
2572
|
-
}
|
|
2573
|
-
}
|
|
1085
|
+
var Blacklist = class extends SmrtObject {
|
|
1086
|
+
pattern = "";
|
|
1087
|
+
type = "email";
|
|
1088
|
+
reason = "";
|
|
1089
|
+
autoArchive = true;
|
|
1090
|
+
constructor(options = {}) {
|
|
1091
|
+
super(options);
|
|
1092
|
+
if (options.pattern !== void 0) this.pattern = options.pattern;
|
|
1093
|
+
if (options.type !== void 0) this.type = options.type;
|
|
1094
|
+
}
|
|
1095
|
+
/**
|
|
1096
|
+
* Check if an email address matches this blacklist entry
|
|
1097
|
+
*/
|
|
1098
|
+
matches(email) {
|
|
1099
|
+
const normalizedEmail = email.toLowerCase().trim();
|
|
1100
|
+
switch (this.type) {
|
|
1101
|
+
case "email": return normalizedEmail === this.pattern.toLowerCase().trim();
|
|
1102
|
+
case "domain": return normalizedEmail.split("@")[1] === this.pattern.toLowerCase().trim();
|
|
1103
|
+
case "regex": {
|
|
1104
|
+
const pattern = this.pattern.trim();
|
|
1105
|
+
if (!pattern) return false;
|
|
1106
|
+
try {
|
|
1107
|
+
return new RegExp(pattern, "i").test(normalizedEmail);
|
|
1108
|
+
} catch {
|
|
1109
|
+
return false;
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
default: return false;
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
2574
1115
|
};
|
|
2575
|
-
Blacklist = __decorateClass$5([
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
1116
|
+
Blacklist = __decorateClass$5([smrt({
|
|
1117
|
+
api: { include: [
|
|
1118
|
+
"list",
|
|
1119
|
+
"get",
|
|
1120
|
+
"create",
|
|
1121
|
+
"update",
|
|
1122
|
+
"delete"
|
|
1123
|
+
] },
|
|
1124
|
+
cli: true,
|
|
1125
|
+
tenantScoped: true
|
|
1126
|
+
})], Blacklist);
|
|
1127
|
+
//#endregion
|
|
1128
|
+
//#region src/collections/BlacklistCollection.ts
|
|
1129
|
+
var BlacklistCollection = class extends SmrtCollection {
|
|
1130
|
+
static _itemClass = Blacklist;
|
|
1131
|
+
/**
|
|
1132
|
+
* Check if an email is blacklisted
|
|
1133
|
+
*/
|
|
1134
|
+
async isBlacklisted(email) {
|
|
1135
|
+
const entries = await this.list({});
|
|
1136
|
+
for (const entry of entries) if (entry.matches(email)) return true;
|
|
1137
|
+
return false;
|
|
1138
|
+
}
|
|
1139
|
+
/**
|
|
1140
|
+
* Get the matching blacklist entry for an email
|
|
1141
|
+
*/
|
|
1142
|
+
async getMatchingEntry(email) {
|
|
1143
|
+
const entries = await this.list({});
|
|
1144
|
+
for (const entry of entries) if (entry.matches(email)) return entry;
|
|
1145
|
+
return null;
|
|
1146
|
+
}
|
|
1147
|
+
};
|
|
1148
|
+
//#endregion
|
|
1149
|
+
//#region src/models/Whitelist.ts
|
|
1150
|
+
var __defProp$4 = Object.defineProperty;
|
|
2609
1151
|
var __getOwnPropDesc$4 = Object.getOwnPropertyDescriptor;
|
|
2610
1152
|
var __decorateClass$4 = (decorators, target, key, kind) => {
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
1153
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$4(target, key) : target;
|
|
1154
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
1155
|
+
if (kind && result) __defProp$4(target, key, result);
|
|
1156
|
+
return result;
|
|
1157
|
+
};
|
|
1158
|
+
var Whitelist = class extends SmrtObject {
|
|
1159
|
+
pattern = "";
|
|
1160
|
+
type = "email";
|
|
1161
|
+
category = null;
|
|
1162
|
+
description = "";
|
|
1163
|
+
constructor(options = {}) {
|
|
1164
|
+
super(options);
|
|
1165
|
+
if (options.pattern !== void 0) this.pattern = options.pattern;
|
|
1166
|
+
if (options.type !== void 0) this.type = options.type;
|
|
1167
|
+
}
|
|
1168
|
+
/**
|
|
1169
|
+
* Check if an email address matches this whitelist entry
|
|
1170
|
+
*/
|
|
1171
|
+
matches(email) {
|
|
1172
|
+
const normalizedEmail = email.toLowerCase().trim();
|
|
1173
|
+
switch (this.type) {
|
|
1174
|
+
case "email": return normalizedEmail === this.pattern.toLowerCase().trim();
|
|
1175
|
+
case "domain": return normalizedEmail.split("@")[1] === this.pattern.toLowerCase().trim();
|
|
1176
|
+
case "regex": {
|
|
1177
|
+
const pattern = this.pattern.trim();
|
|
1178
|
+
if (!pattern) return false;
|
|
1179
|
+
try {
|
|
1180
|
+
return new RegExp(pattern, "i").test(normalizedEmail);
|
|
1181
|
+
} catch {
|
|
1182
|
+
return false;
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
default: return false;
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
2616
1188
|
};
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
}
|
|
1189
|
+
Whitelist = __decorateClass$4([smrt({
|
|
1190
|
+
api: { include: [
|
|
1191
|
+
"list",
|
|
1192
|
+
"get",
|
|
1193
|
+
"create",
|
|
1194
|
+
"update",
|
|
1195
|
+
"delete"
|
|
1196
|
+
] },
|
|
1197
|
+
cli: true,
|
|
1198
|
+
tenantScoped: true
|
|
1199
|
+
})], Whitelist);
|
|
1200
|
+
//#endregion
|
|
1201
|
+
//#region src/collections/WhitelistCollection.ts
|
|
1202
|
+
var WhitelistCollection = class extends SmrtCollection {
|
|
1203
|
+
static _itemClass = Whitelist;
|
|
1204
|
+
/**
|
|
1205
|
+
* Check if an email is whitelisted for a specific category
|
|
1206
|
+
*/
|
|
1207
|
+
async isWhitelisted(email, category) {
|
|
1208
|
+
const entries = await this.list({});
|
|
1209
|
+
for (const entry of entries) {
|
|
1210
|
+
if (!entry.matches(email)) continue;
|
|
1211
|
+
if (!category) return true;
|
|
1212
|
+
if (entry.category === category || entry.category === null) return true;
|
|
1213
|
+
}
|
|
1214
|
+
return false;
|
|
1215
|
+
}
|
|
1216
|
+
/**
|
|
1217
|
+
* Get the matching whitelist entry for an email
|
|
1218
|
+
*/
|
|
1219
|
+
async getMatchingEntry(email) {
|
|
1220
|
+
const entries = await this.list({});
|
|
1221
|
+
for (const entry of entries) if (entry.matches(email)) return entry;
|
|
1222
|
+
return null;
|
|
1223
|
+
}
|
|
1224
|
+
/**
|
|
1225
|
+
* Get whitelist entries by category
|
|
1226
|
+
*/
|
|
1227
|
+
async getByCategory(category) {
|
|
1228
|
+
return await this.list({ where: { category } });
|
|
1229
|
+
}
|
|
2659
1230
|
};
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
cli: true,
|
|
2664
|
-
tenantScoped: true
|
|
2665
|
-
})
|
|
2666
|
-
], Whitelist);
|
|
2667
|
-
class WhitelistCollection extends SmrtCollection {
|
|
2668
|
-
static _itemClass = Whitelist;
|
|
2669
|
-
/**
|
|
2670
|
-
* Check if an email is whitelisted for a specific category
|
|
2671
|
-
*/
|
|
2672
|
-
async isWhitelisted(email, category) {
|
|
2673
|
-
const entries = await this.list({});
|
|
2674
|
-
for (const entry of entries) {
|
|
2675
|
-
if (!entry.matches(email)) {
|
|
2676
|
-
continue;
|
|
2677
|
-
}
|
|
2678
|
-
if (!category) {
|
|
2679
|
-
return true;
|
|
2680
|
-
}
|
|
2681
|
-
if (entry.category === category || entry.category === null) {
|
|
2682
|
-
return true;
|
|
2683
|
-
}
|
|
2684
|
-
}
|
|
2685
|
-
return false;
|
|
2686
|
-
}
|
|
2687
|
-
/**
|
|
2688
|
-
* Get the matching whitelist entry for an email
|
|
2689
|
-
*/
|
|
2690
|
-
async getMatchingEntry(email) {
|
|
2691
|
-
const entries = await this.list({});
|
|
2692
|
-
for (const entry of entries) {
|
|
2693
|
-
if (entry.matches(email)) {
|
|
2694
|
-
return entry;
|
|
2695
|
-
}
|
|
2696
|
-
}
|
|
2697
|
-
return null;
|
|
2698
|
-
}
|
|
2699
|
-
/**
|
|
2700
|
-
* Get whitelist entries by category
|
|
2701
|
-
*/
|
|
2702
|
-
async getByCategory(category) {
|
|
2703
|
-
return await this.list({
|
|
2704
|
-
where: { category }
|
|
2705
|
-
});
|
|
2706
|
-
}
|
|
2707
|
-
}
|
|
1231
|
+
//#endregion
|
|
1232
|
+
//#region src/models/SlackAccount.ts
|
|
1233
|
+
var __defProp$3 = Object.defineProperty;
|
|
2708
1234
|
var __getOwnPropDesc$3 = Object.getOwnPropertyDescriptor;
|
|
2709
1235
|
var __decorateClass$3 = (decorators, target, key, kind) => {
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
return result;
|
|
1236
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$3(target, key) : target;
|
|
1237
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
1238
|
+
if (kind && result) __defProp$3(target, key, result);
|
|
1239
|
+
return result;
|
|
2715
1240
|
};
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
return new SlackSender2(this);
|
|
2735
|
-
}
|
|
1241
|
+
var SlackAccount = class extends Account {
|
|
1242
|
+
workspaceId = "";
|
|
1243
|
+
workspaceName = "";
|
|
1244
|
+
botUserId = "";
|
|
1245
|
+
constructor(options = {}) {
|
|
1246
|
+
super(options);
|
|
1247
|
+
if (options.workspaceId !== void 0) this.workspaceId = options.workspaceId;
|
|
1248
|
+
if (options.workspaceName !== void 0) this.workspaceName = options.workspaceName;
|
|
1249
|
+
if (options.botUserId !== void 0) this.botUserId = options.botUserId;
|
|
1250
|
+
if (!this.providerType) this.providerType = "slack";
|
|
1251
|
+
}
|
|
1252
|
+
/**
|
|
1253
|
+
* Create a sender for this Slack account
|
|
1254
|
+
*/
|
|
1255
|
+
async createSender() {
|
|
1256
|
+
const { SlackSender } = await Promise.resolve().then(() => SlackSender_exports);
|
|
1257
|
+
return new SlackSender(this);
|
|
1258
|
+
}
|
|
2736
1259
|
};
|
|
2737
|
-
SlackAccount = __decorateClass$3([
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
1260
|
+
SlackAccount = __decorateClass$3([smrt({
|
|
1261
|
+
tableStrategy: "sti",
|
|
1262
|
+
api: { include: [
|
|
1263
|
+
"list",
|
|
1264
|
+
"get",
|
|
1265
|
+
"create",
|
|
1266
|
+
"update",
|
|
1267
|
+
"delete"
|
|
1268
|
+
] },
|
|
1269
|
+
mcp: { include: ["list", "get"] },
|
|
1270
|
+
cli: true
|
|
1271
|
+
})], SlackAccount);
|
|
1272
|
+
//#endregion
|
|
1273
|
+
//#region src/models/SlackMessage.ts
|
|
1274
|
+
var __defProp$2 = Object.defineProperty;
|
|
2745
1275
|
var __getOwnPropDesc$2 = Object.getOwnPropertyDescriptor;
|
|
2746
1276
|
var __decorateClass$2 = (decorators, target, key, kind) => {
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
return result;
|
|
1277
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$2(target, key) : target;
|
|
1278
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
1279
|
+
if (kind && result) __defProp$2(target, key, result);
|
|
1280
|
+
return result;
|
|
2752
1281
|
};
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
return !!this.slackThreadTs && this.slackThreadTs !== this.slackTs;
|
|
2808
|
-
}
|
|
2809
|
-
/**
|
|
2810
|
-
* Get total reaction count
|
|
2811
|
-
*/
|
|
2812
|
-
getTotalReactions() {
|
|
2813
|
-
return this.getReactions().reduce((sum, r) => sum + r.count, 0);
|
|
2814
|
-
}
|
|
1282
|
+
var SlackMessage = class extends Message {
|
|
1283
|
+
channelId = "";
|
|
1284
|
+
channelName = "";
|
|
1285
|
+
slackTs = "";
|
|
1286
|
+
slackThreadTs = "";
|
|
1287
|
+
reactions = "";
|
|
1288
|
+
isEdited = false;
|
|
1289
|
+
messageType = "";
|
|
1290
|
+
blocks = "";
|
|
1291
|
+
constructor(options = {}) {
|
|
1292
|
+
super(options);
|
|
1293
|
+
if (options.channelId !== void 0) this.channelId = options.channelId;
|
|
1294
|
+
if (options.channelName !== void 0) this.channelName = options.channelName;
|
|
1295
|
+
if (options.slackTs !== void 0) this.slackTs = options.slackTs;
|
|
1296
|
+
if (options.slackThreadTs !== void 0) this.slackThreadTs = options.slackThreadTs;
|
|
1297
|
+
if (options.reactions !== void 0) this.reactions = options.reactions;
|
|
1298
|
+
if (options.isEdited !== void 0) this.isEdited = options.isEdited;
|
|
1299
|
+
if (options.messageType !== void 0) this.messageType = options.messageType;
|
|
1300
|
+
if (options.blocks !== void 0) this.blocks = options.blocks;
|
|
1301
|
+
}
|
|
1302
|
+
/**
|
|
1303
|
+
* Get reactions as parsed array
|
|
1304
|
+
*/
|
|
1305
|
+
getReactions() {
|
|
1306
|
+
if (!this.reactions) return [];
|
|
1307
|
+
try {
|
|
1308
|
+
return JSON.parse(this.reactions);
|
|
1309
|
+
} catch {
|
|
1310
|
+
return [];
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
/**
|
|
1314
|
+
* Get blocks as parsed array
|
|
1315
|
+
*/
|
|
1316
|
+
getBlocks() {
|
|
1317
|
+
if (!this.blocks) return [];
|
|
1318
|
+
try {
|
|
1319
|
+
return JSON.parse(this.blocks);
|
|
1320
|
+
} catch {
|
|
1321
|
+
return [];
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
/**
|
|
1325
|
+
* Check if message is in a thread
|
|
1326
|
+
*/
|
|
1327
|
+
isInThread() {
|
|
1328
|
+
return !!this.slackThreadTs && this.slackThreadTs !== this.slackTs;
|
|
1329
|
+
}
|
|
1330
|
+
/**
|
|
1331
|
+
* Get total reaction count
|
|
1332
|
+
*/
|
|
1333
|
+
getTotalReactions() {
|
|
1334
|
+
return this.getReactions().reduce((sum, r) => sum + r.count, 0);
|
|
1335
|
+
}
|
|
2815
1336
|
};
|
|
2816
|
-
SlackMessage = __decorateClass$2([
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
],
|
|
1337
|
+
SlackMessage = __decorateClass$2([smrt({
|
|
1338
|
+
tableStrategy: "sti",
|
|
1339
|
+
api: { include: [
|
|
1340
|
+
"list",
|
|
1341
|
+
"get",
|
|
1342
|
+
"create"
|
|
1343
|
+
] },
|
|
1344
|
+
mcp: { include: ["list", "get"] },
|
|
1345
|
+
cli: true
|
|
1346
|
+
})], SlackMessage);
|
|
1347
|
+
//#endregion
|
|
1348
|
+
//#region src/models/Tweet.ts
|
|
1349
|
+
var __defProp$1 = Object.defineProperty;
|
|
2824
1350
|
var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
|
|
2825
1351
|
var __decorateClass$1 = (decorators, target, key, kind) => {
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
return result;
|
|
1352
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$1(target, key) : target;
|
|
1353
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
1354
|
+
if (kind && result) __defProp$1(target, key, result);
|
|
1355
|
+
return result;
|
|
2831
1356
|
};
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
*/
|
|
2894
|
-
getEngagement() {
|
|
2895
|
-
return this.retweetCount + this.likeCount + this.replyCount;
|
|
2896
|
-
}
|
|
1357
|
+
var Tweet = class extends Message {
|
|
1358
|
+
tweetId = "";
|
|
1359
|
+
retweetCount = 0;
|
|
1360
|
+
likeCount = 0;
|
|
1361
|
+
replyCount = 0;
|
|
1362
|
+
isRetweet = false;
|
|
1363
|
+
isReply = false;
|
|
1364
|
+
mediaUrls = "";
|
|
1365
|
+
hashtags = "";
|
|
1366
|
+
mentions = "";
|
|
1367
|
+
constructor(options = {}) {
|
|
1368
|
+
super(options);
|
|
1369
|
+
if (options.tweetId !== void 0) this.tweetId = options.tweetId;
|
|
1370
|
+
if (options.retweetCount !== void 0) this.retweetCount = options.retweetCount;
|
|
1371
|
+
if (options.likeCount !== void 0) this.likeCount = options.likeCount;
|
|
1372
|
+
if (options.replyCount !== void 0) this.replyCount = options.replyCount;
|
|
1373
|
+
if (options.isRetweet !== void 0) this.isRetweet = options.isRetweet;
|
|
1374
|
+
if (options.isReply !== void 0) this.isReply = options.isReply;
|
|
1375
|
+
if (options.mediaUrls !== void 0) this.mediaUrls = options.mediaUrls;
|
|
1376
|
+
if (options.hashtags !== void 0) this.hashtags = options.hashtags;
|
|
1377
|
+
if (options.mentions !== void 0) this.mentions = options.mentions;
|
|
1378
|
+
}
|
|
1379
|
+
/**
|
|
1380
|
+
* Get media URLs as parsed array
|
|
1381
|
+
*/
|
|
1382
|
+
getMediaUrls() {
|
|
1383
|
+
if (!this.mediaUrls) return [];
|
|
1384
|
+
try {
|
|
1385
|
+
return JSON.parse(this.mediaUrls);
|
|
1386
|
+
} catch {
|
|
1387
|
+
return [];
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
/**
|
|
1391
|
+
* Get hashtags as parsed array
|
|
1392
|
+
*/
|
|
1393
|
+
getHashtags() {
|
|
1394
|
+
if (!this.hashtags) return [];
|
|
1395
|
+
try {
|
|
1396
|
+
return JSON.parse(this.hashtags);
|
|
1397
|
+
} catch {
|
|
1398
|
+
return [];
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
/**
|
|
1402
|
+
* Get mentions as parsed array
|
|
1403
|
+
*/
|
|
1404
|
+
getMentions() {
|
|
1405
|
+
if (!this.mentions) return [];
|
|
1406
|
+
try {
|
|
1407
|
+
return JSON.parse(this.mentions);
|
|
1408
|
+
} catch {
|
|
1409
|
+
return [];
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
/**
|
|
1413
|
+
* Get total engagement count
|
|
1414
|
+
*/
|
|
1415
|
+
getEngagement() {
|
|
1416
|
+
return this.retweetCount + this.likeCount + this.replyCount;
|
|
1417
|
+
}
|
|
2897
1418
|
};
|
|
2898
|
-
Tweet = __decorateClass$1([
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
],
|
|
1419
|
+
Tweet = __decorateClass$1([smrt({
|
|
1420
|
+
tableStrategy: "sti",
|
|
1421
|
+
api: { include: [
|
|
1422
|
+
"list",
|
|
1423
|
+
"get",
|
|
1424
|
+
"create"
|
|
1425
|
+
] },
|
|
1426
|
+
mcp: { include: ["list", "get"] },
|
|
1427
|
+
cli: true
|
|
1428
|
+
})], Tweet);
|
|
1429
|
+
//#endregion
|
|
1430
|
+
//#region src/models/TwitterAccount.ts
|
|
1431
|
+
var __defProp = Object.defineProperty;
|
|
2906
1432
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
2907
1433
|
var __decorateClass = (decorators, target, key, kind) => {
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
return result;
|
|
1434
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
1435
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
1436
|
+
if (kind && result) __defProp(target, key, result);
|
|
1437
|
+
return result;
|
|
2913
1438
|
};
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
}
|
|
1439
|
+
var TwitterAccount = class extends Account {
|
|
1440
|
+
handle = "";
|
|
1441
|
+
twitterUserId = "";
|
|
1442
|
+
constructor(options = {}) {
|
|
1443
|
+
super(options);
|
|
1444
|
+
if (options.handle !== void 0) this.handle = options.handle;
|
|
1445
|
+
if (options.twitterUserId !== void 0) this.twitterUserId = options.twitterUserId;
|
|
1446
|
+
if (!this.providerType) this.providerType = "twitter";
|
|
1447
|
+
}
|
|
1448
|
+
/**
|
|
1449
|
+
* Create a sender for this Twitter account
|
|
1450
|
+
*/
|
|
1451
|
+
async createSender() {
|
|
1452
|
+
const { TweetSender } = await Promise.resolve().then(() => TweetSender_exports);
|
|
1453
|
+
return new TweetSender(this);
|
|
1454
|
+
}
|
|
2931
1455
|
};
|
|
2932
|
-
TwitterAccount = __decorateClass([
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
}
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
}
|
|
3003
|
-
async send(message, _options) {
|
|
3004
|
-
const { getMessageClient } = await import("@happyvertical/messages");
|
|
3005
|
-
const credentials = await this.account.getCredentials();
|
|
3006
|
-
if (!credentials?.botToken) {
|
|
3007
|
-
return {
|
|
3008
|
-
success: false,
|
|
3009
|
-
error: "No botToken found in account credentials",
|
|
3010
|
-
sentAt: /* @__PURE__ */ new Date()
|
|
3011
|
-
};
|
|
3012
|
-
}
|
|
3013
|
-
const client = await getMessageClient({
|
|
3014
|
-
type: "slack",
|
|
3015
|
-
botToken: credentials.botToken
|
|
3016
|
-
});
|
|
3017
|
-
try {
|
|
3018
|
-
await client.connect();
|
|
3019
|
-
const result = await client.send(
|
|
3020
|
-
{
|
|
3021
|
-
from: { id: this.account.botUserId, name: this.account.name },
|
|
3022
|
-
channelId: message.channelId,
|
|
3023
|
-
content: message.body
|
|
3024
|
-
},
|
|
3025
|
-
{
|
|
3026
|
-
replyTo: message.slackThreadTs || void 0
|
|
3027
|
-
}
|
|
3028
|
-
);
|
|
3029
|
-
await client.disconnect();
|
|
3030
|
-
return {
|
|
3031
|
-
success: result.success,
|
|
3032
|
-
providerMessageId: result.messageId,
|
|
3033
|
-
providerResponse: result.providerResponse,
|
|
3034
|
-
sentAt: result.timestamp
|
|
3035
|
-
};
|
|
3036
|
-
} catch (error) {
|
|
3037
|
-
return {
|
|
3038
|
-
success: false,
|
|
3039
|
-
error: error instanceof Error ? error.message : String(error),
|
|
3040
|
-
sentAt: /* @__PURE__ */ new Date()
|
|
3041
|
-
};
|
|
3042
|
-
}
|
|
3043
|
-
}
|
|
3044
|
-
}
|
|
3045
|
-
const SlackSender$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
3046
|
-
__proto__: null,
|
|
3047
|
-
SlackSender
|
|
3048
|
-
}, Symbol.toStringTag, { value: "Module" }));
|
|
3049
|
-
class TweetSender {
|
|
3050
|
-
providerType = "twitter";
|
|
3051
|
-
account;
|
|
3052
|
-
constructor(account) {
|
|
3053
|
-
this.account = account;
|
|
3054
|
-
}
|
|
3055
|
-
isReady() {
|
|
3056
|
-
return this.account.isActive;
|
|
3057
|
-
}
|
|
3058
|
-
async send(message, _options) {
|
|
3059
|
-
const { getMessageClient } = await import("@happyvertical/messages");
|
|
3060
|
-
const credentials = await this.account.getCredentials();
|
|
3061
|
-
if (!credentials?.apiKey || !credentials?.apiSecret || !credentials?.accessToken || !credentials?.accessSecret) {
|
|
3062
|
-
return {
|
|
3063
|
-
success: false,
|
|
3064
|
-
error: "Missing Twitter API credentials in account",
|
|
3065
|
-
sentAt: /* @__PURE__ */ new Date()
|
|
3066
|
-
};
|
|
3067
|
-
}
|
|
3068
|
-
const client = await getMessageClient({
|
|
3069
|
-
type: "twitter",
|
|
3070
|
-
apiKey: credentials.apiKey,
|
|
3071
|
-
apiSecret: credentials.apiSecret,
|
|
3072
|
-
accessToken: credentials.accessToken,
|
|
3073
|
-
accessSecret: credentials.accessSecret
|
|
3074
|
-
});
|
|
3075
|
-
try {
|
|
3076
|
-
const sendOptions = {};
|
|
3077
|
-
if (message.isReply && message.inReplyToMessageId) {
|
|
3078
|
-
sendOptions.replyTo = message.inReplyToMessageId;
|
|
3079
|
-
}
|
|
3080
|
-
const result = await client.send(
|
|
3081
|
-
{
|
|
3082
|
-
from: { id: this.account.twitterUserId, name: this.account.handle },
|
|
3083
|
-
content: message.body
|
|
3084
|
-
},
|
|
3085
|
-
sendOptions.replyTo ? { replyTo: sendOptions.replyTo } : void 0
|
|
3086
|
-
);
|
|
3087
|
-
return {
|
|
3088
|
-
success: result.success,
|
|
3089
|
-
providerMessageId: result.messageId,
|
|
3090
|
-
providerResponse: result.providerResponse,
|
|
3091
|
-
sentAt: result.timestamp
|
|
3092
|
-
};
|
|
3093
|
-
} catch (error) {
|
|
3094
|
-
return {
|
|
3095
|
-
success: false,
|
|
3096
|
-
error: error instanceof Error ? error.message : String(error),
|
|
3097
|
-
sentAt: /* @__PURE__ */ new Date()
|
|
3098
|
-
};
|
|
3099
|
-
}
|
|
3100
|
-
}
|
|
3101
|
-
}
|
|
3102
|
-
const TweetSender$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
3103
|
-
__proto__: null,
|
|
3104
|
-
TweetSender
|
|
3105
|
-
}, Symbol.toStringTag, { value: "Module" }));
|
|
3106
|
-
export {
|
|
3107
|
-
Account,
|
|
3108
|
-
AccountCollection,
|
|
3109
|
-
Attachment,
|
|
3110
|
-
AttachmentCollection,
|
|
3111
|
-
Blacklist,
|
|
3112
|
-
BlacklistCollection,
|
|
3113
|
-
Email,
|
|
3114
|
-
EmailAccount,
|
|
3115
|
-
EmailAccountCollection,
|
|
3116
|
-
EmailAttachment,
|
|
3117
|
-
EmailAttachmentCollection,
|
|
3118
|
-
EmailCollection,
|
|
3119
|
-
EmailFolder,
|
|
3120
|
-
EmailFolderCollection,
|
|
3121
|
-
EmailSender,
|
|
3122
|
-
Message,
|
|
3123
|
-
MessageCollection,
|
|
3124
|
-
SlackAccount,
|
|
3125
|
-
SlackMessage,
|
|
3126
|
-
SlackSender,
|
|
3127
|
-
Tweet,
|
|
3128
|
-
TweetSender,
|
|
3129
|
-
TwitterAccount,
|
|
3130
|
-
Whitelist,
|
|
3131
|
-
WhitelistCollection
|
|
1456
|
+
TwitterAccount = __decorateClass([smrt({
|
|
1457
|
+
tableStrategy: "sti",
|
|
1458
|
+
api: { include: [
|
|
1459
|
+
"list",
|
|
1460
|
+
"get",
|
|
1461
|
+
"create",
|
|
1462
|
+
"update",
|
|
1463
|
+
"delete"
|
|
1464
|
+
] },
|
|
1465
|
+
mcp: { include: ["list", "get"] },
|
|
1466
|
+
cli: true
|
|
1467
|
+
})], TwitterAccount);
|
|
1468
|
+
//#endregion
|
|
1469
|
+
//#region src/senders/EmailSender.ts
|
|
1470
|
+
var EmailSender_exports = /* @__PURE__ */ __exportAll({ EmailSender: () => EmailSender });
|
|
1471
|
+
var EmailSender = class {
|
|
1472
|
+
providerType = "email";
|
|
1473
|
+
client;
|
|
1474
|
+
account;
|
|
1475
|
+
constructor(client, account) {
|
|
1476
|
+
this.client = client;
|
|
1477
|
+
this.account = account;
|
|
1478
|
+
}
|
|
1479
|
+
isReady() {
|
|
1480
|
+
return this.client.isConnected();
|
|
1481
|
+
}
|
|
1482
|
+
async send(message, _options) {
|
|
1483
|
+
const toAddresses = message.getToAddresses();
|
|
1484
|
+
const ccAddresses = message.getCcAddresses();
|
|
1485
|
+
const bccAddresses = message.getBccAddresses();
|
|
1486
|
+
const emailMessage = {
|
|
1487
|
+
from: {
|
|
1488
|
+
name: message.fromName || this.account.name,
|
|
1489
|
+
address: message.fromAddress || this.account.email
|
|
1490
|
+
},
|
|
1491
|
+
to: toAddresses.map((r) => ({
|
|
1492
|
+
name: r.name,
|
|
1493
|
+
address: r.address
|
|
1494
|
+
})),
|
|
1495
|
+
cc: ccAddresses.length > 0 ? ccAddresses.map((r) => ({
|
|
1496
|
+
name: r.name,
|
|
1497
|
+
address: r.address
|
|
1498
|
+
})) : void 0,
|
|
1499
|
+
bcc: bccAddresses.length > 0 ? bccAddresses.map((r) => ({
|
|
1500
|
+
name: r.name,
|
|
1501
|
+
address: r.address
|
|
1502
|
+
})) : void 0,
|
|
1503
|
+
subject: message.subject,
|
|
1504
|
+
text: message.textBody || message.body,
|
|
1505
|
+
html: message.htmlBody || void 0,
|
|
1506
|
+
inReplyTo: message.inReplyTo || void 0
|
|
1507
|
+
};
|
|
1508
|
+
try {
|
|
1509
|
+
const result = await this.client.send(emailMessage);
|
|
1510
|
+
return {
|
|
1511
|
+
success: true,
|
|
1512
|
+
providerMessageId: result.messageId,
|
|
1513
|
+
accepted: result.accepted,
|
|
1514
|
+
rejected: result.rejected,
|
|
1515
|
+
providerResponse: { response: result.response },
|
|
1516
|
+
sentAt: /* @__PURE__ */ new Date()
|
|
1517
|
+
};
|
|
1518
|
+
} catch (error) {
|
|
1519
|
+
return {
|
|
1520
|
+
success: false,
|
|
1521
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1522
|
+
sentAt: /* @__PURE__ */ new Date()
|
|
1523
|
+
};
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
3132
1526
|
};
|
|
3133
|
-
//#
|
|
1527
|
+
//#endregion
|
|
1528
|
+
//#region src/senders/SlackSender.ts
|
|
1529
|
+
var SlackSender_exports = /* @__PURE__ */ __exportAll({ SlackSender: () => SlackSender });
|
|
1530
|
+
var SlackSender = class {
|
|
1531
|
+
providerType = "slack";
|
|
1532
|
+
account;
|
|
1533
|
+
constructor(account) {
|
|
1534
|
+
this.account = account;
|
|
1535
|
+
}
|
|
1536
|
+
isReady() {
|
|
1537
|
+
return this.account.isActive;
|
|
1538
|
+
}
|
|
1539
|
+
async send(message, _options) {
|
|
1540
|
+
const { getMessageClient } = await import("@happyvertical/messages");
|
|
1541
|
+
const credentials = await this.account.getCredentials();
|
|
1542
|
+
if (!credentials?.botToken) return {
|
|
1543
|
+
success: false,
|
|
1544
|
+
error: "No botToken found in account credentials",
|
|
1545
|
+
sentAt: /* @__PURE__ */ new Date()
|
|
1546
|
+
};
|
|
1547
|
+
const client = await getMessageClient({
|
|
1548
|
+
type: "slack",
|
|
1549
|
+
botToken: credentials.botToken
|
|
1550
|
+
});
|
|
1551
|
+
try {
|
|
1552
|
+
await client.connect();
|
|
1553
|
+
const result = await client.send({
|
|
1554
|
+
from: {
|
|
1555
|
+
id: this.account.botUserId,
|
|
1556
|
+
name: this.account.name
|
|
1557
|
+
},
|
|
1558
|
+
channelId: message.channelId,
|
|
1559
|
+
content: message.body
|
|
1560
|
+
}, { replyTo: message.slackThreadTs || void 0 });
|
|
1561
|
+
await client.disconnect();
|
|
1562
|
+
return {
|
|
1563
|
+
success: result.success,
|
|
1564
|
+
providerMessageId: result.messageId,
|
|
1565
|
+
providerResponse: result.providerResponse,
|
|
1566
|
+
sentAt: result.timestamp
|
|
1567
|
+
};
|
|
1568
|
+
} catch (error) {
|
|
1569
|
+
return {
|
|
1570
|
+
success: false,
|
|
1571
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1572
|
+
sentAt: /* @__PURE__ */ new Date()
|
|
1573
|
+
};
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1576
|
+
};
|
|
1577
|
+
//#endregion
|
|
1578
|
+
//#region src/senders/TweetSender.ts
|
|
1579
|
+
var TweetSender_exports = /* @__PURE__ */ __exportAll({ TweetSender: () => TweetSender });
|
|
1580
|
+
var TweetSender = class {
|
|
1581
|
+
providerType = "twitter";
|
|
1582
|
+
account;
|
|
1583
|
+
constructor(account) {
|
|
1584
|
+
this.account = account;
|
|
1585
|
+
}
|
|
1586
|
+
isReady() {
|
|
1587
|
+
return this.account.isActive;
|
|
1588
|
+
}
|
|
1589
|
+
async send(message, _options) {
|
|
1590
|
+
const { getMessageClient } = await import("@happyvertical/messages");
|
|
1591
|
+
const credentials = await this.account.getCredentials();
|
|
1592
|
+
if (!credentials?.apiKey || !credentials?.apiSecret || !credentials?.accessToken || !credentials?.accessSecret) return {
|
|
1593
|
+
success: false,
|
|
1594
|
+
error: "Missing Twitter API credentials in account",
|
|
1595
|
+
sentAt: /* @__PURE__ */ new Date()
|
|
1596
|
+
};
|
|
1597
|
+
const client = await getMessageClient({
|
|
1598
|
+
type: "twitter",
|
|
1599
|
+
apiKey: credentials.apiKey,
|
|
1600
|
+
apiSecret: credentials.apiSecret,
|
|
1601
|
+
accessToken: credentials.accessToken,
|
|
1602
|
+
accessSecret: credentials.accessSecret
|
|
1603
|
+
});
|
|
1604
|
+
try {
|
|
1605
|
+
const sendOptions = {};
|
|
1606
|
+
if (message.isReply && message.inReplyToMessageId) sendOptions.replyTo = message.inReplyToMessageId;
|
|
1607
|
+
const result = await client.send({
|
|
1608
|
+
from: {
|
|
1609
|
+
id: this.account.twitterUserId,
|
|
1610
|
+
name: this.account.handle
|
|
1611
|
+
},
|
|
1612
|
+
content: message.body
|
|
1613
|
+
}, sendOptions.replyTo ? { replyTo: sendOptions.replyTo } : void 0);
|
|
1614
|
+
return {
|
|
1615
|
+
success: result.success,
|
|
1616
|
+
providerMessageId: result.messageId,
|
|
1617
|
+
providerResponse: result.providerResponse,
|
|
1618
|
+
sentAt: result.timestamp
|
|
1619
|
+
};
|
|
1620
|
+
} catch (error) {
|
|
1621
|
+
return {
|
|
1622
|
+
success: false,
|
|
1623
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1624
|
+
sentAt: /* @__PURE__ */ new Date()
|
|
1625
|
+
};
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
};
|
|
1629
|
+
//#endregion
|
|
1630
|
+
export { Account, AccountCollection, Attachment, AttachmentCollection, Blacklist, BlacklistCollection, Email, EmailAccount, EmailAccountCollection, EmailAttachment, EmailAttachmentCollection, EmailCollection, EmailFolder, EmailFolderCollection, EmailSender, Message, MessageCollection, SlackAccount, SlackMessage, SlackSender, Tweet, TweetSender, TwitterAccount, Whitelist, WhitelistCollection, AttachmentCollection_exports as i, EmailCollection_exports as n, EmailAccountCollection_exports as r, EmailFolderCollection_exports as t };
|
|
1631
|
+
|
|
1632
|
+
//# sourceMappingURL=index.js.map
|