diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..bfdec5f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,33 @@ +FROM ghcr.io/astral-sh/uv:python3.13-alpine AS builder + +WORKDIR /app + +RUN \ + apk add --no-cache postgresql-libs && \ + apk add --no-cache --virtual .build-deps gcc musl-dev postgresql-dev + + +RUN --mount=type=cache,target=/root/.cache/uv \ + --mount=type=bind,source=uv.lock,target=uv.lock \ + --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ + uv sync --frozen --no-install-project + +ADD ./app /app/app +ADD uv.lock /app/ +ADD pyproject.toml /app/ +ADD ./alembic /app/alembic +ADD alembic.ini /app + +RUN --mount=type=cache,target=/root/.cache/uv \ + uv sync --frozen --no-editable + + +RUN apk --purge del .build-deps + +FROM python:3.13-alpine + +RUN apk add --no-cache ffmpeg + +COPY --from=builder --chown=app:app /app /app +ENV PATH="/app/.venv/bin:$PATH" +WORKDIR /app diff --git a/app/__init__.py b/app/__init__.py index 31223fc..016e5c8 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -114,7 +114,7 @@ async def fetch_file(url) -> bytes: # TODO: make faster and somehow fix cover not displaying in response async def update_dummy_file_cover(cover_url: str): cover = await fetch_file(cover_url) - dummy_name = 'empty.mp3' + dummy_name = 'app/empty.mp3' audio = ID3(dummy_name) audio.delall('APIC') audio.add(APIC( @@ -131,7 +131,7 @@ async def update_dummy_file_cover(cover_url: str): async def build_response(track: Track, track_id: str, links: str): if not track.telegram_id: - dummy_file = await client.upload_file('empty.mp3') + dummy_file = await client.upload_file('app/empty.mp3') buttons = [Button.inline('Loading', 'loading')] else: dummy_file = InputDocument( diff --git a/app/callback_listener.py b/app/callback_listener.py index ac77daf..7a39dd4 100644 --- a/app/callback_listener.py +++ b/app/callback_listener.py @@ -25,7 +25,7 @@ async def lifespan(app: FastAPI): app = FastAPI(lifespan=lifespan) -app.mount('/static', StaticFiles(directory='static', html=True), name='static') +app.mount('/static', StaticFiles(directory='app/static', html=True), name='static') class LinkException(Exception): diff --git a/empty.mp3 b/app/empty.mp3 similarity index 100% rename from empty.mp3 rename to app/empty.mp3 diff --git a/static/error.html b/app/static/error.html similarity index 100% rename from static/error.html rename to app/static/error.html diff --git a/static/success.html b/app/static/success.html similarity index 100% rename from static/success.html rename to app/static/success.html diff --git a/compose.yml b/compose.yml new file mode 100644 index 0000000..6c6dd88 --- /dev/null +++ b/compose.yml @@ -0,0 +1,21 @@ +services: + bot: + &bot + image: nowplaybot + build: . + env_file: + - .env + restart: unless-stopped + command: > + sh -c "ls && python -m alembic upgrade head && python app/__main__.py" + + + callback_listener: + <<: *bot + ports: + - 8080:8080 + labels: + - traefik.enable=true + - traefik.http.routers.music_mootfrost_dev.rule=Host(`music.mootfrost.dev`) + - traefik.http.services.music_mootfrost_dev.loadbalancer.server.port=8080 + command: python app/callback_listener.py