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"))
|
m.update(bytes(input,encoding="utf-8"))
|
||||||
return m.hexdigest()
|
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):
|
#def check_sig(keys):
|
||||||
# try:
|
# try:
|
||||||
@ -26,15 +32,28 @@ def md5(input):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
def handle(path,keys):
|
def handle(path,keys,headers,auth):
|
||||||
# log("API REQUEST")
|
print("API request: " + str(path))
|
||||||
# log(str(path))
|
print("Keys:")
|
||||||
# for k in keys:
|
for k in keys:
|
||||||
# log(str(k) + ": " + str(keys.getall(k)))
|
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":
|
try:
|
||||||
return handle_audioscrobbler(path[1:],keys)
|
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
|
# no need to save these on disk, clients can always request a new session
|
||||||
mobile_sessions = []
|
mobile_sessions = []
|
||||||
@ -46,13 +65,18 @@ def handle_audioscrobbler(path,keys):
|
|||||||
if keys.get("method") == "auth.getMobileSession":
|
if keys.get("method") == "auth.getMobileSession":
|
||||||
token = keys.get("authToken")
|
token = keys.get("authToken")
|
||||||
user = keys.get("username")
|
user = keys.get("username")
|
||||||
for key in database.allAPIkeys():
|
password = keys.get("password")
|
||||||
if md5(user + md5(key)) == token:
|
# either username and password
|
||||||
sessionkey = ""
|
if user is not None and password is not None:
|
||||||
for i in range(64):
|
if password in database.allAPIkeys():
|
||||||
sessionkey += str(random.choice(list(range(10)) + list("abcdefghijklmnopqrstuvwxyz") + list("ABCDEFGHIJKLMNOPQRSTUVWXYZ")))
|
sessionkey = generate_key(mobile_sessions)
|
||||||
mobile_sessions.append(sessionkey)
|
|
||||||
return {"session":{"key":sessionkey}}
|
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}
|
return {"error":4}
|
||||||
|
|
||||||
|
|
||||||
@ -77,3 +101,20 @@ def handle_audioscrobbler(path,keys):
|
|||||||
return {"scrobbles":{"@attr":{"ignored":0}}}
|
return {"scrobbles":{"@attr":{"ignored":0}}}
|
||||||
|
|
||||||
return {"error":3}
|
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
|
# server
|
||||||
from bottle import Bottle, route, get, post, run, template, static_file, request, response, FormsDict
|
from bottle import request, response, FormsDict
|
||||||
import waitress
|
|
||||||
# rest of the project
|
# rest of the project
|
||||||
from cleanup import *
|
from cleanup import *
|
||||||
from utilities import *
|
from utilities import *
|
||||||
@ -29,8 +28,6 @@ import urllib
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
dbserver = Bottle()
|
|
||||||
|
|
||||||
dblock = Lock() #global database lock
|
dblock = Lock() #global database lock
|
||||||
|
|
||||||
SCROBBLES = [] # Format: tuple(track_ref,timestamp,saved)
|
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")
|
@dbserver.route("/test")
|
||||||
@ -618,15 +656,14 @@ def post_scrobble():
|
|||||||
# standard-compliant scrobbling methods
|
# standard-compliant scrobbling methods
|
||||||
|
|
||||||
@dbserver.post("/s/<path:path>")
|
@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>")
|
@dbserver.get("/s/<path:path>")
|
||||||
def sapi(path):
|
def sapi(path):
|
||||||
path = path.split("/")
|
path = path.split("/")
|
||||||
keys = FormsDict.decode(request.query)
|
path = list(filter(None,path))
|
||||||
return compliant_api.handle(path,keys)
|
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
|
# Starts the server
|
||||||
def runserver(PORT):
|
def start_db():
|
||||||
log("Starting database server...")
|
log("Starting database...")
|
||||||
global lastsync
|
global lastsync
|
||||||
lastsync = int(datetime.datetime.now(tz=datetime.timezone.utc).timestamp())
|
lastsync = int(datetime.datetime.now(tz=datetime.timezone.utc).timestamp())
|
||||||
build_db()
|
build_db()
|
||||||
|
|
||||||
|
|
||||||
loadAPIkeys()
|
loadAPIkeys()
|
||||||
|
#run(dbserver, host='::', port=PORT, server='waitress')
|
||||||
run(dbserver, host='::', port=PORT, server='waitress')
|
log("Database reachable!")
|
||||||
log("Database server reachable!")
|
|
||||||
|
|
||||||
def build_db():
|
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.config(files=["settings/default.ini","settings/settings.ini"])
|
||||||
#settings.update("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()
|
webserver = Bottle()
|
||||||
@ -68,10 +68,19 @@ def customerror(error):
|
|||||||
return html
|
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
|
# 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; }
|
# 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):
|
def database_get(pth):
|
||||||
keys = FormsDict.decode(request.query) # The Dal★Shabet handler
|
keys = FormsDict.decode(request.query) # The Dal★Shabet handler
|
||||||
keystring = "?"
|
keystring = "?"
|
||||||
@ -88,13 +97,22 @@ def database_get(pth):
|
|||||||
response.status = e.code
|
response.status = e.code
|
||||||
return
|
return
|
||||||
|
|
||||||
@webserver.post("/api/<pth:path>")
|
#@webserver.post("/api/<pth:path>")
|
||||||
def database_post(pth):
|
def database_post(pth):
|
||||||
response.set_header("Access-Control-Allow-Origin","*")
|
#print(request.headers)
|
||||||
|
#response.set_header("Access-Control-Allow-Origin","*")
|
||||||
try:
|
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()
|
contents = proxyresponse.read()
|
||||||
response.status = proxyresponse.getcode()
|
response.status = proxyresponse.getcode()
|
||||||
|
response.headers = proxyresponse.headers
|
||||||
response.content_type = "application/json"
|
response.content_type = "application/json"
|
||||||
return contents
|
return contents
|
||||||
except HTTPError as e:
|
except HTTPError as e:
|
||||||
@ -215,7 +233,9 @@ setproctitle.setproctitle("Maloja")
|
|||||||
|
|
||||||
## start database server
|
## start database server
|
||||||
#_thread.start_new_thread(SourceFileLoader("database","database.py").load_module().runserver,(DATABASE_PORT,))
|
#_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...")
|
log("Starting up Maloja server...")
|
||||||
run(webserver, host='::', port=MAIN_PORT, server='waitress')
|
run(webserver, host='::', port=MAIN_PORT, server='waitress')
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
[HTTP]
|
[HTTP]
|
||||||
|
|
||||||
WEB_PORT = 42010
|
WEB_PORT = 42010
|
||||||
API_PORT = 42011
|
|
||||||
|
|
||||||
[Third Party Services]
|
[Third Party Services]
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user