Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Store access tokens in db #57

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,12 @@ The frontend and backend applications are deployed to two Azure Web Applications
3. Sending a curl request to the appropriate Webhook url:
- Frontend: `curl -dH -X POST "Frontend Webhook from Azure"`
- Backend: `curl -dH -X POST "Backend Webhook from Azure"`


## Database Migrations

Database migrations can be run with the following steps:
1. Navigate to the /backend folder.
3. Run Python locally (e.g. `python3`).
4. From the Python terminal import the migration functions you wish to run. (e.g. `from src.database.migrations.m_241030_add_access_tokens_table import up, down`).
5. Running the desired migration (e.g. `up()`).
47 changes: 31 additions & 16 deletions backend/poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion backend/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ packages = [

[tool.poetry.dependencies]
python = "^3.10"
flask = "^3.0.3"
flask = "^3.1.0"
python-dotenv = "^1.0.0"
flask-cors = "^5.0.0"
pydantic = "^2.6.4"
Expand All @@ -20,6 +20,7 @@ gunicorn = "^22.0.0"
peewee = "^3.17.6"
asyncio = "^3.4.3"
psycopg2 = "^2.9.9"
tenacity = "^9.0.0"

[tool.poetry.group.dev.dependencies]
pytest = "^8.1.1"
Expand Down
8 changes: 2 additions & 6 deletions backend/src/app.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from flask import Flask, make_response
from flask import Flask, redirect
from flask_cors import CORS
from src.controllers.database import database_controller
from src.controllers.spotify import spotify_controller
Expand Down Expand Up @@ -43,11 +43,7 @@ def create_app():

@app.errorhandler(UnauthorizedException)
def handle_unauthorized_exception(_):
resp = make_response(
"Spotify access token invalid or missing. Please re-authenticate.", 401
)
resp.delete_cookie("spotify_access_token")
resp.delete_cookie("user_id")
resp = redirect("/login", 401)
return resp

app.register_blueprint(auth_controller(spotify=spotify))
Expand Down
11 changes: 9 additions & 2 deletions backend/src/controllers/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from flask import Blueprint, make_response, redirect, request, session
from src.flask_config import Config
from src.spotify import SpotifyClient
from src.utils.response_creator import add_cookies_to_response


def auth_controller(spotify: SpotifyClient):
Expand Down Expand Up @@ -36,7 +37,13 @@ def auth_redirect():

@auth_controller.route("refresh-user-code")
def auth_refresh():
refresh_token = request.cookies.get("spotify_refresh_token")
return spotify.refresh_access_token(refresh_token=refresh_token)
user_id = request.cookies.get("user_id")
(user_id, _, _) = spotify.refresh_access_token(user_id=user_id)
return add_cookies_to_response(
make_response(),
{
"user_id": user_id,
},
)

return auth_controller
30 changes: 14 additions & 16 deletions backend/src/controllers/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,10 @@ def database_controller(

@database_controller.route("populate_user", methods=["GET"])
def populate_user():
access_token = request.cookies.get("spotify_access_token")
user = spotify.get_current_user(access_token)
user_id = request.cookies.get("user_id")
user = spotify.get_user_by_id(user_id=user_id)
(db_user, _) = get_or_create_user(user)
simplified_playlists = spotify.get_all_playlists(
user_id=user.id, access_token=access_token
)
simplified_playlists = spotify.get_all_playlists(user_id=user.id)

for simplified_playlist in simplified_playlists:
with database.database.atomic():
Expand All @@ -49,27 +47,28 @@ def populate_user():
if db_playlist is not None:
delete_playlist(db_playlist.id)
playlist = spotify.get_playlist(
access_token=access_token, id=simplified_playlist.id
user_id=user.id,
id=simplified_playlist.id,
)
create_playlist(playlist, db_user)

return make_response("Playlist data populated", 201)

@database_controller.route("populate_playlist/<id>", methods=["GET"])
def populate_playlist(id):
access_token = request.cookies.get("spotify_access_token")
user = spotify.get_current_user(access_token)
user_id = request.cookies.get("user_id")
user = spotify.get_user_by_id(user_id=user_id)
(db_user, _) = get_or_create_user(user)
db_playlist = get_playlist_by_id_or_none(id)
if db_playlist is not None:
delete_playlist(db_playlist.id)
playlist = spotify.get_playlist(access_token=access_token, id=id)
playlist = spotify.get_playlist(user_id=user.id, id=id)
create_playlist(playlist, db_user)
albums = get_playlist_albums(playlist.id)
batch_albums = split_list(albums, 20)
for album_chunk in batch_albums:
albums = spotify.get_multiple_albums(
access_token=access_token, ids=[album.id for album in album_chunk]
user_id=user_id, ids=[album.id for album in album_chunk]
)
for db_album in albums:
with database.database.atomic():
Expand All @@ -82,13 +81,12 @@ def populate_playlist(id):

@database_controller.route("populate_additional_album_details", methods=["GET"])
def populate_additional_album_details():
access_token = request.cookies.get("spotify_access_token")
user = spotify.get_current_user(access_token)
albums = get_user_albums_with_no_artists(user.id)
user_id = request.cookies.get("user_id")
albums = get_user_albums_with_no_artists(user_id=user_id)
batch_albums = split_list(albums, 20)
for album_chunk in batch_albums:
albums = spotify.get_multiple_albums(
access_token=access_token, ids=[album.id for album in album_chunk]
user_id=user_id, ids=[album.id for album in album_chunk]
)
for db_album in albums:
with database.database.atomic():
Expand All @@ -107,8 +105,8 @@ def populate_universal_genre_list():

@database_controller.route("populate_user_album_genres", methods=["GET"])
def populate_user_album_genres():
access_token = request.cookies.get("spotify_access_token")
user = spotify.get_current_user(access_token)
user_id = request.cookies.get("user_id")
user = spotify.get_user_by_id(user_id=user_id)
populate_album_genres_by_user_id(user.id, musicbrainz)
return make_response("User album genres populated", 201)

Expand Down
30 changes: 13 additions & 17 deletions backend/src/controllers/music_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,18 +79,18 @@ def get_playlist(id):
if db_playlist is not None:
return make_response(jsonify(db_playlist.__data__), 200)
else:
access_token = request.cookies.get("spotify_access_token")
playlist = spotify.get_playlist(access_token=access_token, id=id)
return make_response(jsonify(playlist.to_dict()), 200)
user_id = request.cookies.get("user_id")
playlist = spotify.get_playlist(user_id=user_id, id=id)
return make_response(jsonify(playlist.model_dump()), 200)

@music_controller.route("playlist/<id>", methods=["POST"])
def post_edit_playlist(id):
access_token = request.cookies.get("spotify_access_token")
user_id = request.cookies.get("user_id")
name = request.json.get("name")
description = request.json.get("description")
update_playlist_info(id=id, name=name, description=description)
spotify.update_playlist(
access_token=access_token,
user_id=user_id,
id=id,
name=name,
description=description,
Expand All @@ -104,12 +104,10 @@ def get_playlist_album_info(id):
album_info_list = get_playlist_albums_with_genres(id)
return jsonify(album_info_list)
else:
access_token = request.cookies.get("spotify_access_token")
user_id = request.cookies.get("user_id")
return [
album.model_dump()
for album in spotify.get_playlist_album_info(
access_token=access_token, id=id
)
for album in spotify.get_playlist_album_info(user_id=user_id, id=id)
]

@music_controller.route("playlist/<id>/tracks", methods=["GET"])
Expand All @@ -119,12 +117,10 @@ def get_playlist_tracks(id):
track_list = get_playlist_track_list(id)
return jsonify(track_list)
else:
access_token = request.cookies.get("spotify_access_token")
user_id = request.cookies.get("user_id")
return [
album.model_dump()
for album in spotify.get_playlist_album_info(
access_token=access_token, id=id
)
for album in spotify.get_playlist_album_info(user_id=user_id, id=id)
]

@music_controller.route("playlist/search", methods=["POST"])
Expand All @@ -135,7 +131,7 @@ def find_associated_playlists():

@music_controller.route("add_album_to_playlist", methods=["POST"])
def add_album_to_playlist():
access_token = request.cookies.get("spotify_access_token")
user_id = request.cookies.get("user_id")
request_body = request.json
playlist_id = request_body["playlistId"]
album_id = request_body["albumId"]
Expand All @@ -145,13 +141,13 @@ def add_album_to_playlist():
)
create_playlist_album_relationship(playlist_id=playlist_id, album_id=album_id)
return spotify.add_album_to_playlist(
access_token=access_token, playlist_id=playlist_id, album_id=album_id
user_id=user_id, playlist_id=playlist_id, album_id=album_id
)

@music_controller.route("playback", methods=["GET"])
def get_playback_info():
access_token = request.cookies.get("spotify_access_token")
playback_info = spotify.get_my_current_playback(access_token=access_token)
user_id = request.cookies.get("user_id")
playback_info = spotify.get_my_current_playback(user_id=user_id)
if playback_info is None:
return ("", 204)
if playback_info.playlist_id is not None:
Expand Down
Loading