things shalt be broken and thou shalt fix them later
parent
474f13842b
commit
73a9063f93
217
server.py
217
server.py
|
@ -7,12 +7,17 @@ import cherrypy
|
|||
import sqlite3
|
||||
import json
|
||||
|
||||
|
||||
dbname = "data.sqlite"
|
||||
|
||||
# user anonymity is achieved in the laziest possible way: a literal user
|
||||
# named anonymous. may god have mercy on my soul.
|
||||
with sqlite3.connect(dbname) as _c:
|
||||
db.anon_object = db.user_resolve(_c, "anonymous")
|
||||
if not db.anon_object:
|
||||
db.anon_object = db.user_register(_c, *db.anon_credentials)
|
||||
db.anon = db.user_resolve(_c, "anonymous")
|
||||
if not db.anon:
|
||||
db.anon = db.user_register(
|
||||
_c, "anonymous", # this is the hash for "anon"
|
||||
"5430eeed859cad61d925097ec4f53246"
|
||||
"1ccf1ab6b9802b09a313be1478a4d614")
|
||||
|
||||
|
||||
# creates a database connection for each thread
|
||||
|
@ -41,32 +46,27 @@ def api_method(function):
|
|||
try:
|
||||
# read in the body from the request to a string...
|
||||
body = str(cherrypy.request.body.read(), "utf8")
|
||||
# is it empty? not all methods require an input
|
||||
# is it just empty bytes? not all methods require an input
|
||||
if body:
|
||||
# if this fucks up, we throw code 0 instead of code 1
|
||||
body = json.loads(body)
|
||||
if isinstance(body, dict):
|
||||
# lowercase all of its keys
|
||||
body = {str(key).lower(): value for key, value
|
||||
in body.items()}
|
||||
cherrypy.request.json = body
|
||||
|
||||
else:
|
||||
cherrypy.request.json = None
|
||||
else: # would rather a NoneType than b""
|
||||
body = None
|
||||
|
||||
username = cherrypy.request.headers.get("User")
|
||||
auth = cherrypy.request.headers.get("Auth")
|
||||
anon = False
|
||||
|
||||
if not username and not auth:
|
||||
user = db.anon_object
|
||||
anon = True
|
||||
|
||||
elif not username or not auth:
|
||||
if (username and not auth) or (auth and not username):
|
||||
return json.dumps(schema.error(5,
|
||||
"User or Auth was given without the other."))
|
||||
|
||||
if not anon:
|
||||
elif not username and not auth:
|
||||
user = db.anon
|
||||
|
||||
else:
|
||||
user = db.user_resolve(cherrypy.thread_data.db, username)
|
||||
if not user:
|
||||
raise BBJUserError("User %s is not registered" % username)
|
||||
|
@ -75,24 +75,28 @@ def api_method(function):
|
|||
return json.dumps(schema.error(5,
|
||||
"Invalid authorization key for user."))
|
||||
|
||||
cherrypy.thread_data.user = user
|
||||
cherrypy.thread_data.anon = anon
|
||||
response = function(*args, **kwargs)
|
||||
|
||||
except json.JSONDecodeError as e:
|
||||
response = schema.error(0, str(e))
|
||||
# api_methods may choose to bind a usermap into the thread_data
|
||||
# which will send it off with the response
|
||||
cherrypy.thread_data.usermap = {}
|
||||
# TODO: Why in kek's name is self needing to be supplied a value positionally?
|
||||
value = function(None, body, cherrypy.thread_data.db, user)
|
||||
response = schema.response(value, cherrypy.thread_data.usermap)
|
||||
|
||||
except BBJException as e:
|
||||
response = e.schema
|
||||
|
||||
except json.JSONDecodeError as e:
|
||||
response = schema.error(0, str(e))
|
||||
|
||||
except Exception as e:
|
||||
error_id = uuid1().hex
|
||||
response = schema.error(1,
|
||||
"Internal server error: code {}. Tell ~desvox (the code too)"
|
||||
.format(error_id))
|
||||
"Internal server error: code {}. {}"
|
||||
.format(error_id, repr(e)))
|
||||
with open("logs/exceptions/" + error_id, "a") as log:
|
||||
traceback.print_tb(e.__traceback__, file=log)
|
||||
log.write(repr(e))
|
||||
print("logged code 1 exception " + error_id)
|
||||
|
||||
finally:
|
||||
return json.dumps(response)
|
||||
|
@ -117,9 +121,7 @@ def create_usermap(connection, obj):
|
|||
user_id,
|
||||
externalize=True,
|
||||
return_false=False)
|
||||
for user_id in {
|
||||
item["author"] for item in obj
|
||||
}
|
||||
for user_id in {item["author"] for item in obj}
|
||||
}
|
||||
|
||||
|
||||
|
@ -154,111 +156,148 @@ APICONFIG = {
|
|||
class API(object):
|
||||
@api_method
|
||||
@cherrypy.expose
|
||||
def get_me(self, *args, **kwargs):
|
||||
def user_register(self, args, database, user, **kwargs):
|
||||
"""
|
||||
Register a new user into the system and return the new object.
|
||||
Requires the string arguments `user_name` and `auth_hash`
|
||||
"""
|
||||
validate(args, ["user_name", "auth_hash"])
|
||||
return db.user_register(
|
||||
database, args["user_name"], args["auth_hash"])
|
||||
|
||||
|
||||
@api_method
|
||||
@cherrypy.expose
|
||||
def user_update(self, args, database, user, **kwargs):
|
||||
"""
|
||||
Receives new parameters and assigns them to the user_object
|
||||
in the database. The following new parameters can be supplied:
|
||||
`user_name`, `auth_hash`, `quip`, `bio`, and `color`. Any number
|
||||
of them may be supplied.
|
||||
|
||||
The newly updated user object is returned on success.
|
||||
"""
|
||||
validate(args, []) # just make sure its not empty
|
||||
return db.user_update(database, user, args)
|
||||
|
||||
|
||||
@api_method
|
||||
@cherrypy.expose
|
||||
def get_me(self, args, database, user, **kwargs):
|
||||
"""
|
||||
Requires no arguments. Returns your internal user object,
|
||||
including your authorization hash.
|
||||
"""
|
||||
return schema.response(cherrypy.thread_data.user)
|
||||
return user
|
||||
|
||||
|
||||
@api_method
|
||||
@cherrypy.expose
|
||||
def user_get(self, *args, **kwargs):
|
||||
def user_get(self, args, database, user, **kwargs):
|
||||
"""
|
||||
Retreive an external user object for the given `user`.
|
||||
Can be a user_id or user_name.
|
||||
"""
|
||||
args = cherrypy.request.json
|
||||
validate(args, ["user"])
|
||||
return schema.response(db.user_resolve(
|
||||
cherrypy.thread_data.db,
|
||||
args["user"],
|
||||
return_false=False,
|
||||
externalize=True))
|
||||
return db.user_resolve(
|
||||
database, args["user"], return_false=False, externalize=True)
|
||||
|
||||
|
||||
@api_method
|
||||
@cherrypy.expose
|
||||
def thread_index(self, *args, **kwargs):
|
||||
threads = db.thread_index(cherrypy.thread_data.db)
|
||||
usermap = create_usermap(cherrypy.thread_data.db, threads)
|
||||
return schema.response(threads, usermap)
|
||||
def thread_index(self, args, database, user, **kwargs):
|
||||
"""
|
||||
Return an array with all the threads, ordered by most recent activity.
|
||||
Requires no arguments.
|
||||
"""
|
||||
threads = db.thread_index(database)
|
||||
cherrypy.thread_data.usermap = create_usermap(database, threads)
|
||||
return threads
|
||||
|
||||
|
||||
@api_method
|
||||
@cherrypy.expose
|
||||
def thread_create(self, *args, **kwargs):
|
||||
args = cherrypy.request.json
|
||||
def thread_create(self, args, database, user, **kwargs):
|
||||
"""
|
||||
Creates a new thread and returns it. Requires the non-empty
|
||||
string arguments `body` and `title`
|
||||
"""
|
||||
validate(args, ["body", "title"])
|
||||
|
||||
thread = db.thread_create(
|
||||
cherrypy.thread_data.db,
|
||||
cherrypy.thread_data.user["user_id"],
|
||||
args["body"], args["title"])
|
||||
|
||||
usermap = {
|
||||
cherrypy.thread_data.user["user_id"]:
|
||||
cherrypy.thread_data.user
|
||||
}
|
||||
|
||||
return schema.response(thread, usermap)
|
||||
database, user["user_id"], args["body"], args["title"])
|
||||
cherrypy.thread_data.usermap = {user["user_id"]: user}
|
||||
return thread
|
||||
|
||||
|
||||
@api_method
|
||||
@cherrypy.expose
|
||||
def thread_reply(self, *args, **kwargs):
|
||||
args = cherrypy.request.json
|
||||
def thread_reply(self, args, database, user, **kwargs):
|
||||
"""
|
||||
Creates a new reply for the given thread and returns it.
|
||||
Requires the string arguments `thread_id` and `body`
|
||||
"""
|
||||
validate(args, ["thread_id", "body"])
|
||||
return schema.response(db.thread_reply(
|
||||
cherrypy.thread_data.db,
|
||||
cherrypy.thread_data.user["user_id"],
|
||||
args["thread_id"], args["body"]))
|
||||
return db.thread_reply(
|
||||
database, user["user_id"], args["thread_id"], args["body"])
|
||||
|
||||
|
||||
@api_method
|
||||
@cherrypy.expose
|
||||
def thread_load(self, *args, **kwargs):
|
||||
args = cherrypy.request.json
|
||||
def thread_load(self, args, database, user, **kwargs):
|
||||
"""
|
||||
Returns the thread object with all of its messages loaded.
|
||||
Requires the argument `thread_id`
|
||||
"""
|
||||
validate(args, ["thread_id"])
|
||||
|
||||
thread = db.thread_get(
|
||||
cherrypy.thread_data.db,
|
||||
args["thread_id"])
|
||||
|
||||
usermap = create_usermap(
|
||||
cherrypy.thread_data.db,
|
||||
thread["messages"])
|
||||
|
||||
return schema.response(thread, usermap)
|
||||
thread = db.thread_get(database, args["thread_id"])
|
||||
cherrypy.thread_data.usermap = \
|
||||
create_usermap(database, thread["messages"])
|
||||
return thread
|
||||
|
||||
|
||||
@api_method
|
||||
@cherrypy.expose
|
||||
def user_register(self, *args, **kwargs):
|
||||
args = cherrypy.request.json
|
||||
validate(args, ["user_name", "auth_hash"])
|
||||
return schema.response(db.user_register(
|
||||
cherrypy.thread_data.db,
|
||||
args["user_name"],
|
||||
args["auth_hash"]))
|
||||
def edit_post(self, args, database, user, **kwargs):
|
||||
"""
|
||||
Replace a post with a new body. Requires the arguments
|
||||
`thread_id`, `post_id`, and `body`. This method verifies
|
||||
that the user can edit a post before commiting the change,
|
||||
otherwise an error object is returned whose description
|
||||
should be shown to the user.
|
||||
|
||||
To perform sanity checks without actually attempting to
|
||||
edit a post, use `edit_query`
|
||||
|
||||
Returns the new message object.
|
||||
"""
|
||||
if user == db.anon:
|
||||
raise BBJUserError("Anons cannot edit messages.")
|
||||
validate(args, ["body", "thread_id", "post_id"])
|
||||
return message_edit_commit(
|
||||
database, user["user_id"], args["thread_id"], args["post_id"], args["body"])
|
||||
|
||||
|
||||
@api_method
|
||||
@cherrypy.expose
|
||||
def edit_query(self, *args, **kwargs):
|
||||
args = cherrypy.request.json
|
||||
def edit_query(self, args, database, user, **kwargs):
|
||||
"""
|
||||
Queries the database to ensure the user can edit a given
|
||||
message. Requires the arguments `thread_id` and `post_id`
|
||||
(does not require a new body)
|
||||
|
||||
Returns either boolean true or the current message object
|
||||
"""
|
||||
if user == db.anon:
|
||||
raise BBJUserError("Anons cannot edit messages.")
|
||||
validate(args, ["thread_id", "post_id"])
|
||||
return schema.response(message_edit_query(
|
||||
cherrypy.thread_data.db,
|
||||
cherrypy.thread_data.user["user_id"],
|
||||
args["thread_id"],
|
||||
args["post_id"]))
|
||||
return message_edit_query(
|
||||
database, user["user_id"], args["thread_id"], args["post_id"])
|
||||
|
||||
|
||||
@cherrypy.expose
|
||||
def test(self, *args, **kwargs):
|
||||
def test(self, **kwargs):
|
||||
print(cherrypy.request.body.read())
|
||||
return "{\"wow\": \"good job!\"}"
|
||||
return "{\"wow\": \"jolly good show!\"}"
|
||||
|
||||
|
||||
|
||||
|
@ -267,4 +306,4 @@ def run():
|
|||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("wew")
|
||||
print("yo lets do that -i shit mang")
|
||||
|
|
158
src/db.py
158
src/db.py
|
@ -25,16 +25,7 @@ import pickle
|
|||
import json
|
||||
import os
|
||||
|
||||
anon_object = None
|
||||
anon_credentials = \
|
||||
("anonymous",
|
||||
"5430eeed859cad61d925097ec4f53246"
|
||||
"1ccf1ab6b9802b09a313be1478a4d614")
|
||||
# this is the hash for "anon"
|
||||
|
||||
# if os.path.exists("cache"):
|
||||
# os.rmdir("cache")
|
||||
# os.mkdir("cache")
|
||||
anon = None
|
||||
|
||||
### THREADS ###
|
||||
|
||||
|
@ -267,11 +258,6 @@ def user_externalize(user_object):
|
|||
return user_object
|
||||
|
||||
|
||||
def user_auth(auth_hash, user_object):
|
||||
# nominating this for most useless function in the program
|
||||
return auth_hash == user_object["auth_hash"]
|
||||
|
||||
|
||||
### SANITY CHECKS ###
|
||||
|
||||
def contains_nonspaces(string):
|
||||
|
@ -358,145 +344,3 @@ def validate(keys_and_values):
|
|||
"Color specification out of range (int 0-8)")
|
||||
|
||||
return True
|
||||
|
||||
|
||||
### OLD SHIT ###
|
||||
|
||||
# def thread_index(key="lastmod", markup=True):
|
||||
# result = list()
|
||||
# for ID in path.os.listdir(path.join(PATH, "threads")):
|
||||
# thread = thread_load(ID, markup)
|
||||
# thread.pop("replies")
|
||||
# result.append(thread)
|
||||
# return sorted(result, key=lambda i: i[key], reverse=True)
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
# def thread_load(ID, markup=True):
|
||||
# try:
|
||||
# with open(path.join(PATH, "threads", ID), "r") as f:
|
||||
# return json.loads(f.read())
|
||||
# except FileNotFoundError:
|
||||
# return False
|
||||
#
|
||||
#
|
||||
# def thread_dump(ID, obj):
|
||||
# with open(path.join(PATH, "threads", ID), "w") as f:
|
||||
# f.write(json.dumps(obj))
|
||||
#
|
||||
#
|
||||
# def thread_reply(ID, author, body):
|
||||
# thread = thread_load(ID)
|
||||
# if not thread:
|
||||
# return schema.error(7, "Requested thread does not exist.")
|
||||
#
|
||||
# thread["reply_count"] += 1
|
||||
# thread["lastmod"] = time()
|
||||
#
|
||||
# if thread["replies"]:
|
||||
# lastpost = thread["replies"][-1]["post_id"]
|
||||
# else:
|
||||
# lastpost = 1
|
||||
#
|
||||
# reply = schema.reply(lastpost + 1, author, body)
|
||||
# thread["replies"].append(reply)
|
||||
# thread_dump(ID, thread)
|
||||
# return reply
|
||||
#
|
||||
#
|
||||
# def index_reply(reply_list, post_id):
|
||||
# for index, reply in enumerate(reply_list):
|
||||
# if reply["post_id"] == post_id:
|
||||
# return index
|
||||
# else:
|
||||
# raise IndexError
|
||||
#
|
||||
#
|
||||
# def edit_handler(json, thread=None):
|
||||
# try:
|
||||
# target_id = json["post_id"]
|
||||
# if not thread:
|
||||
# thread = thread_load(json["thread_id"])
|
||||
# if not thread:
|
||||
# return False, schema.error(7, "Requested thread does not exist.")
|
||||
#
|
||||
#
|
||||
# if target_id == 1:
|
||||
# target = thread
|
||||
# else:
|
||||
# target = thread["replies"][
|
||||
# index_reply(thread["replies"], target_id)]
|
||||
#
|
||||
# if not user_is_admin(json["user"]):
|
||||
# if json["user"] != target["author"]:
|
||||
# return False, schema.error(10,
|
||||
# "non-admin attempt to edit another user's message")
|
||||
#
|
||||
# elif (time() - target["created"]) > 86400:
|
||||
# return False, schema.error(9,
|
||||
# "message is too old to edit (24hr limit)")
|
||||
#
|
||||
# return True, target
|
||||
#
|
||||
# except IndexError:
|
||||
# return False, schema.error(3, "post_id out of bounds for requested thread")
|
||||
#
|
||||
#
|
||||
# ### USER MANAGEMENT ###
|
||||
#
|
||||
# def user_dbdump(dictionary):
|
||||
# with open(path.join(PATH, "userdb"), "w") as f:
|
||||
# f.write(json.dumps(dictionary))
|
||||
#
|
||||
#
|
||||
# def user_resolve(name_or_id):
|
||||
# check = USERDB.get(name_or_id)
|
||||
# try:
|
||||
# if check:
|
||||
# return name_or_id
|
||||
# else:
|
||||
# return USERDB["namemap"][name_or_id]
|
||||
# except KeyError:
|
||||
# return False
|
||||
#
|
||||
#
|
||||
# def user_register(auth_hash, name, quip, bio):
|
||||
# if USERDB["namemap"].get(name):
|
||||
# return schema.error(4, "Username taken.")
|
||||
#
|
||||
# for ok, error in [
|
||||
# user_namecheck(name),
|
||||
# user_authcheck(auth_hash),
|
||||
# user_quipcheck(quip),
|
||||
# user_biocheck(bio)]:
|
||||
#
|
||||
# if not ok:
|
||||
# return error
|
||||
#
|
||||
# ID = uuid1().hex
|
||||
# scheme = schema.user_internal(ID, auth_hash, name, quip, bio, False)
|
||||
# USERDB.update({ID: scheme})
|
||||
# USERDB["namemap"].update({name: ID})
|
||||
# user_dbdump(USERDB)
|
||||
# return scheme
|
||||
#
|
||||
#
|
||||
# def user_get(ID):
|
||||
# user = USERDB[ID]
|
||||
# return schema.user_external(
|
||||
# ID, user["name"], user["quip"],
|
||||
# user["bio"], user["admin"])
|
||||
#
|
||||
#
|
||||
# def user_auth(ID, auth_hash):
|
||||
# return auth_hash == USERDB[ID]["auth_hash"]
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
# def user_update(ID, **params):
|
||||
# USERDB[ID].update(params)
|
||||
# return USERDB[ID]
|
||||
#
|
||||
#
|
||||
|
|
Loading…
Reference in New Issue