From cb475d7f8840b03ad259c674dff4d0a345ebddfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Tue, 14 Feb 2023 10:50:25 +0100 Subject: [PATCH] remove smtp --- lnbits/extensions/smtp/README.md | 14 - lnbits/extensions/smtp/__init__.py | 34 - lnbits/extensions/smtp/config.json | 6 - lnbits/extensions/smtp/crud.py | 158 ----- lnbits/extensions/smtp/migrations.py | 39 -- lnbits/extensions/smtp/models.py | 47 -- lnbits/extensions/smtp/smtp.py | 110 ---- .../smtp/static/smtp-bitcoin-email.png | Bin 18854 -> 0 bytes lnbits/extensions/smtp/tasks.py | 36 -- .../smtp/templates/smtp/_api_docs.html | 23 - .../smtp/templates/smtp/display.html | 175 ----- .../extensions/smtp/templates/smtp/index.html | 604 ------------------ lnbits/extensions/smtp/views.py | 40 -- lnbits/extensions/smtp/views_api.py | 190 ------ 14 files changed, 1476 deletions(-) delete mode 100644 lnbits/extensions/smtp/README.md delete mode 100644 lnbits/extensions/smtp/__init__.py delete mode 100644 lnbits/extensions/smtp/config.json delete mode 100644 lnbits/extensions/smtp/crud.py delete mode 100644 lnbits/extensions/smtp/migrations.py delete mode 100644 lnbits/extensions/smtp/models.py delete mode 100644 lnbits/extensions/smtp/smtp.py delete mode 100644 lnbits/extensions/smtp/static/smtp-bitcoin-email.png delete mode 100644 lnbits/extensions/smtp/tasks.py delete mode 100644 lnbits/extensions/smtp/templates/smtp/_api_docs.html delete mode 100644 lnbits/extensions/smtp/templates/smtp/display.html delete mode 100644 lnbits/extensions/smtp/templates/smtp/index.html delete mode 100644 lnbits/extensions/smtp/views.py delete mode 100644 lnbits/extensions/smtp/views_api.py diff --git a/lnbits/extensions/smtp/README.md b/lnbits/extensions/smtp/README.md deleted file mode 100644 index 5b7757e2..00000000 --- a/lnbits/extensions/smtp/README.md +++ /dev/null @@ -1,14 +0,0 @@ -

SMTP Extension

