@bobfrankston/mailx 1.0.93 → 1.0.95

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/client/app.js CHANGED
@@ -562,41 +562,70 @@ onWsEvent((event) => {
562
562
  showAlert(event.message, "ws-error");
563
563
  break;
564
564
  case "accountError": {
565
- // Show in alert banner with re-auth button
566
- const msg = `${event.accountId}: ${event.hint}`;
565
+ // Show actual error + hint in banner
566
+ const msg = `${event.accountId}: ${event.error}`;
567
567
  showAlert(msg, `acct-${event.accountId}`);
568
- // Add re-auth button to the banner
568
+ // Add action button: Re-authenticate for OAuth, Retry for password accounts
569
569
  const bannerText = document.getElementById("alert-text");
570
570
  if (bannerText && bannerText.textContent === msg) {
571
571
  const existing = bannerText.parentElement?.querySelector(".status-action");
572
572
  if (!existing) {
573
573
  const btn = document.createElement("button");
574
- btn.textContent = "Re-authenticate";
575
574
  btn.className = "status-action";
576
- btn.addEventListener("click", async () => {
577
- btn.disabled = true;
578
- btn.textContent = "Authenticating...";
579
- try {
580
- const res = await fetch(`/api/reauth/${event.accountId}`, { method: "POST" });
581
- const data = await res.json();
582
- if (data.ok) {
583
- hideAlert();
584
- const acctEl = document.getElementById("status-accounts");
585
- if (acctEl) {
586
- acctEl.textContent = `${event.accountId}: reconnected`;
587
- acctEl.style.color = "";
575
+ if (event.isOAuth) {
576
+ btn.textContent = "Re-authenticate";
577
+ btn.addEventListener("click", async () => {
578
+ btn.disabled = true;
579
+ btn.textContent = "Authenticating...";
580
+ try {
581
+ const res = await fetch(`/api/reauth/${event.accountId}`, { method: "POST" });
582
+ const data = await res.json();
583
+ if (data.ok) {
584
+ hideAlert();
585
+ const acctEl = document.getElementById("status-accounts");
586
+ if (acctEl) {
587
+ acctEl.textContent = `${event.accountId}: reconnected`;
588
+ acctEl.style.color = "";
589
+ }
590
+ }
591
+ else {
592
+ btn.textContent = "Re-authenticate";
593
+ btn.disabled = false;
588
594
  }
589
595
  }
590
- else {
596
+ catch {
591
597
  btn.textContent = "Re-authenticate";
592
598
  btn.disabled = false;
593
599
  }
594
- }
595
- catch {
596
- btn.textContent = "Re-authenticate";
597
- btn.disabled = false;
598
- }
599
- });
600
+ });
601
+ }
602
+ else {
603
+ btn.textContent = "Retry";
604
+ btn.addEventListener("click", async () => {
605
+ btn.disabled = true;
606
+ btn.textContent = "Syncing...";
607
+ try {
608
+ const res = await fetch(`/api/sync/${event.accountId}`, { method: "POST" });
609
+ const data = await res.json();
610
+ if (data.ok) {
611
+ hideAlert();
612
+ const acctEl = document.getElementById("status-accounts");
613
+ if (acctEl) {
614
+ acctEl.textContent = `${event.accountId}: reconnected`;
615
+ acctEl.style.color = "";
616
+ }
617
+ }
618
+ else {
619
+ btn.textContent = "Retry";
620
+ btn.disabled = false;
621
+ }
622
+ }
623
+ catch {
624
+ btn.textContent = "Retry";
625
+ btn.disabled = false;
626
+ }
627
+ });
628
+ }
600
629
  bannerText.parentElement?.insertBefore(btn, document.getElementById("alert-dismiss"));
601
630
  }
602
631
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/mailx-client",
3
- "version": "1.0.8",
3
+ "version": "1.0.9",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "dependencies": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/mailx",
3
- "version": "1.0.93",
3
+ "version": "1.0.95",
4
4
  "description": "Local-first email client with IMAP sync and standalone native app",
5
5
  "type": "module",
6
6
  "main": "bin/mailx.js",
@@ -17,6 +17,7 @@ export interface ImapManagerEvents {
17
17
  total: number;
18
18
  unread: number;
19
19
  }>) => void;
20
+ accountError: (accountId: string, error: string, hint: string, isOAuth: boolean) => void;
20
21
  }
21
22
  export declare class ImapManager extends EventEmitter {
22
23
  private configs;
@@ -210,7 +210,7 @@ export class ImapManager extends EventEmitter {
210
210
  console.error(` [auth] ${account.id}: ${imapError(e)}`);
211
211
  if (!this.accountErrorShown.has(account.id)) {
212
212
  this.accountErrorShown.add(account.id);
213
- this.emit("accountError", account.id, imapError(e), "Re-authenticate: click the button below or run mailx -setup");
213
+ this.emit("accountError", account.id, imapError(e), "Authentication may have expired", true);
214
214
  }
215
215
  }
216
216
  }
@@ -480,7 +480,7 @@ export class ImapManager extends EventEmitter {
480
480
  const hint = errMsg.includes("max_userip_connections") || errMsg.includes("Too many")
481
481
  ? "Too many connections — backing off"
482
482
  : isOAuth ? "Authentication may have expired" : "Check server connectivity";
483
- this.emit("accountError", accountId, errMsg, hint);
483
+ this.emit("accountError", accountId, errMsg, hint, isOAuth);
484
484
  }
485
485
  }
486
486
  finally {
@@ -538,6 +538,7 @@ export class ImapManager extends EventEmitter {
538
538
  }
539
539
  }
540
540
  }
541
+ this.accountErrorShown.delete(accountId);
541
542
  this.emit("syncComplete", accountId);
542
543
  }
543
544
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/mailx-imap",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "type": "module",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -122,6 +122,11 @@ ${accountInfo.map((a) => `<tr><td>${a.name}</td><td>${a.folders}</td><td>${a.inb
122
122
  <p style="margin-top:2rem;font-size:0.8rem"><a href="/">Open mailx</a> | Auto-refreshes every 10s</p>
123
123
  </body></html>`);
124
124
  });
125
+ // Graceful exit — close IMAP, DB, HTTP then exit
126
+ app.all("/api/exit", (req, res) => {
127
+ res.json({ ok: true });
128
+ setTimeout(() => shutdown(), 100);
129
+ });
125
130
  // Restart server + reload clients
126
131
  app.post("/api/restart", (req, res) => {
127
132
  res.json({ ok: true });
@@ -203,8 +208,8 @@ imapManager.on("folderCountsChanged", (accountId, counts) => {
203
208
  imapManager.on("syncError", (accountId, error) => {
204
209
  broadcast({ type: "error", message: `${accountId}: ${error}` });
205
210
  });
206
- imapManager.on("accountError", (accountId, error, hint) => {
207
- broadcast({ type: "accountError", accountId, error, hint });
211
+ imapManager.on("accountError", (accountId, error, hint, isOAuth) => {
212
+ broadcast({ type: "accountError", accountId, error, hint, isOAuth });
208
213
  });
209
214
  // ── Startup ──
210
215
  async function start() {
@@ -286,7 +291,12 @@ process.on("unhandledRejection", (err) => {
286
291
  process.on("uncaughtException", (err) => {
287
292
  console.error("FATAL uncaught exception:", err.message);
288
293
  console.error(err.stack);
289
- // Don't exitlet node --watch handle restart
294
+ // EADDRINUSE = another instance holds the port exit so node --watch can retry
295
+ if (err.code === "EADDRINUSE") {
296
+ console.error("Port in use — exiting so node --watch can retry");
297
+ process.exit(1);
298
+ }
299
+ // Other exceptions: stay alive, let node --watch handle file-change restarts
290
300
  });
291
301
  process.on("exit", (code) => {
292
302
  console.log(`Process exiting with code ${code}`);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/mailx-server",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "type": "module",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -177,6 +177,7 @@ export type WsEvent = {
177
177
  accountId: string;
178
178
  error: string;
179
179
  hint: string;
180
+ isOAuth: boolean;
180
181
  };
181
182
  export interface MailxSettings {
182
183
  accounts: AccountConfig[];
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/mailx-types",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "type": "module",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
package/showports.cmd ADDED
@@ -0,0 +1 @@
1
+ netstat -ano | findstr :9333