.dockerignore
backend/.dockerignore
.dockerignore
backend/.dockerignore
.env.example
backend/.env.example
.env.example
backend/.env.example
+3
-3
.github/path-filters.yml
+3
-3
.github/path-filters.yml
+1
-1
.github/workflows/deploy-prod.yml
+1
-1
.github/workflows/deploy-prod.yml
···
25
25
if [ "${{ steps.changes.outputs.migrations }}" == "true" ]; then
26
26
echo "🔄 migrations detected - will run via release_command before deployment"
27
27
fi
28
-
flyctl deploy --config fly.toml --remote-only -a relay-api
28
+
flyctl deploy --config backend/fly.toml --remote-only -a relay-api
29
29
env:
30
30
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN_PROD }}
+8
-8
.github/workflows/deploy-staging.yml
+8
-8
.github/workflows/deploy-staging.yml
···
5
5
branches:
6
6
- main
7
7
paths:
8
-
- "src/**"
9
-
- "pyproject.toml"
10
-
- "uv.lock"
11
-
- "Dockerfile"
12
-
- "fly.staging.toml"
13
-
- "alembic/**"
14
-
- "alembic.ini"
8
+
- "backend/src/**"
9
+
- "backend/pyproject.toml"
10
+
- "backend/uv.lock"
11
+
- "backend/Dockerfile"
12
+
- "backend/fly.staging.toml"
13
+
- "backend/alembic/**"
14
+
- "backend/alembic.ini"
15
15
- ".github/workflows/deploy-staging.yml"
16
16
workflow_dispatch:
17
17
···
36
36
if [ "${{ steps.changes.outputs.migrations }}" == "true" ]; then
37
37
echo "🔄 migrations detected - will run via release_command before deployment"
38
38
fi
39
-
flyctl deploy --config fly.staging.toml --remote-only -a relay-api-staging
39
+
flyctl deploy --config backend/fly.staging.toml --remote-only -a relay-api-staging
40
40
env:
41
41
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN_STAGING }}
+3
-2
.github/workflows/run-pre-commit.yml
+3
-2
.github/workflows/run-pre-commit.yml
···
20
20
uses: astral-sh/setup-uv@v7
21
21
with:
22
22
enable-cache: true
23
-
cache-dependency-glob: "uv.lock"
23
+
cache-dependency-glob: "backend/uv.lock"
24
24
25
25
- name: install bun
26
26
uses: oven-sh/setup-bun@v2
···
34
34
${{ runner.os }}-bun-
35
35
36
36
- name: install dependencies
37
-
run: uv sync
37
+
run: cd backend && uv sync
38
38
39
39
- name: install frontend dependencies
40
40
run: cd frontend && bun install
···
44
44
45
45
- name: check lockfile is up to date
46
46
run: |
47
+
cd backend
47
48
if ! uv lock --check; then
48
49
echo "❌ lockfile is out of date!"
49
50
echo "to update the lockfile, run 'uv lock'."
+7
-7
.github/workflows/test-backend.yml
+7
-7
.github/workflows/test-backend.yml
···
3
3
on:
4
4
pull_request:
5
5
paths:
6
-
- "src/backend/**"
7
-
- "tests/**"
8
-
- "pyproject.toml"
9
-
- "uv.lock"
6
+
- "backend/src/backend/**"
7
+
- "backend/tests/**"
8
+
- "backend/pyproject.toml"
9
+
- "backend/uv.lock"
10
10
- ".github/workflows/test-backend.yml"
11
11
12
12
permissions:
···
44
44
uses: astral-sh/setup-uv@v7
45
45
with:
46
46
enable-cache: true
47
-
cache-dependency-glob: "uv.lock"
47
+
cache-dependency-glob: "backend/uv.lock"
48
48
49
49
- name: install dependencies
50
-
run: uv sync --locked
50
+
run: cd backend && uv sync --locked
51
51
52
52
- name: run tests
53
53
env:
54
54
DATABASE_URL: postgresql+asyncpg://relay_test:relay_test@localhost:5432/relay_test
55
-
run: uv run pytest tests/
55
+
run: cd backend && uv run pytest tests/
56
56
57
57
- name: prune uv cache
58
58
if: always()
+2
-1
.pre-commit-config.yaml
+2
-1
.pre-commit-config.yaml
···
5
5
rev: v0.24.1
6
6
hooks:
7
7
- id: validate-pyproject
8
+
files: ^backend/pyproject\.toml$
8
9
9
10
- repo: https://github.com/pre-commit/mirrors-prettier
10
11
rev: v3.1.0
···
23
24
hooks:
24
25
- id: type-check
25
26
name: type check
26
-
entry: uv run ty check
27
+
entry: bash -c 'cd backend && uv run ty check'
27
28
language: system
28
29
types: [python]
29
30
pass_filenames: false
Dockerfile
backend/Dockerfile
Dockerfile
backend/Dockerfile
alembic.ini
backend/alembic.ini
alembic.ini
backend/alembic.ini
alembic/README
backend/alembic/README
alembic/README
backend/alembic/README
alembic/env.py
backend/alembic/env.py
alembic/env.py
backend/alembic/env.py
alembic/script.py.mako
backend/alembic/script.py.mako
alembic/script.py.mako
backend/alembic/script.py.mako
alembic/versions/2025_11_01_13_34_23_aabe82c8f0fb_add_features_column_to_tracks.py
backend/alembic/versions/2025_11_01_13_34_23_aabe82c8f0fb_add_features_column_to_tracks.py
alembic/versions/2025_11_01_13_34_23_aabe82c8f0fb_add_features_column_to_tracks.py
backend/alembic/versions/2025_11_01_13_34_23_aabe82c8f0fb_add_features_column_to_tracks.py
alembic/versions/2025_11_01_14_18_45_547a171dfd11_drop_old_artist_columns.py
backend/alembic/versions/2025_11_01_14_18_45_547a171dfd11_drop_old_artist_columns.py
alembic/versions/2025_11_01_14_18_45_547a171dfd11_drop_old_artist_columns.py
backend/alembic/versions/2025_11_01_14_18_45_547a171dfd11_drop_old_artist_columns.py
alembic/versions/2025_11_02_12_21_41_06daebe03213_add_user_preferences_table.py
backend/alembic/versions/2025_11_02_12_21_41_06daebe03213_add_user_preferences_table.py
alembic/versions/2025_11_02_12_21_41_06daebe03213_add_user_preferences_table.py
backend/alembic/versions/2025_11_02_12_21_41_06daebe03213_add_user_preferences_table.py
alembic/versions/2025_11_02_12_22_19_9e8c7aa5b945_add_user_preferences_table_fixed.py
backend/alembic/versions/2025_11_02_12_22_19_9e8c7aa5b945_add_user_preferences_table_fixed.py
alembic/versions/2025_11_02_12_22_19_9e8c7aa5b945_add_user_preferences_table_fixed.py
backend/alembic/versions/2025_11_02_12_22_19_9e8c7aa5b945_add_user_preferences_table_fixed.py
alembic/versions/2025_11_02_14_04_59_31e69ba0c570_add_timezone_support_to_datetime_columns.py
backend/alembic/versions/2025_11_02_14_04_59_31e69ba0c570_add_timezone_support_to_datetime_columns.py
alembic/versions/2025_11_02_14_04_59_31e69ba0c570_add_timezone_support_to_datetime_columns.py
backend/alembic/versions/2025_11_02_14_04_59_31e69ba0c570_add_timezone_support_to_datetime_columns.py
alembic/versions/2025_11_02_20_53_02_846cc6b867b8_add_oauth_states_table.py
backend/alembic/versions/2025_11_02_20_53_02_846cc6b867b8_add_oauth_states_table.py
alembic/versions/2025_11_02_20_53_02_846cc6b867b8_add_oauth_states_table.py
backend/alembic/versions/2025_11_02_20_53_02_846cc6b867b8_add_oauth_states_table.py
alembic/versions/2025_11_03_10_15_32_ec40ac6453bc_add_exchange_tokens_table_for_secure_.py
backend/alembic/versions/2025_11_03_10_15_32_ec40ac6453bc_add_exchange_tokens_table_for_secure_.py
alembic/versions/2025_11_03_10_15_32_ec40ac6453bc_add_exchange_tokens_table_for_secure_.py
backend/alembic/versions/2025_11_03_10_15_32_ec40ac6453bc_add_exchange_tokens_table_for_secure_.py
alembic/versions/2025_11_03_18_13_14_5684967eb462_add_queue_state_table.py
backend/alembic/versions/2025_11_03_18_13_14_5684967eb462_add_queue_state_table.py
alembic/versions/2025_11_03_18_13_14_5684967eb462_add_queue_state_table.py
backend/alembic/versions/2025_11_03_18_13_14_5684967eb462_add_queue_state_table.py
alembic/versions/2025_11_04_00_27_13_008ffaa79bea_merge_queue_and_auto_advance_migrations.py
backend/alembic/versions/2025_11_04_00_27_13_008ffaa79bea_merge_queue_and_auto_advance_migrations.py
alembic/versions/2025_11_04_00_27_13_008ffaa79bea_merge_queue_and_auto_advance_migrations.py
backend/alembic/versions/2025_11_04_00_27_13_008ffaa79bea_merge_queue_and_auto_advance_migrations.py
alembic/versions/2025_11_04_04_10_00_bcb5c0fd5d43_add_auto_advance_preference.py
backend/alembic/versions/2025_11_04_04_10_00_bcb5c0fd5d43_add_auto_advance_preference.py
alembic/versions/2025_11_04_04_10_00_bcb5c0fd5d43_add_auto_advance_preference.py
backend/alembic/versions/2025_11_04_04_10_00_bcb5c0fd5d43_add_auto_advance_preference.py
alembic/versions/2025_11_09_02_23_52_ba46ea4ba64e_remove_unique_constraint_from_tracks_.py
backend/alembic/versions/2025_11_09_02_23_52_ba46ea4ba64e_remove_unique_constraint_from_tracks_.py
alembic/versions/2025_11_09_02_23_52_ba46ea4ba64e_remove_unique_constraint_from_tracks_.py
backend/alembic/versions/2025_11_09_02_23_52_ba46ea4ba64e_remove_unique_constraint_from_tracks_.py
alembic/versions/2025_11_09_14_34_08_36868f2c20e5_add_image_id_to_tracks.py
backend/alembic/versions/2025_11_09_14_34_08_36868f2c20e5_add_image_id_to_tracks.py
alembic/versions/2025_11_09_14_34_08_36868f2c20e5_add_image_id_to_tracks.py
backend/alembic/versions/2025_11_09_14_34_08_36868f2c20e5_add_image_id_to_tracks.py
alembic/versions/2025_11_10_17_10_08_2d9a62e170d6_add_pds_url_to_artists_table_for_caching.py
backend/alembic/versions/2025_11_10_17_10_08_2d9a62e170d6_add_pds_url_to_artists_table_for_caching.py
alembic/versions/2025_11_10_17_10_08_2d9a62e170d6_add_pds_url_to_artists_table_for_caching.py
backend/alembic/versions/2025_11_10_17_10_08_2d9a62e170d6_add_pds_url_to_artists_table_for_caching.py
alembic/versions/2025_11_11_12_11_10_32c17ef04e98_add_track_likes_table_for_indexing_.py
backend/alembic/versions/2025_11_11_12_11_10_32c17ef04e98_add_track_likes_table_for_indexing_.py
alembic/versions/2025_11_11_12_11_10_32c17ef04e98_add_track_likes_table_for_indexing_.py
backend/alembic/versions/2025_11_11_12_11_10_32c17ef04e98_add_track_likes_table_for_indexing_.py
alembic/versions/2025_11_12_01_54_02_14fff0296365_add_image_url_to_tracks.py
backend/alembic/versions/2025_11_12_01_54_02_14fff0296365_add_image_url_to_tracks.py
alembic/versions/2025_11_12_01_54_02_14fff0296365_add_image_url_to_tracks.py
backend/alembic/versions/2025_11_12_01_54_02_14fff0296365_add_image_url_to_tracks.py
alembic/versions/2025_11_12_02_06_23_2d6d201752ef_add_performance_indexes_on_track_.py
backend/alembic/versions/2025_11_12_02_06_23_2d6d201752ef_add_performance_indexes_on_track_.py
alembic/versions/2025_11_12_02_06_23_2d6d201752ef_add_performance_indexes_on_track_.py
backend/alembic/versions/2025_11_12_02_06_23_2d6d201752ef_add_performance_indexes_on_track_.py
alembic/versions/2025_11_12_14_34_51_07c4ad820a27_add_notification_sent_to_tracks.py
backend/alembic/versions/2025_11_12_14_34_51_07c4ad820a27_add_notification_sent_to_tracks.py
alembic/versions/2025_11_12_14_34_51_07c4ad820a27_add_notification_sent_to_tracks.py
backend/alembic/versions/2025_11_12_14_34_51_07c4ad820a27_add_notification_sent_to_tracks.py
alembic/versions/2025_11_13_100209_30344721c491_add_album_slug_column_to_tracks.py
backend/alembic/versions/2025_11_13_100209_30344721c491_add_album_slug_column_to_tracks.py
alembic/versions/2025_11_13_100209_30344721c491_add_album_slug_column_to_tracks.py
backend/alembic/versions/2025_11_13_100209_30344721c491_add_album_slug_column_to_tracks.py
alembic/versions/2025_11_13_210000_add_albums_table.py
backend/alembic/versions/2025_11_13_210000_add_albums_table.py
alembic/versions/2025_11_13_210000_add_albums_table.py
backend/alembic/versions/2025_11_13_210000_add_albums_table.py
+1
backend/README.md
+1
backend/README.md
···
1
+
../README.md
+34
backend/justfile
+34
backend/justfile
···
1
+
# backend/justfile
2
+
set shell := ["bash", "-eu", "-o", "pipefail", "-c"]
3
+
default := "run-backend"
4
+
5
+
# run backend server (hot reloads)
6
+
run-backend:
7
+
uv run uvicorn backend.main:app --reload --host 0.0.0.0 --port ${PORT:-8001}
8
+
9
+
# run tests with docker-compose
10
+
test *ARGS='tests/':
11
+
docker compose -f tests/docker-compose.yml up -d
12
+
uv run pytest {{ ARGS }}
13
+
docker compose -f tests/docker-compose.yml down
14
+
15
+
# run type checking and linting
16
+
lint:
17
+
uv run ty check
18
+
uv run ruff check .
19
+
20
+
# create a new database migration
21
+
migrate MESSAGE:
22
+
uv run alembic revision --autogenerate -m "{{ MESSAGE }}"
23
+
24
+
# upgrade database to latest migration
25
+
migrate-up:
26
+
uv run alembic upgrade head
27
+
28
+
# show current migration status
29
+
migrate-status:
30
+
uv run alembic current
31
+
32
+
# create a github release (triggers production deployment)
33
+
release:
34
+
./scripts/release
fly.staging.toml
backend/fly.staging.toml
fly.staging.toml
backend/fly.staging.toml
fly.toml
backend/fly.toml
fly.toml
backend/fly.toml
+5
-30
justfile
+5
-30
justfile
···
1
1
# plyr.fm dev workflows
2
2
mod frontend
3
3
mod transcoder
4
+
mod backend # Backend commands are now managed in backend/justfile
4
5
5
6
6
7
# show available commands
···
13
14
ln -s AGENTS.md CLAUDE.md
14
15
ln -s AGENTS.md GEMINI.md
15
16
16
-
# run backend server (hot reloads)
17
-
run-backend:
18
-
uv run uvicorn backend.main:app --reload --host 0.0.0.0 --port ${PORT:-8001}
19
-
20
-
# run tests with docker-compose
21
-
test *ARGS='tests/':
22
-
docker compose -f tests/docker-compose.yml up -d
23
-
uv run pytest {{ ARGS }}
24
-
docker compose -f tests/docker-compose.yml down
25
-
26
-
# run type checking
27
-
lint:
28
-
uv run ty check
29
-
30
-
# create a new database migration
31
-
migrate MESSAGE:
32
-
uv run alembic revision --autogenerate -m "{{ MESSAGE }}"
33
-
34
-
# upgrade database to latest migration
35
-
migrate-up:
36
-
uv run alembic upgrade head
37
-
38
-
# show current migration status
39
-
migrate-status:
40
-
uv run alembic current
17
+
# Setup sub-modules if they have setup recipes
18
+
# just frontend setup # Uncomment if frontend/justfile gets a setup
19
+
# just backend setup # Uncomment if backend/justfile gets a setup
41
20
42
21
43
22
# show commits since last release
44
23
changelog:
45
24
@git log $(git describe --tags --abbrev=0)..HEAD --pretty=format:'%C(yellow)%h%Creset %C(blue)%ad%Creset %C(green)%s%Creset %C(dim)- %an%Creset' --date=relative
46
25
47
-
# create a github release (triggers production deployment)
48
-
release:
49
-
./scripts/release
50
-
51
26
# deploy frontend only (promote remote main to production-fe branch)
52
27
release-frontend-only:
53
28
git fetch origin main
54
-
git push origin origin/main:production-fe
29
+
git push origin origin/main:production-fe
+4
-2
pyproject.toml
backend/pyproject.toml
+4
-2
pyproject.toml
backend/pyproject.toml
···
24
24
"cachetools>=6.2.1",
25
25
"pytest-asyncio>=0.25.3",
26
26
"aioboto3>=15.5.0",
27
-
"slowapi>=0.1.9",
27
+
"slowapi @ git+https://github.com/zzstoatzz/slowapi.git@fix-deprecation",
28
28
]
29
29
30
30
requires-python = ">=3.11"
···
83
83
python_files = ["test_*.py", "*_test.py"]
84
84
python_classes = ["Test*"]
85
85
python_functions = ["test_*"]
86
-
filterwarnings = ["ignore::pydantic.warnings.UnsupportedFieldAttributeWarning"]
86
+
filterwarnings = [
87
+
"ignore::pydantic.warnings.UnsupportedFieldAttributeWarning",
88
+
]
87
89
88
90
[tool.ruff.lint]
89
91
fixable = ["ALL"]
scripts/backfill_atproto_records.py
backend/scripts/backfill_atproto_records.py
scripts/backfill_atproto_records.py
backend/scripts/backfill_atproto_records.py
scripts/backfill_image_urls.py
backend/scripts/backfill_image_urls.py
scripts/backfill_image_urls.py
backend/scripts/backfill_image_urls.py
scripts/copy_r2_buckets.py
backend/scripts/copy_r2_buckets.py
scripts/copy_r2_buckets.py
backend/scripts/copy_r2_buckets.py
scripts/delete_track.py
backend/scripts/delete_track.py
scripts/delete_track.py
backend/scripts/delete_track.py
scripts/generate_audio_sample.py
backend/scripts/generate_audio_sample.py
scripts/generate_audio_sample.py
backend/scripts/generate_audio_sample.py
scripts/migrate_atproto_namespace.py
backend/scripts/migrate_atproto_namespace.py
scripts/migrate_atproto_namespace.py
backend/scripts/migrate_atproto_namespace.py
scripts/migrate_images_to_new_buckets.py
backend/scripts/migrate_images_to_new_buckets.py
scripts/migrate_images_to_new_buckets.py
backend/scripts/migrate_images_to_new_buckets.py
scripts/migrate_r2_bucket.py
backend/scripts/migrate_r2_bucket.py
scripts/migrate_r2_bucket.py
backend/scripts/migrate_r2_bucket.py
scripts/release
backend/scripts/release
scripts/release
backend/scripts/release
scripts/run_migration.py
backend/scripts/run_migration.py
scripts/run_migration.py
backend/scripts/run_migration.py
scripts/simulate_broken_tracks.py
backend/scripts/simulate_broken_tracks.py
scripts/simulate_broken_tracks.py
backend/scripts/simulate_broken_tracks.py
scripts/smoke_test_staging.py
backend/scripts/smoke_test_staging.py
scripts/smoke_test_staging.py
backend/scripts/smoke_test_staging.py
scripts/test-transcoder.sh
backend/scripts/test-transcoder.sh
scripts/test-transcoder.sh
backend/scripts/test-transcoder.sh
src/backend/__init__.py
backend/src/backend/__init__.py
src/backend/__init__.py
backend/src/backend/__init__.py
src/backend/_internal/CLAUDE.md
backend/src/backend/_internal/CLAUDE.md
src/backend/_internal/CLAUDE.md
backend/src/backend/_internal/CLAUDE.md
src/backend/_internal/__init__.py
backend/src/backend/_internal/__init__.py
src/backend/_internal/__init__.py
backend/src/backend/_internal/__init__.py
src/backend/_internal/atproto/__init__.py
backend/src/backend/_internal/atproto/__init__.py
src/backend/_internal/atproto/__init__.py
backend/src/backend/_internal/atproto/__init__.py
src/backend/_internal/atproto/handles.py
backend/src/backend/_internal/atproto/handles.py
src/backend/_internal/atproto/handles.py
backend/src/backend/_internal/atproto/handles.py
src/backend/_internal/atproto/profile.py
backend/src/backend/_internal/atproto/profile.py
src/backend/_internal/atproto/profile.py
backend/src/backend/_internal/atproto/profile.py
src/backend/_internal/atproto/records.py
backend/src/backend/_internal/atproto/records.py
src/backend/_internal/atproto/records.py
backend/src/backend/_internal/atproto/records.py
src/backend/_internal/atproto/tid.py
backend/src/backend/_internal/atproto/tid.py
src/backend/_internal/atproto/tid.py
backend/src/backend/_internal/atproto/tid.py
src/backend/_internal/audio.py
backend/src/backend/_internal/audio.py
src/backend/_internal/audio.py
backend/src/backend/_internal/audio.py
src/backend/_internal/auth.py
backend/src/backend/_internal/auth.py
src/backend/_internal/auth.py
backend/src/backend/_internal/auth.py
src/backend/_internal/exports.py
backend/src/backend/_internal/exports.py
src/backend/_internal/exports.py
backend/src/backend/_internal/exports.py
src/backend/_internal/image.py
backend/src/backend/_internal/image.py
src/backend/_internal/image.py
backend/src/backend/_internal/image.py
src/backend/_internal/notifications.py
backend/src/backend/_internal/notifications.py
src/backend/_internal/notifications.py
backend/src/backend/_internal/notifications.py
src/backend/_internal/oauth_stores/__init__.py
backend/src/backend/_internal/oauth_stores/__init__.py
src/backend/_internal/oauth_stores/__init__.py
backend/src/backend/_internal/oauth_stores/__init__.py
src/backend/_internal/oauth_stores/postgres.py
backend/src/backend/_internal/oauth_stores/postgres.py
src/backend/_internal/oauth_stores/postgres.py
backend/src/backend/_internal/oauth_stores/postgres.py
src/backend/_internal/queue.py
backend/src/backend/_internal/queue.py
src/backend/_internal/queue.py
backend/src/backend/_internal/queue.py
src/backend/_internal/uploads.py
backend/src/backend/_internal/uploads.py
src/backend/_internal/uploads.py
backend/src/backend/_internal/uploads.py
src/backend/api/CLAUDE.md
backend/src/backend/api/CLAUDE.md
src/backend/api/CLAUDE.md
backend/src/backend/api/CLAUDE.md
src/backend/api/__init__.py
backend/src/backend/api/__init__.py
src/backend/api/__init__.py
backend/src/backend/api/__init__.py
src/backend/api/albums.py
backend/src/backend/api/albums.py
src/backend/api/albums.py
backend/src/backend/api/albums.py
src/backend/api/artists.py
backend/src/backend/api/artists.py
src/backend/api/artists.py
backend/src/backend/api/artists.py
src/backend/api/audio.py
backend/src/backend/api/audio.py
src/backend/api/audio.py
backend/src/backend/api/audio.py
src/backend/api/auth.py
backend/src/backend/api/auth.py
src/backend/api/auth.py
backend/src/backend/api/auth.py
src/backend/api/exports.py
backend/src/backend/api/exports.py
src/backend/api/exports.py
backend/src/backend/api/exports.py
src/backend/api/migration.py
backend/src/backend/api/migration.py
src/backend/api/migration.py
backend/src/backend/api/migration.py
src/backend/api/preferences.py
backend/src/backend/api/preferences.py
src/backend/api/preferences.py
backend/src/backend/api/preferences.py
src/backend/api/queue.py
backend/src/backend/api/queue.py
src/backend/api/queue.py
backend/src/backend/api/queue.py
src/backend/api/search.py
backend/src/backend/api/search.py
src/backend/api/search.py
backend/src/backend/api/search.py
src/backend/api/tracks/__init__.py
backend/src/backend/api/tracks/__init__.py
src/backend/api/tracks/__init__.py
backend/src/backend/api/tracks/__init__.py
src/backend/api/tracks/constants.py
backend/src/backend/api/tracks/constants.py
src/backend/api/tracks/constants.py
backend/src/backend/api/tracks/constants.py
src/backend/api/tracks/likes.py
backend/src/backend/api/tracks/likes.py
src/backend/api/tracks/likes.py
backend/src/backend/api/tracks/likes.py
src/backend/api/tracks/listing.py
backend/src/backend/api/tracks/listing.py
src/backend/api/tracks/listing.py
backend/src/backend/api/tracks/listing.py
src/backend/api/tracks/metadata_service.py
backend/src/backend/api/tracks/metadata_service.py
src/backend/api/tracks/metadata_service.py
backend/src/backend/api/tracks/metadata_service.py
src/backend/api/tracks/mutations.py
backend/src/backend/api/tracks/mutations.py
src/backend/api/tracks/mutations.py
backend/src/backend/api/tracks/mutations.py
src/backend/api/tracks/playback.py
backend/src/backend/api/tracks/playback.py
src/backend/api/tracks/playback.py
backend/src/backend/api/tracks/playback.py
src/backend/api/tracks/router.py
backend/src/backend/api/tracks/router.py
src/backend/api/tracks/router.py
backend/src/backend/api/tracks/router.py
src/backend/api/tracks/services.py
backend/src/backend/api/tracks/services.py
src/backend/api/tracks/services.py
backend/src/backend/api/tracks/services.py
src/backend/api/tracks/uploads.py
backend/src/backend/api/tracks/uploads.py
src/backend/api/tracks/uploads.py
backend/src/backend/api/tracks/uploads.py
src/backend/config.py
backend/src/backend/config.py
src/backend/config.py
backend/src/backend/config.py
src/backend/main.py
backend/src/backend/main.py
src/backend/main.py
backend/src/backend/main.py
src/backend/models/__init__.py
backend/src/backend/models/__init__.py
src/backend/models/__init__.py
backend/src/backend/models/__init__.py
src/backend/models/album.py
backend/src/backend/models/album.py
src/backend/models/album.py
backend/src/backend/models/album.py
src/backend/models/artist.py
backend/src/backend/models/artist.py
src/backend/models/artist.py
backend/src/backend/models/artist.py
src/backend/models/database.py
backend/src/backend/models/database.py
src/backend/models/database.py
backend/src/backend/models/database.py
src/backend/models/exchange_token.py
backend/src/backend/models/exchange_token.py
src/backend/models/exchange_token.py
backend/src/backend/models/exchange_token.py
src/backend/models/oauth_state.py
backend/src/backend/models/oauth_state.py
src/backend/models/oauth_state.py
backend/src/backend/models/oauth_state.py
src/backend/models/preferences.py
backend/src/backend/models/preferences.py
src/backend/models/preferences.py
backend/src/backend/models/preferences.py
src/backend/models/queue.py
backend/src/backend/models/queue.py
src/backend/models/queue.py
backend/src/backend/models/queue.py
src/backend/models/session.py
backend/src/backend/models/session.py
src/backend/models/session.py
backend/src/backend/models/session.py
src/backend/models/track.py
backend/src/backend/models/track.py
src/backend/models/track.py
backend/src/backend/models/track.py
src/backend/models/track_like.py
backend/src/backend/models/track_like.py
src/backend/models/track_like.py
backend/src/backend/models/track_like.py
src/backend/py.typed
backend/src/backend/py.typed
src/backend/py.typed
backend/src/backend/py.typed
src/backend/schemas.py
backend/src/backend/schemas.py
src/backend/schemas.py
backend/src/backend/schemas.py
src/backend/storage/__init__.py
backend/src/backend/storage/__init__.py
src/backend/storage/__init__.py
backend/src/backend/storage/__init__.py
src/backend/storage/r2.py
backend/src/backend/storage/r2.py
src/backend/storage/r2.py
backend/src/backend/storage/r2.py
src/backend/utilities/__init__.py
backend/src/backend/utilities/__init__.py
src/backend/utilities/__init__.py
backend/src/backend/utilities/__init__.py
src/backend/utilities/aggregations.py
backend/src/backend/utilities/aggregations.py
src/backend/utilities/aggregations.py
backend/src/backend/utilities/aggregations.py
src/backend/utilities/database.py
backend/src/backend/utilities/database.py
src/backend/utilities/database.py
backend/src/backend/utilities/database.py
src/backend/utilities/hashing.py
backend/src/backend/utilities/hashing.py
src/backend/utilities/hashing.py
backend/src/backend/utilities/hashing.py
src/backend/utilities/rate_limit.py
backend/src/backend/utilities/rate_limit.py
src/backend/utilities/rate_limit.py
backend/src/backend/utilities/rate_limit.py
src/backend/utilities/slugs.py
backend/src/backend/utilities/slugs.py
src/backend/utilities/slugs.py
backend/src/backend/utilities/slugs.py
tests/CLAUDE.md
backend/tests/CLAUDE.md
tests/CLAUDE.md
backend/tests/CLAUDE.md
tests/__init__.py
backend/tests/__init__.py
tests/__init__.py
backend/tests/__init__.py
tests/api/__init__.py
backend/tests/api/__init__.py
tests/api/__init__.py
backend/tests/api/__init__.py
tests/api/test_albums.py
backend/tests/api/test_albums.py
tests/api/test_albums.py
backend/tests/api/test_albums.py
tests/api/test_analytics.py
backend/tests/api/test_analytics.py
tests/api/test_analytics.py
backend/tests/api/test_analytics.py
tests/api/test_audio.py
backend/tests/api/test_audio.py
tests/api/test_audio.py
backend/tests/api/test_audio.py
tests/api/test_queue.py
backend/tests/api/test_queue.py
tests/api/test_queue.py
backend/tests/api/test_queue.py
tests/api/test_track_deletion.py
backend/tests/api/test_track_deletion.py
tests/api/test_track_deletion.py
backend/tests/api/test_track_deletion.py
tests/api/test_track_likes.py
backend/tests/api/test_track_likes.py
tests/api/test_track_likes.py
backend/tests/api/test_track_likes.py
tests/api/test_upload_size_limits.py
backend/tests/api/test_upload_size_limits.py
tests/api/test_upload_size_limits.py
backend/tests/api/test_upload_size_limits.py
tests/conftest.py
backend/tests/conftest.py
tests/conftest.py
backend/tests/conftest.py
tests/docker-compose.yml
backend/tests/docker-compose.yml
tests/docker-compose.yml
backend/tests/docker-compose.yml
tests/settings/__init__.py
backend/tests/settings/__init__.py
tests/settings/__init__.py
backend/tests/settings/__init__.py
tests/settings/test_notifications.py
backend/tests/settings/test_notifications.py
tests/settings/test_notifications.py
backend/tests/settings/test_notifications.py
tests/test_audio_formats.py
backend/tests/test_audio_formats.py
tests/test_audio_formats.py
backend/tests/test_audio_formats.py
tests/test_auth.py
backend/tests/test_auth.py
tests/test_auth.py
backend/tests/test_auth.py
tests/test_database.py
backend/tests/test_database.py
tests/test_database.py
backend/tests/test_database.py
tests/test_image_formats.py
backend/tests/test_image_formats.py
tests/test_image_formats.py
backend/tests/test_image_formats.py
tests/test_origin_validation.py
backend/tests/test_origin_validation.py
tests/test_origin_validation.py
backend/tests/test_origin_validation.py
tests/test_queue.py
backend/tests/test_queue.py
tests/test_queue.py
backend/tests/test_queue.py
tests/test_security_headers.py
backend/tests/test_security_headers.py
tests/test_security_headers.py
backend/tests/test_security_headers.py
tests/test_settings.py
backend/tests/test_settings.py
tests/test_settings.py
backend/tests/test_settings.py
tests/test_token_refresh.py
backend/tests/test_token_refresh.py
tests/test_token_refresh.py
backend/tests/test_token_refresh.py
tests/utilities/__init__.py
backend/tests/utilities/__init__.py
tests/utilities/__init__.py
backend/tests/utilities/__init__.py
tests/utilities/test_aggregations.py
backend/tests/utilities/test_aggregations.py
tests/utilities/test_aggregations.py
backend/tests/utilities/test_aggregations.py
tests/utilities/test_hashing.py
backend/tests/utilities/test_hashing.py
tests/utilities/test_hashing.py
backend/tests/utilities/test_hashing.py
tests/utilities/test_slugs.py
backend/tests/utilities/test_slugs.py
tests/utilities/test_slugs.py
backend/tests/utilities/test_slugs.py
+2
-6
uv.lock
backend/uv.lock
+2
-6
uv.lock
backend/uv.lock
···
364
364
{ name = "python-dotenv", specifier = ">=1.1.0" },
365
365
{ name = "python-jose", extras = ["cryptography"], specifier = ">=3.3.0" },
366
366
{ name = "python-multipart", specifier = ">=0.0.20" },
367
-
{ name = "slowapi", specifier = ">=0.1.9" },
367
+
{ name = "slowapi", git = "https://github.com/zzstoatzz/slowapi.git?rev=fix-deprecation" },
368
368
{ name = "sqlalchemy", specifier = ">=2.0.36" },
369
369
{ name = "uvicorn", extras = ["standard"], specifier = ">=0.34.0" },
370
370
]
···
2461
2461
[[package]]
2462
2462
name = "slowapi"
2463
2463
version = "0.1.9"
2464
-
source = { registry = "https://pypi.org/simple" }
2464
+
source = { git = "https://github.com/zzstoatzz/slowapi.git?rev=fix-deprecation#4a9e9a228ec3dbcad28e8fa8b6112da7052ee2d9" }
2465
2465
dependencies = [
2466
2466
{ name = "limits" },
2467
-
]
2468
-
sdist = { url = "https://files.pythonhosted.org/packages/a0/99/adfc7f94ca024736f061257d39118e1542bade7a52e86415a4c4ae92d8ff/slowapi-0.1.9.tar.gz", hash = "sha256:639192d0f1ca01b1c6d95bf6c71d794c3a9ee189855337b4821f7f457dddad77", size = 14028, upload-time = "2024-02-05T12:11:52.13Z" }
2469
-
wheels = [
2470
-
{ url = "https://files.pythonhosted.org/packages/2b/bb/f71c4b7d7e7eb3fc1e8c0458a8979b912f40b58002b9fbf37729b8cb464b/slowapi-0.1.9-py3-none-any.whl", hash = "sha256:cfad116cfb84ad9d763ee155c1e5c5cbf00b0d47399a769b227865f5df576e36", size = 14670, upload-time = "2024-02-05T12:11:50.898Z" },
2471
2467
]
2472
2468
2473
2469
[[package]]