Skip to content

Commit 096af4f

Browse files
authored
feat(imap): added support for imap trigger (#2663)
* feat(tools): added support for imap trigger * feat(imap): added parity, tested * ack PR comments * final cleanup
1 parent dc3de95 commit 096af4f

File tree

22 files changed

+1529
-26
lines changed

22 files changed

+1529
-26
lines changed

apps/docs/components/icons.tsx

Lines changed: 55 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,56 @@ export function MailIcon(props: SVGProps<SVGSVGElement>) {
295295
)
296296
}
297297

298+
export function MailServerIcon(props: SVGProps<SVGSVGElement>) {
299+
return (
300+
<svg
301+
{...props}
302+
width='24'
303+
height='24'
304+
viewBox='0 0 24 24'
305+
fill='none'
306+
xmlns='http://www.w3.org/2000/svg'
307+
>
308+
<rect
309+
x='3'
310+
y='4'
311+
width='18'
312+
height='16'
313+
rx='2'
314+
stroke='currentColor'
315+
strokeWidth='2'
316+
strokeLinecap='round'
317+
strokeLinejoin='round'
318+
/>
319+
<path
320+
d='M3 8L10.89 13.26C11.2187 13.4793 11.6049 13.5963 12 13.5963C12.3951 13.5963 12.7813 13.4793 13.11 13.26L21 8'
321+
stroke='currentColor'
322+
strokeWidth='2'
323+
strokeLinecap='round'
324+
strokeLinejoin='round'
325+
/>
326+
<line
327+
x1='7'
328+
y1='16'
329+
x2='7'
330+
y2='16'
331+
stroke='currentColor'
332+
strokeWidth='2'
333+
strokeLinecap='round'
334+
/>
335+
<line
336+
x1='10'
337+
y1='16'
338+
x2='10'
339+
y2='16'
340+
stroke='currentColor'
341+
strokeWidth='2'
342+
strokeLinecap='round'
343+
/>
344+
</svg>
345+
)
346+
}
347+
298348
export function CodeIcon(props: SVGProps<SVGSVGElement>) {
299349
return (
300350
<svg
@@ -4284,20 +4334,12 @@ export function RssIcon(props: SVGProps<SVGSVGElement>) {
42844334

42854335
export function SpotifyIcon(props: SVGProps<SVGSVGElement>) {
42864336
return (
4287-
<svg
4288-
{...props}
4289-
width='386'
4290-
height='386'
4291-
viewBox='100 100 186 186'
4292-
fill='none'
4293-
xmlns='http://www.w3.org/2000/svg'
4294-
xmlnsXlink='http://www.w3.org/1999/xlink'
4295-
>
4296-
<image
4297-
width='386'
4298-
height='386'
4299-
xlinkHref='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAYIAAAGCCAIAAABimFPeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAFapJREFUeNrs3U1IXOmex/Foab1oldUpX7rQocAgRIVLhCEFFwwD0QbpQNNCYMIszGz6buJqZmVWd6Wzuiuz6llMHGimIWC4MI3QVsNwa1XZ2AyoDdKBokuqk1iZii+lpRXnf+pJVySdxKryvJ/vhyB2376Jeeqc3/n/z3me51y6BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgGa1MAT4uNaI3z8Sk2/ah2OtXX71L/3yfcT/+//48Em+9v1RRvv+9atyebPAMIIYQr2CybjETdtAWILGNxCWb3T5bV/vlo83Cie5vZPtPYkn7ZvcHqMNYghv6ppAMq6+6hU69QeTVE+SSuWNgvwjnwUxBM9FjxQ+8vW9vZX5pHGTPCqlsoeZPB8QMQQ3N1yhiYT8MrPqaaJKkjw6SGVLq1lKJGII7ql9IjOjocmETQqf+klxJHm0v7zFh0gMwZGk5OmcHur8csjOtU+d9ZFURrtL6zxxI4bgpOZLK38mEi77e0kMSRjRrBFDsDUpf6L3xpxe/pxbHEkYSafGI39iCDbSGvFH7o66oP9qiCRR8cEaYUQMwRYBJC2Y424/E0YghmjBXEXatOLiGveMiCGYJ5iMx+bHCaCz1D0jCSOGghiCsSR6Ls8l3fcUTC/SnRXup5mKTQzBKNHZMS/fBqpfKZXdmUvTozmFjyFwBP9wrPfrzzo/H2wJ8JGdr/1KNHzn6ulRpfzjc0aDagj6FEHRe2OMQxOOMvmd+2meo1ENoXltA+G+pSkpghiKpgewc3ro+OfiydMio0EMoWFy/vQu3uRx2EUL/oBPclyGUSqj03KFAaEpQ11aI/7Lc0mJIYZCR+XNQmEuzfpYqiHU1Uf0fv1Z6MYAQ6Hzsd4T6rg1SINGDOEcwWS8b2mKRszQBk16gCMmFhFDeC91M4hH8iZkPbeK7HV5YAhs4vJcMjIzyjiYprxZeDazwhRHqiG80T0/Hr5zlXEw9dDvCQVvDBymcyQR1ZDX8VDMWpJBUhPx+IwY8nQG9S1N+YdjDAVJRFMGMsjDl+KAr+PW4OHfcpUXJUaDGCKDQBIRQzBL39efBa71Mg4kEbSrMkNgvu758UAyzjjYs0RlOydiyBMZxHMxkgjEkGUiM6NkkM35h2OSRIyDmbg3ZB4JoNif/8g4OOCs6Am1DYRLqSxDQQy57Rrb85d/YL2YYz6vkdjr3TJ7yBJDLmp9q3cc5BrLUDhI6MZAebPAriBmnCAMgQnYRNGhuhd4GRwx5ArR2TEezzu3jO1ZvMk40JQ5WzAZ754fZxwcfIb0hFq7/IfpHENBDDn1Wvrpt7e4Le10gWu93CSiKXOq7oVxJsLxUYIYskxoIsGb5t1U2EoSMQ40ZU5rx5amaMfcpP1KlNaMaogaHnysxBBoxzxf5F6eSzIOxBBHKqzUOT0UZBYYMWRzkbujzLt1t0+4zBBDdiYBxLvGXM8/HGO3FmLIvqL3xriF6QXSd/NBE0NcJGHpaRPxS/fNOOiF95Tp5tOHUzZcwlreLJy+0t5Kevgkr/7NUSb/9n/dKDTxzlI5Cf0jb98pUvtbSxCrGsELS3ll3LYnHvHGV2LIRoLJeN9DK3cOPcntVXJ7EjpyYqigOTwTN1aVh61dft9AuG0grBKqfSTmpl6m+GCtuLjGwU8MebQUkqw53ihIgaPSx/LEabSS8lWDSX45OpgoiIghz5VCUuNIsSOJc7xZkPRxzeipYJIQ11JpOOas6Q4URMSQXfQu3jRo2rTkTimVlQByUL1zQRJDEkaS7CqYKIiIIdR12vSv3tb3yC6tZg+q6ePx41sKJQkjm0cSBRExZD0dX38oofNyIbO/vMWovjeSQpOJ4PW4fLXV7SRpkLcnH/EBEUNWnhv9qdu6nBXSfz2bWaG8r4eaoiWNsE1uJO3cT3PxuAg2xLmQjluDnZ8PkkEmq7woHaZzu0vrpVS2slOSMGrtsrI+8kX8+4+JIaohi8SXv9DlnkV++q+SRIync+sj6cvc9PiSashJh350duziv4/U83vf/sR46lIfSZq3BnztV6Lm/wy8vYMYssAn//L3Z9c0NO3//i3DhVQvJ0+LB989lRZJOlwzmzVfT0hCkPEnhszWvTCuy27TO/fTDKa+1IoWyYWT7T1fxG9CpyZ5x07VxJDZQhOJsB7P6eXYpSMzzvFmQSqjoyd5iQnDO7WjSimVZcybCXGGoDkd7DbtHIeZ/PPZH7YnHxn6WD00ySFBDJlcDel0zNl/vYJrnOT2pP81Loy0CZZcnGjKTBNMxsP/eFWv3417CmbS1sqkstKmtVV3INH5N68+sGOQG9XGEDRTCul60ZP+zuh7CrWNylrO7FhW26WspqXL/97qrLZ32tmTuTbRSW02or5xyiO/Q2218IpcTmLz4zqGkRwYLxcynCCNYvpiM/SatVij4/RFObXUTmNt/dpXnwHX/HO92Xetut+jTfZg+4jo7Fj03phevxvzGIkhUwpIvZfUX7rAYg61c5j8SMHrcZtvIVbbH1J+2W2rNsnunsWbuoyeVENMICKGDNc5PdQ9P677b1tnEqm9L6Sxktxx+pbP8lc+3tBS6bi6nZs7Plbpr5/P/sBpQgwZS8edPd7xkY0+QhMJp+wEdpFUUhu8WbXRki47+cpP/kvyG04TYshY0pEZerdFzQCu3SqS3JFzw2tvxapFkpkTAvXazJeFysSQsSQO/i7zT4yDmSSJ1F6UJtz61eUaw+2hRvHAvjG6rGVFQ6QhVTMkpMSQjlVSybg8quT2Lh5DTEltFNMXG9M5PRT0wLsAbXqw9oRCNwYiM6OhyURLwHfyc/G0XNH9MhO41nvxHoN1go01GQwBFzonfgqX55LSHev+ThRdPl8OEpoyY7VzhNmvX5Mebf/x1u7D9Ys/X2vRaX8iSSLuUlMNGRbbjnqZn3c+lOi9MSmOui+2MkO95FqfyxX3EIkhg3BXyOY6p4f6V29Lp9bcJ6Xjkg4uVzRlRmnx2OQdR3dqR5l88cFa/ZOz1ab6ev0M3B6iGjIKT+sdJFCdiyiVUT2Fifw3fUtTep5XXLGohozKbMceW2qZu7YRx/abHTkqZ6benFsynO1xakv2tc1Dqtd8Oy+pVZXR7tJ6cXHtQzewdVzXWsO9IWLIsGrICZW2mm0scVPeKJzulnVZMlrPb6LtXtTlb69+DV6PW7LByIdEZkal4ZIw2l/eOjv1URKqYyJhxApBqqHGbncwBPXTZemjvuQKf7xROHySl9A53izYbacbtfmRJJRaGWeHH6lWCRr987DxENWQIWxSacvBrS37fJJXu2TYecRUGbV/pmJSmwTIV6tqpTazyjT5U4ghYkh/Flbaatm9aSs8DaL2PKudpRJGatckHm8TQ7C1/eWt/cdbdt5Etema7kT+atXNlVR91DGRCLhoZpaPbCWGXFAKSeFQmEt7YU2AqpJ2l9a1d+xMJqREkq9Ov8tLiUcM6c/kSUNN707taPL33Vcl0v0378XlzV/EECwjdZDXMugdpVRWfqn6KDIzyrxkYghmn4Gsz36nPpIeR03/YUqO+7CYw44OTNyA2SlOcnsvFzK/JL/ZuZ8mo6mGYLgK800+TBVHwWRc24aRO0fEEGCV6tuf82qnIYPe1wSaMk9jR5H6OzXp0bYnH7335W6gGkLzpOMw6P1c2rL432Ye/H6uYPB6/OwZrpbj17x+VT7+7aaMraZTqjAqPlijMiKGoBs5lz6yMUX9caOWuWtr3yP+RrfjCNR3/ldye/JzljcLKqTKGwWr5hmoMNpdWr88lwywT6azyn+GoN7AHgj3r9427Y9r9FXo8uO1D8fUu+0t3wBIvXVWcsGql9OHJhISRtbOY5bqTK4lnDjEkM4SG/9s5h+3v7wll/eP/AdqKZZ6t72dZ9NIJKndSExelxudHdNxe2liiBjyYgypRkOO5rP3X9XCdLUK1IkT+Wq7lJgTSZLUfUtTlgzUs7sr7luQTAx5MYbOtjmXqm/RctOaBokhtWLD0NNVRiy+/AUxZGe8PLqROw6TCV9PyJI/Wm3WZdWfbpDWLn/gWm/n9FDk7mj7YFQLpqdF3f+UyouSXG3Nf7nT/uMttj2jGtKfDTeBdZnXu+XSalb3/ZVMfrygZEf+gw+03g+IIWjoJGEQjK2PIn4pjuSXeh/0OzvYX6T7K28WWKNv38+dIagfKyrNbEKj98bUK1h1WTh2+qrMoUI15Ipq6BXVkNnUi8bUE0Pp15xSkJ5yqFANGeSYS5x1xVH3/Hh/6nZ0dqy5p+/tpm+eyadGDBlzbG1wbFl6sEb8WqfWeBgFTZ9jxTMyYsiwpmy3zF1qW4VRnf+XT+aSFM7EkIv6MgoiW4XR6u1zb2BLN2f+MzIKZ2KInt8r2gbCvYs3P3049d5VrBJVkkHmb/1B1dzw58gQeDOGjs7MDzx8Utdcwbb+t69dtnwR/1mBZFzKov3lrYNU9ri65Yh/JCZVklX751MyE0McYW+vycfV3X9qmwGpZkHHC3VtEzW1zlZ6H59Zb4j/PTXv0Q4jX2esgxhqvhqS09iGS9vVDmRyArz5xpRFlTIU6g96548LJuO+6nZraisSrx0kR6xoJYZMKIjscGqpTXy0r5buefj+cqB6Hu6fSaX24Zj990XS7aOhKSOGTCi5rYqh2k49dsudc1NJfu0urV+qbrsRmtQmRrt1hZeqlzlNGsIK+4ZZsn/N/vLWy4WMm45v9VbojupaDTcdHpK28klxmlANmXG5M7O5cOV2orW3Qqs8Cn855I67SGx11gS2PWtG4Fpv+5WoaY3Yx3ekdrrTcuV4s6Bt6/F4S7KpbSDc2uXg+0c7//o/nCDEkCmtbMDXYVYrIRW+EXsS2rM+OqreQpJ6s60nZO17NZpTSmUPvnvKCdJwh84QNHO0rWZN+7M8+PRXTuZf76448V2sB6ksZwcxZN5127Tp1J597KJef/hL8pvigzWnDIKZ1yeaMmh9WejGgDmlgbapuym0V7yOxNT2+7Vf1u5ZcVquSD24918/yTfys8mw2/aQkCuTmpSAhs8mhqDpM9acXdZ1eUymvT+6y19baVF7V30TS8NqTaJaslDeKJxKbWjKPCb5US/PJW37lnqp3RzXRRJDjqfXNsnnNmXPZlbq7wFVCWP+Ii+1hM2Ed0bLX0fCyIazjaR/ZOIiMWQ2uSx3z4+bU+0X5tIfSiLJGilqbLiAS35g9TJ7I17QGkzGY/Pj9nmaJr3z89kfOCmIIbNJudGfum3OPEY12U+94FQVO85aOKrCSH74kq7PkqQsisyM2uEvKBlU4jEZMWQJS3bVcjQ1OeggldXrNRt2SCKp9bYnH/HhNo0nZRdSye2F71xlHBq47gV87VeiHROJrq/+oFqqC07OPEzntO3NLJ14vfuf62zuQQxZF0MvSsFk3Inzfe3APxLr/HxQhYgEetPFkdalXuu18C9SuJ/m5jQxZHFf2+GuNeImkwySKJfGSlLp9YtSEzezpcKSOLPq59cW6D7mOf3FjgGG4OJHIS+l0kVoItH3cOrTh1ON3m6zuCNjyiLVkB1IQU5BpBfpsDqqu9lrE5Hqmy0Vnh6yqik7yuRf/fv/8qlRDdmiIOLWgO5h1D0/Hl/+InjejITWiN/Ch5XFB2t8WFRDdtES8AW9t/e74UdnT0giRgZWyqIPLayL/fmPFpZCxBAxZCPHG4WOzwcdvV+XnSuj8J2rMrblH5+flitn6yDJIAtLocL9NLcF9bmKMwR6MW1th2dJ51tazapFLWprfQvf8yGl0K93V/hQiCHb6V+9zRwij3h2d4Vtp/XCLWo98UoGj9hf3iKDiCGbKqWyTOr3Qm/InWliiIIIVtpdWufOtL54UqazyotSa5ff2iVOMI4E0Av2FaIasr/i4hpXS7cquPqdccSQq+4d0Jq5EnemacocVbo/LfpHYqa92RXmXF2ef/X92fmToBqyu5059qDhAwUxZPXFk3uZrrG7tM5W0zRlzmzNcns8NXMB9WYU2jFiyKkO07nQZMLXE2IoHF3Vnmzz6JOmzMnkIOaegnO9XMjU/65KUA3Z93J6/HPRws2S0bT95S3WbRBDLqG9A6flEvuiOYsUQc//9D3jQAy5x1H1bav+kRhD4ZQaNj/9V25LE0MuTKLgjQFuVzsig57NrHBb2jTcojb74OZ+p/3tzKX5mKiGXEuK/PKPzztuDbYEGHm7ZtD99MF3TxkHYsjNKi9Kh3/LkUS2zaD9ZV7BSlPmAVLwS3fGZCK70V4DTQZRDVETwcIM2mEvIWKIJAIZ5E28IMhi/uFY39KUhe/bQvHBWnGRqdLEEElEElmEe9I0ZXjbnfnHepnZSAZRDcFKUg1JTSSVEUNhAqaSUg3hPU7LlYPvnkpBxLozo0n6vJj9gQwihvD+JCqlsnKhDt0YYDQMcpTJP//qe9aL0ZThHMFkvGfxJjetdcdDMWIIDWgbCEsScatIL1Jj7syl2daepgyNnTZ73/7EZml6NWK/3vnvY24GUQ2h6QYtNj8uxRFD0ZyXC5ndpXXGgWoIzTvJ7e0vb7UEfLxoqFHaLq5ffU8jRjUEyiJrWlqpgLgbTTUEQ8qi03KFu0Ufpz2S/xNFENUQjCQF0eW5ZGgiwVD8PqlfLmQIIGII5vVon8wleaJPF0YMwWKd00PRe2NevmGkAmj34Tq7WRJDIIwIIBBDhNH0UGRm1AttGgFEDMHWgsm4hJFbb2Cf5PaKD9bYJIgYggNIgybFUeeXQ67p1LTXZjzeOszk+XCJITiMlEUdE4nQZMKh6/XLmwXpv0qrWfovYgjOJhkkSSR5FEjGHZFHkj5S/pRSWenC+PiIIbiwPgom4/LVbv2a1DtHmfxBKkvtQwzBKySGpDgKXo/LV6siSUXPYSYvX9mVlRiC17s2/0hM8sg/HPMNhI176i9NViW3d/gkX94oHG8W6LlADOFjtZL8ah+OtXb52/rDqlxqH4nVeWvp6LdHWpI4tX/kORcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwH3+X4ABAMfYGSMKaT32AAAAAElFTkSuQmCC'
4337+
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 496 512'>
4338+
<path
4339+
fill='#1ed760'
4340+
d='M248 8C111.1 8 0 119.1 0 256s111.1 248 248 248 248-111.1 248-248S384.9 8 248 8Z'
43004341
/>
4342+
<path d='M406.6 231.1c-5.2 0-8.4-1.3-12.9-3.9-71.2-42.5-198.5-52.7-280.9-29.7-3.6 1-8.1 2.6-12.9 2.6-13.2 0-23.3-10.3-23.3-23.6 0-13.6 8.4-21.3 17.4-23.9 35.2-10.3 74.6-15.2 117.5-15.2 73 0 149.5 15.2 205.4 47.8 7.8 4.5 12.9 10.7 12.9 22.6 0 13.6-11 23.3-23.2 23.3zm-31 76.2c-5.2 0-8.7-2.3-12.3-4.2-62.5-37-155.7-51.9-238.6-29.4-4.8 1.3-7.4 2.6-11.9 2.6-10.7 0-19.4-8.7-19.4-19.4s5.2-17.8 15.5-20.7c27.8-7.8 56.2-13.6 97.8-13.6 64.9 0 127.6 16.1 177 45.5 8.1 4.8 11.3 11 11.3 19.7-.1 10.8-8.5 19.5-19.4 19.5zm-26.9 65.6c-4.2 0-6.8-1.3-10.7-3.6-62.4-37.6-135-39.2-206.7-24.5-3.9 1-9 2.6-11.9 2.6-9.7 0-15.8-7.7-15.8-15.8 0-10.3 6.1-15.2 13.6-16.8 81.9-18.1 165.6-16.5 237 26.2 6.1 3.9 9.7 7.4 9.7 16.5s-7.1 15.4-15.2 15.4z' />
43014343
</svg>
43024344
)
43034345
}

apps/docs/components/ui/icon-mapping.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ import {
5858
LinkupIcon,
5959
MailchimpIcon,
6060
MailgunIcon,
61+
MailServerIcon,
6162
Mem0Icon,
6263
MicrosoftExcelIcon,
6364
MicrosoftOneDriveIcon,
@@ -165,6 +166,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
165166
huggingface: HuggingFaceIcon,
166167
hunter: HunterIOIcon,
167168
image_generator: ImageIcon,
169+
imap: MailServerIcon,
168170
incidentio: IncidentioIcon,
169171
intercom: IntercomIcon,
170172
jina: JinaAIIcon,
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
title: IMAP Email
3+
description: Trigger workflows when new emails arrive via IMAP (works with any email provider)
4+
---
5+
6+
import { BlockInfoCard } from "@/components/ui/block-info-card"
7+
8+
<BlockInfoCard
9+
type="imap"
10+
color="#6366F1"
11+
/>
12+
13+
{/* MANUAL-CONTENT-START:intro */}
14+
The IMAP Email trigger allows your Sim workflows to start automatically whenever a new email is received in any mailbox that supports the IMAP protocol. This works with Gmail, Outlook, Yahoo, and most other email providers.
15+
16+
With the IMAP trigger, you can:
17+
18+
- **Automate email processing**: Start workflows in real time when new messages arrive in your inbox.
19+
- **Filter by sender, subject, or folder**: Configure your trigger to react only to emails that match certain conditions.
20+
- **Extract and process attachments**: Automatically download and use file attachments in your automated flows.
21+
- **Parse and use email content**: Access the subject, sender, recipients, full body, and other metadata in downstream workflow steps.
22+
- **Integrate with any email provider**: Works with any service that provides standard IMAP access, without vendor lock-in.
23+
- **Trigger on unread, flagged, or custom criteria**: Set up advanced filters for the kinds of emails that start your workflows.
24+
25+
With Sim, the IMAP integration gives you the power to turn email into an actionable source of automation. Respond to customer inquiries, process notifications, kick off data pipelines, and more—directly from your email inbox, with no manual intervention.
26+
{/* MANUAL-CONTENT-END */}
27+
28+
29+
## Usage Instructions
30+
31+
Connect to any email server via IMAP protocol to trigger workflows when new emails are received. Supports Gmail, Outlook, Yahoo, and any other IMAP-compatible email provider.
32+
33+
34+
35+
36+
37+
## Notes
38+
39+
- Category: `triggers`
40+
- Type: `imap`

apps/docs/content/docs/en/tools/meta.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"huggingface",
4343
"hunter",
4444
"image_generator",
45+
"imap",
4546
"incidentio",
4647
"intercom",
4748
"jina",
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import { createLogger } from '@sim/logger'
2+
import { ImapFlow } from 'imapflow'
3+
import { type NextRequest, NextResponse } from 'next/server'
4+
import { getSession } from '@/lib/auth'
5+
6+
const logger = createLogger('ImapMailboxesAPI')
7+
8+
interface ImapMailboxRequest {
9+
host: string
10+
port: number
11+
secure: boolean
12+
rejectUnauthorized: boolean
13+
username: string
14+
password: string
15+
}
16+
17+
export async function POST(request: NextRequest) {
18+
const session = await getSession()
19+
if (!session?.user?.id) {
20+
return NextResponse.json({ success: false, message: 'Unauthorized' }, { status: 401 })
21+
}
22+
23+
try {
24+
const body = (await request.json()) as ImapMailboxRequest
25+
const { host, port, secure, rejectUnauthorized, username, password } = body
26+
27+
if (!host || !username || !password) {
28+
return NextResponse.json(
29+
{ success: false, message: 'Missing required fields: host, username, password' },
30+
{ status: 400 }
31+
)
32+
}
33+
34+
const client = new ImapFlow({
35+
host,
36+
port: port || 993,
37+
secure: secure ?? true,
38+
auth: {
39+
user: username,
40+
pass: password,
41+
},
42+
tls: {
43+
rejectUnauthorized: rejectUnauthorized ?? true,
44+
},
45+
logger: false,
46+
})
47+
48+
try {
49+
await client.connect()
50+
51+
const listResult = await client.list()
52+
const mailboxes = listResult.map((mailbox) => ({
53+
path: mailbox.path,
54+
name: mailbox.name,
55+
delimiter: mailbox.delimiter,
56+
}))
57+
58+
await client.logout()
59+
60+
mailboxes.sort((a, b) => {
61+
if (a.path === 'INBOX') return -1
62+
if (b.path === 'INBOX') return 1
63+
return a.path.localeCompare(b.path)
64+
})
65+
66+
return NextResponse.json({
67+
success: true,
68+
mailboxes,
69+
})
70+
} catch (error) {
71+
try {
72+
await client.logout()
73+
} catch {
74+
// Ignore logout errors
75+
}
76+
throw error
77+
}
78+
} catch (error) {
79+
const errorMessage = error instanceof Error ? error.message : 'Unknown error'
80+
logger.error('Error fetching IMAP mailboxes:', errorMessage)
81+
82+
let userMessage = 'Failed to connect to IMAP server'
83+
if (
84+
errorMessage.includes('AUTHENTICATIONFAILED') ||
85+
errorMessage.includes('Invalid credentials')
86+
) {
87+
userMessage = 'Invalid username or password. For Gmail, use an App Password.'
88+
} else if (errorMessage.includes('ENOTFOUND') || errorMessage.includes('getaddrinfo')) {
89+
userMessage = 'Could not find IMAP server. Please check the hostname.'
90+
} else if (errorMessage.includes('ECONNREFUSED')) {
91+
userMessage = 'Connection refused. Please check the port and SSL settings.'
92+
} else if (errorMessage.includes('certificate') || errorMessage.includes('SSL')) {
93+
userMessage =
94+
'TLS/SSL error. Try disabling "Verify TLS Certificate" for self-signed certificates.'
95+
} else if (errorMessage.includes('timeout')) {
96+
userMessage = 'Connection timed out. Please check your network and server settings.'
97+
}
98+
99+
return NextResponse.json({ success: false, message: userMessage }, { status: 500 })
100+
}
101+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { createLogger } from '@sim/logger'
2+
import { nanoid } from 'nanoid'
3+
import { type NextRequest, NextResponse } from 'next/server'
4+
import { verifyCronAuth } from '@/lib/auth/internal'
5+
import { acquireLock, releaseLock } from '@/lib/core/config/redis'
6+
import { pollImapWebhooks } from '@/lib/webhooks/imap-polling-service'
7+
8+
const logger = createLogger('ImapPollingAPI')
9+
10+
export const dynamic = 'force-dynamic'
11+
export const maxDuration = 180 // Allow up to 3 minutes for polling to complete
12+
13+
const LOCK_KEY = 'imap-polling-lock'
14+
const LOCK_TTL_SECONDS = 180 // Same as maxDuration (3 min)
15+
16+
export async function GET(request: NextRequest) {
17+
const requestId = nanoid()
18+
logger.info(`IMAP webhook polling triggered (${requestId})`)
19+
20+
let lockValue: string | undefined
21+
22+
try {
23+
const authError = verifyCronAuth(request, 'IMAP webhook polling')
24+
if (authError) {
25+
return authError
26+
}
27+
28+
lockValue = requestId // unique value to identify the holder
29+
const locked = await acquireLock(LOCK_KEY, lockValue, LOCK_TTL_SECONDS)
30+
31+
if (!locked) {
32+
return NextResponse.json(
33+
{
34+
success: true,
35+
message: 'Polling already in progress – skipped',
36+
requestId,
37+
status: 'skip',
38+
},
39+
{ status: 202 }
40+
)
41+
}
42+
43+
const results = await pollImapWebhooks()
44+
45+
return NextResponse.json({
46+
success: true,
47+
message: 'IMAP polling completed',
48+
requestId,
49+
status: 'completed',
50+
...results,
51+
})
52+
} catch (error) {
53+
logger.error(`Error during IMAP polling (${requestId}):`, error)
54+
return NextResponse.json(
55+
{
56+
success: false,
57+
message: 'IMAP polling failed',
58+
error: error instanceof Error ? error.message : 'Unknown error',
59+
requestId,
60+
},
61+
{ status: 500 }
62+
)
63+
} finally {
64+
if (lockValue) {
65+
await releaseLock(LOCK_KEY, lockValue).catch(() => {})
66+
}
67+
}
68+
}

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/dropdown/dropdown.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,13 @@ export function Dropdown({
113113
const value = isPreview ? previewValue : propValue !== undefined ? propValue : storeValue
114114

115115
const singleValue = multiSelect ? null : (value as string | null | undefined)
116-
const multiValues = multiSelect ? (value as string[] | null | undefined) || [] : null
116+
const multiValues = multiSelect
117+
? Array.isArray(value)
118+
? value
119+
: value
120+
? [value as string]
121+
: []
122+
: null
117123

118124
const fetchOptionsIfNeeded = useCallback(async () => {
119125
if (!fetchOptions || isPreview || disabled) return

0 commit comments

Comments
 (0)