def edit(u=None,cid=0): try: db = open_db() # Query comment from id comment = db.query(Comment).filter_by(id=cid).first() if comment is None: abort(404) if comment.user_id != u.id and u.is_admin == False: abort(403) if request.method == 'POST': body = request.form.get('body') if len(body) == 0: raise XaieconException(gettext('Body too short')) # Update comment db.query(Comment).filter_by(id=cid).update({ 'body':body, 'body_html':md(body)}) db.commit() db.close() cache.delete_memoized(view) return redirect(f'/comment/view/{cid}') else: db.close() return render_template('post/edit_comment.html',u=u,title='Edit comment',comment=comment) except XaieconException as e: return render_template('user_error.html',u=u,title = 'Whoops!',err=e)
def mark_all_as_read(u=None): db = open_db() db.query(Notification).filter_by(user_id=u.id).update({'is_read':True}) db.commit() db.close() cache.delete_memoized(notifications) return redirect('/user/notifications')
def csam_check_post(uid: int, pid: int): db = open_db() post = db.query(Post).filter_by(id=pid).first() # Nothing to scan if post.is_link == False and post.is_image == False: return headers = {'User-Agent':'xaiecon-csam-check'} # Check link of post if post.is_link == True: for i in range(10): x = requests.get(post.link_url,headers=headers) if x.status_code in [200, 451]: break else: time.sleep(10) # And check image if it has one... if post.is_image == True: for i in range(10): x = requests.get(f'https://{os.environ.get("DOMAIN_NAME")}/post/thumb/{post.id}',headers=headers) if x.status_code in [200, 451]: break else: time.sleep(10) for i in range(10): x = requests.get(f'https://{os.environ.get("DOMAIN_NAME")}/post/image/{post.id}',headers=headers) if x.status_code in [200, 451]: break else: time.sleep(10) # If status code is not 451, else... if x.status_code != 451: return print(f'Offensive post {post.id} found!') # Ban user user = db.query(User).filter_by(id=post.user_id).first() db.query(User).filter_by(id=post.user_id).update({ 'ban_reason':'CSAM Automatic Removal', 'is_banned':True}) db.commit() db.refresh(user) db.close() cache.delete_memoized(view) return
def vote(u=None,pid=0): db = open_db() try: val = int(request.values.get('value','1')) if pid is None: abort(404) if val not in [-1,1]: abort(400) post = db.query(Post).filter_by(id=pid).first() if post is None: abort(404) user = db.query(User).filter_by(id=post.user_id).first() vote = db.query(Vote).filter_by(user_id=u.id,post_id=pid).first() if vote is not None and vote.value == val: db.query(User).filter_by(id=post.user_id).update({ 'net_points':user.net_points-vote.value}) db.query(Vote).filter_by(user_id=u.id,post_id=pid).delete() else: db.query(Vote).filter_by(user_id=u.id,post_id=pid).delete() # Create vote relation vote = Vote(user_id=u.id,post_id=post.id,value=val) db.add(vote) db.query(User).filter_by(id=post.user_id).update({ 'net_points':user.net_points+val}) # Update vote count downvotes = db.query(Vote).filter_by(post_id=pid,value=-1).count() upvotes = db.query(Vote).filter_by(post_id=pid,value=1).count() db.query(Post).filter_by(id=pid).update({ 'downvote_count':downvotes, 'upvote_count':upvotes, 'total_vote_count':upvotes-downvotes}) db.commit() db.close() cache.delete_memoized(ballot) cache.delete_memoized(view,pid=pid) cache.delete_memoized(list_posts) cache.delete_memoized(list_nuked) cache.delete_memoized(list_feed) return '',200 except XaieconException as e: db.rollback() db.close() return jsonify({'error':e}),400
def nuke(u=None,cid=0): db = open_db() try: comment = db.query(Comment).filter_by(id=cid).first() if comment is None: abort(404) # User must be also mod of the post's origin board # Increment number of comments post_comment = None post_cid = cid post = None while post is None: post_comment = db.query(Comment).filter_by(id=post_cid).first() if post_comment.post_id is None: post_cid = post_comment.comment_id else: post = db.query(Post).filter_by(id=post_comment.post_id).options(joinedload('*')).first() if post is None: abort(404) break board = db.query(Board).filter_by(id=post.board_id).first() # Check that post is not already nuked and that user mods # the guild if board is None: raise XaieconException('Post cannot be nuked because it is not in any board') if comment.is_nuked == True: raise XaieconException('Post already nuked/deleted by user or someone else') if not u.mods(board.id) and u.is_admin == False: raise XaieconException('You do not mod the origin board') # "Nuke" post db.query(Comment).filter_by(id=cid).update({'is_nuked':True,'nuker_id':u.id}) db.commit() db.close() cache.delete_memoized(view_p,pid=post.id) cache.delete_memoized(view,cid=cid) cache.delete_memoized(list_posts) cache.delete_memoized(list_nuked) cache.delete_memoized(list_feed) send_admin_notification(f'Comment with id {cid} has been nuked! Review it [here](/comment/view/{cid}) and [do a final nuke](/admin/comment/nuke/{cid}) if you consider it nescesary.') return redirect(f'/comment/view/{cid}') except XaieconException as e: db.rollback() db.close() return render_template('user_error.html',u=u,title = 'Whoops!',err=e)
def unnuke(u=None,pid=0): db = open_db() try: if u.is_admin == False: raise XaieconException('Only admins can un-nuke') post = db.query(Post).filter_by(id=pid).first() if post is None: abort(404) # Check that post is not already nuked and that user mods # the guild if post.is_deleted == True or post.is_nuked == False: raise XaieconException('Post already deleted or has been unnuked by user or someone else') # "Un-Nuke" post db.query(Post).filter_by(id=pid).update({'is_nuked':False}) db.commit() db.close() cache.delete_memoized(view,pid=pid) cache.delete_memoized(list_posts) cache.delete_memoized(list_nuked) cache.delete_memoized(list_feed) return redirect(f'/post/view/{pid}') except XaieconException as e: db.rollback() db.close() return render_template('user_error.html',u=u,title = 'Whoops!',err=e)
def admin_nuke(u=None,cid=0): db = open_db() comment = db.query(Comment).filter_by(id=cid).first() if comment is None: abort(404) # Delete all stuff of the comment db.query(Comment).filter_by(id=cid).update({ 'body':gettext('[deleted by nuking]'), 'body_html':gettext('[deleted by nuking]')}) db.commit() # Kill user user = db.query(User).filter_by(id=comment.user_id).first() db.query(User).filter_by(id=comment.user_id).update({ 'ban_reason':'ToS break. Nuking.', 'is_banned':True}) db.commit() db.refresh(user) db.close() # Delete caching cache.delete_memoized(view,cid=cid) cache.delete_memoized(list_posts) cache.delete_memoized(list_nuked) cache.delete_memoized(list_feed) return '',200
def delete(u=None,cid=0): db = open_db() # Query comment from id comment = db.query(Comment).filter_by(id=cid).first() if comment is None: abort(404) if comment.user_id != u.id and u.is_admin == False: abort(403) # Update comment db.query(Comment).filter_by(id=cid).update({ 'body':gettext('[deleted by user]'), 'body_html':gettext('[deleted by user]')}) db.commit() db.close() cache.delete_memoized(view) return redirect(f'/comment/view/{cid}')
def view(u=None,pid=0): db = open_db() # Query post from database post = db.query(Post).filter_by(id=pid).options(joinedload('*')).first() if post is None: abort(404) # Dont let people see nsfw if post.is_nsfw == True and u.is_nsfw == False: abort(403) # Add one view if u is not None: if db.query(View).filter_by(user_id=u.id,post_id=pid).first() is None: view = View(user_id=u.id,post_id=pid) db.add(view) db.query(Post).filter_by(id=pid).update({'views':post.views+1}) db.commit() comment = db.query(Comment).filter_by(post_id=post.id).options(joinedload('*')).order_by(desc(Comment.id)).all() # This is how we get replies, pardon for so many cringe comments = [] # First add the top comment(s) for c in comment: c.depth_level = 1 comments.append(c) # Obtain comments that reference the current comment (top) comms = db.query(Comment).filter_by(comment_id=c.id).options(joinedload('*')).all() if comms is not None: # Obtain the comments of comments for d in comms: d.depth_level = 2 ecomms = db.query(Comment).filter_by(comment_id=d.id).options(joinedload('*')).all() comments.append(d) for l in ecomms: l.depth_level = 3 # Deepest comments, check if they have even more children if db.query(Comment).filter_by(comment_id=l.id).options(joinedload('*')).first() is not None: l.more_children = True comments.append(l) ret = render_template('post/details.html',u=u,title=post.title,post=post,comment=comments) db.close() cache.delete_memoized(list_posts) cache.delete_memoized(list_nuked) cache.delete_memoized(list_feed) return ret
def unhide(u=None,cid=0): db = open_db() # Obtain boardinfo post_comment = None post_cid = cid post = None while post is None: post_comment = db.query(Comment).filter_by(id=post_cid).first() if post_comment.post_id is None: post_cid = post_comment.comment_id else: post = db.query(Post).filter_by(id=post_comment.post_id).options(joinedload('*')).first() if post is None: abort(404) break if u.mods(post.board_id) == False and u.is_admin == False: abort(403) db.query(Comment).filter_by(id=cid).update({'is_hidden':False}) db.close() cache.delete_memoized(view) cache.delete_memoized(view_p,pid=post.id) return redirect(f'/comment/view/{cid}')
def yank(u=None,pid=0): db = open_db() try: if request.method == 'POST': bid = request.values.get('bid') post = db.query(Post).filter_by(id=pid).first() # User must be also mod of the post's origin board # Or the post must not have a bid board = db.query(Board).filter_by(id=post.board_id).first() # If post is standalone or baord is not exists then just skip the # auth if board is not None or board.id == 1: if not u.mods(board.id) and u.is_admin == False: raise XaieconException('You do not mod the origin board') # Check that user mods the board he is trying to yank to if not u.mods(bid) and u.is_admin == False: raise XaieconException('You do not mod the target board') # Change post's bid board = db.query(Board).filter_by(id=bid).first() db.query(Post).filter_by(id=pid).update({'board_id':board.id}) db.commit() db.close() cache.delete_memoized(view,pid=pid) cache.delete_memoized(list_posts) cache.delete_memoized(list_nuked) cache.delete_memoized(list_feed) return redirect(f'/post/view/{pid}') else: boards = db.query(Board).filter_by(user_id=u.id).options(joinedload('user_info')).all() post = db.query(Post).filter_by(id=pid).first() db.close() return render_template('post/yank.html',u=u,title='Yank post',post=post,boards=boards) except XaieconException as e: db.rollback() db.close() return render_template('user_error.html',u=u,title = 'Whoops!',err=e)
def admin_nuke(u=None,pid=0): db = open_db() post = db.query(Post).filter_by(id=pid).first() if post is None: abort(404) # Remove old image try: os.remove(os.path.join('user_data',post.image_file)) os.remove(os.path.join('user_data',post.thumb_file)) except FileNotFoundError: pass # Delete all stuff of the post db.query(Post).filter_by(id=pid).update({ 'is_deleted':True, 'body':'[deleted by user]', 'body_html':'[deleted by user]', 'is_link':False, 'is_image':False, 'image_file':'', 'thumb_file':'', 'link_url':''}) db.commit() # Kill user user = db.query(User).filter_by(id=post.user_id).first() db.query(User).filter_by(id=post.user_id).update({ 'ban_reason':'ToS breaking. Nuking', 'is_banned':True}) db.commit() db.refresh(user) db.close() # Delete caching cache.delete_memoized(view,pid=pid) cache.delete_memoized(list_posts) cache.delete_memoized(list_nuked) cache.delete_memoized(list_feed) return '',200
def vote(u=None,cid=0): try: val = int(request.values.get('value')) if cid is None or val is None or val not in [-1,1]: abort(400) db = open_db() vote = db.query(Vote).filter_by(user_id=u.id,comment_id=cid).first() comment = db.query(Comment).filter_by(id=cid).first() if comment is None: abort(404) # Delete previous vote db.query(Vote).filter_by(user_id=u.id,comment_id=cid).delete() if vote is not None and vote.value != val: # Create vote relation vote = Vote(user_id=u.id,comment_id=cid,value=val) db.add(vote) # Update vote count downvotes = db.query(Vote).filter_by(comment_id=cid,value=-1).count() upvotes = db.query(Vote).filter_by(comment_id=cid,value=1).count() db.query(Post).filter_by(id=cid).update({ 'downvote_count':downvotes, 'upvote_count':upvotes, 'total_vote_count':upvotes-downvotes}) db.commit() db.close() cache.delete_memoized(view_p) cache.delete_memoized(list_posts) cache.delete_memoized(list_nuked) cache.delete_memoized(list_feed) return '',200 except XaieconException as e: return jsonify({'error':e}),400
def delete(u=None,pid=0): db = open_db() try: post = db.query(Post).filter_by(id=pid).first() if post == None: abort(404) if u.id != post.user_id and u.is_admin == False: raise XaieconException('User is not authorized') # Remove old image if post.is_image == True: try: os.remove(os.path.join('user_data',post.image_file)) os.remove(os.path.join('user_data',post.thumb_file)) except FileNotFoundError: pass # Set is_deleted to true db.query(Post).filter_by(id=pid).update({ 'is_deleted':True, 'body':'[deleted by user]', 'body_html':'[deleted by user]', 'is_link':False, 'is_image':False, 'image_file':'', 'thumb_file':'', 'link_url':''}) db.commit() db.close() cache.delete_memoized(view,pid=pid) cache.delete_memoized(list_posts) cache.delete_memoized(list_nuked) cache.delete_memoized(list_feed) return redirect(f'/post/view/{pid}') except XaieconException as e: db.rollback() db.close() return jsonify({'error':e}),400
def kick(u=None,pid=0): db = open_db() try: if request.method == 'POST': post = db.query(Post).filter_by(id=pid).first() if post is None: abort(404) # User must be also mod of the post's origin board board = db.query(Board).filter_by(id=post.board_id).first() # Check that post is not already kicked and that user mods # the guild if board is None: raise XaieconException('Post cannot be kicked because it is not in any board') if not u.mods(board.id) and u.is_admin == False: raise XaieconException('You do not mod the origin board') # Change post's bid to general waters db.query(Post).filter_by(id=pid).update({'board_id':1}) db.commit() db.close() cache.delete_memoized(view,pid=pid) cache.delete_memoized(list_posts) cache.delete_memoized(list_nuked) cache.delete_memoized(list_feed) return redirect(f'/post/view/{pid}') else: post = db.query(Post).filter_by(id=pid).first() db.close() return render_template('post/kick.html',u=u,title='Kick post',post=post) except XaieconException as e: db.rollback() db.close() return render_template('user_error.html',u=u,title = 'Whoops!',err=e)
def write(u=None): db = open_db() try: if request.method == 'POST': body = request.form.get('body','') title = request.form.get('title') keywords = request.form.get('keywords') link = request.form.get('link','') bid = request.form.get('bid') category = int(request.values.get('category','0')) show_votes = strtobool(request.form.get('show_votes','False')) is_nsfw = strtobool(request.form.get('is_nsfw','False')) if len(title) > 255: raise XaieconException('Too long title') if len(body) > 16000: raise XaieconException('Too long body') category = db.query(Category).filter_by(id=category).first() if category is None: raise XaieconException('Not a valid category') board = db.query(Board).filter_by(id=bid).first() if board is None: raise XaieconException('Invalid board') bid = board.id if u.is_banned_from_board(bid) == True: raise XaieconException(f'You\'re banned from /b/{board.name}') is_link = False embed_html = '' if link != '': is_link = True embed = obtain_embed_url(link) if embed is not None: embed_html = f'<iframe width="560" height="315" src="{embed}" allowfullscreen frameborder=\'0\'></iframe>' if body == '' and is_link == False: raise XaieconException('Empty body') if title is None or title == '': raise XaieconException('Empty title') body_html = md(body) post = Post(keywords=keywords, title=title, body=body, link_url=link, is_link=is_link, user_id=u.id, is_nsfw=is_nsfw, downvote_count=0, upvote_count=0, total_vote_count=0, category_id=category.id, board_id=bid, embed_html=embed_html, body_html=body_html, show_votes=show_votes) file = request.files['image'] if file: try: # Build paths and filenames image_filename = secure_filename(f'{secrets.token_hex(12)}.jpeg') image_filepath = os.path.join('user_data',image_filename) thumb_filename = secure_filename(f'thumb_{image_filename}') thumb_filepath = os.path.join('user_data',thumb_filename) # Save full image file file.save(image_filepath) # Create thumbnail for image image = PIL.Image.open(image_filepath) image = image.convert('RGB') image.thumbnail((128,128)) image.save(thumb_filepath) post.image_file = image_filename post.thumb_file = thumb_filename post.is_image = True post.is_thumb = True except PIL.UnidentifiedImageError: pass else: post.is_image = False if is_link == True: img = obtain_post_thumb(link) if img is not None: thumb_filename = secure_filename(f'thumb_{secrets.token_hex(12)}.jpeg') thumb_filepath = os.path.join('user_data',thumb_filename) timg = img.convert('RGB') timg.resize((128,128)) timg.save(thumb_filepath) post.thumb_file = thumb_filename post.is_thumb = True db.add(post) db.commit() db.refresh(post) csam_thread = threading.Thread(target=csam_check_post, args=(u.id,post.id,)) csam_thread.start() notif_msg = f'# {post.title}\n\rBy [/u/{u.username}](/user/view/{u.id}) on [/b/{board.name}](/board/view/{board.id})\n\r{post.body}' # Alert boardmaster of the posts in the guild if board.user_id != u.id: send_notification(notif_msg,board.user_id) # Notify followers follows = db.query(UserFollow).filter_by(target_id=u.id,notify=True).all() for f in follows: if f.user_id != u.id: send_notification(notif_msg,f.user_id) ping = body.find('@everyone') if ping != -1 and u.is_admin == True: users = db.query(User).all() for us in users: if us.id != u.id: send_notification(notif_msg,us.id) ping = body.find('@here') if ping != -1 and u.mods(post.board_id): subs = db.query(BoardSub).filter_by(board_id=post.board_id).all() for s in subs: if s.user_id != u.id: send_notification(notif_msg,s.user_id) for m in re.finditer(r'([u][\/]|[@])([a-zA-Z0-9#][^ ,.;:\n\r\t<>\/\'])*\w+',body): m = m.group(0) try: name = re.split(r'([u][\/]|[@])',m)[2] tag = name.split('#') # direct mention if len(tag) > 1: uid = int(tag[1]) user = db.query(User).filter_by(id=uid).first() if user is None: raise IndexError send_notification(notif_msg,user.id) else: users = db.query(User).filter_by(username=name).all() if users is None: raise IndexError for user in users: send_notification(notif_msg,user.id) except IndexError: pass db.close() # Mess with everyone's feed cache.delete_memoized(list_posts) cache.delete_memoized(list_nuked) cache.delete_memoized(list_feed) return redirect(f'/post/view/{post.id}') else: board = db.query(Board).filter_by(is_banned=False).options(joinedload('user_info')).all() categories = db.query(Category).all() db.close() return render_template('post/write.html',u=u,title='New post',boards=board,categories=categories) except XaieconException as e: db.rollback() db.close() return render_template('user_error.html',u=u,title = 'Whoops!',err=e)
def edit(u=None,pid=0): db = open_db() try: post = db.query(Post).filter_by(id=pid).first() if post is None: abort(404) if u.id != post.user_id and u.is_admin == False: raise XaieconException('User is not authorized') if request.method == 'POST': body = request.form.get('body') title = request.form.get('title') keywords = request.form.get('keywords') link = request.form.get('link','') category = int(request.form.get('category','0')) is_nsfw = strtobool(request.form.get('is_nsfw','False')) if len(title) > 255: raise XaieconException('Too long title') category = db.query(Category).filter_by(id=category).first() if category is None: raise XaieconException('Not a valid category') is_link = False embed_html = '' if link != '': is_link = True embed = obtain_embed_url(link) if embed is not None: embed_html = f'<iframe width="560" height="315" src="{embed}" allowfullscreen frameborder=\'0\'></iframe>' if body == None or body == '': raise XaieconException('Empty body') if title == None or title == '': raise XaieconException('Empty title') body_html = '' # Remove old image try: if post.is_image == True: os.remove(os.path.join('user_data',post.image_file)) os.remove(os.path.join('user_data',post.thumb_file)) except FileNotFoundError: pass file = request.files['image'] if file: try: # Build paths and filenames image_filename = secure_filename(f'{secrets.token_hex(12)}.jpeg') image_filepath = os.path.join('user_data',image_filename) thumb_filename = secure_filename(f'thumb_{image_filename}') thumb_filepath = os.path.join('user_data',thumb_filename) # Save full image file file.save(image_filepath) # Create thumbnail for image image = PIL.Image.open(image_filepath) image = image.convert('RGB') image.resize((128,128)) image.save(thumb_filepath) db.query(Post).filter_by(id=pid).update({ 'image_file':image_filename, 'thumb_file':thumb_filename, 'is_image':True, 'is_thumb':True}) except PIL.UnidentifiedImageError: # Failure creating image! db.query(Post).filter_by(id=pid).update({ 'is_image':False, 'is_thumb':False}) else: db.query(Post).filter_by(id=pid).update({ 'is_image':False, 'is_thumb':False}) if is_link == True: img = obtain_post_thumb(link) if img is not None: thumb_filename = secure_filename(f'thumb_{secrets.token_hex(12)}.jpeg') thumb_filepath = os.path.join('user_data',thumb_filename) img = img.convert('RGB') img.thumbnail((128,128)) img.save(thumb_filepath) db.query(Post).filter_by(id=pid).update({ 'is_thumb':True, 'thumb_file':thumb_filename}) body_html = md(body) # Update post entry on database db.query(Post).filter_by(id=pid).update({ 'keywords':keywords, 'body':body, 'body_html':body_html, 'is_link':is_link, 'is_nsfw':is_nsfw, 'title':title, 'link_url':link, 'category_id':category.id, 'body_html':body_html, 'embed_html':embed_html}) db.commit() csam_thread = threading.Thread(target=csam_check_post, args=(u.id,post.id,)) csam_thread.start() db.close() cache.delete_memoized(view,pid=pid) cache.delete_memoized(list_posts) cache.delete_memoized(list_nuked) cache.delete_memoized(list_feed) return redirect(f'/post/view/{pid}') else: categories = db.query(Category).all() db.close() return render_template('post/edit.html',u=u,title='Edit',post=post,categories=categories) except XaieconException as e: db.rollback() db.close() return render_template('user_error.html',u=u,title = 'Whoops!',err=e)
def create(u=None,pid=0): try: db = open_db() body = request.form.get('body') if len(body) == 0: raise XaieconException('Body too short') # Post exists in first place? post = db.query(Post).filter_by(id=pid).options(joinedload('*')).first() if post is None: abort(404) # Add comment comment = Comment(body=body,body_html=md(body),user_id=u.id,post_id=pid) db.add(comment) # Increment number of comments db.query(Post).filter_by(id=pid).update({'number_comments':post.number_comments+1}) db.commit() notif_msg = f'Comment by [/u/{comment.user_info.username}](/user/view/{post.user_info.id}) on [/b/{post.board_info.name}](/board/view/{post.board_info.id}) in post ***{post.title}*** [View](/comment/view/{comment.id})\n\r{comment.body}' idlist = [] if post.user_id != u.id and post.user_id not in idlist: idlist.append(post.user_id) # Notify followers follows = db.query(UserFollow).filter_by(target_id=u.id,notify=True).all() for f in follows: if f.user_id != u.id and f.user_id not in idlist: idlist.append(f.user_id) # Notify post poster if post.user_id != u.id and post.user_id not in idlist: idlist.append(post.user_id) # Notify commenter if comment.user_id != u.id and comment.user_id not in idlist: idlist.append(comment.user_id) for id in idlist: send_notification(notif_msg,id) ping = body.find('@everyone') if ping != -1 and u.is_admin == True: users = db.query(User).all() for us in users: if us.id != u.id: send_notification(notif_msg,us.id) ping = body.find('@here') if ping != -1 and u.mods(post.board_id): subs = db.query(BoardSub).filter_by(board_id=post.board_id).all() for s in subs: if s.user_id != u.id: send_notification(notif_msg,s.user_id) for m in re.finditer(r'([u][\/]|[@])([a-zA-Z0-9#][^ ,.;:\n\r\t<>\/\'])*\w+',body): m = m.group(0) print(m) try: name = re.split(r'([u][\/]|[@])',m)[2] tag = name.split('#') # direct mention if len(tag) > 1: uid = int(tag[1]) user = db.query(User).filter_by(id=uid).first() if user is None: raise IndexError send_notification(notif_msg,user.id) else: users = db.query(User).filter_by(username=name).all() if users is None: raise IndexError for user in users: send_notification(notif_msg,user.id) except IndexError: pass db.close() cache.delete_memoized(view_p) cache.delete_memoized(list_posts) cache.delete_memoized(list_nuked) cache.delete_memoized(list_feed) return redirect(f'/post/view/{pid}') except XaieconException as e: return render_template('user_error.html',u=u,title = 'Whoops!',err=e)