feat: add test email and improve on email notifications (#3018)
This commit is contained in:
parent
9bd037b6e7
commit
f61471b0f2
7 changed files with 111 additions and 30 deletions
|
|
@ -24,7 +24,7 @@ from lnbits.core.models.notifications import (
|
||||||
)
|
)
|
||||||
from lnbits.core.services.nostr import fetch_nip5_details, send_nostr_dm
|
from lnbits.core.services.nostr import fetch_nip5_details, send_nostr_dm
|
||||||
from lnbits.core.services.websockets import websocket_manager
|
from lnbits.core.services.websockets import websocket_manager
|
||||||
from lnbits.helpers import check_callback_url
|
from lnbits.helpers import check_callback_url, is_valid_email_address
|
||||||
from lnbits.settings import settings
|
from lnbits.settings import settings
|
||||||
from lnbits.utils.nostr import normalize_private_key
|
from lnbits.utils.nostr import normalize_private_key
|
||||||
|
|
||||||
|
|
@ -111,41 +111,56 @@ async def send_telegram_message(token: str, chat_id: str, message: str) -> dict:
|
||||||
return response.json()
|
return response.json()
|
||||||
|
|
||||||
|
|
||||||
async def send_email_notification(message: str) -> dict:
|
async def send_email_notification(
|
||||||
await send_email(
|
message: str, subject: str = "LNbits Notification"
|
||||||
settings.lnbits_email_notifications_server,
|
) -> dict:
|
||||||
settings.lnbits_email_notifications_port,
|
if not settings.lnbits_email_notifications_enabled:
|
||||||
settings.lnbits_email_notifications_password,
|
return {"status": "error", "message": "Email notifications are disabled"}
|
||||||
settings.lnbits_email_notifications_email,
|
try:
|
||||||
settings.lnbits_email_notifications_to_emails,
|
await send_email(
|
||||||
"LNbits Notification",
|
settings.lnbits_email_notifications_server,
|
||||||
message,
|
settings.lnbits_email_notifications_port,
|
||||||
)
|
settings.lnbits_email_notifications_username,
|
||||||
return {"status": "ok"}
|
settings.lnbits_email_notifications_password,
|
||||||
|
settings.lnbits_email_notifications_email,
|
||||||
|
settings.lnbits_email_notifications_to_emails,
|
||||||
|
subject,
|
||||||
|
message,
|
||||||
|
)
|
||||||
|
return {"status": "ok"}
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error sending email notification: {e}")
|
||||||
|
return {"status": "error", "message": str(e)}
|
||||||
|
|
||||||
|
|
||||||
async def send_email(
|
async def send_email(
|
||||||
server: str,
|
server: str,
|
||||||
port: int,
|
port: int,
|
||||||
|
username: str,
|
||||||
password: str,
|
password: str,
|
||||||
from_email: str,
|
from_email: str,
|
||||||
to_emails: list,
|
to_emails: list[str],
|
||||||
subject: str,
|
subject: str,
|
||||||
message: str,
|
message: str,
|
||||||
):
|
) -> bool:
|
||||||
|
if not is_valid_email_address(from_email):
|
||||||
|
raise ValueError(f"Invalid from email address: {from_email}")
|
||||||
|
if len(to_emails) == 0:
|
||||||
|
raise ValueError("No email addresses provided")
|
||||||
|
for email in to_emails:
|
||||||
|
if not is_valid_email_address(email):
|
||||||
|
raise ValueError(f"Invalid email address: {email}")
|
||||||
msg = MIMEMultipart()
|
msg = MIMEMultipart()
|
||||||
msg["From"] = from_email
|
msg["From"] = from_email
|
||||||
msg["To"] = ", ".join(to_emails)
|
msg["To"] = ", ".join(to_emails)
|
||||||
msg["Subject"] = subject
|
msg["Subject"] = subject
|
||||||
msg.attach(MIMEText(message, "plain"))
|
msg.attach(MIMEText(message, "plain"))
|
||||||
try:
|
username = username if len(username) > 0 else from_email
|
||||||
with smtplib.SMTP(server, port) as smtp_server:
|
with smtplib.SMTP(server, port) as smtp_server:
|
||||||
smtp_server.starttls()
|
smtp_server.starttls()
|
||||||
smtp_server.login(from_email, password)
|
smtp_server.login(username, password)
|
||||||
smtp_server.sendmail(from_email, to_emails, msg.as_string())
|
smtp_server.sendmail(from_email, to_emails, msg.as_string())
|
||||||
logger.debug(f"Emails sent successfully to: {', '.join(to_emails)}")
|
return True
|
||||||
except Exception as e:
|
|
||||||
logger.debug(f"Failed to send email: {e}")
|
|
||||||
|
|
||||||
|
|
||||||
def is_message_type_enabled(message_type: NotificationType) -> bool:
|
def is_message_type_enabled(message_type: NotificationType) -> bool:
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,6 @@
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<q-separator></q-separator>
|
<q-separator></q-separator>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<strong v-text="$t('notifications_email_config')"></strong>
|
<strong v-text="$t('notifications_email_config')"></strong>
|
||||||
<q-item tag="label" v-ripple>
|
<q-item tag="label" v-ripple>
|
||||||
|
|
@ -194,6 +193,24 @@
|
||||||
/>
|
/>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
|
<q-item tag="label" v-ripple>
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label
|
||||||
|
v-text="$t('notifications_send_email_username')"
|
||||||
|
></q-item-label>
|
||||||
|
<q-item-label
|
||||||
|
caption
|
||||||
|
v-text="$t('notifications_send_email_username_desc')"
|
||||||
|
></q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section>
|
||||||
|
<q-input
|
||||||
|
:type="hideInputToggle ? 'password' : 'text'"
|
||||||
|
filled
|
||||||
|
v-model="formData.lnbits_email_notifications_username"
|
||||||
|
/>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
<q-item tag="label" v-ripple>
|
<q-item tag="label" v-ripple>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label
|
<q-item-label
|
||||||
|
|
@ -212,7 +229,19 @@
|
||||||
/>
|
/>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section>
|
||||||
|
<q-btn
|
||||||
|
@click="sendTestEmail()"
|
||||||
|
label="Send Test Email Notification"
|
||||||
|
color="primary"
|
||||||
|
class="q-mt-md"
|
||||||
|
></q-btn>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col">
|
||||||
<q-item tag="label" v-ripple>
|
<q-item tag="label" v-ripple>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label
|
<q-item-label
|
||||||
|
|
@ -249,9 +278,6 @@
|
||||||
</div>
|
</div>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col">
|
|
||||||
<q-item tag="label" v-ripple>
|
<q-item tag="label" v-ripple>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label
|
<q-item-label
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ from lnbits.core.services import (
|
||||||
get_balance_delta,
|
get_balance_delta,
|
||||||
update_cached_settings,
|
update_cached_settings,
|
||||||
)
|
)
|
||||||
|
from lnbits.core.services.notifications import send_email_notification
|
||||||
from lnbits.core.services.settings import dict_to_settings
|
from lnbits.core.services.settings import dict_to_settings
|
||||||
from lnbits.decorators import check_admin, check_super_user
|
from lnbits.decorators import check_admin, check_super_user
|
||||||
from lnbits.server import server_restart
|
from lnbits.server import server_restart
|
||||||
|
|
@ -50,6 +51,18 @@ async def api_monitor():
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@admin_router.get(
|
||||||
|
"/api/v1/testemail",
|
||||||
|
name="TestEmail",
|
||||||
|
description="send a test email to the admin",
|
||||||
|
dependencies=[Depends(check_admin)],
|
||||||
|
)
|
||||||
|
async def api_test_email():
|
||||||
|
return await send_email_notification(
|
||||||
|
"This is a LNbits test email.", "LNbits Test Email"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@admin_router.get("/api/v1/settings", response_model=Optional[AdminSettings])
|
@admin_router.get("/api/v1/settings", response_model=Optional[AdminSettings])
|
||||||
async def api_get_settings(
|
async def api_get_settings(
|
||||||
user: User = Depends(check_admin),
|
user: User = Depends(check_admin),
|
||||||
|
|
|
||||||
|
|
@ -396,6 +396,7 @@ class NotificationsSettings(LNbitsSettings):
|
||||||
lnbits_telegram_notifications_chat_id: str = Field(default="")
|
lnbits_telegram_notifications_chat_id: str = Field(default="")
|
||||||
lnbits_email_notifications_enabled: bool = Field(default=False)
|
lnbits_email_notifications_enabled: bool = Field(default=False)
|
||||||
lnbits_email_notifications_email: str = Field(default="")
|
lnbits_email_notifications_email: str = Field(default="")
|
||||||
|
lnbits_email_notifications_username: str = Field(default="")
|
||||||
lnbits_email_notifications_password: str = Field(default="")
|
lnbits_email_notifications_password: str = Field(default="")
|
||||||
lnbits_email_notifications_server: str = Field(default="smtp.protonmail.ch")
|
lnbits_email_notifications_server: str = Field(default="smtp.protonmail.ch")
|
||||||
lnbits_email_notifications_port: int = Field(default=587)
|
lnbits_email_notifications_port: int = Field(default=587)
|
||||||
|
|
|
||||||
2
lnbits/static/bundle.min.js
vendored
2
lnbits/static/bundle.min.js
vendored
File diff suppressed because one or more lines are too long
|
|
@ -198,9 +198,13 @@ window.localisation.en = {
|
||||||
|
|
||||||
notifications_email_config: 'Email Configuration',
|
notifications_email_config: 'Email Configuration',
|
||||||
notifications_enable_email: 'Enable Email',
|
notifications_enable_email: 'Enable Email',
|
||||||
notifications_enable_email_desc: 'Send notfications over Email',
|
notifications_enable_email_desc: 'Send notfications over email',
|
||||||
|
notifications_send_test_email: 'Send test email',
|
||||||
notifications_send_email: 'Send email',
|
notifications_send_email: 'Send email',
|
||||||
notifications_send_email_desc: 'Email you will send from',
|
notifications_send_email_desc: 'Email you will send from',
|
||||||
|
notifications_send_email_username: 'Username',
|
||||||
|
notifications_send_email_username_desc:
|
||||||
|
'Username, will use the email if not set',
|
||||||
notifications_send_email_password: 'Send email password',
|
notifications_send_email_password: 'Send email password',
|
||||||
notifications_send_email_password_desc:
|
notifications_send_email_password_desc:
|
||||||
'Password for the email you will send from',
|
'Password for the email you will send from',
|
||||||
|
|
|
||||||
|
|
@ -438,7 +438,7 @@ window.AdminPageLogic = {
|
||||||
LNbits.api
|
LNbits.api
|
||||||
.request('GET', '/admin/api/v1/restart/')
|
.request('GET', '/admin/api/v1/restart/')
|
||||||
.then(response => {
|
.then(response => {
|
||||||
Quasar.Notify.create({
|
this.$q.notify({
|
||||||
type: 'positive',
|
type: 'positive',
|
||||||
message: 'Success! Restarted Server',
|
message: 'Success! Restarted Server',
|
||||||
icon: null
|
icon: null
|
||||||
|
|
@ -450,7 +450,29 @@ window.AdminPageLogic = {
|
||||||
formatDate(date) {
|
formatDate(date) {
|
||||||
return moment(date * 1000).fromNow()
|
return moment(date * 1000).fromNow()
|
||||||
},
|
},
|
||||||
|
sendTestEmail() {
|
||||||
|
LNbits.api
|
||||||
|
.request(
|
||||||
|
'GET',
|
||||||
|
'/admin/api/v1/testemail',
|
||||||
|
this.g.user.wallets[0].adminkey
|
||||||
|
)
|
||||||
|
.then(response => {
|
||||||
|
if (response.data.status === 'error') {
|
||||||
|
throw new Error(response.data.message)
|
||||||
|
}
|
||||||
|
this.$q.notify({
|
||||||
|
message: 'Test email sent!',
|
||||||
|
color: 'positive'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
this.$q.notify({
|
||||||
|
message: error.message,
|
||||||
|
color: 'negative'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
getAudit() {
|
getAudit() {
|
||||||
LNbits.api
|
LNbits.api
|
||||||
.request('GET', '/admin/api/v1/audit', this.g.user.wallets[0].adminkey)
|
.request('GET', '/admin/api/v1/audit', this.g.user.wallets[0].adminkey)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue