@social-mail/social-mail-web-server 1.5.98 → 1.5.99

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/server/model/entities/LoginSession.d.ts +2 -2
  2. package/dist/server/model/entities/LoginSession.d.ts.map +1 -1
  3. package/dist/server/model/entities/LoginSession.js +2 -2
  4. package/dist/server/model/entities/LoginSession.js.map +1 -1
  5. package/dist/server/model/entities/MailboxEmail.d.ts +13 -0
  6. package/dist/server/model/entities/MailboxEmail.d.ts.map +1 -1
  7. package/dist/server/model/entities/MailboxEmail.js +30 -1
  8. package/dist/server/model/entities/MailboxEmail.js.map +1 -1
  9. package/dist/server/model/events/EmailEvents.d.ts.map +1 -1
  10. package/dist/server/model/events/EmailEvents.js +13 -2
  11. package/dist/server/model/events/EmailEvents.js.map +1 -1
  12. package/dist/server/model/events/LoginSessionEvents.js +7 -7
  13. package/dist/server/model/events/LoginSessionEvents.js.map +1 -1
  14. package/dist/server/model/events/MailboxEmailEvents.d.ts +1 -0
  15. package/dist/server/model/events/MailboxEmailEvents.d.ts.map +1 -1
  16. package/dist/server/model/events/MailboxEmailEvents.js +32 -0
  17. package/dist/server/model/events/MailboxEmailEvents.js.map +1 -1
  18. package/dist/server/model/events/MailboxEvents.d.ts.map +1 -1
  19. package/dist/server/model/events/MailboxEvents.js +4 -2
  20. package/dist/server/model/events/MailboxEvents.js.map +1 -1
  21. package/dist/server/model/events/UserEvents.js +1 -1
  22. package/dist/server/model/events/UserEvents.js.map +1 -1
  23. package/dist/server/services/UserInfoProvider.js +1 -1
  24. package/dist/server/services/UserInfoProvider.js.map +1 -1
  25. package/dist/server/smtp/services/EngagementService.d.ts.map +1 -1
  26. package/dist/server/smtp/services/EngagementService.js +7 -0
  27. package/dist/server/smtp/services/EngagementService.js.map +1 -1
  28. package/dist/server/workflows/SocialWorkflowContext.d.ts.map +1 -1
  29. package/dist/server/workflows/SocialWorkflowContext.js +2 -0
  30. package/dist/server/workflows/SocialWorkflowContext.js.map +1 -1
  31. package/dist/server/workflows/channel/BroadcastEmail.d.ts.map +1 -1
  32. package/dist/server/workflows/channel/BroadcastEmail.js +6 -1
  33. package/dist/server/workflows/channel/BroadcastEmail.js.map +1 -1
  34. package/dist/server/workflows/daily/DailyWorkflow.js +1 -1
  35. package/dist/server/workflows/daily/DailyWorkflow.js.map +1 -1
  36. package/dist/server/workflows/repair/channel/RepairChannelEmailCountsWorkflow.d.ts +8 -0
  37. package/dist/server/workflows/repair/channel/RepairChannelEmailCountsWorkflow.d.ts.map +1 -0
  38. package/dist/server/workflows/repair/channel/RepairChannelEmailCountsWorkflow.js +57 -0
  39. package/dist/server/workflows/repair/channel/RepairChannelEmailCountsWorkflow.js.map +1 -0
  40. package/dist/tsconfig.tsbuildinfo +1 -1
  41. package/package.json +2 -2
  42. package/src/server/model/entities/LoginSession.ts +2 -2
  43. package/src/server/model/entities/MailboxEmail.ts +35 -0
  44. package/src/server/model/events/EmailEvents.ts +13 -2
  45. package/src/server/model/events/LoginSessionEvents.ts +7 -7
  46. package/src/server/model/events/MailboxEmailEvents.ts +23 -0
  47. package/src/server/model/events/MailboxEvents.ts +7 -2
  48. package/src/server/model/events/UserEvents.ts +1 -1
  49. package/src/server/services/UserInfoProvider.ts +1 -1
  50. package/src/server/smtp/services/EngagementService.ts +8 -0
  51. package/src/server/workflows/SocialWorkflowContext.ts +2 -0
  52. package/src/server/workflows/channel/BroadcastEmail.ts +7 -1
  53. package/src/server/workflows/daily/DailyWorkflow.ts +1 -1
  54. package/src/server/workflows/repair/channel/RepairChannelEmailCountsWorkflow.ts +49 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@social-mail/social-mail-web-server",
