@noartem/xews 1.0.0 → 1.1.0
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 +16 -0
- package/index.js +67 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
It is useful when you already have EWS access and want a simple way to upload, list, download, and clear files through Exchange without opening a mail client.
|
|
6
6
|
|
|
7
|
+
It can also synchronize one local folder through a dedicated draft-based channel. Sync sends changes as compressed batch attachments and removes each package after it is applied.
|
|
8
|
+
|
|
7
9
|
## Install
|
|
8
10
|
|
|
9
11
|
Install from a local checkout:
|
|
@@ -58,6 +60,7 @@ Typical file transfer flow:
|
|
|
58
60
|
xews upload --file ./archive.zip
|
|
59
61
|
xews ls
|
|
60
62
|
xews download
|
|
63
|
+
xews sync --dir ./data --channel team-a
|
|
61
64
|
```
|
|
62
65
|
|
|
63
66
|
Show help:
|
|
@@ -97,6 +100,18 @@ Clear all attachments from the first draft:
|
|
|
97
100
|
xews clear
|
|
98
101
|
```
|
|
99
102
|
|
|
103
|
+
Synchronize one local folder through a dedicated draft subject:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
xews sync --dir ./tmp/a --channel local-test
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Run a single sync cycle and exit:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
xews sync --dir ./tmp/a --channel local-test --once
|
|
113
|
+
```
|
|
114
|
+
|
|
100
115
|
## Commands
|
|
101
116
|
|
|
102
117
|
- `init`: create `~/.config/xews/auth.json` if needed and open it in your default editor
|
|
@@ -104,6 +119,7 @@ xews clear
|
|
|
104
119
|
- `download`: download all file attachments from the first draft into the current directory
|
|
105
120
|
- `list` / `ls`: show attachment names and sizes from the first draft
|
|
106
121
|
- `clear`: remove all attachments from the first draft
|
|
122
|
+
- `sync`: synchronize one folder through a dedicated draft channel like `xews-sync:<channel>`
|
|
107
123
|
|
|
108
124
|
## Publish
|
|
109
125
|
|
package/index.js
CHANGED
|
@@ -18,9 +18,12 @@ import {
|
|
|
18
18
|
FileAttachment,
|
|
19
19
|
DeleteMode,
|
|
20
20
|
MessageBody,
|
|
21
|
+
SearchFilter,
|
|
22
|
+
ItemSchema,
|
|
21
23
|
} from "ews-javascript-api";
|
|
22
24
|
|
|
23
25
|
import { Cli, z } from "incur";
|
|
26
|
+
import { runSync } from "./sync.js";
|
|
24
27
|
|
|
25
28
|
const CLI_VERSION = "1.0.0";
|
|
26
29
|
const CONFIG_PATH = path.join(os.homedir(), ".config", "xews", "auth.json");
|
|
@@ -129,6 +132,36 @@ async function getFirstDraft(service) {
|
|
|
129
132
|
return draft;
|
|
130
133
|
}
|
|
131
134
|
|
|
135
|
+
async function getDraftBySubject(service, subject) {
|
|
136
|
+
const filter = new SearchFilter.IsEqualTo(ItemSchema.Subject, subject);
|
|
137
|
+
const res = await service.FindItems(
|
|
138
|
+
WellKnownFolderName.Drafts,
|
|
139
|
+
filter,
|
|
140
|
+
new ItemView(1),
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
if (res.Items.length === 0) return null;
|
|
144
|
+
|
|
145
|
+
const draft = res.Items[0];
|
|
146
|
+
await draft.Load(new PropertySet(BasePropertySet.FirstClassProperties));
|
|
147
|
+
return draft;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async function getOrCreateDraftBySubject(service, subject) {
|
|
151
|
+
const draft = await getDraftBySubject(service, subject);
|
|
152
|
+
|
|
153
|
+
if (draft) return draft;
|
|
154
|
+
|
|
155
|
+
const created = new EmailMessage(service);
|
|
156
|
+
created.Subject = subject;
|
|
157
|
+
created.Body = new MessageBody(`Generated for ${subject}`);
|
|
158
|
+
|
|
159
|
+
await created.Save(WellKnownFolderName.Drafts);
|
|
160
|
+
await created.Load(new PropertySet(BasePropertySet.FirstClassProperties));
|
|
161
|
+
|
|
162
|
+
return created;
|
|
163
|
+
}
|
|
164
|
+
|
|
132
165
|
/* ================= ENTRY ================= */
|
|
133
166
|
|
|
134
167
|
const cli = Cli.create("xews", {
|
|
@@ -274,4 +307,38 @@ cli.command("clear", {
|
|
|
274
307
|
},
|
|
275
308
|
});
|
|
276
309
|
|
|
310
|
+
cli.command("sync", {
|
|
311
|
+
description: "Synchronize one folder through an EWS draft channel",
|
|
312
|
+
options: z.object({
|
|
313
|
+
dir: z.string().describe("Folder to synchronize"),
|
|
314
|
+
channel: z
|
|
315
|
+
.string()
|
|
316
|
+
.default("default")
|
|
317
|
+
.describe("Sync channel name stored in a dedicated draft subject"),
|
|
318
|
+
interval: z
|
|
319
|
+
.number()
|
|
320
|
+
.int()
|
|
321
|
+
.positive()
|
|
322
|
+
.default(2000)
|
|
323
|
+
.describe("Polling interval in milliseconds"),
|
|
324
|
+
once: z.boolean().default(false).describe("Run one sync cycle and exit"),
|
|
325
|
+
}),
|
|
326
|
+
alias: {
|
|
327
|
+
dir: "d",
|
|
328
|
+
channel: "c",
|
|
329
|
+
},
|
|
330
|
+
async run(c) {
|
|
331
|
+
const service = createService();
|
|
332
|
+
|
|
333
|
+
await runSync({
|
|
334
|
+
service,
|
|
335
|
+
rootDir: c.options.dir,
|
|
336
|
+
channel: c.options.channel,
|
|
337
|
+
intervalMs: c.options.interval,
|
|
338
|
+
once: c.options.once,
|
|
339
|
+
getOrCreateDraftBySubject,
|
|
340
|
+
});
|
|
341
|
+
},
|
|
342
|
+
});
|
|
343
|
+
|
|
277
344
|
await cli.serve();
|