@rapidmx/activesync-plugin 1.0.0-beta.2 → 1.0.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/lib/BaseDeviceSyncStateRoute.js +32 -0
- package/dist/lib/BaseDeviceSyncStateRoute.js.map +1 -1
- package/dist/lib/BaseEasRoute.js +91 -15
- package/dist/lib/BaseEasRoute.js.map +1 -1
- package/dist/lib/EasAuditLog.js +56 -0
- package/dist/lib/EasAuditLog.js.map +1 -0
- package/dist/lib/EasCollectionLease.js +199 -0
- package/dist/lib/EasCollectionLease.js.map +1 -0
- package/dist/lib/EasCollectionStore.js +102 -0
- package/dist/lib/EasCollectionStore.js.map +1 -0
- package/dist/lib/EasCollectionSync.js +294 -0
- package/dist/lib/EasCollectionSync.js.map +1 -0
- package/dist/lib/EasSyncKeyUtils.js +125 -62
- package/dist/lib/EasSyncKeyUtils.js.map +1 -1
- package/dist/lib/MessageMoveRules.js +100 -0
- package/dist/lib/MessageMoveRules.js.map +1 -0
- package/dist/lib/MimeHeaderUtils.js +395 -0
- package/dist/lib/MimeHeaderUtils.js.map +1 -0
- package/dist/lib/RegexPatternUtils.js +30 -0
- package/dist/lib/RegexPatternUtils.js.map +1 -0
- package/dist/lib/RestapiCompat.js +41 -0
- package/dist/lib/RestapiCompat.js.map +1 -0
- package/dist/lib/adapters/CalendarSyncAdapter.js +142 -33
- package/dist/lib/adapters/CalendarSyncAdapter.js.map +1 -1
- package/dist/lib/adapters/EmailSyncAdapter.js +114 -27
- package/dist/lib/adapters/EmailSyncAdapter.js.map +1 -1
- package/dist/lib/adapters/mongo/EmailSyncAdapterMongo.js +2 -1
- package/dist/lib/adapters/mongo/EmailSyncAdapterMongo.js.map +1 -1
- package/dist/lib/adapters/sql/EmailSyncAdapterSQL.js +2 -1
- package/dist/lib/adapters/sql/EmailSyncAdapterSQL.js.map +1 -1
- package/dist/lib/codec/WbxmlDecoder.js +86 -34
- package/dist/lib/codec/WbxmlDecoder.js.map +1 -1
- package/dist/lib/codec/WbxmlEncoder.js +103 -22
- package/dist/lib/codec/WbxmlEncoder.js.map +1 -1
- package/dist/lib/commands/ComposeMailCommand.js +105 -31
- package/dist/lib/commands/ComposeMailCommand.js.map +1 -1
- package/dist/lib/commands/FolderSyncCommand.js +67 -27
- package/dist/lib/commands/FolderSyncCommand.js.map +1 -1
- package/dist/lib/commands/GetItemEstimateCommand.js +80 -59
- package/dist/lib/commands/GetItemEstimateCommand.js.map +1 -1
- package/dist/lib/commands/ItemOperationsCommand.js +219 -56
- package/dist/lib/commands/ItemOperationsCommand.js.map +1 -1
- package/dist/lib/commands/MeetingResponseCommand.js +212 -53
- package/dist/lib/commands/MeetingResponseCommand.js.map +1 -1
- package/dist/lib/commands/MoveItemsCommand.js +54 -21
- package/dist/lib/commands/MoveItemsCommand.js.map +1 -1
- package/dist/lib/commands/PingCommand.js +288 -56
- package/dist/lib/commands/PingCommand.js.map +1 -1
- package/dist/lib/commands/ProvisionCommand.js +34 -17
- package/dist/lib/commands/ProvisionCommand.js.map +1 -1
- package/dist/lib/commands/ResolveRecipientsCommand.js +43 -5
- package/dist/lib/commands/ResolveRecipientsCommand.js.map +1 -1
- package/dist/lib/commands/SearchCommand.js +92 -17
- package/dist/lib/commands/SearchCommand.js.map +1 -1
- package/dist/lib/commands/SettingsCommand.js +2 -1
- package/dist/lib/commands/SettingsCommand.js.map +1 -1
- package/dist/lib/commands/SmartForwardCommand.js +2 -1
- package/dist/lib/commands/SmartForwardCommand.js.map +1 -1
- package/dist/lib/commands/SmartReplyCommand.js +2 -1
- package/dist/lib/commands/SmartReplyCommand.js.map +1 -1
- package/dist/lib/commands/SyncCommand.js +481 -244
- package/dist/lib/commands/SyncCommand.js.map +1 -1
- package/dist/lib/commands/mongo/GetItemEstimateCommandMongo.js +6 -1
- package/dist/lib/commands/mongo/GetItemEstimateCommandMongo.js.map +1 -1
- package/dist/lib/commands/mongo/ItemOperationsCommandMongo.js +3 -1
- package/dist/lib/commands/mongo/ItemOperationsCommandMongo.js.map +1 -1
- package/dist/lib/commands/mongo/MeetingResponseCommandMongo.js +2 -1
- package/dist/lib/commands/mongo/MeetingResponseCommandMongo.js.map +1 -1
- package/dist/lib/commands/mongo/PingCommandMongo.js +23 -0
- package/dist/lib/commands/mongo/PingCommandMongo.js.map +1 -0
- package/dist/lib/commands/mongo/SearchCommandMongo.js +3 -1
- package/dist/lib/commands/mongo/SearchCommandMongo.js.map +1 -1
- package/dist/lib/commands/mongo/SendMailCommandMongo.js +2 -1
- package/dist/lib/commands/mongo/SendMailCommandMongo.js.map +1 -1
- package/dist/lib/commands/mongo/SmartForwardCommandMongo.js +2 -1
- package/dist/lib/commands/mongo/SmartForwardCommandMongo.js.map +1 -1
- package/dist/lib/commands/mongo/SmartReplyCommandMongo.js +2 -1
- package/dist/lib/commands/mongo/SmartReplyCommandMongo.js.map +1 -1
- package/dist/lib/commands/mongo/SyncCommandMongo.js +7 -1
- package/dist/lib/commands/mongo/SyncCommandMongo.js.map +1 -1
- package/dist/lib/commands/mongo/index.js +1 -0
- package/dist/lib/commands/mongo/index.js.map +1 -1
- package/dist/lib/commands/sql/GetItemEstimateCommandSQL.js +6 -1
- package/dist/lib/commands/sql/GetItemEstimateCommandSQL.js.map +1 -1
- package/dist/lib/commands/sql/ItemOperationsCommandSQL.js +3 -1
- package/dist/lib/commands/sql/ItemOperationsCommandSQL.js.map +1 -1
- package/dist/lib/commands/sql/MeetingResponseCommandSQL.js +2 -1
- package/dist/lib/commands/sql/MeetingResponseCommandSQL.js.map +1 -1
- package/dist/lib/commands/sql/PingCommandSQL.js +23 -0
- package/dist/lib/commands/sql/PingCommandSQL.js.map +1 -0
- package/dist/lib/commands/sql/SearchCommandSQL.js +3 -1
- package/dist/lib/commands/sql/SearchCommandSQL.js.map +1 -1
- package/dist/lib/commands/sql/SendMailCommandSQL.js +2 -1
- package/dist/lib/commands/sql/SendMailCommandSQL.js.map +1 -1
- package/dist/lib/commands/sql/SmartForwardCommandSQL.js +2 -1
- package/dist/lib/commands/sql/SmartForwardCommandSQL.js.map +1 -1
- package/dist/lib/commands/sql/SmartReplyCommandSQL.js +2 -1
- package/dist/lib/commands/sql/SmartReplyCommandSQL.js.map +1 -1
- package/dist/lib/commands/sql/SyncCommandSQL.js +7 -1
- package/dist/lib/commands/sql/SyncCommandSQL.js.map +1 -1
- package/dist/lib/commands/sql/index.js +1 -0
- package/dist/lib/commands/sql/index.js.map +1 -1
- package/dist/lib/index.js +5 -0
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/jobs/EasDeviceStateCleanupJob.js +44 -2
- package/dist/lib/jobs/EasDeviceStateCleanupJob.js.map +1 -1
- package/dist/lib/jobs/mongo/EasDeviceStateCleanupJobMongo.js +4 -0
- package/dist/lib/jobs/mongo/EasDeviceStateCleanupJobMongo.js.map +1 -1
- package/dist/lib/jobs/sql/EasDeviceStateCleanupJobSQL.js +9 -0
- package/dist/lib/jobs/sql/EasDeviceStateCleanupJobSQL.js.map +1 -1
- package/dist/lib/models/EasCollectionChunk.js +2 -0
- package/dist/lib/models/EasCollectionChunk.js.map +1 -0
- package/dist/lib/models/EasCollectionState.js +2 -0
- package/dist/lib/models/EasCollectionState.js.map +1 -0
- package/dist/lib/models/mongo/DeviceSyncStateMongo.js +7 -0
- package/dist/lib/models/mongo/DeviceSyncStateMongo.js.map +1 -1
- package/dist/lib/models/mongo/EasCollectionChunkMongo.js +83 -0
- package/dist/lib/models/mongo/EasCollectionChunkMongo.js.map +1 -0
- package/dist/lib/models/mongo/EasCollectionStateMongo.js +162 -0
- package/dist/lib/models/mongo/EasCollectionStateMongo.js.map +1 -0
- package/dist/lib/models/sql/DeviceSyncStateSQL.js +7 -0
- package/dist/lib/models/sql/DeviceSyncStateSQL.js.map +1 -1
- package/dist/lib/models/sql/EasCollectionChunkSQL.js +83 -0
- package/dist/lib/models/sql/EasCollectionChunkSQL.js.map +1 -0
- package/dist/lib/models/sql/EasCollectionStateSQL.js +162 -0
- package/dist/lib/models/sql/EasCollectionStateSQL.js.map +1 -0
- package/dist/lib/mongo/EasRouteMongo.js +2 -2
- package/dist/lib/mongo/EasRouteMongo.js.map +1 -1
- package/dist/lib/mongo.js +2 -0
- package/dist/lib/mongo.js.map +1 -1
- package/dist/lib/sql/EasRouteSQL.js +2 -2
- package/dist/lib/sql/EasRouteSQL.js.map +1 -1
- package/dist/lib/sql.js +2 -0
- package/dist/lib/sql.js.map +1 -1
- package/dist/types/BaseDeviceSyncStateRoute.d.ts +10 -0
- package/dist/types/BaseEasRoute.d.ts +15 -4
- package/dist/types/EasAuditLog.d.ts +47 -0
- package/dist/types/EasCollectionLease.d.ts +60 -0
- package/dist/types/EasCollectionStore.d.ts +51 -0
- package/dist/types/EasCollectionSync.d.ts +96 -0
- package/dist/types/EasCommandHandler.d.ts +7 -4
- package/dist/types/EasSyncKeyUtils.d.ts +73 -42
- package/dist/types/MessageMoveRules.d.ts +45 -0
- package/dist/types/MimeHeaderUtils.d.ts +73 -0
- package/dist/types/RegexPatternUtils.d.ts +14 -0
- package/dist/types/RestapiCompat.d.ts +24 -0
- package/dist/types/adapters/CalendarSyncAdapter.d.ts +47 -18
- package/dist/types/adapters/EasCollectionSyncAdapter.d.ts +18 -2
- package/dist/types/adapters/EmailSyncAdapter.d.ts +24 -7
- package/dist/types/adapters/mongo/EmailSyncAdapterMongo.d.ts +1 -0
- package/dist/types/adapters/sql/EmailSyncAdapterSQL.d.ts +1 -0
- package/dist/types/codec/WbxmlDecoder.d.ts +39 -0
- package/dist/types/codec/WbxmlEncoder.d.ts +36 -1
- package/dist/types/commands/ComposeMailCommand.d.ts +33 -15
- package/dist/types/commands/FolderSyncCommand.d.ts +4 -1
- package/dist/types/commands/GetItemEstimateCommand.d.ts +20 -15
- package/dist/types/commands/ItemOperationsCommand.d.ts +29 -4
- package/dist/types/commands/MeetingResponseCommand.d.ts +52 -13
- package/dist/types/commands/MoveItemsCommand.d.ts +13 -3
- package/dist/types/commands/PingCommand.d.ts +72 -14
- package/dist/types/commands/ProvisionCommand.d.ts +16 -11
- package/dist/types/commands/ResolveRecipientsCommand.d.ts +4 -0
- package/dist/types/commands/SearchCommand.d.ts +13 -1
- package/dist/types/commands/SyncCommand.d.ts +102 -62
- package/dist/types/commands/mongo/GetItemEstimateCommandMongo.d.ts +3 -0
- package/dist/types/commands/mongo/ItemOperationsCommandMongo.d.ts +2 -0
- package/dist/types/commands/mongo/MeetingResponseCommandMongo.d.ts +1 -0
- package/dist/types/commands/mongo/PingCommandMongo.d.ts +8 -0
- package/dist/types/commands/mongo/SearchCommandMongo.d.ts +2 -0
- package/dist/types/commands/mongo/SendMailCommandMongo.d.ts +1 -0
- package/dist/types/commands/mongo/SmartForwardCommandMongo.d.ts +1 -0
- package/dist/types/commands/mongo/SmartReplyCommandMongo.d.ts +1 -0
- package/dist/types/commands/mongo/SyncCommandMongo.d.ts +4 -0
- package/dist/types/commands/mongo/index.d.ts +1 -0
- package/dist/types/commands/sql/GetItemEstimateCommandSQL.d.ts +3 -0
- package/dist/types/commands/sql/ItemOperationsCommandSQL.d.ts +2 -0
- package/dist/types/commands/sql/MeetingResponseCommandSQL.d.ts +1 -0
- package/dist/types/commands/sql/PingCommandSQL.d.ts +8 -0
- package/dist/types/commands/sql/SearchCommandSQL.d.ts +2 -0
- package/dist/types/commands/sql/SendMailCommandSQL.d.ts +1 -0
- package/dist/types/commands/sql/SmartForwardCommandSQL.d.ts +1 -0
- package/dist/types/commands/sql/SmartReplyCommandSQL.d.ts +1 -0
- package/dist/types/commands/sql/SyncCommandSQL.d.ts +4 -0
- package/dist/types/commands/sql/index.d.ts +1 -0
- package/dist/types/index.d.ts +5 -0
- package/dist/types/jobs/EasDeviceStateCleanupJob.d.ts +15 -0
- package/dist/types/jobs/mongo/EasDeviceStateCleanupJobMongo.d.ts +2 -0
- package/dist/types/jobs/sql/EasDeviceStateCleanupJobSQL.d.ts +4 -0
- package/dist/types/models/DeviceSyncState.d.ts +13 -2
- package/dist/types/models/EasCollectionChunk.d.ts +20 -0
- package/dist/types/models/EasCollectionState.d.ts +79 -0
- package/dist/types/models/mongo/DeviceSyncStateMongo.d.ts +1 -0
- package/dist/types/models/mongo/EasCollectionChunkMongo.d.ts +16 -0
- package/dist/types/models/mongo/EasCollectionStateMongo.d.ts +27 -0
- package/dist/types/models/sql/DeviceSyncStateSQL.d.ts +1 -0
- package/dist/types/models/sql/EasCollectionChunkSQL.d.ts +16 -0
- package/dist/types/models/sql/EasCollectionStateSQL.d.ts +27 -0
- package/dist/types/mongo.d.ts +2 -0
- package/dist/types/sql.d.ts +2 -0
- package/package.json +267 -238
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
11
|
+
// Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
|
|
12
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
13
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
14
|
+
import { BaseEntity, DocDecorators, ModelDecorators, PersistenceDecorators } from "@rapidrest/service-core";
|
|
15
|
+
import { MailboxScopedData } from "@rapidmx/restapi";
|
|
16
|
+
const { Description } = DocDecorators;
|
|
17
|
+
const { DataStore, Protect } = ModelDecorators;
|
|
18
|
+
const { Column, Entity, Index } = PersistenceDecorators;
|
|
19
|
+
/**
|
|
20
|
+
* Implementation of the `EasCollectionChunk` interface for storage in a SQL database. If MongoDB is desired,
|
|
21
|
+
* please use `models.mongo.EasCollectionChunkMongo` instead.
|
|
22
|
+
*
|
|
23
|
+
* @author Jean-Philippe Steinmetz
|
|
24
|
+
*/
|
|
25
|
+
let EasCollectionChunkSQL = class EasCollectionChunkSQL extends BaseEntity {
|
|
26
|
+
constructor(other) {
|
|
27
|
+
super(other);
|
|
28
|
+
this.mailboxUid = "";
|
|
29
|
+
this.deviceId = "";
|
|
30
|
+
this.folderUid = "";
|
|
31
|
+
this.chunkIndex = 0;
|
|
32
|
+
this.ids = [];
|
|
33
|
+
if (other) {
|
|
34
|
+
this.mailboxUid = other.mailboxUid !== undefined ? other.mailboxUid : this.mailboxUid;
|
|
35
|
+
this.deviceId = other.deviceId !== undefined ? other.deviceId : this.deviceId;
|
|
36
|
+
this.folderUid = other.folderUid !== undefined ? other.folderUid : this.folderUid;
|
|
37
|
+
this.chunkIndex = other.chunkIndex !== undefined ? other.chunkIndex : this.chunkIndex;
|
|
38
|
+
this.ids = other.ids !== undefined ? other.ids : this.ids;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
__decorate([
|
|
43
|
+
Column(),
|
|
44
|
+
Description("The unique identifier of the `Mailbox` the device is synced against."),
|
|
45
|
+
__metadata("design:type", String)
|
|
46
|
+
], EasCollectionChunkSQL.prototype, "mailboxUid", void 0);
|
|
47
|
+
__decorate([
|
|
48
|
+
Column(),
|
|
49
|
+
Description("The unique identifier of the paired device."),
|
|
50
|
+
__metadata("design:type", String)
|
|
51
|
+
], EasCollectionChunkSQL.prototype, "deviceId", void 0);
|
|
52
|
+
__decorate([
|
|
53
|
+
Column(),
|
|
54
|
+
Description("The unique identifier of the synced `Folder`."),
|
|
55
|
+
__metadata("design:type", String)
|
|
56
|
+
], EasCollectionChunkSQL.prototype, "folderUid", void 0);
|
|
57
|
+
__decorate([
|
|
58
|
+
Column(),
|
|
59
|
+
Description("Position of the chunk within the collection."),
|
|
60
|
+
__metadata("design:type", Number)
|
|
61
|
+
], EasCollectionChunkSQL.prototype, "chunkIndex", void 0);
|
|
62
|
+
__decorate([
|
|
63
|
+
Column({ type: "simple-json" }),
|
|
64
|
+
Description("The `ServerId`s this chunk holds."),
|
|
65
|
+
__metadata("design:type", Array)
|
|
66
|
+
], EasCollectionChunkSQL.prototype, "ids", void 0);
|
|
67
|
+
EasCollectionChunkSQL = __decorate([
|
|
68
|
+
DataStore("sql"),
|
|
69
|
+
Entity(),
|
|
70
|
+
MailboxScopedData(),
|
|
71
|
+
Description("One slice of the items a device holds for an EAS `Sync` collection."),
|
|
72
|
+
Index("eascollectionchunk_mailbox_device_folder_index", ["mailboxUid", "deviceId", "folderUid", "chunkIndex"], { unique: true }),
|
|
73
|
+
Protect({
|
|
74
|
+
uid: "EasCollectionChunk",
|
|
75
|
+
records: [
|
|
76
|
+
{ userOrRoleId: "anonymous", actions: [] },
|
|
77
|
+
{ userOrRoleId: ".*", actions: [] },
|
|
78
|
+
],
|
|
79
|
+
}, false),
|
|
80
|
+
__metadata("design:paramtypes", [Object])
|
|
81
|
+
], EasCollectionChunkSQL);
|
|
82
|
+
export { EasCollectionChunkSQL };
|
|
83
|
+
//# sourceMappingURL=EasCollectionChunkSQL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EasCollectionChunkSQL.js","sourceRoot":"","sources":["../../../../src/models/sql/EasCollectionChunkSQL.ts"],"names":[],"mappings":";;;;;;;;;AAAA,+EAA+E;AAC/E,mEAAmE;AACnE,mCAAmC;AACnC,+EAA+E;AAC/E,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAC5G,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAErD,MAAM,EAAE,WAAW,EAAE,GAAG,aAAa,CAAC;AACtC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,eAAe,CAAC;AAC/C,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,qBAAqB,CAAC;AAExD;;;;;GAKG;AAgBI,IAAM,qBAAqB,GAA3B,MAAM,qBAAsB,SAAQ,UAAU;IAqBjD,YAAY,KAAsC;QAC9C,KAAK,CAAC,KAAK,CAAC,CAAC;QAnBV,eAAU,GAAW,EAAE,CAAC;QAIxB,aAAQ,GAAW,EAAE,CAAC;QAItB,cAAS,GAAW,EAAE,CAAC;QAIvB,eAAU,GAAW,CAAC,CAAC;QAIvB,QAAG,GAAa,EAAE,CAAC;QAKtB,IAAI,KAAK,EAAE,CAAC;YACR,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC;YACtF,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC;YAC9E,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC;YAClF,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC;YACtF,IAAI,CAAC,GAAG,GAAG,KAAK,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;QAC9D,CAAC;IACL,CAAC;CACJ,CAAA;AA7BU;IAFN,MAAM,EAAE;IACR,WAAW,CAAC,sEAAsE,CAAC;;yDACrD;AAIxB;IAFN,MAAM,EAAE;IACR,WAAW,CAAC,6CAA6C,CAAC;;uDAC9B;AAItB;IAFN,MAAM,EAAE;IACR,WAAW,CAAC,+CAA+C,CAAC;;wDAC/B;AAIvB;IAFN,MAAM,EAAE;IACR,WAAW,CAAC,8CAA8C,CAAC;;yDAC9B;AAIvB;IAFN,MAAM,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;IAC/B,WAAW,CAAC,mCAAmC,CAAC;;kDACvB;AAnBjB,qBAAqB;IAfjC,SAAS,CAAC,KAAK,CAAC;IAChB,MAAM,EAAE;IACR,iBAAiB,EAAE;IACnB,WAAW,CAAC,qEAAqE,CAAC;IAClF,KAAK,CAAC,gDAAgD,EAAE,CAAC,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAChI,OAAO,CACJ;QACI,GAAG,EAAE,oBAAoB;QACzB,OAAO,EAAE;YACL,EAAE,YAAY,EAAE,WAAW,EAAE,OAAO,EAAE,EAAE,EAAE;YAC1C,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE;SACtC;KACJ,EACD,KAAK,CACR;;GACY,qBAAqB,CAgCjC"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
11
|
+
// Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
|
|
12
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
13
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
14
|
+
import { BaseEntity, DocDecorators, ModelDecorators, PersistenceDecorators } from "@rapidrest/service-core";
|
|
15
|
+
import { ObjectDecorators } from "@rapidrest/core";
|
|
16
|
+
import { MailboxScopedData } from "@rapidmx/restapi";
|
|
17
|
+
const { Description } = DocDecorators;
|
|
18
|
+
const { DataStore, Protect } = ModelDecorators;
|
|
19
|
+
const { Nullable } = ObjectDecorators;
|
|
20
|
+
const { Column, Entity, Index } = PersistenceDecorators;
|
|
21
|
+
/**
|
|
22
|
+
* Implementation of the `EasCollectionState` interface for storage in a SQL database. If MongoDB is desired,
|
|
23
|
+
* please use `models.mongo.EasCollectionStateMongo` instead.
|
|
24
|
+
*
|
|
25
|
+
* @author Jean-Philippe Steinmetz
|
|
26
|
+
*/
|
|
27
|
+
let EasCollectionStateSQL = class EasCollectionStateSQL extends BaseEntity {
|
|
28
|
+
constructor(other) {
|
|
29
|
+
super(other);
|
|
30
|
+
this.mailboxUid = "";
|
|
31
|
+
this.deviceId = "";
|
|
32
|
+
this.folderUid = "";
|
|
33
|
+
this.collectionClass = "";
|
|
34
|
+
this.syncKey = "";
|
|
35
|
+
this.cursorDate = new Date(0);
|
|
36
|
+
this.cursorUid = "";
|
|
37
|
+
this.moveCursorDate = new Date(0);
|
|
38
|
+
this.moveCursorUid = "";
|
|
39
|
+
this.serverIds = [];
|
|
40
|
+
this.echoes = {};
|
|
41
|
+
if (other) {
|
|
42
|
+
this.mailboxUid = other.mailboxUid !== undefined ? other.mailboxUid : this.mailboxUid;
|
|
43
|
+
this.deviceId = other.deviceId !== undefined ? other.deviceId : this.deviceId;
|
|
44
|
+
this.folderUid = other.folderUid !== undefined ? other.folderUid : this.folderUid;
|
|
45
|
+
this.collectionClass = other.collectionClass !== undefined ? other.collectionClass : this.collectionClass;
|
|
46
|
+
this.syncKey = other.syncKey !== undefined ? other.syncKey : this.syncKey;
|
|
47
|
+
this.cursorDate = other.cursorDate !== undefined ? other.cursorDate : this.cursorDate;
|
|
48
|
+
this.cursorUid = other.cursorUid !== undefined ? other.cursorUid : this.cursorUid;
|
|
49
|
+
this.moveCursorDate = other.moveCursorDate !== undefined ? other.moveCursorDate : this.moveCursorDate;
|
|
50
|
+
this.moveCursorUid = other.moveCursorUid !== undefined ? other.moveCursorUid : this.moveCursorUid;
|
|
51
|
+
this.serverIds = other.serverIds !== undefined ? other.serverIds : this.serverIds;
|
|
52
|
+
this.echoes = other.echoes !== undefined ? other.echoes : this.echoes;
|
|
53
|
+
this.chunked = "chunked" in other ? other.chunked : this.chunked;
|
|
54
|
+
this.recent = "recent" in other ? other.recent : this.recent;
|
|
55
|
+
this.reconcileCursor = "reconcileCursor" in other ? other.reconcileCursor : this.reconcileCursor;
|
|
56
|
+
this.filterType = "filterType" in other ? other.filterType : this.filterType;
|
|
57
|
+
this.previous = "previous" in other ? other.previous : this.previous;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
__decorate([
|
|
62
|
+
Column(),
|
|
63
|
+
Description("The unique identifier of the `Mailbox` the device is synced against."),
|
|
64
|
+
__metadata("design:type", String)
|
|
65
|
+
], EasCollectionStateSQL.prototype, "mailboxUid", void 0);
|
|
66
|
+
__decorate([
|
|
67
|
+
Column(),
|
|
68
|
+
Description("The unique identifier of the paired device."),
|
|
69
|
+
__metadata("design:type", String)
|
|
70
|
+
], EasCollectionStateSQL.prototype, "deviceId", void 0);
|
|
71
|
+
__decorate([
|
|
72
|
+
Column(),
|
|
73
|
+
Description("The unique identifier of the synced `Folder`."),
|
|
74
|
+
__metadata("design:type", String)
|
|
75
|
+
], EasCollectionStateSQL.prototype, "folderUid", void 0);
|
|
76
|
+
__decorate([
|
|
77
|
+
Column(),
|
|
78
|
+
Description("The EAS `Class` of the collection."),
|
|
79
|
+
__metadata("design:type", String)
|
|
80
|
+
], EasCollectionStateSQL.prototype, "collectionClass", void 0);
|
|
81
|
+
__decorate([
|
|
82
|
+
Column(),
|
|
83
|
+
Description("The `SyncKey` most recently issued for this collection."),
|
|
84
|
+
__metadata("design:type", String)
|
|
85
|
+
], EasCollectionStateSQL.prototype, "syncKey", void 0);
|
|
86
|
+
__decorate([
|
|
87
|
+
Column(),
|
|
88
|
+
Description("The `dateModified` position in the folder's change stream."),
|
|
89
|
+
__metadata("design:type", Date)
|
|
90
|
+
], EasCollectionStateSQL.prototype, "cursorDate", void 0);
|
|
91
|
+
__decorate([
|
|
92
|
+
Column(),
|
|
93
|
+
Description("The `uid` tie-breaker of the folder change stream position."),
|
|
94
|
+
__metadata("design:type", String)
|
|
95
|
+
], EasCollectionStateSQL.prototype, "cursorUid", void 0);
|
|
96
|
+
__decorate([
|
|
97
|
+
Column(),
|
|
98
|
+
Description("The `dateModified` position in the stream of items outside the folder."),
|
|
99
|
+
__metadata("design:type", Date)
|
|
100
|
+
], EasCollectionStateSQL.prototype, "moveCursorDate", void 0);
|
|
101
|
+
__decorate([
|
|
102
|
+
Column(),
|
|
103
|
+
Description("The `uid` tie-breaker of the outside-the-folder stream position."),
|
|
104
|
+
__metadata("design:type", String)
|
|
105
|
+
], EasCollectionStateSQL.prototype, "moveCursorUid", void 0);
|
|
106
|
+
__decorate([
|
|
107
|
+
Column({ type: "simple-json" }),
|
|
108
|
+
Description("The `ServerId`s the device currently holds for this collection."),
|
|
109
|
+
__metadata("design:type", Array)
|
|
110
|
+
], EasCollectionStateSQL.prototype, "serverIds", void 0);
|
|
111
|
+
__decorate([
|
|
112
|
+
Column({ type: "simple-json" }),
|
|
113
|
+
Description("Item uid to the `dateModified` left by the device's own write."),
|
|
114
|
+
__metadata("design:type", Object)
|
|
115
|
+
], EasCollectionStateSQL.prototype, "echoes", void 0);
|
|
116
|
+
__decorate([
|
|
117
|
+
Column({ nullable: true }),
|
|
118
|
+
Description("`true` once the held set is stored in `EasCollectionChunk` rows."),
|
|
119
|
+
Nullable,
|
|
120
|
+
__metadata("design:type", Boolean)
|
|
121
|
+
], EasCollectionStateSQL.prototype, "chunked", void 0);
|
|
122
|
+
__decorate([
|
|
123
|
+
Column({ type: "simple-json", nullable: true }),
|
|
124
|
+
Description("Item uid to `dateModified` of rows processed within the overlap window before the cursor."),
|
|
125
|
+
Nullable,
|
|
126
|
+
__metadata("design:type", Object)
|
|
127
|
+
], EasCollectionStateSQL.prototype, "recent", void 0);
|
|
128
|
+
__decorate([
|
|
129
|
+
Column({ nullable: true }),
|
|
130
|
+
Description("The last held `ServerId` checked by the periodic reconcile."),
|
|
131
|
+
Nullable,
|
|
132
|
+
__metadata("design:type", String)
|
|
133
|
+
], EasCollectionStateSQL.prototype, "reconcileCursor", void 0);
|
|
134
|
+
__decorate([
|
|
135
|
+
Column({ nullable: true }),
|
|
136
|
+
Description("The `FilterType` the collection was synced with."),
|
|
137
|
+
Nullable,
|
|
138
|
+
__metadata("design:type", String)
|
|
139
|
+
], EasCollectionStateSQL.prototype, "filterType", void 0);
|
|
140
|
+
__decorate([
|
|
141
|
+
Column({ type: "simple-json", nullable: true }),
|
|
142
|
+
Description("The state before the most recent round, for a retried `SyncKey`."),
|
|
143
|
+
Nullable,
|
|
144
|
+
__metadata("design:type", Object)
|
|
145
|
+
], EasCollectionStateSQL.prototype, "previous", void 0);
|
|
146
|
+
EasCollectionStateSQL = __decorate([
|
|
147
|
+
DataStore("sql"),
|
|
148
|
+
Entity(),
|
|
149
|
+
MailboxScopedData(),
|
|
150
|
+
Description("Per-device state of one EAS `Sync` collection."),
|
|
151
|
+
Index("eascollectionstate_mailbox_device_folder", ["mailboxUid", "deviceId", "folderUid"], { unique: true }),
|
|
152
|
+
Protect({
|
|
153
|
+
uid: "EasCollectionState",
|
|
154
|
+
records: [
|
|
155
|
+
{ userOrRoleId: "anonymous", actions: [] },
|
|
156
|
+
{ userOrRoleId: ".*", actions: [] },
|
|
157
|
+
],
|
|
158
|
+
}, false),
|
|
159
|
+
__metadata("design:paramtypes", [Object])
|
|
160
|
+
], EasCollectionStateSQL);
|
|
161
|
+
export { EasCollectionStateSQL };
|
|
162
|
+
//# sourceMappingURL=EasCollectionStateSQL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EasCollectionStateSQL.js","sourceRoot":"","sources":["../../../../src/models/sql/EasCollectionStateSQL.ts"],"names":[],"mappings":"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"}
|
|
@@ -13,7 +13,7 @@ import { RouteDecorators } from "@rapidrest/service-core";
|
|
|
13
13
|
import { DeviceSyncStateMongo } from "../models/mongo/DeviceSyncStateMongo.js";
|
|
14
14
|
import { BaseEasRoute } from "../BaseEasRoute.js";
|
|
15
15
|
import { ProvisionCommand } from "../commands/ProvisionCommand.js";
|
|
16
|
-
import {
|
|
16
|
+
import { PingCommandMongo } from "../commands/mongo/PingCommandMongo.js";
|
|
17
17
|
import { FolderSyncCommandMongo } from "../commands/mongo/FolderSyncCommandMongo.js";
|
|
18
18
|
import { SyncCommandMongo } from "../commands/mongo/SyncCommandMongo.js";
|
|
19
19
|
import { SendMailCommandMongo } from "../commands/mongo/SendMailCommandMongo.js";
|
|
@@ -49,7 +49,7 @@ let EasRouteMongo = class EasRouteMongo extends BaseEasRoute {
|
|
|
49
49
|
SearchCommandMongo,
|
|
50
50
|
MeetingResponseCommandMongo,
|
|
51
51
|
SettingsCommandMongo,
|
|
52
|
-
|
|
52
|
+
PingCommandMongo,
|
|
53
53
|
GetItemEstimateCommandMongo,
|
|
54
54
|
MoveItemsCommandMongo,
|
|
55
55
|
ResolveRecipientsCommandMongo,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EasRouteMongo.js","sourceRoot":"","sources":["../../../src/mongo/EasRouteMongo.ts"],"names":[],"mappings":";;;;;;AAAA,+EAA+E;AAC/E,mEAAmE;AACnE,mCAAmC;AACnC,+EAA+E;AAC/E,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,yCAAyC,CAAC;AAC/E,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"EasRouteMongo.js","sourceRoot":"","sources":["../../../src/mongo/EasRouteMongo.ts"],"names":[],"mappings":";;;;;;AAAA,+EAA+E;AAC/E,mEAAmE;AACnE,mCAAmC;AACnC,+EAA+E;AAC/E,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,yCAAyC,CAAC;AAC/E,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AACzE,OAAO,EAAE,sBAAsB,EAAE,MAAM,6CAA6C,CAAC;AACrF,OAAO,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AACzE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,wBAAwB,EAAE,MAAM,+CAA+C,CAAC;AACzF,OAAO,EAAE,sBAAsB,EAAE,MAAM,6CAA6C,CAAC;AACrF,OAAO,EAAE,0BAA0B,EAAE,MAAM,iDAAiD,CAAC;AAC7F,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,EAAE,2BAA2B,EAAE,MAAM,kDAAkD,CAAC;AAC/F,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,2BAA2B,EAAE,MAAM,kDAAkD,CAAC;AAC/F,OAAO,EAAE,qBAAqB,EAAE,MAAM,4CAA4C,CAAC;AACnF,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AACnG,MAAM,EAAE,KAAK,EAAE,GAAG,eAAe,CAAC;AAElC;;;;;GAKG;AAEI,IAAM,aAAa,GAAnB,MAAM,aAAc,SAAQ,YAAgD;IAA5E;;QACO,yBAAoB,GAAQ,oBAAoB,CAAC;QACjD,iBAAY,GAAQ,YAAY,CAAC;QACjC,0BAAqB,GAAU;YACrC,gBAAgB;YAChB,sBAAsB;YACtB,gBAAgB;YAChB,oBAAoB;YACpB,wBAAwB;YACxB,sBAAsB;YACtB,0BAA0B;YAC1B,kBAAkB;YAClB,2BAA2B;YAC3B,oBAAoB;YACpB,gBAAgB;YAChB,2BAA2B;YAC3B,qBAAqB;YACrB,6BAA6B;SAChC,CAAC;IACN,CAAC;CAAA,CAAA;AAnBY,aAAa;IADzB,KAAK,CAAC,8BAA8B,CAAC;GACzB,aAAa,CAmBzB"}
|
package/dist/lib/mongo.js
CHANGED
|
@@ -11,5 +11,7 @@
|
|
|
11
11
|
export { EasRouteMongo } from "./mongo/EasRouteMongo.js";
|
|
12
12
|
export { DeviceSyncStateRouteMongo } from "./mongo/DeviceSyncStateRouteMongo.js";
|
|
13
13
|
export { DeviceSyncStateMongo } from "./models/mongo/DeviceSyncStateMongo.js";
|
|
14
|
+
export { EasCollectionStateMongo } from "./models/mongo/EasCollectionStateMongo.js";
|
|
15
|
+
export { EasCollectionChunkMongo } from "./models/mongo/EasCollectionChunkMongo.js";
|
|
14
16
|
export { EasDeviceStateCleanupJobMongo } from "./jobs/mongo/EasDeviceStateCleanupJobMongo.js";
|
|
15
17
|
//# sourceMappingURL=mongo.js.map
|
package/dist/lib/mongo.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mongo.js","sourceRoot":"","sources":["../../src/mongo.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,mEAAmE;AACnE,mCAAmC;AACnC,+EAA+E;AAC/E;;;;;GAKG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,yBAAyB,EAAE,MAAM,sCAAsC,CAAC;AACjF,OAAO,EAAE,oBAAoB,EAAE,MAAM,wCAAwC,CAAC;AAC9E,OAAO,EAAE,6BAA6B,EAAE,MAAM,+CAA+C,CAAC"}
|
|
1
|
+
{"version":3,"file":"mongo.js","sourceRoot":"","sources":["../../src/mongo.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,mEAAmE;AACnE,mCAAmC;AACnC,+EAA+E;AAC/E;;;;;GAKG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,yBAAyB,EAAE,MAAM,sCAAsC,CAAC;AACjF,OAAO,EAAE,oBAAoB,EAAE,MAAM,wCAAwC,CAAC;AAC9E,OAAO,EAAE,uBAAuB,EAAE,MAAM,2CAA2C,CAAC;AACpF,OAAO,EAAE,uBAAuB,EAAE,MAAM,2CAA2C,CAAC;AACpF,OAAO,EAAE,6BAA6B,EAAE,MAAM,+CAA+C,CAAC"}
|
|
@@ -13,7 +13,7 @@ import { RouteDecorators } from "@rapidrest/service-core";
|
|
|
13
13
|
import { DeviceSyncStateSQL } from "../models/sql/DeviceSyncStateSQL.js";
|
|
14
14
|
import { BaseEasRoute } from "../BaseEasRoute.js";
|
|
15
15
|
import { ProvisionCommand } from "../commands/ProvisionCommand.js";
|
|
16
|
-
import {
|
|
16
|
+
import { PingCommandSQL } from "../commands/sql/PingCommandSQL.js";
|
|
17
17
|
import { FolderSyncCommandSQL } from "../commands/sql/FolderSyncCommandSQL.js";
|
|
18
18
|
import { SyncCommandSQL } from "../commands/sql/SyncCommandSQL.js";
|
|
19
19
|
import { SendMailCommandSQL } from "../commands/sql/SendMailCommandSQL.js";
|
|
@@ -49,7 +49,7 @@ let EasRouteSQL = class EasRouteSQL extends BaseEasRoute {
|
|
|
49
49
|
SearchCommandSQL,
|
|
50
50
|
MeetingResponseCommandSQL,
|
|
51
51
|
SettingsCommandSQL,
|
|
52
|
-
|
|
52
|
+
PingCommandSQL,
|
|
53
53
|
GetItemEstimateCommandSQL,
|
|
54
54
|
MoveItemsCommandSQL,
|
|
55
55
|
ResolveRecipientsCommandSQL,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EasRouteSQL.js","sourceRoot":"","sources":["../../../src/sql/EasRouteSQL.ts"],"names":[],"mappings":";;;;;;AAAA,+EAA+E;AAC/E,mEAAmE;AACnE,mCAAmC;AACnC,+EAA+E;AAC/E,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AACzE,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"EasRouteSQL.js","sourceRoot":"","sources":["../../../src/sql/EasRouteSQL.ts"],"names":[],"mappings":";;;;;;AAAA,+EAA+E;AAC/E,mEAAmE;AACnE,mCAAmC;AACnC,+EAA+E;AAC/E,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AACzE,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,yCAAyC,CAAC;AAC/E,OAAO,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAC3E,OAAO,EAAE,sBAAsB,EAAE,MAAM,2CAA2C,CAAC;AACnF,OAAO,EAAE,oBAAoB,EAAE,MAAM,yCAAyC,CAAC;AAC/E,OAAO,EAAE,wBAAwB,EAAE,MAAM,6CAA6C,CAAC;AACvF,OAAO,EAAE,gBAAgB,EAAE,MAAM,qCAAqC,CAAC;AACvE,OAAO,EAAE,yBAAyB,EAAE,MAAM,8CAA8C,CAAC;AACzF,OAAO,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAC3E,OAAO,EAAE,yBAAyB,EAAE,MAAM,8CAA8C,CAAC;AACzF,OAAO,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAC;AAC7E,OAAO,EAAE,2BAA2B,EAAE,MAAM,gDAAgD,CAAC;AAC7F,MAAM,EAAE,KAAK,EAAE,GAAG,eAAe,CAAC;AAElC;;;;;GAKG;AAEI,IAAM,WAAW,GAAjB,MAAM,WAAY,SAAQ,YAA4C;IAAtE;;QACO,yBAAoB,GAAQ,kBAAkB,CAAC;QAC/C,iBAAY,GAAQ,UAAU,CAAC;QAC/B,0BAAqB,GAAU;YACrC,gBAAgB;YAChB,oBAAoB;YACpB,cAAc;YACd,kBAAkB;YAClB,sBAAsB;YACtB,oBAAoB;YACpB,wBAAwB;YACxB,gBAAgB;YAChB,yBAAyB;YACzB,kBAAkB;YAClB,cAAc;YACd,yBAAyB;YACzB,mBAAmB;YACnB,2BAA2B;SAC9B,CAAC;IACN,CAAC;CAAA,CAAA;AAnBY,WAAW;IADvB,KAAK,CAAC,8BAA8B,CAAC;GACzB,WAAW,CAmBvB"}
|
package/dist/lib/sql.js
CHANGED
|
@@ -11,5 +11,7 @@
|
|
|
11
11
|
export { EasRouteSQL } from "./sql/EasRouteSQL.js";
|
|
12
12
|
export { DeviceSyncStateRouteSQL } from "./sql/DeviceSyncStateRouteSQL.js";
|
|
13
13
|
export { DeviceSyncStateSQL } from "./models/sql/DeviceSyncStateSQL.js";
|
|
14
|
+
export { EasCollectionStateSQL } from "./models/sql/EasCollectionStateSQL.js";
|
|
15
|
+
export { EasCollectionChunkSQL } from "./models/sql/EasCollectionChunkSQL.js";
|
|
14
16
|
export { EasDeviceStateCleanupJobSQL } from "./jobs/sql/EasDeviceStateCleanupJobSQL.js";
|
|
15
17
|
//# sourceMappingURL=sql.js.map
|
package/dist/lib/sql.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sql.js","sourceRoot":"","sources":["../../src/sql.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,mEAAmE;AACnE,mCAAmC;AACnC,+EAA+E;AAC/E;;;;;GAKG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AACxE,OAAO,EAAE,2BAA2B,EAAE,MAAM,2CAA2C,CAAC"}
|
|
1
|
+
{"version":3,"file":"sql.js","sourceRoot":"","sources":["../../src/sql.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,mEAAmE;AACnE,mCAAmC;AACnC,+EAA+E;AAC/E;;;;;GAKG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AACxE,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAC9E,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAC9E,OAAO,EAAE,2BAA2B,EAAE,MAAM,2CAA2C,CAAC"}
|
|
@@ -12,6 +12,11 @@ import type { DeviceSyncState } from "./models/DeviceSyncState.js";
|
|
|
12
12
|
* existing 449 gate, at which point `ProvisionCommand.issuePolicy` sees the flag and sends the device a
|
|
13
13
|
* `RemoteWipe` directive instead of a normal policy document - no separate push/notification channel is needed.
|
|
14
14
|
*
|
|
15
|
+
* **Scope**: a `DeviceSyncState` row is one (mailbox, client-supplied `DeviceId`) pairing, so the wipe and the block
|
|
16
|
+
* that follows its acknowledgement apply to that `DeviceId` only. A compliant client wipes itself; a client that
|
|
17
|
+
* ignores the directive can pair again under a different `DeviceId` with the same credentials. Cutting such a client
|
|
18
|
+
* off requires revoking the account's credentials - not something this route does.
|
|
19
|
+
*
|
|
15
20
|
* `deviceSyncStateClass` is supplied by the Mongo/SQL concrete subclasses, the same one-line-per-backend
|
|
16
21
|
* pattern used throughout this library.
|
|
17
22
|
*
|
|
@@ -31,4 +36,9 @@ export declare abstract class BaseDeviceSyncStateRoute<D extends DeviceSyncState
|
|
|
31
36
|
* the two.
|
|
32
37
|
*/
|
|
33
38
|
remoteWipe(uid: string, req: HttpRequest, user?: JWTUser): Promise<D>;
|
|
39
|
+
/**
|
|
40
|
+
* Lets a device that acknowledged a remote wipe (`blocked`, see `ProvisionCommand`) connect again: it must still
|
|
41
|
+
* complete a fresh Provision handshake. Admin-only, like `remoteWipe`.
|
|
42
|
+
*/
|
|
43
|
+
unblock(uid: string, user?: JWTUser): Promise<D>;
|
|
34
44
|
}
|
|
@@ -2,6 +2,8 @@ import { type JWTUser } from "@rapidrest/core";
|
|
|
2
2
|
import { HttpRequest, HttpResponse } from "@rapidrest/service-core";
|
|
3
3
|
import { Mailbox } from "@rapidmx/restapi";
|
|
4
4
|
import { DeviceSyncState } from "./models/DeviceSyncState.js";
|
|
5
|
+
/** Whether `deviceId` is an acceptable `DeviceId` (`DEVICE_ID_PATTERN`, and not one of `RESERVED_DEVICE_IDS`). */
|
|
6
|
+
export declare function isValidDeviceId(deviceId: string | undefined): deviceId is string;
|
|
5
7
|
/**
|
|
6
8
|
* Abstract base for the single fixed EAS endpoint (`POST /Microsoft-Server-ActiveSync` by MS-ASHTTP
|
|
7
9
|
* convention, though the concrete path is left to the consuming application to mount via `@Route(...)` — see
|
|
@@ -15,10 +17,13 @@ import { DeviceSyncState } from "./models/DeviceSyncState.js";
|
|
|
15
17
|
* deliberately does not implement itself — see the architecture plan's "Auth" section for the full reasoning
|
|
16
18
|
* behind this choice over a per-request Basic Auth strategy.
|
|
17
19
|
*
|
|
18
|
-
* **Dispatch flow**: resolve the caller's own `Mailbox` (never
|
|
19
|
-
*
|
|
20
|
-
* `
|
|
21
|
-
*
|
|
20
|
+
* **Dispatch flow**: reject a body over `mail:eas:max_request_bytes` (413), resolve the caller's own `Mailbox` (never
|
|
21
|
+
* a client-supplied one — `resolveCallerMailboxUid`), find-or-create that (mailbox, device) pair's `DeviceSyncState`,
|
|
22
|
+
* refuse a `blocked` device (one that acknowledged a remote wipe) with 403 for anything but `Provision` (which
|
|
23
|
+
* answers Status 129 itself), decode the WBXML request body (if any; malformed or over the decoder's element limits
|
|
24
|
+
* -> 400), enforce the provisioning gate (provisioned *and* presenting the stored policy key, else 449 - only
|
|
25
|
+
* `Provision` and a `Settings` request limited to `UserInformation`/`DeviceInformation` are exempt), dispatch to the matching registered
|
|
26
|
+
* `EasCommandHandler`, record `lastSyncAt` (best-effort), and encode the handler's response back to WBXML.
|
|
22
27
|
*
|
|
23
28
|
* **Command handlers** are supplied via `commandHandlerClasses` (empty by default — this class alone is just
|
|
24
29
|
* the transport skeleton; concrete command support, e.g. `ProvisionCommand`/`FolderSyncCommand`, is added
|
|
@@ -49,6 +54,8 @@ export declare abstract class BaseEasRoute<D extends DeviceSyncState, M extends
|
|
|
49
54
|
private deviceSyncStateRepo?;
|
|
50
55
|
private mailboxRepo?;
|
|
51
56
|
private readonly handlers;
|
|
57
|
+
/** Largest request body `dispatch()` accepts (HTTP 413 beyond it). */
|
|
58
|
+
private maxRequestBytes;
|
|
52
59
|
private logger;
|
|
53
60
|
init(): Promise<void>;
|
|
54
61
|
/**
|
|
@@ -61,5 +68,9 @@ export declare abstract class BaseEasRoute<D extends DeviceSyncState, M extends
|
|
|
61
68
|
*/
|
|
62
69
|
options(res: HttpResponse): Promise<void>;
|
|
63
70
|
dispatch(req: HttpRequest, res: HttpResponse, user?: JWTUser): Promise<void>;
|
|
71
|
+
/** Decodes the WBXML request body, if any (malformed or over the decoder's element limits -> 400). */
|
|
72
|
+
private decodeRequest;
|
|
73
|
+
/** `Provision`, and a `Settings` request whose every element is one `SETTINGS_WITHOUT_PROVISIONING` allows. */
|
|
74
|
+
private exemptFromProvisioning;
|
|
64
75
|
private findOrCreateDeviceSyncState;
|
|
65
76
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { ObjectFactory } from "@rapidrest/core";
|
|
2
|
+
import type { RepoUtils } from "@rapidrest/service-core";
|
|
3
|
+
import { AuditAction } from "@rapidmx/restapi";
|
|
4
|
+
import type { EasCommandContext } from "./EasCommandHandler.js";
|
|
5
|
+
/** What an `EasAuditLog` needs from the command using it. */
|
|
6
|
+
export interface EasAuditDependencies {
|
|
7
|
+
objectFactory: ObjectFactory;
|
|
8
|
+
/** The concrete `AuditLogEntry` class (`AuditLogEntryMongo`/`AuditLogEntrySQL`). */
|
|
9
|
+
auditLogClass: any;
|
|
10
|
+
mailboxRepo: RepoUtils<any>;
|
|
11
|
+
/** The whole configuration (`@Config()`), as restapi's routes pass it to `recordAuditLog()`. */
|
|
12
|
+
config?: any;
|
|
13
|
+
logger?: any;
|
|
14
|
+
}
|
|
15
|
+
/** One audited ActiveSync action against a mailbox. */
|
|
16
|
+
export interface EasAuditEntry {
|
|
17
|
+
action: AuditAction;
|
|
18
|
+
/** The mailbox whose content was read or deleted - only a mailbox the caller doesn't own is audited. */
|
|
19
|
+
mailboxUid: string;
|
|
20
|
+
targetType: string;
|
|
21
|
+
targetUid: string;
|
|
22
|
+
details?: Record<string, any>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Audit entries for ActiveSync access to mail in a mailbox the caller doesn't own - an administrator reaching in
|
|
26
|
+
* through a trusted role, or a delegate working in a shared folder. It uses restapi's `recordAuditLog()` and
|
|
27
|
+
* `isNonOwnerAccess()`, as `BaseMessageRoute` does: REST records `MESSAGE_CONTENT_ACCESSED` for a non-owner content
|
|
28
|
+
* read and `MESSAGE_DELETE` for a delete.
|
|
29
|
+
*
|
|
30
|
+
* Built once per request. The caller's own mailbox (`ctx.mailboxUid`, resolved by `ownerUserUid`) is never audited
|
|
31
|
+
* and needs no lookup. Any other mailbox is loaded once. A mailbox that can't be loaded counts as non-owner, the
|
|
32
|
+
* defensive reading restapi's `content()` uses. Recording never throws: an audit failure is logged and the command
|
|
33
|
+
* goes on, like restapi's own best-effort persistence.
|
|
34
|
+
*
|
|
35
|
+
* Every entry's `details` carries `protocol: "ActiveSync"`, the command and the `DeviceId`.
|
|
36
|
+
*/
|
|
37
|
+
export declare class EasAuditLog {
|
|
38
|
+
private readonly deps;
|
|
39
|
+
private readonly ctx;
|
|
40
|
+
private readonly command;
|
|
41
|
+
private readonly nonOwner;
|
|
42
|
+
constructor(deps: EasAuditDependencies, ctx: EasCommandContext, command: string);
|
|
43
|
+
/** Whether reading `mailboxUid`'s content is a non-owner access for this request's caller. */
|
|
44
|
+
isNonOwner(mailboxUid: string): Promise<boolean>;
|
|
45
|
+
/** Records `entry` when its mailbox isn't the caller's own. */
|
|
46
|
+
record(entry: EasAuditEntry): Promise<void>;
|
|
47
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/** Releases an acquired lease. Never throws. */
|
|
2
|
+
export type LeaseRelease = () => Promise<void>;
|
|
3
|
+
export interface LeaseOptions {
|
|
4
|
+
/** Redis to hold the lease in across server copies (`datastores:cache`); in-process only without it. */
|
|
5
|
+
redisUrl?: string;
|
|
6
|
+
/** How long a Redis lease lives if its holder never releases it (a crashed server copy). While held, it's renewed
|
|
7
|
+
* every third of this. */
|
|
8
|
+
ttlMs: number;
|
|
9
|
+
/** How long to wait for a held lease before giving up. */
|
|
10
|
+
waitMs: number;
|
|
11
|
+
/** Delay between Redis acquisition attempts. */
|
|
12
|
+
pollMs?: number;
|
|
13
|
+
/** Longest a single Redis connect, `SET` or release may take before the lease fails open. The connect and the first
|
|
14
|
+
* `SET` always get all of it, however long the in-process wait took; a later `SET` while polling for another
|
|
15
|
+
* copy's key is also capped by `waitMs`'s deadline. */
|
|
16
|
+
redisTimeoutMs?: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A short exclusive lease on one key - `SyncCommand` holds one per (mailbox, device, folder) for the duration of a
|
|
20
|
+
* collection's round, so two concurrent `Sync`s of the same collection (a client retrying while its first request is
|
|
21
|
+
* still running, or two server copies) can't both read the same state, apply their commands and overwrite each
|
|
22
|
+
* other's result.
|
|
23
|
+
*
|
|
24
|
+
* Always taken in-process first (a per-key promise chain), then - when a Redis URL is configured - as a `SET NX PX`
|
|
25
|
+
* key shared by every server copy, released with a token check and renewed (token-checked `PEXPIRE`) every
|
|
26
|
+
* `ttlMs / 3` while held, so a round that outlasts `ttlMs` doesn't lose it to another copy.
|
|
27
|
+
*
|
|
28
|
+
* **Redis being unreachable fails open** to the in-process lease alone rather than refusing - or hanging - every
|
|
29
|
+
* `Sync`: the client is created with `disableOfflineQueue` (a command fails at once while disconnected instead of
|
|
30
|
+
* waiting in a queue) and a bounded `reconnectStrategy` (so `connect()` rejects instead of retrying forever), and
|
|
31
|
+
* every connect/`SET` is additionally raced against `redisTimeoutMs`. The connect and the first `SET` get the whole
|
|
32
|
+
* `redisTimeoutMs` even when the in-process wait already used up `waitMs` - otherwise a healthy Redis would get no time
|
|
33
|
+
* at all and the lease would fail open for nothing; only the polling `SET`s after Redis has answered are cut short by
|
|
34
|
+
* the deadline, which then means another copy still holds the key (`undefined`). A `SET` that times out but lands later
|
|
35
|
+
* is released again straight away. A client whose reconnects gave up is replaced on the next lease.
|
|
36
|
+
*
|
|
37
|
+
* **Release never hangs**: the token-checked release is raced against `redisTimeoutMs` too (the key expires on its
|
|
38
|
+
* own), and the in-process lease is freed whatever happens to it.
|
|
39
|
+
*
|
|
40
|
+
* @author Jean-Philippe Steinmetz
|
|
41
|
+
*/
|
|
42
|
+
export declare class EasCollectionLease {
|
|
43
|
+
/** The in-process holder of each key: resolves once that holder releases. */
|
|
44
|
+
private static readonly local;
|
|
45
|
+
/** Shared command clients, keyed by Redis URL. */
|
|
46
|
+
private static readonly clients;
|
|
47
|
+
/** Forgets all process-wide state. Intended for tests. */
|
|
48
|
+
static resetSharedState(): void;
|
|
49
|
+
private static getClient;
|
|
50
|
+
private static forgetClient;
|
|
51
|
+
/** Waits for `key`'s in-process holder (if any) to release, up to `deadline`, then takes it over with `held` - the
|
|
52
|
+
* check and the take-over happen in the same tick, so two waiters can never both find the key free. `false` on
|
|
53
|
+
* timeout. */
|
|
54
|
+
private static takeLocal;
|
|
55
|
+
/**
|
|
56
|
+
* Acquires the lease on `key`, waiting up to `options.waitMs` for a current holder. Resolves with the release
|
|
57
|
+
* function, or `undefined` when the lease couldn't be acquired in time.
|
|
58
|
+
*/
|
|
59
|
+
static acquire(key: string, options: LeaseOptions): Promise<LeaseRelease | undefined>;
|
|
60
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { RepoUtils } from "@rapidrest/service-core";
|
|
2
|
+
import type { EasCollectionChunk } from "./models/EasCollectionChunk.js";
|
|
3
|
+
import type { EasCollectionState } from "./models/EasCollectionState.js";
|
|
4
|
+
/** Largest held set kept inline on the `EasCollectionState` row (`serverIds`) - past it the set moves to chunk rows. */
|
|
5
|
+
export declare const INLINE_HELD_LIMIT = 2000;
|
|
6
|
+
/** Most `ServerId`s one `EasCollectionChunk` row holds. */
|
|
7
|
+
export declare const HELD_CHUNK_SIZE = 2000;
|
|
8
|
+
/** Identifies one collection's rows. */
|
|
9
|
+
export interface CollectionKey {
|
|
10
|
+
mailboxUid: string;
|
|
11
|
+
deviceId: string;
|
|
12
|
+
folderUid: string;
|
|
13
|
+
}
|
|
14
|
+
type StoredChunk = EasCollectionChunk & {
|
|
15
|
+
uid: string;
|
|
16
|
+
version: number;
|
|
17
|
+
};
|
|
18
|
+
/** A collection's held set as loaded for one round, with the chunk rows it came from (empty while inline). */
|
|
19
|
+
export interface HeldSet {
|
|
20
|
+
ids: Set<string>;
|
|
21
|
+
chunks: StoredChunk[];
|
|
22
|
+
}
|
|
23
|
+
/** Everything the held-set functions need to reach the chunk rows. */
|
|
24
|
+
export interface ChunkStore {
|
|
25
|
+
repo: RepoUtils<any>;
|
|
26
|
+
chunkClass: any;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Loads the `ServerId`s a device holds for a collection: `state.serverIds` while the set is inline, otherwise every
|
|
30
|
+
* `EasCollectionChunk` row of the collection (read in pages, ordered by `chunkIndex`).
|
|
31
|
+
*/
|
|
32
|
+
export declare function loadHeldSet(state: EasCollectionState | undefined, store: ChunkStore): Promise<HeldSet>;
|
|
33
|
+
/**
|
|
34
|
+
* Persists `ids` as the collection's new held set, given the `loaded` set the round started from, and returns the
|
|
35
|
+
* `serverIds`/`chunked` values the state row must be saved with.
|
|
36
|
+
*
|
|
37
|
+
* A set within `INLINE_HELD_LIMIT` that was never chunked stays inline (no chunk rows touched). Otherwise only the
|
|
38
|
+
* difference is written: each existing chunk drops the ids no longer held, new ids fill the free space of existing
|
|
39
|
+
* chunks before new chunks are appended, and only chunks whose content changed are updated (an emptied chunk is
|
|
40
|
+
* deleted) - a round that adds a window of items rewrites one or two chunks, never the whole set.
|
|
41
|
+
*
|
|
42
|
+
* Writes are not atomic with the state row: the caller must treat a failure here (or of the state row after this)
|
|
43
|
+
* as a lost round and make the device restart the collection.
|
|
44
|
+
*/
|
|
45
|
+
export declare function saveHeldSet(key: CollectionKey, loaded: HeldSet, ids: Set<string>, wasChunked: boolean, store: ChunkStore): Promise<{
|
|
46
|
+
serverIds: string[];
|
|
47
|
+
chunked: boolean;
|
|
48
|
+
}>;
|
|
49
|
+
/** Removes every chunk row of a collection (a restart with `SyncKey 0`, or a forgotten device). */
|
|
50
|
+
export declare function clearHeldSet(key: CollectionKey, store: ChunkStore): Promise<void>;
|
|
51
|
+
export {};
|