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 sqlite3
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
|
||||||
dbname = "data.sqlite"
|
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:
|
with sqlite3.connect(dbname) as _c:
|
||||||
db.anon_object = db.user_resolve(_c, "anonymous")
|
db.anon = db.user_resolve(_c, "anonymous")
|
||||||
if not db.anon_object:
|
if not db.anon:
|
||||||
db.anon_object = db.user_register(_c, *db.anon_credentials)
|
db.anon = db.user_register(
|
||||||
|
_c, "anonymous", # this is the hash for "anon"
|
||||||
|
"5430eeed859cad61d925097ec4f53246"
|
||||||
|
"1ccf1ab6b9802b09a313be1478a4d614")
|
||||||
|
|
||||||
|
|
||||||
# creates a database connection for each thread
|
# creates a database connection for each thread
|
||||||
|
@ -41,32 +46,27 @@ def api_method(function):
|
||||||
try:
|
try:
|
||||||
# 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 empty? not all methods require an input
|
# is it just empty bytes? not all methods require an input
|
||||||
if body:
|
if body:
|
||||||
# if this fucks up, we throw code 0 instead of code 1
|
|
||||||
body = json.loads(body)
|
body = json.loads(body)
|
||||||
if isinstance(body, dict):
|
if isinstance(body, dict):
|
||||||
# lowercase all of its keys
|
# lowercase all of its keys
|
||||||
body = {str(key).lower(): value for key, value
|
body = {str(key).lower(): value for key, value
|
||||||
in body.items()}
|
in body.items()}
|
||||||
cherrypy.request.json = body
|
else: # would rather a NoneType than b""
|
||||||
|
body = None
|
||||||
else:
|
|
||||||
cherrypy.request.json = None
|
|
||||||
|
|
||||||
username = cherrypy.request.headers.get("User")
|
username = cherrypy.request.headers.get("User")
|
||||||
auth = cherrypy.request.headers.get("Auth")
|
auth = cherrypy.request.headers.get("Auth")
|
||||||
anon = False
|
|
||||||
|
|
||||||
if not username and not auth:
|
if (username and not auth) or (auth and not username):
|
||||||
user = db.anon_object
|
|
||||||
anon = True
|
|
||||||
|
|
||||||
elif not username or not auth:
|
|
||||||
return json.dumps(schema.error(5,
|
return json.dumps(schema.error(5,
|
||||||
"User or Auth was given without the other."))
|
"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)
|
user = db.user_resolve(cherrypy.thread_data.db, username)
|
||||||
if not user:
|
if not user:
|
||||||
raise BBJUserError("User %s is not registered" % username)
|
raise BBJUserError("User %s is not registered" % username)
|
||||||
|
@ -75,24 +75,28 @@ def api_method(function):
|
||||||
return json.dumps(schema.error(5,
|
return json.dumps(schema.error(5,
|
||||||
"Invalid authorization key for user."))
|
"Invalid authorization key for user."))
|
||||||
|
|
||||||
cherrypy.thread_data.user = user
|
# api_methods may choose to bind a usermap into the thread_data
|
||||||
cherrypy.thread_data.anon = anon
|
# which will send it off with the response
|
||||||
response = function(*args, **kwargs)
|
cherrypy.thread_data.usermap = {}
|
||||||
|
# TODO: Why in kek's name is self needing to be supplied a value positionally?
|
||||||
except json.JSONDecodeError as e:
|
value = function(None, body, cherrypy.thread_data.db, user)
|
||||||
response = schema.error(0, str(e))
|
response = schema.response(value, cherrypy.thread_data.usermap)
|
||||||
|
|
||||||
except BBJException as e:
|
except BBJException as e:
|
||||||
response = e.schema
|
response = e.schema
|
||||||
|
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
response = schema.error(0, str(e))
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error_id = uuid1().hex
|
error_id = uuid1().hex
|
||||||
response = schema.error(1,
|
response = schema.error(1,
|
||||||
"Internal server error: code {}. Tell ~desvox (the code too)"
|
"Internal server error: code {}. {}"
|
||||||
.format(error_id))
|
.format(error_id, repr(e)))
|
||||||
with open("logs/exceptions/" + error_id, "a") as log:
|
with open("logs/exceptions/" + error_id, "a") as log:
|
||||||
traceback.print_tb(e.__traceback__, file=log)
|
traceback.print_tb(e.__traceback__, file=log)
|
||||||
log.write(repr(e))
|
log.write(repr(e))
|
||||||
|
print("logged code 1 exception " + error_id)
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
return json.dumps(response)
|
return json.dumps(response)
|
||||||
|
@ -117,9 +121,7 @@ def create_usermap(connection, obj):
|
||||||
user_id,
|
user_id,
|
||||||
externalize=True,
|
externalize=True,
|
||||||
return_false=False)
|
return_false=False)
|
||||||
for user_id in {
|
for user_id in {item["author"] for item in obj}
|
||||||
item["author"] for item in obj
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -154,111 +156,148 @@ APICONFIG = {
|
||||||
class API(object):
|
class API(object):
|
||||||
@api_method
|
@api_method
|
||||||
@cherrypy.expose
|
@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,
|
Requires no arguments. Returns your internal user object,
|
||||||
including your authorization hash.
|
including your authorization hash.
|
||||||
"""
|
"""
|
||||||
return schema.response(cherrypy.thread_data.user)
|
return user
|
||||||
|
|
||||||
|
|
||||||
@api_method
|
@api_method
|
||||||
@cherrypy.expose
|
@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`.
|
Retreive an external user object for the given `user`.
|
||||||
Can be a user_id or user_name.
|
Can be a user_id or user_name.
|
||||||
"""
|
"""
|
||||||
args = cherrypy.request.json
|
|
||||||
validate(args, ["user"])
|
validate(args, ["user"])
|
||||||
return schema.response(db.user_resolve(
|
return db.user_resolve(
|
||||||
cherrypy.thread_data.db,
|
database, args["user"], return_false=False, externalize=True)
|
||||||
args["user"],
|
|
||||||
return_false=False,
|
|
||||||
externalize=True))
|
|
||||||
|
|
||||||
|
|
||||||
@api_method
|
@api_method
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
def thread_index(self, *args, **kwargs):
|
def thread_index(self, args, database, user, **kwargs):
|
||||||
threads = db.thread_index(cherrypy.thread_data.db)
|
"""
|
||||||
usermap = create_usermap(cherrypy.thread_data.db, threads)
|
Return an array with all the threads, ordered by most recent activity.
|
||||||
return schema.response(threads, usermap)
|
Requires no arguments.
|
||||||
|
"""
|
||||||
|
threads = db.thread_index(database)
|
||||||
|
cherrypy.thread_data.usermap = create_usermap(database, threads)
|
||||||
|
return threads
|
||||||
|
|
||||||
|
|
||||||
@api_method
|
@api_method
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
def thread_create(self, *args, **kwargs):
|
def thread_create(self, args, database, user, **kwargs):
|
||||||
args = cherrypy.request.json
|
"""
|
||||||
|
Creates a new thread and returns it. Requires the non-empty
|
||||||
|
string arguments `body` and `title`
|
||||||
|
"""
|
||||||
validate(args, ["body", "title"])
|
validate(args, ["body", "title"])
|
||||||
|
|
||||||
thread = db.thread_create(
|
thread = db.thread_create(
|
||||||
cherrypy.thread_data.db,
|
database, user["user_id"], args["body"], args["title"])
|
||||||
cherrypy.thread_data.user["user_id"],
|
cherrypy.thread_data.usermap = {user["user_id"]: user}
|
||||||
args["body"], args["title"])
|
return thread
|
||||||
|
|
||||||
usermap = {
|
|
||||||
cherrypy.thread_data.user["user_id"]:
|
|
||||||
cherrypy.thread_data.user
|
|
||||||
}
|
|
||||||
|
|
||||||
return schema.response(thread, usermap)
|
|
||||||
|
|
||||||
|
|
||||||
@api_method
|
@api_method
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
def thread_reply(self, *args, **kwargs):
|
def thread_reply(self, args, database, user, **kwargs):
|
||||||
args = cherrypy.request.json
|
"""
|
||||||
|
Creates a new reply for the given thread and returns it.
|
||||||
|
Requires the string arguments `thread_id` and `body`
|
||||||
|
"""
|
||||||
validate(args, ["thread_id", "body"])
|
validate(args, ["thread_id", "body"])
|
||||||
return schema.response(db.thread_reply(
|
return db.thread_reply(
|
||||||
cherrypy.thread_data.db,
|
database, user["user_id"], args["thread_id"], args["body"])
|
||||||
cherrypy.thread_data.user["user_id"],
|
|
||||||
args["thread_id"], args["body"]))
|
|
||||||
|
|
||||||
|
|
||||||
@api_method
|
@api_method
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
def thread_load(self, *args, **kwargs):
|
def thread_load(self, args, database, user, **kwargs):
|
||||||
args = cherrypy.request.json
|
"""
|
||||||
|
Returns the thread object with all of its messages loaded.
|
||||||
|
Requires the argument `thread_id`
|
||||||
|
"""
|
||||||
validate(args, ["thread_id"])
|
validate(args, ["thread_id"])
|
||||||
|
thread = db.thread_get(database, args["thread_id"])
|
||||||
thread = db.thread_get(
|
cherrypy.thread_data.usermap = \
|
||||||
cherrypy.thread_data.db,
|
create_usermap(database, thread["messages"])
|
||||||
args["thread_id"])
|
return thread
|
||||||
|
|
||||||
usermap = create_usermap(
|
|
||||||
cherrypy.thread_data.db,
|
|
||||||
thread["messages"])
|
|
||||||
|
|
||||||
return schema.response(thread, usermap)
|
|
||||||
|
|
||||||
|
|
||||||
@api_method
|
@api_method
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
def user_register(self, *args, **kwargs):
|
def edit_post(self, args, database, user, **kwargs):
|
||||||
args = cherrypy.request.json
|
"""
|
||||||
validate(args, ["user_name", "auth_hash"])
|
Replace a post with a new body. Requires the arguments
|
||||||
return schema.response(db.user_register(
|
`thread_id`, `post_id`, and `body`. This method verifies
|
||||||
cherrypy.thread_data.db,
|
that the user can edit a post before commiting the change,
|
||||||
args["user_name"],
|
otherwise an error object is returned whose description
|
||||||
args["auth_hash"]))
|
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
|
@api_method
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
def edit_query(self, *args, **kwargs):
|
def edit_query(self, args, database, user, **kwargs):
|
||||||
args = cherrypy.request.json
|
"""
|
||||||
|
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"])
|
validate(args, ["thread_id", "post_id"])
|
||||||
return schema.response(message_edit_query(
|
return message_edit_query(
|
||||||
cherrypy.thread_data.db,
|
database, user["user_id"], args["thread_id"], args["post_id"])
|
||||||
cherrypy.thread_data.user["user_id"],
|
|
||||||
args["thread_id"],
|
|
||||||
args["post_id"]))
|
|
||||||
|
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
def test(self, *args, **kwargs):
|
def test(self, **kwargs):
|
||||||
print(cherrypy.request.body.read())
|
print(cherrypy.request.body.read())
|
||||||
return "{\"wow\": \"good job!\"}"
|
return "{\"wow\": \"jolly good show!\"}"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -267,4 +306,4 @@ def run():
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
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 json
|
||||||
import os
|
import os
|
||||||
|
|
||||||
anon_object = None
|
anon = None
|
||||||
anon_credentials = \
|
|
||||||
("anonymous",
|
|
||||||
"5430eeed859cad61d925097ec4f53246"
|
|
||||||
"1ccf1ab6b9802b09a313be1478a4d614")
|
|
||||||
# this is the hash for "anon"
|
|
||||||
|
|
||||||
# if os.path.exists("cache"):
|
|
||||||
# os.rmdir("cache")
|
|
||||||
# os.mkdir("cache")
|
|
||||||
|
|
||||||
### THREADS ###
|
### THREADS ###
|
||||||
|
|
||||||
|
@ -267,11 +258,6 @@ def user_externalize(user_object):
|
||||||
return 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 ###
|
### SANITY CHECKS ###
|
||||||
|
|
||||||
def contains_nonspaces(string):
|
def contains_nonspaces(string):
|
||||||
|
@ -358,145 +344,3 @@ def validate(keys_and_values):
|
||||||
"Color specification out of range (int 0-8)")
|
"Color specification out of range (int 0-8)")
|
||||||
|
|
||||||
return True
|
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