update database connection logic; reformat some sql transactions
parent
cf16169027
commit
2fd58556fc
27
server.py
27
server.py
|
@ -11,19 +11,17 @@ dbname = "data.sqlite"
|
||||||
|
|
||||||
# user anonymity is achieved in the laziest possible way: a literal user
|
# user anonymity is achieved in the laziest possible way: a literal user
|
||||||
# named anonymous. may god have mercy on my soul.
|
# named anonymous. may god have mercy on my soul.
|
||||||
with sqlite3.connect(dbname) as _c:
|
_c = sqlite3.connect(dbname)
|
||||||
|
try:
|
||||||
db.anon = db.user_resolve(_c, "anonymous")
|
db.anon = db.user_resolve(_c, "anonymous")
|
||||||
if not db.anon:
|
if not db.anon:
|
||||||
db.anon = db.user_register(
|
db.anon = db.user_register(
|
||||||
_c, "anonymous", # this is the hash for "anon"
|
_c, "anonymous", # this is the hash for "anon"
|
||||||
"5430eeed859cad61d925097ec4f53246"
|
"5430eeed859cad61d925097ec4f53246"
|
||||||
"1ccf1ab6b9802b09a313be1478a4d614")
|
"1ccf1ab6b9802b09a313be1478a4d614")
|
||||||
|
finally:
|
||||||
|
_c.close()
|
||||||
# creates a database connection for each thread
|
del _c
|
||||||
def db_connect(_):
|
|
||||||
cherrypy.thread_data.db = sqlite3.connect(dbname)
|
|
||||||
cherrypy.engine.subscribe('start_thread', db_connect)
|
|
||||||
|
|
||||||
|
|
||||||
def api_method(function):
|
def api_method(function):
|
||||||
|
@ -44,6 +42,7 @@ def api_method(function):
|
||||||
def wrapper(self, *args, **kwargs):
|
def wrapper(self, *args, **kwargs):
|
||||||
response = None
|
response = None
|
||||||
try:
|
try:
|
||||||
|
connection = sqlite3.connect(dbname)
|
||||||
# read in the body from the request to a string...
|
# read in the body from the request to a string...
|
||||||
body = str(cherrypy.request.body.read(), "utf8")
|
body = str(cherrypy.request.body.read(), "utf8")
|
||||||
# is it just empty bytes? not all methods require an input
|
# is it just empty bytes? not all methods require an input
|
||||||
|
@ -64,7 +63,7 @@ def api_method(function):
|
||||||
user = db.anon
|
user = db.anon
|
||||||
|
|
||||||
else:
|
else:
|
||||||
user = db.user_resolve(cherrypy.thread_data.db, username)
|
user = db.user_resolve(connection, username)
|
||||||
if not user:
|
if not user:
|
||||||
raise BBJUserError("User %s is not registered" % username)
|
raise BBJUserError("User %s is not registered" % username)
|
||||||
|
|
||||||
|
@ -75,7 +74,7 @@ def api_method(function):
|
||||||
# api_methods may choose to bind a usermap into the thread_data
|
# api_methods may choose to bind a usermap into the thread_data
|
||||||
# which will send it off with the response
|
# which will send it off with the response
|
||||||
cherrypy.thread_data.usermap = {}
|
cherrypy.thread_data.usermap = {}
|
||||||
value = function(self, body, cherrypy.thread_data.db, user)
|
value = function(self, body, connection, user)
|
||||||
response = schema.response(value, cherrypy.thread_data.usermap)
|
response = schema.response(value, cherrypy.thread_data.usermap)
|
||||||
|
|
||||||
except BBJException as e:
|
except BBJException as e:
|
||||||
|
@ -95,6 +94,7 @@ def api_method(function):
|
||||||
print("logged code 1 exception " + error_id)
|
print("logged code 1 exception " + error_id)
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
|
connection.close()
|
||||||
return json.dumps(response)
|
return json.dumps(response)
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
@ -165,6 +165,8 @@ class API(object):
|
||||||
|
|
||||||
The newly updated user object is returned on success.
|
The newly updated user object is returned on success.
|
||||||
"""
|
"""
|
||||||
|
if user == db.anon:
|
||||||
|
raise BBJParameterError("Anons cannot modify their account.")
|
||||||
validate(args, []) # just make sure its not empty
|
validate(args, []) # just make sure its not empty
|
||||||
return db.user_update(database, user, args)
|
return db.user_update(database, user, args)
|
||||||
|
|
||||||
|
@ -209,7 +211,8 @@ class API(object):
|
||||||
validate(args, ["body", "title"])
|
validate(args, ["body", "title"])
|
||||||
thread = db.thread_create(
|
thread = db.thread_create(
|
||||||
database, user["user_id"], args["body"], args["title"])
|
database, user["user_id"], args["body"], args["title"])
|
||||||
cherrypy.thread_data.usermap = thread
|
cherrypy.thread_data.usermap = \
|
||||||
|
create_usermap(database, thread["messages"])
|
||||||
return thread
|
return thread
|
||||||
|
|
||||||
|
|
||||||
|
@ -255,7 +258,7 @@ class API(object):
|
||||||
if user == db.anon:
|
if user == db.anon:
|
||||||
raise BBJUserError("Anons cannot edit messages.")
|
raise BBJUserError("Anons cannot edit messages.")
|
||||||
validate(args, ["body", "thread_id", "post_id"])
|
validate(args, ["body", "thread_id", "post_id"])
|
||||||
return message_edit_commit(
|
return db.message_edit_commit(
|
||||||
database, user["user_id"], args["thread_id"], args["post_id"], args["body"])
|
database, user["user_id"], args["thread_id"], args["post_id"], args["body"])
|
||||||
|
|
||||||
|
|
||||||
|
@ -272,7 +275,7 @@ class API(object):
|
||||||
if user == db.anon:
|
if user == db.anon:
|
||||||
raise BBJUserError("Anons cannot edit messages.")
|
raise BBJUserError("Anons cannot edit messages.")
|
||||||
validate(args, ["thread_id", "post_id"])
|
validate(args, ["thread_id", "post_id"])
|
||||||
return message_edit_query(
|
return db.message_edit_query(
|
||||||
database, user["user_id"], args["thread_id"], args["post_id"])
|
database, user["user_id"], args["thread_id"], args["post_id"])
|
||||||
|
|
||||||
|
|
||||||
|
|
71
src/db.py
71
src/db.py
|
@ -41,16 +41,18 @@ def thread_get(connection, thread_id, messages=True):
|
||||||
and only get its metadata, such as title, author, etc.
|
and only get its metadata, such as title, author, etc.
|
||||||
"""
|
"""
|
||||||
c = connection.cursor()
|
c = connection.cursor()
|
||||||
c.execute("SELECT * FROM threads WHERE thread_id = ?", (thread_id,))
|
thread = c.execute(
|
||||||
thread = c.fetchone()
|
"SELECT * FROM threads WHERE thread_id = ?",
|
||||||
|
(thread_id,)).fetchone()
|
||||||
|
|
||||||
if not thread:
|
if not thread:
|
||||||
raise BBJParameterError("Thread does not exist.")
|
raise BBJParameterError("Thread does not exist.")
|
||||||
thread = schema.thread(*thread)
|
thread = schema.thread(*thread)
|
||||||
|
|
||||||
if messages:
|
if messages:
|
||||||
c.execute("""SELECT * FROM messages WHERE thread_id = ?
|
c.execute("""
|
||||||
ORDER BY post_id""", (thread_id,))
|
SELECT * FROM messages WHERE thread_id = ?
|
||||||
|
ORDER BY post_id""", (thread_id,))
|
||||||
# create a list where each post_id matches its list[index]
|
# create a list where each post_id matches its list[index]
|
||||||
thread["messages"] = [schema.message(*values) for values in c.fetchall()]
|
thread["messages"] = [schema.message(*values) for values in c.fetchall()]
|
||||||
|
|
||||||
|
@ -65,10 +67,10 @@ def thread_index(connection):
|
||||||
|
|
||||||
Please note that thred["messages"] is omitted.
|
Please note that thred["messages"] is omitted.
|
||||||
"""
|
"""
|
||||||
c = connection.cursor()
|
c = connection.execute("""
|
||||||
c.execute("""
|
SELECT thread_id FROM threads
|
||||||
SELECT thread_id FROM threads
|
ORDER BY last_mod DESC""")
|
||||||
ORDER BY last_mod DESC""")
|
|
||||||
threads = [
|
threads = [
|
||||||
thread_get(connection, obj[0], messages=False)
|
thread_get(connection, obj[0], messages=False)
|
||||||
for obj in c.fetchall()
|
for obj in c.fetchall()
|
||||||
|
@ -91,29 +93,23 @@ def thread_create(connection, author_id, body, title):
|
||||||
thread_id, author_id, title,
|
thread_id, author_id, title,
|
||||||
now, now, -1) # see below for why i set -1 instead of 0
|
now, now, -1) # see below for why i set -1 instead of 0
|
||||||
|
|
||||||
connection.cursor().execute("""
|
connection.execute("""
|
||||||
INSERT INTO threads
|
INSERT INTO threads
|
||||||
VALUES (?,?,?,?,?,?)
|
VALUES (?,?,?,?,?,?)
|
||||||
""", schema_values("thread", scheme))
|
""", schema_values("thread", scheme))
|
||||||
connection.commit()
|
connection.commit()
|
||||||
|
thread_reply(connection, author_id, thread_id, body, time_override=now)
|
||||||
scheme["messages"] = [
|
# fetch the new thread out of the database instead of reusing the returned
|
||||||
thread_reply(connection, author_id, thread_id, body, time_override=now)
|
# objects, just to be 100% sure what is returned is what was committed
|
||||||
]
|
return thread_get(connection, thread_id)
|
||||||
scheme["reply_count"] = 0
|
|
||||||
# note that thread_reply returns a schema object
|
|
||||||
# after committing the new message to the database.
|
|
||||||
# here i mimic a real thread_get by including a mock
|
|
||||||
# message array, and then setting the reply_count
|
|
||||||
# to reflect its new database value, so the response
|
|
||||||
# can be loaded as a normal thread object
|
|
||||||
# this hackery needs a bit more testing
|
|
||||||
return scheme
|
|
||||||
|
|
||||||
|
|
||||||
def thread_reply(connection, author_id, thread_id, body, time_override=None):
|
def thread_reply(connection, author_id, thread_id, body, time_override=None):
|
||||||
"""
|
"""
|
||||||
Submit a new reply for thread_id. Return the new reply object.
|
Submit a new reply for thread_id. Return the new reply object.
|
||||||
|
|
||||||
|
time_overide can be time() value to set as the new message time.
|
||||||
|
This is to keep post_id 0 in exact parity with its parent thread.
|
||||||
"""
|
"""
|
||||||
validate([("body", body)])
|
validate([("body", body)])
|
||||||
|
|
||||||
|
@ -124,14 +120,12 @@ def thread_reply(connection, author_id, thread_id, body, time_override=None):
|
||||||
thread_id, count, author_id,
|
thread_id, count, author_id,
|
||||||
now, False, body)
|
now, False, body)
|
||||||
|
|
||||||
c = connection.cursor()
|
connection.execute("""
|
||||||
|
|
||||||
c.execute("""
|
|
||||||
INSERT INTO messages
|
INSERT INTO messages
|
||||||
VALUES (?,?,?,?,?,?)
|
VALUES (?,?,?,?,?,?)
|
||||||
""", schema_values("message", scheme))
|
""", schema_values("message", scheme))
|
||||||
|
|
||||||
c.execute("""
|
connection.execute("""
|
||||||
UPDATE threads SET
|
UPDATE threads SET
|
||||||
reply_count = ?,
|
reply_count = ?,
|
||||||
last_mod = ?
|
last_mod = ?
|
||||||
|
@ -154,7 +148,7 @@ def message_edit_query(connection, author, thread_id, post_id):
|
||||||
except IndexError:
|
except IndexError:
|
||||||
raise BBJParameterError("post_id out of bounds for requested thread")
|
raise BBJParameterError("post_id out of bounds for requested thread")
|
||||||
|
|
||||||
if not user["admin"]:
|
if not user["is_admin"]:
|
||||||
if not user["user_id"] == message["author"]:
|
if not user["user_id"] == message["author"]:
|
||||||
raise BBJUserError(
|
raise BBJUserError(
|
||||||
"non-admin attempt to edit another user's message")
|
"non-admin attempt to edit another user's message")
|
||||||
|
@ -172,15 +166,16 @@ def message_edit_commit(connection, author_id, thread_id, post_id, new_body):
|
||||||
message_edit_query first. Returns the newly updated message object.
|
message_edit_query first. Returns the newly updated message object.
|
||||||
"""
|
"""
|
||||||
validate([("body", new_body)])
|
validate([("body", new_body)])
|
||||||
message = message_edit_query(author_id, thread_id, post_id)
|
message = message_edit_query(connection, author_id, thread_id, post_id)
|
||||||
message["body"] = new_body
|
message["body"] = new_body
|
||||||
message["edited"] = True
|
message["edited"] = True
|
||||||
|
|
||||||
connection.cursor().excute("""
|
connection.execute("""
|
||||||
UPDATE messages SET
|
UPDATE messages SET
|
||||||
body = ? edited = ?
|
body = ?,
|
||||||
WHERE
|
edited = ?
|
||||||
thread_id = ? AND post_id = ?
|
WHERE thread_id = ?
|
||||||
|
AND post_id = ?
|
||||||
""", (new_body, True, thread_id, post_id))
|
""", (new_body, True, thread_id, post_id))
|
||||||
|
|
||||||
connection.commit()
|
connection.commit()
|
||||||
|
@ -208,7 +203,7 @@ def user_register(connection, user_name, auth_hash):
|
||||||
uuid1().hex, user_name, auth_hash,
|
uuid1().hex, user_name, auth_hash,
|
||||||
"", "", 0, False, time())
|
"", "", 0, False, time())
|
||||||
|
|
||||||
connection.cursor().execute("""
|
connection.execute("""
|
||||||
INSERT INTO users
|
INSERT INTO users
|
||||||
VALUES (?,?,?,?,?,?,?,?)
|
VALUES (?,?,?,?,?,?,?,?)
|
||||||
""", schema_values("user", scheme))
|
""", schema_values("user", scheme))
|
||||||
|
@ -226,14 +221,12 @@ def user_resolve(connection, name_or_id, externalize=False, return_false=True):
|
||||||
RETURN_FALSE determines whether to raise an exception or just
|
RETURN_FALSE determines whether to raise an exception or just
|
||||||
return bool False if the user doesn't exist
|
return bool False if the user doesn't exist
|
||||||
"""
|
"""
|
||||||
c = connection.cursor()
|
user = connection.execute("""
|
||||||
c.execute("""
|
|
||||||
SELECT * FROM users
|
SELECT * FROM users
|
||||||
WHERE user_name = ?
|
WHERE user_name = ?
|
||||||
OR user_id = ?
|
OR user_id = ? """,
|
||||||
""", (name_or_id, name_or_id))
|
(name_or_id, name_or_id)).fetchone()
|
||||||
|
|
||||||
user = c.fetchone()
|
|
||||||
if user:
|
if user:
|
||||||
user = schema.user_internal(*user)
|
user = schema.user_internal(*user)
|
||||||
if externalize:
|
if externalize:
|
||||||
|
@ -265,7 +258,7 @@ def user_update(connection, user_object, parameters):
|
||||||
"user_name", "quip", "auth_hash",
|
"user_name", "quip", "auth_hash",
|
||||||
"bio", "color", "user_id")
|
"bio", "color", "user_id")
|
||||||
|
|
||||||
connection.cursor().execute("""
|
connection.execute("""
|
||||||
UPDATE users SET
|
UPDATE users SET
|
||||||
user_name = ?, quip = ?,
|
user_name = ?, quip = ?,
|
||||||
auth_hash = ?, bio = ?,
|
auth_hash = ?, bio = ?,
|
||||||
|
|
Loading…
Reference in New Issue