from flask import Flask, render_template, request, jsonify, make_response from flask_assets import Bundle, Environment from arango import ArangoClient from minio import Minio import socket, os app = Flask(__name__) assets = Environment(app) css = Bundle("src/*.css", output="dist/main.css") # https://unpkg.com/htmx.org #js = Bundle("src/*.js", output="dist/main.js") assets.register("css", css) #assets.register("js", js) css.build() #js.build() docker_short_id = socket.gethostname() arango_client = ArangoClient(hosts='https://arango.guaranteedstruggle.host') db = arango_client.db('board1', username='root', password='123-very-unsafe-way-to-protect-yourself321') # предполагается что меняться список будет весьма редко, поэтому подхватываем при лишь при перезапуске # # boards0 = db.collection('boards') # # print(boards0) # # board_list = [ k['_key'] for k in boards0] @app.route("/get_my_ip") def get_my_ip(): return jsonify({'ip': request.remote_addr, 'real-ip': request.environ.get('HTTP_X_REAL_IP', request.remote_addr)}), 200 @app.route('/liveness') def healthx(): return "

Liveness check completed

" @app.route('/readiness') def healthz(): #### TODU return arango check return "

Readiness check completed

" @app.route("/") def homepage(): boards0 = db.collection('boards') board_list = [ k['_key'] for k in boards0] return render_template("main-page.html", host_id=docker_short_id, boards=board_list) #### TODO если борды нет, то возвращать 404 мемную #### TODO если треда нет, то возвращать 404 мемную #### DONE route :: /{board}/ @app.route('/') def board_posts(board=None): postos = db.collection('posts') postos = [ p for p in postos if p.get('root_post') == True and p.get('board') == board ] postos = sorted(postos, key=lambda posto: int(posto['_key']), reverse=False) boards0 = db.collection('boards') board_list = [ k['_key'] for k in boards0] return render_template("board-posts.html", host_id=docker_short_id, postos=postos, board=board, target_post_id=None, boards=board_list) #### TODO route :: /{board}/{thread}/ #### TODO убрать отсюда чужие рут-потсы @app.route("//") def thread_posts(board=None, target_post_id=None): ## взять рут-пост ## взять всех его детей # postos = db.collection('posts') # postos = [ p for p in postos] # postos = sorted(postos, key=lambda posto: int(posto['_key']), reverse=False) cursor = db.aql.execute( f"""FOR ppp IN posts FILTER ppp.root_post == True and ppp._key == "{target_post_id}" FOR v IN 1..9999 OUTBOUND ppp post_parents RETURN v""" ) postos = [ ] while not cursor.empty(): # Pop until nothing is left on the cursor. postos.append(cursor.pop()) cursor = db.aql.execute( f'RETURN DOCUMENT("posts/{target_post_id}")' ) while not cursor.empty(): # Pop until nothing is left on the cursor. postos.append(cursor.pop()) postos = [ p for p in postos ] postos = sorted(postos, key=lambda posto: int(posto['_key']), reverse=False) if not target_post_id: target_post_id = postos[0]['_key'] boards0 = db.collection('boards') board_list = [ k['_key'] for k in boards0] return render_template("interactive-posts.html", board=board, host_id=docker_short_id, postos=postos, target_post_id=target_post_id, boards=board_list) #### TODO route :: /{board}/create_thread/{target_post_id} @app.route("//create-thread/", methods=['POST']) def create_thread(board=None, target_post_id=None): postos = db.collection('posts') data = request.form['send_this_text'] metadata = postos.insert({ 'texto': data, "root_post": True, 'board': board, 'images': [], "children_num": 0, "answers_num": 0, "answers_list": [ [], [], [] ] }, overwrite_mode='update') response = make_response() response.headers["HX-Redirect"] = f"/{board}/{metadata['_key']}" response.status_code = 200 return response #### TODO route :: /{board}/{thread}/answer_post/ #### TODO route :: /{board}/{thread}/post_to_another_post/ # @app.route('/db_posts') # @app.route("/db_posts/") # def page_posts_from_db(target_post_id=None): # postos = db.collection('posts') # postos = [ p for p in postos] # postos = sorted(postos, key=lambda posto: int(posto['_key']), reverse=False) # for p in postos: # #if p['root_post']: # print(p) # if not target_post_id: # target_post_id = postos[0]['_key'] # return render_template("interactive-posts.html", host_id=docker_short_id, postos=postos, target_post_id=target_post_id) @app.route("//answer_post/", methods=['POST']) def answer_post(board, target_post_id): postos = db.collection('posts') postos = [ p for p in postos ] postos = sorted(postos, key=lambda posto: int(posto['_key']), reverse=False) ## ?? wut if not target_post_id: target_post_id = postos[0]['_key'] return render_template("answer-post.html", board=board, target_post_id=target_post_id) ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'mp4', 'webm', 'webp'} def allowed_file(filename): return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS @app.route('//post_to_another_post/', methods=['POST']) def post_to_post(board, post_key): #### TODO resize pics https://stackoverflow.com/questions/53337318/how-can-i-resize-image-with-pil-on-upload-and-serve-them-with-flask-cloudy #### TODO allow only imgs, videos, and gifs if 'file' in request.files: minioClient = Minio( "static.guaranteedstruggle.host" ) bucket_name = "thread-pics" files = request.files.getlist("file") for file in files: size = os.fstat(file.fileno()).st_size #### работает ли?? if allowed_file(file.filename): minioClient.put_object( bucket_name, file.filename, file, size ) else: print(f'somebody tried to put this inside minio: {file.filename} size: {size}') postos = db.collection('posts') data = request.form['send_this_text'] cursor = db.aql.execute( f'RETURN DOCUMENT("posts/{post_key}")' ) p = cursor.pop() if p.get('root_post') == True: thread = p.get('_key') elif not p.get('root_post'): thread = p.get('thread') else: thread = 'ERROR' print(p) metadata = postos.insert({ 'texto': data, 'parent_post': f'{post_key}', 'images': [], "thread": thread }, overwrite_mode='update') metadata = db.collection('post_parents').insert({ '_from': f'posts/{post_key}', '_to': f'posts/{metadata["_key"]}' }, overwrite_mode='update') #### TODO оптимайз для только тредовых штук cursor = db.aql.execute( """FOR ppp IN posts LET children = (FOR v IN 1..9999 OUTBOUND ppp post_parents RETURN v) UPDATE ppp WITH { children_num : COUNT_DISTINCT( children[*]._key ) } IN posts""" ) cursor = db.aql.execute( """FOR ppp IN posts LET children = (FOR v IN 1 OUTBOUND ppp post_parents RETURN v) UPDATE ppp WITH { answers_num : COUNT_DISTINCT( children[*]._key ) } IN posts""" ) cursor = db.aql.execute( """FOR ppp IN posts LET children0 = (FOR v IN 1 OUTBOUND ppp post_parents RETURN v) LET children = (FOR c IN children0 SORT c._key DESC RETURN c) UPDATE ppp WITH { answers_list : [ children[*]._key, children[*].answers_num, children[*].children_num ] } IN posts""" ) postos = db.collection('posts') postos = [ p for p in postos if (not p.get('root_post')) and p.get('thread') == thread ] cursor = db.aql.execute( f'RETURN DOCUMENT("posts/{thread}")' ) postos.append(cursor.pop()) postos = sorted(postos, key=lambda posto: int(posto['_key']), reverse=False) return render_template("i-posts.html", board=board, host_id=docker_short_id, postos=postos) #### TODO websockets #### TODO sse #### TODO kafka #### TODO shards #### TODO grpc if __name__ == "__main__": app.run(debug=True, host='0.0.0.0')