add /ws/2/notify/ socket

This commit is contained in:
j3d1 2023-11-27 01:38:43 +01:00
parent 97503e91e0
commit 101fa7b69d
10 changed files with 138 additions and 81 deletions

View file

@ -1,50 +0,0 @@
import asyncio
import logging
from helper import create_task
def make_reply(message, to, subject):
from email.message import EmailMessage
from core.settings import MAIL_DOMAIN
reply = EmailMessage()
reply["From"] = "noreply@" + MAIL_DOMAIN
reply["To"] = to
reply["Subject"] = subject
reply.set_content(message)
return reply
async def send_smtp(message):
await asyncio.sleep(30)
import aiosmtplib
log = logging.getLogger('mail.log')
log.info('Sending message to %s' % message['To'])
await aiosmtplib.send(message, hostname="127.0.0.1", port=25, use_tls=False, start_tls=False)
class LMTPHandler:
async def handle_RCPT(self, server, session, envelope, address, rcpt_options):
from core.settings import MAIL_DOMAIN
if not address.endswith('@' + MAIL_DOMAIN):
return '550 not relaying to that domain'
envelope.rcpt_tos.append(address)
return '250 OK'
async def handle_DATA(self, server, session, envelope):
log = logging.getLogger('mail.log')
log.info('Message from %s' % envelope.mail_from)
log.info('Message for %s' % envelope.rcpt_tos)
log.info('Message data:\n')
for ln in envelope.content.decode('utf8', errors='replace').splitlines():
log.info(f'> {ln}'.strip())
log.info('End of message')
create_task(send_smtp(make_reply("Thank you for your message.", envelope.mail_from, 'Message received')))
# asyncio.create_task(send_reply())
return '250 Message accepted for delivery'

View file

69
core/mail/protocol.py Normal file
View file

@ -0,0 +1,69 @@
import logging
import aiosmtplib
def make_reply(message, to, subject):
from email.message import EmailMessage
from core.settings import MAIL_DOMAIN
reply = EmailMessage()
reply["From"] = "noreply@" + MAIL_DOMAIN
reply["To"] = to
reply["Subject"] = subject
reply.set_content(message)
return reply
async def send_smtp(message, log):
log.info('Sending message to %s' % message['To'])
await aiosmtplib.send(message, hostname="127.0.0.1", port=25, use_tls=False, start_tls=False)
class LMTPHandler:
async def handle_RCPT(self, server, session, envelope, address, rcpt_options):
from core.settings import MAIL_DOMAIN
if not address.endswith('@' + MAIL_DOMAIN):
return '550 not relaying to that domain'
envelope.rcpt_tos.append(address)
return '250 OK'
async def handle_DATA(self, server, session, envelope):
import email
log = logging.getLogger('mail.log')
log.info('Message from %s' % envelope.mail_from)
log.info('Message for %s' % envelope.rcpt_tos)
log.info('Message data:\n')
try:
parsed = email.message_from_bytes(envelope.content)
body = ""
if parsed.is_multipart():
for part in parsed.walk():
ctype = part.get_content_type()
cdispo = str(part.get('Content-Disposition'))
# skip any text/plain (txt) attachments
if ctype == 'text/plain' and 'attachment' not in cdispo:
body = part.get_payload(decode=True)
else:
log.info("Attachment", ctype, cdispo)
else:
body = parsed.get_payload(decode=True)
log.info(body)
header_from = parsed.get('From')
header_to = parsed.get('To')
if header_from != envelope.mail_from:
log.warning("Header from does not match envelope from")
if header_to != envelope.rcpt_tos[0]:
log.warning("Header to does not match envelope to")
await send_smtp(make_reply("Thank you for your message.", envelope.mail_from, 'Message received'), log)
log.info("Sent reply")
return '250 Message accepted for delivery'
except Exception as e:
log.error(e)
return '550 Message rejected'

View file

View file

View file

