mirror of
https://github.com/simple-login/app.git
synced 2024-11-13 07:31:12 +01:00
commit
6cec75a066
4 changed files with 176 additions and 14 deletions
22
README.md
22
README.md
|
@ -802,6 +802,7 @@ Get user aliases.
|
|||
Input:
|
||||
- `Authentication` header that contains the api key
|
||||
- `page_id` used for the pagination. The endpoint returns maximum 20 aliases for each page. `page_id` starts at 0.
|
||||
- (Optional) query: included in request body. Some frameworks might prevent GET request having a non-empty body, in this case this endpoint also supports POST.
|
||||
|
||||
Output:
|
||||
If success, 200 with the list of aliases, for example:
|
||||
|
@ -929,6 +930,7 @@ If success, 200 with the list of contacts, for example:
|
|||
{
|
||||
"contacts": [
|
||||
{
|
||||
"id": 1,
|
||||
"contact": "marketing@example.com",
|
||||
"creation_date": "2020-02-21 11:35:00+00:00",
|
||||
"creation_timestamp": 1582284900,
|
||||
|
@ -937,6 +939,7 @@ If success, 200 with the list of contacts, for example:
|
|||
"reverse_alias": "marketing at example.com <reply+bzvpazcdedcgcpztehxzgjgzmxskqa@sl.co>"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"contact": "newsletter@example.com",
|
||||
"creation_date": "2020-02-21 11:35:00+00:00",
|
||||
"creation_timestamp": 1582284900,
|
||||
|
@ -966,6 +969,7 @@ Return 409 if contact is already added.
|
|||
|
||||
```
|
||||
{
|
||||
"id": 1,
|
||||
"contact": "First Last <first@example.com>",
|
||||
"creation_date": "2020-03-14 11:52:41+00:00",
|
||||
"creation_timestamp": 1584186761,
|
||||
|
@ -975,6 +979,24 @@ Return 409 if contact is already added.
|
|||
}
|
||||
```
|
||||
|
||||
#### DELETE /api/contacts/:contact_id
|
||||
|
||||
Delete a contact
|
||||
|
||||
Input:
|
||||
- `Authentication` header that contains the api key
|
||||
- `contact_id` in url.
|
||||
|
||||
Output:
|
||||
If success, 200.
|
||||
|
||||
|
||||
```json
|
||||
{
|
||||
"deleted": true
|
||||
}
|
||||
```
|
||||
|
||||
### Database migration
|
||||
|
||||
The database migration is handled by `alembic`
|
||||
|
|
|
@ -9,15 +9,18 @@ from app.api.base import api_bp, verify_api_key
|
|||
from app.config import EMAIL_DOMAIN
|
||||
from app.config import PAGE_LIMIT
|
||||
from app.dashboard.views.alias_log import get_alias_log
|
||||
from app.dashboard.views.index import get_alias_info, AliasInfo
|
||||
from app.dashboard.views.index import (
|
||||
AliasInfo,
|
||||
get_alias_infos_with_pagination,
|
||||
)
|
||||
from app.extensions import db
|
||||
from app.log import LOG
|
||||
from app.models import EmailLog
|
||||
from app.models import Alias, Contact
|
||||
from app.models import EmailLog
|
||||
from app.utils import random_string
|
||||
|
||||
|
||||
@api_bp.route("/aliases")
|
||||
@api_bp.route("/aliases", methods=["GET", "POST"])
|
||||
@cross_origin()
|
||||
@verify_api_key
|
||||
def get_aliases():
|
||||
|
@ -43,7 +46,14 @@ def get_aliases():
|
|||
except (ValueError, TypeError):
|
||||
return jsonify(error="page_id must be provided in request query"), 400
|
||||
|
||||
alias_infos: [AliasInfo] = get_alias_info(user, page_id=page_id)
|
||||
query = None
|
||||
data = request.get_json(silent=True)
|
||||
if data:
|
||||
query = data.get("query")
|
||||
|
||||
alias_infos: [AliasInfo] = get_alias_infos_with_pagination(
|
||||
user, page_id=page_id, query=query
|
||||
)
|
||||
|
||||
return (
|
||||
jsonify(
|
||||
|
@ -202,6 +212,7 @@ def update_alias(alias_id):
|
|||
def serialize_contact(fe: Contact) -> dict:
|
||||
|
||||
res = {
|
||||
"id": fe.id,
|
||||
"creation_date": fe.created_at.format(),
|
||||
"creation_timestamp": fe.created_at.timestamp,
|
||||
"last_email_sent_date": None,
|
||||
|
@ -319,3 +330,28 @@ def create_contact_route(alias_id):
|
|||
db.session.commit()
|
||||
|
||||
return jsonify(**serialize_contact(contact)), 201
|
||||
|
||||
|
||||
@api_bp.route("/contacts/<int:contact_id>", methods=["DELETE"])
|
||||
@cross_origin()
|
||||
@verify_api_key
|
||||
def delete_contact(contact_id):
|
||||
"""
|
||||
Delete contact
|
||||
Input:
|
||||
contact_id: in url
|
||||
Output:
|
||||
200
|
||||
|
||||
|
||||
"""
|
||||
user = g.user
|
||||
contact = Contact.get(contact_id)
|
||||
|
||||
if not contact or contact.alias.user_id != user.id:
|
||||
return jsonify(error="Forbidden"), 403
|
||||
|
||||
Contact.delete(contact_id)
|
||||
db.session.commit()
|
||||
|
||||
return jsonify(deleted=True), 200
|
||||
|
|
|
@ -176,7 +176,7 @@ def index():
|
|||
return render_template(
|
||||
"dashboard/index.html",
|
||||
client_users=client_users,
|
||||
aliases=get_alias_info(current_user, query, highlight_alias_id),
|
||||
aliases=get_alias_infos(current_user, query, highlight_alias_id),
|
||||
highlight_alias_id=highlight_alias_id,
|
||||
query=query,
|
||||
AliasGeneratorEnum=AliasGeneratorEnum,
|
||||
|
@ -184,9 +184,56 @@ def index():
|
|||
)
|
||||
|
||||
|
||||
def get_alias_info(
|
||||
user, query=None, highlight_alias_id=None, page_id=None
|
||||
) -> [AliasInfo]:
|
||||
def get_alias_info(alias: Alias) -> AliasInfo:
|
||||
q = (
|
||||
db.session.query(Contact, EmailLog)
|
||||
.filter(Contact.alias_id == alias.id)
|
||||
.filter(EmailLog.contact_id == Contact.id)
|
||||
)
|
||||
|
||||
alias_info = AliasInfo(
|
||||
id=alias.id,
|
||||
alias=alias,
|
||||
mailbox=alias.mailbox,
|
||||
note=alias.note,
|
||||
nb_blocked=0,
|
||||
nb_forward=0,
|
||||
nb_reply=0,
|
||||
)
|
||||
|
||||
for _, el in q:
|
||||
if el.is_reply:
|
||||
alias_info.nb_reply += 1
|
||||
elif el.blocked:
|
||||
alias_info.nb_blocked += 1
|
||||
else:
|
||||
alias_info.nb_forward += 1
|
||||
|
||||
return alias_info
|
||||
|
||||
|
||||
def get_alias_infos_with_pagination(user, page_id=0, query=None) -> [AliasInfo]:
|
||||
ret = []
|
||||
q = (
|
||||
db.session.query(Alias)
|
||||
.options(joinedload(Alias.mailbox))
|
||||
.filter(Alias.user_id == user.id)
|
||||
)
|
||||
|
||||
if query:
|
||||
q = q.filter(
|
||||
or_(Alias.email.ilike(f"%{query}%"), Alias.note.ilike(f"%{query}%"))
|
||||
)
|
||||
|
||||
q = q.limit(PAGE_LIMIT).offset(page_id * PAGE_LIMIT)
|
||||
|
||||
for alias in q:
|
||||
ret.append(get_alias_info(alias))
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def get_alias_infos(user, query=None, highlight_alias_id=None) -> [AliasInfo]:
|
||||
if query:
|
||||
query = query.strip().lower()
|
||||
|
||||
|
@ -206,10 +253,6 @@ def get_alias_info(
|
|||
or_(Alias.email.ilike(f"%{query}%"), Alias.note.ilike(f"%{query}%"))
|
||||
)
|
||||
|
||||
# pagination activated
|
||||
if page_id is not None:
|
||||
q = q.limit(PAGE_LIMIT).offset(page_id * PAGE_LIMIT)
|
||||
|
||||
for ge, fe, fel, mb in q:
|
||||
if ge.email not in aliases:
|
||||
aliases[ge.email] = AliasInfo(
|
||||
|
|
|
@ -8,7 +8,7 @@ from app.models import User, ApiKey, Alias, Contact, EmailLog
|
|||
from app.utils import random_word
|
||||
|
||||
|
||||
def test_error_without_pagination(flask_client):
|
||||
def test_get_aliases_error_without_pagination(flask_client):
|
||||
user = User.create(
|
||||
email="a@b.c", password="password", name="Test User", activated=True
|
||||
)
|
||||
|
@ -26,7 +26,7 @@ def test_error_without_pagination(flask_client):
|
|||
assert r.json["error"]
|
||||
|
||||
|
||||
def test_success_with_pagination(flask_client):
|
||||
def test_get_aliases_with_pagination(flask_client):
|
||||
user = User.create(
|
||||
email="a@b.c", password="password", name="Test User", activated=True
|
||||
)
|
||||
|
@ -70,6 +70,38 @@ def test_success_with_pagination(flask_client):
|
|||
assert len(r.json["aliases"]) == 2
|
||||
|
||||
|
||||
def test_get_aliases_with_pagination(flask_client):
|
||||
user = User.create(
|
||||
email="a@b.c", password="password", name="Test User", activated=True
|
||||
)
|
||||
db.session.commit()
|
||||
|
||||
# create api_key
|
||||
api_key = ApiKey.create(user.id, "for test")
|
||||
db.session.commit()
|
||||
|
||||
# create more aliases than PAGE_LIMIT
|
||||
Alias.create_new(user, "prefix1")
|
||||
Alias.create_new(user, "prefix2")
|
||||
db.session.commit()
|
||||
|
||||
# get aliases without query, should return 3 aliases as one alias is created when user is created
|
||||
r = flask_client.get(
|
||||
url_for("api.get_aliases", page_id=0), headers={"Authentication": api_key.code}
|
||||
)
|
||||
assert r.status_code == 200
|
||||
assert len(r.json["aliases"]) == 3
|
||||
|
||||
# get aliases with "prefix1" query, should return 1 alias
|
||||
r = flask_client.get(
|
||||
url_for("api.get_aliases", page_id=0),
|
||||
headers={"Authentication": api_key.code},
|
||||
json={"query": "prefix1"},
|
||||
)
|
||||
assert r.status_code == 200
|
||||
assert len(r.json["aliases"]) == 1
|
||||
|
||||
|
||||
def test_delete_alias(flask_client):
|
||||
user = User.create(
|
||||
email="a@b.c", password="password", name="Test User", activated=True
|
||||
|
@ -267,3 +299,32 @@ def test_create_contact_route(flask_client):
|
|||
json={"contact": "First2 Last2 <first@example.com>"},
|
||||
)
|
||||
assert r.status_code == 409
|
||||
|
||||
|
||||
def test_delete_contact(flask_client):
|
||||
user = User.create(
|
||||
email="a@b.c", password="password", name="Test User", activated=True
|
||||
)
|
||||
db.session.commit()
|
||||
|
||||
# create api_key
|
||||
api_key = ApiKey.create(user.id, "for test")
|
||||
db.session.commit()
|
||||
|
||||
alias = Alias.create_new_random(user)
|
||||
db.session.commit()
|
||||
|
||||
contact = Contact.create(
|
||||
alias_id=alias.id,
|
||||
website_email="contact@example.com",
|
||||
reply_email="reply+random@sl.io",
|
||||
)
|
||||
db.session.commit()
|
||||
|
||||
r = flask_client.delete(
|
||||
url_for("api.delete_contact", contact_id=contact.id),
|
||||
headers={"Authentication": api_key.code},
|
||||
)
|
||||
|
||||
assert r.status_code == 200
|
||||
assert r.json == {"deleted": True}
|
||||
|
|
Loading…
Reference in a new issue