- -This extension allows you to setup a smtp, to offer sending emails with it for a small fee. - -## Requirements - -- SMTP Server - -## Usage - -1. Create new emailaddress -2. Verify if email goes to your testemail. Testmail is sent on create and update -3. Share the link with the email form. - diff --git a/lnbits/extensions/smtp/__init__.py b/lnbits/extensions/smtp/__init__.py deleted file mode 100644 index 9b89a0c4..00000000 --- a/lnbits/extensions/smtp/__init__.py +++ /dev/null @@ -1,34 +0,0 @@ -import asyncio - -from fastapi import APIRouter -from fastapi.staticfiles import StaticFiles - -from lnbits.db import Database -from lnbits.helpers import template_renderer -from lnbits.tasks import catch_everything_and_restart - -db = Database("ext_smtp") - -smtp_static_files = [ - { - "path": "/smtp/static", - "app": StaticFiles(directory="lnbits/extensions/smtp/static"), - "name": "smtp_static", - } -] - -smtp_ext: APIRouter = APIRouter(prefix="/smtp", tags=["smtp"]) - - -def smtp_renderer(): - return template_renderer(["lnbits/extensions/smtp/templates"]) - - -from .tasks import wait_for_paid_invoices -from .views import * # noqa: F401,F403 -from .views_api import * # noqa: F401,F403 - - -def smtp_start(): - loop = asyncio.get_event_loop() - loop.create_task(catch_everything_and_restart(wait_for_paid_invoices)) diff --git a/lnbits/extensions/smtp/config.json b/lnbits/extensions/smtp/config.json deleted file mode 100644 index 325ebfa7..00000000 --- a/lnbits/extensions/smtp/config.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "SMTP", - "short_description": "Charge sats for sending emails", - "tile": "/smtp/static/smtp-bitcoin-email.png", - "contributors": ["dni"] -} diff --git a/lnbits/extensions/smtp/crud.py b/lnbits/extensions/smtp/crud.py deleted file mode 100644 index bc8d9e1a..00000000 --- a/lnbits/extensions/smtp/crud.py +++ /dev/null @@ -1,158 +0,0 @@ -from typing import List, Optional, Union - -from lnbits.helpers import urlsafe_short_hash - -from . import db -from .models import CreateEmail, CreateEmailaddress, Email, Emailaddress -from .smtp import send_mail - - -def get_test_mail(email, testemail): - return CreateEmail( - emailaddress_id=email, - subject="LNBits SMTP - Test Email", - message="This is a test email from the LNBits SMTP extension! email is working!", - receiver=testemail, - ) - - -async def create_emailaddress(data: CreateEmailaddress) -> Emailaddress: - - emailaddress_id = urlsafe_short_hash() - - # send test mail for checking connection - email = get_test_mail(data.email, data.testemail) - await send_mail(data, email) - - await db.execute( - """ - INSERT INTO smtp.emailaddress (id, wallet, email, testemail, smtp_server, smtp_user, smtp_password, smtp_port, anonymize, description, cost) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - """, - ( - emailaddress_id, - data.wallet, - data.email, - data.testemail, - data.smtp_server, - data.smtp_user, - data.smtp_password, - data.smtp_port, - data.anonymize, - data.description, - data.cost, - ), - ) - - new_emailaddress = await get_emailaddress(emailaddress_id) - assert new_emailaddress, "Newly created emailaddress couldn't be retrieved" - return new_emailaddress - - -async def update_emailaddress(emailaddress_id: str, **kwargs) -> Emailaddress: - q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()]) - await db.execute( - f"UPDATE smtp.emailaddress SET {q} WHERE id = ?", - (*kwargs.values(), emailaddress_id), - ) - row = await db.fetchone( - "SELECT * FROM smtp.emailaddress WHERE id = ?", (emailaddress_id,) - ) - - # send test mail for checking connection - email = get_test_mail(row.email, row.testemail) - await send_mail(row, email) - - assert row, "Newly updated emailaddress couldn't be retrieved" - return Emailaddress(**row) - - -async def get_emailaddress(emailaddress_id: str) -> Optional[Emailaddress]: - row = await db.fetchone( - "SELECT * FROM smtp.emailaddress WHERE id = ?", (emailaddress_id,) - ) - return Emailaddress(**row) if row else None - - -async def get_emailaddress_by_email(email: str) -> Optional[Emailaddress]: - row = await db.fetchone("SELECT * FROM smtp.emailaddress WHERE email = ?", (email,)) - return Emailaddress(**row) if row else None - - -async def get_emailaddresses(wallet_ids: Union[str, List[str]]) -> List[Emailaddress]: - if isinstance(wallet_ids, str): - wallet_ids = [wallet_ids] - - q = ",".join(["?"] * len(wallet_ids)) - rows = await db.fetchall( - f"SELECT * FROM smtp.emailaddress WHERE wallet IN ({q})", (*wallet_ids,) - ) - - return [Emailaddress(**row) for row in rows] - - -async def delete_emailaddress(emailaddress_id: str) -> None: - await db.execute("DELETE FROM smtp.emailaddress WHERE id = ?", (emailaddress_id,)) - - -async def create_email(wallet: str, data: CreateEmail, payment_hash: str = "") -> Email: - id = urlsafe_short_hash() - await db.execute( - """ - INSERT INTO smtp.email (id, payment_hash, wallet, emailaddress_id, subject, receiver, message, paid) - VALUES (?, ?, ?, ?, ?, ?, ?, ?) - """, - ( - id, - payment_hash, - wallet, - data.emailaddress_id, - data.subject, - data.receiver, - data.message, - False, - ), - ) - - new_email = await get_email(id) - assert new_email, "Newly created email couldn't be retrieved" - return new_email - - -async def set_email_paid(payment_hash: str) -> bool: - email = await get_email_by_payment_hash(payment_hash) - if email and email.paid is False: - await db.execute( - "UPDATE smtp.email SET paid = true WHERE payment_hash = ?", (payment_hash,) - ) - return True - return False - - -async def get_email_by_payment_hash(payment_hash: str) -> Optional[Email]: - row = await db.fetchone( - "SELECT * FROM smtp.email WHERE payment_hash = ?", (payment_hash,) - ) - return Email(**row) if row else None - - -async def get_email(id: str) -> Optional[Email]: - row = await db.fetchone("SELECT * FROM smtp.email WHERE id = ?", (id,)) - return Email(**row) if row else None - - -async def get_emails(wallet_ids: Union[str, List[str]]) -> List[Email]: - if isinstance(wallet_ids, str): - wallet_ids = [wallet_ids] - - q = ",".join(["?"] * len(wallet_ids)) - rows = await db.fetchall( - f"SELECT s.*, d.email as emailaddress FROM smtp.email s INNER JOIN smtp.emailaddress d ON (s.emailaddress_id = d.id) WHERE s.wallet IN ({q})", - (*wallet_ids,), - ) - - return [Email(**row) for row in rows] - - -async def delete_email(email_id: str) -> None: - await db.execute("DELETE FROM smtp.email WHERE id = ?", (email_id,)) diff --git a/lnbits/extensions/smtp/migrations.py b/lnbits/extensions/smtp/migrations.py deleted file mode 100644 index 22500e10..00000000 --- a/lnbits/extensions/smtp/migrations.py +++ /dev/null @@ -1,39 +0,0 @@ -async def m001_initial(db): - - await db.execute( - f""" - CREATE TABLE smtp.emailaddress ( - id TEXT PRIMARY KEY, - wallet TEXT NOT NULL, - email TEXT NOT NULL, - testemail TEXT NOT NULL, - smtp_server TEXT NOT NULL, - smtp_user TEXT NOT NULL, - smtp_password TEXT NOT NULL, - smtp_port TEXT NOT NULL, - anonymize BOOLEAN NOT NULL, - description TEXT NOT NULL, - cost INTEGER NOT NULL, - time TIMESTAMP NOT NULL DEFAULT {db.timestamp_now} - ); - """ - ) - - await db.execute( - f""" - CREATE TABLE smtp.email ( - id TEXT PRIMARY KEY, - wallet TEXT NOT NULL, - emailaddress_id TEXT NOT NULL, - subject TEXT NOT NULL, - receiver TEXT NOT NULL, - message TEXT NOT NULL, - paid BOOLEAN NOT NULL, - time TIMESTAMP NOT NULL DEFAULT {db.timestamp_now} - ); - """ - ) - - -async def m002_add_payment_hash(db): - await db.execute("ALTER TABLE smtp.email ADD COLUMN payment_hash TEXT;") diff --git a/lnbits/extensions/smtp/models.py b/lnbits/extensions/smtp/models.py deleted file mode 100644 index bb0e1f2c..00000000 --- a/lnbits/extensions/smtp/models.py +++ /dev/null @@ -1,47 +0,0 @@ -from fastapi import Query -from pydantic import BaseModel - - -class CreateEmailaddress(BaseModel): - wallet: str = Query(...) - email: str = Query(...) - testemail: str = Query(...) - smtp_server: str = Query(...) - smtp_user: str = Query(...) - smtp_password: str = Query(...) - smtp_port: str = Query(...) - description: str = Query(...) - anonymize: bool - cost: int = Query(..., ge=0) - - -class Emailaddress(BaseModel): - id: str - wallet: str - email: str - testemail: str - smtp_server: str - smtp_user: str - smtp_password: str - smtp_port: str - anonymize: bool - description: str - cost: int - - -class CreateEmail(BaseModel): - emailaddress_id: str = Query(...) - subject: str = Query(...) - receiver: str = Query(...) - message: str = Query(...) - - -class Email(BaseModel): - id: str - wallet: str - emailaddress_id: str - subject: str - receiver: str - message: str - paid: bool - time: int diff --git a/lnbits/extensions/smtp/smtp.py b/lnbits/extensions/smtp/smtp.py deleted file mode 100644 index 43253b54..00000000 --- a/lnbits/extensions/smtp/smtp.py +++ /dev/null @@ -1,110 +0,0 @@ -import re -import socket -import time -from email.mime.multipart import MIMEMultipart -from email.mime.text import MIMEText -from email.utils import formatdate -from smtplib import SMTP_SSL as SMTP -from typing import Union - -from loguru import logger - -from .models import CreateEmail, CreateEmailaddress, Email, Emailaddress - - -async def send_mail( - emailaddress: Union[Emailaddress, CreateEmailaddress], - email: Union[Email, CreateEmail], -): - smtp_client = SmtpService(emailaddress) - message = smtp_client.create_message(email) - await smtp_client.send_mail(email.receiver, message) - - -def valid_email(s): - # https://regexr.com/2rhq7 - pat = r"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?" - if re.match(pat, s): - return True - log = f"SMTP - invalid email: {s}." - logger.error(log) - raise Exception(log) - - -class SmtpService: - def __init__(self, emailaddress: Union[Emailaddress, CreateEmailaddress]) -> None: - self.sender = emailaddress.email - self.smtp_server = emailaddress.smtp_server - self.smtp_port = emailaddress.smtp_port - self.smtp_user = emailaddress.smtp_user - self.smtp_password = emailaddress.smtp_password - - def render_email(self, email: Union[Email, CreateEmail]): - signature: str = "Email sent by LNbits SMTP extension." - text = f"{email.message}\n\n{signature}" - html = ( - """ - - - -

