add a pairing endpoint

+ append / to folder paths
+ filter recently played by logged in user id
+ fix typo in login response
+ update track logger migration to add foreign key
This commit is contained in:
cwilvx
2024-06-09 16:14:09 +03:00
parent fcf1469428
commit b32d0a5743
8 changed files with 76 additions and 19 deletions
+5 -5
View File
@@ -1,16 +1,16 @@
# TODO
- Migrations:
1. Move userdata to new hashing algorithm
- Store (and read) from the correct user account:
1. Playlists
2. Favorites
- Package jsoni and publish on PyPi
- Rewrite stores to use dictionaries instead of list pools
- last updated date on tracks added via watchdog is broken
- hide "remove from playlist" option on system playlists
# DONE
- Support auth headers
- Add recently played playlist
- Move user track logs to user zero
- Move future logs to appropriate user id
- Move future logs to appropriate user id
- Store (and read) from the correct user account:
1. Playlists
2. Favorites
+34 -2
View File
@@ -48,7 +48,7 @@ def create_new_token(user: dict):
return {
"msg": f"Logged in as {user['username']}",
"acccesstoken": access_token,
"accesstoken": access_token,
"refreshtoken": create_refresh_token(identity=user),
"maxage": max_age,
}
@@ -76,7 +76,7 @@ def login(body: LoginBody):
return {"msg": "Hehe! invalid password"}, 401
res = create_new_token(user.todict())
token = res["acccesstoken"]
token = res["accesstoken"]
age = res["maxage"]
res = jsonify(res)
set_access_cookies(res, token, max_age=age)
@@ -84,6 +84,38 @@ def login(body: LoginBody):
return res
pair_token = dict()
class PairDeviceQuery(BaseModel):
code: str = Field("", description="The code")
@api.get("/pair")
@jwt_required(optional=True)
def pair_device(query: PairDeviceQuery):
"""
Pair the Swing Music mobile app with this server
Send a code to get an access token. Send an authenticated request without the code to generate a new token.
"""
if current_user:
token = create_new_token(get_jwt_identity())
key = token["accesstoken"][-6:]
global pair_token
pair_token = {
key: token,
}
return {"code": key}
if query.code:
return pair_token.get(query.code, {"msg": "Invalid code"})
return {"msg": "No code provided"}, 400
@api.post("/refresh")
@jwt_required(refresh=True)
def refresh():
-2
View File
@@ -1,4 +1,3 @@
from flask_jwt_extended import current_user
from flask_openapi3 import Tag
from flask_openapi3 import APIBlueprint
from pydantic import Field
@@ -40,7 +39,6 @@ def log_track(body: LogTrackBody):
timestamp=timestamp,
duration=duration,
source=source,
userid=current_user["id"],
)
return {"total entries": last_row}
+8 -5
View File
@@ -1,10 +1,11 @@
from flask_jwt_extended import current_user
from app.db.sqlite.utils import SQLiteManager
from app.models.logger import TrackLog as TrackLog
class SQLiteTrackLogger:
@classmethod
def insert_track(cls, trackhash: str, duration: int, source: str, timestamp: int, userid: int):
def insert_track(cls, trackhash: str, duration: int, source: str, timestamp: int):
"""
Inserts a track play record into the database
"""
@@ -19,7 +20,9 @@ class SQLiteTrackLogger:
) VALUES(?,?,?,?,?)
"""
cur.execute(sql, (trackhash, duration, timestamp, source, userid))
cur.execute(
sql, (trackhash, duration, timestamp, source, current_user["id"])
)
lastrowid = cur.lastrowid
return lastrowid
@@ -31,7 +34,7 @@ class SQLiteTrackLogger:
"""
with SQLiteManager(userdata_db=True) as cur:
sql = """SELECT * FROM track_logger ORDER BY timestamp DESC"""
sql = f"""SELECT * FROM track_logger WHERE userid = {current_user['id']} ORDER BY timestamp DESC"""
cur.execute(sql)
rows = cur.fetchall()
@@ -45,9 +48,9 @@ class SQLiteTrackLogger:
"""
with SQLiteManager(userdata_db=True) as cur:
sql = """SELECT * FROM track_logger ORDER BY timestamp DESC LIMIT ?,?"""
sql = f"""SELECT * FROM track_logger WHERE userid = {current_user['id']} ORDER BY timestamp DESC LIMIT ?,?"""
cur.execute(sql, (start, limit))
rows = cur.fetchall()
return [TrackLog(*row) for row in rows]
return [TrackLog(*row) for row in rows]
+2 -1
View File
@@ -55,7 +55,8 @@ CREATE TABLE IF NOT EXISTS track_logger (
duration integer NOT NULL,
timestamp integer NOT NULL,
source text,
userid integer NOT NULL DEFAULT 0
userid integer NOT NULL DEFAULT 1,
constraint fk_users foreign key (userid) references users(id) on delete cascade
);
CREATE TABLE IF NOT EXISTS users (
+1 -1
View File
@@ -18,7 +18,7 @@ def create_folder(path: str, trackcount=0, foldercount=0) -> Folder:
return Folder(
name=folder.name,
path=win_replace_slash(str(folder)),
path=win_replace_slash(str(folder)) + "/",
is_sym=folder.is_symlink(),
trackcount=trackcount,
foldercount=foldercount,
+18 -2
View File
@@ -69,9 +69,25 @@ class _3MoveScrobbleToUserId1(Migration):
@staticmethod
def migrate():
sql = """
UPDATE track_logger SET userid = 1 WHERE userid = 0;
ALTER TABLE track_logger RENAME TO _track_logger;
CREATE TABLE IF NOT EXISTS track_logger (
id integer PRIMARY KEY,
trackhash text NOT NULL,
duration integer NOT NULL,
timestamp integer NOT NULL,
source text,
userid integer NOT NULL DEFAULT 1,
constraint fk_users foreign key (userid) references users(id) on delete cascade
);
INSERT INTO track_logger SELECT * FROM _track_logger;
DROP TABLE _track_logger;
"""
# INFO: Move the scrobble table to the user id 1
with SQLiteManager(userdata_db=True) as cur:
cur.execute("UPDATE track_logger SET userid = 1 WHERE userid = 0")
cur.executescript(sql)
cur.close()
@@ -108,7 +124,7 @@ class _4AddUserIdToFavoritesTable(Migration):
data = data.fetchone()
if data[0] == 1:
return # INFO: column already exists
return # INFO: column already exists
cur.executescript(sql)
+8 -1
View File
@@ -84,7 +84,14 @@ app = create_api()
app.static_folder = get_home_res_path("client")
# INFO: Routes that don't need authentication
whitelisted_routes = {"/auth/login", "/auth/users", "/auth/logout", "/auth/refresh", "/docs"}
whitelisted_routes = {
"/auth/login",
"/auth/users",
"/auth/pair",
"/auth/logout",
"/auth/refresh",
"/docs",
}
blacklist_extensions = {".webp"}.union(getClientFilesExtensions())