fluxmail 0.0.1 → 0.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/dist/accounts/googleAuth.d.ts +31 -0
- package/dist/accounts/googleAuth.d.ts.map +1 -0
- package/dist/accounts/googleAuth.js +143 -0
- package/dist/accounts/googleAuth.js.map +1 -0
- package/dist/accounts/registry.d.ts +28 -0
- package/dist/accounts/registry.d.ts.map +1 -0
- package/dist/accounts/registry.js +185 -0
- package/dist/accounts/registry.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +434 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +40 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +215 -0
- package/dist/config.js.map +1 -0
- package/dist/context.d.ts +15 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +15 -0
- package/dist/context.js.map +1 -0
- package/dist/http/app.d.ts +16 -0
- package/dist/http/app.d.ts.map +1 -0
- package/dist/http/app.js +110 -0
- package/dist/http/app.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/licensing/activation.d.ts +5 -0
- package/dist/licensing/activation.d.ts.map +1 -0
- package/dist/licensing/activation.js +11 -0
- package/dist/licensing/activation.js.map +1 -0
- package/dist/licensing/client.d.ts +52 -0
- package/dist/licensing/client.d.ts.map +1 -0
- package/dist/licensing/client.js +75 -0
- package/dist/licensing/client.js.map +1 -0
- package/dist/licensing/entitlements.d.ts +55 -0
- package/dist/licensing/entitlements.d.ts.map +1 -0
- package/dist/licensing/entitlements.js +116 -0
- package/dist/licensing/entitlements.js.map +1 -0
- package/dist/licensing/lease.d.ts +47 -0
- package/dist/licensing/lease.d.ts.map +1 -0
- package/dist/licensing/lease.js +103 -0
- package/dist/licensing/lease.js.map +1 -0
- package/dist/licensing/refresher.d.ts +46 -0
- package/dist/licensing/refresher.d.ts.map +1 -0
- package/dist/licensing/refresher.js +150 -0
- package/dist/licensing/refresher.js.map +1 -0
- package/dist/mcp/buildServer.d.ts +28 -0
- package/dist/mcp/buildServer.d.ts.map +1 -0
- package/dist/mcp/buildServer.js +394 -0
- package/dist/mcp/buildServer.js.map +1 -0
- package/dist/scheduler/sendScheduler.d.ts +38 -0
- package/dist/scheduler/sendScheduler.d.ts.map +1 -0
- package/dist/scheduler/sendScheduler.js +130 -0
- package/dist/scheduler/sendScheduler.js.map +1 -0
- package/dist/service/emailService.d.ts +138 -0
- package/dist/service/emailService.d.ts.map +1 -0
- package/dist/service/emailService.js +375 -0
- package/dist/service/emailService.js.map +1 -0
- package/dist/storage/apiKeys.d.ts +27 -0
- package/dist/storage/apiKeys.d.ts.map +1 -0
- package/dist/storage/apiKeys.js +43 -0
- package/dist/storage/apiKeys.js.map +1 -0
- package/dist/storage/crypto.d.ts +4 -0
- package/dist/storage/crypto.d.ts.map +1 -0
- package/dist/storage/crypto.js +21 -0
- package/dist/storage/crypto.js.map +1 -0
- package/dist/storage/db.d.ts +729 -0
- package/dist/storage/db.d.ts.map +1 -0
- package/dist/storage/db.js +143 -0
- package/dist/storage/db.js.map +1 -0
- package/dist/storage/members.d.ts +29 -0
- package/dist/storage/members.d.ts.map +1 -0
- package/dist/storage/members.js +67 -0
- package/dist/storage/members.js.map +1 -0
- package/dist/storage/scheduledSends.d.ts +60 -0
- package/dist/storage/scheduledSends.d.ts.map +1 -0
- package/dist/storage/scheduledSends.js +169 -0
- package/dist/storage/scheduledSends.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +7 -0
- package/dist/version.js.map +1 -0
- package/package.json +38 -15
- package/README.md +0 -29
- package/bin/fluxmail.js +0 -4
- package/src/cli.js +0 -36
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { OAuth2Client, type Credentials } from 'google-auth-library';
|
|
2
|
+
import type { FluxmailConfig } from '../config.js';
|
|
3
|
+
/**
|
|
4
|
+
* Full mail scope: the unified API includes permanent delete, which Gmail only
|
|
5
|
+
* allows with https://mail.google.com/ (gmail.modify cannot call messages.delete).
|
|
6
|
+
* Users bring their own OAuth app, so they are consenting to their own credentials.
|
|
7
|
+
*/
|
|
8
|
+
export declare const GMAIL_SCOPES: string[];
|
|
9
|
+
export declare function requireGoogleConfig(config: FluxmailConfig): {
|
|
10
|
+
clientId: string;
|
|
11
|
+
clientSecret: string;
|
|
12
|
+
};
|
|
13
|
+
export declare function createOAuthClient(config: FluxmailConfig, redirectUri: string): OAuth2Client;
|
|
14
|
+
export declare function buildAuthUrl(client: OAuth2Client, state: string): string;
|
|
15
|
+
/** Verify the OAuth id_token and extract the authenticated Google identity. */
|
|
16
|
+
export declare function identityFromIdToken(client: OAuth2Client, tokens: Credentials): Promise<{
|
|
17
|
+
email: string;
|
|
18
|
+
displayName?: string;
|
|
19
|
+
}>;
|
|
20
|
+
export interface OAuthResult {
|
|
21
|
+
email: string;
|
|
22
|
+
displayName?: string;
|
|
23
|
+
tokens: Credentials;
|
|
24
|
+
}
|
|
25
|
+
export declare function exchangeCode(client: OAuth2Client, code: string): Promise<OAuthResult>;
|
|
26
|
+
/**
|
|
27
|
+
* Loopback OAuth flow for the CLI: listens once on config.oauthPort, prints the
|
|
28
|
+
* consent URL, and resolves when Google redirects back with a code.
|
|
29
|
+
*/
|
|
30
|
+
export declare function runLoopbackFlow<T = OAuthResult>(config: FluxmailConfig, onAuthUrl: (url: string) => void, onAuthorized?: (result: OAuthResult) => T | Promise<T>): Promise<T>;
|
|
31
|
+
//# sourceMappingURL=googleAuth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"googleAuth.d.ts","sourceRoot":"","sources":["../../src/accounts/googleAuth.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAErE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD;;;;GAIG;AACH,eAAO,MAAM,YAAY,UAA6D,CAAC;AAEvF,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,cAAc,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAStG;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,GAAG,YAAY,CAG3F;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAOxE;AAED,+EAA+E;AAC/E,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,YAAY,EACpB,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAYlD;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,WAAW,CAAC;CACrB;AAoCD,wBAAsB,YAAY,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAU3F;AAED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,CAAC,GAAG,WAAW,EACnD,MAAM,EAAE,cAAc,EACtB,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,EAChC,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GACrD,OAAO,CAAC,CAAC,CAAC,CA4DZ"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { createServer } from 'node:http';
|
|
2
|
+
import { randomBytes } from 'node:crypto';
|
|
3
|
+
import { OAuth2Client } from 'google-auth-library';
|
|
4
|
+
import { EmailError } from '@fluxmail/core';
|
|
5
|
+
/**
|
|
6
|
+
* Full mail scope: the unified API includes permanent delete, which Gmail only
|
|
7
|
+
* allows with https://mail.google.com/ (gmail.modify cannot call messages.delete).
|
|
8
|
+
* Users bring their own OAuth app, so they are consenting to their own credentials.
|
|
9
|
+
*/
|
|
10
|
+
export const GMAIL_SCOPES = ['https://mail.google.com/', 'openid', 'email', 'profile'];
|
|
11
|
+
export function requireGoogleConfig(config) {
|
|
12
|
+
if (!config.google) {
|
|
13
|
+
throw new EmailError('invalid_request', 'GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET are not set. Create OAuth credentials in Google Cloud ' +
|
|
14
|
+
'(see README "Google setup") and set both environment variables.');
|
|
15
|
+
}
|
|
16
|
+
return config.google;
|
|
17
|
+
}
|
|
18
|
+
export function createOAuthClient(config, redirectUri) {
|
|
19
|
+
const { clientId, clientSecret } = requireGoogleConfig(config);
|
|
20
|
+
return new OAuth2Client({ clientId, clientSecret, redirectUri });
|
|
21
|
+
}
|
|
22
|
+
export function buildAuthUrl(client, state) {
|
|
23
|
+
return client.generateAuthUrl({
|
|
24
|
+
access_type: 'offline',
|
|
25
|
+
prompt: 'consent',
|
|
26
|
+
scope: GMAIL_SCOPES,
|
|
27
|
+
state,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
/** Verify the OAuth id_token and extract the authenticated Google identity. */
|
|
31
|
+
export async function identityFromIdToken(client, tokens) {
|
|
32
|
+
const idToken = tokens.id_token;
|
|
33
|
+
if (!idToken)
|
|
34
|
+
throw new EmailError('provider_unavailable', 'Google did not return an id_token');
|
|
35
|
+
if (!client._clientId)
|
|
36
|
+
throw new EmailError('provider_unavailable', 'Google OAuth client has no client id');
|
|
37
|
+
const ticket = await client.verifyIdToken({ idToken, audience: client._clientId });
|
|
38
|
+
const claims = ticket.getPayload();
|
|
39
|
+
if (!claims)
|
|
40
|
+
throw new EmailError('provider_unavailable', 'Google id_token has no claims');
|
|
41
|
+
if (!claims.email)
|
|
42
|
+
throw new EmailError('provider_unavailable', 'Google id_token has no email claim');
|
|
43
|
+
if (!claims.email_verified) {
|
|
44
|
+
throw new EmailError('provider_unavailable', 'Google did not verify the account email address');
|
|
45
|
+
}
|
|
46
|
+
return { email: claims.email, ...(claims.name ? { displayName: claims.name } : {}) };
|
|
47
|
+
}
|
|
48
|
+
function oauthListenerError(err, port) {
|
|
49
|
+
const code = err.code;
|
|
50
|
+
if (code !== 'EADDRINUSE')
|
|
51
|
+
return err;
|
|
52
|
+
return new Error(`OAuth callback port ${port} is already in use.\n\n` +
|
|
53
|
+
'If Fluxmail is already running with Docker Compose, connect the account inside the container:\n\n' +
|
|
54
|
+
' docker compose exec fluxmail fluxmail accounts add gmail\n\n' +
|
|
55
|
+
`Otherwise, stop the process using port ${port} and try again.`, { cause: err });
|
|
56
|
+
}
|
|
57
|
+
function escapeHtml(value) {
|
|
58
|
+
return value.replace(/[&<>"']/g, (char) => {
|
|
59
|
+
const entities = {
|
|
60
|
+
'&': '&',
|
|
61
|
+
'<': '<',
|
|
62
|
+
'>': '>',
|
|
63
|
+
'"': '"',
|
|
64
|
+
"'": ''',
|
|
65
|
+
};
|
|
66
|
+
return entities[char];
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
function callbackPage(title, message) {
|
|
70
|
+
return ('<html><body style="font-family: sans-serif">' +
|
|
71
|
+
`<h2>${escapeHtml(title)}</h2><p>${escapeHtml(message)}</p>` +
|
|
72
|
+
'<p>You can close this tab and return to the terminal.</p></body></html>');
|
|
73
|
+
}
|
|
74
|
+
export async function exchangeCode(client, code) {
|
|
75
|
+
const { tokens } = await client.getToken(code);
|
|
76
|
+
if (!tokens.refresh_token) {
|
|
77
|
+
throw new EmailError('invalid_request', 'Google did not return a refresh token. Remove Fluxmail from the account\'s third-party access ' +
|
|
78
|
+
'(myaccount.google.com/permissions) and try again.');
|
|
79
|
+
}
|
|
80
|
+
return { ...(await identityFromIdToken(client, tokens)), tokens };
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Loopback OAuth flow for the CLI: listens once on config.oauthPort, prints the
|
|
84
|
+
* consent URL, and resolves when Google redirects back with a code.
|
|
85
|
+
*/
|
|
86
|
+
export async function runLoopbackFlow(config, onAuthUrl, onAuthorized) {
|
|
87
|
+
const redirectUri = `http://localhost:${config.oauthPort}/oauth/callback`;
|
|
88
|
+
const client = createOAuthClient(config, redirectUri);
|
|
89
|
+
const state = randomBytes(16).toString('hex');
|
|
90
|
+
return new Promise((resolve, reject) => {
|
|
91
|
+
const server = createServer(async (req, res) => {
|
|
92
|
+
// Close the listener and destroy lingering keep-alive sockets (the browser
|
|
93
|
+
// holds its connection open after loading the page); otherwise the CLI
|
|
94
|
+
// process never exits.
|
|
95
|
+
const finish = (settle) => {
|
|
96
|
+
res.once('close', () => {
|
|
97
|
+
server.close();
|
|
98
|
+
server.closeAllConnections();
|
|
99
|
+
settle();
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
try {
|
|
103
|
+
const url = new URL(req.url ?? '/', redirectUri);
|
|
104
|
+
if (url.pathname !== '/oauth/callback') {
|
|
105
|
+
res.writeHead(404).end('Not found');
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (url.searchParams.get('state') !== state) {
|
|
109
|
+
res.writeHead(400).end('State mismatch. Restart the flow.');
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const error = url.searchParams.get('error');
|
|
113
|
+
if (error) {
|
|
114
|
+
finish(() => reject(new EmailError('invalid_request', `Google OAuth error: ${error}`)));
|
|
115
|
+
res.writeHead(400).end(`Google returned an error: ${error}. You can close this tab.`);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const code = url.searchParams.get('code');
|
|
119
|
+
if (!code) {
|
|
120
|
+
res.writeHead(400).end('Missing code parameter.');
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const result = await exchangeCode(client, code);
|
|
124
|
+
const accepted = onAuthorized ? await onAuthorized(result) : result;
|
|
125
|
+
finish(() => resolve(accepted));
|
|
126
|
+
res.writeHead(200, { 'content-type': 'text/html; charset=utf-8' }).end(callbackPage(`${result.email} is connected to Fluxmail`, 'The account is ready to use.'));
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
finish(() => reject(err));
|
|
130
|
+
const expected = err instanceof EmailError;
|
|
131
|
+
const message = expected
|
|
132
|
+
? err.message
|
|
133
|
+
: 'Fluxmail could not finish connecting this account. Check the terminal for details.';
|
|
134
|
+
res.writeHead(expected ? 400 : 500, { 'content-type': 'text/html; charset=utf-8' }).end(callbackPage('Fluxmail could not connect this account', message));
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
server.on('error', (err) => reject(oauthListenerError(err, config.oauthPort)));
|
|
138
|
+
server.listen(config.oauthPort, config.oauthHost, () => {
|
|
139
|
+
onAuthUrl(buildAuthUrl(client, state));
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=googleAuth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"googleAuth.js","sourceRoot":"","sources":["../../src/accounts/googleAuth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type Credentials } from 'google-auth-library';
|
|
2
|
+
import { type Account, type EmailProvider } from '@fluxmail/core';
|
|
3
|
+
import type { FluxmailConfig } from '../config.js';
|
|
4
|
+
import { type FluxmailDb } from '../storage/db.js';
|
|
5
|
+
export declare class AccountRegistry {
|
|
6
|
+
private readonly db;
|
|
7
|
+
private readonly config;
|
|
8
|
+
private readonly providers;
|
|
9
|
+
constructor(db: FluxmailDb, config: FluxmailConfig);
|
|
10
|
+
listAccounts(): Account[];
|
|
11
|
+
getAccount(accountId: string): Account;
|
|
12
|
+
/** Fail before OAuth when a new account would exceed the current plan. */
|
|
13
|
+
assertCanAddAccount(): void;
|
|
14
|
+
/** Resolve an account id, defaulting to the sole account when only one exists. */
|
|
15
|
+
resolveAccountId(accountId?: string): string;
|
|
16
|
+
getProvider(accountId: string): EmailProvider;
|
|
17
|
+
private loadTokenRow;
|
|
18
|
+
private loadTokens;
|
|
19
|
+
private writeTokens;
|
|
20
|
+
private saveTokens;
|
|
21
|
+
private buildGmailProvider;
|
|
22
|
+
addGmailAccount(email: string, tokens: Credentials, displayName?: string, memberId?: string): Account;
|
|
23
|
+
/** Assign a mailbox to a member, or make it shared again (memberId = null). */
|
|
24
|
+
assignAccountMember(accountId: string, memberId: string | null): Account;
|
|
25
|
+
removeAccount(accountId: string): void;
|
|
26
|
+
markStatus(accountId: string, status: Account['status']): void;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/accounts/registry.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgB,KAAK,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACrE,OAAO,EAAc,KAAK,OAAO,EAAE,KAAK,aAAa,EAAiB,MAAM,gBAAgB,CAAC;AAE7F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAyB,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAM1E,qBAAa,eAAe;IAOxB,OAAO,CAAC,QAAQ,CAAC,EAAE;IACnB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAPzB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAGtB;gBAGe,EAAE,EAAE,UAAU,EACd,MAAM,EAAE,cAAc;IAGzC,YAAY,IAAI,OAAO,EAAE;IAgBzB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAMtC,0EAA0E;IAC1E,mBAAmB,IAAI,IAAI;IAkB3B,kFAAkF;IAClF,gBAAgB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM;IAoB5C,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa;IAiB7C,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,UAAU;IAKlB,OAAO,CAAC,WAAW;IAiBnB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,kBAAkB;IAyB1B,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO;IAuCrG,+EAA+E;IAC/E,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO;IAOxE,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAMtC,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,IAAI;CAG/D"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { randomBytes } from 'node:crypto';
|
|
2
|
+
import { eq } from 'drizzle-orm';
|
|
3
|
+
import { OAuth2Client } from 'google-auth-library';
|
|
4
|
+
import { EmailError } from '@fluxmail/core';
|
|
5
|
+
import { GmailProvider, GMAIL_CAPABILITIES } from '@fluxmail/provider-gmail';
|
|
6
|
+
import { accounts, oauthTokens } from '../storage/db.js';
|
|
7
|
+
import { decryptString, encryptString } from '../storage/crypto.js';
|
|
8
|
+
import { assertAccountLimit, getEntitlements } from '../licensing/entitlements.js';
|
|
9
|
+
import { getMember } from '../storage/members.js';
|
|
10
|
+
import { requireGoogleConfig } from './googleAuth.js';
|
|
11
|
+
export class AccountRegistry {
|
|
12
|
+
db;
|
|
13
|
+
config;
|
|
14
|
+
providers = new Map();
|
|
15
|
+
constructor(db, config) {
|
|
16
|
+
this.db = db;
|
|
17
|
+
this.config = config;
|
|
18
|
+
}
|
|
19
|
+
listAccounts() {
|
|
20
|
+
return this.db
|
|
21
|
+
.select()
|
|
22
|
+
.from(accounts)
|
|
23
|
+
.all()
|
|
24
|
+
.map((row) => ({
|
|
25
|
+
id: row.id,
|
|
26
|
+
provider: row.provider,
|
|
27
|
+
email: row.email,
|
|
28
|
+
...(row.displayName ? { displayName: row.displayName } : {}),
|
|
29
|
+
status: row.status,
|
|
30
|
+
capabilities: GMAIL_CAPABILITIES,
|
|
31
|
+
...(row.memberId ? { memberId: row.memberId } : {}),
|
|
32
|
+
}));
|
|
33
|
+
}
|
|
34
|
+
getAccount(accountId) {
|
|
35
|
+
const account = this.listAccounts().find((a) => a.id === accountId);
|
|
36
|
+
if (!account)
|
|
37
|
+
throw new EmailError('not_found', `No account with id "${accountId}"`);
|
|
38
|
+
return account;
|
|
39
|
+
}
|
|
40
|
+
/** Fail before OAuth when a new account would exceed the current plan. */
|
|
41
|
+
assertCanAddAccount() {
|
|
42
|
+
const existing = this.listAccounts();
|
|
43
|
+
try {
|
|
44
|
+
assertAccountLimit(existing.length, getEntitlements(this.db));
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
if (!(err instanceof EmailError))
|
|
48
|
+
throw err;
|
|
49
|
+
const reconnect = existing.length === 1
|
|
50
|
+
? `To reconnect ${existing[0].email}, rerun this command with ` +
|
|
51
|
+
`"--reauthorize ${existing[0].id}".`
|
|
52
|
+
: 'To reconnect an existing account, rerun this command with "--reauthorize <account-id>".';
|
|
53
|
+
throw new EmailError(err.code, `${err.message} ${reconnect}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/** Resolve an account id, defaulting to the sole account when only one exists. */
|
|
57
|
+
resolveAccountId(accountId) {
|
|
58
|
+
if (accountId)
|
|
59
|
+
return this.getAccount(accountId).id;
|
|
60
|
+
const all = this.listAccounts();
|
|
61
|
+
if (all.length === 0) {
|
|
62
|
+
throw new EmailError('invalid_request', 'No email accounts are connected yet. Run "fluxmail accounts add gmail" to connect one.');
|
|
63
|
+
}
|
|
64
|
+
if (all.length > 1) {
|
|
65
|
+
throw new EmailError('invalid_request', `Multiple accounts are connected; specify accountId. Available: ${all
|
|
66
|
+
.map((a) => `${a.id} (${a.email})`)
|
|
67
|
+
.join(', ')}`);
|
|
68
|
+
}
|
|
69
|
+
return all[0].id;
|
|
70
|
+
}
|
|
71
|
+
getProvider(accountId) {
|
|
72
|
+
const account = this.getAccount(accountId);
|
|
73
|
+
if (account.provider !== 'gmail') {
|
|
74
|
+
throw new EmailError('unsupported_capability', `Provider "${account.provider}" is not supported yet`);
|
|
75
|
+
}
|
|
76
|
+
const tokenRow = this.loadTokenRow(accountId);
|
|
77
|
+
const cached = this.providers.get(accountId);
|
|
78
|
+
if (cached?.encryptedTokens === tokenRow.encryptedTokens)
|
|
79
|
+
return cached.provider;
|
|
80
|
+
const stored = JSON.parse(decryptString(this.config.encryptionKey, tokenRow.encryptedTokens));
|
|
81
|
+
const provider = this.buildGmailProvider(accountId, account.email, stored, account.displayName);
|
|
82
|
+
this.providers.set(accountId, { provider, encryptedTokens: tokenRow.encryptedTokens });
|
|
83
|
+
return provider;
|
|
84
|
+
}
|
|
85
|
+
loadTokenRow(accountId) {
|
|
86
|
+
const row = this.db.select().from(oauthTokens).where(eq(oauthTokens.accountId, accountId)).get();
|
|
87
|
+
if (!row)
|
|
88
|
+
throw new EmailError('auth_expired', `No stored credentials for account ${accountId}`);
|
|
89
|
+
return row;
|
|
90
|
+
}
|
|
91
|
+
loadTokens(accountId) {
|
|
92
|
+
const row = this.loadTokenRow(accountId);
|
|
93
|
+
return JSON.parse(decryptString(this.config.encryptionKey, row.encryptedTokens));
|
|
94
|
+
}
|
|
95
|
+
writeTokens(db, accountId, tokens) {
|
|
96
|
+
const encrypted = encryptString(this.config.encryptionKey, JSON.stringify(tokens));
|
|
97
|
+
db
|
|
98
|
+
.insert(oauthTokens)
|
|
99
|
+
.values({ accountId, encryptedTokens: encrypted, updatedAt: Date.now() })
|
|
100
|
+
.onConflictDoUpdate({
|
|
101
|
+
target: oauthTokens.accountId,
|
|
102
|
+
set: { encryptedTokens: encrypted, updatedAt: Date.now() },
|
|
103
|
+
})
|
|
104
|
+
.run();
|
|
105
|
+
return encrypted;
|
|
106
|
+
}
|
|
107
|
+
saveTokens(accountId, tokens) {
|
|
108
|
+
return this.writeTokens(this.db, accountId, tokens);
|
|
109
|
+
}
|
|
110
|
+
buildGmailProvider(accountId, email, stored, displayName) {
|
|
111
|
+
const { clientId, clientSecret } = requireGoogleConfig(this.config);
|
|
112
|
+
const auth = new OAuth2Client({ clientId, clientSecret });
|
|
113
|
+
auth.setCredentials(stored);
|
|
114
|
+
const provider = new GmailProvider({
|
|
115
|
+
accountId,
|
|
116
|
+
email,
|
|
117
|
+
...(displayName ? { displayName } : {}),
|
|
118
|
+
auth,
|
|
119
|
+
});
|
|
120
|
+
// google-auth-library refreshes access tokens transparently; persist them so
|
|
121
|
+
// restarts don't need a refresh round-trip (and rotated refresh tokens survive).
|
|
122
|
+
auth.on('tokens', (fresh) => {
|
|
123
|
+
const encryptedTokens = this.saveTokens(accountId, { ...this.loadTokens(accountId), ...fresh });
|
|
124
|
+
const cached = this.providers.get(accountId);
|
|
125
|
+
if (cached?.provider === provider)
|
|
126
|
+
cached.encryptedTokens = encryptedTokens;
|
|
127
|
+
});
|
|
128
|
+
return provider;
|
|
129
|
+
}
|
|
130
|
+
addGmailAccount(email, tokens, displayName, memberId) {
|
|
131
|
+
// Validate up front for a clean not_found instead of a FK constraint error.
|
|
132
|
+
if (memberId)
|
|
133
|
+
getMember(this.db, memberId);
|
|
134
|
+
const existing = this.db.select().from(accounts).all();
|
|
135
|
+
const duplicate = existing.find((a) => a.provider === 'gmail' && a.email === email);
|
|
136
|
+
if (duplicate) {
|
|
137
|
+
// Re-authenticating an existing account: refresh tokens, clear error
|
|
138
|
+
// state. Ownership is untouched; reassign with assignAccountMember.
|
|
139
|
+
this.db.transaction((tx) => {
|
|
140
|
+
this.writeTokens(tx, duplicate.id, tokens);
|
|
141
|
+
tx.update(accounts)
|
|
142
|
+
.set({ status: 'active', ...(displayName ? { displayName } : {}) })
|
|
143
|
+
.where(eq(accounts.id, duplicate.id))
|
|
144
|
+
.run();
|
|
145
|
+
});
|
|
146
|
+
this.providers.delete(duplicate.id);
|
|
147
|
+
return this.getAccount(duplicate.id);
|
|
148
|
+
}
|
|
149
|
+
const id = `acct_${randomBytes(6).toString('hex')}`;
|
|
150
|
+
this.db.transaction((tx) => {
|
|
151
|
+
const accountCount = tx.select().from(accounts).all().length;
|
|
152
|
+
assertAccountLimit(accountCount, getEntitlements(tx));
|
|
153
|
+
tx.insert(accounts)
|
|
154
|
+
.values({
|
|
155
|
+
id,
|
|
156
|
+
provider: 'gmail',
|
|
157
|
+
email,
|
|
158
|
+
displayName: displayName ?? null,
|
|
159
|
+
status: 'active',
|
|
160
|
+
createdAt: Date.now(),
|
|
161
|
+
memberId: memberId ?? null,
|
|
162
|
+
})
|
|
163
|
+
.run();
|
|
164
|
+
this.writeTokens(tx, id, tokens);
|
|
165
|
+
});
|
|
166
|
+
return this.getAccount(id);
|
|
167
|
+
}
|
|
168
|
+
/** Assign a mailbox to a member, or make it shared again (memberId = null). */
|
|
169
|
+
assignAccountMember(accountId, memberId) {
|
|
170
|
+
this.getAccount(accountId);
|
|
171
|
+
if (memberId)
|
|
172
|
+
getMember(this.db, memberId);
|
|
173
|
+
this.db.update(accounts).set({ memberId }).where(eq(accounts.id, accountId)).run();
|
|
174
|
+
return this.getAccount(accountId);
|
|
175
|
+
}
|
|
176
|
+
removeAccount(accountId) {
|
|
177
|
+
this.getAccount(accountId);
|
|
178
|
+
this.db.delete(accounts).where(eq(accounts.id, accountId)).run();
|
|
179
|
+
this.providers.delete(accountId);
|
|
180
|
+
}
|
|
181
|
+
markStatus(accountId, status) {
|
|
182
|
+
this.db.update(accounts).set({ status }).where(eq(accounts.id, accountId)).run();
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/accounts/registry.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|