from flask import Blueprint, request, url_for from ..lib.babycode import babycode_to_html from ..constants import REACTION_EMOJI from .users import is_logged_in, get_active_user from ..models import APIRateLimits, Threads, Reactions from ..db import db bp = Blueprint("api", __name__, url_prefix="/api/") @bp.post('/thread-updates/') def thread_updates(thread_id): thread = Threads.find({'id': thread_id}) if not thread: return {'error': 'no such thread'}, 404 target_time = request.json.get('since') if not target_time: return {'error': 'missing parameter "since"'}, 400 try: target_time = int(target_time) except: return {'error': 'parameter "since" is not/cannot be converted to a number'}, 400 q = 'SELECT id FROM posts WHERE thread_id = ? AND posts.created_at > ? ORDER BY posts.created_at ASC LIMIT 1' new_post = db.fetch_one(q, thread_id, target_time) if not new_post: return {'status': 'none'} url = url_for('threads.thread', slug=thread.slug, after=new_post['id'], _anchor=f"post-{new_post['id']}") return {'status': 'new_post', 'url': url} @bp.post('/babycode-preview') def babycode_preview(): if not is_logged_in(): return {'error': 'not authorized'}, 401 user = get_active_user() if not APIRateLimits.is_allowed(user.id, 'babycode_preview', 5): return {'error': 'too many requests'}, 429 markup = request.json.get('markup') if not markup or not isinstance(markup, str): return {'error': 'markup field missing or invalid type'}, 400 rendered = babycode_to_html(markup) return {'html': rendered} @bp.post('/add-reaction/') def add_reaction(post_id): if not is_logged_in(): return {'error': 'not authorized', 'error_code': 401}, 401 user = get_active_user() reaction_text = request.json.get('emoji') if not reaction_text or not isinstance(reaction_text, str): return {'error': 'emoji field missing or invalid type', 'error_code': 400}, 400 if reaction_text not in REACTION_EMOJI: return {'error': 'unsupported reaction', 'error_code': 400}, 400 reaction = Reactions.find({ 'user_id': user.id, 'post_id': int(post_id), 'reaction_text': reaction_text, }) if reaction: return {'error': 'reaction already exists', 'error_code': 409}, 409 reaction = Reactions.create({ 'user_id': user.id, 'post_id': int(post_id), 'reaction_text': reaction_text, }) return {'status': 'added'} @bp.post('/remove-reaction/') def remove_reaction(post_id): if not is_logged_in(): return {'error': 'not authorized'}, 401 user = get_active_user() reaction_text = request.json.get('emoji') if not reaction_text or not isinstance(reaction_text, str): return {'error': 'emoji field missing or invalid type'}, 400 if reaction_text not in REACTION_EMOJI: return {'error': 'unsupported reaction'}, 400 reaction = Reactions.find({ 'user_id': user.id, 'post_id': int(post_id), 'reaction_text': reaction_text, }) if not reaction: return {'error': 'reaction does not exist'}, 404 reaction.delete() return {'status': 'removed'}