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 psycopg2
from orgparse import loads
import userHandler
import orgHandler
import dbHandler
@ -13,6 +15,70 @@ def debugPrint(msg: str) -> None:
if debug:
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
def createBlogEntry(dbConnection: psycopg2.extensions.connection, userID: int, categoryID: int) -> int:
datePosted = datetime.datetime.now()
@ -24,6 +90,12 @@ def createBlogEntry(dbConnection: psycopg2.extensions.connection, userID: int, c
[userID, categoryID, datePostedStr])
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):
try:
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.write(orgParsedOut)
HTMLFileData.close()
generateBlogTitle(dbConnection, blogID)
generateBlogDescription(dbConnection, blogID)
return blogID
else:
return False
except Exception as error:
debugPrint("Create blog failed! " + repr(error))
debugPrint("Create blog failed! Unexpected error: " + repr(error) + ".")
return False

View File

@ -49,9 +49,9 @@ def initTable(dbConnection: psycopg2.extensions.connection, tableName: str, tabl
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = '""" + tableName.lower() + """'))
THEN
RAISE NOTICE '""" + tableName + """ Table does exist! Skipping creating table.';
RAISE NOTICE '""" + tableName + """ Table already exists! Skipping table creation...';
ELSE
RAISE NOTICE '""" + tableName + """ Table does not exist! Creating table.';
RAISE NOTICE '""" + tableName + """ Table does not exist! Creating table...';
CREATE TABLE """ + tableName.lower() + """ (
""" + tableFormat + """
);
@ -64,28 +64,34 @@ def initTable(dbConnection: psycopg2.extensions.connection, tableName: str, tabl
dbCursor.close()
# 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:
debugPrint("Commit query executing...")
dbCursor = dbConnection.cursor()
dbCursor.execute(query)
dbConnection.commit()
dbResults = dbCursor.fetchall()
dbCursor.close()
return dbResults
if fetchResults:
dbResults = dbCursor.fetchall()
dbCursor.close()
return dbResults
else:
return True
except Exception as error:
errorPrint("Commit query failed! " + repr(error))
errorPrint("Commit query failed! Unexpected error: " + repr(error))
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:
debugPrint("Exec query executing...")
dbCursor = dbConnection.cursor()
dbCursor.execute(query)
dbResults = dbCursor.fetchall()
dbCursor.close()
return dbResults
if fetchResults:
dbResults = dbCursor.fetchall()
dbCursor.close()
return dbResults
else:
return True
except Exception as error:
errorPrint("Exec query failed! " + repr(error))
errorPrint("Exec query failed! Unexpected error: " + repr(error))
return None
# Callable helper functions
@ -118,6 +124,18 @@ def getFieldValueByID(dbConnection: psycopg2.extensions.connection, tableName: s
)
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:
debugPrint("Attempting to get row values by ID " + str(RowID) + " in table name " + tableName + "...")
sanitisedQuery = sql.SQL("""
@ -146,3 +164,14 @@ def checkFieldValueExistence(dbConnection: psycopg2.extensions.connection, table
)
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,
AuthorID INTEGER,
CategoryID INTEGER,
DatePosted TIMESTAMP
DatePosted TIMESTAMP,
Title VARCHAR(255),
Description VARCHAR(255)
""")
dbHandler.initTable(dbConnection, "Categories", """
ID SERIAL PRIMARY KEY,
Name VARCHAR(255)
""")
debugInitPrint("Creating blog data directory at " + blogDir + "...")
if not os.path.exists(blogDir):
debugInitPrint("Blog data directory does not exist! Creating blog data directory at " + blogDir + "...")
os.mkdir(blogDir)
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")
def apiCleanup():
@ -115,8 +117,8 @@ class ApiBody(BaseModel):
password: str
@app.post("/api")
def postapi(body: ApiBody):
print(body.username)
print(body.password)
debugPrint(body.username)
debugPrint(body.password)
return body
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."}
except Exception as error:
msg = "User login failed! Unexpected server error. " + repr(error)
print(msg)
debugPrint(msg)
return {"success": False, "authToken": None, "message": msg}
@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."}
except Exception as error:
msg = "Get userID by authToken failed! Unexpected server error. " + repr(error)
print(msg)
debugPrint(msg)
return {"success": False, "authToken": None, "message": msg}
@app.get("/api/user/publicInfo/{userID}")
@ -173,7 +175,7 @@ def getuserPublicInfo(userID: int):
}
except Exception as error:
msg = "Get public info failed! Unexpected server error. " + repr(error)
print(msg)
debugPrint(msg)
return {
"success": False,
"username": None,
@ -204,7 +206,7 @@ def getuserSettingsAccount(authToken: Annotated[str | None, Header()] = None):
}
except Exception as error:
msg = "Get user settings failed! Unexpected server error. " + repr(error)
print(msg)
debugPrint(msg)
return {
"success": False,
"username": None,
@ -213,6 +215,49 @@ def getuserSettingsAccount(authToken: Annotated[str | None, Header()] = None):
"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):
authToken: str
@ -243,7 +288,7 @@ def postblogCreate(body: postblogCreateBody):
}
except Exception as error:
msg = "Create blog failed! Unexpected server error. " + repr(error)
print(msg)
debugPrint(msg)
return {
"success": False,
"blogID": None,
@ -275,6 +320,8 @@ def postblogCreate(body: postblogCreateBody):
# /api/blog/pictureURL/{blogID}
# /api/blog/description/{blogID}
# /api/blog/datePosted/{blogID}
# /api/blog/contents/HTML/{blogID}
# /api/blog/contents/org/{blogID}
# POST
# /api/user/changeInfo/{infotype}

View File

@ -16,32 +16,7 @@ def checkAndRetrieveMetadata(fileData: str, metadataName: str):
else:
debugPrint("Could not find " + metadataFullName + " metadata field of document!")
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:
try:
orgRoot = loads(orgData)
@ -49,10 +24,10 @@ def orgToHTML(orgData: str) -> str:
for node in orgRoot[1:]:
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"
if node.body:
parsedHTML += node.body + "\n"
return parsedHTML
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
def handlePasswordVerification(dbConnection: psycopg2.extensions.connection, password: str, userID: int) -> bool:
hash = userHandler.getHashValueByUserID(dbConnection, userID)
userIDstr = str(userID)
debugPrint("Now verifying password against hash for user ID " + userIDstr + "...")
if verifyPassword(password, hash):
debugPrint("(USER ID " + userIDstr + ") Password verification success!")
if verifyRehash(hash):
debugPrint("(USER ID " + userIDstr + ") Hash needs to be rehashed! Will now rehash...")
return True
else:
debugPrint("(USER ID " + userIDstr + ") Password verification failure!")
try:
hash = userHandler.getHashValueByUserID(dbConnection, userID)
userIDstr = str(userID)
debugPrint("Now verifying password against hash for user ID " + userIDstr + "...")
if verifyPassword(password, hash):
debugPrint("(USER ID " + userIDstr + ") Password verification success!")
if verifyRehash(hash):
debugPrint("(USER ID " + userIDstr + ") Hash needs to be rehashed! Will now rehash...")
return True
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