flask-htmx-board1/app.py
hogweed1 b923665c07
All checks were successful
continuous-integration/drone/push Build is passing
Update app.py
2024-08-04 12:53:06 +10:00

281 lines
8.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 "<h1><center>Liveness check completed</center><h1>"
@app.route('/readiness')
def healthz():
#### TODU return arango check
return "<h1><center>Readiness check completed</center><h1>"
@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('/<board>')
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("/<board>/<int:target_post_id>")
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("/<board>/create-thread/<target_post_id>", 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/<target_post_id>")
# 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("/<board>/answer_post/<target_post_id>", 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('/<board>/post_to_another_post/<post_key>', 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')