nothumanallowed 13.5.114 → 13.5.115
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/package.json +4 -1
- package/src/commands/ui.mjs +403 -0
- package/src/constants.mjs +1 -1
- package/src/services/email-db.mjs +688 -0
- package/src/services/email-imap.mjs +421 -0
- package/src/services/email-smtp.mjs +135 -0
- package/src/services/tool-executor.mjs +95 -1
- package/src/services/web-ui.mjs +701 -83
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nothumanallowed",
|
|
3
|
-
"version": "13.5.
|
|
3
|
+
"version": "13.5.115",
|
|
4
4
|
"description": "NotHumanAllowed — 38 AI agents, 80 tools, Studio (visual agentic workflows). Email, calendar, browser automation, screen capture, canvas, cron/heartbeat, Alexandria E2E messaging, GitHub, Notion, Slack, voice chat, free AI (Liara), 28 languages. Zero-dependency CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -57,6 +57,9 @@
|
|
|
57
57
|
"check-bundle": "node check-bundle.mjs"
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
|
+
"better-sqlite3": "^12.9.0",
|
|
61
|
+
"imapflow": "^1.3.3",
|
|
62
|
+
"mailparser": "^3.9.8",
|
|
60
63
|
"ws": "^8.18.0"
|
|
61
64
|
}
|
|
62
65
|
}
|
package/src/commands/ui.mjs
CHANGED
|
@@ -707,6 +707,409 @@ export async function cmdUI(args) {
|
|
|
707
707
|
return;
|
|
708
708
|
}
|
|
709
709
|
|
|
710
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
711
|
+
// IMAP EMAIL CLIENT ROUTES — READ-ONLY IMAP, local SQLite DB
|
|
712
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
713
|
+
|
|
714
|
+
// GET /api/imap/accounts
|
|
715
|
+
if (method === 'GET' && pathname === '/api/imap/accounts') {
|
|
716
|
+
try {
|
|
717
|
+
const { listAccounts } = await import('../services/email-db.mjs');
|
|
718
|
+
sendJSON(res, 200, { accounts: listAccounts() });
|
|
719
|
+
} catch (e) { sendJSON(res, 500, { error: e.message }); }
|
|
720
|
+
logRequest(method, pathname, 200, Date.now() - start); return;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
// POST /api/imap/accounts — create account
|
|
724
|
+
if (method === 'POST' && pathname === '/api/imap/accounts') {
|
|
725
|
+
try {
|
|
726
|
+
const body = await parseBody(req);
|
|
727
|
+
const { createAccount, listAccounts } = await import('../services/email-db.mjs');
|
|
728
|
+
if (!body.email_address || !body.imap_host || !body.smtp_host || !body.password) {
|
|
729
|
+
sendJSON(res, 400, { error: 'email_address, imap_host, smtp_host, password required' }); return;
|
|
730
|
+
}
|
|
731
|
+
const id = createAccount(body);
|
|
732
|
+
sendJSON(res, 200, { ok: true, id });
|
|
733
|
+
} catch (e) { sendJSON(res, 500, { error: e.message }); }
|
|
734
|
+
logRequest(method, pathname, 200, Date.now() - start); return;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
// POST /api/imap/accounts/update
|
|
738
|
+
if (method === 'POST' && pathname === '/api/imap/accounts/update') {
|
|
739
|
+
try {
|
|
740
|
+
const body = await parseBody(req);
|
|
741
|
+
const { updateAccount } = await import('../services/email-db.mjs');
|
|
742
|
+
if (!body.id) { sendJSON(res, 400, { error: 'id required' }); return; }
|
|
743
|
+
updateAccount(body.id, body);
|
|
744
|
+
sendJSON(res, 200, { ok: true });
|
|
745
|
+
} catch (e) { sendJSON(res, 500, { error: e.message }); }
|
|
746
|
+
logRequest(method, pathname, 200, Date.now() - start); return;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
// POST /api/imap/accounts/delete
|
|
750
|
+
if (method === 'POST' && pathname === '/api/imap/accounts/delete') {
|
|
751
|
+
try {
|
|
752
|
+
const body = await parseBody(req);
|
|
753
|
+
const { deleteAccount } = await import('../services/email-db.mjs');
|
|
754
|
+
if (!body.id) { sendJSON(res, 400, { error: 'id required' }); return; }
|
|
755
|
+
deleteAccount(body.id);
|
|
756
|
+
sendJSON(res, 200, { ok: true });
|
|
757
|
+
} catch (e) { sendJSON(res, 500, { error: e.message }); }
|
|
758
|
+
logRequest(method, pathname, 200, Date.now() - start); return;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
// POST /api/imap/sync — trigger incremental sync for an account
|
|
762
|
+
if (method === 'POST' && pathname === '/api/imap/sync') {
|
|
763
|
+
try {
|
|
764
|
+
const body = await parseBody(req);
|
|
765
|
+
if (!body.accountId) { sendJSON(res, 400, { error: 'accountId required' }); return; }
|
|
766
|
+
const { syncAccount } = await import('../services/email-imap.mjs');
|
|
767
|
+
// Run async — respond immediately
|
|
768
|
+
sendJSON(res, 200, { ok: true, status: 'syncing' });
|
|
769
|
+
syncAccount(body.accountId).catch(e => console.error('[email:sync]', e.message));
|
|
770
|
+
} catch (e) { sendJSON(res, 500, { error: e.message }); }
|
|
771
|
+
logRequest(method, pathname, 200, Date.now() - start); return;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
// GET /api/imap/messages?accountId=&labelId=&limit=&offset=&search=
|
|
775
|
+
if (method === 'GET' && pathname === '/api/imap/messages') {
|
|
776
|
+
try {
|
|
777
|
+
const accountId = url.searchParams.get('accountId');
|
|
778
|
+
const labelId = url.searchParams.get('labelId') || null;
|
|
779
|
+
const limit = parseInt(url.searchParams.get('limit') || '50', 10);
|
|
780
|
+
const offset = parseInt(url.searchParams.get('offset') || '0', 10);
|
|
781
|
+
const search = url.searchParams.get('search') || null;
|
|
782
|
+
if (!accountId) { sendJSON(res, 400, { error: 'accountId required' }); return; }
|
|
783
|
+
const { listMessages } = await import('../services/email-db.mjs');
|
|
784
|
+
const result = listMessages(accountId, labelId, limit, offset, search);
|
|
785
|
+
sendJSON(res, 200, result);
|
|
786
|
+
} catch (e) { sendJSON(res, 500, { error: e.message }); }
|
|
787
|
+
logRequest(method, pathname, 200, Date.now() - start); return;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
// GET /api/imap/message?id=
|
|
791
|
+
if (method === 'GET' && pathname === '/api/imap/message') {
|
|
792
|
+
try {
|
|
793
|
+
const id = url.searchParams.get('id');
|
|
794
|
+
if (!id) { sendJSON(res, 400, { error: 'id required' }); return; }
|
|
795
|
+
const { getMessage, markRead } = await import('../services/email-db.mjs');
|
|
796
|
+
const msg = getMessage(id);
|
|
797
|
+
if (!msg) { sendJSON(res, 404, { error: 'not found' }); return; }
|
|
798
|
+
markRead(id, true);
|
|
799
|
+
sendJSON(res, 200, { message: msg });
|
|
800
|
+
} catch (e) { sendJSON(res, 500, { error: e.message }); }
|
|
801
|
+
logRequest(method, pathname, 200, Date.now() - start); return;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
// GET /api/imap/thread?threadId=&accountId=
|
|
805
|
+
if (method === 'GET' && pathname === '/api/imap/thread') {
|
|
806
|
+
try {
|
|
807
|
+
const threadId = url.searchParams.get('threadId');
|
|
808
|
+
const accountId = url.searchParams.get('accountId');
|
|
809
|
+
if (!threadId || !accountId) { sendJSON(res, 400, { error: 'threadId and accountId required' }); return; }
|
|
810
|
+
const { getThread } = await import('../services/email-db.mjs');
|
|
811
|
+
sendJSON(res, 200, { messages: getThread(threadId, accountId) });
|
|
812
|
+
} catch (e) { sendJSON(res, 500, { error: e.message }); }
|
|
813
|
+
logRequest(method, pathname, 200, Date.now() - start); return;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
// GET /api/imap/labels?accountId=
|
|
817
|
+
if (method === 'GET' && pathname === '/api/imap/labels') {
|
|
818
|
+
try {
|
|
819
|
+
const accountId = url.searchParams.get('accountId');
|
|
820
|
+
if (!accountId) { sendJSON(res, 400, { error: 'accountId required' }); return; }
|
|
821
|
+
const { listLabels } = await import('../services/email-db.mjs');
|
|
822
|
+
sendJSON(res, 200, { labels: listLabels(accountId) });
|
|
823
|
+
} catch (e) { sendJSON(res, 500, { error: e.message }); }
|
|
824
|
+
logRequest(method, pathname, 200, Date.now() - start); return;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
// POST /api/imap/labels/create
|
|
828
|
+
if (method === 'POST' && pathname === '/api/imap/labels/create') {
|
|
829
|
+
try {
|
|
830
|
+
const body = await parseBody(req);
|
|
831
|
+
const { createLabel } = await import('../services/email-db.mjs');
|
|
832
|
+
if (!body.accountId || !body.name) { sendJSON(res, 400, { error: 'accountId, name required' }); return; }
|
|
833
|
+
const id = createLabel(body.accountId, body.name, body.color, body.parentId);
|
|
834
|
+
sendJSON(res, 200, { ok: true, id });
|
|
835
|
+
} catch (e) { sendJSON(res, 500, { error: e.message }); }
|
|
836
|
+
logRequest(method, pathname, 200, Date.now() - start); return;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
// POST /api/imap/labels/update
|
|
840
|
+
if (method === 'POST' && pathname === '/api/imap/labels/update') {
|
|
841
|
+
try {
|
|
842
|
+
const body = await parseBody(req);
|
|
843
|
+
const { updateLabel } = await import('../services/email-db.mjs');
|
|
844
|
+
if (!body.id) { sendJSON(res, 400, { error: 'id required' }); return; }
|
|
845
|
+
updateLabel(body.id, body);
|
|
846
|
+
sendJSON(res, 200, { ok: true });
|
|
847
|
+
} catch (e) { sendJSON(res, 500, { error: e.message }); }
|
|
848
|
+
logRequest(method, pathname, 200, Date.now() - start); return;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
// POST /api/imap/labels/delete
|
|
852
|
+
if (method === 'POST' && pathname === '/api/imap/labels/delete') {
|
|
853
|
+
try {
|
|
854
|
+
const body = await parseBody(req);
|
|
855
|
+
const { deleteLabel } = await import('../services/email-db.mjs');
|
|
856
|
+
if (!body.id) { sendJSON(res, 400, { error: 'id required' }); return; }
|
|
857
|
+
deleteLabel(body.id);
|
|
858
|
+
sendJSON(res, 200, { ok: true });
|
|
859
|
+
} catch (e) { sendJSON(res, 500, { error: e.message }); }
|
|
860
|
+
logRequest(method, pathname, 200, Date.now() - start); return;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
// POST /api/imap/labels/assign — add label to message
|
|
864
|
+
if (method === 'POST' && pathname === '/api/imap/labels/assign') {
|
|
865
|
+
try {
|
|
866
|
+
const body = await parseBody(req);
|
|
867
|
+
const { addMessageToLabel, removeMessageFromLabel } = await import('../services/email-db.mjs');
|
|
868
|
+
if (!body.messageId || !body.labelId) { sendJSON(res, 400, { error: 'messageId, labelId required' }); return; }
|
|
869
|
+
if (body.remove) removeMessageFromLabel(body.messageId, body.labelId);
|
|
870
|
+
else addMessageToLabel(body.messageId, body.labelId);
|
|
871
|
+
sendJSON(res, 200, { ok: true });
|
|
872
|
+
} catch (e) { sendJSON(res, 500, { error: e.message }); }
|
|
873
|
+
logRequest(method, pathname, 200, Date.now() - start); return;
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
// POST /api/imap/mark-read
|
|
877
|
+
if (method === 'POST' && pathname === '/api/imap/mark-read') {
|
|
878
|
+
try {
|
|
879
|
+
const body = await parseBody(req);
|
|
880
|
+
const { markRead } = await import('../services/email-db.mjs');
|
|
881
|
+
if (!body.messageId) { sendJSON(res, 400, { error: 'messageId required' }); return; }
|
|
882
|
+
markRead(body.messageId, body.isRead !== false);
|
|
883
|
+
sendJSON(res, 200, { ok: true });
|
|
884
|
+
} catch (e) { sendJSON(res, 500, { error: e.message }); }
|
|
885
|
+
logRequest(method, pathname, 200, Date.now() - start); return;
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
// POST /api/imap/mark-starred
|
|
889
|
+
if (method === 'POST' && pathname === '/api/imap/mark-starred') {
|
|
890
|
+
try {
|
|
891
|
+
const body = await parseBody(req);
|
|
892
|
+
const { markStarred } = await import('../services/email-db.mjs');
|
|
893
|
+
if (!body.messageId) { sendJSON(res, 400, { error: 'messageId required' }); return; }
|
|
894
|
+
markStarred(body.messageId, body.isStarred !== false);
|
|
895
|
+
sendJSON(res, 200, { ok: true });
|
|
896
|
+
} catch (e) { sendJSON(res, 500, { error: e.message }); }
|
|
897
|
+
logRequest(method, pathname, 200, Date.now() - start); return;
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
// POST /api/imap/mark-all-read
|
|
901
|
+
if (method === 'POST' && pathname === '/api/imap/mark-all-read') {
|
|
902
|
+
try {
|
|
903
|
+
const body = await parseBody(req);
|
|
904
|
+
const { markAllRead } = await import('../services/email-db.mjs');
|
|
905
|
+
if (!body.accountId) { sendJSON(res, 400, { error: 'accountId required' }); return; }
|
|
906
|
+
const count = markAllRead(body.accountId, body.labelId || null);
|
|
907
|
+
sendJSON(res, 200, { ok: true, count });
|
|
908
|
+
} catch (e) { sendJSON(res, 500, { error: e.message }); }
|
|
909
|
+
logRequest(method, pathname, 200, Date.now() - start); return;
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
// POST /api/imap/trash — soft delete (moves to trash label, NEVER touches IMAP)
|
|
913
|
+
if (method === 'POST' && pathname === '/api/imap/trash') {
|
|
914
|
+
try {
|
|
915
|
+
const body = await parseBody(req);
|
|
916
|
+
const { softDelete } = await import('../services/email-db.mjs');
|
|
917
|
+
if (!body.messageId) { sendJSON(res, 400, { error: 'messageId required' }); return; }
|
|
918
|
+
softDelete(body.messageId);
|
|
919
|
+
sendJSON(res, 200, { ok: true });
|
|
920
|
+
} catch (e) { sendJSON(res, 500, { error: e.message }); }
|
|
921
|
+
logRequest(method, pathname, 200, Date.now() - start); return;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
// POST /api/imap/send — send email via SMTP
|
|
925
|
+
if (method === 'POST' && pathname === '/api/imap/send') {
|
|
926
|
+
try {
|
|
927
|
+
const body = await parseBody(req);
|
|
928
|
+
const { sendEmail } = await import('../services/email-smtp.mjs');
|
|
929
|
+
if (!body.accountId || !body.to || !body.subject) {
|
|
930
|
+
sendJSON(res, 400, { error: 'accountId, to, subject required' }); return;
|
|
931
|
+
}
|
|
932
|
+
const result = await sendEmail(body.accountId, body);
|
|
933
|
+
sendJSON(res, 200, { ok: true, messageId: result.messageId });
|
|
934
|
+
} catch (e) { sendJSON(res, 500, { error: e.message }); }
|
|
935
|
+
logRequest(method, pathname, 200, Date.now() - start); return;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
// POST /api/imap/drafts/save
|
|
939
|
+
if (method === 'POST' && pathname === '/api/imap/drafts/save') {
|
|
940
|
+
try {
|
|
941
|
+
const body = await parseBody(req);
|
|
942
|
+
const { saveDraft } = await import('../services/email-db.mjs');
|
|
943
|
+
if (!body.accountId) { sendJSON(res, 400, { error: 'accountId required' }); return; }
|
|
944
|
+
const id = saveDraft(body.accountId, body);
|
|
945
|
+
sendJSON(res, 200, { ok: true, id });
|
|
946
|
+
} catch (e) { sendJSON(res, 500, { error: e.message }); }
|
|
947
|
+
logRequest(method, pathname, 200, Date.now() - start); return;
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
// GET /api/imap/drafts?accountId=
|
|
951
|
+
if (method === 'GET' && pathname === '/api/imap/drafts') {
|
|
952
|
+
try {
|
|
953
|
+
const accountId = url.searchParams.get('accountId');
|
|
954
|
+
if (!accountId) { sendJSON(res, 400, { error: 'accountId required' }); return; }
|
|
955
|
+
const { listDrafts } = await import('../services/email-db.mjs');
|
|
956
|
+
sendJSON(res, 200, { drafts: listDrafts(accountId) });
|
|
957
|
+
} catch (e) { sendJSON(res, 500, { error: e.message }); }
|
|
958
|
+
logRequest(method, pathname, 200, Date.now() - start); return;
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
// POST /api/imap/drafts/delete
|
|
962
|
+
if (method === 'POST' && pathname === '/api/imap/drafts/delete') {
|
|
963
|
+
try {
|
|
964
|
+
const body = await parseBody(req);
|
|
965
|
+
const { deleteDraft } = await import('../services/email-db.mjs');
|
|
966
|
+
if (!body.id) { sendJSON(res, 400, { error: 'id required' }); return; }
|
|
967
|
+
deleteDraft(body.id);
|
|
968
|
+
sendJSON(res, 200, { ok: true });
|
|
969
|
+
} catch (e) { sendJSON(res, 500, { error: e.message }); }
|
|
970
|
+
logRequest(method, pathname, 200, Date.now() - start); return;
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
// GET /api/imap/blocked?accountId=
|
|
974
|
+
if (method === 'GET' && pathname === '/api/imap/blocked') {
|
|
975
|
+
try {
|
|
976
|
+
const accountId = url.searchParams.get('accountId');
|
|
977
|
+
if (!accountId) { sendJSON(res, 400, { error: 'accountId required' }); return; }
|
|
978
|
+
const { listBlockedSenders } = await import('../services/email-db.mjs');
|
|
979
|
+
sendJSON(res, 200, { blocked: listBlockedSenders(accountId) });
|
|
980
|
+
} catch (e) { sendJSON(res, 500, { error: e.message }); }
|
|
981
|
+
logRequest(method, pathname, 200, Date.now() - start); return;
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
// POST /api/imap/blocked/add
|
|
985
|
+
if (method === 'POST' && pathname === '/api/imap/blocked/add') {
|
|
986
|
+
try {
|
|
987
|
+
const body = await parseBody(req);
|
|
988
|
+
const { blockSender } = await import('../services/email-db.mjs');
|
|
989
|
+
if (!body.accountId || !body.email) { sendJSON(res, 400, { error: 'accountId, email required' }); return; }
|
|
990
|
+
blockSender(body.accountId, body.email);
|
|
991
|
+
sendJSON(res, 200, { ok: true });
|
|
992
|
+
} catch (e) { sendJSON(res, 500, { error: e.message }); }
|
|
993
|
+
logRequest(method, pathname, 200, Date.now() - start); return;
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
// POST /api/imap/blocked/remove
|
|
997
|
+
if (method === 'POST' && pathname === '/api/imap/blocked/remove') {
|
|
998
|
+
try {
|
|
999
|
+
const body = await parseBody(req);
|
|
1000
|
+
const { unblockSender } = await import('../services/email-db.mjs');
|
|
1001
|
+
if (!body.id) { sendJSON(res, 400, { error: 'id required' }); return; }
|
|
1002
|
+
unblockSender(body.id);
|
|
1003
|
+
sendJSON(res, 200, { ok: true });
|
|
1004
|
+
} catch (e) { sendJSON(res, 500, { error: e.message }); }
|
|
1005
|
+
logRequest(method, pathname, 200, Date.now() - start); return;
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
// GET /api/imap/rules?accountId=
|
|
1009
|
+
if (method === 'GET' && pathname === '/api/imap/rules') {
|
|
1010
|
+
try {
|
|
1011
|
+
const accountId = url.searchParams.get('accountId');
|
|
1012
|
+
if (!accountId) { sendJSON(res, 400, { error: 'accountId required' }); return; }
|
|
1013
|
+
const { listArchivingRules } = await import('../services/email-db.mjs');
|
|
1014
|
+
sendJSON(res, 200, { rules: listArchivingRules(accountId) });
|
|
1015
|
+
} catch (e) { sendJSON(res, 500, { error: e.message }); }
|
|
1016
|
+
logRequest(method, pathname, 200, Date.now() - start); return;
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
// POST /api/imap/rules/create
|
|
1020
|
+
if (method === 'POST' && pathname === '/api/imap/rules/create') {
|
|
1021
|
+
try {
|
|
1022
|
+
const body = await parseBody(req);
|
|
1023
|
+
const { createArchivingRule } = await import('../services/email-db.mjs');
|
|
1024
|
+
if (!body.accountId || !body.matchType || !body.matchValue) {
|
|
1025
|
+
sendJSON(res, 400, { error: 'accountId, matchType, matchValue required' }); return;
|
|
1026
|
+
}
|
|
1027
|
+
const id = createArchivingRule(body.accountId, body.matchType, body.matchValue, body.targetLabelId);
|
|
1028
|
+
sendJSON(res, 200, { ok: true, id });
|
|
1029
|
+
} catch (e) { sendJSON(res, 500, { error: e.message }); }
|
|
1030
|
+
logRequest(method, pathname, 200, Date.now() - start); return;
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
// POST /api/imap/rules/delete
|
|
1034
|
+
if (method === 'POST' && pathname === '/api/imap/rules/delete') {
|
|
1035
|
+
try {
|
|
1036
|
+
const body = await parseBody(req);
|
|
1037
|
+
const { deleteArchivingRule } = await import('../services/email-db.mjs');
|
|
1038
|
+
if (!body.id) { sendJSON(res, 400, { error: 'id required' }); return; }
|
|
1039
|
+
deleteArchivingRule(body.id);
|
|
1040
|
+
sendJSON(res, 200, { ok: true });
|
|
1041
|
+
} catch (e) { sendJSON(res, 500, { error: e.message }); }
|
|
1042
|
+
logRequest(method, pathname, 200, Date.now() - start); return;
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
// GET /api/imap/signatures?accountId=
|
|
1046
|
+
if (method === 'GET' && pathname === '/api/imap/signatures') {
|
|
1047
|
+
try {
|
|
1048
|
+
const accountId = url.searchParams.get('accountId');
|
|
1049
|
+
if (!accountId) { sendJSON(res, 400, { error: 'accountId required' }); return; }
|
|
1050
|
+
const { listSignatures } = await import('../services/email-db.mjs');
|
|
1051
|
+
sendJSON(res, 200, { signatures: listSignatures(accountId) });
|
|
1052
|
+
} catch (e) { sendJSON(res, 500, { error: e.message }); }
|
|
1053
|
+
logRequest(method, pathname, 200, Date.now() - start); return;
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
// POST /api/imap/signatures/create
|
|
1057
|
+
if (method === 'POST' && pathname === '/api/imap/signatures/create') {
|
|
1058
|
+
try {
|
|
1059
|
+
const body = await parseBody(req);
|
|
1060
|
+
const { createSignature } = await import('../services/email-db.mjs');
|
|
1061
|
+
if (!body.accountId || !body.name || !body.content) {
|
|
1062
|
+
sendJSON(res, 400, { error: 'accountId, name, content required' }); return;
|
|
1063
|
+
}
|
|
1064
|
+
const id = createSignature(body.accountId, body.name, body.content, body.isDefault || false);
|
|
1065
|
+
sendJSON(res, 200, { ok: true, id });
|
|
1066
|
+
} catch (e) { sendJSON(res, 500, { error: e.message }); }
|
|
1067
|
+
logRequest(method, pathname, 200, Date.now() - start); return;
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
// POST /api/imap/signatures/delete
|
|
1071
|
+
if (method === 'POST' && pathname === '/api/imap/signatures/delete') {
|
|
1072
|
+
try {
|
|
1073
|
+
const body = await parseBody(req);
|
|
1074
|
+
const { deleteSignature } = await import('../services/email-db.mjs');
|
|
1075
|
+
if (!body.id) { sendJSON(res, 400, { error: 'id required' }); return; }
|
|
1076
|
+
deleteSignature(body.id);
|
|
1077
|
+
sendJSON(res, 200, { ok: true });
|
|
1078
|
+
} catch (e) { sendJSON(res, 500, { error: e.message }); }
|
|
1079
|
+
logRequest(method, pathname, 200, Date.now() - start); return;
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
// GET /api/imap/attachment?messageId=&partId=&accountId=
|
|
1083
|
+
if (method === 'GET' && pathname === '/api/imap/attachment') {
|
|
1084
|
+
try {
|
|
1085
|
+
const messageId = url.searchParams.get('messageId');
|
|
1086
|
+
const partId = url.searchParams.get('partId');
|
|
1087
|
+
const accountId = url.searchParams.get('accountId');
|
|
1088
|
+
if (!messageId || !accountId) { sendJSON(res, 400, { error: 'messageId, accountId required' }); return; }
|
|
1089
|
+
const { getMessage } = await import('../services/email-db.mjs');
|
|
1090
|
+
const { fetchAttachmentContent } = await import('../services/email-imap.mjs');
|
|
1091
|
+
const msg = getMessage(messageId);
|
|
1092
|
+
if (!msg) { sendJSON(res, 404, { error: 'message not found' }); return; }
|
|
1093
|
+
const att = msg.attachments?.find(a => a.part_id === partId);
|
|
1094
|
+
if (!att) { sendJSON(res, 404, { error: 'attachment not found' }); return; }
|
|
1095
|
+
// Check local cache first
|
|
1096
|
+
const db = (await import('../services/email-db.mjs')).getDb();
|
|
1097
|
+
const cached = db.prepare('SELECT content_blob, content_type FROM email_attachments WHERE id = ?').get(att.id);
|
|
1098
|
+
if (cached?.content_blob) {
|
|
1099
|
+
res.writeHead(200, { 'Content-Type': cached.content_type || 'application/octet-stream', 'Content-Disposition': `attachment; filename="${att.filename || 'attachment'}"` });
|
|
1100
|
+
res.end(cached.content_blob);
|
|
1101
|
+
} else {
|
|
1102
|
+
const result = await fetchAttachmentContent(accountId, msg.imap_folder_path, msg.uid, partId);
|
|
1103
|
+
if (!result) { sendJSON(res, 404, { error: 'could not fetch attachment' }); return; }
|
|
1104
|
+
res.writeHead(200, { 'Content-Type': result.contentType || att.content_type || 'application/octet-stream', 'Content-Disposition': `attachment; filename="${att.filename || 'attachment'}"` });
|
|
1105
|
+
res.end(result.buffer);
|
|
1106
|
+
}
|
|
1107
|
+
} catch (e) { sendJSON(res, 500, { error: e.message }); }
|
|
1108
|
+
logRequest(method, pathname, 200, Date.now() - start); return;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
1112
|
+
|
|
710
1113
|
// POST /api/contacts — create contact
|
|
711
1114
|
if (method === 'POST' && pathname === '/api/contacts') {
|
|
712
1115
|
try {
|
package/src/constants.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import { fileURLToPath } from 'url';
|
|
|
5
5
|
const __filename = fileURLToPath(import.meta.url);
|
|
6
6
|
const __dirname = path.dirname(__filename);
|
|
7
7
|
|
|
8
|
-
export const VERSION = '13.5.
|
|
8
|
+
export const VERSION = '13.5.115';
|
|
9
9
|
export const BASE_URL = 'https://nothumanallowed.com/cli';
|
|
10
10
|
export const API_BASE = 'https://nothumanallowed.com/api/v1';
|
|
11
11
|
|