async def list_posts(request): limit = int(request.args.get('limit')) or PER_PAGE page = int(request.args.get('page')) or 1 special_id = int(request.args.get('special_id') or 0) offset = (page - 1) * limit _posts = await Post.filter().order_by('-id').offset(offset).limit(limit) total = await Post.filter().count() posts = [] exclude_ids = [] if special_id: topic = await SpecialTopic.cache(special_id) if topic: items = await topic.get_items() exclude_ids = [s.post_id for s in items] total -= len(exclude_ids) for post in _posts: dct = post.to_dict() if post.id in exclude_ids: continue if limit < 100: author = await post.author dct['author_name'] = author.name tags = await post.tags dct['tags'] = [t.name for t in tags] posts.append(dct) return json({'items': posts, 'total': total})
async def topic(request, topic_id): if request.method == 'PUT': return await _topic(request, topic_id=topic_id) topic = await SpecialTopic.get_or_404(topic_id) topic = await topic.to_sync_dict() topic['status'] = str(topic['status']) return json(topic)
async def upload(request): try: file = request.files['avatar'][0] is_avatar = True except KeyError: file = request.files['file'][0] is_avatar = False suffix = file.name.split(".")[-1] fid = generate_id() filename = f'{fid}.{suffix}' uploaded_file = Path(UPLOAD_FOLDER) / filename with open(uploaded_file, 'wb') as f: f.write(file.body) mime, _ = mimetypes.guess_type(str(uploaded_file)) encoded = b''.join(base64.encodestring(file.body).splitlines()).decode() dct: Dict[str, Union[Dict, int, str]] if is_avatar: dct = { 'files': { 'avatar': f'data:{mime};base64,{encoded}', 'avatar_path': filename } } else: if USE_FFMPEG and suffix == 'mp4': subprocess.call( f'ffmpeg -loglevel error -y -i {uploaded_file} -qscale:v 1 -qmin 1 -qmax 1 -frames:v 1 {Path(UPLOAD_FOLDER) / fid}.png', shell=True) # noqa dct = {'r': 0, 'filename': filename} return json(dct)
async def _post(request, post_id=None): form = PostForm(request) form.status.data = int(form.status.data) post = None if post_id is not None: post = await Post.get_or_404(post_id) if request.method in ('POST', 'PUT') and form.validate(): title = form.title.data assert str(form.author_id.data).isdigit() if post_id is None: post = await Post.filter(title=title).first() if not post: post = Post() tags = form.tags.data content = form.content.data is_page = form.is_page.data del form.tags del form.content del form.is_page form.populate_obj(post) post.type = Post.TYPE_PAGE if is_page else Post.TYPE_ARTICLE await post.save() await post.update_tags(tags) await post.set_content(content) ok = True else: ok = False post = await post.to_sync_dict() post['tags'] = [t.name for t in post['tags']] return json({'post': post if post else None, 'ok': ok})
async def target_react(request, user, target): reaction_type = int(request.form.get('reaction_type', ReactItem.K_LOVE)) if reaction_type not in (ReactItem.K_LOVE, ReactItem.K_UPVOTE): return json({'r': 1, 'msg': 'Not supported reaction_type'}) if request.method == 'POST': rv = await target.add_reaction(user['gid'], reaction_type) elif request.method == 'DELETE': rv = await target.cancel_reaction(user['gid'], reaction_type) n_reacted = 0 if reaction_type == ReactItem.K_LOVE: n_reacted = await target.n_likes elif reaction_type == ReactItem.K_UPVOTE: n_reacted = await target.n_upvotes return json({'r': int(not rv), 'n_reacted': n_reacted})
def login_required(f: Callable) -> Callable: async def wrapped(request, **kwargs): if not (user := request.ctx.session.get('user')): return json({'r': 403, 'msg': 'Login required.'}) user = {'gid': 841395} # My Github id if (target_id := kwargs.pop('target_id', None)) is not None: target_kind = kwargs.pop('target_kind', 'post') if target_kind == 'post': kls = Post elif target_kind == 'activity': kls = Activity elif target_kind == 'comment': kls = Comment else: return json({'r': 403, 'msg': 'Not support'}) if not (target := await kls.cache(target_id)): return json({'r': 1, 'msg': f'{kls.__class__.__name__} not exist'})
async def user(request, user_id): if request.method == 'PUT': return await _user(request, user_id=user_id) user = await User.get_or_404(user_id) user = await user.to_sync_dict() avatar = user.avatar user['avatar_url'] = ( request.app.url_for('static', filename=f'upload/{avatar}') # noqa if avatar else '') return json(user)
async def post(request, post_id): if request.method == 'PUT': return await _post(request, post_id=post_id) post = await Post.get_or_404(post_id) rv = await post.to_sync_dict() rv['tags'] = [t.name for t in rv['tags']] author = rv['author'] rv['status'] = str(rv['status']) rv['author'] = {'id': author.id, 'name': author.name} return json(rv)
async def react(request, user, post): if request.method == 'POST': reaction_type = request.form.get('reaction_type', None) if reaction_type is None: return json({'r': 1, 'msg': 'Reaction type error.'}) rv = await post.add_reaction(user['gid'], reaction_type) elif request.method == 'DELETE': rv = await post.cancel_reaction(user['gid']) stats = await post.stats reaction_type = None if user: reaction_type = await post.get_reaction_type(user['gid']) return json({'r': int(not rv), 'html': await render_template_def( 'utils.html', 'render_react_container', request, {'stats': stats, 'reaction_type': reaction_type}) })
async def list_posts(request): limit = int(request.args.get('limit')) or PER_PAGE page = int(request.args.get('page')) or 1 offset = (page - 1) * limit _posts = await Post.sync_filter(limit=limit, offset=offset, orderings='-id') total = await Post.filter().count() posts = [] for post in _posts: post['author_name'] = post['author'].name post['tags'] = [t.name for t in post['tags']] posts.append(post) return json({'items': posts, 'total': total})
async def post(request, post_id): if request.method == 'PUT': return await _post(request, post_id=post_id) post = await Post.get_or_404(id=post_id) if not post: return response.json({'r': 0, 'msg': 'Post not exist'}) if request.method == 'DELETE': await post.delete() await PostTag.filter(Q(post_id=post_id)).delete() return response.json({'r': 1}) rv = await post.to_sync_dict() rv['tags'] = [t.name for t in rv['tags']] author = rv['author'] rv['status'] = str(rv['status']) rv['author'] = {'id': author.id, 'name': author.name} return json(rv)
async def _topic(request, topic_id=None): form = TopicForm(request) form.status.data = int(form.status.data) topic = None if topic_id is not None: topic = await SpecialTopic.get_or_404(topic_id) if request.method in ('POST', 'PUT') and form.validate(): dct = defaultdict(dict) for k in copy.copy(request.form): if k.startswith('posts'): match = FORM_REGEX.search(k) if match: key = match['key'] val = request.form[k][0] dct[match['index']][key] = int(val) if key == 'id' else val del request.form[k] title = form.title.data if topic_id is None: topic = await SpecialTopic.filter(title=title).first() if not topic: topic = SpecialTopic() form.populate_obj(topic) if dct: indexes = [ (i['id'], int(index)) for index, i in sorted(dct.items(), key=lambda i: i[0]) ] else: indexes = [] if topic_id is not None: await topic.set_indexes(indexes) await topic.save() if topic_id is None: await topic.set_indexes(indexes) ok = True else: ok = False topic = await topic.to_sync_dict() return json({'topic': topic if topic else None, 'ok': ok})
ref_id = int(request.form.get('ref_id', 0)) comment = await target.add_comment(user['gid'], content, ref_id, target.kind) comment = await comment.to_sync_dict() rv = {'r': 0 if comment else 1} if target.kind == K_POST: reacted_comments = await target.comments_reacted_by(user['gid']) rv.update({ 'html': await render_template_def( 'utils.html', 'render_single_comment', request, {'comment': comment, 'github_user': user, 'reacted_comments': reacted_comments}) }) else: rv.update({'comment': comment}) return json(rv) @bp.route('/post/<post_id>/comments') async def comments(request, post_id): if not (post := await Post.cache(post_id)): return json({'r': 1, 'msg': 'Post not exist'}) page = int(request.args.get('page', 1)) per_page = int(request.args.get('per_page', 20)) start = (page - 1) * per_page comments = (await post.comments)[start: start + per_page] reacted_comments: List[List[int]] = [] if (user := request.ctx.session.get('user')):
if (special_id := int(request.args.get('special_id') or 0)): if (topic := await SpecialTopic.cache(special_id)): items = await topic.get_items() # type: ignore exclude_ids = [s.post_id for s in items] total -= len(exclude_ids) for post in _posts: dct = post.to_dict() if post.id in exclude_ids: continue if with_tag: author = await post.author dct['author_name'] = author.name tags = await post.tags dct['tags'] = [t.name for t in tags] posts.append(dct) return json({'items': posts, 'total': total}) @bp.route('/api/post/new', methods=['POST']) @protected(bp) async def new_post(request: Request): return await _post(request) async def _post(request: Request, post_id: Optional[Any] = None): form = PostForm(request) form.status.data = int(form.status.data) post = None if post_id is not None: post = await Post.get_or_404(post_id)
async def render_markdown(request, user): if not (text := request.form.get('text')): return json({'r': 1, 'msg': 'Text required.'})
async def comments(request, post_id): if not (post := await Post.cache(post_id)): return json({'r': 1, 'msg': 'Post not exist'})
async def create_comment(request, user, target): if not (content := request.form.get('content')): return json({'r': 1, 'msg': 'Content required.'})
async def activity_comments(request, activity_id): if not (activity := await Activity.cache(activity_id)): return json({'r': 1, 'msg': 'Activity not exist'})
async def wrapped(request, **kwargs): if not (user := request.ctx.session.get('user')): return json({'r': 403, 'msg': 'Login required.'}) user = {'gid': 841395} # My Github id
async def list_users(request: Request) -> HTTPResponse: users = await User.sync_all() total = await User.filter().count() return json({'items': users, 'total': total})
async def list_topics(request: Request) -> HTTPResponse: topics = await SpecialTopic.sync_all() total = len(topics) return json({'items': topics, 'total': total})
async def api_status(request): user_id = request.user.id obj, msg = await create_status(user_id, request.json) activity = None if not obj else await obj.to_full_dict() # type: ignore return json({'r': not bool(obj), 'msg': msg, 'activity': activity})
async def get_url_info(request): if not (url := request.json.get('url')): return json({'r': 403, 'msg': 'URL required'})
if (special_id := int(request.args.get('special_id') or 0)): if (topic := await SpecialTopic.cache(special_id)): items = await topic.get_items() # type: ignore exclude_ids = [s.post_id for s in items] total -= len(exclude_ids) for post in _posts: dct = post.to_dict() if post.id in exclude_ids: continue if with_tag: author = await post.author dct['author_name'] = author.name tags = await post.tags dct['tags'] = [t.name for t in tags] posts.append(dct) return json({'items': posts, 'total': total}) @bp.route('/api/post/new', methods=['POST']) @protected(bp) async def new_post(request: Request): return await _post(request) async def _post(request: Request, post_id: Optional[Any] = None): form = PostForm(request) form.status.data = int(form.status.data) post = None if post_id is not None: post = await Post.get_or_404(post_id)