diff --git a/.gitignore b/.gitignore index ffb4e68..8f9a392 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ .pytest_cache .venv #.vscode +static/node_modules +static/dist \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 26f4007..5049f85 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,6 +7,14 @@ COPY pyproject.toml poetry.lock README.md ./ RUN poetry config virtualenvs.in-project true RUN poetry install --with app --no-root +FROM node:24 AS node-builder +ENV PATH=/app/node_modules/.bin:$PATH +WORKDIR /app +COPY static/package.json static/package-lock.json ./ +RUN npm ci +COPY static ./ +RUN npm run build + FROM python:3.12-slim ENV PATH="/app/.venv/bin:$PATH" WORKDIR /app @@ -18,6 +26,7 @@ ENV LANG=ru_RU.UTF-8 ENV LC_ALL=ru_RU.UTF-8 ENV TZ="Europe/Moscow" COPY --from=builder /app ./ +COPY --from=node-builder /app/dist ./static/dist COPY gallery gallery/ CMD ["uvicorn", "gallery.main:app", "--host", "0.0.0.0", "--port", "80", "--log-config", "gallery/logging.yaml"] diff --git a/README.md b/README.md index 25d1e9e..280af29 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ -# Gallery +# API Gallery ## View https://api.shmyga.ru -## API Docs +## Swagger https://api.shmyga.ru/docs diff --git a/docker/develop.sh b/docker/develop.sh deleted file mode 100755 index 140a903..0000000 --- a/docker/develop.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env bash -docker compose -f docker-compose-develop.yaml up --build --watch \ No newline at end of file diff --git a/gallery.code-workspace b/gallery.code-workspace index 72d0b3a..7959793 100644 --- a/gallery.code-workspace +++ b/gallery.code-workspace @@ -33,12 +33,14 @@ "type": "debugpy", "request": "launch", "module": "uvicorn", - "args": [ - "gallery.main:app", - "--reload", - "--log-config", - "gallery/logging.yaml", - ], + "args": ["gallery.main:app", "--reload", "--log-config", "gallery/logging.yaml"], + }, + { + "name": "gallery:static", + "cwd": "${workspaceFolder}/static", + "request": "launch", + "type": "node-terminal", + "command": "npm run dev", }, ], }, diff --git a/gallery/easel/route/api/schedule.py b/gallery/easel/route/api/schedule.py index b7e30c7..531e130 100644 --- a/gallery/easel/route/api/schedule.py +++ b/gallery/easel/route/api/schedule.py @@ -7,12 +7,12 @@ from gallery.sketch.schedule.model import ChannelId, Schedule def mount(app: FastAPI): - @app.get("/api/schedule/channels") + @app.get("/api/schedule/channels", tags=["API"]) async def get_api_schedule_channels(request: AppRequest) -> list[ChannelId]: schedule_api = request.app.state.api.schedule return await schedule_api.get_channels() - @app.get("/api/schedule/{channel}/{date}") + @app.get("/api/schedule/{channel}/{date}", tags=["API"]) async def get_api_schedule_channel_schedule( request: AppRequest, channel: str, date: datetime.date ) -> Schedule: diff --git a/gallery/easel/route/api/weather.py b/gallery/easel/route/api/weather.py index 68fd70d..8820f51 100644 --- a/gallery/easel/route/api/weather.py +++ b/gallery/easel/route/api/weather.py @@ -7,21 +7,21 @@ from gallery.sketch.weather.model import Location, WeatherResponse def mount(app: FastAPI): - @app.get("/api/weather/locations") + @app.get("/api/weather/locations", tags=["API"]) async def get_api_weather_locations( request: AppRequest, query: str ) -> list[Location]: weather_api = request.app.state.api.weather return await weather_api.find_locations(query) - @app.get("/api/weather/{location}/day/{date}") + @app.get("/api/weather/{location}/day/{date}", tags=["API"]) async def get_api_weather_day( request: AppRequest, location: str, date: datetime.date ) -> WeatherResponse: weather_api = request.app.state.api.weather return await weather_api.get_day(location, date) - @app.get("/api/weather/{location}/days/{days}") + @app.get("/api/weather/{location}/days/{days}", tags=["API"]) async def get_api_weather_days( request: AppRequest, location: str, days: int ) -> WeatherResponse: diff --git a/gallery/easel/route/view/common/__init__.py b/gallery/easel/route/view/common/__init__.py index 442a098..22aca52 100644 --- a/gallery/easel/route/view/common/__init__.py +++ b/gallery/easel/route/view/common/__init__.py @@ -6,6 +6,7 @@ from fastapi.responses import HTMLResponse from fastapi.staticfiles import StaticFiles from fastapi.templating import Jinja2Templates +from gallery.util import root_path from gallery.version import __version__ @@ -15,13 +16,15 @@ class Section(NamedTuple): SECTIONS = [ - Section("weather", "Погода"), - Section("schedule", "Телепрограмма"), + Section("weather", "Weather"), + Section("schedule", "TV program"), ] def mount(app: FastAPI): base_dir = Path(__file__).parent + print("!", root_path / "static/dist") + app.mount("/static/main", StaticFiles(directory=root_path / "static/dist")) app.mount("/static/common", StaticFiles(directory=base_dir / "static")) templates = Jinja2Templates(directory=base_dir / "templates") diff --git a/gallery/easel/route/view/common/static/style.css b/gallery/easel/route/view/common/static/style.css index b5eac59..cae081f 100644 --- a/gallery/easel/route/view/common/static/style.css +++ b/gallery/easel/route/view/common/static/style.css @@ -1,84 +1,3 @@ -/* -base -*/ -body { - font-size: 1.5rem; -} - -h3 { - margin: 0.5rem 0; -} - -/* -table -*/ -table { - table-layout: fixed; - border-collapse: collapse; -} - -table, -th, -td { - text-align: center; -} - -td { - padding: 0.1rem 0.4rem; -} - -/* -a.button -*/ -a.button { - text-decoration: none; - color: inherit; -} - -.button.disabled { - pointer-events: none; - cursor: default; - color: gray; - filter: grayscale(100%); -} - -/* -app -*/ -.app-container { - display: flex; - flex-direction: row; - flex-wrap: nowrap; - align-items: stretch; -} - -.app-menu { - display: flex; - flex-direction: column; - margin: 0.5rem; -} - -.app-content { - display: flex; - flex: 1; - flex-direction: column; -} - -.app-header { - width: 100%; - display: flex; - flex-direction: row; - gap: 0.5rem; - justify-content: center; -} - -.app-link-home > * { - width: 2rem; - height: 2rem; - background-image: url("/static/common/gallery.png"); - background-size: contain; -} - .icon { display: inline-block; width: 2rem; @@ -86,29 +5,20 @@ app background-size: contain; } -ul.app-list { - list-style: none; - padding-left: 0; - width: 30rem; - margin: auto; +.widget .app { + padding: 0.5rem !important; } -ul.app-list > li { - border: 1px solid lightgrey; +.widget header { + display: none !important; } -ul.app-list > li > a { +.widget main { display: flex; - gap: 0.25rem; - padding: 0.5rem 2rem; - text-decoration: none; - color: inherit; + flex-direction: column; + align-items: center; } -ul.app-list > li:hover { - border-color: rgb(125, 125, 255); -} - -ul.app-list > li:hover > a { - color: rgb(125, 125, 255); +.widget footer { + display: none !important; } diff --git a/gallery/easel/route/view/common/templates/base.html b/gallery/easel/route/view/common/templates/base.html index 42ad0da..f334ed7 100644 --- a/gallery/easel/route/view/common/templates/base.html +++ b/gallery/easel/route/view/common/templates/base.html @@ -9,6 +9,10 @@
| @@ -32,7 +31,7 @@ | ||||||||||
| {{value.start.strftime('%H:%M')}} | {{(value.end - value.start) | timedelta_format}} | {{value.label}} | diff --git a/gallery/easel/route/view/schedule/templates/index.html b/gallery/easel/route/view/schedule/templates/index.html index 35e15a4..20d3ebd 100644 --- a/gallery/easel/route/view/schedule/templates/index.html +++ b/gallery/easel/route/view/schedule/templates/index.html @@ -1,5 +1,5 @@ {% extends "base.html" %} -{% block title %}ТВ{% endblock %} +{% block title %}TV program{% endblock %} {% block head %} {{ super() }} {% endblock %} -{% block header %}Телепрограмма{% endblock %} - {% block content %} -||||||||
| @@ -35,7 +34,7 @@ {% for response in responses %} {% set values = (response.values|selectattr('live') if live else response.values)|list %} {% if values|length > 0 %} - | |||
|
{{response.channel.name}}
|
@@ -43,7 +42,7 @@
|||
| {{value.start.strftime('%H:%M')}} | {{(value.end - value.start) | timedelta_format}} | {{value.label}} | diff --git a/gallery/easel/route/view/weather/static/style.css b/gallery/easel/route/view/weather/static/style.css index af87dda..615922e 100644 --- a/gallery/easel/route/view/weather/static/style.css +++ b/gallery/easel/route/view/weather/static/style.css @@ -1,16 +1,15 @@ .header { - font-size: 1rem; + font-size: 0.9rem; text-align: left; padding-top: 0.25rem; } .date { - font-size: 1.5rem; - background: rgba(0, 0, 0, 0.1); + background: rgba(1, 0, 0, 0.1) !important; } .date.now { - background: rgba(0, 128, 255, 0.2); + background: rgba(0, 128, 255, 0.2) !important; } .date .value a { @@ -59,7 +58,7 @@ } .pressure { - padding: 0; + padding: 0 !important; } .pressure .value { diff --git a/gallery/easel/route/view/weather/templates/index.html b/gallery/easel/route/view/weather/templates/index.html index dfcd5fb..6f94f8e 100644 --- a/gallery/easel/route/view/weather/templates/index.html +++ b/gallery/easel/route/view/weather/templates/index.html @@ -1,5 +1,5 @@ {% extends "base.html" %} -{% block title %}Погода{% endblock %} +{% block title %}Weather{% endblock %} {% block head %} {{ super() }} {% endblock %} -{% block header %}Погода{% endblock %} - {% block content %} +|