mirror of
https://codeberg.org/catask-org/catask.git
synced 2025-04-20 13:53:42 -05:00
add custom emojis, split admin panel, debug logging, etc (app.py)
This commit is contained in:
parent
1be2efe894
commit
ca7e584922
1 changed files with 248 additions and 14 deletions
262
app.py
262
app.py
|
@ -4,6 +4,11 @@ from dotenv import load_dotenv
|
||||||
from mysql.connector import errorcode
|
from mysql.connector import errorcode
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from werkzeug.utils import secure_filename
|
from werkzeug.utils import secure_filename
|
||||||
|
from pathlib import Path
|
||||||
|
import secrets
|
||||||
|
import shutil
|
||||||
|
import zipfile
|
||||||
|
import tarfile
|
||||||
import mysql.connector
|
import mysql.connector
|
||||||
import urllib
|
import urllib
|
||||||
import functions as func
|
import functions as func
|
||||||
|
@ -88,6 +93,7 @@ def loginRequired(f):
|
||||||
|
|
||||||
@app.before_request
|
@app.before_request
|
||||||
def before_request():
|
def before_request():
|
||||||
|
# app.logger.debug("[CatAsk] app.before_request triggered")
|
||||||
global logged_in
|
global logged_in
|
||||||
logged_in = session.get('logged_in')
|
logged_in = session.get('logged_in')
|
||||||
|
|
||||||
|
@ -95,6 +101,7 @@ def before_request():
|
||||||
|
|
||||||
@app.context_processor
|
@app.context_processor
|
||||||
def inject_stuff():
|
def inject_stuff():
|
||||||
|
app.logger.debug("[CatAsk] app.context_processor inject_stuff() triggered")
|
||||||
cfg = func.loadJSON(const.configFile)
|
cfg = func.loadJSON(const.configFile)
|
||||||
# for 1.6.0 or later
|
# for 1.6.0 or later
|
||||||
# questionCount = getQuestionCount()
|
# questionCount = getQuestionCount()
|
||||||
|
@ -103,6 +110,7 @@ def inject_stuff():
|
||||||
# -- template filters --
|
# -- template filters --
|
||||||
@app.template_filter('render_markdown')
|
@app.template_filter('render_markdown')
|
||||||
def render_markdown(text):
|
def render_markdown(text):
|
||||||
|
# app.logger.debug("[CatAsk] app.template_filter render_markdown(text) triggered")
|
||||||
return func.renderMarkdown(text)
|
return func.renderMarkdown(text)
|
||||||
|
|
||||||
# -- client (frontend) routes --
|
# -- client (frontend) routes --
|
||||||
|
@ -112,14 +120,20 @@ def index():
|
||||||
conn = func.connectToDb()
|
conn = func.connectToDb()
|
||||||
cursor = conn.cursor(dictionary=True)
|
cursor = conn.cursor(dictionary=True)
|
||||||
|
|
||||||
|
app.logger.debug("[CatAsk/Home] SELECT'ing pinned questions")
|
||||||
|
|
||||||
cursor.execute("SELECT * FROM questions WHERE answered=%s AND pinned=%s ORDER BY creation_date DESC", (True, True))
|
cursor.execute("SELECT * FROM questions WHERE answered=%s AND pinned=%s ORDER BY creation_date DESC", (True, True))
|
||||||
pinned_questions = cursor.fetchall()
|
pinned_questions = cursor.fetchall()
|
||||||
|
|
||||||
|
app.logger.debug("[CatAsk/Home] SELECT'ing non-pinned questions")
|
||||||
|
|
||||||
cursor.execute("SELECT * FROM questions WHERE answered=%s AND pinned=%s ORDER BY creation_date DESC", (True, False))
|
cursor.execute("SELECT * FROM questions WHERE answered=%s AND pinned=%s ORDER BY creation_date DESC", (True, False))
|
||||||
non_pinned_questions = cursor.fetchall()
|
non_pinned_questions = cursor.fetchall()
|
||||||
|
|
||||||
questions = pinned_questions + non_pinned_questions
|
questions = pinned_questions + non_pinned_questions
|
||||||
|
|
||||||
|
app.logger.debug("[CatAsk/Home] SELECT'ing answers")
|
||||||
|
|
||||||
cursor.execute("SELECT * FROM answers ORDER BY creation_date DESC")
|
cursor.execute("SELECT * FROM answers ORDER BY creation_date DESC")
|
||||||
answers = cursor.fetchall()
|
answers = cursor.fetchall()
|
||||||
|
|
||||||
|
@ -135,6 +149,7 @@ def index():
|
||||||
|
|
||||||
cursor.close()
|
cursor.close()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
return render_template('index.html', combined=combined, urllib=urllib, trimContent=func.trimContent, metadata=metadata, getRandomWord=func.getRandomWord, formatRelativeTime=func.formatRelativeTime)
|
return render_template('index.html', combined=combined, urllib=urllib, trimContent=func.trimContent, metadata=metadata, getRandomWord=func.getRandomWord, formatRelativeTime=func.formatRelativeTime)
|
||||||
|
|
||||||
@app.route('/inbox/', methods=['GET'])
|
@app.route('/inbox/', methods=['GET'])
|
||||||
|
@ -142,6 +157,9 @@ def index():
|
||||||
def inbox():
|
def inbox():
|
||||||
conn = func.connectToDb()
|
conn = func.connectToDb()
|
||||||
cursor = conn.cursor(dictionary=True)
|
cursor = conn.cursor(dictionary=True)
|
||||||
|
|
||||||
|
app.logger.debug("[CatAsk/Inbox] SELECT'ing unanswered questions")
|
||||||
|
|
||||||
cursor.execute("SELECT * FROM questions WHERE answered=%s ORDER BY creation_date DESC", (False,))
|
cursor.execute("SELECT * FROM questions WHERE answered=%s ORDER BY creation_date DESC", (False,))
|
||||||
questions = cursor.fetchall()
|
questions = cursor.fetchall()
|
||||||
|
|
||||||
|
@ -179,6 +197,7 @@ def login():
|
||||||
admin_password = request.form.get('admin_password')
|
admin_password = request.form.get('admin_password')
|
||||||
if admin_password == os.environ.get('ADMIN_PASSWORD'):
|
if admin_password == os.environ.get('ADMIN_PASSWORD'):
|
||||||
session['logged_in'] = True
|
session['logged_in'] = True
|
||||||
|
session.permanent = request.form.get('remember_me', False)
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('index'))
|
||||||
else:
|
else:
|
||||||
flash("Wrong password", 'danger')
|
flash("Wrong password", 'danger')
|
||||||
|
@ -189,7 +208,7 @@ def login():
|
||||||
else:
|
else:
|
||||||
return render_template('admin/login.html')
|
return render_template('admin/login.html')
|
||||||
|
|
||||||
@admin_bp.route('/logout/')
|
@admin_bp.route('/logout/', methods=['POST'])
|
||||||
@loginRequired
|
@loginRequired
|
||||||
def logout():
|
def logout():
|
||||||
session['logged_in'] = False
|
session['logged_in'] = False
|
||||||
|
@ -198,20 +217,56 @@ def logout():
|
||||||
@admin_bp.route('/', methods=['GET', 'POST'])
|
@admin_bp.route('/', methods=['GET', 'POST'])
|
||||||
@loginRequired
|
@loginRequired
|
||||||
def index():
|
def index():
|
||||||
|
return redirect(url_for('admin.information'))
|
||||||
|
|
||||||
|
@admin_bp.route('/information/', methods=['GET', 'POST'])
|
||||||
|
@loginRequired
|
||||||
|
def information():
|
||||||
|
return render_template('admin/categories/instance.html')
|
||||||
|
|
||||||
|
@admin_bp.route('/general/', methods=['GET', 'POST'])
|
||||||
|
@loginRequired
|
||||||
|
def general():
|
||||||
|
return render_template('admin/categories/general.html')
|
||||||
|
|
||||||
|
@admin_bp.route('/customize/', methods=['GET', 'POST'])
|
||||||
|
@loginRequired
|
||||||
|
def customize():
|
||||||
|
return render_template('admin/categories/customize.html')
|
||||||
|
|
||||||
|
@admin_bp.route('/emojis/', methods=['GET', 'POST'])
|
||||||
|
@loginRequired
|
||||||
|
def emojis():
|
||||||
|
packs = func.listEmojiPacks()
|
||||||
|
if packs:
|
||||||
|
for pack in packs:
|
||||||
|
# print(pack,'\n')
|
||||||
|
json_pack = bool(pack.get('website') and pack.get('exportedAt'))
|
||||||
|
print(json_pack)
|
||||||
|
if json_pack:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
json_pack = False
|
||||||
|
emojis = func.listEmojis()
|
||||||
|
|
||||||
|
return render_template('admin/categories/emojis.html', json_pack=json_pack, packs=packs, emojis=emojis, formatRelativeTime=func.formatRelativeTime2)
|
||||||
|
|
||||||
|
@admin_bp.route('/blacklist/', methods=['GET', 'POST'])
|
||||||
|
@loginRequired
|
||||||
|
def blacklist():
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
action = request.form.get('action')
|
action = request.form.get('action')
|
||||||
blacklist = request.form.get('blacklist')
|
blacklist = request.form.get('blacklist')
|
||||||
with open(const.blacklistFile, 'w') as file:
|
with open(const.blacklistFile, 'w') as file:
|
||||||
file.write(blacklist)
|
file.write(blacklist)
|
||||||
return {'message': 'Changes saved!'}, 200
|
return {'message': 'Blacklist updated!'}, 200
|
||||||
|
|
||||||
else:
|
else:
|
||||||
blacklist = func.readPlainFile(const.blacklistFile)
|
blacklist = func.readPlainFile(const.blacklistFile)
|
||||||
if blacklist == []:
|
if blacklist == []:
|
||||||
blacklist = ''
|
blacklist = ''
|
||||||
cfg_vars = func.loadJSON(const.configFile)
|
|
||||||
|
|
||||||
return render_template('admin/index.html', blacklist=blacklist, cfg=cfg_vars)
|
return render_template('admin/categories/blacklist.html', blacklist=blacklist)
|
||||||
|
|
||||||
# TODO: implement first-launch setup route
|
# TODO: implement first-launch setup route
|
||||||
"""
|
"""
|
||||||
|
@ -238,24 +293,27 @@ def badRequest(e):
|
||||||
def internalServerError(e):
|
def internalServerError(e):
|
||||||
return jsonify({'error': str(e)}), 500
|
return jsonify({'error': str(e)}), 500
|
||||||
|
|
||||||
|
# -- question routes --
|
||||||
|
|
||||||
@api_bp.route('/add_question/', methods=['POST'])
|
@api_bp.route('/add_question/', methods=['POST'])
|
||||||
def addQuestion():
|
def addQuestion():
|
||||||
from_who = request.form.get('from_who', cfg['anonName'])
|
from_who = request.form.get('from_who', cfg['anonName'])
|
||||||
question = request.form.get('question', '')
|
question = request.form.get('question', '')
|
||||||
antispam = request.form.get('antispam', '')
|
antispam = request.form.get('antispam', '')
|
||||||
# reserved for version 1.5.0 or later
|
cw = request.form.get('cw', '')
|
||||||
# private = request.form.get('private')
|
|
||||||
|
|
||||||
if not question:
|
if not question:
|
||||||
abort(400, "Question field must not be empty")
|
abort(400, "Question field must not be empty")
|
||||||
if not antispam:
|
|
||||||
abort(400, "Anti-spam word must not be empty")
|
|
||||||
if len(question) > int(cfg['charLimit']) or len(from_who) > int(cfg['charLimit']):
|
if len(question) > int(cfg['charLimit']) or len(from_who) > int(cfg['charLimit']):
|
||||||
abort(400, "Question exceeds the character limit")
|
abort(400, "Question exceeds the character limit")
|
||||||
|
|
||||||
|
if not antispam:
|
||||||
|
abort(400, "Anti-spam word must not be empty")
|
||||||
|
|
||||||
antispam_wordlist = func.readPlainFile(const.antiSpamFile, split=True)
|
antispam_wordlist = func.readPlainFile(const.antiSpamFile, split=True)
|
||||||
antispam_valid = antispam in antispam_wordlist
|
antispam_valid = antispam in antispam_wordlist
|
||||||
if not antispam_valid:
|
if not antispam_valid:
|
||||||
|
# return a generic error message so bad actors wouldn't figure out the antispam list
|
||||||
return {'error': 'An error has occurred'}, 500
|
return {'error': 'An error has occurred'}, 500
|
||||||
|
|
||||||
blacklist = func.readPlainFile(const.blacklistFile, split=True)
|
blacklist = func.readPlainFile(const.blacklistFile, split=True)
|
||||||
|
@ -267,7 +325,10 @@ def addQuestion():
|
||||||
|
|
||||||
conn = func.connectToDb()
|
conn = func.connectToDb()
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute("INSERT INTO questions (from_who, content, answered) VALUES (%s, %s, %s)", (from_who, question, False,))
|
|
||||||
|
app.logger.debug("[CatAsk/API/add_question] INSERT'ing new question into database")
|
||||||
|
|
||||||
|
cursor.execute("INSERT INTO questions (from_who, content, answered, cw) VALUES (%s, %s, %s, %s)", (from_who, question, False, cw))
|
||||||
cursor.close()
|
cursor.close()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
|
@ -282,6 +343,9 @@ def deleteQuestion():
|
||||||
|
|
||||||
conn = func.connectToDb()
|
conn = func.connectToDb()
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
app.logger.debug("[CatAsk/API/delete_question] DELETE'ing a question from database")
|
||||||
|
|
||||||
cursor.execute("DELETE FROM questions WHERE id=%s", (question_id,))
|
cursor.execute("DELETE FROM questions WHERE id=%s", (question_id,))
|
||||||
cursor.close()
|
cursor.close()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
@ -297,17 +361,26 @@ def returnToInbox():
|
||||||
|
|
||||||
conn = func.connectToDb()
|
conn = func.connectToDb()
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute("SELECT from_who, content, creation_date FROM questions WHERE id=%s", (question_id,))
|
|
||||||
|
app.logger.debug("[CatAsk/API/return_to_inbox] SELECT'ing a question from database")
|
||||||
|
|
||||||
|
cursor.execute("SELECT from_who, content, creation_date, cw FROM questions WHERE id=%s", (question_id,))
|
||||||
row = cursor.fetchone()
|
row = cursor.fetchone()
|
||||||
|
|
||||||
question = {
|
question = {
|
||||||
'from_who': row[0],
|
'from_who': row[0],
|
||||||
'content': row[1],
|
'content': row[1],
|
||||||
'creation_date': row[2]
|
'creation_date': row[2],
|
||||||
|
'cw': row[3]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
app.logger.debug("[CatAsk/API/return_to_inbox] DELETE'ing a question from database")
|
||||||
|
|
||||||
cursor.execute("DELETE FROM questions WHERE id=%s", (question_id,))
|
cursor.execute("DELETE FROM questions WHERE id=%s", (question_id,))
|
||||||
cursor.execute("INSERT INTO questions (from_who, content, creation_date, answered) VALUES (%s, %s, %s, %s)", (question["from_who"], question["content"], question["creation_date"], False,))
|
|
||||||
|
app.logger.debug("[CatAsk/API/return_to_inbox] INSERT'ing a question into database")
|
||||||
|
|
||||||
|
cursor.execute("INSERT INTO questions (from_who, content, creation_date, answered, cw) VALUES (%s, %s, %s, %s, %s)", (question["from_who"], question["content"], question["creation_date"], False, question['cw']))
|
||||||
cursor.close()
|
cursor.close()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
|
@ -322,6 +395,9 @@ def pinQuestion():
|
||||||
|
|
||||||
conn = func.connectToDb()
|
conn = func.connectToDb()
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
app.logger.debug("[CatAsk/API/pin_question] UPDATE'ing a question to pin it")
|
||||||
|
|
||||||
cursor.execute("UPDATE questions SET pinned=%s WHERE id=%s", (True, question_id))
|
cursor.execute("UPDATE questions SET pinned=%s WHERE id=%s", (True, question_id))
|
||||||
cursor.close()
|
cursor.close()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
@ -337,6 +413,9 @@ def unpinQuestion():
|
||||||
|
|
||||||
conn = func.connectToDb()
|
conn = func.connectToDb()
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
app.logger.debug("[CatAsk/API/unpin_question] UPDATE'ing a question to unpin it")
|
||||||
|
|
||||||
cursor.execute("UPDATE questions SET pinned=%s WHERE id=%s", (False, question_id))
|
cursor.execute("UPDATE questions SET pinned=%s WHERE id=%s", (False, question_id))
|
||||||
cursor.close()
|
cursor.close()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
@ -348,6 +427,7 @@ def unpinQuestion():
|
||||||
def addAnswer():
|
def addAnswer():
|
||||||
question_id = request.args.get('question_id', '')
|
question_id = request.args.get('question_id', '')
|
||||||
answer = request.form.get('answer')
|
answer = request.form.get('answer')
|
||||||
|
cw = request.form.get('cw', '')
|
||||||
|
|
||||||
if not question_id:
|
if not question_id:
|
||||||
abort(400, "Missing 'question_id' attribute or 'question_id' is empty")
|
abort(400, "Missing 'question_id' attribute or 'question_id' is empty")
|
||||||
|
@ -357,8 +437,14 @@ def addAnswer():
|
||||||
conn = func.connectToDb()
|
conn = func.connectToDb()
|
||||||
try:
|
try:
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute("INSERT INTO answers (question_id, content) VALUES (%s, %s)", (question_id, answer))
|
|
||||||
|
app.logger.debug("[CatAsk/API/add_answer] INSERT'ing an answer into database")
|
||||||
|
|
||||||
|
cursor.execute("INSERT INTO answers (question_id, content, cw) VALUES (%s, %s, %s)", (question_id, answer, cw))
|
||||||
answer_id = cursor.lastrowid
|
answer_id = cursor.lastrowid
|
||||||
|
|
||||||
|
app.logger.debug("[CatAsk/API/add_answer] UPDATE'ing question to set answered and answer_id")
|
||||||
|
|
||||||
cursor.execute("UPDATE questions SET answered=%s, answer_id=%s WHERE id=%s", (True, answer_id, question_id))
|
cursor.execute("UPDATE questions SET answered=%s, answer_id=%s WHERE id=%s", (True, answer_id, question_id))
|
||||||
conn.commit()
|
conn.commit()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -370,6 +456,8 @@ def addAnswer():
|
||||||
|
|
||||||
return jsonify({'message': 'Answer added successfully!'}), 201
|
return jsonify({'message': 'Answer added successfully!'}), 201
|
||||||
|
|
||||||
|
# -- uploaders --
|
||||||
|
|
||||||
@api_bp.route('/upload_favicon/', methods=['POST'])
|
@api_bp.route('/upload_favicon/', methods=['POST'])
|
||||||
@loginRequired
|
@loginRequired
|
||||||
def uploadFavicon():
|
def uploadFavicon():
|
||||||
|
@ -388,10 +476,157 @@ def uploadFavicon():
|
||||||
elif not favicon:
|
elif not favicon:
|
||||||
return {'error': "favicon is not specified"}, 400
|
return {'error': "favicon is not specified"}, 400
|
||||||
|
|
||||||
|
@api_bp.route('/upload_emoji/', methods=['POST'])
|
||||||
|
@loginRequired
|
||||||
|
def uploadEmoji():
|
||||||
|
if 'emoji' not in request.files:
|
||||||
|
return jsonify({'error': 'No file part in the request'}), 400
|
||||||
|
|
||||||
|
emoji = request.files['emoji']
|
||||||
|
if emoji.filename == '':
|
||||||
|
return jsonify({'error': 'No file selected for uploading'}), 400
|
||||||
|
|
||||||
|
if not func.allowedFile(emoji.filename):
|
||||||
|
return jsonify({'error': 'Invalid file type. Only png, jpg, jpeg, webp, bmp, jxl supported'}), 400
|
||||||
|
|
||||||
|
# Secure the filename and determine the archive name
|
||||||
|
filename = secure_filename(emoji.filename)
|
||||||
|
|
||||||
|
emoji_path = const.emojiPath / filename
|
||||||
|
|
||||||
|
emoji.save(emoji_path)
|
||||||
|
|
||||||
|
return jsonify({'message': f'Emoji {filename} successfully uploaded'}), 201
|
||||||
|
|
||||||
|
@api_bp.route('/upload_emoji_pack/', methods=['POST'])
|
||||||
|
@loginRequired
|
||||||
|
def uploadEmojiPack():
|
||||||
|
if 'emoji_archive' not in request.files:
|
||||||
|
return jsonify({'error': 'No file part in the request'}), 400
|
||||||
|
|
||||||
|
emoji_archive = request.files['emoji_archive']
|
||||||
|
if emoji_archive.filename == '':
|
||||||
|
return jsonify({'error': 'No file selected for uploading'}), 400
|
||||||
|
|
||||||
|
if not func.allowedArchive(emoji_archive.filename):
|
||||||
|
return jsonify({'error': 'Invalid file type. Only .zip, .tar, .tar.gz, .tar.bz2 allowed'}), 400
|
||||||
|
|
||||||
|
filename = secure_filename(emoji_archive.filename)
|
||||||
|
archive_name = func.stripArchExtension(filename)
|
||||||
|
|
||||||
|
extract_path = const.emojiPath / archive_name
|
||||||
|
extract_path.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
archive_path = extract_path / filename
|
||||||
|
emoji_archive.save(archive_path)
|
||||||
|
|
||||||
|
try:
|
||||||
|
if zipfile.is_zipfile(archive_path):
|
||||||
|
with zipfile.ZipFile(archive_path, 'r') as zip_ref:
|
||||||
|
zip_ref.extractall(extract_path)
|
||||||
|
elif tarfile.is_tarfile(archive_path):
|
||||||
|
with tarfile.open(archive_path, 'r:*') as tar_ref:
|
||||||
|
tar_ref.extractall(extract_path)
|
||||||
|
else:
|
||||||
|
return jsonify({'error': 'Unsupported archive format'}), 400
|
||||||
|
|
||||||
|
# parse meta.json if it exists
|
||||||
|
meta_json_path = extract_path / 'meta.json'
|
||||||
|
if meta_json_path.exists():
|
||||||
|
processed_emojis = func.processEmojis(meta_json_path)
|
||||||
|
# emoji_metadata = func.loadJSON(meta_json_path)
|
||||||
|
# emojis = emoji_metadata.get('emojis', [])
|
||||||
|
|
||||||
|
# processed_emojis = []
|
||||||
|
# for emoji in emojis:
|
||||||
|
# emoji_info = {
|
||||||
|
# 'name': emoji['emoji']['name'],
|
||||||
|
# 'file_name': emoji['fileName']
|
||||||
|
# }
|
||||||
|
# processed_emojis.append(emoji_info)
|
||||||
|
# app.logger.debug(f"[CatAsk/API/upload_emoji_pack] Processed emoji: {emoji_info['name']} (File: {emoji_info['file_name']})")
|
||||||
|
|
||||||
|
# pack_json = {
|
||||||
|
# 'name': emoji_metadata['emojis'][0]['emoji']['category'].capitalize(),
|
||||||
|
# 'exportedAt': emoji_metadata["exportedAt"],
|
||||||
|
# 'emojis': processed_emojis
|
||||||
|
# }
|
||||||
|
# pack_json_name = const.emojiPath / (emoji_metadata['emojis'][0]['emoji']['category'] + '.json')
|
||||||
|
# func.saveJSON(pack_json, pack_json_name)
|
||||||
|
|
||||||
|
return jsonify({'message': f'Successfully uploaded and processed {len(processed_emojis)} emojis from archive "{filename}".'}), 201
|
||||||
|
|
||||||
|
else:
|
||||||
|
return jsonify({'message': f'Archive {filename} successfully uploaded and extracted.'}), 201
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({'error': str(e)}), 500
|
||||||
|
finally:
|
||||||
|
archive_path.unlink()
|
||||||
|
|
||||||
|
# -- getters --
|
||||||
|
|
||||||
|
# this isn't used anywhere yet, but maybe it will in the future
|
||||||
|
|
||||||
|
@api_bp.route('/get_emojis/', methods=['GET'])
|
||||||
|
@loginRequired
|
||||||
|
def getEmojis():
|
||||||
|
return func.listEmojis()
|
||||||
|
|
||||||
|
@api_bp.route('/get_emoji_packs/', methods=['GET'])
|
||||||
|
@loginRequired
|
||||||
|
def getEmojiPacks():
|
||||||
|
return func.listEmojiPacks()
|
||||||
|
|
||||||
|
# -- delete routes --
|
||||||
|
|
||||||
|
@api_bp.route('/delete_emoji/', methods=['DELETE'])
|
||||||
|
@loginRequired
|
||||||
|
def deleteEmoji():
|
||||||
|
emoji_name = request.args.get('emoji_name')
|
||||||
|
emoji_base_path = const.emojiPath
|
||||||
|
emoji_file_path = emoji_base_path / emoji_name
|
||||||
|
print(emoji_file_path)
|
||||||
|
emoji_ext = os.path.splitext(f"{emoji_base_path}/{emoji_file_path}")
|
||||||
|
print("emoji ext:", emoji_ext)
|
||||||
|
|
||||||
|
if not emoji_file_path.exists() or not emoji_file_path.is_file():
|
||||||
|
return jsonify({'error': 'Emoji not found'}), 404
|
||||||
|
|
||||||
|
try:
|
||||||
|
emoji_file_path.unlink()
|
||||||
|
return jsonify({'message': f'Emoji "{emoji_name}" deleted successfully'}), 200
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({'error': str(e)}), 500
|
||||||
|
|
||||||
|
@api_bp.route('/delete_emoji_pack/', methods=['DELETE'])
|
||||||
|
@loginRequired
|
||||||
|
def deleteEmojiPack():
|
||||||
|
pack_name = request.args.get('pack_name')
|
||||||
|
emojis_path = const.emojiPath
|
||||||
|
emoji_base_path = Path.cwd() / 'static' / 'emojis' / pack_name.lower()
|
||||||
|
|
||||||
|
if not emoji_base_path.exists() or not emoji_base_path.is_dir():
|
||||||
|
return jsonify({'error': f'Emoji pack "{pack_name.capitalize()}" not found'}), 404
|
||||||
|
|
||||||
|
try:
|
||||||
|
for json_file in emojis_path.glob(f'{pack_name.lower()}.json'):
|
||||||
|
json_file.unlink()
|
||||||
|
|
||||||
|
shutil.rmtree(emoji_base_path)
|
||||||
|
return jsonify({'message': f'Emoji pack "{pack_name.capitalize()}" deleted successfully.'}), 200
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({'error': str(e)}), 500
|
||||||
|
|
||||||
|
# -- misc --
|
||||||
|
|
||||||
@api_bp.route('/get_question_count/', methods=['GET'])
|
@api_bp.route('/get_question_count/', methods=['GET'])
|
||||||
def getQuestionCount():
|
def getQuestionCount():
|
||||||
conn = func.connectToDb()
|
conn = func.connectToDb()
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
app.logger.debug("[CatAsk/API/get_question_count] SELECT'ing question count from database")
|
||||||
|
|
||||||
cursor.execute("SELECT COUNT(id) FROM questions WHERE answered=%s", (False,))
|
cursor.execute("SELECT COUNT(id) FROM questions WHERE answered=%s", (False,))
|
||||||
question_count = cursor.fetchone()
|
question_count = cursor.fetchone()
|
||||||
|
|
||||||
|
@ -458,7 +693,6 @@ def updateConfig():
|
||||||
app.config.update(cfg)
|
app.config.update(cfg)
|
||||||
return {'message': 'Changes saved!'}
|
return {'message': 'Changes saved!'}
|
||||||
|
|
||||||
|
|
||||||
app.register_blueprint(api_bp, url_prefix='/api/v1')
|
app.register_blueprint(api_bp, url_prefix='/api/v1')
|
||||||
app.register_blueprint(admin_bp, url_prefix='/admin')
|
app.register_blueprint(admin_bp, url_prefix='/admin')
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue