Use new message API for inbound mails (#8445)

This commit is contained in:
Chunosov
2025-04-03 08:01:17 +07:00
committed by GitHub
parent 7cdeb49651
commit 0085c5aa42
7 changed files with 687 additions and 126 deletions
+389 -7
View File
@@ -762,7 +762,7 @@ importers:
version: file:projects/pod-gmail.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(encoding@0.1.13)(gcp-metadata@5.3.0(encoding@0.1.13))(snappy@7.2.2)(socks@2.8.3)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3))
'@rush-temp/pod-inbound-mail':
specifier: file:./projects/pod-inbound-mail.tgz
version: file:projects/pod-inbound-mail.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))
version: file:projects/pod-inbound-mail.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(bufferutil@4.0.8)(utf-8-validate@6.0.4)
'@rush-temp/pod-love':
specifier: file:./projects/pod-love.tgz
version: file:projects/pod-love.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(bufferutil@4.0.8)(utf-8-validate@6.0.4)
@@ -1576,6 +1576,9 @@ importers:
'@types/toposort':
specifier: ^2.0.3
version: 2.0.7
'@types/turndown':
specifier: ^5.0.5
version: 5.0.5
'@types/uuid':
specifier: ^8.3.1
version: 8.3.4
@@ -1840,6 +1843,9 @@ importers:
intl-messageformat:
specifier: ^10.7.14
version: 10.7.14
isomorphic-dompurify:
specifier: ^2.22.0
version: 2.22.0(bufferutil@4.0.8)(utf-8-validate@6.0.4)
jest:
specifier: ^29.7.0
version: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3))
@@ -2101,6 +2107,9 @@ importers:
ts-standard:
specifier: ^12.0.2
version: 12.0.2(typescript@5.3.3)
turndown:
specifier: ^7.2.0
version: 7.2.0
tus-js-client:
specifier: ^4.3.1
version: 4.3.1
@@ -2184,6 +2193,9 @@ packages:
'@anticrm/skillset@0.6.0':
resolution: {integrity: sha512-k5/57q9ie73oCFKAxZrKy/BSnakiaVZsJmfjzmqhoko+3dZq2M2o/h1r+RSZrQ6re51iMqFdsVZAtfdPL15hjA==}
'@asamuzakjp/css-color@3.1.1':
resolution: {integrity: sha512-hpRD68SV2OMcZCsrbdkccTw5FXjNDLo5OuqSHyHZfwweGsDWZwDJ2+gONyNAbazZclobMirACLw0lk8WVxIqxA==}
'@aws-crypto/crc32@5.2.0':
resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==}
engines: {node: '>=16.0.0'}
@@ -2556,6 +2568,34 @@ packages:
resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
engines: {node: '>=12'}
'@csstools/color-helpers@5.0.2':
resolution: {integrity: sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==}
engines: {node: '>=18'}
'@csstools/css-calc@2.1.2':
resolution: {integrity: sha512-TklMyb3uBB28b5uQdxjReG4L80NxAqgrECqLZFQbyLekwwlcDDS8r3f07DKqeo8C4926Br0gf/ZDe17Zv4wIuw==}
engines: {node: '>=18'}
peerDependencies:
'@csstools/css-parser-algorithms': ^3.0.4
'@csstools/css-tokenizer': ^3.0.3
'@csstools/css-color-parser@3.0.8':
resolution: {integrity: sha512-pdwotQjCCnRPuNi06jFuP68cykU1f3ZWExLe/8MQ1LOs8Xq+fTkYgd+2V8mWUWMrOn9iS2HftPVaMZDaXzGbhQ==}
engines: {node: '>=18'}
peerDependencies:
'@csstools/css-parser-algorithms': ^3.0.4
'@csstools/css-tokenizer': ^3.0.3
'@csstools/css-parser-algorithms@3.0.4':
resolution: {integrity: sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==}
engines: {node: '>=18'}
peerDependencies:
'@csstools/css-tokenizer': ^3.0.3
'@csstools/css-tokenizer@3.0.3':
resolution: {integrity: sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==}
engines: {node: '>=18'}
'@dabh/diagnostics@2.0.3':
resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==}
@@ -3356,6 +3396,9 @@ packages:
'@mermaid-js/parser@0.3.0':
resolution: {integrity: sha512-HsvL6zgE5sUPGgkIDlmAWR1HTNHz2Iy11BAWPTa4Jjabkpguy4Ze2gzfLrg6pdRuBvFwgUYyxiaNqZwrEEXepA==}
'@mixmark-io/domino@2.2.0':
resolution: {integrity: sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw==}
'@mongodb-js/saslprep@1.1.9':
resolution: {integrity: sha512-tVkljjeEaAhCqTzajSdgbQ6gE6f3oneVwa3iXR6csiEwXXOFsiC6Uh9iAjAhXPtqa/XMDHWjjeNH/77m/Yq2dw==}
@@ -4039,7 +4082,7 @@ packages:
version: 0.0.0
'@rush-temp/contact@file:projects/contact.tgz':
resolution: {integrity: sha512-79dZ9DTczWnB5eBmX1XH8d/mw8RjbGeCK+ryNpym3Bh3kykq7RwuTeTRvIlPTzk+vkCCXzb34A/zSdaslTyfnw==, tarball: file:projects/contact.tgz}
resolution: {integrity: sha512-mbni7SPWQvWylU2uoM88r+eOGhktRpU70B26/2smq4B8TMd3GnHKDnjzKmVE7Ck6kh43z/9B9f2t203UAR0Tpg==, tarball: file:projects/contact.tgz}
version: 0.0.0
'@rush-temp/controlled-documents-assets@file:projects/controlled-documents-assets.tgz':
@@ -4351,7 +4394,7 @@ packages:
version: 0.0.0
'@rush-temp/model-contact@file:projects/model-contact.tgz':
resolution: {integrity: sha512-xIr8DSFUT2GSZ/4VvDc1zrlab+WhjCCM2tN/TNBEGroVUL2QW0/3VqODWs0UorvniaVWVoQNAj13cPZpE7yEfw==, tarball: file:projects/model-contact.tgz}
resolution: {integrity: sha512-R5QhJlkccqefuHjHaIg/POYsrtnWCYDPE6MwNTQ+1F/CxGYuCJ2kDfEZMlKZiLitiruAkKbvq8xBo/nl2/PNfw==, tarball: file:projects/model-contact.tgz}
version: 0.0.0
'@rush-temp/model-controlled-documents@file:projects/model-controlled-documents.tgz':
@@ -4759,7 +4802,7 @@ packages:
version: 0.0.0
'@rush-temp/pod-inbound-mail@file:projects/pod-inbound-mail.tgz':
resolution: {integrity: sha512-aRbv7vSb4jFYBQc+EroIkXWtJK21XAmGCYeMdCuhyKU862V5Ik3Dag68jW7pNLRupf4dw9gzJ2/0LlSr89FvgQ==, tarball: file:projects/pod-inbound-mail.tgz}
resolution: {integrity: sha512-AUKPM2FBTj0I2baAcnQVRI8vDJRdKEfPGwGLsxJSfvMyx2KhtZQi2if6h7mwS3lN+hFBHEqjRsj4VG9Ag8x7tg==, tarball: file:projects/pod-inbound-mail.tgz}
version: 0.0.0
'@rush-temp/pod-love@file:projects/pod-love.tgz':
@@ -4847,7 +4890,7 @@ packages:
version: 0.0.0
'@rush-temp/process-resources@file:projects/process-resources.tgz':
resolution: {integrity: sha512-ZUhW+nxxAlPdXaL99Tdd5iKav4paqRvB1t4zOs5Pd4ez2K9EVIe0/OFbZAn1ic0MClrp5TxmLSPbu//YrFT3VQ==, tarball: file:projects/process-resources.tgz}
resolution: {integrity: sha512-A97floT9oEaheLWcX3NSIqoX0b8ZpGmGJpvsGxZ4aMLP3tbVyVD9x8wvWywv8O5LlLHe6qVMI9U75OB4EOC2+g==, tarball: file:projects/process-resources.tgz}
version: 0.0.0
'@rush-temp/process@file:projects/process.tgz':
@@ -5027,7 +5070,7 @@ packages:
version: 0.0.0
'@rush-temp/server-contact-resources@file:projects/server-contact-resources.tgz':
resolution: {integrity: sha512-6R2S1cbwLtDKCesTPG9tJVy7mJB6EJogkf8TDAv9RFPRqxBkzdQh6OlgkLQsRMgpuneoJjwmSxXFHhX3btVnug==, tarball: file:projects/server-contact-resources.tgz}
resolution: {integrity: sha512-NrPPkOfgwC2q7Dw5MKB+lQwUAWpsI/F/C0n8KHOlPT6ZASxXkR0TPNxtul1oQl2EnxCN+Z6NmUAi+2rbHOOtXQ==, tarball: file:projects/server-contact-resources.tgz}
version: 0.0.0
'@rush-temp/server-contact@file:projects/server-contact.tgz':
@@ -6526,6 +6569,9 @@ packages:
'@types/trusted-types@2.0.7':
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
'@types/turndown@5.0.5':
resolution: {integrity: sha512-TL2IgGgc7B5j78rIccBtlYAnkuv8nUQqhQc+DSYV5j9Be9XOcm/SKOVRuA47xAVI3680Tk9B1d8flK2GWT2+4w==}
'@types/unist@2.0.10':
resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==}
@@ -6830,6 +6876,10 @@ packages:
resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==}
engines: {node: '>= 14'}
agent-base@7.1.3:
resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==}
engines: {node: '>= 14'}
agentkeepalive@4.5.0:
resolution: {integrity: sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==}
engines: {node: '>= 8.0.0'}
@@ -7326,6 +7376,10 @@ packages:
resolution: {integrity: sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==}
engines: {node: '>=8'}
call-bind-apply-helpers@1.0.2:
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
engines: {node: '>= 0.4'}
call-bind@1.0.7:
resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==}
engines: {node: '>= 0.4'}
@@ -7771,6 +7825,10 @@ packages:
resolution: {integrity: sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==}
engines: {node: '>=8'}
cssstyle@4.3.0:
resolution: {integrity: sha512-6r0NiY0xizYqfBvWp1G7WXJ06/bZyrk7Dc6PHql82C/pKGUTKu4yAX4Y8JPamb1ob9nBKuxWzCGTRuGwU3yxJQ==}
engines: {node: '>=18'}
csv-parse@5.1.0:
resolution: {integrity: sha512-JL+Q6YEikT2uoe57InjFFa6VejhSv0tDwOxeQ1bVQKeUC/NCnLAAZ8n3PzowPQQLuZ37fysDYZipB2UJkH9C6A==}
@@ -7956,6 +8014,10 @@ packages:
resolution: {integrity: sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==}
engines: {node: '>=12'}
data-urls@5.0.0:
resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==}
engines: {node: '>=18'}
data-view-buffer@1.0.1:
resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==}
engines: {node: '>= 0.4'}
@@ -8249,6 +8311,9 @@ packages:
dompurify@3.2.3:
resolution: {integrity: sha512-U1U5Hzc2MO0oW3DF+G9qYN0aT7atAou4AgI0XjWz061nyBPbdxkfdhfy5uMgGn6+oLFCfn44ZGbdDqCzVmlOWA==}
dompurify@3.2.4:
resolution: {integrity: sha512-ysFSFEDVduQpyhzAob/kkuJjf5zWkZD8/A9ywSp1byueyuCfHamrCBa14/Oc2iiB0e51B+NpxSl5gmzn+Ms/mg==}
domutils@1.5.1:
resolution: {integrity: sha512-gSu5Oi/I+3wDENBsOWBiRK1eoGxcywYSqg3rR960/+EfY0CF4EX1VPkgHOZ3WiS/Jg2DtliF6BhWcHlfpYUcGw==}
@@ -8296,6 +8361,10 @@ packages:
duck@0.1.12:
resolution: {integrity: sha512-wkctla1O6VfP89gQ+J/yDesM0S7B7XLXjKGzXxMDVFg7uEn706niAtyYovKbyq1oT9YwDcly721/iUWoc8MVRg==}
dunder-proto@1.0.1:
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
engines: {node: '>= 0.4'}
duplexer@0.1.2:
resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==}
@@ -8469,6 +8538,10 @@ packages:
resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==}
engines: {node: '>= 0.4'}
es-define-property@1.0.1:
resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
engines: {node: '>= 0.4'}
es-errors@1.3.0:
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
engines: {node: '>= 0.4'}
@@ -8484,10 +8557,18 @@ packages:
resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==}
engines: {node: '>= 0.4'}
es-object-atoms@1.1.1:
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
engines: {node: '>= 0.4'}
es-set-tostringtag@2.0.3:
resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==}
engines: {node: '>= 0.4'}
es-set-tostringtag@2.1.0:
resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
engines: {node: '>= 0.4'}
es-shim-unscopables@1.0.2:
resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==}
@@ -9039,6 +9120,10 @@ packages:
resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
engines: {node: '>= 6'}
form-data@4.0.2:
resolution: {integrity: sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==}
engines: {node: '>= 6'}
formdata-node@4.4.1:
resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==}
engines: {node: '>= 12.20'}
@@ -9144,10 +9229,18 @@ packages:
resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==}
engines: {node: '>= 0.4'}
get-intrinsic@1.3.0:
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
engines: {node: '>= 0.4'}
get-package-type@0.1.0:
resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==}
engines: {node: '>=8.0.0'}
get-proto@1.0.1:
resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
engines: {node: '>= 0.4'}
get-stdin@8.0.0:
resolution: {integrity: sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==}
engines: {node: '>=10'}
@@ -9255,6 +9348,10 @@ packages:
gopd@1.0.1:
resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
gopd@1.2.0:
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
engines: {node: '>= 0.4'}
got@11.8.6:
resolution: {integrity: sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==}
engines: {node: '>=10.19.0'}
@@ -9330,6 +9427,10 @@ packages:
resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
engines: {node: '>= 0.4'}
has-symbols@1.1.0:
resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
engines: {node: '>= 0.4'}
has-tostringtag@1.0.2:
resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
engines: {node: '>= 0.4'}
@@ -9382,6 +9483,10 @@ packages:
resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==}
engines: {node: '>=12'}
html-encoding-sniffer@4.0.0:
resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==}
engines: {node: '>=18'}
html-entities@2.4.0:
resolution: {integrity: sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ==}
@@ -9487,6 +9592,10 @@ packages:
resolution: {integrity: sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==}
engines: {node: '>= 14'}
https-proxy-agent@7.0.6:
resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
engines: {node: '>= 14'}
human-signals@2.1.0:
resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
engines: {node: '>=10.17.0'}
@@ -9845,6 +9954,10 @@ packages:
resolution: {integrity: sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==}
engines: {node: '>=0.10.0'}
isomorphic-dompurify@2.22.0:
resolution: {integrity: sha512-A2xsDNST1yB94rErEnwqlzSvGllCJ4e8lDMe1OWBH2hvpfc/2qzgMEiDshTO1HwO+PIDTiYeOc7ZDB7Ds49BOg==}
engines: {node: '>=18'}
isomorphic.js@0.2.5:
resolution: {integrity: sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==}
@@ -10076,6 +10189,15 @@ packages:
canvas:
optional: true
jsdom@26.0.0:
resolution: {integrity: sha512-BZYDGVAIriBWTpIxYzrXjv3E/4u8+/pSG5bQdIYCbNCGOvsPkDQfTVLAIXAf9ETdCpduCVTkDe2NNZ8NIwUVzw==}
engines: {node: '>=18'}
peerDependencies:
canvas: ^3.0.0
peerDependenciesMeta:
canvas:
optional: true
jsesc@2.5.2:
resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==}
engines: {node: '>=4'}
@@ -10520,6 +10642,9 @@ packages:
resolution: {integrity: sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==}
engines: {node: 14 || >=16.14}
lru-cache@10.4.3:
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
lru-cache@5.1.1:
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
@@ -10578,6 +10703,10 @@ packages:
resolution: {integrity: sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==}
engines: {node: '>=10'}
math-intrinsics@1.1.0:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'}
md5@2.3.0:
resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==}
@@ -11007,6 +11136,9 @@ packages:
nwsapi@2.2.13:
resolution: {integrity: sha512-cTGB9ptp9dY9A5VbMSe7fQBcl/tt22Vcqdq8+eN93rblOuE0aCFu4aZ2vMwct/2t+lFnosm8RkQW1I0Omb1UtQ==}
nwsapi@2.2.20:
resolution: {integrity: sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==}
oauth@0.10.0:
resolution: {integrity: sha512-1orQ9MT1vHFGQxhuy7E/0gECD3fd2fCC+PIX+/jgmU/gI3EpRocXtmtvxCO5x3WZ443FLTLFWNDjl5MPJf9u+Q==}
@@ -11259,6 +11391,9 @@ packages:
parse5@7.1.2:
resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==}
parse5@7.2.1:
resolution: {integrity: sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==}
parseley@0.12.1:
resolution: {integrity: sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==}
@@ -12011,6 +12146,9 @@ packages:
roughjs@4.6.6:
resolution: {integrity: sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==}
rrweb-cssom@0.8.0:
resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==}
run-parallel@1.2.0:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
@@ -12739,6 +12877,13 @@ packages:
tippy.js@6.3.7:
resolution: {integrity: sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==}
tldts-core@6.1.85:
resolution: {integrity: sha512-DTjUVvxckL1fIoPSb3KE7ISNtkWSawZdpfxGxwiIrZoO6EbHVDXXUIlIuWympPaeS+BLGyggozX/HTMsRAdsoA==}
tldts@6.1.85:
resolution: {integrity: sha512-gBdZ1RjCSevRPFix/hpaUWeak2/RNUZB4/8frF1r5uYMHjFptkiT0JXIebWvgI/0ZHXvxaUDDJshiA0j6GdL3w==}
hasBin: true
tmp-promise@3.0.3:
resolution: {integrity: sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==}
@@ -12780,6 +12925,10 @@ packages:
resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==}
engines: {node: '>=6'}
tough-cookie@5.1.2:
resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==}
engines: {node: '>=16'}
tr46@0.0.3:
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
@@ -12791,6 +12940,10 @@ packages:
resolution: {integrity: sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==}
engines: {node: '>=14'}
tr46@5.1.0:
resolution: {integrity: sha512-IUWnUK7ADYR5Sl1fZlO1INDUhVhatWl7BtJWsIhwJ0UAK7ilzzIa8uIqOO/aYVWHZPJkKbEL+362wrzoeRF7bw==}
engines: {node: '>=18'}
tree-kill@1.2.2:
resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
hasBin: true
@@ -12918,6 +13071,9 @@ packages:
tunnel-agent@0.6.0:
resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
turndown@7.2.0:
resolution: {integrity: sha512-eCZGBN4nNNqM9Owkv9HAtWRYfLA4h909E/WGAWWBpmB275ehNhZyk87/Tpvjbp0jjNl9XwCsbe6bm6CqFsgD+A==}
tus-js-client@4.3.1:
resolution: {integrity: sha512-ZLeYmjrkaU1fUsKbIi8JML52uAocjEZtBx4DKjRrqzrZa0O4MYwT6db+oqePlspV+FxXJAyFBc/L5gwUi2OFsg==}
engines: {node: '>=18'}
@@ -13185,6 +13341,10 @@ packages:
resolution: {integrity: sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==}
engines: {node: '>=14'}
w3c-xmlserializer@5.0.0:
resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
engines: {node: '>=18'}
walker@1.0.8:
resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==}
@@ -13299,10 +13459,18 @@ packages:
resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==}
engines: {node: '>=12'}
whatwg-encoding@3.1.1:
resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==}
engines: {node: '>=18'}
whatwg-mimetype@3.0.0:
resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==}
engines: {node: '>=12'}
whatwg-mimetype@4.0.0:
resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==}
engines: {node: '>=18'}
whatwg-url@11.0.0:
resolution: {integrity: sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==}
engines: {node: '>=12'}
@@ -13311,6 +13479,10 @@ packages:
resolution: {integrity: sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==}
engines: {node: '>=16'}
whatwg-url@14.2.0:
resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==}
engines: {node: '>=18'}
whatwg-url@5.0.0:
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
@@ -13424,6 +13596,10 @@ packages:
resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==}
engines: {node: '>=12'}
xml-name-validator@5.0.0:
resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
engines: {node: '>=18'}
xml-parse-from-string@1.0.1:
resolution: {integrity: sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g==}
@@ -13591,6 +13767,14 @@ snapshots:
'@anticrm/skillset@0.6.0': {}
'@asamuzakjp/css-color@3.1.1':
dependencies:
'@csstools/css-calc': 2.1.2(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)
'@csstools/css-color-parser': 3.0.8(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)
'@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3)
'@csstools/css-tokenizer': 3.0.3
lru-cache: 10.4.3
'@aws-crypto/crc32@5.2.0':
dependencies:
'@aws-crypto/util': 5.2.0
@@ -14353,6 +14537,26 @@ snapshots:
dependencies:
'@jridgewell/trace-mapping': 0.3.9
'@csstools/color-helpers@5.0.2': {}
'@csstools/css-calc@2.1.2(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)':
dependencies:
'@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3)
'@csstools/css-tokenizer': 3.0.3
'@csstools/css-color-parser@3.0.8(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)':
dependencies:
'@csstools/color-helpers': 5.0.2
'@csstools/css-calc': 2.1.2(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)
'@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3)
'@csstools/css-tokenizer': 3.0.3
'@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3)':
dependencies:
'@csstools/css-tokenizer': 3.0.3
'@csstools/css-tokenizer@3.0.3': {}
'@dabh/diagnostics@2.0.3':
dependencies:
colorspace: 1.1.4
@@ -15381,6 +15585,8 @@ snapshots:
dependencies:
langium: 3.0.0
'@mixmark-io/domino@2.2.0': {}
'@mongodb-js/saslprep@1.1.9':
dependencies:
sparse-bitfield: 3.0.3
@@ -21763,13 +21969,17 @@ snapshots:
- supports-color
- ts-node
'@rush-temp/pod-inbound-mail@file:projects/pod-inbound-mail.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))':
'@rush-temp/pod-inbound-mail@file:projects/pod-inbound-mail.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(bufferutil@4.0.8)(utf-8-validate@6.0.4)':
dependencies:
'@hcengineering/communication-rest-client': 0.1.155(typescript@5.7.3)
'@hcengineering/communication-sdk-types': 0.1.155(typescript@5.7.3)
'@hcengineering/communication-types': 0.1.155(typescript@5.7.3)
'@tsconfig/node16': 1.0.4
'@types/cors': 2.8.17
'@types/express': 4.17.21
'@types/jest': 29.5.12
'@types/node': 20.11.19
'@types/turndown': 5.0.5
'@types/uuid': 8.3.4
'@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.3.3))(eslint@8.56.0)(typescript@5.7.3)
'@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.7.3)
@@ -21785,11 +21995,13 @@ snapshots:
eslint-plugin-node: 11.1.0(eslint@8.56.0)
eslint-plugin-promise: 6.1.1(eslint@8.56.0)
express: 4.21.2
isomorphic-dompurify: 2.22.0(bufferutil@4.0.8)(utf-8-validate@6.0.4)
jest: 29.7.0(@types/node@20.11.19)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3))
jwt-simple: 0.5.6
prettier: 3.2.5
ts-jest: 29.1.2(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.24.2)(jest@29.7.0(@types/node@20.11.19)(ts-node@10.9.2(@types/node@20.11.19)(typescript@5.3.3)))(typescript@5.7.3)
ts-node: 10.9.2(@types/node@20.11.19)(typescript@5.7.3)
turndown: 7.2.0
typescript: 5.7.3
uuid: 8.3.2
transitivePeerDependencies:
@@ -21799,8 +22011,11 @@ snapshots:
- '@swc/wasm'
- babel-jest
- babel-plugin-macros
- bufferutil
- canvas
- node-notifier
- supports-color
- utf-8-validate
'@rush-temp/pod-love@file:projects/pod-love.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(bufferutil@4.0.8)(utf-8-validate@6.0.4)':
dependencies:
@@ -28571,6 +28786,8 @@ snapshots:
'@types/trusted-types@2.0.7': {}
'@types/turndown@5.0.5': {}
'@types/unist@2.0.10': {}
'@types/uuid@8.3.4': {}
@@ -29101,6 +29318,8 @@ snapshots:
transitivePeerDependencies:
- supports-color
agent-base@7.1.3: {}
agentkeepalive@4.5.0:
dependencies:
humanize-ms: 1.2.1
@@ -29740,6 +29959,11 @@ snapshots:
normalize-url: 6.1.0
responselike: 2.0.1
call-bind-apply-helpers@1.0.2:
dependencies:
es-errors: 1.3.0
function-bind: 1.1.2
call-bind@1.0.7:
dependencies:
es-define-property: 1.0.0
@@ -30245,6 +30469,11 @@ snapshots:
dependencies:
cssom: 0.3.8
cssstyle@4.3.0:
dependencies:
'@asamuzakjp/css-color': 3.1.1
rrweb-cssom: 0.8.0
csv-parse@5.1.0: {}
csv-stringify@6.5.2: {}
@@ -30456,6 +30685,11 @@ snapshots:
whatwg-mimetype: 3.0.0
whatwg-url: 11.0.0
data-urls@5.0.0:
dependencies:
whatwg-mimetype: 4.0.0
whatwg-url: 14.2.0
data-view-buffer@1.0.1:
dependencies:
call-bind: 1.0.7
@@ -30733,6 +30967,10 @@ snapshots:
optionalDependencies:
'@types/trusted-types': 2.0.7
dompurify@3.2.4:
optionalDependencies:
'@types/trusted-types': 2.0.7
domutils@1.5.1:
dependencies:
dom-serializer: 0.1.1
@@ -30787,6 +31025,12 @@ snapshots:
dependencies:
underscore: 1.13.7
dunder-proto@1.0.1:
dependencies:
call-bind-apply-helpers: 1.0.2
es-errors: 1.3.0
gopd: 1.2.0
duplexer@0.1.2: {}
dynamic-dedupe@0.3.0:
@@ -31059,6 +31303,8 @@ snapshots:
dependencies:
get-intrinsic: 1.2.4
es-define-property@1.0.1: {}
es-errors@1.3.0: {}
es-iterator-helpers@1.0.19:
@@ -31084,12 +31330,23 @@ snapshots:
dependencies:
es-errors: 1.3.0
es-object-atoms@1.1.1:
dependencies:
es-errors: 1.3.0
es-set-tostringtag@2.0.3:
dependencies:
get-intrinsic: 1.2.4
has-tostringtag: 1.0.2
hasown: 2.0.1
es-set-tostringtag@2.1.0:
dependencies:
es-errors: 1.3.0
get-intrinsic: 1.3.0
has-tostringtag: 1.0.2
hasown: 2.0.2
es-shim-unscopables@1.0.2:
dependencies:
hasown: 2.0.1
@@ -31839,6 +32096,13 @@ snapshots:
combined-stream: 1.0.8
mime-types: 2.1.35
form-data@4.0.2:
dependencies:
asynckit: 0.4.0
combined-stream: 1.0.8
es-set-tostringtag: 2.1.0
mime-types: 2.1.35
formdata-node@4.4.1:
dependencies:
node-domexception: 1.0.0
@@ -31965,8 +32229,26 @@ snapshots:
has-symbols: 1.0.3
hasown: 2.0.1
get-intrinsic@1.3.0:
dependencies:
call-bind-apply-helpers: 1.0.2
es-define-property: 1.0.1
es-errors: 1.3.0
es-object-atoms: 1.1.1
function-bind: 1.1.2
get-proto: 1.0.1
gopd: 1.2.0
has-symbols: 1.1.0
hasown: 2.0.2
math-intrinsics: 1.1.0
get-package-type@0.1.0: {}
get-proto@1.0.1:
dependencies:
dunder-proto: 1.0.1
es-object-atoms: 1.1.1
get-stdin@8.0.0: {}
get-stream@4.1.0:
@@ -32128,6 +32410,8 @@ snapshots:
dependencies:
get-intrinsic: 1.2.4
gopd@1.2.0: {}
got@11.8.6:
dependencies:
'@sindresorhus/is': 4.6.0
@@ -32209,6 +32493,8 @@ snapshots:
has-symbols@1.0.3: {}
has-symbols@1.1.0: {}
has-tostringtag@1.0.2:
dependencies:
has-symbols: 1.0.3
@@ -32256,6 +32542,10 @@ snapshots:
dependencies:
whatwg-encoding: 2.0.0
html-encoding-sniffer@4.0.0:
dependencies:
whatwg-encoding: 3.1.1
html-entities@2.4.0: {}
html-escaper@2.0.2: {}
@@ -32416,6 +32706,13 @@ snapshots:
transitivePeerDependencies:
- supports-color
https-proxy-agent@7.0.6:
dependencies:
agent-base: 7.1.3
debug: 4.4.0
transitivePeerDependencies:
- supports-color
human-signals@2.1.0: {}
humanize-ms@1.2.1:
@@ -32714,6 +33011,16 @@ snapshots:
isobject@4.0.0: {}
isomorphic-dompurify@2.22.0(bufferutil@4.0.8)(utf-8-validate@6.0.4):
dependencies:
dompurify: 3.2.4
jsdom: 26.0.0(bufferutil@4.0.8)(utf-8-validate@6.0.4)
transitivePeerDependencies:
- bufferutil
- canvas
- supports-color
- utf-8-validate
isomorphic.js@0.2.5: {}
isstream@0.1.2: {}
@@ -33182,6 +33489,34 @@ snapshots:
- supports-color
- utf-8-validate
jsdom@26.0.0(bufferutil@4.0.8)(utf-8-validate@6.0.4):
dependencies:
cssstyle: 4.3.0
data-urls: 5.0.0
decimal.js: 10.4.3
form-data: 4.0.2
html-encoding-sniffer: 4.0.0
http-proxy-agent: 7.0.2
https-proxy-agent: 7.0.6
is-potential-custom-element-name: 1.0.1
nwsapi: 2.2.20
parse5: 7.2.1
rrweb-cssom: 0.8.0
saxes: 6.0.0
symbol-tree: 3.2.4
tough-cookie: 5.1.2
w3c-xmlserializer: 5.0.0
webidl-conversions: 7.0.0
whatwg-encoding: 3.1.1
whatwg-mimetype: 4.0.0
whatwg-url: 14.2.0
ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.4)
xml-name-validator: 5.0.0
transitivePeerDependencies:
- bufferutil
- supports-color
- utf-8-validate
jsesc@2.5.2: {}
json-bigint@1.0.0:
@@ -33686,6 +34021,8 @@ snapshots:
lru-cache@10.2.0: {}
lru-cache@10.4.3: {}
lru-cache@5.1.1:
dependencies:
yallist: 3.1.1
@@ -33769,6 +34106,8 @@ snapshots:
escape-string-regexp: 4.0.0
optional: true
math-intrinsics@1.1.0: {}
md5@2.3.0:
dependencies:
charenc: 0.0.2
@@ -34175,6 +34514,8 @@ snapshots:
nwsapi@2.2.13: {}
nwsapi@2.2.20: {}
oauth@0.10.0: {}
object-assign@4.1.1: {}
@@ -34460,6 +34801,10 @@ snapshots:
dependencies:
entities: 4.5.0
parse5@7.2.1:
dependencies:
entities: 4.5.0
parseley@0.12.1:
dependencies:
leac: 0.6.0
@@ -35257,6 +35602,8 @@ snapshots:
points-on-curve: 0.2.0
points-on-path: 0.2.1
rrweb-cssom@0.8.0: {}
run-parallel@1.2.0:
dependencies:
queue-microtask: 1.2.3
@@ -36171,6 +36518,12 @@ snapshots:
dependencies:
'@popperjs/core': 2.11.8
tldts-core@6.1.85: {}
tldts@6.1.85:
dependencies:
tldts-core: 6.1.85
tmp-promise@3.0.3:
dependencies:
tmp: 0.2.3
@@ -36205,6 +36558,10 @@ snapshots:
universalify: 0.2.0
url-parse: 1.5.10
tough-cookie@5.1.2:
dependencies:
tldts: 6.1.85
tr46@0.0.3: {}
tr46@3.0.0:
@@ -36215,6 +36572,10 @@ snapshots:
dependencies:
punycode: 2.3.1
tr46@5.1.0:
dependencies:
punycode: 2.3.1
tree-kill@1.2.2: {}
triple-beam@1.4.1: {}
@@ -36443,6 +36804,10 @@ snapshots:
dependencies:
safe-buffer: 5.2.1
turndown@7.2.0:
dependencies:
'@mixmark-io/domino': 2.2.0
tus-js-client@4.3.1:
dependencies:
buffer-from: 1.1.2
@@ -36698,6 +37063,10 @@ snapshots:
dependencies:
xml-name-validator: 4.0.0
w3c-xmlserializer@5.0.0:
dependencies:
xml-name-validator: 5.0.0
walker@1.0.8:
dependencies:
makeerror: 1.0.12
@@ -36895,8 +37264,14 @@ snapshots:
dependencies:
iconv-lite: 0.6.3
whatwg-encoding@3.1.1:
dependencies:
iconv-lite: 0.6.3
whatwg-mimetype@3.0.0: {}
whatwg-mimetype@4.0.0: {}
whatwg-url@11.0.0:
dependencies:
tr46: 3.0.0
@@ -36907,6 +37282,11 @@ snapshots:
tr46: 4.1.1
webidl-conversions: 7.0.0
whatwg-url@14.2.0:
dependencies:
tr46: 5.1.0
webidl-conversions: 7.0.0
whatwg-url@5.0.0:
dependencies:
tr46: 0.0.3
@@ -37058,6 +37438,8 @@ snapshots:
xml-name-validator@4.0.0: {}
xml-name-validator@5.0.0: {}
xml-parse-from-string@1.0.1: {}
xml2js@0.5.0:
+30 -24
View File
@@ -30,44 +30,50 @@
"_phase:validate": "compile validate"
},
"devDependencies": {
"cross-env": "~7.0.3",
"@hcengineering/platform-rig": "^0.6.0",
"@types/node": "~20.11.16",
"@typescript-eslint/eslint-plugin": "^6.11.0",
"@typescript-eslint/parser": "^6.11.0",
"eslint-config-standard-with-typescript": "^40.0.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-n": "^15.4.0",
"eslint": "^8.54.0",
"esbuild": "^0.24.2",
"prettier": "^3.1.0",
"ts-node": "^10.8.0",
"typescript": "^5.3.3",
"jest": "^29.7.0",
"ts-jest": "^29.1.1",
"@types/jest": "^29.5.5",
"@tsconfig/node16": "^1.0.4",
"@types/cors": "^2.8.12",
"@types/express": "^4.17.13",
"eslint-plugin-node": "^11.1.0"
"@types/jest": "^29.5.5",
"@types/node": "~20.11.16",
"@types/turndown": "^5.0.5",
"@typescript-eslint/eslint-plugin": "^6.11.0",
"@typescript-eslint/parser": "^6.11.0",
"cross-env": "~7.0.3",
"esbuild": "^0.24.2",
"eslint": "^8.54.0",
"eslint-config-standard-with-typescript": "^40.0.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-n": "^15.4.0",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^6.1.1",
"jest": "^29.7.0",
"prettier": "^3.1.0",
"ts-jest": "^29.1.1",
"ts-node": "^10.8.0",
"typescript": "^5.3.3"
},
"dependencies": {
"@hcengineering/account-client": "^0.6.0",
"@hcengineering/analytics-service": "^0.6.0",
"@hcengineering/api-client": "^0.6.0",
"@hcengineering/attachment": "^0.6.14",
"@hcengineering/card": "^0.6.0",
"@hcengineering/chunter": "^0.6.20",
"@hcengineering/communication-rest-client": "0.1.155",
"@hcengineering/communication-sdk-types": "0.1.155",
"@hcengineering/communication-types": "0.1.155",
"@hcengineering/contact": "^0.6.24",
"@hcengineering/core": "^0.6.32",
"@hcengineering/mail": "^0.6.0",
"@hcengineering/text-html": "^0.6.0",
"@hcengineering/server-core": "^0.6.1",
"@hcengineering/server-storage": "^0.6.0",
"@types/uuid": "^8.3.1",
"eml-parse-js": "^1.2.0-beta.0",
"uuid": "^8.3.2",
"cors": "^2.8.5",
"express": "^4.21.2",
"dotenv": "~16.0.0",
"jwt-simple": "^0.5.6"
"eml-parse-js": "^1.2.0-beta.0",
"express": "^4.21.2",
"isomorphic-dompurify": "^2.22.0",
"jwt-simple": "^0.5.6",
"turndown": "^7.2.0",
"uuid": "^8.3.2"
}
}
+5 -1
View File
@@ -23,6 +23,8 @@ interface Config {
workspaceUrl: string
ignoredAddresses: string[]
hookToken?: string
mailSizeLimit?: string
storageConfig?: string
}
const config: Config = {
@@ -36,7 +38,9 @@ const config: Config = {
throw Error('WORKSPACE_URL env var is not set')
})(),
ignoredAddresses: process.env.IGNORED_ADDRESSES?.split(',') ?? [],
hookToken: process.env.HOOK_TOKEN
hookToken: process.env.HOOK_TOKEN,
mailSizeLimit: process.env.MAIL_SIZE_LIMIT ?? '50mb',
storageConfig: process.env.STORAGE_CONFIG
}
export default config
@@ -12,11 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//
import { createHash } from 'crypto'
import { createHash, randomUUID } from 'crypto'
import { readEml, ReadedEmlJson } from 'eml-parse-js'
import { Request, Response } from 'express'
import { htmlToMarkup } from '@hcengineering/text-html'
import { createMessages } from './message'
import TurndownService from 'turndown'
import DOMPurify from 'isomorphic-dompurify'
import { MeasureContext } from '@hcengineering/core'
import { type Attachment, createMessages } from './message'
import config from './config'
interface MtaMessage {
@@ -34,7 +36,7 @@ interface MtaMessage {
}
}
export async function handleMtaHook (req: Request, res: Response): Promise<void> {
export async function handleMtaHook (req: Request, res: Response, ctx: MeasureContext): Promise<void> {
try {
if (config.hookToken !== undefined) {
const token = req.headers['x-hook-token']
@@ -47,12 +49,11 @@ export async function handleMtaHook (req: Request, res: Response): Promise<void>
const from = { address: mta.envelope.from.address, name: '' }
if (config.ignoredAddresses.includes(from.address)) {
console.log(`Ignoring message from ${from.address}`)
return
}
const fromHeader = mta.message.headers.find((header) => header[0] === 'From')?.[1]
if (fromHeader !== undefined) {
from.name = extractContactName(fromHeader)
from.name = extractContactName(ctx, fromHeader)
}
const tos = mta.envelope.to.map((to) => ({ address: to.address, name: '' }))
@@ -61,7 +62,7 @@ export async function handleMtaHook (req: Request, res: Response): Promise<void>
for (const part of toHeader.split(',')) {
for (const to of tos) {
if (part.includes(to.address)) {
to.name = extractContactName(part)
to.name = extractContactName(ctx, part)
}
}
}
@@ -69,7 +70,7 @@ export async function handleMtaHook (req: Request, res: Response): Promise<void>
const subject = (mta.message.headers.find((header) => header[0] === 'Subject')?.[1] ?? '').trim()
const inReplyTo = mta.message.headers.find((header) => header[0] === 'In-Reply-To')?.[1]?.trim()
const content = await getContent(mta)
const { content, attachments } = await parseContent(ctx, mta)
let mailId = mta.message.headers.find((header) => header[0] === 'Message-ID')?.[1].trim()
if (mailId === undefined) {
@@ -85,15 +86,19 @@ export async function handleMtaHook (req: Request, res: Response): Promise<void>
.digest('hex')
}
await createMessages(mailId, from, tos, subject, content, inReplyTo)
} catch (err) {
console.error('mta-hook', err)
await createMessages(ctx, mailId, from, tos, subject, content, attachments, inReplyTo)
} catch (error) {
ctx.error('mta-hook', { error })
} finally {
// Any error in the mta-hook should not prevent the mail server from handling emails
res.status(200).send({ action: 'accept' })
}
}
async function getContent (mta: MtaMessage): Promise<string> {
async function parseContent (
ctx: MeasureContext,
mta: MtaMessage
): Promise<{ content: string, attachments: Attachment[] }> {
const contentType = mta.message.headers.find((header) => header[0] === 'Content-Type')?.[1]
if (contentType === undefined) {
throw new Error('Content-Type header not found')
@@ -110,30 +115,63 @@ async function getContent (mta: MtaMessage): Promise<string> {
}
})
})
let content = email.text ?? ''
let isMarkdown = false
if (email.html !== undefined) {
try {
// Some mailers (e.g. Google) use divs instead of paragraphs
const html = email.html.replaceAll('<div', '<p').replaceAll('</div>', '</p>')
const markup = htmlToMarkup(html)
return JSON.stringify(markup)
} catch (err) {
console.warn('Failed to parse html content', err)
const html = DOMPurify.sanitize(email.html)
const tds = new TurndownService()
content = tds.turndown(html)
isMarkdown = true
console.log('Markdown:\n', content)
} catch (error) {
ctx.warn('Failed to parse html content', { error })
}
}
return email.text ?? ''
const attachments: Attachment[] = []
if (config.storageConfig !== undefined) {
for (const a of email.attachments ?? []) {
console.log('Attachment', a)
if (a.name === undefined || a.name.length === 0) {
// EML parser returns attachments with empty name for parts of content
// that do not have "Content-Disposition: attachment" e.g. for part
// Content-Type: text/calendar; charset="UTF-8"; method=REQUEST
continue
}
const attachment: Attachment = {
id: randomUUID(),
name: a.name,
data: Buffer.from(a.data64, 'base64'),
contentType: a.contentType.split(';')[0].trim()
}
attachments.push(attachment)
// For inline images, replace the CID references with the blob id
if (isMarkdown && a.inline && a.id !== undefined) {
const cid = a.id.replace(/[<>]/g, '')
content = content.replaceAll(
new RegExp(`!\\[.*?\\]\\(cid:${cid}\\)`, 'g'),
`![${a.name}](cid:${attachment.id})`
)
}
}
}
return { content, attachments }
}
function extractContactName (fromHeader: string): string {
function extractContactName (ctx: MeasureContext, fromHeader: string): string {
// Match name part that appears before an email in angle brackets
const nameMatch = fromHeader.match(/^\s*"?([^"<]+?)"?\s*<.+?>/)
const name = nameMatch?.[1].trim() ?? ''
if (name.length > 0) {
return decodeMimeWord(name)
return decodeMimeWord(ctx, name)
}
return ''
}
function decodeMimeWord (text: string): string {
function decodeMimeWord (ctx: MeasureContext, text: string): string {
return text.replace(/=\?([^?]+)\?([BQ])\?([^?]+)\?=/gi, (match, charset, encoding, content) => {
try {
if (encoding.toUpperCase() === 'B') {
@@ -149,7 +187,7 @@ function decodeMimeWord (text: string): string {
}
return match
} catch (error) {
console.warn('Failed to decode encoded word:', match, error)
ctx.warn('Failed to decode encoded word', { error })
return match
}
})
+48 -20
View File
@@ -15,27 +15,49 @@
import cors from 'cors'
import express, { NextFunction, Request, Response } from 'express'
import { join } from 'path'
import { SplitLogger } from '@hcengineering/analytics-service'
import { MeasureContext, MeasureMetricsContext, newMetrics } from '@hcengineering/core'
import { initStatisticsContext } from '@hcengineering/server-core'
import { handleMtaHook } from './handlerMta'
import config from './config'
type RequestHandler = (req: Request, res: Response, next?: NextFunction) => Promise<void>
const catchError = (fn: RequestHandler) => (req: Request, res: Response, next: NextFunction) => {
void (async () => {
try {
await fn(req, res, next)
} catch (err: unknown) {
console.error(req.method, req.path, err)
next(err)
}
})()
}
type RequestHandler = (req: Request, res: Response, ctx: MeasureContext, next?: NextFunction) => Promise<void>
async function main (): Promise<void> {
const ctx = initStatisticsContext('inbound-mail', {
factory: () =>
new MeasureMetricsContext(
'inbound-mail',
{},
{},
newMetrics(),
new SplitLogger('inbound-mail', {
root: join(process.cwd(), 'logs'),
enableConsole: (process.env.ENABLE_CONSOLE ?? 'true') === 'true'
})
)
})
const app = express()
app.use(cors())
app.use(express.json())
app.use(
express.json({
limit: config.mailSizeLimit
})
)
const catchError = (fn: RequestHandler) => (req: Request, res: Response, next: NextFunction) => {
void (async () => {
try {
await fn(req, res, ctx)
} catch (error: any) {
ctx.error('Failed to handle request', { method: req.method, path: req.path, error })
next(error)
}
})()
}
app.post('/mta-hook', catchError(handleMtaHook))
@@ -43,13 +65,19 @@ async function main (): Promise<void> {
res.status(404).send({ message: 'Not found' })
})
app.use((err: any, _req: any, res: any, _next: any) => {
app.use((err: any, req: Request, res: any, _next: any) => {
ctx.error(err)
if (req.path === '/mta-hook') {
// Any error in the mta-hook should not prevent the mail server from handling emails
// At least the PayloadTooLargeError falls here before reacing our code
res.status(200).send({ action: 'accept' })
return
}
res.status(500).send({ message: err.message })
})
const server = app.listen(config.port, () => {
console.log(`server started on port ${config.port}`)
console.log({ ...config, secret: '(stripped)', hookToken: '(stripped)' })
ctx.info('server started', { ...config, secret: '(stripped)', hookToken: '(stripped)' })
})
const shutdown = (): void => {
@@ -60,11 +88,11 @@ async function main (): Promise<void> {
process.on('SIGINT', shutdown)
process.on('SIGTERM', shutdown)
process.on('uncaughtException', (e) => {
console.error(e)
process.on('uncaughtException', (error: any) => {
ctx.error('Uncaught exception', { error })
})
process.on('unhandledRejection', (e) => {
console.error(e)
process.on('unhandledRejection', (error: any) => {
ctx.error('Unhandled rejection', { error })
})
}
+138 -44
View File
@@ -13,7 +13,19 @@
// limitations under the License.
//
import { encode } from 'jwt-simple'
import { getClient as getAccountClient } from '@hcengineering/account-client'
import { createRestTxOperations } from '@hcengineering/api-client'
import { type Card } from '@hcengineering/card'
import {
type RestClient as CommunicationClient,
createRestClient as getCommunicationClient
} from '@hcengineering/communication-rest-client'
import { type CreateFileEvent, type CreateMessageEvent, RequestEventType } from '@hcengineering/communication-sdk-types'
import { MessageType } from '@hcengineering/communication-types'
import contact, { PersonSpace } from '@hcengineering/contact'
import {
type Blob,
type MeasureContext,
type PersonId,
type Ref,
type TxOperations,
@@ -22,15 +34,18 @@ import {
RateLimiter,
systemAccountUuid
} from '@hcengineering/core'
import { getClient as getAccountClient } from '@hcengineering/account-client'
import { createRestTxOperations } from '@hcengineering/api-client'
import { type Card } from '@hcengineering/card'
import chunter from '@hcengineering/chunter'
import contact, { PersonSpace } from '@hcengineering/contact'
import mail from '@hcengineering/mail'
import { buildStorageFromConfig, storageConfigFromEnv } from '@hcengineering/server-storage'
import config from './config'
import { ensureGlobalPerson, ensureLocalPerson } from './person'
export interface Attachment {
id: string
name: string
data: Buffer
contentType: string
}
function generateToken (): string {
return encode(
{
@@ -42,28 +57,32 @@ function generateToken (): string {
}
export async function createMessages (
ctx: MeasureContext,
mailId: string,
from: { address: string, name: string },
tos: { address: string, name: string }[],
subject: string,
content: string,
attachments: Attachment[],
inReplyTo?: string
): Promise<void> {
console.log(`[${mailId}] Sending message ${from.address} --> ${tos.map((to) => to.address).join(',')}`)
ctx.info('Sending message', { mailId, from: from.address, to: tos.map((to) => to.address).join(',') })
const accountClient = getAccountClient(config.accountsUrl, generateToken())
const wsInfo = await accountClient.selectWorkspace(config.workspaceUrl)
const transactorUrl = wsInfo.endpoint.replace('ws://', 'http://').replace('wss://', 'https://')
const client = await createRestTxOperations(transactorUrl, wsInfo.workspace, wsInfo.token)
const txClient = await createRestTxOperations(transactorUrl, wsInfo.workspace, wsInfo.token)
const msgClient = getCommunicationClient(wsInfo.endpoint, wsInfo.workspace, wsInfo.token)
const fromPerson = await ensureGlobalPerson(accountClient, mailId, from)
const fromPerson = await ensureGlobalPerson(ctx, accountClient, mailId, from)
if (fromPerson === undefined) {
console.error(`[${mailId}] Unable to create message without a proper FROM`)
ctx.error('Unable to create message without a proper FROM', { mailId })
return
}
try {
await ensureLocalPerson(
client,
ctx,
txClient,
mailId,
fromPerson.uuid,
fromPerson.socialId,
@@ -71,21 +90,22 @@ export async function createMessages (
fromPerson.firstName,
fromPerson.lastName
)
} catch (err) {
console.error(`[${mailId}] Failed to ensure local FROM person`, err)
console.error(`[${mailId}] Unable to create message without a proper FROM`)
} catch (error) {
ctx.error('Failed to ensure local FROM person', { error, mailId })
ctx.error('Unable to create message without a proper FROM', { mailId })
return
}
const toPersons: { address: string, uuid: PersonUuid, socialId: PersonId }[] = []
for (const to of tos) {
const toPerson = await ensureGlobalPerson(accountClient, mailId, to)
const toPerson = await ensureGlobalPerson(ctx, accountClient, mailId, to)
if (toPerson === undefined) {
continue
}
try {
await ensureLocalPerson(
client,
ctx,
txClient,
mailId,
toPerson.uuid,
toPerson.socialId,
@@ -93,42 +113,101 @@ export async function createMessages (
toPerson.firstName,
toPerson.lastName
)
} catch (err) {
console.error(`[${mailId}] Failed to ensure local TO person, skip`, err)
} catch (error) {
ctx.error('Failed to ensure local TO person, skip', { error, mailId })
continue
}
toPersons.push({ address: to.address, ...toPerson })
}
if (toPersons.length === 0) {
console.error(`[${mailId}] Unable to create message without a proper TO`)
ctx.error('Unable to create message without a proper TO', { mailId })
return
}
const modifiedBy = fromPerson.socialId
const participants = [fromPerson.socialId, ...toPersons.map((p) => p.socialId)]
try {
const spaces = await getPersonSpaces(client, mailId, fromPerson.uuid, from.address)
if (spaces.length > 0) {
await saveMessageToSpaces(client, mailId, spaces, participants, modifiedBy, subject, content, inReplyTo)
const attachedBlobs: Attachment[] = []
if (config.storageConfig !== undefined) {
const storageConfig = storageConfigFromEnv()
const storageAdapter = buildStorageFromConfig(storageConfig)
try {
for (const a of attachments ?? []) {
try {
await storageAdapter.put(
ctx,
{
uuid: wsInfo.workspace,
url: wsInfo.workspaceUrl,
dataId: wsInfo.workspaceDataId
},
a.id,
a.data,
a.contentType
)
attachedBlobs.push(a)
ctx.info('Uploaded attachment', { mailId, blobId: a.id, name: a.name, contentType: a.contentType })
} catch (error) {
ctx.error('Failed to upload attachment', { name: a.name, error, mailId })
}
}
} finally {
await storageAdapter.close()
}
} catch (err) {
console.error(`[${mailId}] Failed to save message to personal spaces of ${fromPerson.uuid} (${from.address})`, err)
}
try {
const spaces = await getPersonSpaces(ctx, txClient, mailId, fromPerson.uuid, from.address)
if (spaces.length > 0) {
await saveMessageToSpaces(
ctx,
txClient,
msgClient,
mailId,
spaces,
participants,
modifiedBy,
subject,
content,
attachedBlobs,
inReplyTo
)
}
} catch (error) {
ctx.error('Failed to save message to personal spaces', {
error,
mailId,
personUuid: fromPerson.uuid,
email: from.address
})
}
for (const to of toPersons) {
try {
const spaces = await getPersonSpaces(client, mailId, to.uuid, to.address)
const spaces = await getPersonSpaces(ctx, txClient, mailId, to.uuid, to.address)
if (spaces.length > 0) {
await saveMessageToSpaces(client, mailId, spaces, participants, modifiedBy, subject, content, inReplyTo)
await saveMessageToSpaces(
ctx,
txClient,
msgClient,
mailId,
spaces,
participants,
modifiedBy,
subject,
content,
attachedBlobs,
inReplyTo
)
}
} catch (err) {
console.error(`[${mailId}] Failed to save message spaces of ${to.socialId} (${to.address})`, err)
} catch (error) {
ctx.error('Failed to save message spaces', { error, mailId, personUuid: to.uuid, email: to.address })
}
}
}
async function getPersonSpaces (
ctx: MeasureContext,
client: TxOperations,
mailId: string,
personUuid: PersonUuid,
@@ -138,29 +217,32 @@ async function getPersonSpaces (
const personRefs = persons.map((p) => p._id)
const spaces = await client.findAll(contact.class.PersonSpace, { person: { $in: personRefs } })
if (spaces.length === 0) {
console.log(`[${mailId}] No personal space found for ${personUuid} (${email}), skip`)
ctx.info('No personal space found, skip', { mailId, personUuid, email })
}
return spaces
}
async function saveMessageToSpaces (
ctx: MeasureContext,
client: TxOperations,
msgClient: CommunicationClient,
mailId: string,
spaces: PersonSpace[],
participants: PersonId[],
modifiedBy: PersonId,
subject: string,
content: string,
attachments: Attachment[],
inReplyTo?: string
): Promise<void> {
const rateLimiter = new RateLimiter(10)
for (const space of spaces) {
await rateLimiter.add(async () => {
console.log(`[${mailId}] Saving message to space ${space._id}`)
ctx.info('Saving message to space', { mailId, space: space._id })
const route = await client.findOne(mail.class.MailRoute, { mailId })
if (route !== undefined) {
console.log(`[${mailId}] Message is already in the thread ${route.threadId}, skip`)
ctx.info('Message is already in the thread, skip', { mailId, threadId: route.threadId })
return
}
@@ -169,7 +251,7 @@ async function saveMessageToSpaces (
const route = await client.findOne(mail.class.MailRoute, { mailId: inReplyTo })
if (route !== undefined) {
threadId = route.threadId as Ref<Card>
console.log(`[${mailId}] Found existing thread ${threadId}`)
ctx.info('Found existing thread', { mailId, threadId })
}
}
if (threadId === undefined) {
@@ -190,20 +272,32 @@ async function saveMessageToSpaces (
modifiedBy
)
threadId = newThreadId as Ref<Card>
console.log(`[${mailId}] Created new thread ${threadId}`)
ctx.info('Created new thread', { mailId, threadId })
}
await client.addCollection(
chunter.class.ChatMessage,
space._id,
threadId,
mail.class.MailThread,
'messages',
{ message: content },
generateId(),
undefined,
modifiedBy
)
const msgEvent: CreateMessageEvent = {
type: RequestEventType.CreateMessage,
messageType: MessageType.Message,
card: threadId,
content,
creator: modifiedBy
}
const { id: messageId } = (await msgClient.event(msgEvent)) as any
ctx.info('Created message', { mailId, messageId })
for (const a of attachments) {
const fileEvent: CreateFileEvent = {
type: RequestEventType.CreateFile,
card: threadId,
message: messageId,
blobId: a.id as Ref<Blob>,
fileType: a.contentType,
filename: a.name,
size: a.data.length,
creator: modifiedBy
}
await msgClient.event(fileEvent)
}
await client.createDoc(
mail.class.MailRoute,
+16 -7
View File
@@ -13,6 +13,7 @@
// limitations under the License.
//
import {
type MeasureContext,
buildSocialIdString,
generateId,
PersonId,
@@ -25,11 +26,18 @@ import contact, { AvatarType, combineName, SocialIdentity } from '@hcengineering
import { AccountClient } from '@hcengineering/account-client'
export async function ensureGlobalPerson (
ctx: MeasureContext,
client: AccountClient,
mailId: string,
contact: { address: string, name: string }
): Promise<{ socialId: PersonId, uuid: PersonUuid, firstName: string, lastName: string } | undefined> {
const [firstName, lastName] = contact.name.split(' ')
let [firstName, lastName] = contact.name.split(' ')
if (firstName === undefined || firstName.length === 0) {
firstName = contact.address.split('@')[0]
}
if (lastName === undefined || lastName.length === 0) {
lastName = contact.address.split('@')[1]
}
const socialKey = buildSocialIdString({ type: SocialIdType.EMAIL, value: contact.address })
const socialId = await client.findSocialIdBySocialKey(socialKey)
const uuid = await client.findPersonBySocialKey(socialKey)
@@ -38,15 +46,16 @@ export async function ensureGlobalPerson (
}
try {
const globalPerson = await client.ensurePerson(SocialIdType.EMAIL, contact.address, firstName, lastName)
console.log(`[${mailId}] Created global person for ${contact.address}: ${globalPerson.uuid}`)
ctx.info('Created global person', { mailId, email: contact.address, personUuid: globalPerson.uuid })
return { ...globalPerson, firstName, lastName }
} catch (err) {
console.error(`[${mailId}] Failed to create global person for ${contact.address}`, err)
} catch (error) {
ctx.error('Failed to create global person', { mailId, error, email: contact.address })
}
return undefined
}
export async function ensureLocalPerson (
ctx: MeasureContext,
client: TxOperations,
mailId: string,
personUuid: PersonUuid,
@@ -71,7 +80,7 @@ export async function ensureLocalPerson (
if (person === undefined) {
throw new Error(`Failed to create local person for ${personUuid}`)
} else {
console.log(`[${mailId}] Created local person for ${personUuid}: ${person._id}`)
ctx.info('Created local person', { mailId, personUuid, _id: person._id })
}
}
const socialId = await client.findOne(contact.class.SocialIdentity, {
@@ -93,7 +102,7 @@ export async function ensureLocalPerson (
},
personId as any as Ref<SocialIdentity>
)
console.log(`[${mailId}] Created local socialId for ${personUuid}: ${email}`)
ctx.info('Created local socialId', { mailId, personUuid, email })
}
const channel = await client.findOne(contact.class.Channel, {
attachedTo: person._id,
@@ -114,6 +123,6 @@ export async function ensureLocalPerson (
},
generateId()
)
console.log(`[${mailId}] Created channel for ${personUuid}: ${email}`)
ctx.info('Created channel', { mailId, personUuid, email })
}
}