Implementing retrieving blog data

This commit is contained in:
Curt Spark 2024-05-08 20:56:28 +01:00
parent faa718c7d5
commit 107df32af9
5 changed files with 191 additions and 60 deletions

View File

@ -2,7 +2,9 @@ import os
import datetime import datetime
import psycopg2 import psycopg2
from orgparse import loads
import userHandler
import orgHandler import orgHandler
import dbHandler import dbHandler
@ -13,6 +15,70 @@ def debugPrint(msg: str) -> None:
if debug: if debug:
print("(BLOG HANDLER) PRINT: " + msg) print("(BLOG HANDLER) PRINT: " + msg)
def checkIDExistence(dbConnection: psycopg2.extensions.connection, blogID: int) -> bool:
return dbHandler.checkFieldValueExistence(dbConnection, "blogs", "id", blogID)
def getBlogCardRange(dbConnection: psycopg2.extensions.connection, rangeStart: int, rangeEnd: int, latestRecords = True) -> dict:
records = dbHandler.getRowRangeByID(dbConnection, "blogs", rangeStart, rangeEnd, latestRecords)
recordsDictionary = {}
for index, record in enumerate(records):
datePostedStr = record[3].strftime("%G-%m-%d %X")
# 0 index reserved for success/message status
recordsDictionary[index+1] = {
"blogID": record[0],
"authorInfo": {
"ID": record[1],
"username": userHandler.getUserInfoByID(dbConnection, record[1], "username"),
"firstName": userHandler.getUserInfoByID(dbConnection, record[1], "firstname"),
"lastName": userHandler.getUserInfoByID(dbConnection, record[1], "lastname")
},
"categoryID": record[2],
"datePosted": datePostedStr,
"title": record[4],
"description": record[5]
}
return recordsDictionary
def getBlogTitle(blogID: int) -> str:
debugPrint("Attempting to retrieve blog title of blog ID " + str(blogID))
try:
fileData = open(blogDir + "/" + str(blogID) + "/" + str(blogID) + ".org", 'r')
title = orgHandler.checkAndRetrieveMetadata(fileData, "TITLE")
if title:
return title
except Exception as error:
debugPrint("Error getting blog title! " + repr(error))
def getBlogDescription(blogID: int) -> str:
debugPrint("Attempting to retrieve blog description of blog ID " + str(blogID))
try:
fileData = open(blogDir + "/" + str(blogID) + "/" + str(blogID) + ".org", 'r')
orgRoot = loads(fileData.read())
fileData.readline()
shortDescription = orgHandler.checkAndRetrieveMetadata(fileData, "DESCRIPTION")
if not shortDescription:
debugPrint("No valid description found, will generate a placeholder from the text itself...")
firstText = orgRoot[1].body
shortDescription = (firstText[:60] + "...") if len(firstText) > 60 else firstText
return shortDescription
except Exception as error:
debugPrint("Error getting blog description! Unexpected error: " + repr(error) + ".")
def getBlogContentsHTML(blogID: int) -> str:
try:
debugPrint("Attempting to retrieve blog contents of blogID " + str(blogID) + "...")
FullBlogDir = blogDir + "/" + str(blogID)
HTMLFileData = open(FullBlogDir + "/" + str(blogID) + ".html", 'r')
HTMLFileContents = HTMLFileData.read()
HTMLFileData.close()
return HTMLFileContents
except Exception as error:
msg = "Retrieve blog contents of blogID " + str(blogID) + " failed! Unexpected error: " + repr(error) + "."
debugPrint(msg)
return msg
# Returns new Blog ID # Returns new Blog ID
def createBlogEntry(dbConnection: psycopg2.extensions.connection, userID: int, categoryID: int) -> int: def createBlogEntry(dbConnection: psycopg2.extensions.connection, userID: int, categoryID: int) -> int:
datePosted = datetime.datetime.now() datePosted = datetime.datetime.now()
@ -24,6 +90,12 @@ def createBlogEntry(dbConnection: psycopg2.extensions.connection, userID: int, c
[userID, categoryID, datePostedStr]) [userID, categoryID, datePostedStr])
return newRow[0] return newRow[0]
def generateBlogTitle(dbConnection: psycopg2.extensions.connection, blogID: int) -> tuple:
return dbHandler.changeFieldValueByID(dbConnection, "blogs", blogID, "title", getBlogTitle(blogID))
def generateBlogDescription(dbConnection: psycopg2.extensions.connection, blogID: int) -> tuple:
return dbHandler.changeFieldValueByID(dbConnection, "blogs", blogID, "description", getBlogDescription(blogID))
def uploadBlog(dbConnection: psycopg2.extensions.connection, userID: int, orgRawIn: str): def uploadBlog(dbConnection: psycopg2.extensions.connection, userID: int, orgRawIn: str):
try: try:
orgParsedOut = orgHandler.orgToHTML(orgRawIn) orgParsedOut = orgHandler.orgToHTML(orgRawIn)
@ -43,9 +115,13 @@ def uploadBlog(dbConnection: psycopg2.extensions.connection, userID: int, orgRaw
HTMLFileData = open(newBlogDir + "/" + str(blogID) + ".html", 'w') HTMLFileData = open(newBlogDir + "/" + str(blogID) + ".html", 'w')
HTMLFileData.write(orgParsedOut) HTMLFileData.write(orgParsedOut)
HTMLFileData.close() HTMLFileData.close()
generateBlogTitle(dbConnection, blogID)
generateBlogDescription(dbConnection, blogID)
return blogID return blogID
else: else:
return False return False
except Exception as error: except Exception as error:
debugPrint("Create blog failed! " + repr(error)) debugPrint("Create blog failed! Unexpected error: " + repr(error) + ".")
return False return False

View File

@ -49,9 +49,9 @@ def initTable(dbConnection: psycopg2.extensions.connection, tableName: str, tabl
FROM INFORMATION_SCHEMA.TABLES FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = '""" + tableName.lower() + """')) WHERE TABLE_NAME = '""" + tableName.lower() + """'))
THEN THEN
RAISE NOTICE '""" + tableName + """ Table does exist! Skipping creating table.'; RAISE NOTICE '""" + tableName + """ Table already exists! Skipping table creation...';
ELSE ELSE
RAISE NOTICE '""" + tableName + """ Table does not exist! Creating table.'; RAISE NOTICE '""" + tableName + """ Table does not exist! Creating table...';
CREATE TABLE """ + tableName.lower() + """ ( CREATE TABLE """ + tableName.lower() + """ (
""" + tableFormat + """ """ + tableFormat + """
); );
@ -64,28 +64,34 @@ def initTable(dbConnection: psycopg2.extensions.connection, tableName: str, tabl
dbCursor.close() dbCursor.close()
# These base functions should not be called directly as they perform no string query sanitisation (Therefore vulnerable to SQL injection attacks) # These base functions should not be called directly as they perform no string query sanitisation (Therefore vulnerable to SQL injection attacks)
def _commitQuery(dbConnection: psycopg2.extensions.connection, query: sql.Composable) -> list: def _commitQuery(dbConnection: psycopg2.extensions.connection, query: sql.Composable, fetchResults: bool = True):
try: try:
debugPrint("Commit query executing...") debugPrint("Commit query executing...")
dbCursor = dbConnection.cursor() dbCursor = dbConnection.cursor()
dbCursor.execute(query) dbCursor.execute(query)
dbConnection.commit() dbConnection.commit()
dbResults = dbCursor.fetchall() if fetchResults:
dbCursor.close() dbResults = dbCursor.fetchall()
return dbResults dbCursor.close()
return dbResults
else:
return True
except Exception as error: except Exception as error:
errorPrint("Commit query failed! " + repr(error)) errorPrint("Commit query failed! Unexpected error: " + repr(error))
return None return None
def _execQuery(dbConnection: psycopg2.extensions.connection, query: sql.Composable) -> list: def _execQuery(dbConnection: psycopg2.extensions.connection, query: sql.Composable, fetchResults: bool = True):
try: try:
debugPrint("Exec query executing...") debugPrint("Exec query executing...")
dbCursor = dbConnection.cursor() dbCursor = dbConnection.cursor()
dbCursor.execute(query) dbCursor.execute(query)
dbResults = dbCursor.fetchall() if fetchResults:
dbCursor.close() dbResults = dbCursor.fetchall()
return dbResults dbCursor.close()
return dbResults
else:
return True
except Exception as error: except Exception as error:
errorPrint("Exec query failed! " + repr(error)) errorPrint("Exec query failed! Unexpected error: " + repr(error))
return None return None
# Callable helper functions # Callable helper functions
@ -118,6 +124,18 @@ def getFieldValueByID(dbConnection: psycopg2.extensions.connection, tableName: s
) )
return str(_execQuery(dbConnection, sanitisedQuery)[0][0]) return str(_execQuery(dbConnection, sanitisedQuery)[0][0])
def changeFieldValueByID(dbConnection: psycopg2.extensions.connection, tableName: str, RowID: int, tableField: str, newValue) -> str:
debugPrint("Attempting to change value of field name " + tableField + " in ID row " + str(RowID) + " in table name " + tableName + " to " + str(newValue) + "...")
sanitisedQuery = sql.SQL("""
UPDATE {table} SET {field} = {value} WHERE "id" = {id}
""").format(
table=sql.Identifier(tableName),
field=sql.Identifier(tableField),
id=sql.Literal(RowID),
value=sql.Literal(newValue)
)
return _commitQuery(dbConnection, sanitisedQuery, False)
def getRowByID(dbConnection: psycopg2.extensions.connection, tableName: str, RowID: int) -> tuple: def getRowByID(dbConnection: psycopg2.extensions.connection, tableName: str, RowID: int) -> tuple:
debugPrint("Attempting to get row values by ID " + str(RowID) + " in table name " + tableName + "...") debugPrint("Attempting to get row values by ID " + str(RowID) + " in table name " + tableName + "...")
sanitisedQuery = sql.SQL(""" sanitisedQuery = sql.SQL("""
@ -146,3 +164,14 @@ def checkFieldValueExistence(dbConnection: psycopg2.extensions.connection, table
) )
return bool(_execQuery(dbConnection, sanitisedQuery)[0][0]) return bool(_execQuery(dbConnection, sanitisedQuery)[0][0])
def getRowRangeByID(dbConnection: psycopg2.extensions.connection, tableName: str, rangeStart: int, rangeEnd: int, latestRecords = True) -> tuple:
debugPrint("Getting rows from table name " + tableName + " from range " + str(rangeStart) + "-" + str(rangeEnd) + "...")
sanitisedQuery = sql.SQL("""
SELECT * FROM {table} WHERE id >= {start} AND id <= {end} ORDER BY id {order}
""").format(
table=sql.Identifier(tableName),
start=sql.Literal(rangeStart),
end=sql.Literal(rangeEnd),
order=sql.SQL("DESC" if latestRecords else "ASC")
)
return tuple(_execQuery(dbConnection, sanitisedQuery))