3
- "version": "1.5.98",
3
+ "version": "1.5.99",
4
4
  "description": "## Phase 1",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -21,7 +21,7 @@
21
21
  "dependencies": {
22
22
  "@aws-sdk/client-s3": "^3.609.0",
23
23
  "@azure/storage-blob": "^12.25.0",
24
- "@entity-access/entity-access": "^1.0.360",
24
+ "@entity-access/entity-access": "^1.0.364",
25
25
  "@entity-access/server-pages": "^1.1.292",
26
26
  "@otplib/preset-v11": "^12.0.1",
27
27
  "@social-mail/noto-emoji-svg": "^2.0.42",
@@ -35,11 +35,11 @@ export default class LoginSession {
35
35
 
36
36
  @Column({})
37
37
  @IgnoreJsonProperty
38
- public start: Date;
38
+ public start: DateTime;
39
39
 
40
40
  @Column({})
41
41
  @IgnoreJsonProperty
42
- public expiry: Date;
42
+ public expiry: DateTime;
43
43
 
44
44
  @Column({})
45
45
  public invalid: boolean;
@@ -3,8 +3,25 @@ import { RelateTo } from "@entity-access/entity-access/dist/decorators/Relate.js
3
3
  import Table from "@entity-access/entity-access/dist/decorators/Table.js";
4
4
  import { Email } from "./Email.js";
5
5
  import Mailbox from "./Mailbox.js";
6
+ import DateTime from "@entity-access/entity-access/dist/types/DateTime.js";
7
+ import Index from "@entity-access/entity-access/dist/decorators/Index.js";
6
8
 
7
9
  @Table("MailboxEmails")
10
+ @Index({
11
+ name: "IX_Mailbox_Email_Engagement_Count",
12
+ columns: [
13
+ { name: (x) => x.dateUpdated, descending: true },
14
+ { name: (x) => x.engagementCount, descending: true },
15
+ ],
16
+ filter: (x) => x.dateUpdated !== null
17
+ })
18
+ @Index({
19
+ name: "IX_Mailbox_Email_Published",
20
+ columns: [
21
+ { name: (x) => x.datePublished, descending: true },
22
+ ],
23
+ filter: (x) => x.datePublished !== null
24
+ })
8
25
  export default class MailboxEmail {
9
26
 
10
27
  @Column({ dataType: "BigInt", key: true, order: 1 })
@@ -29,6 +46,24 @@ export default class MailboxEmail {
29
46
 
30
47
  email: Email;
31
48
 
49
+ /**
50
+ * Forwarded from email for caching, only for channels
51
+ */
52
+ @Column({ dataType: "Int", default: () => 0})
53
+ engagementCount: number;
54
+
55
+ /**
56
+ * Forwarded from email for caching, only for channels
57
+ */
58
+ @Column({ dataType: "DateTime", nullable: true})
59
+ dateUpdated: DateTime;
60
+
61
+ /**
62
+ * Forwarded from email for caching, only for channels
63
+ */
64
+ @Column({ dataType: "DateTime", nullable: true})
65
+ datePublished: DateTime;
66
+
32
67
  mailbox: Mailbox;
33
68
 
34
69
  }
@@ -208,8 +208,19 @@ export default class EmailEvents extends AuthenticatedEvents<Email> {
208
208
  return this.updateScheduleAndReferences(entity);
209
209
  }
210
210
 
211
- afterUpdate(entity: Email, entry: ChangeEntry<Email>) {
212
- return this.updateScheduleAndReferences(entity);
211
+ async afterUpdate(entity: Email, entry: ChangeEntry<Email>) {
212
+ if (entry.isUpdated("status")) {
213
+ switch(entity.status) {
214
+ case "published":
215
+ await this.db.mailboxEmails.where(entity, (p) => (x) => x.emailID === p.emailID)
216
+ .update(entity, (p) => (x) => ({ datePublished: Sql.cast.asDateTime(p.dateSent) as any }));
217
+ break;
218
+ default:
219
+ await this.db.mailboxEmails.where(entity, (p) => (x) => x.emailID === p.emailID)
220
+ .update(void 0, (p) => (x) => ({ datePublished: Sql.cast.asDateTime(null) as any }));
221
+ }
222
+ }
223
+ await this.updateScheduleAndReferences(entity);
213
224
  }
214
225
 
215
226
  includeFilter(query: IEntityQuery<Email>, type?: any, key?: string): IEntityQuery<Email> {
@@ -95,8 +95,8 @@ export default class LoginSessionEvents extends EntityEvents<LoginSession> {
95
95
  && x.expires >= p.now)
96
96
  .firstOrFail();
97
97
  entity.userID = user.userID;
98
- entity.start = now.asJSDate;
99
- entity.expiry = now.addDays(30).asJSDate;
98
+ entity.start = now;
99
+ entity.expiry = now.addDays(30);
100
100
  entity.dateUpdated = now;
101
101
  entity.invalid = false;
102
102
  entity.status = "created";
@@ -120,8 +120,8 @@ export default class LoginSessionEvents extends EntityEvents<LoginSession> {
120
120
  }
121
121
  }
122
122
  entity.userID = user.userID;
123
- entity.start = now.asJSDate;
124
- entity.expiry = now.addDays(30).asJSDate;
123
+ entity.start = now;
124
+ entity.expiry = now.addDays(30);
125
125
  entity.dateUpdated = now;
126
126
  entity.invalid = false;
127
127
  entity.status = "created";
@@ -165,7 +165,7 @@ export default class LoginSessionEvents extends EntityEvents<LoginSession> {
165
165
  await this.sessionUser.setAuthCookie({
166
166
  userID: entity.userID,
167
167
  id: entity.sessionID,
168
- expiry: entity.expiry,
168
+ expiry: entity.expiry.asJSDate,
169
169
  version: "1.1",
170
170
  active: false
171
171
  });
@@ -176,7 +176,7 @@ export default class LoginSessionEvents extends EntityEvents<LoginSession> {
176
176
  await this.sessionUser.setAuthCookie({
177
177
  userID: entity.userID,
178
178
  id: entity.sessionID,
179
- expiry: entity.expiry,
179
+ expiry: entity.expiry.asJSDate,
180
180
  version: "1.1",
181
181
  active: true
182
182
  });
@@ -304,7 +304,7 @@ export default class LoginSessionEvents extends EntityEvents<LoginSession> {
304
304
  await this.sessionUser.setAuthCookie({
305
305
  userID: entity.userID,
306
306
  id: entity.sessionID,
307
- expiry: entity.expiry,
307
+ expiry: entity.expiry.asJSDate,
308
308
  version: "1.1",
309
309
  active: true
310
310
  });
@@ -2,6 +2,12 @@ import ChangeEntry from "@entity-access/entity-access/dist/model/changes/ChangeE
2
2
  import MailboxEmail from "../entities/MailboxEmail.js";
3
3
  import AuthenticatedEvents from "./AuthenticatedEvents.js";
4
4
  import { IEntityQuery } from "@entity-access/entity-access/dist/model/IFilterWithParameter.js";
5
+ import { ServiceProvider } from "@entity-access/entity-access/dist/di/di.js";
6
+ import Sql from "@entity-access/entity-access/dist/sql/Sql.js";
7
+ import ExternalQuery from "@entity-access/server-pages/dist/decorators/ExternalQuery.js";
8
+ import WebSiteChannelService from "../../smtp/services/WebSiteChannelService.js";
9
+ import Page from "@entity-access/server-pages/dist/Page.js";
10
+ import SocialMailContext from "../SocialMailContext.js";
5
11
 
6
12
  export default class MailboxEmailEvents extends AuthenticatedEvents<MailboxEmail> {
7
13
 
@@ -19,6 +25,23 @@ export default class MailboxEmailEvents extends AuthenticatedEvents<MailboxEmail
19
25
  || x.mailbox.domain.hostedDomain.domainUsers.some((d) => d.userID === p.userID));
20
26
  }
21
27
 
28
+ @ExternalQuery
29
+ async channelEmails(channelID) {
30
+ // get current host
31
+ const page = ServiceProvider.resolve(this, Page as any) as Page;
32
+ const host = page.request.host;
33
+
34
+ const wcs = ServiceProvider.resolve(this, WebSiteChannelService);
35
+ const mailboxes = await wcs.getMailboxes(host);
36
+
37
+ const db = ServiceProvider.resolve(this, SocialMailContext);
38
+
39
+ const q = db.mailboxEmails.where({ channelID, host, mailboxes}, (p) => (x) =>
40
+ x.datePublished !== null
41
+ && Sql.in(x.mailboxID, p.mailboxes));
42
+ return q.limit(25);
43
+ }
44
+
22
45
  beforeInsert(entity: MailboxEmail, entry: ChangeEntry<MailboxEmail>) {
23
46
  if (this.verify) {
24
47
  this.sessionUser.ensureLoggedIn();
@@ -17,6 +17,7 @@ import { Prepare } from "@entity-access/server-pages/dist/decorators/Prepare.js"
17
17
  import EntityAccessError from "@entity-access/entity-access/dist/common/EntityAccessError.js";
18
18
  import SyncMailboxWorkflow from "../../workflows/sync/SyncMailboxWorkflow.js";
19
19
  import TagService from "../../services/tags/TagService.js";
20
+ import WebSiteChannelService from "../../smtp/services/WebSiteChannelService.js";
20
21
 
21
22
  export default class MailboxEvents extends AuthenticatedEvents<Mailbox> {
22
23
 
@@ -45,8 +46,12 @@ export default class MailboxEvents extends AuthenticatedEvents<Mailbox> {
45
46
  const page = ServiceProvider.resolve(this, Page as any) as Page;
46
47
  const host = page.request.host;
47
48
 
48
- let q = this.db.mailboxes.where(void 0, (p) => (x) => x.isChannel === true && x.isPrivateChannel === false);
49
- q = q.where({ host }, (p) => (x) => x.websiteChannels.some((c) => c.website.hosts.some((h) => h.host === p.host)));
49
+ const wcs = ServiceProvider.resolve(this, WebSiteChannelService);
50
+
51
+ const mailboxes = await wcs.getMailboxes(host);
52
+
53
+ let q = this.db.mailboxes.where({ mailboxes }, (p) => (x) => Sql.in(x.mailboxID, p.mailboxes) );
54
+
50
55
  if (text) {
51
56
  text = text.toLocaleLowerCase();
52
57
  if (text.startsWith("@")) {
@@ -218,7 +218,7 @@ export default class UserEvents extends AuthenticatedEvents<User> {
218
218
  async afterUpdate(entity: User, entry: ChangeEntry<User>) {
219
219
  if (entry.isUpdated("status")) {
220
220
  if (entity.status === "blocked") {
221
- const now = new Date();
221
+ const now = DateTime.now;
222
222
  const { userID } = entity;
223
223
 
224
224
  await this.db.loginSessions.where({ userID, now}, (p) => (x) => x.userID === p.userID
@@ -30,7 +30,7 @@ export default class UserSessionProvider extends USP {
30
30
  userID,
31
31
  sessionID,
32
32
  userName: session.user.userName,
33
- expiry: session.expiry,
33
+ expiry: session.expiry.asJSDate,
34
34
  roles: session.user.roles?.map((x) => x.roleName) ?? [],
35
35
  fileAccessList: session.user.fileAccessList?.map(({ type, file: { path }}) => ({ type, path })),
36
36
  invalid: false
@@ -187,6 +187,14 @@ export default class EngagementService {
187
187
 
188
188
  const ea = { emailID, engagement };
189
189
 
190
+ if (email.status === "published") {
191
+ await this.db.mailboxEmails.where(ea, (p) => (x) => x.emailID === p.emailID)
192
+ .update(void 0, (p) => (x) => ({
193
+ engagementCount: x.engagementCount + 1,
194
+ dateUpdated: Sql.date.now()
195
+ }));
196
+ }
197
+
190
198
  await this.db.emails.where(ea, (p) => (x) => x.emailID === p.emailID)
191
199
  .update(void 0, (p) => (x) => ({
192
200
  engagementCount: x.engagementCount + 1,
@@ -33,6 +33,7 @@ import RepairMailboxFoldersWorkflow from "./repair/mailbox-folders/RepairMailbox
33
33
  import DispatchNotificationToMX from "./smtp/DispatchNotificationToMX.js";
34
34
  import RepairCacheClearWorkflow from "./repair/cache/RepairCacheClearWorkflow.js";
35
35
  import RepairEmailNameTagsWorkflow from "./repair/tags/RepairEmailNameTagsWorkflow.js";
36
+ import RepairChannelEmailCountsWorkflow from "./repair/channel/RepairChannelEmailCountsWorkflow.js";
36
37
 
37
38
  @RegisterSingleton
38
39
  export default class SocialWorkflowContext extends WorkflowContext {
@@ -66,6 +67,7 @@ export default class SocialWorkflowContext extends WorkflowContext {
66
67
  this.register(RepairStoreTagsWorkflow);
67
68
  this.register(RepairCacheClearWorkflow);
68
69
  this.register(RepairEmailNameTagsWorkflow);
70
+ this.register(RepairChannelEmailCountsWorkflow);
69
71
  // this.register(MigrateEmailFilesWorkflow);
70
72
  this.register(CleanupDeletedFilesWorkflow);
71
73
 
@@ -85,7 +85,13 @@ export default class BroadcastEmail extends Workflow<IBroadcastEmail> {
85
85
  .update(void 0, (p) => (x) => ({ status: "published"}));
86
86
 
87
87
  await db.mailboxes.where(this.input, (p) => (x) => x.mailboxID === p.mailboxID)
88
- .update(void 0, (p) => (x) => ({ datePublished: Sql.date.now()}));
88
+ .update(void 0, (p) => (x) => ({ datePublished: Sql.cast.asDateTime(Sql.date.now()) }));
89
+
90
+ await db.mailboxEmails.where(this.input, (p) => (x) => x.emailID === p.emailID && x.mailboxID === p.mailboxID)
91
+ .update(void 0, (p) => (x) => ({
92
+ dateUpdated: Sql.cast.asDateTime(Sql.date.now()),
93
+ datePublished: Sql.cast.asDateTime(Sql.date.now())
94
+ }));
89
95
 
90
96
  // load email..
91
97
  const from = (await eas.getEmailAddress(this.input.mailboxID)).emailAddress;
@@ -72,7 +72,7 @@ export default class DailyWorkflow extends Workflow<any,any> {
72
72
 
73
73
  // deactivate all expired sessions...
74
74
  await db.loginSessions
75
- .where(void 0, (p) => (x) => x.expiry <= (Sql.date.now() as any as Date))
75
+ .where(void 0, (p) => (x) => x.expiry <= (Sql.date.now()))
76
76
  .limit(1000)
77
77
  .update(void 0, (p) => (x) => ({ invalid: true }));
78
78
 
@@ -0,0 +1,49 @@
1
+ import Inject from "@entity-access/entity-access/dist/di/di.js";
2
+ import Workflow, { UniqueActivity } from "@entity-access/entity-access/dist/workflows/Workflow.js";
3
+ import SocialMailContext from "../../../model/SocialMailContext.js";
4
+ import Sql from "@entity-access/entity-access/dist/sql/Sql.js";
5
+
6
+ export default class RepairChannelEmailCountsWorkflow extends Workflow {
7
+
8
+ static taskGroup = "batch";
9
+
10
+ async run() {
11
+ let start = 0;
12
+ do{
13
+ start = await this.fixEmailTags(start);
14
+ } while(BigInt(start));
15
+ }
16
+
17
+ @UniqueActivity
18
+ async fixEmailTags(
19
+ start: number,
20
+ @Inject db?: SocialMailContext
21
+ ) {
22
+
23
+ for(let i = 0;i< 100;i++) {
24
+ const mailboxes = await db.mailboxes.where({ start }, (p) => (x) => x.mailboxID > p.start && x.isChannel === true)
25
+ .orderBy(void 0, (p) => (x) => x.mailboxID)
26
+ .limit(10)
27
+ .include((x) => [x.folders])
28
+ .toArray();
29
+
30
+ if (!mailboxes.length) {
31
+ return 0;
32
+ }
33
+
34
+ for (const iterator of mailboxes) {
35
+ start = iterator.mailboxID;
36
+
37
+ await db.mailboxEmails.where(iterator, (p) => (x) => x.mailboxID === p.mailboxID)
38
+ .update(void 0, (p) => (x) => ({
39
+ datePublished: Sql.cast.asDateTime( x.email.status === "published" ? x.email.dateSent : null ) as any,
40
+ dateUpdated: Sql.cast.asDateTime( x.email.status === "published" ? x.email.dateUpdated : null ) as any,
41
+ engagementCount: Sql.cast.asInteger( x.email.status === "published" ? x.email.engagementCount : 0 ) as any
42
+ }));
43
+
44
+ }
45
+ db.changeSet.clear();
46
+ }
47
+ return start;
48
+ }
49
+ }