def create_with_password(moderator: ModeratorModel, password: str): """ Create a moderator account. The password is given unhashed. :param moderator: filled in moderator model :param password: password to create moderator with :raises ArgumentError if the username or password doesn't fit the requirements. See {USERNAME,PASSWORD}_{MIN,MAX}_LENGTH and {USERNAME,PASSWORD}_ALLOWED_CHARS :raises ArgumentError if the username is already in use. Username checking is done case-insensitively """ if not validation.check_username_validity(moderator.username): raise ArgumentError(MESSAGE_INVALID_USERNAME) if not validation.check_password_validity(password): raise ArgumentError(MESSAGE_INVALID_PASSWORD) if find_by_username_case_insensitive(moderator.username) is not None: raise ArgumentError(MESSAGE_USERNAME_IN_USE) with session() as s: orm_moderator = moderator.to_orm_model() orm_moderator.password = _hash_password(password) s.add(orm_moderator) s.flush() moderator = ModeratorModel.from_orm_model(orm_moderator) s.commit() return moderator
def get_all(offset: int, limit: int): with session() as s: q = s.query(BanOrmModel).order_by(desc( BanOrmModel.date)).limit(limit).offset(offset) r = list(map(lambda i: BanModel.from_orm_model(i), q.all())) s.commit() return r
def is_verified(verifying_client: VerifyingClient) -> bool: verification_model = None verification_model_cache = cache.get( cache_key('verifications', verifying_client.verification_id)) if verification_model_cache: verification_model = VerificationsModel.from_cache( verification_model_cache) if not verification_model: with session() as s: q = s.query(VerificationOrmModel) q = q.filter_by(verification_id=verifying_client.verification_id) verifications_orm_model = q.one_or_none() if verifications_orm_model: verification_model = VerificationsModel.from_orm_model( verifications_orm_model) cached = verification_model.to_cache() timeout = max(1, (verification_model.expires - now()) // 1000) cache.set(cache_key('verifications', verification_model.id), cached, timeout=timeout) s.commit() return verification_model and _is_verifications_valid( verifying_client, verification_model)
def find_post_by_id(post_id: int, include_thread=False) -> Optional[PostModel]: with session() as s: m = s.query(PostOrmModel).filter_by(id=post_id).one_or_none() res = None if m: res = PostModel.from_orm_model(m, include_thread=include_thread) return res
def find_thread_by_board_thread_refno_with_posts( board: BoardModel, thread_refno: int) -> Optional[ThreadModel]: thread_cache = cache.get(cache_key('thread', board.name, thread_refno)) if not thread_cache: with session() as s: q = s.query(ThreadOrmModel) q = q.options(lazyload('posts')) q = q.filter(ThreadOrmModel.refno == thread_refno, ThreadOrmModel.board_id == BoardOrmModel.id, BoardOrmModel.name == board.name) thread_orm_model = q.one_or_none() if not thread_orm_model or not thread_orm_model.posts: return None # TODO: also load board in q above thread = ThreadModel.from_orm_model(thread_orm_model, include_board=True, include_posts=True) thread_cache = thread.to_cache(include_board=True, include_posts=True) cache.set(cache_key('thread', thread.board.name, thread.refno), thread_cache, timeout=0) return thread if thread_cache: return ThreadModel.from_cache(thread_cache) return None
def find_by_id(page_id: int) -> PageModel: with session() as s: m = s.query(PageOrmModel).filter_by(id=page_id).one_or_none() res = None if m: res = PageModel.from_orm_model(m) return res
def moderator_has_board_id(moderator: ModeratorModel, board_id: int) -> bool: with session() as s: m = s.query(BoardModeratorOrmModel).filter_by( moderator_id=moderator.id, board_id=board_id).one_or_none() res = m is not None s.commit() return res
def find_by_type(page_type: str) -> 'List[PageModel]': _check_page_type(page_type) lc = local_cache.get(cache_key('type', page_type)) if lc: return list(map(lambda i: i.copy(), lc)) pages_by_type_cached = cache.get(cache_key('pages_by_type', page_type)) if pages_by_type_cached is not None: res = list(map(lambda i: PageModel.from_cache(i), pages_by_type_cached)) else: with session() as s: q = s.query(PageOrmModel).filter_by(type=page_type) q = q.order_by(asc(PageOrmModel.order)) res = list(map(lambda i: PageModel.from_orm_model(i), q.all())) cache.set(cache_key('pages_by_type', page_type), list(map(lambda i: i.to_cache(), res))) s.commit() local_cache.set(cache_key('type', page_type), res) return res
def create(board: BoardModel) -> BoardModel: if not validation.check_board_name_validity(board.name): raise ArgumentError(MESSAGE_INVALID_NAME) with session() as s: existing = s.query(BoardOrmModel).filter_by(name=board.name).one_or_none() if existing: raise ArgumentError(MESSAGE_DUPLICATE_BOARD_NAME) orm_board = board.to_orm_model() board_config = BoardConfigModel.from_defaults() board_config_orm = board_config.to_orm_model() s.add(board_config_orm) s.flush() orm_board.config_id = board_config_orm.id s.add(orm_board) s.commit() board = board.from_orm_model(orm_board) cache.set(cache_key('board_and_config', board.name), board.to_cache()) _set_all_board_names_cache(s) s.commit() return board
def board_remove_moderator(board: BoardModel, moderator: ModeratorModel): with session() as s: bm = s.query(BoardModeratorOrmModel).filter_by(board_id=board.id, moderator_id=moderator.id).one_or_none() if not bm: raise ArgumentError(MESSAGE_BOARD_NOT_ADDED) s.delete(bm) s.commit()
def board_remove_moderator(board: BoardModel, moderator: ModeratorModel): with session() as s: bm = s.query(BoardModeratorOrmModel).filter_by( board_id=board.id, moderator_id=moderator.id).one_or_none() if not bm: raise ArgumentError(MESSAGE_BOARD_NOT_ADDED) s.delete(bm) s.commit()
def get_all_board_moderators_by_board( board: BoardModel) -> 'List[BoardModeratorModel]': with session() as s: bms = s.query(BoardModeratorOrmModel).filter_by( board_id=board.id).all() res = list(map(lambda i: BoardModeratorModel.from_orm_model(i), bms)) s.commit() return res
def has_role(moderator: ModeratorModel, role: str) -> bool: _check_roles([role]) with session() as s: moderator_orm_model = s.query(ModeratorOrmModel).filter_by(id=moderator.id).one() res = role in moderator_orm_model.roles s.commit() return res
def create_ban(ban: BanModel) -> BanModel: with session() as s: eq = s.query(BanOrmModel) m = ban.to_orm_model() s.add(m) s.commit() return BanModel.from_orm_model(m)
def get_catalog(board: BoardModel) -> CatalogModel: catalog_cache = cache.get(cache_key('board', board.name)) if not catalog_cache: with session() as s: catalog, board_pages = _invalidate_board_pages_catalog_cache(s, board) return catalog return CatalogModel.from_cache(catalog_cache)
def find_by_id(ban_id: int) -> Optional[BanModel]: with session() as s: m = s.query(BanOrmModel).filter_by(id=ban_id).one_or_none() res = None if m: res = BanModel.from_orm_model(m) s.commit() return res
def get_board_page(board: BoardModel, page: int) -> BoardPageModel: board_page_cache = cache.get(cache_key('board', board.name, page)) if not board_page_cache: with session() as s: catalog, board_pages = _invalidate_board_pages_catalog_cache(s, board) return board_pages[page] return BoardPageModel.from_cache(board_page_cache)
def create(report: ReportModel): with session() as s: m = report.to_orm_model() s.add(m) s.commit() r = ReportModel.from_orm_model(m) s.commit() return r
def find_by_id(moderator_id: int) -> ModeratorModel: with session() as s: m = s.query(ModeratorOrmModel).filter_by(id=moderator_id).one_or_none() res = None if m: res = ModeratorModel.from_orm_model(m) s.commit() return res
def find_by_id(report_id: int) -> Optional[ReportModel]: with session() as s: m = s.query(ReportOrmModel).filter_by(id=report_id).one() res = None if m: res = ReportModel.from_orm_model(m) s.commit() return res
def get_board_moderator(board: BoardModel, moderator: ModeratorModel) -> BoardModeratorModel: with session() as s: m = s.query(BoardModeratorOrmModel).filter_by(moderator_id=moderator.id, board_id=board.id).one_or_none() res = None if m: res = BoardModeratorModel.from_orm_model(m) s.commit() return res
def has_any_of_board_roles(moderator: ModeratorModel, board: BoardModel, role_list: 'List[str]') -> bool: _check_board_roles(role_list) with session() as s: board_moderator = s.query(BoardModeratorOrmModel).filter_by(moderator_id=moderator.id, board_id=board.id).one() res = any(role in board_moderator.roles for role in role_list) s.commit() return res
def find_by_post(post: PostModel) -> Optional[ReportModel]: with session() as s: m = s.query(ReportOrmModel).filter_by(post_id=post.id).one_or_none() res = None if m: res = ReportModel.from_orm_model(m) s.commit() return res
def get_catalog(board: BoardModel) -> CatalogModel: catalog_cache = cache.get(cache_key('board', board.name)) if not catalog_cache: with session() as s: catalog, board_pages = _invalidate_board_pages_catalog_cache( s, board) return catalog return CatalogModel.from_cache(catalog_cache)
def get_all(include_boards=False) -> 'List[ModeratorModel]': with session() as s: q = s.query(ModeratorOrmModel) # TODO: fix this optimisation, it now requires way too many queries # if include_boards: # q = q.options(joinedload(ModeratorOrmModel.boards.of_type(BoardOrmModel.moderators))) all_moderators = list(map(lambda m: ModeratorModel.from_orm_model(m, include_boards), q.all())) s.commit() return all_moderators
def add_role(moderator: ModeratorModel, role: str): _check_roles([role]) with session() as s: moderator_orm_model = s.query(ModeratorOrmModel).filter_by(id=moderator.id).one() if role in moderator_orm_model.roles: raise ArgumentError('Role already added') moderator_orm_model.roles.append(role) s.commit()
def get_board_page(board: BoardModel, page: int) -> BoardPageModel: board_page_cache = cache.get(cache_key('board', board.name, page)) if not board_page_cache: with session() as s: catalog, board_pages = _invalidate_board_pages_catalog_cache( s, board) return board_pages[page] return BoardPageModel.from_cache(board_page_cache)
def remove_role(moderator: ModeratorModel, role: str): _check_roles([role]) with session() as s: moderator_orm_model = s.query(ModeratorOrmModel).filter_by(id=moderator.id).one() if role not in moderator_orm_model.roles: raise ArgumentError('Role not added') moderator_orm_model.roles.remove(role) s.commit()
def has_role(moderator: ModeratorModel, role: str) -> bool: _check_roles([role]) with session() as s: moderator_orm_model = s.query(ModeratorOrmModel).filter_by( id=moderator.id).one() res = role in moderator_orm_model.roles s.commit() return res
def add_board_role(moderator: ModeratorModel, board: BoardModel, role: str): _check_board_roles([role]) with session() as s: board_moderator = s.query(BoardModeratorOrmModel).filter_by(moderator_id=moderator.id, board_id=board.id).one() if role in board_moderator.roles: raise ArgumentError('Role already added') board_moderator.roles.append(role) s.commit()
def board_add_moderator(board: BoardModel, moderator: ModeratorModel): with session() as s: bm = s.query(BoardModeratorOrmModel).filter_by(moderator_id=moderator.id, board_id=board.id).one_or_none() if bm: raise ArgumentError(MESSAGE_BOARD_ALREADY_ADDED) m = s.query(ModeratorOrmModel).filter_by(id=moderator.id).one() b = s.query(BoardOrmModel).filter_by(id=board.id).one() b.moderators.append(m) s.commit()
def create_post(board: BoardModel, thread: ThreadModel, post: PostModel, sage: bool) \ -> Tuple[PostResultModel, int, int]: start_time = now() with session() as s: post_orm_model = post.to_orm_model() s.add(post_orm_model) to_thread_orm_model = s.query(ThreadOrmModel).filter_by( id=thread.id).one() post_orm_model.thread = to_thread_orm_model post_orm_model.refno = 0 # Atomically update the refno counter to_thread_orm_model.refno_counter = ThreadOrmModel.refno_counter + 1 s.commit() # Set it to the post after the commit to make sure there aren't any duplicates post_refno = post_orm_model.refno = to_thread_orm_model.refno_counter post_id = post_orm_model.id # Attach file to the post id if post.file: file_orm_model = post.file.to_orm_model() file_orm_model.post_id = post_id s.add(file_orm_model) if post.moderator: post_orm_model.moderator_id = post.moderator.id modify_date = now() # Use the refno to avoid a count(*) if not sage and post_refno <= board.config.bump_limit: to_thread_orm_model.last_modified = modify_date s.commit() insert_time = now() - start_time start_time = now() _invalidate_thread_cache(s, thread, board) _invalidate_board_pages_catalog_cache(s, board) purge_thread_future = document_cache.purge_thread(board, thread) # Wait for the thread to be purged, otherwise the chance exists that the client reloads a cached version. # This only holds up the posting client, others have the updated memcache available. purge_thread_future.result() # Don't wait for this document_cache.purge_board(board) cache_time = now() - start_time res = PostResultModel.from_board_name_thread_refno_post_refno( board.name, thread.refno, post_refno) return res, insert_time, cache_time
def create_thread(board: BoardModel, post: PostModel) \ -> Tuple[PostResultModel, int, int]: start_time = now() with session() as s: board_orm_model = s.query(BoardOrmModel).filter_by(id=board.id).one() thread_orm_model = ThreadOrmModel() thread_orm_model.last_modified = now() thread_orm_model.refno = 0 thread_orm_model.board_id = board.id post_orm_model = post.to_orm_model() post_orm_model.thread = thread_orm_model post_orm_model.refno = 1 s.add(thread_orm_model) # Atomically update the refno counter board_orm_model.refno_counter = BoardOrmModel.refno_counter + 1 s.commit() # Set it to the board after the commit to make sure there aren't any duplicates thread_refno = thread_orm_model.refno = board_orm_model.refno_counter # Attach file to the post id if post.file: file_orm_model = post.file.to_orm_model() file_orm_model.post_id = post_orm_model.id s.add(file_orm_model) if post.moderator: post_orm_model.moderator_id = post.moderator.id # Purge overflowed threads threads_refnos_to_purge = _purge_threads(s, board, board.config.pages, board.config.per_page) s.commit() insert_time = now() - start_time start_time = now() for purging_refno in threads_refnos_to_purge: cache.delete(cache_key('thread', board.name, purging_refno)) cache.delete(cache_key('thread_stub', board.name, purging_refno)) thread = ThreadModel.from_orm_model(thread_orm_model) _invalidate_thread_cache(s, thread, board) _invalidate_board_pages_catalog_cache(s, board) document_cache.purge_board(board) cache_time = now() - start_time res = PostResultModel.from_board_name_thread_refno_post_refno( board.name, thread_refno, 1) return res, insert_time, cache_time
def remove_role(moderator: ModeratorModel, role: str): _check_roles([role]) with session() as s: moderator_orm_model = s.query(ModeratorOrmModel).filter_by( id=moderator.id).one() if role not in moderator_orm_model.roles: raise ArgumentError('Role not added') moderator_orm_model.roles.remove(role) s.commit()
def add_role(moderator: ModeratorModel, role: str): _check_roles([role]) with session() as s: moderator_orm_model = s.query(ModeratorOrmModel).filter_by( id=moderator.id).one() if role in moderator_orm_model.roles: raise ArgumentError('Role already added') moderator_orm_model.roles.append(role) s.commit()
def remove_board_role(moderator: ModeratorModel, board: BoardModel, role: str): _check_board_roles([role]) with session() as s: board_moderator = s.query(BoardModeratorOrmModel).filter_by( moderator_id=moderator.id, board_id=board.id).one() if role not in board_moderator.roles: raise ArgumentError('Role not added') board_moderator.roles.remove(role) s.commit()
def delete(page: PageModel): with session() as s: m = s.query(PageOrmModel).filter_by(id=page.id).one() s.delete(m) s.flush() cache.delete(cache_key('page_by_link_name', page.link_name)) _cache_pages_by_type(s, page.type) s.commit()
def get_board_moderator(board: BoardModel, moderator: ModeratorModel) -> BoardModeratorModel: with session() as s: m = s.query(BoardModeratorOrmModel).filter_by( moderator_id=moderator.id, board_id=board.id).one_or_none() res = None if m: res = BoardModeratorModel.from_orm_model(m) s.commit() return res
def board_add_moderator(board: BoardModel, moderator: ModeratorModel): with session() as s: bm = s.query(BoardModeratorOrmModel).filter_by( moderator_id=moderator.id, board_id=board.id).one_or_none() if bm: raise ArgumentError(MESSAGE_BOARD_ALREADY_ADDED) m = s.query(ModeratorOrmModel).filter_by(id=moderator.id).one() b = s.query(BoardOrmModel).filter_by(id=board.id).one() b.moderators.append(m) s.commit()
def has_any_of_board_roles(moderator: ModeratorModel, board: BoardModel, role_list: 'List[str]') -> bool: _check_board_roles(role_list) with session() as s: board_moderator = s.query(BoardModeratorOrmModel).filter_by( moderator_id=moderator.id, board_id=board.id).one() res = any(role in board_moderator.roles for role in role_list) s.commit() return res
def get_all(include_boards=False) -> 'List[ModeratorModel]': with session() as s: q = s.query(ModeratorOrmModel) # TODO: fix this optimisation, it now requires way too many queries # if include_boards: # q = q.options(joinedload(ModeratorOrmModel.boards.of_type(BoardOrmModel.moderators))) all_moderators = list( map(lambda m: ModeratorModel.from_orm_model(m, include_boards), q.all())) s.commit() return all_moderators
def update_thread_locked(thread: ThreadModel, locked: bool): with session() as s: existing = s.query(ThreadOrmModel).filter_by(id=thread.id).one() existing.locked = locked s.commit() _invalidate_thread_cache(s, thread, thread.board) _invalidate_board_pages_catalog_cache(s, thread.board) document_cache.purge_thread(thread.board, thread) document_cache.purge_board(thread.board)
def create_post(board: BoardModel, thread: ThreadModel, post: PostModel, sage: bool) \ -> Tuple[PostResultModel, int, int]: start_time = now() with session() as s: post_orm_model = post.to_orm_model() s.add(post_orm_model) to_thread_orm_model = s.query(ThreadOrmModel).filter_by(id=thread.id).one() post_orm_model.thread = to_thread_orm_model post_orm_model.refno = 0 # Atomically update the refno counter to_thread_orm_model.refno_counter = ThreadOrmModel.refno_counter + 1 s.commit() # Set it to the post after the commit to make sure there aren't any duplicates post_refno = post_orm_model.refno = to_thread_orm_model.refno_counter post_id = post_orm_model.id # Attach file to the post id if post.file: file_orm_model = post.file.to_orm_model() file_orm_model.post_id = post_id s.add(file_orm_model) if post.moderator: post_orm_model.moderator_id = post.moderator.id modify_date = now() # Use the refno to avoid a count(*) if not sage and post_refno <= board.config.bump_limit: to_thread_orm_model.last_modified = modify_date s.commit() insert_time = now() - start_time start_time = now() _invalidate_thread_cache(s, thread, board) _invalidate_board_pages_catalog_cache(s, board) purge_thread_future = document_cache.purge_thread(board, thread) # Wait for the thread to be purged, otherwise the chance exists that the client reloads a cached version. # This only holds up the posting client, others have the updated memcache available. purge_thread_future.result() # Don't wait for this document_cache.purge_board(board) cache_time = now() - start_time res = PostResultModel.from_board_name_thread_refno_post_refno(board.name, thread.refno, post_refno) return res, insert_time, cache_time
def delete_thread(thread: ThreadModel): with session() as s: thread_orm_model = s.query(ThreadOrmModel).filter_by(id=thread.id).one() s.delete(thread_orm_model) s.commit() _invalidate_thread_cache(s, thread, thread.board) _invalidate_board_pages_catalog_cache(s, thread.board) document_cache.purge_thread(thread.board, thread) document_cache.purge_board(thread.board)
def check_password_match(moderator: ModeratorModel, password: str): if not validation.check_password_validity(password): raise ArgumentError(MESSAGE_INVALID_PASSWORD) with session() as s: moderator_orm_model = s.query(ModeratorOrmModel).filter_by(id=moderator.id).one() moderator_hashed_password = moderator_orm_model.password s.commit() if not bcrypt.checkpw(password.encode(), moderator_hashed_password): raise ArgumentError(MESSAGE_PASSWORD_INCORRECT)
def create_thread(board: BoardModel, post: PostModel) \ -> Tuple[PostResultModel, int, int]: start_time = now() with session() as s: board_orm_model = s.query(BoardOrmModel).filter_by(id=board.id).one() thread_orm_model = ThreadOrmModel() thread_orm_model.last_modified = now() thread_orm_model.refno = 0 thread_orm_model.board_id = board.id post_orm_model = post.to_orm_model() post_orm_model.thread = thread_orm_model post_orm_model.refno = 1 s.add(thread_orm_model) # Atomically update the refno counter board_orm_model.refno_counter = BoardOrmModel.refno_counter + 1 s.commit() # Set it to the board after the commit to make sure there aren't any duplicates thread_refno = thread_orm_model.refno = board_orm_model.refno_counter # Attach file to the post id if post.file: file_orm_model = post.file.to_orm_model() file_orm_model.post_id = post_orm_model.id s.add(file_orm_model) if post.moderator: post_orm_model.moderator_id = post.moderator.id # Purge overflowed threads threads_refnos_to_purge = _purge_threads(s, board, board.config.pages, board.config.per_page) s.commit() insert_time = now() - start_time start_time = now() for purging_refno in threads_refnos_to_purge: cache.delete(cache_key('thread', board.name, purging_refno)) cache.delete(cache_key('thread_stub', board.name, purging_refno)) thread = ThreadModel.from_orm_model(thread_orm_model) _invalidate_thread_cache(s, thread, board) _invalidate_board_pages_catalog_cache(s, board) document_cache.purge_board(board) cache_time = now() - start_time res = PostResultModel.from_board_name_thread_refno_post_refno(board.name, thread_refno, 1) return res, insert_time, cache_time
def update(page: PageModel): _validate(page) with session() as s: existing = s.query(PageOrmModel).filter_by(id=page.id).one_or_none() if not existing: raise ArgumentError(MESSAGE_PAGE_NOT_FOUND) s.merge(page.to_orm_model()) _cache_page(s, page) s.commit()
def get_all_logs_by_board(board: BoardModel, offset: int, limit: int) -> 'List[ModeratorLogModel]': with session() as s: ls = s.query(ModeratorLogOrmModel) \ .filter_by(board_id=board.id) \ .order_by(desc(ModeratorLogOrmModel.date)) \ .options(joinedload('moderator')) \ .offset(offset).limit(limit) \ .all() res = list(map(lambda i: ModeratorLogModel.from_orm_model(i, with_moderator=True), ls)) s.commit() return res
def find_by_username_case_insensitive(username: str) -> ModeratorModel: if not validation.check_username_validity(username): raise ArgumentError(MESSAGE_INVALID_USERNAME) with session() as s: # Username chars are safe because it is checked above m = s.query(ModeratorOrmModel).filter(ModeratorOrmModel.username.ilike(username)).one_or_none() res = None if m: res = ModeratorModel.from_orm_model(m) s.commit() return res