mirror of
https://codeberg.org/catask-org/catask.git
synced 2025-04-19 13:23:41 -05:00
updating the code to work with postgres
This commit is contained in:
parent
eca33dcbdd
commit
6e2056a685
4 changed files with 120 additions and 113 deletions
81
app.py
81
app.py
|
@ -60,7 +60,7 @@ admin_bp = Blueprint('admin', const.appName)
|
||||||
@app.cli.command("init-db")
|
@app.cli.command("init-db")
|
||||||
def initDatabase():
|
def initDatabase():
|
||||||
dbName = os.environ.get("DB_NAME")
|
dbName = os.environ.get("DB_NAME")
|
||||||
print(f"Attempting to connect to database {dbName}...")
|
print("Attempting to connect to database {dbName}...")
|
||||||
try:
|
try:
|
||||||
conn = func.connectToDb()
|
conn = func.connectToDb()
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
|
@ -68,34 +68,30 @@ def initDatabase():
|
||||||
|
|
||||||
conn.database = dbName
|
conn.database = dbName
|
||||||
|
|
||||||
except mysql.connector.Error as error:
|
except psycopg.Error as error:
|
||||||
if error.errno == errorcode.ER_ACCESS_DENIED_ERROR:
|
app.logger.error("Database error:", error)
|
||||||
print("Bad credentials")
|
# if error.errno == errorcode.ER_ACCESS_DENIED_ERROR:
|
||||||
elif error.errno == errorcode.ER_BAD_DB_ERROR:
|
# print("Bad credentials")
|
||||||
dbPort = os.environ.get("DB_PORT")
|
# elif error.errno == errorcode.ER_BAD_DB_ERROR:
|
||||||
if not dbPort:
|
# dbPort = os.environ.get("DB_PORT")
|
||||||
dbPort = 3306
|
# if not dbPort:
|
||||||
|
# dbPort = 3306
|
||||||
conn = mysql.connector.connect(
|
#
|
||||||
user=os.environ.get("DB_USER"),
|
# conn = func.connectToDb()
|
||||||
password=os.environ.get("DB_PASS"),
|
# cursor = conn.cursor()
|
||||||
host=os.environ.get("DB_HOST"),
|
# func.createDatabase(cursor, dbName)
|
||||||
port=dbPort,
|
# conn.database = dbName
|
||||||
database='mysql'
|
# else:
|
||||||
)
|
# print("Error:", error)
|
||||||
cursor = conn.cursor()
|
# return
|
||||||
func.createDatabase(cursor, dbName)
|
|
||||||
conn.database = dbName
|
|
||||||
else:
|
|
||||||
print("Error:", error)
|
|
||||||
return
|
|
||||||
|
|
||||||
with open('schema.sql', 'r') as schema_file:
|
with open('schema.sql', 'r') as schema_file:
|
||||||
schema = schema_file.read()
|
schema = schema_file.read()
|
||||||
try:
|
try:
|
||||||
cursor.execute(schema, multi=True)
|
cursor.execute(schema)
|
||||||
|
conn.commit()
|
||||||
print(f"Database {dbName} was successfully initialized!")
|
print(f"Database {dbName} was successfully initialized!")
|
||||||
except mysql.connector.Error as error:
|
except psycopg.Error as error:
|
||||||
print(f"Failed to initialize database {dbName}: {error}")
|
print(f"Failed to initialize database {dbName}: {error}")
|
||||||
finally:
|
finally:
|
||||||
cursor.close()
|
cursor.close()
|
||||||
|
@ -209,7 +205,7 @@ def viewQuestion(question_id):
|
||||||
@app.route('/questions/', methods=['GET'])
|
@app.route('/questions/', methods=['GET'])
|
||||||
def seeAskedQuestions():
|
def seeAskedQuestions():
|
||||||
conn = func.connectToDb()
|
conn = func.connectToDb()
|
||||||
cursor = conn.cursor(dictionary=True)
|
cursor = conn.cursor()
|
||||||
cursor.execute("SELECT * FROM questions WHERE asker_id=%s ORDER BY creation_date DESC", (asker_id,))
|
cursor.execute("SELECT * FROM questions WHERE asker_id=%s ORDER BY creation_date DESC", (asker_id,))
|
||||||
questions = cursor.fetchall()
|
questions = cursor.fetchall()
|
||||||
|
|
||||||
|
@ -411,7 +407,7 @@ def addQuestion():
|
||||||
def deleteQuestion():
|
def deleteQuestion():
|
||||||
question_id = request.args.get('question_id', '')
|
question_id = request.args.get('question_id', '')
|
||||||
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"))
|
||||||
|
|
||||||
conn = func.connectToDb()
|
conn = func.connectToDb()
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
|
@ -419,17 +415,18 @@ def deleteQuestion():
|
||||||
app.logger.debug("[CatAsk/API/delete_question] DELETE'ing a question from database")
|
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,))
|
||||||
|
conn.commit()
|
||||||
cursor.close()
|
cursor.close()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
return {'message': 'Successfully deleted question.'}, 200
|
return {'message': _("Successfully deleted question.")}, 200
|
||||||
|
|
||||||
@api_bp.route('/return_to_inbox/', methods=['POST'])
|
@api_bp.route('/return_to_inbox/', methods=['POST'])
|
||||||
@loginRequired
|
@loginRequired
|
||||||
def returnToInbox():
|
def returnToInbox():
|
||||||
question_id = request.args.get('question_id', '')
|
question_id = request.args.get('question_id', '')
|
||||||
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"))
|
||||||
|
|
||||||
conn = func.connectToDb()
|
conn = func.connectToDb()
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
|
@ -437,14 +434,14 @@ def returnToInbox():
|
||||||
app.logger.debug("[CatAsk/API/return_to_inbox] SELECT'ing a question from database")
|
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,))
|
cursor.execute("SELECT from_who, content, creation_date, cw FROM questions WHERE id=%s", (question_id,))
|
||||||
row = cursor.fetchone()
|
question = 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]
|
# 'cw': row[3]
|
||||||
}
|
# }
|
||||||
|
|
||||||
app.logger.debug("[CatAsk/API/return_to_inbox] DELETE'ing a question from database")
|
app.logger.debug("[CatAsk/API/return_to_inbox] DELETE'ing a question from database")
|
||||||
|
|
||||||
|
@ -453,17 +450,18 @@ def returnToInbox():
|
||||||
app.logger.debug("[CatAsk/API/return_to_inbox] INSERT'ing a question into database")
|
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.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']))
|
||||||
|
conn.commit()
|
||||||
cursor.close()
|
cursor.close()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
return {'message': 'Successfully returned question to inbox.'}, 200
|
return {'message': _("Successfully returned question to inbox.")}, 200
|
||||||
|
|
||||||
@api_bp.route('/pin_question/', methods=['POST'])
|
@api_bp.route('/pin_question/', methods=['POST'])
|
||||||
@loginRequired
|
@loginRequired
|
||||||
def pinQuestion():
|
def pinQuestion():
|
||||||
question_id = request.args.get('question_id', '')
|
question_id = request.args.get('question_id', '')
|
||||||
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"))
|
||||||
|
|
||||||
conn = func.connectToDb()
|
conn = func.connectToDb()
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
|
@ -471,17 +469,18 @@ def pinQuestion():
|
||||||
app.logger.debug("[CatAsk/API/pin_question] UPDATE'ing a question to pin it")
|
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))
|
||||||
|
conn.commit()
|
||||||
cursor.close()
|
cursor.close()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
return {'message': 'Successfully pinned question.'}, 200
|
return {'message': _("Successfully pinned question.")}, 200
|
||||||
|
|
||||||
@api_bp.route('/unpin_question/', methods=['POST'])
|
@api_bp.route('/unpin_question/', methods=['POST'])
|
||||||
@loginRequired
|
@loginRequired
|
||||||
def unpinQuestion():
|
def unpinQuestion():
|
||||||
question_id = request.args.get('question_id', '')
|
question_id = request.args.get('question_id', '')
|
||||||
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"))
|
||||||
|
|
||||||
conn = func.connectToDb()
|
conn = func.connectToDb()
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
|
@ -489,10 +488,12 @@ def unpinQuestion():
|
||||||
app.logger.debug("[CatAsk/API/unpin_question] UPDATE'ing a question to unpin it")
|
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))
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
cursor.close()
|
cursor.close()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
return {'message': 'Successfully unpinned question.'}, 200
|
return {'message': _("Successfully unpinned question.")}, 200
|
||||||
|
|
||||||
@api_bp.route('/add_answer/', methods=['POST'])
|
@api_bp.route('/add_answer/', methods=['POST'])
|
||||||
@loginRequired
|
@loginRequired
|
||||||
|
|
112
functions.py
112
functions.py
|
@ -101,37 +101,31 @@ dbPort = os.environ.get("DB_PORT")
|
||||||
if not dbPort:
|
if not dbPort:
|
||||||
dbPort = 3306
|
dbPort = 3306
|
||||||
|
|
||||||
def createDatabase(cursor, dbName):
|
def createDatabase(cursor, dbName) -> None:
|
||||||
try:
|
try:
|
||||||
cursor.execute("CREATE DATABASE {} DEFAULT CHARACTER SET 'utf8'".format(dbName))
|
cursor.execute("CREATE DATABASE {} OWNER {}".format(dbName, dbUser))
|
||||||
print(f"Database {dbName} created successfully")
|
print(f"Database {dbName} created successfully")
|
||||||
except mysql.connector.Error as error:
|
except psycopg.Error as error:
|
||||||
print("Failed to create database:", error)
|
print("Failed to create database:", error)
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
def connectToDb():
|
def connectToDb():
|
||||||
conn = mysql.connector.connect(
|
# using dict_row factory here because its easier than modifying now-legacy mysql code
|
||||||
host=dbHost,
|
return psycopg.connect(f"postgresql://{dbUser}:{dbPass}@{dbHost}/{dbName}", row_factory=dict_row)
|
||||||
user=dbUser,
|
|
||||||
password=dbPass,
|
|
||||||
database=dbName,
|
|
||||||
port=dbPort,
|
|
||||||
autocommit=True
|
|
||||||
)
|
|
||||||
return conn
|
|
||||||
|
|
||||||
def getQuestion(question_id: int):
|
def getQuestion(question_id: int) -> dict:
|
||||||
conn = connectToDb()
|
conn = connectToDb()
|
||||||
cursor = conn.cursor(dictionary=True)
|
cursor = conn.cursor()
|
||||||
cursor.execute("SELECT * FROM questions WHERE id=%s", (question_id,))
|
cursor.execute("SELECT * FROM questions WHERE id=%s", (question_id,))
|
||||||
question = cursor.fetchone()
|
question = cursor.fetchone()
|
||||||
|
question['creation_date'] = question['creation_date'].replace(microsecond=0).replace(tzinfo=None)
|
||||||
cursor.close()
|
cursor.close()
|
||||||
conn.close()
|
conn.close()
|
||||||
return question
|
return question
|
||||||
|
|
||||||
def getAllQuestions():
|
def getAllQuestions(limit: int = None, offset: int = None) -> dict:
|
||||||
conn = connectToDb()
|
conn = connectToDb()
|
||||||
cursor = conn.cursor(dictionary=True)
|
cursor = conn.cursor()
|
||||||
|
|
||||||
app.logger.debug("[CatAsk/functions/getAllQuestions] SELECT'ing all questions with latest answers")
|
app.logger.debug("[CatAsk/functions/getAllQuestions] SELECT'ing all questions with latest answers")
|
||||||
|
|
||||||
|
@ -147,11 +141,18 @@ def getAllQuestions():
|
||||||
ORDER BY q.pinned DESC, (a.creation_date IS NULL), a.creation_date DESC, q.creation_date DESC
|
ORDER BY q.pinned DESC, (a.creation_date IS NULL), a.creation_date DESC, q.creation_date DESC
|
||||||
"""
|
"""
|
||||||
|
|
||||||
cursor.execute(query, (True,))
|
params = [True]
|
||||||
|
if limit is not None:
|
||||||
|
query += " LIMIT %s"
|
||||||
|
params.append(limit)
|
||||||
|
if offset is not None:
|
||||||
|
query += " OFFSET %s"
|
||||||
|
params.append(offset)
|
||||||
|
|
||||||
|
cursor.execute(query, tuple(params))
|
||||||
questions = cursor.fetchall()
|
questions = cursor.fetchall()
|
||||||
|
|
||||||
app.logger.debug("[CatAsk/functions/getAllQuestions] SELECT'ing answers")
|
app.logger.debug("[CatAsk/functions/getAllQuestions] 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()
|
||||||
|
|
||||||
|
@ -159,6 +160,9 @@ def getAllQuestions():
|
||||||
|
|
||||||
combined = []
|
combined = []
|
||||||
for question in questions:
|
for question in questions:
|
||||||
|
question['creation_date'] = question['creation_date'].replace(microsecond=0).replace(tzinfo=None)
|
||||||
|
for answer in answers:
|
||||||
|
answer['creation_date'] = answer['creation_date'].replace(microsecond=0).replace(tzinfo=None)
|
||||||
question_answers = [answer for answer in answers if answer['question_id'] == question['id']]
|
question_answers = [answer for answer in answers if answer['question_id'] == question['id']]
|
||||||
combined.append({
|
combined.append({
|
||||||
'question': question,
|
'question': question,
|
||||||
|
@ -201,7 +205,7 @@ def addQuestion(from_who, question, cw, noAntispam=False):
|
||||||
json_r = r.json()
|
json_r = r.json()
|
||||||
success = json_r['success']
|
success = json_r['success']
|
||||||
if not success:
|
if not success:
|
||||||
return {'error': 'An error has occurred'}, 500
|
return {'error': _('An error has occurred')}, 500
|
||||||
elif cfg['antispam']['type'] == 'turnstile':
|
elif cfg['antispam']['type'] == 'turnstile':
|
||||||
r = requests.post(
|
r = requests.post(
|
||||||
'https://challenges.cloudflare.com/turnstile/v0/siteverify',
|
'https://challenges.cloudflare.com/turnstile/v0/siteverify',
|
||||||
|
@ -210,7 +214,7 @@ def addQuestion(from_who, question, cw, noAntispam=False):
|
||||||
json_r = r.json()
|
json_r = r.json()
|
||||||
success = json_r['success']
|
success = json_r['success']
|
||||||
if not success:
|
if not success:
|
||||||
return {'error': 'An error has occurred'}, 500
|
return {'error': _('An error has occurred')}, 500
|
||||||
elif cfg['antispam']['type'] == 'frc':
|
elif cfg['antispam']['type'] == 'frc':
|
||||||
url = 'https://global.frcapi.com/api/v2/captcha/siteverify'
|
url = 'https://global.frcapi.com/api/v2/captcha/siteverify'
|
||||||
headers = {'X-API-Key': cfg['antispam']['frc']['apikey']}
|
headers = {'X-API-Key': cfg['antispam']['frc']['apikey']}
|
||||||
|
@ -219,14 +223,14 @@ def addQuestion(from_who, question, cw, noAntispam=False):
|
||||||
json_r = r.json()
|
json_r = r.json()
|
||||||
success = json_r['success']
|
success = json_r['success']
|
||||||
if not success:
|
if not success:
|
||||||
return {'error': 'An error has occurred'}, 500
|
return {'error': _('An error has occurred')}, 500
|
||||||
|
|
||||||
blacklist = readPlainFile(const.blacklistFile, split=True)
|
blacklist = readPlainFile(const.blacklistFile, split=True)
|
||||||
|
|
||||||
for bad_word in blacklist:
|
for bad_word in blacklist:
|
||||||
if bad_word in question or bad_word in from_who:
|
if bad_word in question or bad_word in from_who:
|
||||||
# return a generic error message so bad actors wouldn't figure out the blacklist
|
# return a generic error message so bad actors wouldn't figure out the blacklist
|
||||||
return {'error': 'An error has occurred'}, 500
|
return {'error': _('An error has occurred')}, 500
|
||||||
|
|
||||||
conn = connectToDb()
|
conn = connectToDb()
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
|
@ -234,45 +238,48 @@ def addQuestion(from_who, question, cw, noAntispam=False):
|
||||||
app.logger.debug("[CatAsk/API/add_question] INSERT'ing new question into database")
|
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) RETURNING id", (from_who, question, False, cw))
|
cursor.execute("INSERT INTO questions (from_who, content, answered, cw) VALUES (%s, %s, %s, %s) RETURNING id", (from_who, question, False, cw))
|
||||||
question_id = cursor.fetchone()[0]
|
question_id = cursor.fetchone()['id']
|
||||||
|
conn.commit()
|
||||||
cursor.close()
|
cursor.close()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
return {'message': 'Question asked successfully!'}, 201, question_id
|
return {'message': _('Question asked successfully!')}, 201, question_id
|
||||||
|
|
||||||
def getAnswer(question_id: int):
|
def getAnswer(question_id: int) -> dict:
|
||||||
conn = connectToDb()
|
conn = connectToDb()
|
||||||
cursor = conn.cursor(dictionary=True)
|
cursor = conn.cursor()
|
||||||
cursor.execute("SELECT * FROM answers WHERE question_id=%s", (question_id,))
|
cursor.execute("SELECT * FROM answers WHERE question_id=%s", (question_id,))
|
||||||
answer = cursor.fetchone()
|
answer = cursor.fetchone()
|
||||||
|
answer['creation_date'] = answer['creation_date'].replace(microsecond=0).replace(tzinfo=None)
|
||||||
cursor.close()
|
cursor.close()
|
||||||
conn.close()
|
conn.close()
|
||||||
return answer
|
return answer
|
||||||
|
|
||||||
def addAnswer(question_id, answer, cw):
|
def addAnswer(question_id: int, answer: str, cw: str) -> dict:
|
||||||
conn = connectToDb()
|
conn = connectToDb()
|
||||||
try:
|
try:
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
|
|
||||||
app.logger.debug("[CatAsk/API/add_answer] INSERT'ing an answer into database")
|
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))
|
cursor.execute("INSERT INTO answers (question_id, content, cw) VALUES (%s, %s, %s) RETURNING id", (question_id, answer, cw))
|
||||||
answer_id = cursor.lastrowid
|
answer_id = cursor.fetchone()['id']
|
||||||
|
|
||||||
app.logger.debug("[CatAsk/API/add_answer] UPDATE'ing question to set answered and answer_id")
|
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:
|
||||||
conn.rollback()
|
# conn.rollback()
|
||||||
return jsonify({'error': str(e)}), 500
|
# app.logger.error(e)
|
||||||
|
# return jsonify({'error': str(e)}), 500
|
||||||
finally:
|
finally:
|
||||||
cursor.close()
|
cursor.close()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
return jsonify({'message': 'Answer added successfully!'}), 201
|
return jsonify({'message': _('Answer added successfully!')}), 201
|
||||||
|
|
||||||
def ntfySend(cw, return_val, from_who, question):
|
def ntfySend(cw, return_val, from_who, question) -> None:
|
||||||
app.logger.debug("[CatAsk/functions/ntfySend] started ntfy flow")
|
app.logger.debug("[CatAsk/functions/ntfySend] started ntfy flow")
|
||||||
ntfy_cw = f" [CW: {cw}]" if cw else ""
|
ntfy_cw = f" [CW: {cw}]" if cw else ""
|
||||||
ntfy_host = cfg['ntfy']['host']
|
ntfy_host = cfg['ntfy']['host']
|
||||||
|
@ -636,12 +643,13 @@ def createExport():
|
||||||
# Export database to SQL file
|
# Export database to SQL file
|
||||||
dump_file = temp_dir / 'database.sql'
|
dump_file = temp_dir / 'database.sql'
|
||||||
result = subprocess.Popen(
|
result = subprocess.Popen(
|
||||||
f'mysqldump --quote-names -u {dbUser} -p{dbPass} {dbName} --result-file={dump_file}',
|
f'pg_dump -U {dbUser} -d {dbName} -F c -E UTF8 -f {dump_file}',
|
||||||
stdin=subprocess.PIPE,
|
stdin=subprocess.PIPE,
|
||||||
shell=True,
|
shell=True,
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
stderr=subprocess.PIPE,
|
stderr=subprocess.PIPE,
|
||||||
encoding="utf-8"
|
encoding="utf-8",
|
||||||
|
env=dict(os.environ, PGPASSWORD=dbPass)
|
||||||
)
|
)
|
||||||
# absolutely dumb workaround for an error
|
# absolutely dumb workaround for an error
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
@ -677,14 +685,14 @@ def createExport():
|
||||||
appendToJSON(export_data, const.exportsFile)
|
appendToJSON(export_data, const.exportsFile)
|
||||||
shutil.rmtree(temp_dir)
|
shutil.rmtree(temp_dir)
|
||||||
|
|
||||||
return {'message': 'Export created successfully!'}
|
return {'message': _('Export created successfully!')}
|
||||||
except mysql.connector.Error as e:
|
except psycopg.Error as e:
|
||||||
return {'error': str(e)}, 500
|
return {'error': str(e)}, 500
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return {'error': str(e)}, 500
|
return {'error': str(e)}, 500
|
||||||
|
|
||||||
|
|
||||||
def importData(export_file):
|
def importData(export_file) -> dict:
|
||||||
try:
|
try:
|
||||||
shutil.unpack_archive(export_file, const.tempDir)
|
shutil.unpack_archive(export_file, const.tempDir)
|
||||||
|
|
||||||
|
@ -706,25 +714,23 @@ def importData(export_file):
|
||||||
conn = connectToDb()
|
conn = connectToDb()
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
|
|
||||||
with open(const.tempDir / 'database.sql', 'r') as schema_file:
|
dump_file = const.tempDir / 'database.sql'
|
||||||
try:
|
process = subprocess.Popen(
|
||||||
# for some reason `cursor.execute(schema, multi=True)` doesn't work, so we use this instead
|
f'pg_restore --clean -U {dbUser} -d {dbName} {dump_file}',
|
||||||
schema = schema_file.read()
|
shell=True,
|
||||||
queries = schema.split(';')
|
stdout=subprocess.PIPE,
|
||||||
for query in queries:
|
stderr=subprocess.PIPE,
|
||||||
cursor.execute(query)
|
encoding="utf-8",
|
||||||
except mysql.connector.Error as e:
|
env=dict(os.environ, PGPASSWORD=dbPass)
|
||||||
return {'error': str(e)}, 500
|
)
|
||||||
finally:
|
shutil.rmtree(const.tempDir)
|
||||||
cursor.close()
|
|
||||||
conn.close()
|
|
||||||
shutil.rmtree(const.tempDir)
|
|
||||||
|
|
||||||
return {'message': 'Data imported successfully!'}
|
return {'message': _('Data imported successfully!')}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return {'error': str(e)}, 500
|
return {'error': str(e)}, 500
|
||||||
|
|
||||||
# will probably get to it in 1.8.0 because my brain can't do it rn
|
# will probably get to it in 1.8.0 because my brain can't do it rn
|
||||||
|
# 2.1.0 maybe -1/12/25
|
||||||
"""
|
"""
|
||||||
def retrospringImport(export_file):
|
def retrospringImport(export_file):
|
||||||
shutil.unpack_archive(export_file, const.tempDir)
|
shutil.unpack_archive(export_file, const.tempDir)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
flask
|
flask
|
||||||
python-dotenv
|
python-dotenv
|
||||||
mysql-connector-python==8.2.0
|
psycopg
|
||||||
humanize
|
humanize
|
||||||
mistune
|
mistune
|
||||||
bleach
|
bleach
|
||||||
|
@ -9,3 +9,4 @@ Flask-Compress
|
||||||
gunicorn
|
gunicorn
|
||||||
pillow
|
pillow
|
||||||
requests
|
requests
|
||||||
|
Flask-Babel
|
||||||
|
|
37
schema.sql
37
schema.sql
|
@ -1,25 +1,24 @@
|
||||||
CREATE TABLE IF NOT EXISTS answers (
|
CREATE TABLE IF NOT EXISTS answers (
|
||||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
id SERIAL PRIMARY KEY,
|
||||||
question_id INT NOT NULL,
|
question_id INTEGER NOT NULL,
|
||||||
creation_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
creation_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
content TEXT NOT NULL,
|
content TEXT NOT NULL,
|
||||||
cw VARCHAR(255) NOT NULL DEFAULT ''
|
cw VARCHAR(255) NOT NULL DEFAULT ''
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS questions (
|
CREATE TABLE IF NOT EXISTS questions (
|
||||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
id SERIAL PRIMARY KEY,
|
||||||
from_who VARCHAR(255) NOT NULL,
|
from_who VARCHAR(255) NOT NULL,
|
||||||
creation_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
creation_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
content TEXT NOT NULL,
|
content TEXT NOT NULL,
|
||||||
answered BOOLEAN NOT NULL DEFAULT FALSE,
|
answered BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
answer_id INT,
|
answer_id INTEGER,
|
||||||
pinned BOOLEAN NOT NULL DEFAULT FALSE,
|
pinned BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
cw VARCHAR(255) NOT NULL DEFAULT '',
|
cw VARCHAR(255) NOT NULL DEFAULT '',
|
||||||
unread BOOLEAN NOT NULL DEFAULT TRUE
|
unread BOOLEAN NOT NULL DEFAULT TRUE
|
||||||
-- below is reserved for version 1.7.0 or later
|
-- private BOOLEAN NOT NULL DEFAULT FALSE, -- For later use
|
||||||
-- private BOOLEAN NOT NULL DEFAULT FALSE,
|
-- user_ip BYTEA NOT NULL DEFAULT '' -- For later use
|
||||||
-- user_ip VARBINARY(16) NOT NULL DEFAULT ''
|
);
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
||||||
|
|
||||||
ALTER TABLE questions
|
ALTER TABLE questions
|
||||||
ADD CONSTRAINT fk_answer_id FOREIGN KEY (answer_id) REFERENCES answers(id) ON DELETE CASCADE;
|
ADD CONSTRAINT fk_answer_id FOREIGN KEY (answer_id) REFERENCES answers(id) ON DELETE CASCADE;
|
||||||
|
|
Loading…
Add table
Reference in a new issue