67
main.py
View File

@ -66,17 +66,19 @@ def apiInit():
ID SERIAL PRIMARY KEY, ID SERIAL PRIMARY KEY,
AuthorID INTEGER, AuthorID INTEGER,
CategoryID INTEGER, CategoryID INTEGER,
DatePosted TIMESTAMP DatePosted TIMESTAMP,
Title VARCHAR(255),
Description VARCHAR(255)
""") """)
dbHandler.initTable(dbConnection, "Categories", """ dbHandler.initTable(dbConnection, "Categories", """
ID SERIAL PRIMARY KEY, ID SERIAL PRIMARY KEY,
Name VARCHAR(255) Name VARCHAR(255)
""") """)
debugInitPrint("Creating blog data directory at " + blogDir + "...")
if not os.path.exists(blogDir): if not os.path.exists(blogDir):
debugInitPrint("Blog data directory does not exist! Creating blog data directory at " + blogDir + "...")
os.mkdir(blogDir) os.mkdir(blogDir)
else: else:
debugInitPrint("Blog data directory already exists! Skipping...") debugInitPrint("Blog data directory already exists! Skipping directory creation...")
# userHandler.createUser(dbConnection, "testuser", "Test", "User", "A test user", "TestCountry", "TestTheme", "TestColor", "testuser") # userHandler.createUser(dbConnection, "testuser", "Test", "User", "A test user", "TestCountry", "TestTheme", "TestColor", "testuser")
def apiCleanup(): def apiCleanup():
@ -115,8 +117,8 @@ class ApiBody(BaseModel):
password: str password: str
@app.post("/api") @app.post("/api")
def postapi(body: ApiBody): def postapi(body: ApiBody):
print(body.username) debugPrint(body.username)
print(body.password) debugPrint(body.password)
return body return body
class postuserLoginBody(BaseModel): class postuserLoginBody(BaseModel):
@ -136,7 +138,7 @@ def postuserLogin(body: postuserLoginBody, request: Request):
return {"success": False, "authToken": None, "message": "User login failed! User does not exist."} return {"success": False, "authToken": None, "message": "User login failed! User does not exist."}
except Exception as error: except Exception as error:
msg = "User login failed! Unexpected server error. " + repr(error) msg = "User login failed! Unexpected server error. " + repr(error)
print(msg) debugPrint(msg)
return {"success": False, "authToken": None, "message": msg} return {"success": False, "authToken": None, "message": msg}
@app.get("/api/user/IDByAuthToken") @app.get("/api/user/IDByAuthToken")
@ -149,7 +151,7 @@ def getuserIDByAuthToken(authToken: Annotated[str | None, Header()] = None):
return {"success": False, "userID": None, "message": "Get userID by authToken failed! authToken provided is not valid."} return {"success": False, "userID": None, "message": "Get userID by authToken failed! authToken provided is not valid."}
except Exception as error: except Exception as error:
msg = "Get userID by authToken failed! Unexpected server error. " + repr(error) msg = "Get userID by authToken failed! Unexpected server error. " + repr(error)
print(msg) debugPrint(msg)
return {"success": False, "authToken": None, "message": msg} return {"success": False, "authToken": None, "message": msg}
@app.get("/api/user/publicInfo/{userID}") @app.get("/api/user/publicInfo/{userID}")
@ -173,7 +175,7 @@ def getuserPublicInfo(userID: int):
} }
except Exception as error: except Exception as error:
msg = "Get public info failed! Unexpected server error. " + repr(error) msg = "Get public info failed! Unexpected server error. " + repr(error)
print(msg) debugPrint(msg)
return { return {
"success": False, "success": False,
"username": None, "username": None,
@ -204,7 +206,7 @@ def getuserSettingsAccount(authToken: Annotated[str | None, Header()] = None):
} }
except Exception as error: except Exception as error:
msg = "Get user settings failed! Unexpected server error. " + repr(error) msg = "Get user settings failed! Unexpected server error. " + repr(error)
print(msg) debugPrint(msg)
return { return {
"success": False, "success": False,
"username": None, "username": None,
@ -213,6 +215,49 @@ def getuserSettingsAccount(authToken: Annotated[str | None, Header()] = None):
"message": msg "message": msg
} }
@app.get("/api/user/blog/cardInfo/")
def getblogCardInfo(rangeStart: int = 1, rangeEnd: int = 25, sortByLatest: bool = True):
try:
blogCardData = blogHandler.getBlogCardRange(dbConnection, rangeStart, rangeEnd, sortByLatest)
blogCardData[0] = {
"success": True,
"message": "Get card info succeeded!"
}
return blogCardData
except Exception as error:
blogCardData = {}
msg = "Get card info failed! Unexpected server error. " + repr(error)
debugPrint(msg)
blogCardData[0] = {
"success": False,
"message": msg
}
return blogCardData
@app.get("/api/blog/contents/HTML/{blogID}")
def getblogContentsHTML(blogID: int):
try:
if blogHandler.checkIDExistence(dbConnection, blogID):
return {
"success": True,
"contents": blogHandler.getBlogContentsHTML(blogID),
"message": "Get blog contents in HTML succeeded!"
}
else:
return {
"success": False,
"contents": None,
"message": "Get blog contents in HTML failed! BlogID provided does not exist."
}
except Exception as error:
msg = "Get blog contents in HTML failed! Unexpected server error. " + repr(error)
debugPrint(msg)
return {
"success": False,
"contents": None,
"message": msg
}
class postblogCreateBody(BaseModel): class postblogCreateBody(BaseModel):
authToken: str authToken: str
@ -243,7 +288,7 @@ def postblogCreate(body: postblogCreateBody):
} }
except Exception as error: except Exception as error:
msg = "Create blog failed! Unexpected server error. " + repr(error) msg = "Create blog failed! Unexpected server error. " + repr(error)
print(msg) debugPrint(msg)
return { return {
"success": False, "success": False,
"blogID": None, "blogID": None,
@ -275,6 +320,8 @@ def postblogCreate(body: postblogCreateBody):
# /api/blog/pictureURL/{blogID} # /api/blog/pictureURL/{blogID}
# /api/blog/description/{blogID} # /api/blog/description/{blogID}
# /api/blog/datePosted/{blogID} # /api/blog/datePosted/{blogID}
# /api/blog/contents/HTML/{blogID}
# /api/blog/contents/org/{blogID}
# POST # POST
# /api/user/changeInfo/{infotype} # /api/user/changeInfo/{infotype}

View File

@ -16,32 +16,7 @@ def checkAndRetrieveMetadata(fileData: str, metadataName: str):
else: else:
debugPrint("Could not find " + metadataFullName + " metadata field of document!") debugPrint("Could not find " + metadataFullName + " metadata field of document!")
return False return False
def getOrgTitle(blogID: int) -> str:
try:
fileData = open(blogDir + "/" + str(blogID) + "/" + str(blogID) + ".org", 'r')
title = checkAndRetrieveMetadata(fileData, "TITLE")
if title:
return title
except Exception as error:
debugPrint("Error getting blog title! " + repr(error))
def getOrgDescription(blogID: str) -> str:
try:
fileData = open(blogDir + "/" + str(blogID) + "/" + str(blogID) + ".org", 'r')
orgRoot = loads(fileData.read())
fileData.readline()
shortDescription = checkAndRetrieveMetadata(fileData, "DESCRIPTION")
if not shortDescription:
debugPrint("No valid description found, will generate a placeholder from the text itself...")
firstText = orgRoot[1].body
shortDescription = (firstText[:60] + "...") if len(firstText) > 60 else firstText
return shortDescription
except Exception as error:
debugPrint("Error getting org title! " + repr(error))
def orgToHTML(orgData: str) -> str: def orgToHTML(orgData: str) -> str:
try: try:
orgRoot = loads(orgData) orgRoot = loads(orgData)
@ -49,10 +24,10 @@ def orgToHTML(orgData: str) -> str:
for node in orgRoot[1:]: for node in orgRoot[1:]:
if node.heading: if node.heading:
headingLevel = str(node.level) headingLevel = str(node.level if node.level <= 6 else 6)
parsedHTML += "<h" + headingLevel + ">" + node.heading + "</h" + headingLevel + ">" + "\n" parsedHTML += "<h" + headingLevel + ">" + node.heading + "</h" + headingLevel + ">" + "\n"
if node.body: if node.body:
parsedHTML += node.body + "\n" parsedHTML += node.body + "\n"
return parsedHTML return parsedHTML
except Exception as error: except Exception as error:
debugPrint("Error parsing org! " + repr(error)) debugPrint("Error parsing org! Unexpected error: " + repr(error) + ".")

View File

@ -35,14 +35,18 @@ def verifyRehash(hash: str) -> bool:
return False return False
def handlePasswordVerification(dbConnection: psycopg2.extensions.connection, password: str, userID: int) -> bool: def handlePasswordVerification(dbConnection: psycopg2.extensions.connection, password: str, userID: int) -> bool:
hash = userHandler.getHashValueByUserID(dbConnection, userID) try:
userIDstr = str(userID) hash = userHandler.getHashValueByUserID(dbConnection, userID)
debugPrint("Now verifying password against hash for user ID " + userIDstr + "...") userIDstr = str(userID)
if verifyPassword(password, hash): debugPrint("Now verifying password against hash for user ID " + userIDstr + "...")
debugPrint("(USER ID " + userIDstr + ") Password verification success!") if verifyPassword(password, hash):
if verifyRehash(hash): debugPrint("(USER ID " + userIDstr + ") Password verification success!")
debugPrint("(USER ID " + userIDstr + ") Hash needs to be rehashed! Will now rehash...") if verifyRehash(hash):
return True debugPrint("(USER ID " + userIDstr + ") Hash needs to be rehashed! Will now rehash...")
else: return True
debugPrint("(USER ID " + userIDstr + ") Password verification failure!") else:
debugPrint("(USER ID " + userIDstr + ") Password verification failure! Invalid password.")
return False
except Exception as error:
debugPrint("(USER ID " + userIDstr + ") Password verification failure! Unexpected error: " + repr(error) + ".")
return False return False