mirror of
https://github.com/krateng/maloja.git
synced 2023-08-10 21:12:55 +03:00
Integrated API server into main server
This commit is contained in:
parent
3ba7e4cfef
commit
e9cde843e1
@ -14,6 +14,12 @@ def md5(input):
|
||||
m.update(bytes(input,encoding="utf-8"))
|
||||
return m.hexdigest()
|
||||
|
||||
def generate_key(ls):
|
||||
key = ""
|
||||
for i in range(64):
|
||||
key += str(random.choice(list(range(10)) + list("abcdefghijklmnopqrstuvwxyz") + list("ABCDEFGHIJKLMNOPQRSTUVWXYZ")))
|
||||
ls.append(key)
|
||||
return key
|
||||
|
||||
#def check_sig(keys):
|
||||
# try:
|
||||
@ -26,15 +32,28 @@ def md5(input):
|
||||
|
||||
|
||||
|
||||
def handle(path,keys):
|
||||
# log("API REQUEST")
|
||||
# log(str(path))
|
||||
# for k in keys:
|
||||
# log(str(k) + ": " + str(keys.getall(k)))
|
||||
def handle(path,keys,headers,auth):
|
||||
print("API request: " + str(path))
|
||||
print("Keys:")
|
||||
for k in keys:
|
||||
print("\t" + str(k) + ": " + str(keys.get(k)))
|
||||
print("Headers:")
|
||||
for h in headers:
|
||||
print("\t" + str(h) + ": " + str(headers.get(h)))
|
||||
print("Auth: " + str(auth))
|
||||
|
||||
if path[0] == "audioscrobbler":
|
||||
return handle_audioscrobbler(path[1:],keys)
|
||||
try:
|
||||
if path[0] in ["audioscrobbler","gnukebox","gnufm"]:
|
||||
response = handle_audioscrobbler(path[1:],keys)
|
||||
elif path[0] in ["listenbrainz","lbrnz"]:
|
||||
response = handle_listenbrainz(path[1:],keys,headers)
|
||||
else:
|
||||
response = {"error_message":"Invalid scrobble protocol"}
|
||||
except:
|
||||
response = {"error_message":"Unknown API error"}
|
||||
|
||||
print("Response: " + str(response))
|
||||
return response
|
||||
|
||||
# no need to save these on disk, clients can always request a new session
|
||||
mobile_sessions = []
|
||||
@ -46,13 +65,18 @@ def handle_audioscrobbler(path,keys):
|
||||
if keys.get("method") == "auth.getMobileSession":
|
||||
token = keys.get("authToken")
|
||||
user = keys.get("username")
|
||||
for key in database.allAPIkeys():
|
||||
if md5(user + md5(key)) == token:
|
||||
sessionkey = ""
|
||||
for i in range(64):
|
||||
sessionkey += str(random.choice(list(range(10)) + list("abcdefghijklmnopqrstuvwxyz") + list("ABCDEFGHIJKLMNOPQRSTUVWXYZ")))
|
||||
mobile_sessions.append(sessionkey)
|
||||
password = keys.get("password")
|
||||
# either username and password
|
||||
if user is not None and password is not None:
|
||||
if password in database.allAPIkeys():
|
||||
sessionkey = generate_key(mobile_sessions)
|
||||
return {"session":{"key":sessionkey}}
|
||||
# or username and token (deprecated by lastfm)
|
||||
elif user is not None and token is not None:
|
||||
for key in database.allAPIkeys():
|
||||
if md5(user + md5(key)) == token:
|
||||
sessionkey = generate_key(mobile_sessions)
|
||||
return {"session":{"key":sessionkey}}
|
||||
return {"error":4}
|
||||
|
||||
|
||||
@ -77,3 +101,20 @@ def handle_audioscrobbler(path,keys):
|
||||
return {"scrobbles":{"@attr":{"ignored":0}}}
|
||||
|
||||
return {"error":3}
|
||||
|
||||
else:
|
||||
return {"error_message":"API version not supported"}
|
||||
|
||||
|
||||
def handle_listenbrainz(path,keys,headers):
|
||||
|
||||
if path[0] == "1":
|
||||
|
||||
if path[1] == "submit-listens":
|
||||
|
||||
if headers.get("Authorization") is not None:
|
||||
print(headers.get("Authorization"))
|
||||
return {"wat":"wut"}
|
||||
|
||||
else:
|
||||
return {"error_message":"API version not supported"}
|
||||
|
68
database.py
68
database.py
@ -1,6 +1,5 @@
|
||||
# server
|
||||
from bottle import Bottle, route, get, post, run, template, static_file, request, response, FormsDict
|
||||
import waitress
|
||||
from bottle import request, response, FormsDict
|
||||
# rest of the project
|
||||
from cleanup import *
|
||||
from utilities import *
|
||||
@ -29,8 +28,6 @@ import urllib
|
||||
|
||||
|
||||
|
||||
dbserver = Bottle()
|
||||
|
||||
dblock = Lock() #global database lock
|
||||
|
||||
SCROBBLES = [] # Format: tuple(track_ref,timestamp,saved)
|
||||
@ -183,6 +180,47 @@ def getTrackID(artists,title):
|
||||
########
|
||||
|
||||
|
||||
# silly patch to get old syntax working without dbserver
|
||||
|
||||
# function to register all the functions to the real server
|
||||
def register_subroutes(server,path):
|
||||
for subpath in dbserver.handlers_get:
|
||||
func = dbserver.handlers_get[subpath]
|
||||
decorator = server.get(path + subpath)
|
||||
decorator(func)
|
||||
for subpath in dbserver.handlers_post:
|
||||
func = dbserver.handlers_post[subpath]
|
||||
decorator = server.post(path + subpath)
|
||||
decorator(func)
|
||||
|
||||
|
||||
# fake server
|
||||
class FakeBottle:
|
||||
def __init__(self):
|
||||
self.handlers_get = {}
|
||||
self.handlers_post = {}
|
||||
|
||||
# these functions pretend that they're the bottle decorators, but only write
|
||||
# down which functions asked for them so they can later report their names
|
||||
# to the real bottle server
|
||||
def get(self,path):
|
||||
def register(func):
|
||||
self.handlers_get[path] = func
|
||||
return func
|
||||
return register
|
||||
def post(self,path):
|
||||
def register(func):
|
||||
self.handlers_post[path] = func
|
||||
return func
|
||||
return register
|
||||
|
||||
def route(self,path):
|
||||
return self.get(path)
|
||||
|
||||
|
||||
dbserver = FakeBottle()
|
||||
|
||||
|
||||
|
||||
|
||||
@dbserver.route("/test")
|
||||
@ -618,15 +656,14 @@ def post_scrobble():
|
||||
# standard-compliant scrobbling methods
|
||||
|
||||
@dbserver.post("/s/<path:path>")
|
||||
def sapi(path):
|
||||
path = path.split("/")
|
||||
keys = FormsDict.decode(request.forms)
|
||||
return compliant_api.handle(path,keys)
|
||||
@dbserver.get("/s/<path:path>")
|
||||
def sapi(path):
|
||||
path = path.split("/")
|
||||
keys = FormsDict.decode(request.query)
|
||||
return compliant_api.handle(path,keys)
|
||||
path = list(filter(None,path))
|
||||
keys = FormsDict.decode(request.params)
|
||||
headers = request.headers
|
||||
auth = request.auth
|
||||
return compliant_api.handle(path,keys,headers,auth)
|
||||
|
||||
|
||||
|
||||
@ -806,17 +843,14 @@ def search():
|
||||
|
||||
|
||||
# Starts the server
|
||||
def runserver(PORT):
|
||||
log("Starting database server...")
|
||||
def start_db():
|
||||
log("Starting database...")
|
||||
global lastsync
|
||||
lastsync = int(datetime.datetime.now(tz=datetime.timezone.utc).timestamp())
|
||||
build_db()
|
||||
|
||||
|
||||
loadAPIkeys()
|
||||
|
||||
run(dbserver, host='::', port=PORT, server='waitress')
|
||||
log("Database server reachable!")
|
||||
#run(dbserver, host='::', port=PORT, server='waitress')
|
||||
log("Database reachable!")
|
||||
|
||||
def build_db():
|
||||
|
||||
|
32
server.py
32
server.py
@ -28,7 +28,7 @@ from urllib.error import *
|
||||
|
||||
#settings.config(files=["settings/default.ini","settings/settings.ini"])
|
||||
#settings.update("settings/default.ini","settings/settings.ini")
|
||||
MAIN_PORT, DATABASE_PORT = settings.get_settings("WEB_PORT","API_PORT")
|
||||
MAIN_PORT = settings.get_settings("WEB_PORT")
|
||||
|
||||
|
||||
webserver = Bottle()
|
||||
@ -68,10 +68,19 @@ def customerror(error):
|
||||
return html
|
||||
|
||||
|
||||
|
||||
#@webserver.get("/api/<pth:path>")
|
||||
#def api(pth):
|
||||
# return database.handle_get(pth,request)
|
||||
|
||||
#@webserver.post("/api/<pth:path>")
|
||||
#def api_post(pth):
|
||||
# return database.handle_post(pth,request)
|
||||
|
||||
# this is the fallback option. If you run this service behind a reverse proxy, it is recommended to rewrite /db/ requests to the port of the db server
|
||||
# e.g. location /db { rewrite ^/db(.*)$ $1 break; proxy_pass http://yoururl:12349; }
|
||||
|
||||
@webserver.get("/api/<pth:path>")
|
||||
#@webserver.get("/api/<pth:path>")
|
||||
def database_get(pth):
|
||||
keys = FormsDict.decode(request.query) # The Dal★Shabet handler
|
||||
keystring = "?"
|
||||
@ -88,13 +97,22 @@ def database_get(pth):
|
||||
response.status = e.code
|
||||
return
|
||||
|
||||
@webserver.post("/api/<pth:path>")
|
||||
#@webserver.post("/api/<pth:path>")
|
||||
def database_post(pth):
|
||||
response.set_header("Access-Control-Allow-Origin","*")
|
||||
#print(request.headers)
|
||||
#response.set_header("Access-Control-Allow-Origin","*")
|
||||
try:
|
||||
proxyresponse = urllib.request.urlopen("http://[::1]:" + str(DATABASE_PORT) + "/" + pth,request.body)
|
||||
proxyrequest = urllib.request.Request(
|
||||
url="http://[::1]:" + str(DATABASE_PORT) + "/" + pth,
|
||||
data=request.body,
|
||||
headers=request.headers,
|
||||
method="POST"
|
||||
)
|
||||
proxyresponse = urllib.request.urlopen(proxyrequest)
|
||||
|
||||
contents = proxyresponse.read()
|
||||
response.status = proxyresponse.getcode()
|
||||
response.headers = proxyresponse.headers
|
||||
response.content_type = "application/json"
|
||||
return contents
|
||||
except HTTPError as e:
|
||||
@ -215,7 +233,9 @@ setproctitle.setproctitle("Maloja")
|
||||
|
||||
## start database server
|
||||
#_thread.start_new_thread(SourceFileLoader("database","database.py").load_module().runserver,(DATABASE_PORT,))
|
||||
_thread.start_new_thread(database.runserver,(DATABASE_PORT,))
|
||||
#_thread.start_new_thread(database.runserver,(DATABASE_PORT,))
|
||||
database.start_db()
|
||||
database.register_subroutes(webserver,"/api")
|
||||
|
||||
log("Starting up Maloja server...")
|
||||
run(webserver, host='::', port=MAIN_PORT, server='waitress')
|
||||
|
@ -1,7 +1,6 @@
|
||||
[HTTP]
|
||||
|
||||
WEB_PORT = 42010
|
||||
API_PORT = 42011
|
||||
|
||||
[Third Party Services]
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user