@bobfrankston/mailx 1.0.293 → 1.0.295
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.
|
@@ -220,7 +220,7 @@ export async function loadSearchResults(query, scope = "all", accountId = "", fo
|
|
|
220
220
|
}
|
|
221
221
|
const source = scope === "current" && accountId
|
|
222
222
|
? await apiGetMessages(accountId, folderId, 1, 10000)
|
|
223
|
-
: await
|
|
223
|
+
: await apiGetUnifiedInbox(1, 10000);
|
|
224
224
|
const matches = source.items.filter((m) => regex.test(m.subject || "") || regex.test(m.from?.name || "") || regex.test(m.from?.address || "") || regex.test(m.preview || ""));
|
|
225
225
|
totalMessages = matches.length;
|
|
226
226
|
state.setMessages(matches);
|
package/package.json
CHANGED
|
@@ -47,9 +47,13 @@ function findGoogleCredentials() {
|
|
|
47
47
|
return null;
|
|
48
48
|
}
|
|
49
49
|
const GDRIVE_TOKEN_DIR = path.join(SETTINGS_DIR, "tokens", "gdrive");
|
|
50
|
-
// drive
|
|
51
|
-
//
|
|
52
|
-
|
|
50
|
+
// drive: full access so we can find pre-existing folders like "home/.mailx"
|
|
51
|
+
// that weren't created by this OAuth client. drive.file was safer but couldn't
|
|
52
|
+
// discover folders created manually or by other tools — which broke first-run
|
|
53
|
+
// setup when the user's convention is a nested path.
|
|
54
|
+
// The Gmail scope is already mail.google.com (full email), so this isn't a
|
|
55
|
+
// bigger privacy ask. Token cache at tokens/gdrive/ will re-auth on scope change.
|
|
56
|
+
const GDRIVE_SCOPES = "https://www.googleapis.com/auth/drive";
|
|
53
57
|
/** Paths to try when no config.path is set (fresh install). Order matters:
|
|
54
58
|
* "home/.mailx" is the user convention for shared family settings; "mailx"
|
|
55
59
|
* is the auto-created default. First one that exists on GDrive wins. */
|
|
@@ -107,11 +111,13 @@ async function getGoogleDriveToken() {
|
|
|
107
111
|
* missing. When `create` is true, creates the LAST segment if missing (won't
|
|
108
112
|
* create intermediate segments — that's a user error). */
|
|
109
113
|
async function walkGDrivePath(token, segments, create) {
|
|
114
|
+
const fullPath = segments.join("/");
|
|
110
115
|
let parentId;
|
|
111
116
|
for (let i = 0; i < segments.length; i++) {
|
|
112
117
|
const seg = segments[i];
|
|
113
118
|
const found = await gDriveFindFolder(token, seg, parentId);
|
|
114
119
|
if (found) {
|
|
120
|
+
console.log(` [cloud] walk '${fullPath}': '${seg}' → ${found}${parentId ? ` (in ${parentId})` : ""}`);
|
|
115
121
|
parentId = found;
|
|
116
122
|
}
|
|
117
123
|
else if (create && i === segments.length - 1) {
|
|
@@ -134,6 +140,21 @@ async function walkGDrivePath(token, segments, create) {
|
|
|
134
140
|
}
|
|
135
141
|
return parentId || null;
|
|
136
142
|
}
|
|
143
|
+
/** Resolve a folder ID back to its name (for diagnostic logging). */
|
|
144
|
+
async function gDriveGetFolderName(token, folderId) {
|
|
145
|
+
try {
|
|
146
|
+
const res = await fetch(`https://www.googleapis.com/drive/v3/files/${folderId}?fields=name,parents`, {
|
|
147
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
148
|
+
});
|
|
149
|
+
if (!res.ok)
|
|
150
|
+
return null;
|
|
151
|
+
const data = await res.json();
|
|
152
|
+
return data.name || null;
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
137
158
|
/** Find a single folder by name, optionally inside a parent. */
|
|
138
159
|
async function gDriveFindFolder(token, name, parentId) {
|
|
139
160
|
let query = `name='${name}' and mimeType='application/vnd.google-apps.folder' and trashed=false`;
|
|
@@ -174,14 +195,18 @@ export async function gDriveFindOrCreateFolder() {
|
|
|
174
195
|
: GDRIVE_PATH_SEARCH_ORDER;
|
|
175
196
|
try {
|
|
176
197
|
for (const tryPath of pathsToTry) {
|
|
198
|
+
console.log(` [cloud] Trying GDrive path: '${tryPath}'`);
|
|
177
199
|
const segments = tryPath.split(/[/\\]/).filter(Boolean);
|
|
178
200
|
const folderId = await walkGDrivePath(token, segments, false);
|
|
179
201
|
if (folderId) {
|
|
180
|
-
|
|
202
|
+
// Resolve folder ID back to name for verification
|
|
203
|
+
const name = await gDriveGetFolderName(token, folderId);
|
|
204
|
+
console.log(` [cloud] Found existing '${tryPath}' → folder '${name || "?"}' (${folderId})`);
|
|
181
205
|
return folderId;
|
|
182
206
|
}
|
|
207
|
+
console.log(` [cloud] Path '${tryPath}' not found on GDrive`);
|
|
183
208
|
}
|
|
184
|
-
// None found — create the
|
|
209
|
+
// None found — create the last (simplest) path
|
|
185
210
|
const createPath = configuredPath || GDRIVE_PATH_SEARCH_ORDER[GDRIVE_PATH_SEARCH_ORDER.length - 1];
|
|
186
211
|
const segments = createPath.split(/[/\\]/).filter(Boolean);
|
|
187
212
|
const created = await walkGDrivePath(token, segments, true);
|
|
@@ -570,8 +570,17 @@ export class MailxDB {
|
|
|
570
570
|
ftsQuery += `subject:${term} `;
|
|
571
571
|
}
|
|
572
572
|
else {
|
|
573
|
-
// Unqualified — search everything
|
|
574
|
-
|
|
573
|
+
// Unqualified — search everything.
|
|
574
|
+
// Support "term1|term2" (regex OR) by converting to FTS5 OR.
|
|
575
|
+
// Strip surrounding /.../ if user typed regex delimiters.
|
|
576
|
+
let term = part.replace(/^\/|\/$/g, "");
|
|
577
|
+
if (term.includes("|")) {
|
|
578
|
+
const alts = term.split("|").filter(Boolean).map(t => `${t}*`).join(" OR ");
|
|
579
|
+
ftsQuery += `(${alts}) `;
|
|
580
|
+
}
|
|
581
|
+
else {
|
|
582
|
+
ftsQuery += `${term}* `;
|
|
583
|
+
}
|
|
575
584
|
}
|
|
576
585
|
}
|
|
577
586
|
ftsQuery = ftsQuery.trim();
|