mirror of
https://codeberg.org/rimu/pyfedi
synced 2025-01-23 19:36:56 -08:00
API: support /user/block endpoint
This commit is contained in:
parent
eff0edf817
commit
7c8dfe6bd3
8 changed files with 181 additions and 30 deletions
|
@ -3,7 +3,7 @@ from app.api.alpha.utils import get_site, \
|
|||
get_post_list, get_post, post_post_like, put_post_save, put_post_subscribe, \
|
||||
get_reply_list, post_reply_like, put_reply_save, put_reply_subscribe, \
|
||||
get_community_list, get_community, \
|
||||
get_user
|
||||
get_user, post_user_block
|
||||
from app.shared.auth import log_user_in
|
||||
|
||||
from flask import current_app, jsonify, request
|
||||
|
@ -170,7 +170,7 @@ def get_alpha_user():
|
|||
|
||||
|
||||
@bp.route('/api/alpha/user/login', methods=['POST'])
|
||||
def post_alpha_login():
|
||||
def post_alpha_user_login():
|
||||
if not current_app.debug:
|
||||
return jsonify({'error': 'alpha api routes only available in debug mode'})
|
||||
try:
|
||||
|
@ -181,6 +181,18 @@ def post_alpha_login():
|
|||
return jsonify({"error": str(ex)}), 400
|
||||
|
||||
|
||||
@bp.route('/api/alpha/user/block', methods=['POST'])
|
||||
def post_alpha_user_block():
|
||||
if not current_app.debug:
|
||||
return jsonify({'error': 'alpha api routes only available in debug mode'})
|
||||
try:
|
||||
auth = request.headers.get('Authorization')
|
||||
data = request.get_json(force=True) or {}
|
||||
return jsonify(post_user_block(auth, data))
|
||||
except Exception as ex:
|
||||
return jsonify({"error": str(ex)}), 400
|
||||
|
||||
|
||||
# Not yet implemented. Copied from lemmy's V3 api, so some aren't needed, and some need changing
|
||||
|
||||
# Site - not yet implemented
|
||||
|
@ -219,7 +231,6 @@ def alpha_community():
|
|||
@bp.route('/api/alpha/post/remove', methods=['POST'])
|
||||
@bp.route('/api/alpha/post/lock', methods=['POST'])
|
||||
@bp.route('/api/alpha/post/feature', methods=['POST'])
|
||||
@bp.route('/api/alpha/post/save', methods=['PUT'])
|
||||
@bp.route('/api/alpha/post/report', methods=['POST'])
|
||||
@bp.route('/api/alpha/post/report/resolve', methods=['PUT'])
|
||||
@bp.route('/api/alpha/post/report/list', methods=['GET'])
|
||||
|
@ -261,7 +272,6 @@ def alpha_chat():
|
|||
@bp.route('/api/alpha/user/replies', methods=['GET'])
|
||||
@bp.route('/api/alpha/user/ban', methods=['POST'])
|
||||
@bp.route('/api/alpha/user/banned', methods=['GET'])
|
||||
@bp.route('/api/alpha/user/block', methods=['POST'])
|
||||
@bp.route('/api/alpha/user/delete_account', methods=['POST'])
|
||||
@bp.route('/api/alpha/user/password_reset', methods=['POST'])
|
||||
@bp.route('/api/alpha/user/password_change', methods=['POST'])
|
||||
|
|
|
@ -2,6 +2,6 @@ from app.api.alpha.utils.site import get_site
|
|||
from app.api.alpha.utils.post import get_post_list, get_post, post_post_like, put_post_save, put_post_subscribe
|
||||
from app.api.alpha.utils.reply import get_reply_list, post_reply_like, put_reply_save, put_reply_subscribe
|
||||
from app.api.alpha.utils.community import get_community, get_community_list
|
||||
from app.api.alpha.utils.user import get_user
|
||||
from app.api.alpha.utils.user import get_user, post_user_block
|
||||
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ from app.api.alpha.views import post_view
|
|||
from app.api.alpha.utils.validators import required, integer_expected, boolean_expected
|
||||
from app.models import Post, Community, CommunityMember, utcnow
|
||||
from app.shared.post import vote_for_post, bookmark_the_post, remove_the_bookmark_from_post, toggle_post_notification
|
||||
from app.utils import authorise_api_user
|
||||
from app.utils import authorise_api_user, blocked_users
|
||||
|
||||
from datetime import timedelta
|
||||
from sqlalchemy import desc
|
||||
|
@ -28,6 +28,11 @@ def cached_post_list(type, sort, user_id, community_id, community_name, person_i
|
|||
else:
|
||||
posts = Post.query.filter_by(deleted=False)
|
||||
|
||||
if user_id is not None:
|
||||
blocked_person_ids = blocked_users(user_id)
|
||||
if blocked_person_ids:
|
||||
posts = posts.filter(Post.user_id.not_in(blocked_person_ids))
|
||||
|
||||
if sort == "Hot":
|
||||
posts = posts.order_by(desc(Post.ranking)).order_by(desc(Post.posted_at))
|
||||
elif sort == "TopDay":
|
||||
|
@ -49,8 +54,8 @@ def get_post_list(auth, data, user_id=None):
|
|||
if auth:
|
||||
try:
|
||||
user_id = authorise_api_user(auth)
|
||||
except Exception as e:
|
||||
raise e
|
||||
except:
|
||||
raise
|
||||
|
||||
# user_id: the logged in user
|
||||
# person_id: the author of the posts being requested
|
||||
|
|
|
@ -1,20 +1,25 @@
|
|||
from app import cache
|
||||
from app.utils import authorise_api_user
|
||||
from app.api.alpha.utils.validators import required, integer_expected, boolean_expected
|
||||
from app.api.alpha.views import reply_view
|
||||
from app.models import PostReply
|
||||
from app.shared.reply import vote_for_reply, bookmark_the_post_reply, remove_the_bookmark_from_post_reply, toggle_post_reply_notification
|
||||
from app.utils import authorise_api_user, blocked_users
|
||||
|
||||
from sqlalchemy import desc
|
||||
|
||||
|
||||
# person_id param: the author of the reply; user_id param: the current logged-in user
|
||||
@cache.memoize(timeout=3)
|
||||
def cached_reply_list(post_id, person_id, sort, max_depth):
|
||||
def cached_reply_list(post_id, person_id, sort, max_depth, user_id):
|
||||
if post_id:
|
||||
replies = PostReply.query.filter(PostReply.deleted == False, PostReply.post_id == post_id, PostReply.depth <= max_depth)
|
||||
if person_id:
|
||||
replies = PostReply.query.filter_by(deleted=False, user_id=person_id)
|
||||
|
||||
if user_id is not None:
|
||||
blocked_person_ids = blocked_users(user_id)
|
||||
if blocked_person_ids:
|
||||
replies = replies.filter(PostReply.user_id.not_in(blocked_person_ids))
|
||||
|
||||
if sort == "Hot":
|
||||
replies = replies.order_by(desc(PostReply.ranking)).order_by(desc(PostReply.posted_at))
|
||||
elif sort == "Top":
|
||||
|
@ -36,13 +41,12 @@ def get_reply_list(auth, data, user_id=None):
|
|||
if data and not post_id and not person_id:
|
||||
raise Exception('missing_parameters')
|
||||
else:
|
||||
replies = cached_reply_list(post_id, person_id, sort, max_depth)
|
||||
|
||||
if auth:
|
||||
try:
|
||||
user_id = authorise_api_user(auth)
|
||||
except Exception as e:
|
||||
raise e
|
||||
if auth:
|
||||
try:
|
||||
user_id = authorise_api_user(auth)
|
||||
except:
|
||||
raise
|
||||
replies = cached_reply_list(post_id, person_id, sort, max_depth, user_id)
|
||||
|
||||
# user_id: the logged in user
|
||||
# person_id: the author of the posts being requested
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from app import db
|
||||
from app.api.alpha.views import user_view
|
||||
from app.utils import authorise_api_user
|
||||
from app.models import Language
|
||||
|
||||
|
@ -71,7 +72,7 @@ def get_site(auth):
|
|||
#"follows": [],
|
||||
"community_blocks": [], # TODO
|
||||
"instance_blocks": [], # TODO
|
||||
"person_blocks": [], # TODO
|
||||
"person_blocks": [],
|
||||
"discussion_languages": [] # TODO
|
||||
}
|
||||
"""
|
||||
|
@ -83,7 +84,9 @@ def get_site(auth):
|
|||
for cm in cms:
|
||||
my_user['follows'].append({'community': Community.api_json(variant=1, id=cm.community_id, stub=True), 'follower': User.api_json(variant=1, id=user_id, stub=True)})
|
||||
"""
|
||||
|
||||
blocked_ids = db.session.execute(text('SELECT blocked_id FROM "user_block" WHERE blocker_id = :blocker_id'), {"blocker_id": user.id}).scalars()
|
||||
for blocked_id in blocked_ids:
|
||||
my_user['person_blocks'].append({'person': user_view(user, variant=1, stub=True), 'target': user_view(blocked_id, variant=1, stub=True)})
|
||||
data = {
|
||||
"version": "1.0.0",
|
||||
"site": site
|
||||
|
|
|
@ -2,6 +2,8 @@ from app.api.alpha.views import user_view
|
|||
from app.utils import authorise_api_user
|
||||
from app.api.alpha.utils.post import get_post_list
|
||||
from app.api.alpha.utils.reply import get_reply_list
|
||||
from app.api.alpha.utils.validators import required, integer_expected, boolean_expected
|
||||
from app.shared.user import block_another_user, unblock_another_user
|
||||
|
||||
|
||||
def get_user(auth, data):
|
||||
|
@ -39,4 +41,26 @@ def get_user(auth, data):
|
|||
raise
|
||||
|
||||
|
||||
# would be in app/constants.py
|
||||
SRC_API = 3
|
||||
|
||||
def post_user_block(auth, data):
|
||||
try:
|
||||
required(['person_id', 'block'], data)
|
||||
integer_expected(['post_id'], data)
|
||||
boolean_expected(['block'], data)
|
||||
except:
|
||||
raise
|
||||
|
||||
person_id = data['person_id']
|
||||
block = data['block']
|
||||
|
||||
try:
|
||||
if block == True:
|
||||
user_id = block_another_user(person_id, SRC_API, auth)
|
||||
else:
|
||||
user_id = unblock_another_user(person_id, SRC_API, auth)
|
||||
user_json = user_view(user=person_id, variant=4, user_id=user_id)
|
||||
return user_json
|
||||
except:
|
||||
raise
|
||||
|
|
|
@ -50,8 +50,11 @@ def post_view(post: Post | int, variant, stub=False, user_id=None, my_vote=0):
|
|||
# counts - models/post/post_aggregates.dart
|
||||
counts = {'post_id': post.id, 'comments': post.reply_count, 'score': post.score, 'upvotes': post.up_votes, 'downvotes': post.down_votes,
|
||||
'published': post.posted_at.isoformat() + 'Z', 'newest_comment_time': post.last_active.isoformat() + 'Z'}
|
||||
bookmarked = db.session.execute(text('SELECT user_id FROM "post_bookmark" WHERE post_id = :post_id and user_id = :user_id'), {'post_id': post.id, 'user_id': user_id}).scalar()
|
||||
post_sub = db.session.execute(text('SELECT user_id FROM "notification_subscription" WHERE type = :type and entity_id = :entity_id and user_id = :user_id'), {'type': NOTIF_POST, 'entity_id': post.id, 'user_id': user_id}).scalar()
|
||||
if user_id:
|
||||
bookmarked = db.session.execute(text('SELECT user_id FROM "post_bookmark" WHERE post_id = :post_id and user_id = :user_id'), {'post_id': post.id, 'user_id': user_id}).scalar()
|
||||
post_sub = db.session.execute(text('SELECT user_id FROM "notification_subscription" WHERE type = :type and entity_id = :entity_id and user_id = :user_id'), {'type': NOTIF_POST, 'entity_id': post.id, 'user_id': user_id}).scalar()
|
||||
else:
|
||||
bookmarked = post_sub = False
|
||||
if not stub:
|
||||
banned = db.session.execute(text('SELECT user_id FROM "community_ban" WHERE user_id = :user_id and community_id = :community_id'), {'user_id': post.user_id, 'community_id': post.community_id}).scalar()
|
||||
moderator = db.session.execute(text('SELECT is_moderator FROM "community_member" WHERE user_id = :user_id and community_id = :community_id'), {'user_id': post.user_id, 'community_id': post.community_id}).scalar()
|
||||
|
@ -73,7 +76,7 @@ def post_view(post: Post | int, variant, stub=False, user_id=None, my_vote=0):
|
|||
creator_is_moderator = True if moderator else False
|
||||
creator_is_admin = True if admin else False
|
||||
v2 = {'post': post_view(post=post, variant=1, stub=stub), 'counts': counts, 'banned_from_community': False, 'subscribed': 'NotSubscribed',
|
||||
'saved': saved, 'read': False, 'hidden': False, 'creator_blocked': False, 'unread_comments': post.reply_count, 'my_vote': my_vote, 'activity_alert': activity_alert,
|
||||
'saved': saved, 'read': False, 'hidden': False, 'unread_comments': post.reply_count, 'my_vote': my_vote, 'activity_alert': activity_alert,
|
||||
'creator_banned_from_community': creator_banned_from_community, 'creator_is_moderator': creator_is_moderator, 'creator_is_admin': creator_is_admin}
|
||||
|
||||
try:
|
||||
|
@ -127,7 +130,8 @@ def cached_user_view_variant_1(user: User, stub=False):
|
|||
return v1
|
||||
|
||||
|
||||
def user_view(user: User | int, variant, stub=False):
|
||||
# 'user' param can be anyone (including the logged in user), 'user_id' param belongs to the user making the request
|
||||
def user_view(user: User | int, variant, stub=False, user_id=None):
|
||||
if isinstance(user, int):
|
||||
user = User.query.get(user)
|
||||
if not user:
|
||||
|
@ -144,13 +148,22 @@ def user_view(user: User | int, variant, stub=False):
|
|||
return v2
|
||||
|
||||
# Variant 3 - models/user/get_person_details.dart - /user?person_id api endpoint
|
||||
modlist = cached_modlist_for_user(user)
|
||||
if variant == 3:
|
||||
modlist = cached_modlist_for_user(user)
|
||||
|
||||
v3 = {'person_view': user_view(user=user, variant=2),
|
||||
'moderates': modlist,
|
||||
'posts': [],
|
||||
'comments': []}
|
||||
return v3
|
||||
v3 = {'person_view': user_view(user=user, variant=2),
|
||||
'moderates': modlist,
|
||||
'posts': [],
|
||||
'comments': []}
|
||||
return v3
|
||||
|
||||
# Variant 4 - models/user/block_person_response.dart - /user/block api endpoint
|
||||
if variant == 4:
|
||||
block = db.session.execute(text('SELECT blocker_id FROM "user_block" WHERE blocker_id = :blocker_id and blocked_id = :blocked_id'), {'blocker_id': user_id, 'blocked_id': user.id}).scalar()
|
||||
blocked = True if block else False
|
||||
v4 = {'person_view': user_view(user=user, variant=2),
|
||||
'blocked': blocked}
|
||||
return v4
|
||||
|
||||
|
||||
@cache.memoize(timeout=600)
|
||||
|
|
92
app/shared/user.py
Normal file
92
app/shared/user.py
Normal file
|
@ -0,0 +1,92 @@
|
|||
from app import db, cache
|
||||
from app.constants import ROLE_STAFF, ROLE_ADMIN
|
||||
from app.models import UserBlock
|
||||
from app.utils import authorise_api_user, blocked_users
|
||||
|
||||
from flask import flash
|
||||
from flask_babel import _
|
||||
from flask_login import current_user
|
||||
|
||||
from sqlalchemy import text
|
||||
|
||||
# would be in app/constants.py
|
||||
SRC_WEB = 1
|
||||
SRC_PUB = 2
|
||||
SRC_API = 3
|
||||
|
||||
# only called from API for now, but can be called from web using [un]block_another_user(user.id, SRC_WEB)
|
||||
|
||||
# user_id: the local, logged-in user
|
||||
# person_id: the person they want to block
|
||||
|
||||
def block_another_user(person_id, src, auth=None):
|
||||
if src == SRC_API:
|
||||
try:
|
||||
user_id = authorise_api_user(auth)
|
||||
except:
|
||||
raise
|
||||
else:
|
||||
user_id = current_user.id
|
||||
|
||||
if user_id == person_id:
|
||||
if src == SRC_API:
|
||||
raise Exception('cannot_block_self')
|
||||
else:
|
||||
flash(_('You cannot block yourself.'), 'error')
|
||||
return
|
||||
|
||||
role = db.session.execute(text('SELECT role_id FROM "user_role" WHERE user_id = :person_id'), {'person_id': person_id}).scalar()
|
||||
if role == ROLE_ADMIN or role == ROLE_STAFF:
|
||||
if src == SRC_API:
|
||||
raise Exception('cannot_block_admin_or_staff')
|
||||
else:
|
||||
flash(_('You cannot block admin or staff.'), 'error')
|
||||
return
|
||||
|
||||
existing_block = UserBlock.query.filter_by(blocker_id=user_id, blocked_id=person_id).first()
|
||||
if not existing_block:
|
||||
block = UserBlock(blocker_id=user_id, blocked_id=person_id)
|
||||
db.session.add(block)
|
||||
db.session.execute(text('DELETE FROM "notification_subscription" WHERE entity_id = :current_user AND user_id = :user_id'),
|
||||
{'current_user': user_id, 'user_id': person_id})
|
||||
db.session.commit()
|
||||
|
||||
cache.delete_memoized(blocked_users, user_id)
|
||||
|
||||
# Nothing to fed? (Lemmy doesn't federate anything to the blocked person)
|
||||
|
||||
if src == SRC_API:
|
||||
return user_id
|
||||
else:
|
||||
return # let calling function handle confirmation flash message and redirect
|
||||
|
||||
|
||||
def unblock_another_user(person_id, src, auth=None):
|
||||
if src == SRC_API:
|
||||
try:
|
||||
user_id = authorise_api_user(auth)
|
||||
except:
|
||||
raise
|
||||
else:
|
||||
user_id = current_user.id
|
||||
|
||||
if user_id == person_id:
|
||||
if src == SRC_API:
|
||||
raise Exception('cannot_unblock_self')
|
||||
else:
|
||||
flash(_('You cannot unblock yourself.'), 'error')
|
||||
return
|
||||
|
||||
existing_block = UserBlock.query.filter_by(blocker_id=user_id, blocked_id=person_id).first()
|
||||
if existing_block:
|
||||
db.session.delete(existing_block)
|
||||
db.session.commit()
|
||||
|
||||
cache.delete_memoized(blocked_users, user_id)
|
||||
|
||||
# Nothing to fed? (Lemmy doesn't federate anything to the unblocked person)
|
||||
|
||||
if src == SRC_API:
|
||||
return user_id
|
||||
else:
|
||||
return # let calling function handle confirmation flash message and redirect
|
Loading…
Add table
Reference in a new issue