""" - + email.message - + """

-

""" - + signature - + """

- - - """ - ) - return text, html - - def create_message(self, email: Union[Email, CreateEmail]): - ts = time.time() - date = formatdate(ts, True) - - msg = MIMEMultipart("alternative") - msg["Date"] = date - msg["Subject"] = email.subject - msg["From"] = self.sender - msg["To"] = email.receiver - - text, html = self.render_email(email) - - part1 = MIMEText(text, "plain") - part2 = MIMEText(html, "html") - msg.attach(part1) - msg.attach(part2) - return msg - - async def send_mail(self, receiver, msg: MIMEMultipart): - - valid_email(self.sender) - valid_email(receiver) - - try: - conn = SMTP(host=self.smtp_server, port=int(self.smtp_port), timeout=10) - logger.debug("SMTP - connected to smtp server.") - # conn.set_debuglevel(True) - except: - log = f"SMTP - error connecting to smtp server: {self.smtp_server}:{self.smtp_port}." - logger.debug(log) - raise Exception(log) - - try: - conn.login(self.smtp_user, self.smtp_password) - logger.debug("SMTP - successful login to smtp server.") - except: - log = f"SMTP - error login into smtp {self.smtp_user}." - logger.error(log) - raise Exception(log) - - try: - conn.sendmail(self.sender, receiver, msg.as_string()) - logger.debug("SMTP - successfully send email.") - except socket.error as e: - log = f"SMTP - error sending email: {str(e)}." - logger.error(log) - raise Exception(log) - finally: - conn.quit() diff --git a/lnbits/extensions/smtp/static/smtp-bitcoin-email.png b/lnbits/extensions/smtp/static/smtp-bitcoin-email.png deleted file mode 100644 index e80b6c9aeccd0474ba735fdbfc6ea7d46b240d5f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18854 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 None: - if payment.extra.get("tag") != "smtp": - return - - email = await get_email_by_payment_hash(payment.checking_id) - if not email: - logger.error("SMTP: email can not by fetched") - return - - emailaddress = await get_emailaddress(email.emailaddress_id) - if not emailaddress: - logger.error("SMTP: emailaddress can not by fetched") - return - - await payment.set_pending(False) - await send_mail(emailaddress, email) - await set_email_paid(payment_hash=payment.payment_hash) diff --git a/lnbits/extensions/smtp/templates/smtp/_api_docs.html b/lnbits/extensions/smtp/templates/smtp/_api_docs.html deleted file mode 100644 index cfb811d1..00000000 --- a/lnbits/extensions/smtp/templates/smtp/_api_docs.html +++ /dev/null @@ -1,23 +0,0 @@ - - - -
- LNBits SMTP: Get paid sats to send emails -
-

- Charge people for using sending an email via your smtp server
- More details -
- Created by, dni -

-
-
-
diff --git a/lnbits/extensions/smtp/templates/smtp/display.html b/lnbits/extensions/smtp/templates/smtp/display.html deleted file mode 100644 index f60d22c1..00000000 --- a/lnbits/extensions/smtp/templates/smtp/display.html +++ /dev/null @@ -1,175 +0,0 @@ -{% extends "public.html" %} {% block page %} -
-
- - -

{{ email }}

-
-
{{ desc }}
-
- - - - -

Total cost: {{ cost }} sats

-
- Submit -
-
-
-
-
- - - - - - -
- Copy invoice - Close -
-
-
-
- -{% endblock %} {% block scripts %} - -{% endblock %} diff --git a/lnbits/extensions/smtp/templates/smtp/index.html b/lnbits/extensions/smtp/templates/smtp/index.html deleted file mode 100644 index 4b00cd08..00000000 --- a/lnbits/extensions/smtp/templates/smtp/index.html +++ /dev/null @@ -1,604 +0,0 @@ -{% extends "base.html" %} {% from "macros.jinja" import window_vars with context -%} {% block page %} - -
-
- - - New Emailaddress - - - - - -
-
-
Emailaddresses
-
-
- Export to CSV -
-
- - {% raw %} - - - {% endraw %} - -
-
- - - -
-
-
Emails
-
-
- Export to CSV -
-
- - {% raw %} - - - {% endraw %} - -
-
-
-
- - -
- {{SITE_TITLE}} Sendmail extension -
-
- - - {% include "smtp/_api_docs.html" %} - -
-
- - - - - - - -
- Submit -
-
-
-
- - - - - - - - - - - - - - - -
- -
- - - - -
- Update Form - Create Emailaddress - Cancel -
-
-
-
-
- -{% endblock %} {% block scripts %} {{ window_vars(user) }} - -{% endblock %} diff --git a/lnbits/extensions/smtp/views.py b/lnbits/extensions/smtp/views.py deleted file mode 100644 index df208a77..00000000 --- a/lnbits/extensions/smtp/views.py +++ /dev/null @@ -1,40 +0,0 @@ -from http import HTTPStatus - -from fastapi import Depends, HTTPException, Request -from fastapi.templating import Jinja2Templates -from starlette.responses import HTMLResponse - -from lnbits.core.models import User -from lnbits.decorators import check_user_exists - -from . import smtp_ext, smtp_renderer -from .crud import get_emailaddress - -templates = Jinja2Templates(directory="templates") - - -@smtp_ext.get("/", response_class=HTMLResponse) -async def index(request: Request, user: User = Depends(check_user_exists)): - return smtp_renderer().TemplateResponse( - "smtp/index.html", {"request": request, "user": user.dict()} - ) - - -@smtp_ext.get("/{emailaddress_id}") -async def display(request: Request, emailaddress_id): - emailaddress = await get_emailaddress(emailaddress_id) - if not emailaddress: - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="Emailaddress does not exist." - ) - - return smtp_renderer().TemplateResponse( - "smtp/display.html", - { - "request": request, - "emailaddress_id": emailaddress.id, - "email": emailaddress.email, - "desc": emailaddress.description, - "cost": emailaddress.cost, - }, - ) diff --git a/lnbits/extensions/smtp/views_api.py b/lnbits/extensions/smtp/views_api.py deleted file mode 100644 index 66bc4983..00000000 --- a/lnbits/extensions/smtp/views_api.py +++ /dev/null @@ -1,190 +0,0 @@ -from http import HTTPStatus - -from fastapi import Depends, HTTPException, Query - -from lnbits.core.crud import get_user -from lnbits.core.services import check_transaction_status, create_invoice -from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key - -from . import smtp_ext -from .crud import ( - create_email, - create_emailaddress, - delete_email, - delete_emailaddress, - get_email, - get_email_by_payment_hash, - get_emailaddress, - get_emailaddresses, - get_emails, - update_emailaddress, -) -from .models import CreateEmail, CreateEmailaddress -from .smtp import send_mail, valid_email - - -## EMAILS -@smtp_ext.get("/api/v1/email") -async def api_email( - g: WalletTypeInfo = Depends(get_key_type), all_wallets: bool = Query(False) -): - wallet_ids = [g.wallet.id] - if all_wallets: - user = await get_user(g.wallet.user) - if user: - wallet_ids = user.wallet_ids - return [email.dict() for email in await get_emails(wallet_ids)] - - -@smtp_ext.get("/api/v1/email/{payment_hash}") -async def api_smtp_send_email(payment_hash): - email = await get_email_by_payment_hash(payment_hash) - if not email: - raise HTTPException( - status_code=HTTPStatus.BAD_REQUEST, detail="paymenthash is wrong" - ) - - emailaddress = await get_emailaddress(email.emailaddress_id) - assert emailaddress - - try: - status = await check_transaction_status(email.wallet, payment_hash) - is_paid = not status.pending - except Exception: - return {"paid": False} - if is_paid: - if emailaddress.anonymize: - await delete_email(email.id) - return {"paid": True} - return {"paid": False} - - -@smtp_ext.post("/api/v1/email/{emailaddress_id}") -async def api_smtp_make_email(emailaddress_id, data: CreateEmail): - valid_email(data.receiver) - - emailaddress = await get_emailaddress(emailaddress_id) - if not emailaddress: - raise HTTPException( - status_code=HTTPStatus.BAD_REQUEST, - detail="Emailaddress address does not exist.", - ) - try: - memo = f"sent email from {emailaddress.email} to {data.receiver}" - if emailaddress.anonymize: - memo = "sent email" - - payment_hash, payment_request = await create_invoice( - wallet_id=emailaddress.wallet, - amount=emailaddress.cost, - memo=memo, - extra={"tag": "smtp"}, - ) - except Exception as e: - raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e)) - - email = await create_email( - payment_hash=payment_hash, wallet=emailaddress.wallet, data=data - ) - - if not email: - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="Email could not be fetched." - ) - return {"payment_hash": payment_hash, "payment_request": payment_request} - - -@smtp_ext.post( - "/api/v1/email/{emailaddress_id}/send", dependencies=[Depends(require_admin_key)] -) -async def api_smtp_make_email_send(emailaddress_id, data: CreateEmail): - valid_email(data.receiver) - emailaddress = await get_emailaddress(emailaddress_id) - if not emailaddress: - raise HTTPException( - status_code=HTTPStatus.BAD_REQUEST, - detail="Emailaddress address does not exist.", - ) - email = await create_email(wallet=emailaddress.wallet, data=data) - if not email: - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="Email could not be fetched." - ) - await send_mail(emailaddress, email) - return {"sent": True} - - -@smtp_ext.delete("/api/v1/email/{email_id}") -async def api_email_delete(email_id, g: WalletTypeInfo = Depends(get_key_type)): - email = await get_email(email_id) - - if not email: - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="LNsubdomain does not exist." - ) - - if email.wallet != g.wallet.id: - raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail="Not your email.") - - await delete_email(email_id) - raise HTTPException(status_code=HTTPStatus.NO_CONTENT) - - -## EMAILADDRESSES -@smtp_ext.get("/api/v1/emailaddress") -async def api_emailaddresses( - g: WalletTypeInfo = Depends(get_key_type), - all_wallets: bool = Query(False), -): - wallet_ids = [g.wallet.id] - if all_wallets: - user = await get_user(g.wallet.user) - if user: - wallet_ids = user.wallet_ids - return [ - emailaddress.dict() for emailaddress in await get_emailaddresses(wallet_ids) - ] - - -@smtp_ext.post("/api/v1/emailaddress") -@smtp_ext.put("/api/v1/emailaddress/{emailaddress_id}") -async def api_emailaddress_create( - data: CreateEmailaddress, - emailaddress_id=None, - g: WalletTypeInfo = Depends(get_key_type), -): - if emailaddress_id: - emailaddress = await get_emailaddress(emailaddress_id) - - if not emailaddress: - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="Emailadress does not exist." - ) - if emailaddress.wallet != g.wallet.id: - raise HTTPException( - status_code=HTTPStatus.FORBIDDEN, detail="Not your emailaddress." - ) - - emailaddress = await update_emailaddress(emailaddress_id, **data.dict()) - else: - emailaddress = await create_emailaddress(data=data) - return emailaddress.dict() - - -@smtp_ext.delete("/api/v1/emailaddress/{emailaddress_id}") -async def api_emailaddress_delete( - emailaddress_id, g: WalletTypeInfo = Depends(get_key_type) -): - emailaddress = await get_emailaddress(emailaddress_id) - - if not emailaddress: - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="Emailaddress does not exist." - ) - if emailaddress.wallet != g.wallet.id: - raise HTTPException( - status_code=HTTPStatus.FORBIDDEN, detail="Not your Emailaddress." - ) - - await delete_emailaddress(emailaddress_id) - raise HTTPException(status_code=HTTPStatus.NO_CONTENT)