mirror of
https://github.com/Dvorinka/swingmusic-extended.git
synced 2026-06-03 20:13:02 +00:00
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:
@@ -1,16 +1,16 @@
|
|||||||
# TODO
|
# TODO
|
||||||
- Migrations:
|
- Migrations:
|
||||||
1. Move userdata to new hashing algorithm
|
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
|
- Package jsoni and publish on PyPi
|
||||||
- Rewrite stores to use dictionaries instead of list pools
|
- Rewrite stores to use dictionaries instead of list pools
|
||||||
- last updated date on tracks added via watchdog is broken
|
- last updated date on tracks added via watchdog is broken
|
||||||
- hide "remove from playlist" option on system playlists
|
|
||||||
|
|
||||||
# DONE
|
# DONE
|
||||||
- Support auth headers
|
- Support auth headers
|
||||||
- Add recently played playlist
|
- Add recently played playlist
|
||||||
- Move user track logs to user zero
|
- 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
@@ -48,7 +48,7 @@ def create_new_token(user: dict):
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
"msg": f"Logged in as {user['username']}",
|
"msg": f"Logged in as {user['username']}",
|
||||||
"acccesstoken": access_token,
|
"accesstoken": access_token,
|
||||||
"refreshtoken": create_refresh_token(identity=user),
|
"refreshtoken": create_refresh_token(identity=user),
|
||||||
"maxage": max_age,
|
"maxage": max_age,
|
||||||
}
|
}
|
||||||
@@ -76,7 +76,7 @@ def login(body: LoginBody):
|
|||||||
return {"msg": "Hehe! invalid password"}, 401
|
return {"msg": "Hehe! invalid password"}, 401
|
||||||
|
|
||||||
res = create_new_token(user.todict())
|
res = create_new_token(user.todict())
|
||||||
token = res["acccesstoken"]
|
token = res["accesstoken"]
|
||||||
age = res["maxage"]
|
age = res["maxage"]
|
||||||
res = jsonify(res)
|
res = jsonify(res)
|
||||||
set_access_cookies(res, token, max_age=age)
|
set_access_cookies(res, token, max_age=age)
|
||||||
@@ -84,6 +84,38 @@ def login(body: LoginBody):
|
|||||||
return res
|
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")
|
@api.post("/refresh")
|
||||||
@jwt_required(refresh=True)
|
@jwt_required(refresh=True)
|
||||||
def refresh():
|
def refresh():
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
from flask_jwt_extended import current_user
|
|
||||||
from flask_openapi3 import Tag
|
from flask_openapi3 import Tag
|
||||||
from flask_openapi3 import APIBlueprint
|
from flask_openapi3 import APIBlueprint
|
||||||
from pydantic import Field
|
from pydantic import Field
|
||||||
@@ -40,7 +39,6 @@ def log_track(body: LogTrackBody):
|
|||||||
timestamp=timestamp,
|
timestamp=timestamp,
|
||||||
duration=duration,
|
duration=duration,
|
||||||
source=source,
|
source=source,
|
||||||
userid=current_user["id"],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return {"total entries": last_row}
|
return {"total entries": last_row}
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
|
from flask_jwt_extended import current_user
|
||||||
from app.db.sqlite.utils import SQLiteManager
|
from app.db.sqlite.utils import SQLiteManager
|
||||||
from app.models.logger import TrackLog as TrackLog
|
from app.models.logger import TrackLog as TrackLog
|
||||||
|
|
||||||
|
|
||||||
class SQLiteTrackLogger:
|
class SQLiteTrackLogger:
|
||||||
@classmethod
|
@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
|
Inserts a track play record into the database
|
||||||
"""
|
"""
|
||||||
@@ -19,7 +20,9 @@ class SQLiteTrackLogger:
|
|||||||
) VALUES(?,?,?,?,?)
|
) VALUES(?,?,?,?,?)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
cur.execute(sql, (trackhash, duration, timestamp, source, userid))
|
cur.execute(
|
||||||
|
sql, (trackhash, duration, timestamp, source, current_user["id"])
|
||||||
|
)
|
||||||
lastrowid = cur.lastrowid
|
lastrowid = cur.lastrowid
|
||||||
|
|
||||||
return lastrowid
|
return lastrowid
|
||||||
@@ -31,7 +34,7 @@ class SQLiteTrackLogger:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
with SQLiteManager(userdata_db=True) as cur:
|
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)
|
cur.execute(sql)
|
||||||
rows = cur.fetchall()
|
rows = cur.fetchall()
|
||||||
@@ -45,9 +48,9 @@ class SQLiteTrackLogger:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
with SQLiteManager(userdata_db=True) as cur:
|
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))
|
cur.execute(sql, (start, limit))
|
||||||
rows = cur.fetchall()
|
rows = cur.fetchall()
|
||||||
|
|
||||||
return [TrackLog(*row) for row in rows]
|
return [TrackLog(*row) for row in rows]
|
||||||
|
|||||||
@@ -55,7 +55,8 @@ CREATE TABLE IF NOT EXISTS track_logger (
|
|||||||
duration integer NOT NULL,
|
duration integer NOT NULL,
|
||||||
timestamp integer NOT NULL,
|
timestamp integer NOT NULL,
|
||||||
source text,
|
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 (
|
CREATE TABLE IF NOT EXISTS users (
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ def create_folder(path: str, trackcount=0, foldercount=0) -> Folder:
|
|||||||
|
|
||||||
return Folder(
|
return Folder(
|
||||||
name=folder.name,
|
name=folder.name,
|
||||||
path=win_replace_slash(str(folder)),
|
path=win_replace_slash(str(folder)) + "/",
|
||||||
is_sym=folder.is_symlink(),
|
is_sym=folder.is_symlink(),
|
||||||
trackcount=trackcount,
|
trackcount=trackcount,
|
||||||
foldercount=foldercount,
|
foldercount=foldercount,
|
||||||
|
|||||||
@@ -69,9 +69,25 @@ class _3MoveScrobbleToUserId1(Migration):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def migrate():
|
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
|
# INFO: Move the scrobble table to the user id 1
|
||||||
with SQLiteManager(userdata_db=True) as cur:
|
with SQLiteManager(userdata_db=True) as cur:
|
||||||
cur.execute("UPDATE track_logger SET userid = 1 WHERE userid = 0")
|
cur.executescript(sql)
|
||||||
cur.close()
|
cur.close()
|
||||||
|
|
||||||
|
|
||||||
@@ -108,7 +124,7 @@ class _4AddUserIdToFavoritesTable(Migration):
|
|||||||
data = data.fetchone()
|
data = data.fetchone()
|
||||||
|
|
||||||
if data[0] == 1:
|
if data[0] == 1:
|
||||||
return # INFO: column already exists
|
return # INFO: column already exists
|
||||||
|
|
||||||
cur.executescript(sql)
|
cur.executescript(sql)
|
||||||
|
|
||||||
|
|||||||
@@ -84,7 +84,14 @@ app = create_api()
|
|||||||
app.static_folder = get_home_res_path("client")
|
app.static_folder = get_home_res_path("client")
|
||||||
|
|
||||||
# INFO: Routes that don't need authentication
|
# 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())
|
blacklist_extensions = {".webp"}.union(getClientFilesExtensions())
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user