def on_accept_freq(data): freq = FriendshipRequest.query.filter_by(id=data["id"]).first() if freq.to_id == current_user.id: from_user = User.get_by_id(freq.from_id) new_friendship1 = Friendship(freq.from_id, freq.to_id) new_friendship2 = Friendship(freq.to_id, freq.from_id) db.session.add(new_friendship1) db.session.add(new_friendship2) emit("remove_friendship_request", data["id"], room=current_user.id) emit("new_friend", {"id": from_user.id, "name": from_user.name, "unreadCount": 0, "lastMsg": "None", "picUrl": from_user.small_avatar_url, "goToUrl": f"/{from_user.name}"}, room=current_user.id) emit("new_friend", {"id": current_user.id, "name": current_user.name, "unreadCount": 0, "lastMsg": "None", "picUrl": current_user.small_avatar_url, "goToUrl": f"/{current_user.name}"}, room=from_user.id) db.session.delete(freq) db.session.commit()
def msgs(): if not current_user.is_authenticated(): abort(404) chat_id = request.args.get("chat-id") chat_type = request.args.get("chat-type") if not chat_id or not chat_type: abort(400) msgs_in_chat = current_user.get_msgs_ordered_asc_by_datetime( chat_id, chat_type) if chat_type == "userToUser": for i, msg in enumerate(msgs_in_chat): msgs_in_chat[i] = { "content": msg.content, "datetime": f"{msg.datetime}+00:00", "fromId": msg.from_id, "fromName": User.get_by_id(msg.from_id).name if msg.from_id is not current_user.id else current_user.name, "toId": msg.to_id } elif chat_type == "chatGroup" or chat_type == "projectChatGroup": for i, msg in enumerate(msgs_in_chat): msgs_in_chat[i] = { "content": msg.content, "datetime": f"{msg.datetime}+00:00", "fromId": msg.from_id, "fromName": User.get_by_id(msg.from_id).name if msg.from_id is not current_user.id else current_user.name } else: abort(400) return jsonify(msgs_in_chat), 200
async def update_user(user_id: UUID, data: UserUpdateRequest) -> Response: user = await get_or_404(User.select(), id=user_id) update_data = data.dict(exclude_unset=True) course = set_attrs(user, **update_data) await db_manager.update(course) return APIResponse(UserResponse.from_orm(course).dict())
def user_profile_projects(username): user = User.get_by_name(username) if not user: abort(404) if not user.sign_up_complete: abort(404) current_user_is_friend_with_user = current_user.is_friend_with(user.id) projects = [] for project in user.projects.order_by(Project.name): if current_user.id == user.id \ or project.visibility == "public" \ or (project.visibility == "friends" and current_user_is_friend_with_user) \ or (project.visibility == "private" and current_user.is_project_collaborator_of(project.id)): projects.append({ "name": project.name, "link": f"/{project.owner_name}/{project.name}", "desc": project.project_desc }) if current_user.id == user.id: return render_template( "user_profile_projects.html.j2", user_id=user.id, user_profile_pic_url=user.avatar_url, username=username, useremail=user.email, userprofile_desc=user.profile_desc, token=user.set_token(), projects=projects, current_user_is_friend_with_user=current_user_is_friend_with_user) elif not user.private or current_user_is_friend_with_user: if user.email_private: return render_template("user_profile_projects.html.j2", user_id=user.id, user_profile_pic_url=user.avatar_url, username=username, userprofile_desc=user.profile_desc, projects=projects, current_user_is_friend_with_user= current_user_is_friend_with_user) return render_template( "user_profile_projects.html.j2", user_id=user.id, user_profile_pic_url=user.avatar_url, username=username, useremail=user.email, userprofile_desc=user.profile_desc, projects=projects, current_user_is_friend_with_user=current_user_is_friend_with_user) abort(404)
def login(): post_to = request.path forgot_password_link = "/forgot-password" url_query_str = "" continue_to = request.args.get("continue") if continue_to: url_query_str = "?continue=" + urllib.parse.quote(continue_to, safe="") post_to += url_query_str forgot_password_link += url_query_str # -- POST if request.method == "POST": form = request.form user = User.get_by_name_or_email(form["username"]) if user: if bcrypt.check_password_hash(user.password_hash, form["password"]): if form.get("keepUserLoggedIn") or user.stay_logged_in: login_user(user, True, timedelta(days=365)) if not user.stay_logged_in: user.stay_logged_in = True db.session.commit() else: login_user(user) continue_to = request.args.get("continue") if user.email_pending_verification is not None: return redirect( f"/verify-email/{user.email_verification_case_id}{url_query_str}" ) if continue_to: return redirect(continue_to) return redirect("/") return render_template( "login.html.j2", post_to=post_to, forgot_password_link=f"/forgot-password{url_query_str}", username=form["username"], invalidLogin=True) # -- GET if current_user.is_authenticated(): return redirect("/") return render_template( "login.html.j2", post_to=post_to, forgot_password_link=f"/forgot-password{url_query_str}")
async def get_current_username( credentials: HTTPBasicCredentials = Depends(security)): user = await get_or_404(User.select(), email=credentials.username) if credentials.password != user.password: raise HTTPException( status_code=HTTP_401_UNAUTHORIZED, detail="Incorrect email or password", headers={"WWW-Authenticate": "Basic"}, ) return user.id
def user_profile_friends(username): user = User.get_by_name(username) if not user: abort(404) if not user.sign_up_complete: abort(404) current_user_is_friend_with_user = current_user.is_friend_with(user.id) friends = [] for friend in db.session.query(User)\ .filter(User.id.in_(db.session.query(Friendship.friend_id).filter_by(user_id=user.id)))\ .with_entities(User.name, User.small_avatar_url): friends.append({ "name": friend.name, "link": f"/{friend.name}", "picUrl": friend.small_avatar_url }) if current_user.id == user.id: return render_template( "user_profile_friends.html.j2", user_id=user.id, user_profile_pic_url=user.avatar_url, username=username, useremail=user.email, userprofile_desc=user.profile_desc, token=user.set_token(), friends=friends, current_user_is_friend_with_user=current_user_is_friend_with_user) elif not user.private or current_user_is_friend_with_user: if user.email_private: return render_template("user_profile_friends.html.j2", user_id=user.id, user_profile_pic_url=user.avatar_url, username=username, userprofile_desc=user.profile_desc, friends=friends, current_user_is_friend_with_user= current_user_is_friend_with_user) return render_template( "user_profile_friends.html.j2", user_id=user.id, user_profile_pic_url=user.avatar_url, username=username, useremail=user.email, userprofile_desc=user.profile_desc, friends=friends, current_user_is_friend_with_user=current_user_is_friend_with_user) abort(404)
def new_friend_page(): if not current_user.is_authenticated(): return redirect("/") continue_to = request.args.get("continue") if not continue_to: continue_to = "/" name = request.args.get("name") if not name: name = "" if request.method == "POST": form = request.form target = User.get_by_name_or_email(form["name"]) if not target: return render_template("new_friend.html.j2", name_does_not_exist=True, continue_to=continue_to, post_to=f"/new-friend?continue={urllib.parse.quote(continue_to, safe='')}") if target.id == current_user.id \ or Friendship.query.filter_by(user_id=current_user.id, friend_id=target.id).scalar() \ or FriendshipRequest.query.filter_by(from_id=current_user.id, to_id=target.id).scalar(): return render_template("new_friend.html.j2", invalid_target=True, continue_to=continue_to, post_to=f"/new-friend?continue={urllib.parse.quote(continue_to, safe='')}") if not target.block_all_friendship_requests: friendship_request = FriendshipRequest(current_user.id, target.id) db.session.add(friendship_request) db.session.commit() socketio.emit("receive_friendship_request", {"id": friendship_request.id, "type": "friendshipRequest", "fromName": current_user.name, "picUrl": current_user.small_avatar_url, "goToUrl": f"/{current_user.name}", "datetime": f"{friendship_request.datetime}+00:00"}, room=target.id, namespace="/") return redirect(continue_to) return render_template("new_friend.html.j2", continue_to=continue_to, name=name)
def on_change_project_owner(data): if data["projectId"] in rooms() and current_user.is_project_owner_of( data["projectId"]): project = Project.query.filter_by(id=data["projectId"]).first() new_owner_user = User.get_by_name_or_email(data["nameOrEmail"]) if not new_owner_user or not new_owner_user.is_project_collaborator_of(data["projectId"]) \ or Project.query.filter_by(owner_name=new_owner_user.name).scalar(): emit("change_project_owner_error_invalid_target", room=request.sid) return ProjectCollaboratorLink.query.filter_by( project_id=data["projectId"], user_id=new_owner_user.id).delete() project.owner_name = new_owner_user.name db.session.add( ProjectCollaboratorLink(data["projectId"], current_user.id, "admin")) db.session.commit() project_data = { "projectId": project.id, "name": project.name, "ownerName": project.owner_name, "projectDesc": project.project_desc } for collab in db.session.query( ProjectCollaboratorLink.user_id).filter_by( project_id=project.id).all(): emit("update_project_attributes", project_data, room=collab.user_id, namespace="/") emit("update_project_attributes", project_data, room=new_owner_user.id, namespace="/") emit("force_reload", room=current_user.id, namespace="/") emit("force_reload", room=new_owner_user.id, namespace="/")
def on_add_friend_to_chat_group(data): current_user_member_link = ChatGroupMemberLink.query.filter_by(user_id=current_user.id, chat_group_id=data["chatGroupId"]) \ .first() if not current_user_member_link or current_user_member_link.user_role != "admin": return friend = User.get_by_name_or_email(data["friendNameOrEmail"]) if not friend \ or ChatGroupMemberLink.query.filter_by(user_id=friend.id, chat_group_id=data["chatGroupId"]).scalar()\ or not current_user.is_friend_with(friend.id): emit("add_friend_to_chat_group_error_invalid_target", room=current_user.id) return chat_group = ChatGroup.query.filter_by(id=data["chatGroupId"]).first() db.session.add(ChatGroupMemberLink(data["chatGroupId"], friend.id, "default")) db.session.commit() member_ids_to_emit_new_chat_group_member_info_to = [] members = {} for member_user in db.session.query(ChatGroupMemberLink) \ .filter_by(chat_group_id=chat_group.id) \ .join(User, User.id == ChatGroupMemberLink.user_id)\ .with_entities(User.id, User.name, User.small_avatar_url, ChatGroupMemberLink.user_role): members[member_user.id] = { "id": member_user.id, "name": member_user.name, "picUrl": member_user.small_avatar_url, "goToUrl": f"/{member_user.name}", "role": member_user.user_role } if member_user.id != friend.id: member_ids_to_emit_new_chat_group_member_info_to.append(member_user.id) last_msg = friend.get_last_msg(chat_group.id, "chatGroup") emit("new_chat_group", {"id": chat_group.id, "type": "chatGroup", "name": chat_group.name, "unreadCount": friend.get_unread_count_in_chat(chat_group.id, "chatGroup"), "lastMsg": {"content": last_msg.content, "datetime": f"{last_msg.datetime}+00:00"} if last_msg else "None", "picUrl": "/static/img/group.png", "roleOfCurrentUser": "******", "members": members}, room=friend.id) for id_ in member_ids_to_emit_new_chat_group_member_info_to: emit("new_chat_group_member", {"chatGroupId": chat_group.id, "chatGroupType": "chatGroup", "member": {"id": friend.id, "name": friend.name, "picUrl": friend.small_avatar_url, "goToUrl": f"/{friend.name}", "role": "default"} }, room=id_) emit("successfully_added_friend_to_chat_group", room=current_user.id)
def on_add_friend_to_project(data): if data["projectId"] in rooms() and current_user.is_project_owner_of( data["projectId"]): project = Project.query.filter_by(id=data["projectId"]).first() friend = User.get_by_name_or_email(data["friendNameOrEmail"]) if not friend or not current_user.is_friend_with(friend.id) \ or friend.is_project_collaborator_of(project.id): emit("add_friend_to_project_error_invalid_target", room=request.sid) return if data["role"] != "admin" and data["role"] != "access-only": return db.session.add( ProjectCollaboratorLink(data["projectId"], friend.id, data["role"])) project_chat_group = project.chat_group.first() if project_chat_group: member_data = { friend.id: { "id": friend.id, "role": "default", "name": friend.name, "picUrl": friend.small_avatar_url, "goToUrl": f"/{friend.name}" } } for member_user in db.session.query(ProjectChatGroupMemberLink) \ .filter_by(project_chat_group_id=project_chat_group.id) \ .join(User, User.id == ProjectChatGroupMemberLink.user_id) \ .with_entities(User.id, User.name, User.small_avatar_url): member_data[member_user.id] = { "id": member_user.id, "name": member_user.name, "picUrl": member_user.small_avatar_url, "goToUrl": f"/{member_user.name}", "role": "default" } emit("new_chat_group_member", { "chatGroupId": project_chat_group.id, "chatGroupType": "projectChatGroup", "member": { "id": friend.id, "name": friend.name, "picUrl": friend.small_avatar_url, "goToUrl": f"/{friend.name}", "role": "default" } }, namespace="/", room=member_user.id) db.session.add( ProjectChatGroupMemberLink(project_chat_group.id, friend.id, "default")) last_msg = current_user.get_last_msg(project_chat_group.id, "projectChatGroup") last_msg = { "content": last_msg.content, "datetime": f"{last_msg.datetime}+00:00" } if last_msg else "None" emit("new_chat_group", { "id": project_chat_group.id, "type": "projectChatGroup", "name": project_chat_group.name, "unreadCount": 0, "lastMsg": last_msg, "picUrl": "/static/img/group.png", "roleOfCurrentUser": "******", "members": member_data }, room=friend.id, namespace="/") db.session.commit() emit("successfully_added_friend_to_project", room=request.sid) emit("new_project_collab", { "id": friend.id, "name": friend.name, "picUrl": friend.small_avatar_url, "goToUrl": f"/{friend.name}", "role": data["role"] }, room=data["projectId"]) emit("now_collab_of_project", { "id": data["projectId"], "name": project.name, "ownerName": project.owner_name, "goToUrl": f"/{project.owner_name}/{project.name}" }, namespace="/", room=friend.id)
def user_profile_settings(username): if not current_user.name == username: abort(404) user = User.get_by_name(username) if not user: abort(404) if not user.sign_up_complete: abort(404) invalid_names_list = invalid_names() # -- POST if request.method == "POST": # - "switch" settings request_json = request.get_json() if request_json and request_json["setting"]: if request_json["token"] == user.token: # Email private if request_json["setting"] == "useremailPrivate": if request_json["value"]: user.email_private = True else: user.email_private = False # Profile private elif request_json["setting"] == "userprofilePrivate": if request_json["value"]: user.private = True else: user.private = False # Stay logged in elif request_json["setting"] == "keepUserLoggedIn": if request_json["value"] and not user.stay_logged_in: user.stay_logged_in = True logout_user() login_user(user, True, timedelta(days=365)) elif not request_json["value"] and user.stay_logged_in: if user.stay_logged_in: user.stay_logged_in = False logout_user() login_user(user) user.stay_logged_in = False # Block all friendship requests elif request_json["setting"] == "blockAllFriendshipRequests": if request_json["value"]: user.block_all_friendship_requests = True else: user.block_all_friendship_requests = False db.session.commit() return make_response(Response(""), 200) else: abort(419) # - "form" settings form = request.form if form and form["submit"] != "editProfile": lex, _ = get_lexicon_and_lang("user_profile_settings_view_func") if form["token"] == user.token: # Change username if form["submit"] == "changeUsername": change_username_invalid = False change_username_username_error_text = "" change_username_password_error_text = "" if User.query.filter(User.name.ilike(form['newUsername'])).scalar() \ or form['newUsername'] in invalid_names_list: change_username_username_error_text = lex[ "This username is not available"] change_username_invalid = True regexexp = re.compile("^[a-zA-Z][a-zA-Z0-9_-]*$") if not 1 <= len( form["newUsername"]) <= 16 or not regexexp.search( form["newUsername"]): change_username_username_error_text = lex[ "Invalid username"] change_username_invalid = True if not bcrypt.check_password_hash(user.password_hash, form["password"]): change_username_password_error_text = lex[ "Wrong password"] change_username_invalid = True if change_username_invalid: return render_template("user_profile_settings.html.j2", user_id=user.id, invalid_names=invalid_names_list, user_profile_pic_url=user.avatar_url, username=username, userprofile_desc=user.profile_desc, useremail=user.email, token=user.set_token(), change_username_invalid=True, new_username=form['newUsername'], change_username_password_error_text=change_username_password_error_text, change_username_username_error_text=change_username_username_error_text ), \ 400 for project in user.projects: project.owner_name = form["newUsername"] project_data = { "projectId": project.id, "name": project.name, "ownerName": project.owner_name, "projectDesc": project.project_desc } for collab in db.session.query(ProjectCollaboratorLink.user_id)\ .filter_by(project_id=project.id): socketio.emit("update_project_attributes", project_data, room=collab.user_id, namespace="/") socketio.emit("update_project_attributes", project_data, room=user.id, namespace="/") user.name = form["newUsername"] # change avatar if default bucket, _ = split_s3_obj_url(user.avatar_url) if bucket != app.config[ "S3_BUCKET_NAME"]: # => default avatar rand_n = random.randint(1, 4) user.avatar_url = f"{app.config['S3_STATIC_BUCKET_URL']}/default_avatar/" \ f"{form['newUsername'][0].lower()}{rand_n}x.png" user.small_avatar_url = f"{app.config['S3_STATIC_BUCKET_URL']}/default_avatar/" \ f"{form['newUsername'][0].lower()}{rand_n}s.png" db.session.commit() for friendship in user.friendships: socketio.emit("update_friend_name", { "id": user.id, "name": user.name }, room=friendship.friend_id, namspace="/") return redirect(f"/{user.name}/settings") # Change email if form["submit"] == "changeEmail": change_email_invalid = False change_email_email_error_text = "" change_email_password_error_text = "" if User.get_by_name_or_email(form['newEmail']) \ or User.query.filter_by(email_pending_verification=form['newEmail']).scalar(): change_email_email_error_text = lex[ "This email address is not available"] change_email_invalid = True if not 1 <= len(form["newEmail"]) <= 32: change_email_email_error_text = lex[ "Invalid email address"] change_email_invalid = True if not bcrypt.check_password_hash(user.password_hash, form["password"]): change_email_password_error_text = lex[ "Wrong password"] change_email_invalid = True if change_email_invalid: return render_template("user_profile_settings.html.j2", user_id=user.id, invalid_names=invalid_names_list, user_profile_pic_url=user.avatar_url, username=username, userprofile_desc=user.profile_desc, useremail=user.email, token=user.set_token(), change_email_invalid=True, new_email=form['newEmail'], change_email_email_error_text=change_email_email_error_text, change_email_password_error_text=change_email_password_error_text ), \ 400 email_verification_case_id = uuid4().hex email_verification_code = token_urlsafe(16) user.email_pending_verification = form["newEmail"] user.email_verification_case_id = email_verification_case_id user.email_verification_code = email_verification_code db.session.commit() email_verification_path_with_code = email_verification_case_id + f"?code={email_verification_code}" email_html = render_template( "email/verify_new_email.html.j2", user_id=user.id, username=user.name, email=user.email_pending_verification, code=email_verification_code, link="https://taskstack.org/verify-email/" + email_verification_path_with_code) email_txt = render_template( "email/verify_new_email.txt.j2", user_id=user.id, username=user.name, email=user.email_pending_verification, code=email_verification_code, link="https://taskstack.org/verify-email/" + email_verification_path_with_code) ses_cli.send_email( Source='*****@*****.**', Destination={ 'ToAddresses': [ user.email_pending_verification, ] }, Message={ 'Subject': { 'Data': lex['[Taskstack] Verify your new email address'], 'Charset': 'UTF-8' }, 'Body': { 'Text': { 'Data': email_txt, 'Charset': 'UTF-8' }, 'Html': { 'Data': email_html, 'Charset': 'UTF-8' } } }) return redirect( f"/verify-email/{email_verification_case_id}" f"?continue={urllib.parse.quote(request.path, safe='')}" ) # Change password elif form["submit"] == "changePassword": change_password_invalid = False change_password_password_error_text = "" change_password_new_password_error_text = "" if not bcrypt.check_password_hash(user.password_hash, form["old_password"]): change_password_password_error_text = lex[ "Wrong password"] change_password_invalid = True if not 8 <= len(form["newPassword"]) <= 64: change_password_password_error_text = lex[ "Invalid password"] change_password_invalid = False if change_password_invalid: return render_template("user_profile_settings.html.j2", user_id=user.id, invalid_names=invalid_names_list, user_profile_pic_url=user.avatar_url, username=username, useremail=user.email, userprofile_desc=user.profile_desc, token=user.set_token(), change_password_invalid=True, change_password_password_error_text=change_password_password_error_text, change_password_new_password_error_text= change_password_new_password_error_text ), \ 400 user.password_hash = bcrypt.generate_password_hash( form["newPassword"]).decode("utf-8") db.session.commit() return redirect(f"/{user.name}/settings") # Delete account elif form["submit"] == "deleteAccount": if not bcrypt.check_password_hash(user.password_hash, form["password"]): delete_account_password_error_text = lex[ "Wrong password"] return render_template("user_profile_settings.html.j2", user_id=user.id, invalid_names=invalid_names_list, user_profile_pic_url=user.avatar_url, username=username, useremail=user.email, userprofile_desc=user.userprofile_desc, token=user.set_token(), delete_account_invalid=True, delete_account_password_error_text=delete_account_password_error_text ), \ 400 logout_user() for project in user.projects.with_entities(Project.id): socketio.emit("project_deleted", room=project.id, namespace="/project") # delete user avatar from s3 bucket avatar_bucket, avatar_key = split_s3_obj_url( user.avatar_url) small_avatar_bucket, small_avatar_key = split_s3_obj_url( user.small_avatar_url) if avatar_bucket == app.config["S3_BUCKET_NAME"]: s3_cli.delete_object(Bucket=avatar_bucket, Key=avatar_key) s3_cli.delete_object(Bucket=small_avatar_bucket, Key=small_avatar_key) # delete projects for project in user.projects: for _list in project.lists: for card in _list.cards: for file in card.attached_files: _, key = split_s3_obj_url(file.url) s3_cli.delete_object( Bucket=app.config["S3_BUCKET_NAME"], Key=key) db.session.delete(file) CardUserAssignment.query.filter_by( card_id=card.id).delete() db.session.delete(card) for file in _list.attached_files: _, key = split_s3_obj_url(file.url) s3_cli.delete_object( Bucket=app.config["S3_BUCKET_NAME"], Key=key) db.session.delete(file) db.session.delete(_list) project_chat_group = project.chat_group.first() if project_chat_group: ProjectChatGroupMsgStatus.query.filter_by( project_chat_group_id=project_chat_group.id ).delete() ProjectChatGroupMsg.query.filter_by( project_chat_group_id=project_chat_group.id ).delete() project_chat_group.member_links.delete() db.session.delete(project_chat_group) for collab in db.session.query(ProjectCollaboratorLink.user_id)\ .filter_by(project_id=project.id): socketio.emit("removed_as_collab_of_project", project.id, room=collab.user_id, namspace="/") if project_chat_group: socketio.emit("chat_group_removed", { "type": "projectChatGroup", "id": project_chat_group.id }, room=collab.user_id, namspace="/") ProjectCollaboratorLink.query.filter_by( project_id=project.id).delete() db.session.delete(project) # delete project collab links for project_collaboration_link in user.project_collaboration_links: socketio.emit( "project_collab_removed", {"id": user.id}, room=project_collaboration_link.project_id, namespace="/project") db.session.delete(project_collaboration_link) # delete card assignments user.card_assignments.delete() # delete chat group member links for chat_group_member_link in user.chat_group_member_links: for member in db.session.query(ChatGroupMemberLink.user_id) \ .filter_by(chat_group_id=chat_group_member_link.chat_group_id).all(): socketio.emit( "chat_group_member_removed", { "userId": user.id, "chatGroupId": chat_group_member_link.chat_group_id, "chatGroupType": "chatGroup" }, room=member.user_id, namspace="/") db.session.delete(chat_group_member_link) for project_chat_group_member_link in user.project_chat_group_member_links: for member in db.session.query(ProjectChatGroupMemberLink.user_id) \ .filter_by(project_chat_group_id=project_chat_group_member_link.chat_group_id).all(): socketio.emit("chat_group_member_removed", { "userId": user.id, "chatGroupId": project_chat_group_member_link.chat_group_id, "chatGroupType": "projectChatGroup" }, room=member.user_id, namspace="/") db.session.delete(project_chat_group_member_link) # delete friendships + friendship reqs for friendship in user.friendships: socketio.emit("friend_removed", {"id": user.id}, room=friendship.friend_id, namspace="/") Friendship.query.filter( or_(Friendship.friend_id == user.id, Friendship.user_id == user.id)).delete() FriendshipRequest.query.filter( or_(FriendshipRequest.from_id == user.id, FriendshipRequest.to_id == user.id)) \ .delete() # delete msgs Msg.query.filter( or_(Msg.from_id == user.id, Msg.to_id == user.id)).delete() ChatGroupMsg.query.filter_by(from_id=user.id).delete() ProjectChatGroupMsg.query.filter_by( from_id=user.id).delete() ChatGroupMsgStatus.query.filter_by( user_id=user.id).delete() ProjectChatGroupMsgStatus.query.filter_by( user_id=user.id).delete() # delete pot. resulting empty chat groups ChatGroup.query.filter(ChatGroup.id.notin_(db.session.query(ChatGroupMemberLink.chat_group_id))) \ .delete(synchronize_session=False) ProjectChatGroup.query \ .filter(ChatGroup.id .notin_(db.session.query(ProjectChatGroupMemberLink.project_chat_group_id)))\ .delete(synchronize_session=False) db.session.delete(user) db.session.commit() return redirect("/") else: return render_template("user_profile_settings.html.j2", user_id=user.id, invalid_names=invalid_names_list, user_profile_pic_url=user.avatar_url, username=username, useremail=user.email, userprofile_desc=user.profile_desc, token=user.set_token(), invalid_token=True ), \ 419 abort(400) # -- GET return render_template("user_profile_settings.html.j2", user_id=user.id, invalid_names=invalid_names_list, user_profile_pic_url=user.avatar_url, username=username, useremail=user.email, userprofile_desc=user.profile_desc, token=user.set_token())
async def get_users() -> Response: users = await execute(User.select()) return APIResponse(invoke(map(UserResponse.from_orm, users), 'dict'))
async def get_user(user_id: UUID = Depends(get_current_username)) -> Response: user = await get_or_404(User.select(), id=user_id) return APIResponse(UserResponse.from_orm(user).dict(), status_code=HTTPStatus.OK)
def user_profile(username): user = User.get_by_name(username) if not user: abort(404) if not user.sign_up_complete: abort(404) # -- POST if request.method == "POST": form = request.form if form["submit"] == "editProfile": if current_user.name != username: abort(404) if form["token"] != user.token: return make_response(Response(""), 419) try: # change profile pic pic = PIL.Image.open(request.files["newProfilePic"].stream) small_pic = pic.copy() pic.thumbnail((280, 280)) small_pic.thumbnail((50, 50)) pic_fileobj = io.BytesIO() pic.save(pic_fileobj, format="png") pic_fileobj.seek(0) small_pic_fileobj = io.BytesIO() small_pic.save(small_pic_fileobj, format="png") small_pic_fileobj.seek(0) key = uuid4().hex + ".png" small_pic_key = uuid4().hex + ".png" s3_cli.upload_fileobj(pic_fileobj, app.config["S3_BUCKET_NAME"], key, ExtraArgs={ 'ACL': 'public-read', 'CacheControl': 'max-age: 86400', "ContentType": "image/png" }) s3_cli.upload_fileobj(small_pic_fileobj, app.config["S3_BUCKET_NAME"], small_pic_key, ExtraArgs={ 'ACL': 'public-read', 'CacheControl': 'max-age: 86400', "ContentType": "image/png" }) # delete prev avatar if not default avatar prev_bucket, prev_key = split_s3_obj_url(user.avatar_url) small_pic_prev_bucket, small_pic_prev_key = split_s3_obj_url( user.small_avatar_url) if prev_bucket == app.config[ "S3_BUCKET_NAME"]: # => not default avatar s3_cli.delete_object(Bucket=prev_bucket, Key=prev_key) s3_cli.delete_object(Bucket=small_pic_prev_bucket, Key=small_pic_prev_key) user.avatar_url = app.config["S3_BUCKET_URL"] + "/" + key user.small_avatar_url = app.config[ "S3_BUCKET_URL"] + "/" + small_pic_key except BadRequestKeyError: pass # change profile desc if len(form["newProfileDesc"]) <= 256: user.profile_desc = form["newProfileDesc"] db.session.commit() return make_response(Response(""), 200) elif form["submit"] == "removeFriend": f1 = Friendship.query.filter_by(user_id=current_user.id, friend_id=user.id).first() f2 = Friendship.query.filter_by(user_id=user.id, friend_id=current_user.id).first() if f1 and f2: db.session.delete(f1) db.session.delete(f2) db.session.commit() socketio.emit("friend_removed", {"id": current_user.id}, room=user.id, namespace="/") return make_response(Response(""), 200) abort(400) # -- GET if current_user.name == username or not user.private or current_user.is_friend_with( user.id): return redirect(f"/{username}/projects") abort(404)
async def create_user(data: UserCreateRequest) -> Response: user_id = await execute(User.insert(**data.dict())) user = first(await execute(User.filter(id=user_id))) return APIResponse(UserResponse.from_orm(user).dict(), status_code=HTTPStatus.CREATED)
def home(): # home if auth if current_user.is_authenticated(): # projects projects_where_is_owner = [] for project in current_user.projects: projects_where_is_owner.append({ "id": project.id, "ownerName": project.owner_name, "name": project.name, "goToUrl": f"/{project.owner_name}/{project.name}" }) projects_where_is_collab = current_user.get_projects_where_is_collaborator( ) for i, project in enumerate(projects_where_is_collab): projects_where_is_collab[i] = { "id": project.id, "ownerName": project.owner_name, "name": project.name, "goToUrl": f"/{project.owner_name}/{project.name}" } # notifications notifications = current_user.get_notifications() for i, notification in enumerate(notifications): from_user = User.get_by_id(notification.from_id) if isinstance(notification, Msg): notifications[i] = { "id": notification.id, "type": "msg", "chatType": "userToUser", "picUrl": from_user.small_avatar_url, "goToUrl": f"/{from_user.name}", "goToChatUrl": f"/chat?with={from_user.name}", "datetime": f"{notification.datetime}+00:00", "content": notification.content } elif isinstance(notification, ChatGroupMsg): notifications[i] = { "id": notification.id, "type": "msg", "chatType": "chatGroup", "picUrl": from_user.small_avatar_url, "goToUrl": f"/{from_user.name}", "goToChatUrl": "/chat", "datetime": f"{notification.datetime}+00:00", "content": notification.content } elif isinstance(notification, ProjectChatGroupMsg): notifications[i] = { "id": notification.id, "type": "msg", "chatType": "projectChatGroup", "picUrl": from_user.small_avatar_url, "goToUrl": f"/{from_user.name}", "goToChatUrl": "/chat", "datetime": f"{notification.datetime}+00:00", "content": notification.content } elif isinstance(notification, FriendshipRequest): notifications[i] = { "id": notification.id, "type": "friendshipRequest", "picUrl": from_user.small_avatar_url, "goToUrl": f"/{from_user.name}", "datetime": f"{notification.datetime}+00:00" } # friends friends = current_user.get_friends() for i, friend in enumerate(friends): friends[i] = { "id": friend.id, "name": friend.name, "picUrl": friend.small_avatar_url, "goToUrl": f"/{friend.name}" } return render_template("home.auth.html.j2", projects={ "whereOwner": projects_where_is_owner, "whereCollab": projects_where_is_collab }, notifications=notifications, friends=friends) # home if not auth return render_template("home.unauth.html.j2")
async def delete_user(user_id: UUID) -> Response: await get_or_404(User.select(), id=user_id) await execute(User.delete().where(User.id == user_id)) return APIResponse(status_code=HTTPStatus.NO_CONTENT)
def load_user(user_id): return User.get_by_id(user_id)
def chat(): if not current_user.is_authenticated(): abort(404) chats = {"userToUser": {}, "chatGroup": {}, "projectChatGroup": {}} for friend in current_user.get_friends(): last_msg = current_user.get_last_msg(friend.id, "userToUser") last_msg = { "content": last_msg.content, "datetime": f"{last_msg.datetime}+00:00" } if last_msg else "None" chats["userToUser"][friend.id] = { "id": friend.id, "type": "userToUser", "name": friend.name, "unreadCount": current_user.get_unread_count_in_chat(friend.id, "userToUser"), "lastMsg": last_msg, "picUrl": friend.small_avatar_url, "goToUrl": f"/{friend.name}" } for chat_group in current_user.get_chat_groups_where_member(): last_msg = current_user.get_last_msg(chat_group.id, "chatGroup") last_msg = { "content": last_msg.content, "datetime": f"{last_msg.datetime}+00:00" } if last_msg else "None" members = {} for member_user in db.session.query(ChatGroupMemberLink) \ .filter_by(chat_group_id=chat_group.id) \ .join(User, User.id == ChatGroupMemberLink.user_id) \ .with_entities(User.id, User.name, User.small_avatar_url, ChatGroupMemberLink.user_role): members[member_user.id] = { "id": member_user.id, "name": member_user.name, "picUrl": member_user.small_avatar_url, "goToUrl": f"/{member_user.name}", "role": member_user.user_role } chats["chatGroup"][chat_group.id] = { "id": chat_group.id, "type": "chatGroup", "name": chat_group.name, "unreadCount": current_user.get_unread_count_in_chat(chat_group.id, "chatGroup"), "lastMsg": last_msg, "picUrl": "/static/img/group.png", "roleOfCurrentUser": chat_group.user_role, "members": members } for project_chat_group in current_user.get_project_chat_groups_where_member( ): last_msg = current_user.get_last_msg(project_chat_group.id, "projectChatGroup") last_msg = { "content": last_msg.content, "datetime": f"{last_msg.datetime}+00:00" } if last_msg else "None" members = {} for member_user in db.session.query(ProjectChatGroupMemberLink) \ .filter_by(project_chat_group_id=project_chat_group.id) \ .join(User, User.id == ProjectChatGroupMemberLink.user_id) \ .with_entities(User.id, User.name, User.small_avatar_url): members[member_user.id] = { "id": member_user.id, "name": member_user.name, "picUrl": member_user.small_avatar_url, "goToUrl": f"/{member_user.name}", "role": "default" } chats["projectChatGroup"][project_chat_group.id] = { "id": project_chat_group.id, "type": "projectChatGroup", "name": project_chat_group.name, "unreadCount": current_user.get_unread_count_in_chat(project_chat_group.id, "projectChatGroup"), "lastMsg": last_msg, "picUrl": "/static/img/group.png", "roleOfCurrentUser": project_chat_group.user_role, "members": members } user_to_chat_with_name = request.args.get("with") if user_to_chat_with_name: member_user = User.get_by_name(user_to_chat_with_name) if member_user: if current_user.is_friend_with(member_user.id): return render_template( "chat.html.j2", chats=chats, chat_to_open={ "id": User.get_by_name(user_to_chat_with_name).id, "type": "userToUser" }) return render_template("chat.html.j2", chats=chats)
def signup(): continue_to = request.args.get("continue") invalid_names_list = invalid_names() # -- POST if request.method == "POST": form = request.form valid = True lex, _ = get_lexicon_and_lang("signup_view_func") name_error_text = "" email_error_text = "" password_error_text = "" if User.query.filter(User.name.ilike(form['username'])).scalar( ) or form['username'] in invalid_names_list: name_error_text = lex["This username is not available"] valid = False if User.get_by_name_or_email(form['useremail']) \ or User.query.filter_by(email_pending_verification=form['useremail']).scalar(): email_error_text = lex["This email address is not available"] valid = False regexexp = re.compile("^[a-zA-Z][a-zA-Z0-9_-]*$") if not 3 <= len(form["username"]) <= 16 or not regexexp.search( form["username"]): name_error_text = lex["Invalid username"] valid = False if not 1 <= len(form["useremail"]) <= 32: email_error_text = lex["Invalid email address"] valid = False if not 8 <= len(form["password"]) <= 64: password_error_text = lex["Invalid password"] valid = False if not valid: return render_template("signup.html.j2", invalid_names=invalid_names_list, username=form["username"], useremail=form["useremail"], name_error_text=name_error_text, email_error_text=email_error_text, password_error_text=password_error_text) email_verification_case_id = uuid4().hex email_verification_code = token_urlsafe(16)[:8] if form.get("keepUserLoggedIn"): user = User( form["username"], form["useremail"], bcrypt.generate_password_hash( form["password"]).decode("utf-8"), email_verification_case_id, email_verification_code, True) db.session.add(user) db.session.commit() login_user(user, True, timedelta(days=365)) else: user = User( form["username"], form["useremail"], bcrypt.generate_password_hash( form["password"]).decode("utf-8"), email_verification_case_id, email_verification_code, False) db.session.add(user) db.session.commit() login_user(user) verify_email_path = email_verification_case_id verify_email_path_with_code = verify_email_path if continue_to: verify_email_path += f"?continue={urllib.parse.quote(continue_to, safe='')}" verify_email_path_with_code = verify_email_path + f"&code={email_verification_code}" else: verify_email_path_with_code += f"?code={email_verification_code}" email_html = render_template( "email/account_created_verify_email.html.j2", username=user.name, email=user.email_pending_verification, code=email_verification_code, link="https://taskstack.org/verify-email/" + verify_email_path_with_code) email_txt = render_template( "email/account_created_verify_email.txt.j2", username=user.name, email=user.email_pending_verification, code=email_verification_code, link="https://taskstack.org/verify-email/" + verify_email_path_with_code) ses_cli.send_email( Source='*****@*****.**', Destination={'ToAddresses': [ user.email_pending_verification, ]}, Message={ 'Subject': { 'Data': lex['[Taskstack] Verify your email address'], 'Charset': 'UTF-8' }, 'Body': { 'Text': { 'Data': email_txt, 'Charset': 'UTF-8' }, 'Html': { 'Data': email_html, 'Charset': 'UTF-8' } } }) return redirect("/verify-email/" + verify_email_path) # -- GET if current_user.is_authenticated(): return redirect("/") post_to = request.path if continue_to: post_to += "?continue=" + urllib.parse.quote(continue_to, safe="") return render_template( "signup.html.j2", post_to=post_to, invalid_names=invalid_names_list, )
def forgot_password(): continue_to = request.args.get("continue") url_query_str = "?" if continue_to: url_query_str += "continue=" + urllib.parse.quote(continue_to, safe="") # -- POST if request.method == "POST": form = request.form user = User.get_by_name_or_email(form["name"]) if not user: return render_template("forgot_password.html.j2", post_to=request.path + url_query_str, username_or_email_does_not_exist=True) user.reset_password_case_id = uuid4().hex reset_password_code = token_urlsafe(16) user.reset_password_code_hash = bcrypt.generate_password_hash( reset_password_code).decode("utf-8") db.session.commit() lex, _ = get_lexicon_and_lang("forgot_password_view_func") reset_password_path_with_code = user.reset_password_case_id + f"?code={reset_password_code}" email_html = render_template( "email/reset_password.html.j2", code=reset_password_code, link="https://taskstack.org/reset-password/" + reset_password_path_with_code) email_txt = render_template( "email/reset_password.txt.j2", code=reset_password_code, link="https://taskstack.org/reset-password/" + reset_password_path_with_code) ses_cli.send_email(Source='*****@*****.**', Destination={'ToAddresses': [ user.email, ]}, Message={ 'Subject': { 'Data': lex['[Taskstack] Reset password'], 'Charset': 'UTF-8' }, 'Body': { 'Text': { 'Data': email_txt, 'Charset': 'UTF-8' }, 'Html': { 'Data': email_html, 'Charset': 'UTF-8' } } }) return redirect("/reset-password/" + user.reset_password_case_id + url_query_str) # -- GET return render_template("forgot_password.html.j2", post_to=request.path + url_query_str)