@ -10,8 +10,9 @@ import uvicorn
django.setup() django.setup()
from helper import init_loop from helper import init_loop
from lmtp.protocol import LMTPHandler from mail.protocol import LMTPHandler
from lmtp.socket import UnixSocketLMTPController from mail.socket import UnixSocketLMTPController
# async def handle_echo(reader, writer): # async def handle_echo(reader, writer):

View file

@ -24,7 +24,9 @@ export default {
components: {Toast, Navbar, AddItemModal}, components: {Toast, Navbar, AddItemModal},
computed: mapState(['loadedItems', 'layout', 'toasts']), computed: mapState(['loadedItems', 'layout', 'toasts']),
data: () => ({ data: () => ({
addModalOpen: false addModalOpen: false,
notify_socket: null,
socket_toast: null,
}), }),
methods: { methods: {
...mapMutations(['removeToast', 'createToast']), ...mapMutations(['removeToast', 'createToast']),
@ -33,35 +35,68 @@ export default {
}, },
closeAddModal() { closeAddModal() {
this.addModalOpen = false; this.addModalOpen = false;
} },
tryConnect() {
if (!this.notify_socket || this.notify_socket.readyState !== WebSocket.OPEN) {
if (this.socket_toast) {
this.removeToast(this.socket_toast.key);
this.socket_toast = null;
}
this.socket_toast = this.createToast({
title: "Connecting...",
message: "Connecting to websocket...",
color: "warning"
});
this.notify_socket = new WebSocket('wss://' + window.location.host + '/ws/2/notify/');
this.notify_socket.onopen = (e) => {
if (this.socket_toast) {
this.removeToast(this.socket_toast.key);
this.socket_toast = null;
}
this.socket_toast = this.createToast({
title: "Connection established",
message: JSON.stringify(e),
color: "success"
});
};
this.notify_socket.onclose = (e) => {
if (this.socket_toast) {
this.removeToast(this.socket_toast.key);
this.socket_toast = null;
}
this.socket_toast = this.createToast({
title: "Connection closed",
message: JSON.stringify(e),
color: "danger"
});
setTimeout(() => {
this.tryConnect();
}, 1000);
};
this.notify_socket.onerror = (e) => {
if (this.socket_toast) {
this.removeToast(this.socket_toast.key);
this.socket_toast = null;
}
this.socket_toast = this.createToast({
title: "Connection error",
message: JSON.stringify(e),
color: "danger"
});
setTimeout(() => {
this.tryConnect();
}, 1000);
};
this.notify_socket.onmessage = (e) => {
let data = JSON.parse(e.data);
console.log(data);
}
}
},
}, },
created: function () { created: function () {
this.notify_socket = new WebSocket('wss://' + window.location.host + '/ws/notify/'); this.tryConnect();
this.notify_socket.onmessage = (e) => {
let data = JSON.parse(e.data);
console.log(data, e.data);
};
this.notify_socket.onopen = (e) => {
this.createToast({
title: "Connection established",
message: JSON.stringify(e),
color: "success"
});
};
this.notify_socket.onclose = (e) => {
this.createToast({
title: "Connection closed",
message: JSON.stringify(e),
color: "danger"
});
};
this.notify_socket.onerror = (e) => {
this.createToast({
title: "Connection error",
message: JSON.stringify(e),
color: "danger"
});
};
} }
}; };
</script> </script>

View file

@ -84,8 +84,10 @@ const store = new Vuex.Store({
state.loadedItems.push(item); state.loadedItems.push(item);
}, },
createToast(state, {title, message, color}) { createToast(state, {title, message, color}) {
state.toasts.push({title, message, color, key: state.keyIncrement}); var toast = {title, message, color, key: state.keyIncrement}
state.toasts.push(toast);
state.keyIncrement += 1; state.keyIncrement += 1;
return toast;
}, },
removeToast(state, key) { removeToast(state, key) {
state.toasts = state.toasts.filter(toast => toast.key !== key); state.toasts = state.toasts.filter(toast => toast.key !== key);