fluxmail 0.9.0 → 0.10.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/dist/accounts/googleAuth.d.ts +4 -2
- package/dist/accounts/googleAuth.d.ts.map +1 -1
- package/dist/accounts/googleAuth.js +18 -84
- package/dist/accounts/googleAuth.js.map +1 -1
- package/dist/accounts/microsoftAuth.d.ts +2 -1
- package/dist/accounts/microsoftAuth.d.ts.map +1 -1
- package/dist/accounts/microsoftAuth.js +16 -80
- package/dist/accounts/microsoftAuth.js.map +1 -1
- package/dist/accounts/oauthCallback.d.ts +52 -0
- package/dist/accounts/oauthCallback.d.ts.map +1 -0
- package/dist/accounts/oauthCallback.js +237 -0
- package/dist/accounts/oauthCallback.js.map +1 -0
- package/dist/accounts/telemetry.d.ts +3 -0
- package/dist/accounts/telemetry.d.ts.map +1 -1
- package/dist/accounts/telemetry.js +1 -0
- package/dist/accounts/telemetry.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +31 -12
- package/dist/cli.js.map +1 -1
- package/dist/cliMail.d.ts.map +1 -1
- package/dist/cliMail.js +7 -1
- package/dist/cliMail.js.map +1 -1
- package/dist/http/rest.d.ts.map +1 -1
- package/dist/http/rest.js +20 -0
- package/dist/http/rest.js.map +1 -1
- package/dist/mcp/buildServer.d.ts.map +1 -1
- package/dist/mcp/buildServer.js +6 -0
- package/dist/mcp/buildServer.js.map +1 -1
- package/dist/service/emailService.d.ts +1 -0
- package/dist/service/emailService.d.ts.map +1 -1
- package/dist/service/emailService.js +15 -0
- package/dist/service/emailService.js.map +1 -1
- package/dist/service/searchCursor.d.ts +2 -0
- package/dist/service/searchCursor.d.ts.map +1 -1
- package/dist/service/searchCursor.js +4 -1
- package/dist/service/searchCursor.js.map +1 -1
- package/package.json +5 -5
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { createServer } from 'node:http';
|
|
2
|
+
import { createInterface } from 'node:readline';
|
|
3
|
+
import { EmailError } from '@fluxmail/core';
|
|
4
|
+
export const DEFAULT_OAUTH_CALLBACK_TIMEOUT_MS = 10 * 60 * 1000;
|
|
5
|
+
function providerName(provider) {
|
|
6
|
+
return provider === 'gmail' ? 'Google' : 'Microsoft';
|
|
7
|
+
}
|
|
8
|
+
function checkCallback(url, params, callbackPath) {
|
|
9
|
+
if (url.pathname !== callbackPath)
|
|
10
|
+
return { kind: 'ignore', status: 404, message: 'Not found' };
|
|
11
|
+
if (url.searchParams.get('state') !== params.state) {
|
|
12
|
+
return { kind: 'ignore', status: 400, message: 'State mismatch. Restart the flow.' };
|
|
13
|
+
}
|
|
14
|
+
const oauthError = url.searchParams.get('error');
|
|
15
|
+
if (oauthError) {
|
|
16
|
+
const description = url.searchParams.get('error_description') ?? oauthError;
|
|
17
|
+
const name = providerName(params.provider);
|
|
18
|
+
return {
|
|
19
|
+
kind: 'error',
|
|
20
|
+
error: new EmailError('invalid_request', `${name} OAuth error: ${description}`),
|
|
21
|
+
message: `${name} returned an error: ${description}.`,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
const code = url.searchParams.get('code');
|
|
25
|
+
if (!code)
|
|
26
|
+
return { kind: 'ignore', status: 400, message: 'Missing code parameter.' };
|
|
27
|
+
return { kind: 'code', code };
|
|
28
|
+
}
|
|
29
|
+
function escapeHtml(value) {
|
|
30
|
+
return value.replace(/[&<>"']/g, (char) => {
|
|
31
|
+
const entities = {
|
|
32
|
+
'&': '&',
|
|
33
|
+
'<': '<',
|
|
34
|
+
'>': '>',
|
|
35
|
+
'"': '"',
|
|
36
|
+
"'": ''',
|
|
37
|
+
};
|
|
38
|
+
return entities[char];
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
function callbackPage(title, message) {
|
|
42
|
+
return ('<html><body style="font-family: sans-serif">' +
|
|
43
|
+
`<h2>${escapeHtml(title)}</h2><p>${escapeHtml(message)}</p>` +
|
|
44
|
+
'<p>You can close this tab and return to the terminal.</p></body></html>');
|
|
45
|
+
}
|
|
46
|
+
function failureMessage(error) {
|
|
47
|
+
return error instanceof EmailError
|
|
48
|
+
? { status: 400, message: error.message }
|
|
49
|
+
: { status: 500, message: 'Fluxmail could not finish connecting this account. Check the terminal for details.' };
|
|
50
|
+
}
|
|
51
|
+
function timeoutError(params, timeoutMs) {
|
|
52
|
+
const minutes = Math.max(1, Math.round(timeoutMs / 60_000));
|
|
53
|
+
const pasteHint = params.options?.paste
|
|
54
|
+
? 'If your browser runs on another computer, try again and paste the URL from its address bar ' +
|
|
55
|
+
'after you approve access. '
|
|
56
|
+
: '';
|
|
57
|
+
return new EmailError('invalid_request', `Timed out after ${minutes} minute${minutes === 1 ? '' : 's'} waiting for ${providerName(params.provider)} to redirect back. ` +
|
|
58
|
+
pasteHint +
|
|
59
|
+
`You can also forward the callback port with "ssh -L ${params.port}:127.0.0.1:${params.port} <server>".`);
|
|
60
|
+
}
|
|
61
|
+
/** Explains a busy callback port, including the Docker Compose case where the container owns it. */
|
|
62
|
+
export function callbackPortInUseError(error, port, provider) {
|
|
63
|
+
if (error.code !== 'EADDRINUSE')
|
|
64
|
+
return error;
|
|
65
|
+
return new Error(`OAuth callback port ${port} is already in use.\n\n` +
|
|
66
|
+
'If Fluxmail is running with Docker Compose, connect the account inside the container:\n\n' +
|
|
67
|
+
` docker compose exec fluxmail fluxmail accounts add ${provider}\n\n` +
|
|
68
|
+
`Otherwise, stop the process using port ${port} and try again.`, { cause: error });
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Waits for the OAuth redirect on the loopback listener and, when a terminal
|
|
72
|
+
* is available, for the same callback URL pasted into it. The first callback
|
|
73
|
+
* that carries a code wins, and the flow gives up after a timeout.
|
|
74
|
+
*/
|
|
75
|
+
export function runOAuthCallbackFlow(params) {
|
|
76
|
+
const callbackPath = new URL(params.redirectUri).pathname;
|
|
77
|
+
const paste = params.options?.paste;
|
|
78
|
+
const timeoutMs = params.options?.timeoutMs ?? DEFAULT_OAUTH_CALLBACK_TIMEOUT_MS;
|
|
79
|
+
return new Promise((resolve, reject) => {
|
|
80
|
+
let settled = false;
|
|
81
|
+
// An authorization code is single use, so only the first one is exchanged.
|
|
82
|
+
// Once a code is claimed, later callbacks, including provider errors, cannot end the flow.
|
|
83
|
+
let claimed = false;
|
|
84
|
+
let listening = false;
|
|
85
|
+
let announced = false;
|
|
86
|
+
const server = createServer((request, response) => {
|
|
87
|
+
void handleRequest(request.url ?? '/', response);
|
|
88
|
+
});
|
|
89
|
+
const lines = paste ? createInterface({ input: paste.input, terminal: false }) : undefined;
|
|
90
|
+
// The wait ends when a code is claimed. The token exchange that follows is
|
|
91
|
+
// bounded by the provider request itself.
|
|
92
|
+
const timer = setTimeout(() => {
|
|
93
|
+
if (claimed)
|
|
94
|
+
return;
|
|
95
|
+
settle(() => {
|
|
96
|
+
params.options?.onCallback?.('timeout');
|
|
97
|
+
reject(timeoutError(params, timeoutMs));
|
|
98
|
+
});
|
|
99
|
+
}, timeoutMs);
|
|
100
|
+
function cleanup() {
|
|
101
|
+
clearTimeout(timer);
|
|
102
|
+
lines?.close();
|
|
103
|
+
if (listening) {
|
|
104
|
+
// Responses carry Connection: close, so open sockets end once the page
|
|
105
|
+
// is written. Force any stragglers shut so the CLI process can exit.
|
|
106
|
+
server.close();
|
|
107
|
+
server.closeIdleConnections();
|
|
108
|
+
setTimeout(() => server.closeAllConnections(), 1000).unref();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function settle(action) {
|
|
112
|
+
if (settled)
|
|
113
|
+
return;
|
|
114
|
+
settled = true;
|
|
115
|
+
cleanup();
|
|
116
|
+
action();
|
|
117
|
+
}
|
|
118
|
+
function announce() {
|
|
119
|
+
if (announced)
|
|
120
|
+
return;
|
|
121
|
+
announced = true;
|
|
122
|
+
params.onAuthUrl(params.authUrl, { listening, pasteEnabled: paste !== undefined });
|
|
123
|
+
}
|
|
124
|
+
function busy() {
|
|
125
|
+
return claimed || settled;
|
|
126
|
+
}
|
|
127
|
+
/** Exchange a claimed code, then report the outcome to whichever path supplied it. */
|
|
128
|
+
async function exchange(source, code) {
|
|
129
|
+
claimed = true;
|
|
130
|
+
params.options?.onCallback?.(source);
|
|
131
|
+
try {
|
|
132
|
+
const { email, value } = await params.complete(code);
|
|
133
|
+
settle(() => resolve(value));
|
|
134
|
+
return { email };
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
settle(() => reject(error));
|
|
138
|
+
return { error };
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
function respond(response, status, body, html = false) {
|
|
142
|
+
response
|
|
143
|
+
.writeHead(status, {
|
|
144
|
+
connection: 'close',
|
|
145
|
+
'content-type': html ? 'text/html; charset=utf-8' : 'text/plain; charset=utf-8',
|
|
146
|
+
})
|
|
147
|
+
.end(body);
|
|
148
|
+
}
|
|
149
|
+
async function handleRequest(rawUrl, response) {
|
|
150
|
+
let url;
|
|
151
|
+
try {
|
|
152
|
+
url = new URL(rawUrl, params.redirectUri);
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
respond(response, 400, 'Bad request');
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const check = checkCallback(url, params, callbackPath);
|
|
159
|
+
if (check.kind === 'ignore') {
|
|
160
|
+
respond(response, check.status, check.message);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
if (busy()) {
|
|
164
|
+
respond(response, 409, 'This connection is already being completed. Return to the terminal.');
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (check.kind === 'error') {
|
|
168
|
+
settle(() => reject(check.error));
|
|
169
|
+
respond(response, 400, `${check.message} You can close this tab.`);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const outcome = await exchange('listener', check.code);
|
|
173
|
+
if (outcome.email !== undefined) {
|
|
174
|
+
respond(response, 200, callbackPage(`${outcome.email} is connected to Fluxmail`, 'The account is ready to use.'), true);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const failure = failureMessage(outcome.error);
|
|
178
|
+
respond(response, failure.status, callbackPage('Fluxmail could not connect this account', failure.message), true);
|
|
179
|
+
}
|
|
180
|
+
async function handlePastedLine(line) {
|
|
181
|
+
const text = line.trim();
|
|
182
|
+
if (!text || settled)
|
|
183
|
+
return;
|
|
184
|
+
if (claimed) {
|
|
185
|
+
paste.write('Fluxmail is already finishing this connection. Wait for it to complete.');
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
let url;
|
|
189
|
+
try {
|
|
190
|
+
url = new URL(text);
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
paste.write('That is not a URL. Paste the full address from your browser, starting with ' +
|
|
194
|
+
`${new URL(params.redirectUri).origin}.`);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
const check = checkCallback(url, params, callbackPath);
|
|
198
|
+
if (check.kind === 'ignore') {
|
|
199
|
+
paste.write(check.status === 404
|
|
200
|
+
? `That URL is not the Fluxmail callback. It should start with ${params.redirectUri}.`
|
|
201
|
+
: `${check.message} Paste the URL from the most recent authorization.`);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
if (check.kind === 'error') {
|
|
205
|
+
settle(() => reject(check.error));
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
await exchange('pasted', check.code);
|
|
209
|
+
}
|
|
210
|
+
lines?.on('line', (line) => void handlePastedLine(line));
|
|
211
|
+
lines?.on('close', () => {
|
|
212
|
+
// With the listener running, the browser redirect can still finish the flow.
|
|
213
|
+
if (settled || claimed || listening)
|
|
214
|
+
return;
|
|
215
|
+
settle(() => reject(new EmailError('invalid_request', 'Input closed before a callback URL was pasted, and the callback port is in use. Run the command again.')));
|
|
216
|
+
});
|
|
217
|
+
server.on('error', (error) => {
|
|
218
|
+
if (listening || settled)
|
|
219
|
+
return;
|
|
220
|
+
// Without a terminal, the listener is the only way to finish.
|
|
221
|
+
if (!paste || error.code !== 'EADDRINUSE') {
|
|
222
|
+
settle(() => reject(callbackPortInUseError(error, params.port, params.provider)));
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
announce();
|
|
226
|
+
});
|
|
227
|
+
server.listen(params.port, params.host, () => {
|
|
228
|
+
listening = true;
|
|
229
|
+
if (settled) {
|
|
230
|
+
server.close();
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
announce();
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
//# sourceMappingURL=oauthCallback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauthCallback.js","sourceRoot":"","sources":["../../src/accounts/oauthCallback.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Provider } from '@fluxmail/core';
|
|
2
2
|
import type { FluxmailConfig } from '../config.js';
|
|
3
|
+
import type { OAuthCallbackSource } from './oauthCallback.js';
|
|
3
4
|
import type { TelemetryProperties } from '../telemetry.js';
|
|
4
5
|
import type { AccountRegistry } from './registry.js';
|
|
5
6
|
/** Where the provider sends the browser after consent. */
|
|
@@ -19,6 +20,8 @@ export interface ConnectionTelemetry {
|
|
|
19
20
|
reauthorize?: boolean;
|
|
20
21
|
flow?: ConnectionFlow;
|
|
21
22
|
oauthApp?: OAuthAppKind;
|
|
23
|
+
/** How a loopback callback reached the CLI, or that it timed out waiting. */
|
|
24
|
+
callback?: OAuthCallbackSource;
|
|
22
25
|
}
|
|
23
26
|
/** Describe a connection attempt without naming the mailbox it connects. */
|
|
24
27
|
export declare function connectionProperties(connection: ConnectionTelemetry): TelemetryProperties;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../../src/accounts/telemetry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAE3D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD,0DAA0D;AAC1D,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,UAAU,CAAC;AAEnD,6EAA6E;AAC7E,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,QAAQ,CAAC;AAEjD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,YAAY,GAAG,SAAS,CAGvG;AAED,+EAA+E;AAC/E,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,cAAc,EAAE,QAAQ,EAAE,QAAQ,GAAG,YAAY,GAAG,SAAS,CAE3G;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,QAAQ,CAAC;IACnB,2EAA2E;IAC3E,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,QAAQ,CAAC,EAAE,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../../src/accounts/telemetry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAE3D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD,0DAA0D;AAC1D,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,UAAU,CAAC;AAEnD,6EAA6E;AAC7E,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,QAAQ,CAAC;AAEjD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,YAAY,GAAG,SAAS,CAGvG;AAED,+EAA+E;AAC/E,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,cAAc,EAAE,QAAQ,EAAE,QAAQ,GAAG,YAAY,GAAG,SAAS,CAE3G;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,QAAQ,CAAC;IACnB,2EAA2E;IAC3E,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,mBAAmB,CAAC;CAChC;AAED,4EAA4E;AAC5E,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,mBAAmB,GAAG,mBAAmB,CAQzF;AAED,8EAA8E;AAC9E,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,IAAI,CAAC,eAAe,EAAE,uBAAuB,CAAC,GAAG,SAAS,GACnE,mBAAmB,CAcrB"}
|
|
@@ -19,6 +19,7 @@ export function connectionProperties(connection) {
|
|
|
19
19
|
...(connection.reauthorize === undefined ? {} : { reauthorize: connection.reauthorize }),
|
|
20
20
|
...(connection.flow ? { connection_flow: connection.flow } : {}),
|
|
21
21
|
...(connection.oauthApp ? { oauth_app: connection.oauthApp } : {}),
|
|
22
|
+
...(connection.callback ? { oauth_callback: connection.callback } : {}),
|
|
22
23
|
};
|
|
23
24
|
}
|
|
24
25
|
/** Mailbox totals by provider. Counts only: no address, id, or credential. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"telemetry.js","sourceRoot":"","sources":["../../src/accounts/telemetry.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"telemetry.js","sourceRoot":"","sources":["../../src/accounts/telemetry.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AASnE;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,QAAkB,EAAE,QAA4B;IAC3E,IAAI,QAAQ,KAAK,MAAM,IAAI,CAAC,QAAQ;QAAE,OAAO,SAAS,CAAC;IACvD,OAAO,QAAQ,KAAK,OAAO,IAAI,QAAQ,KAAK,wBAAwB,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC;AAC/F,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,sBAAsB,CAAC,MAAsB,EAAE,QAAkB;IAC/E,OAAO,YAAY,CAAC,QAAQ,EAAE,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AAC7G,CAAC;AAYD,4EAA4E;AAC5E,MAAM,UAAU,oBAAoB,CAAC,UAA+B;IAClE,OAAO;QACL,QAAQ,EAAE,UAAU,CAAC,QAAQ;QAC7B,GAAG,CAAC,UAAU,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,UAAU,CAAC,WAAW,EAAE,CAAC;QACxF,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChE,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAClE,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACxE,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,0BAA0B,CACxC,QAAoE;IAEpE,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,QAAQ,EAAE,qBAAqB,EAAE,CAAC;QACjD,IAAI,CAAC,MAAM;YAAE,OAAO,EAAE,CAAC;QACvB,OAAO;YACL,aAAa,EAAE,MAAM,CAAC,KAAK;YAC3B,mBAAmB,EAAE,MAAM,CAAC,KAAK;YACjC,qBAAqB,EAAE,MAAM,CAAC,OAAO;YACrC,kBAAkB,EAAE,MAAM,CAAC,IAAI;SAChC,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,mDAAmD;QACnD,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAOA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAOA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AA0B1C,OAAO,EAAe,KAAK,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAapE,OAAO,EAQL,KAAK,gBAAgB,EACtB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAQL,KAAK,SAAS,EAEf,MAAM,gBAAgB,CAAC;AAsBxB,OAAO,EAAmD,KAAK,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAuCrH,UAAU,iBAAiB;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAcD,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,iBAAiB,EAAE,gBAAgB,UAAQ,GAAG,gBAAgB,CAoB/G;AAED,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,iBAAiB,EACvB,QAAQ,EAAE,IAAI,CAAC,UAAU,EAAE,mBAAmB,CAAC,GAC9C,gBAAgB,CAQlB;AAwKD,MAAM,WAAW,iBAAiB;IAChC,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,qBAAqB,CAAC,EAAE,wBAAwB,CAAC;IACjD,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CACrD;AA+ED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,KAAK,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAatF;AAED,UAAU,cAAc;IACtB,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC;CACnD;AAUD,wBAAsB,2BAA2B,IAAI,OAAO,CAAC,IAAI,CAAC,CAQjE;AAED,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAExE;AAED,mFAAmF;AACnF,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,iBAAsB,GAAG,OAAO,CA0rDzE;AAED,wBAAsB,MAAM,CAAC,IAAI,GAAE,SAAS,MAAM,EAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAQlF"}
|
package/dist/cli.js
CHANGED
|
@@ -178,6 +178,33 @@ function readSecretFile(file) {
|
|
|
178
178
|
async function secretInput(file, label) {
|
|
179
179
|
return file === undefined ? hiddenPrompt(label) : readSecretFile(file);
|
|
180
180
|
}
|
|
181
|
+
/** Print the consent URL and explain how to finish when the redirect cannot reach this computer. */
|
|
182
|
+
function printLoopbackInstructions(provider, url, context, port) {
|
|
183
|
+
const providerName = provider === 'gmail' ? 'Google' : 'Microsoft';
|
|
184
|
+
const access = provider === 'gmail' ? 'Gmail access' : 'Microsoft mail access';
|
|
185
|
+
console.log(`\nOpen this URL in your browser to authorize ${access}:\n`);
|
|
186
|
+
console.log(` ${url}\n`);
|
|
187
|
+
if (!context.listening) {
|
|
188
|
+
console.log(`Port ${port} is already in use, so Fluxmail cannot receive the redirect from ${providerName}.\n\n` +
|
|
189
|
+
'If Fluxmail is running with Docker Compose, press Ctrl-C and connect the account inside the container ' +
|
|
190
|
+
'so it is saved there:\n\n' +
|
|
191
|
+
` docker compose exec fluxmail fluxmail accounts add ${provider}\n\n` +
|
|
192
|
+
"Otherwise, approve access, then copy the full URL from your browser's address bar and paste it here.");
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
console.log(`Waiting for ${providerName} to redirect back...`);
|
|
196
|
+
if (context.pasteEnabled) {
|
|
197
|
+
console.log('If your browser runs on another computer, the page will not load after you approve access. ' +
|
|
198
|
+
'Copy the full URL from its address bar and paste it here.');
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
/** Accept a pasted callback URL when a person is at the terminal, and report how the callback arrived. */
|
|
202
|
+
function loopbackFlowOptions(program, provider) {
|
|
203
|
+
return {
|
|
204
|
+
...(process.stdin.isTTY ? { paste: { input: process.stdin, write: (message) => console.error(message) } } : {}),
|
|
205
|
+
onCallback: (callback) => recordCliOperationProperties(program, connectionProperties({ provider, callback })),
|
|
206
|
+
};
|
|
207
|
+
}
|
|
181
208
|
/** Offered when a configured public URL, not an explicit --hosted, chose the hosted flow. */
|
|
182
209
|
const LOOPBACK_FLOW_REMEDY = 'Pass --local to use the loopback flow on this computer instead.';
|
|
183
210
|
function planLine(ent) {
|
|
@@ -979,11 +1006,7 @@ export function createCliProgram(options = {}) {
|
|
|
979
1006
|
console.log('This link expires in 10 minutes and can only be used once.');
|
|
980
1007
|
return;
|
|
981
1008
|
}
|
|
982
|
-
const account = await runMicrosoftLoopbackFlow(ctx.config, (url) => {
|
|
983
|
-
console.log('\nOpen this URL in your browser to authorize Microsoft mail access:\n');
|
|
984
|
-
console.log(` ${url}\n`);
|
|
985
|
-
console.log('Waiting for Microsoft to redirect back...');
|
|
986
|
-
}, (result) => {
|
|
1009
|
+
const account = await runMicrosoftLoopbackFlow(ctx.config, (url, context) => printLoopbackInstructions('outlook', url, context, ctx.config.oauthPort), (result) => {
|
|
987
1010
|
const duplicate = ctx.registry
|
|
988
1011
|
.listAccounts()
|
|
989
1012
|
.find((candidate) => candidate.email.toLowerCase() === result.email.toLowerCase());
|
|
@@ -995,7 +1018,7 @@ export function createCliProgram(options = {}) {
|
|
|
995
1018
|
'Try again and choose the matching Microsoft account.');
|
|
996
1019
|
}
|
|
997
1020
|
return ctx.registry.addOutlookAccount(result.email, result.credentials, result.displayName, owner.id, existing?.id, access);
|
|
998
|
-
});
|
|
1021
|
+
}, loopbackFlowOptions(program, 'outlook'));
|
|
999
1022
|
auditConnection('account.connection.complete', account.id);
|
|
1000
1023
|
recordCliOperationProperties(program, {
|
|
1001
1024
|
reauthorize: accountIdsBeforeConnection.has(account.id),
|
|
@@ -1018,11 +1041,7 @@ export function createCliProgram(options = {}) {
|
|
|
1018
1041
|
console.log('This link expires in 10 minutes and can only be used once.');
|
|
1019
1042
|
return;
|
|
1020
1043
|
}
|
|
1021
|
-
const account = await runLoopbackFlow(ctx.config, (url) => {
|
|
1022
|
-
console.log('\nOpen this URL in your browser to authorize Gmail access:\n');
|
|
1023
|
-
console.log(` ${url}\n`);
|
|
1024
|
-
console.log('Waiting for Google to redirect back…');
|
|
1025
|
-
}, (result) => {
|
|
1044
|
+
const account = await runLoopbackFlow(ctx.config, (url, context) => printLoopbackInstructions('gmail', url, context, ctx.config.oauthPort), (result) => {
|
|
1026
1045
|
const duplicate = ctx.registry
|
|
1027
1046
|
.listAccounts()
|
|
1028
1047
|
.find((candidate) => candidate.email.toLowerCase() === result.email.toLowerCase());
|
|
@@ -1034,7 +1053,7 @@ export function createCliProgram(options = {}) {
|
|
|
1034
1053
|
'Try again and choose the matching Google account.');
|
|
1035
1054
|
}
|
|
1036
1055
|
return ctx.registry.addGmailAccount(result.email, result.tokens, result.displayName, owner.id, access, result.oauthClient);
|
|
1037
|
-
});
|
|
1056
|
+
}, loopbackFlowOptions(program, 'gmail'));
|
|
1038
1057
|
auditConnection('account.connection.complete', account.id);
|
|
1039
1058
|
recordCliOperationProperties(program, {
|
|
1040
1059
|
reauthorize: accountIdsBeforeConnection.has(account.id),
|