diff --git a/Server/static/scripts.js b/Server/static/scripts.js index 6871635..6b1e019 100644 --- a/Server/static/scripts.js +++ b/Server/static/scripts.js @@ -40,14 +40,42 @@ async function alertText(text="Song Added!") { alertbox.innerHTML = "" } } + +async function getFromServer(source,headersIn={},secure = false, password=adminPass) { + try { + let href = ""; + if(secure) { + href = "https://"+ip+"/" + source; + } else { + href = "http://"+ip+"/" + source; + } + headersIn["Jukebox-Auth"] = password; + headersIn["Accept"] = "application/json"; + let response = await fetch(href,{ + method:"GET", + headers:headersIn, + }) + let data = await response.json(); + if (response.status == ERR_NO_ADMIN) { + alertText("Error: Admin restricted action") + } else if(!response.ok){ + throw new Error(data.error); + } + // we add some information from the response just in case it is needed + data["ok"] = response.ok; + data["status"] = response.status; + // console.log(data); + return await data; + } catch(e) { + alertText(e); + } + +} + // a lot of this is kinda waffly because i was trying to get // it to return the right stuff and javascript is asyrcronouse (boo) -async function getFromServer(bodyInfo, source="", secure=false, password=adminPass) { +async function postFromServer(bodyInfo, source="", secure=false, password=adminPass) { try{ - if (bodyInfo != null) { - // the currently set password is always included in every request - bodyInfo["password"] = password; - } let href = ""; if(secure) { href = "https://"+ip+"/" + source; @@ -58,7 +86,8 @@ async function getFromServer(bodyInfo, source="", secure=false, password=adminPa method: "POST", body: JSON.stringify(bodyInfo), headers: { - "Content-type": "application/json; charset=UTF-8" + "Content-type": "application/json; charset=UTF-8", + "Jukebox-Auth": password } }); @@ -111,12 +140,12 @@ function getCookie(cname) { // also someone who likes things not being dumb more than me would have separated the client and server buttons async function controlButton(buttonType) { if (buttonType == "pp") { // Play-Pause button - let result = await getFromServer({control: "play-pause"}, "controls"); + let result = await postFromServer({control: "play-pause"}, "controls"); // console.log(result); currentlyPlaying = result["data"]["playingState"]; } else if (buttonType == "sk") { // Skip button // clearInterval(playlistTimeTimer); - let returnCode = await getFromServer({control: "skip"}, "controls"); + let returnCode = await postFromServer({control: "skip"}, "controls"); // console.log(returnCode["ok"]) if(returnCode["ok"]) { if (document.getElementById("playlist-mode").style.display == "block") { @@ -149,7 +178,7 @@ async function controlButton(buttonType) { document.getElementById("settings-mode").style.display = "block"; checkSettings() } else if (buttonType == "pm") { //Partymode toggle (in settings) - let response = await getFromServer({setting: "partymode-toggle"}, "settings") + let response = await postFromServer({setting: "partymode-toggle"}, "settings") if(response.ok) { justChangedSetting = true; checkSettings(); @@ -171,7 +200,7 @@ function searchSongsEnter(e) { async function searchSongs(searchTerm){ document.getElementById("songlist").innerHTML = "" - let fetchResults = await getFromServer({search:searchTerm},"search").then(); + let fetchResults = await getFromServer("search?query="+searchTerm); let searchResults = fetchResults.data; //generate the visual song list for(var fileName in searchResults) { @@ -229,7 +258,7 @@ function alertTimeSet(time) { } function qrCodeGenerate() { - let tempURL = "http://" + document.location.href.split("/")[2] + "/?ip=" + ip; + let tempURL = "http://" + URL.parse(document.location.href).host document.getElementById("qrcode").innerHTML = ""; // get the current foreground and background let dark = window.getComputedStyle(document.body).getPropertyValue("--text-color"); @@ -304,7 +333,7 @@ async function checkSettings(skipServer=false) { } } //ping the server here - data = await getFromServer({setting: "getsettings"}, "settings"); + data = await getFromServer("settings"); x = data["data"]; if (!(skipServer) || partyButtonState=="N/A") { if (x["partymode"] == false) { @@ -401,7 +430,7 @@ async function skipInPlaylist() { async function generateVisualPlaylist(conditions="") { document.getElementById("playlist").innerHTML = "

"; - data = await getFromServer(null, "playlist"); + data = await getFromServer("playlist"); playlist = data["data"]["playlist"]; currentlyPlaying = data["data"]["playingState"] playlist = Object.values(playlist).map(obj => { @@ -479,9 +508,9 @@ async function generateVisualPlaylist(conditions="") { } async function submitSong(songid) { - let returncode = await getFromServer({song: songid}, "songadd"); + let returncode = await postFromServer({song: songid}, "songadd"); if(returncode["status"] === ERR_NO_ADMIN) { - // right now the error is alerted in getFromServer, maybe will change that + // right now the error is alerted in postFromServer, maybe will change that } else if(returncode["status"]!==200) { alertText("That song's already in the queue! Hang on!") } else { @@ -555,7 +584,7 @@ async function submitPerms(e) { tempData["PM"] = document.getElementById("partymodesettingcheckbox").checked; tempData["VOL"] = document.getElementById("volumechangesettingcheckbox").checked; tempData["DUP"] = document.getElementById("duplicateallowesettingcheckbox").checked; - let returncode = await getFromServer({"setting":"perms","admin":tempData},"settings"); + let returncode = await postFromServer({"setting":"perms","admin":tempData},"settings"); if (!(returncode["ok"])) { // if you aren't allowed to check the box then toggle it again // its not perfect if you spam click, but it gets the point across to the user @@ -567,7 +596,7 @@ async function submitPerms(e) { } async function clearPlaylist() { - let returncode = await getFromServer({control:"clear"},"controls"); + let returncode = await postFromServer({control:"clear"},"controls"); if(returncode["status"] === ERR_NO_ADMIN || returncode == null) { // alertText("Admin Restricted ") // there's an admin restrict alert built into getFromServer @@ -590,7 +619,7 @@ document.getElementById("settings-mode").style.display = "none"; document.getElementById("volumerange").onchange = async function(e) { // there is no reason for this not to be a defined function // FIX THIS - let returnValue = await getFromServer({setting:"volume",level:e.target.value}, "settings") + let returnValue = await postFromServer({setting:"volume",level:e.target.value}, "settings") if (returnValue["status"] == ERR_NO_ADMIN) { // alertText("Error: Admin restricted action"); // there's an admin restrict alert built into getFromServer diff --git a/Server/webbyBits.py b/Server/webbyBits.py index 710180e..e980e62 100644 --- a/Server/webbyBits.py +++ b/Server/webbyBits.py @@ -33,7 +33,7 @@ controlPerms = { "AS":True, "PM":True, "VOL":True, - "DUP":True # Not implemented, allow duplicate songs in queue + "DUP":True } fileofDB = sql.connect("songDatabase.db") @@ -155,7 +155,7 @@ def playerControls(): recieveData=request.get_json(force=True) try: if recieveData["control"] == "play-pause": - if ADMIN_PASS == recieveData['password'] or controlPerms["PP"]: + if ADMIN_PASS == request.headers["Jukebox-Auth"] or controlPerms["PP"]: playingState = str(player.get_state())=="State.Playing" player.pause() return {"error":"ok","data":{"playingState":not(playingState)}},200 @@ -163,14 +163,14 @@ def playerControls(): playingState = str(player.get_state())=="State.Playing" return {"error":"Admin Restricted Action","data":{"playingState":playingState}},401 elif recieveData["control"] == "skip": - if ADMIN_PASS == recieveData['password'] or controlPerms["SK"]: + if ADMIN_PASS == request.headers["Jukebox-Auth"] or controlPerms["SK"]: skipNow = True return ERR_200 else: return ERR_NO_ADMIN # Maybe i should have put this next one in the "settings" section elif recieveData["control"] == "clear": - if ADMIN_PASS == recieveData['password']: # this is only ever allowed with the adminpassword + if ADMIN_PASS == request.headers["Jukebox-Auth"]: # this is only ever allowed with the adminpassword with playlistLock: playlist.clear() return ERR_200 @@ -181,64 +181,64 @@ def playerControls(): except KeyError: return ERR_MISSING_ARGS -@app.route("/settings", methods=['POST']) +@app.route("/settings", methods=['POST','GET']) def settingsControl(): global controlPerms # set the volume and partymode global partyMode global player - recieveData = request.get_json(force=True) - try: - if recieveData["setting"] == "volume": - if ADMIN_PASS == recieveData['password'] or controlPerms["VOL"]: - volumeLevel = int(recieveData["level"]) - if(volumeLevel <= 100 and volumeLevel >= 0): - volumePassed = player.audio_set_volume(volumeLevel) - if(volumePassed == 0): - # only emit a signal i the volume really changed - socketio.emit("settingsChange",{"settingToChange":"volume","newData":volumeLevel}) - return {"error":"ok","data":{"volumePassed":volumePassed}},200 + if (request.method == 'GET'): + return {"error":"ok","data":{"partymode":partyMode,"volume":player.audio_get_volume(),"admin":controlPerms}},200 + elif (request.method == 'POST'): + recieveData = request.get_json(force=True) + try: + if recieveData["setting"] == "volume": + if ADMIN_PASS == request.headers["Jukebox-Auth"] or controlPerms["VOL"]: + volumeLevel = int(recieveData["level"]) + if(volumeLevel <= 100 and volumeLevel >= 0): + volumePassed = player.audio_set_volume(volumeLevel) + if(volumePassed == 0): + # only emit a signal i the volume really changed + socketio.emit("settingsChange",{"settingToChange":"volume","newData":volumeLevel}) + return {"error":"ok","data":{"volumePassed":volumePassed}},200 + else: + return {"error":"Invalid volume level","data":None},422 else: - return {"error":"Invalid volume level","data":None},422 + return ERR_NO_ADMIN + elif recieveData["setting"] == "partymode-toggle": + if ADMIN_PASS == request.headers["Jukebox-Auth"] or controlPerms["PM"]: + partyMode = not(partyMode) + partyModeStr = "On" if partyMode else "Off" + socketio.emit("settingsChange",{"settingToChange":"partymode","newData":partyModeStr}) + return ERR_200 + else: + return ERR_NO_ADMIN + elif recieveData["setting"] == "perms": + if ADMIN_PASS == request.headers["Jukebox-Auth"]: + controlPerms = recieveData["admin"] + # print(recieveData["admin"]) + socketio.emit("settingsChange",{"settingToChange":"perms","newData":controlPerms}) + return ERR_200 + else: + return ERR_NO_ADMIN else: - return ERR_NO_ADMIN - elif recieveData["setting"] == "partymode-toggle": - if ADMIN_PASS == recieveData['password'] or controlPerms["PM"]: - partyMode = not(partyMode) - partyModeStr = "On" if partyMode else "Off" - socketio.emit("settingsChange",{"settingToChange":"partymode","newData":partyModeStr}) - return ERR_200 - else: - return ERR_NO_ADMIN - elif recieveData["setting"] == "perms": - if ADMIN_PASS == recieveData["password"]: - controlPerms = recieveData["admin"] - # print(recieveData["admin"]) - socketio.emit("settingsChange",{"settingToChange":"perms","newData":controlPerms}) - return ERR_200 - else: - return ERR_NO_ADMIN - elif recieveData["setting"] == "getsettings": - # probably should have made this a different request type or something but it works - return {"error":"ok","data":{"partymode":partyMode,"volume":player.audio_get_volume(),"admin":controlPerms}},200 - else: - return {"error":"Not a valid setting","data":None},400 - except: - return ERR_MISSING_ARGS + return {"error":"Not a valid setting","data":None},400 + except Exception as e: + return {"error":e,"data":None},500 -@app.route("/search", methods=['POST']) +@app.route("/search", methods=['GET']) def searchSongDB(): - recieveData=request.get_json(force=True) + recieveData = request.args.get("query") fileofDB = sql.connect("songDatabase.db") songDatabase = fileofDB.cursor() try: results = [] # print(recieveData["search"]) - if (recieveData['search'] == ""): + if (recieveData == None or recieveData == ""): songDatabase.execute("SELECT * FROM virtualSongs") results = songDatabase.fetchall() else: - songDatabase.execute("SELECT * FROM virtualSongs WHERE virtualSongs MATCH ?",['"' + recieveData['search']+'"']) + songDatabase.execute("SELECT * FROM virtualSongs WHERE virtualSongs MATCH ?",['"' + recieveData +'"']) results = songDatabase.fetchall() tempdata = {} # this is a temporary solution so i dont have to change the client @@ -253,9 +253,6 @@ def searchSongDB(): fileofDB.close() return {"error":"ok","data":tempdata},200 - except KeyError: - fileofDB.close() - return ERR_MISSING_ARGS except sql.OperationalError as e: print(e) fileofDB.close() @@ -266,9 +263,9 @@ def searchSongDB(): def songadd(): recieveData=request.get_json(force=True) try: - if (ADMIN_PASS == recieveData['password']) or controlPerms["AS"]: + if (ADMIN_PASS == request.headers["Jukebox-Auth"]) or controlPerms["AS"]: # Password exists and is correct, or it's not restricted - if not(controlPerms["DUP"]) and (recieveData['song'] in playlist) and not(ADMIN_PASS == recieveData['password']): + if not(controlPerms["DUP"]) and (recieveData['song'] in playlist) and not(ADMIN_PASS == request.headers["Jukebox-Auth"]): return {"error":"This song is already in the queue, hang on!","data":None},409 else: queueSong(recieveData['song']) @@ -280,7 +277,7 @@ def songadd(): print(e) return ERR_MISSING_ARGS -@app.route("/playlist", methods=["POST"]) +@app.route("/playlist", methods=["GET"]) def getPlaylist(): global songNext fileofDB = sql.connect("songDatabase.db") diff --git a/wishlist.md b/wishlist.md index 6b1cbe5..77a58ad 100644 --- a/wishlist.md +++ b/wishlist.md @@ -1,5 +1,9 @@ # Wishlist *Features I would like to add, will be completed in any order* +- [ ] RESTful design + - [ ] Change all necesary POSTs to GET,DELETE (probably not PUT) + - [ ] Write an api layout + - Despite the fact this isn't necesary (nobody but the app should access any url but root) it's probably still a good idea. It's going to be very simple anyway - [ ] Loading indicator while awaiting server stuff - [ ] Refactoring existing code - [x] Remove old comments