From 06ca0269d7fce04c3d3b3f1eb566bc1f683dcbae Mon Sep 17 00:00:00 2001 From: ZhuchkaTrilesix Date: Fri, 23 May 2025 11:18:15 +0300 Subject: [PATCH 01/14] init docker-compose --- docker-compose.local.yml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 docker-compose.local.yml diff --git a/docker-compose.local.yml b/docker-compose.local.yml new file mode 100644 index 0000000..dcca6db --- /dev/null +++ b/docker-compose.local.yml @@ -0,0 +1,28 @@ +version: '3.8' + +services: + api: + build: . + command: uvicorn main:app --host 0.0.0.0 --port 8000 --reload + volumes: + - .:/code + ports: + - "8000:8000" + environment: + DATABASE_URL: postgresql+asyncpg://fastapi:secret@postgres:5432/fastapi_dev + depends_on: + - postgres + + postgres: + image: postgres:15-alpine + environment: + POSTGRES_USER: fastapi + POSTGRES_PASSWORD: secret + POSTGRES_DB: fastapi_dev + volumes: + - postgres_data:/var/lib/postgresql/data + ports: + - "5432:5432" + +volumes: + postgres_data: \ No newline at end of file From d4f32a5d26728c39c6d344b9943e3365bd41fa67 Mon Sep 17 00:00:00 2001 From: ZhuchkaTrilesix Date: Fri, 23 May 2025 11:52:17 +0300 Subject: [PATCH 02/14] init architecture --- auth_service/.dockerignore | 3 + auth_service/.gitkeep | 0 auth_service/Dockerfile | 14 ++++ auth_service/requirements.txt | 0 auth_service/src/config.py | 8 +++ auth_service/src/main.py | 0 data_service/.dockerignore | 3 + data_service/.gitkeep | 0 data_service/Dockerfile | 14 ++++ data_service/requirements.txt | 0 data_service/src/config.py | 8 +++ data_service/src/main.py | 0 requirements.txt => gateway/requirements.txt | 76 ++++++++++---------- route_service/.dockerignore | 3 + route_service/.gitkeep | 0 route_service/Dockerfile | 14 ++++ route_service/requirements.txt | 0 route_service/src/config.py | 8 +++ route_service/src/main.py | 0 station_service/.dockerignore | 3 + station_service/.gitkeep | 0 station_service/Dockerfile | 14 ++++ station_service/requirements.txt | 0 station_service/src/config.py | 8 +++ station_service/src/main.py | 0 user_service/.dockerignore | 3 + user_service/.gitkeep | 0 user_service/Dockerfile | 14 ++++ user_service/requirements.txt | 0 user_service/src/config.py | 8 +++ user_service/src/main.py | 0 31 files changed, 163 insertions(+), 38 deletions(-) create mode 100644 auth_service/.dockerignore create mode 100644 auth_service/.gitkeep create mode 100644 auth_service/Dockerfile create mode 100644 auth_service/requirements.txt create mode 100644 auth_service/src/config.py create mode 100644 auth_service/src/main.py create mode 100644 data_service/.dockerignore create mode 100644 data_service/.gitkeep create mode 100644 data_service/Dockerfile create mode 100644 data_service/requirements.txt create mode 100644 data_service/src/config.py create mode 100644 data_service/src/main.py rename requirements.txt => gateway/requirements.txt (94%) create mode 100644 route_service/.dockerignore create mode 100644 route_service/.gitkeep create mode 100644 route_service/Dockerfile create mode 100644 route_service/requirements.txt create mode 100644 route_service/src/config.py create mode 100644 route_service/src/main.py create mode 100644 station_service/.dockerignore create mode 100644 station_service/.gitkeep create mode 100644 station_service/Dockerfile create mode 100644 station_service/requirements.txt create mode 100644 station_service/src/config.py create mode 100644 station_service/src/main.py create mode 100644 user_service/.dockerignore create mode 100644 user_service/.gitkeep create mode 100644 user_service/Dockerfile create mode 100644 user_service/requirements.txt create mode 100644 user_service/src/config.py create mode 100644 user_service/src/main.py diff --git a/auth_service/.dockerignore b/auth_service/.dockerignore new file mode 100644 index 0000000..e31985f --- /dev/null +++ b/auth_service/.dockerignore @@ -0,0 +1,3 @@ +.venv +__pycache__ +.idea \ No newline at end of file diff --git a/auth_service/.gitkeep b/auth_service/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/auth_service/Dockerfile b/auth_service/Dockerfile new file mode 100644 index 0000000..853b26d --- /dev/null +++ b/auth_service/Dockerfile @@ -0,0 +1,14 @@ +FROM python:3.12.9-slim + +WORKDIR /app + +COPY requirements.txt . + +RUN pip install --no-cache-dir fastapi[standard] && \ + pip install --no-cache-dir -r requirements.txt + +COPY . . + +WORKDIR /app/src + +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8001", "--reload"] \ No newline at end of file diff --git a/auth_service/requirements.txt b/auth_service/requirements.txt new file mode 100644 index 0000000..e69de29 diff --git a/auth_service/src/config.py b/auth_service/src/config.py new file mode 100644 index 0000000..f3309f5 --- /dev/null +++ b/auth_service/src/config.py @@ -0,0 +1,8 @@ +import os +from abc import ABC +from dataclasses import asdict, dataclass + + +class CfgBase(ABC): + dict: callable = asdict + diff --git a/auth_service/src/main.py b/auth_service/src/main.py new file mode 100644 index 0000000..e69de29 diff --git a/data_service/.dockerignore b/data_service/.dockerignore new file mode 100644 index 0000000..e31985f --- /dev/null +++ b/data_service/.dockerignore @@ -0,0 +1,3 @@ +.venv +__pycache__ +.idea \ No newline at end of file diff --git a/data_service/.gitkeep b/data_service/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/data_service/Dockerfile b/data_service/Dockerfile new file mode 100644 index 0000000..853b26d --- /dev/null +++ b/data_service/Dockerfile @@ -0,0 +1,14 @@ +FROM python:3.12.9-slim + +WORKDIR /app + +COPY requirements.txt . + +RUN pip install --no-cache-dir fastapi[standard] && \ + pip install --no-cache-dir -r requirements.txt + +COPY . . + +WORKDIR /app/src + +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8001", "--reload"] \ No newline at end of file diff --git a/data_service/requirements.txt b/data_service/requirements.txt new file mode 100644 index 0000000..e69de29 diff --git a/data_service/src/config.py b/data_service/src/config.py new file mode 100644 index 0000000..f3309f5 --- /dev/null +++ b/data_service/src/config.py @@ -0,0 +1,8 @@ +import os +from abc import ABC +from dataclasses import asdict, dataclass + + +class CfgBase(ABC): + dict: callable = asdict + diff --git a/data_service/src/main.py b/data_service/src/main.py new file mode 100644 index 0000000..e69de29 diff --git a/requirements.txt b/gateway/requirements.txt similarity index 94% rename from requirements.txt rename to gateway/requirements.txt index 51352b4..65c38d9 100644 --- a/requirements.txt +++ b/gateway/requirements.txt @@ -1,38 +1,38 @@ -annotated-types==0.7.0 -anyio==4.9.0 -bcrypt==4.3.0 -certifi==2025.4.26 -click==8.1.8 -colorama==0.4.6 -dnspython==2.7.0 -email_validator==2.2.0 -fastapi==0.115.12 -fastapi-cli==0.0.7 -h11==0.16.0 -httpcore==1.0.9 -httptools==0.6.4 -httpx==0.28.1 -idna==3.10 -Jinja2==3.1.6 -markdown-it-py==3.0.0 -MarkupSafe==3.0.2 -mdurl==0.1.2 -psycopg2==2.9.10 -pydantic==2.11.4 -pydantic_core==2.33.2 -Pygments==2.19.1 -PyJWT==2.10.1 -python-dotenv==1.1.0 -python-multipart==0.0.20 -PyYAML==6.0.2 -rich==14.0.0 -rich-toolkit==0.14.6 -shellingham==1.5.4 -sniffio==1.3.1 -starlette==0.46.2 -typer==0.15.4 -typing-inspection==0.4.0 -typing_extensions==4.13.2 -uvicorn==0.34.2 -watchfiles==1.0.5 -websockets==15.0.1 +annotated-types==0.7.0 +anyio==4.9.0 +bcrypt==4.3.0 +certifi==2025.4.26 +click==8.1.8 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +psycopg2==2.9.10 +pydantic==2.11.4 +pydantic_core==2.33.2 +Pygments==2.19.1 +PyJWT==2.10.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.6 +shellingham==1.5.4 +sniffio==1.3.1 +starlette==0.46.2 +typer==0.15.4 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 diff --git a/route_service/.dockerignore b/route_service/.dockerignore new file mode 100644 index 0000000..e31985f --- /dev/null +++ b/route_service/.dockerignore @@ -0,0 +1,3 @@ +.venv +__pycache__ +.idea \ No newline at end of file diff --git a/route_service/.gitkeep b/route_service/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/route_service/Dockerfile b/route_service/Dockerfile new file mode 100644 index 0000000..853b26d --- /dev/null +++ b/route_service/Dockerfile @@ -0,0 +1,14 @@ +FROM python:3.12.9-slim + +WORKDIR /app + +COPY requirements.txt . + +RUN pip install --no-cache-dir fastapi[standard] && \ + pip install --no-cache-dir -r requirements.txt + +COPY . . + +WORKDIR /app/src + +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8001", "--reload"] \ No newline at end of file diff --git a/route_service/requirements.txt b/route_service/requirements.txt new file mode 100644 index 0000000..e69de29 diff --git a/route_service/src/config.py b/route_service/src/config.py new file mode 100644 index 0000000..f3309f5 --- /dev/null +++ b/route_service/src/config.py @@ -0,0 +1,8 @@ +import os +from abc import ABC +from dataclasses import asdict, dataclass + + +class CfgBase(ABC): + dict: callable = asdict + diff --git a/route_service/src/main.py b/route_service/src/main.py new file mode 100644 index 0000000..e69de29 diff --git a/station_service/.dockerignore b/station_service/.dockerignore new file mode 100644 index 0000000..e31985f --- /dev/null +++ b/station_service/.dockerignore @@ -0,0 +1,3 @@ +.venv +__pycache__ +.idea \ No newline at end of file diff --git a/station_service/.gitkeep b/station_service/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/station_service/Dockerfile b/station_service/Dockerfile new file mode 100644 index 0000000..853b26d --- /dev/null +++ b/station_service/Dockerfile @@ -0,0 +1,14 @@ +FROM python:3.12.9-slim + +WORKDIR /app + +COPY requirements.txt . + +RUN pip install --no-cache-dir fastapi[standard] && \ + pip install --no-cache-dir -r requirements.txt + +COPY . . + +WORKDIR /app/src + +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8001", "--reload"] \ No newline at end of file diff --git a/station_service/requirements.txt b/station_service/requirements.txt new file mode 100644 index 0000000..e69de29 diff --git a/station_service/src/config.py b/station_service/src/config.py new file mode 100644 index 0000000..f3309f5 --- /dev/null +++ b/station_service/src/config.py @@ -0,0 +1,8 @@ +import os +from abc import ABC +from dataclasses import asdict, dataclass + + +class CfgBase(ABC): + dict: callable = asdict + diff --git a/station_service/src/main.py b/station_service/src/main.py new file mode 100644 index 0000000..e69de29 diff --git a/user_service/.dockerignore b/user_service/.dockerignore new file mode 100644 index 0000000..e31985f --- /dev/null +++ b/user_service/.dockerignore @@ -0,0 +1,3 @@ +.venv +__pycache__ +.idea \ No newline at end of file diff --git a/user_service/.gitkeep b/user_service/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/user_service/Dockerfile b/user_service/Dockerfile new file mode 100644 index 0000000..853b26d --- /dev/null +++ b/user_service/Dockerfile @@ -0,0 +1,14 @@ +FROM python:3.12.9-slim + +WORKDIR /app + +COPY requirements.txt . + +RUN pip install --no-cache-dir fastapi[standard] && \ + pip install --no-cache-dir -r requirements.txt + +COPY . . + +WORKDIR /app/src + +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8001", "--reload"] \ No newline at end of file diff --git a/user_service/requirements.txt b/user_service/requirements.txt new file mode 100644 index 0000000..e69de29 diff --git a/user_service/src/config.py b/user_service/src/config.py new file mode 100644 index 0000000..f3309f5 --- /dev/null +++ b/user_service/src/config.py @@ -0,0 +1,8 @@ +import os +from abc import ABC +from dataclasses import asdict, dataclass + + +class CfgBase(ABC): + dict: callable = asdict + diff --git a/user_service/src/main.py b/user_service/src/main.py new file mode 100644 index 0000000..e69de29 From cbb63cbb1040bb4ad8e6d767c629bb2a20eff0b2 Mon Sep 17 00:00:00 2001 From: ZhuchkaTrilesix Date: Fri, 23 May 2025 12:10:03 +0300 Subject: [PATCH 03/14] update 3.13.3 --- auth_service/Dockerfile | 2 +- data_service/Dockerfile | 2 +- gateway/Dockerfile | 14 ++++++++++++++ route_service/Dockerfile | 2 +- station_service/Dockerfile | 2 +- user_service/Dockerfile | 2 +- 6 files changed, 19 insertions(+), 5 deletions(-) create mode 100644 gateway/Dockerfile diff --git a/auth_service/Dockerfile b/auth_service/Dockerfile index 853b26d..cafbaf3 100644 --- a/auth_service/Dockerfile +++ b/auth_service/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.12.9-slim +FROM python:3.13.3-slim WORKDIR /app diff --git a/data_service/Dockerfile b/data_service/Dockerfile index 853b26d..cafbaf3 100644 --- a/data_service/Dockerfile +++ b/data_service/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.12.9-slim +FROM python:3.13.3-slim WORKDIR /app diff --git a/gateway/Dockerfile b/gateway/Dockerfile new file mode 100644 index 0000000..cafbaf3 --- /dev/null +++ b/gateway/Dockerfile @@ -0,0 +1,14 @@ +FROM python:3.13.3-slim + +WORKDIR /app + +COPY requirements.txt . + +RUN pip install --no-cache-dir fastapi[standard] && \ + pip install --no-cache-dir -r requirements.txt + +COPY . . + +WORKDIR /app/src + +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8001", "--reload"] \ No newline at end of file diff --git a/route_service/Dockerfile b/route_service/Dockerfile index 853b26d..cafbaf3 100644 --- a/route_service/Dockerfile +++ b/route_service/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.12.9-slim +FROM python:3.13.3-slim WORKDIR /app diff --git a/station_service/Dockerfile b/station_service/Dockerfile index 853b26d..cafbaf3 100644 --- a/station_service/Dockerfile +++ b/station_service/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.12.9-slim +FROM python:3.13.3-slim WORKDIR /app diff --git a/user_service/Dockerfile b/user_service/Dockerfile index 853b26d..cafbaf3 100644 --- a/user_service/Dockerfile +++ b/user_service/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.12.9-slim +FROM python:3.13.3-slim WORKDIR /app From 7fb242ac0c9b9c26d43513feffed6fa8e6d5d6a0 Mon Sep 17 00:00:00 2001 From: ZhuchkaTrilesix Date: Fri, 23 May 2025 12:11:01 +0300 Subject: [PATCH 04/14] ref --- .../auth_service}/.dockerignore | 0 .../auth_service}/.gitkeep | 0 .../auth_service}/Dockerfile | 0 .../auth_service}/requirements.txt | 0 .../auth_service}/src/config.py | 0 .../auth_service}/src/main.py | 0 .../data_service}/.dockerignore | 0 .../data_service}/.gitkeep | 0 .../data_service}/Dockerfile | 0 .../data_service}/requirements.txt | 0 .../data_service}/src/config.py | 0 .../data_service}/src/main.py | 0 .../docker-compose.local.yml | 0 {gateway => server/gateway}/Dockerfile | 0 .../gateway}/dependencies/auth.py | 0 {gateway => server/gateway}/main.py | 82 ++++++++--------- {gateway => server/gateway}/requirements.txt | 0 .../gateway}/routers/__init__.py | 0 {gateway => server/gateway}/routers/auth.py | 88 +++++++++---------- {gateway => server/gateway}/routers/route.py | 34 +++---- .../gateway}/routers/station.py | 66 +++++++------- {gateway => server/gateway}/routers/user.py | 70 +++++++-------- {gateway => server/gateway}/utils.py | 0 .../route_service}/.dockerignore | 0 .../route_service}/.gitkeep | 0 .../route_service}/Dockerfile | 0 .../route_service}/requirements.txt | 0 .../route_service}/src/config.py | 0 .../route_service}/src/main.py | 0 .../station_service}/.dockerignore | 0 .../station_service}/.gitkeep | 0 .../station_service}/Dockerfile | 0 .../station_service}/requirements.txt | 0 .../station_service}/src/config.py | 0 .../station_service}/src/main.py | 0 .../user_service}/.dockerignore | 0 .../user_service}/.gitkeep | 0 .../user_service}/Dockerfile | 0 .../user_service}/requirements.txt | 0 .../user_service}/src/config.py | 0 .../user_service}/src/main.py | 0 41 files changed, 170 insertions(+), 170 deletions(-) rename {auth_service => server/auth_service}/.dockerignore (100%) rename {auth_service => server/auth_service}/.gitkeep (100%) rename {auth_service => server/auth_service}/Dockerfile (100%) rename {auth_service => server/auth_service}/requirements.txt (100%) rename {auth_service => server/auth_service}/src/config.py (100%) rename {auth_service => server/auth_service}/src/main.py (100%) rename {data_service => server/data_service}/.dockerignore (100%) rename {data_service => server/data_service}/.gitkeep (100%) rename {data_service => server/data_service}/Dockerfile (100%) rename {data_service => server/data_service}/requirements.txt (100%) rename {data_service => server/data_service}/src/config.py (100%) rename {data_service => server/data_service}/src/main.py (100%) rename docker-compose.local.yml => server/docker-compose.local.yml (100%) rename {gateway => server/gateway}/Dockerfile (100%) rename {gateway => server/gateway}/dependencies/auth.py (100%) rename {gateway => server/gateway}/main.py (96%) rename {gateway => server/gateway}/requirements.txt (100%) rename {gateway => server/gateway}/routers/__init__.py (100%) rename {gateway => server/gateway}/routers/auth.py (96%) rename {gateway => server/gateway}/routers/route.py (95%) rename {gateway => server/gateway}/routers/station.py (96%) rename {gateway => server/gateway}/routers/user.py (96%) rename {gateway => server/gateway}/utils.py (100%) rename {route_service => server/route_service}/.dockerignore (100%) rename {route_service => server/route_service}/.gitkeep (100%) rename {route_service => server/route_service}/Dockerfile (100%) rename {route_service => server/route_service}/requirements.txt (100%) rename {route_service => server/route_service}/src/config.py (100%) rename {route_service => server/route_service}/src/main.py (100%) rename {station_service => server/station_service}/.dockerignore (100%) rename {station_service => server/station_service}/.gitkeep (100%) rename {station_service => server/station_service}/Dockerfile (100%) rename {station_service => server/station_service}/requirements.txt (100%) rename {station_service => server/station_service}/src/config.py (100%) rename {station_service => server/station_service}/src/main.py (100%) rename {user_service => server/user_service}/.dockerignore (100%) rename {user_service => server/user_service}/.gitkeep (100%) rename {user_service => server/user_service}/Dockerfile (100%) rename {user_service => server/user_service}/requirements.txt (100%) rename {user_service => server/user_service}/src/config.py (100%) rename {user_service => server/user_service}/src/main.py (100%) diff --git a/auth_service/.dockerignore b/server/auth_service/.dockerignore similarity index 100% rename from auth_service/.dockerignore rename to server/auth_service/.dockerignore diff --git a/auth_service/.gitkeep b/server/auth_service/.gitkeep similarity index 100% rename from auth_service/.gitkeep rename to server/auth_service/.gitkeep diff --git a/auth_service/Dockerfile b/server/auth_service/Dockerfile similarity index 100% rename from auth_service/Dockerfile rename to server/auth_service/Dockerfile diff --git a/auth_service/requirements.txt b/server/auth_service/requirements.txt similarity index 100% rename from auth_service/requirements.txt rename to server/auth_service/requirements.txt diff --git a/auth_service/src/config.py b/server/auth_service/src/config.py similarity index 100% rename from auth_service/src/config.py rename to server/auth_service/src/config.py diff --git a/auth_service/src/main.py b/server/auth_service/src/main.py similarity index 100% rename from auth_service/src/main.py rename to server/auth_service/src/main.py diff --git a/data_service/.dockerignore b/server/data_service/.dockerignore similarity index 100% rename from data_service/.dockerignore rename to server/data_service/.dockerignore diff --git a/data_service/.gitkeep b/server/data_service/.gitkeep similarity index 100% rename from data_service/.gitkeep rename to server/data_service/.gitkeep diff --git a/data_service/Dockerfile b/server/data_service/Dockerfile similarity index 100% rename from data_service/Dockerfile rename to server/data_service/Dockerfile diff --git a/data_service/requirements.txt b/server/data_service/requirements.txt similarity index 100% rename from data_service/requirements.txt rename to server/data_service/requirements.txt diff --git a/data_service/src/config.py b/server/data_service/src/config.py similarity index 100% rename from data_service/src/config.py rename to server/data_service/src/config.py diff --git a/data_service/src/main.py b/server/data_service/src/main.py similarity index 100% rename from data_service/src/main.py rename to server/data_service/src/main.py diff --git a/docker-compose.local.yml b/server/docker-compose.local.yml similarity index 100% rename from docker-compose.local.yml rename to server/docker-compose.local.yml diff --git a/gateway/Dockerfile b/server/gateway/Dockerfile similarity index 100% rename from gateway/Dockerfile rename to server/gateway/Dockerfile diff --git a/gateway/dependencies/auth.py b/server/gateway/dependencies/auth.py similarity index 100% rename from gateway/dependencies/auth.py rename to server/gateway/dependencies/auth.py diff --git a/gateway/main.py b/server/gateway/main.py similarity index 96% rename from gateway/main.py rename to server/gateway/main.py index 04672cd..f9fe882 100644 --- a/gateway/main.py +++ b/server/gateway/main.py @@ -1,42 +1,42 @@ -from fastapi import FastAPI, Depends, HTTPException -from fastapi.middleware.cors import CORSMiddleware -from contextlib import asynccontextmanager -import logging -import uvicorn - -from routers import auth, user, station, route - -logging.basicConfig( - level=logging.INFO, - format="%(asctime)s [%(levelname)s] %(name)s: %(message)s" -) -logger = logging.getLogger(__name__) - - -app = FastAPI(title="EV Route Planner Gateway") - -app.add_middleware( - CORSMiddleware, - allow_origins=["*"], - allow_credentials=True, - allow_methods=["*"], - allow_headers=["*"], -) - -# Подключение маршрутов -app.include_router(auth.router, prefix="/auth") -app.include_router(user.router, prefix="/user") -app.include_router(station.router, prefix="/stations") -app.include_router(route.router, prefix="/route") -#app.include_router(data.router, prefix="/data") - -@asynccontextmanager -async def lifespan(app: FastAPI): - logger.info("Gateway server is starting...") - yield - logger.info("Gateway server is shutting down...") - - -if __name__ == "__main__": - logger.info("Running with Uvicorn at http://0.0.0.0:8000") +from fastapi import FastAPI, Depends, HTTPException +from fastapi.middleware.cors import CORSMiddleware +from contextlib import asynccontextmanager +import logging +import uvicorn + +from routers import auth, user, station, route + +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s [%(levelname)s] %(name)s: %(message)s" +) +logger = logging.getLogger(__name__) + + +app = FastAPI(title="EV Route Planner Gateway") + +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +# Подключение маршрутов +app.include_router(auth.router, prefix="/auth") +app.include_router(user.router, prefix="/user") +app.include_router(station.router, prefix="/stations") +app.include_router(route.router, prefix="/route") +#app.include_router(data.router, prefix="/data") + +@asynccontextmanager +async def lifespan(app: FastAPI): + logger.info("Gateway server is starting...") + yield + logger.info("Gateway server is shutting down...") + + +if __name__ == "__main__": + logger.info("Running with Uvicorn at http://0.0.0.0:8000") uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True) \ No newline at end of file diff --git a/gateway/requirements.txt b/server/gateway/requirements.txt similarity index 100% rename from gateway/requirements.txt rename to server/gateway/requirements.txt diff --git a/gateway/routers/__init__.py b/server/gateway/routers/__init__.py similarity index 100% rename from gateway/routers/__init__.py rename to server/gateway/routers/__init__.py diff --git a/gateway/routers/auth.py b/server/gateway/routers/auth.py similarity index 96% rename from gateway/routers/auth.py rename to server/gateway/routers/auth.py index 7c1c952..2e99684 100644 --- a/gateway/routers/auth.py +++ b/server/gateway/routers/auth.py @@ -1,45 +1,45 @@ -from fastapi import APIRouter, HTTPException, Depends -from pydantic import BaseModel, EmailStr -from utils import hash_password, verify_password, create_access_token -import psycopg2 - -router = APIRouter() - -SECRET_KEY = "secret" - -conn = psycopg2.connect(dbname="evroutesusers", user="roki", password="roki", host="localhost") -cursor = conn.cursor() - -class LoginData(BaseModel): - email: EmailStr - password: str - -class RegisterData(BaseModel): - email: EmailStr - password: str - role: str = "user" - -@router.post("/register") -def register(data: RegisterData): - cursor.execute("SELECT 1 FROM users WHERE email = %s", (data.email,)) - if cursor.fetchone(): - raise HTTPException(status_code=400, detail="User already exists") - - hashed_pw = hash_password(data.password) - - cursor.execute( - "INSERT INTO users (email, password_hash, role) VALUES (%s, %s, %s)", - (data.email, hashed_pw, data.role) - ) - conn.commit() - return {"status": "registered"} - -@router.post("/login") -def login(data: LoginData): - cursor.execute("SELECT password_hash, role FROM users WHERE email = %s", (data.email,)) - row = cursor.fetchone() - if not row or not verify_password(data.password, row[0]): - raise HTTPException(status_code=401, detail="Invalid credentials") - - token = create_access_token({"email": data.email, "role": row[1]}) +from fastapi import APIRouter, HTTPException, Depends +from pydantic import BaseModel, EmailStr +from utils import hash_password, verify_password, create_access_token +import psycopg2 + +router = APIRouter() + +SECRET_KEY = "secret" + +conn = psycopg2.connect(dbname="evroutesusers", user="roki", password="roki", host="localhost") +cursor = conn.cursor() + +class LoginData(BaseModel): + email: EmailStr + password: str + +class RegisterData(BaseModel): + email: EmailStr + password: str + role: str = "user" + +@router.post("/register") +def register(data: RegisterData): + cursor.execute("SELECT 1 FROM users WHERE email = %s", (data.email,)) + if cursor.fetchone(): + raise HTTPException(status_code=400, detail="User already exists") + + hashed_pw = hash_password(data.password) + + cursor.execute( + "INSERT INTO users (email, password_hash, role) VALUES (%s, %s, %s)", + (data.email, hashed_pw, data.role) + ) + conn.commit() + return {"status": "registered"} + +@router.post("/login") +def login(data: LoginData): + cursor.execute("SELECT password_hash, role FROM users WHERE email = %s", (data.email,)) + row = cursor.fetchone() + if not row or not verify_password(data.password, row[0]): + raise HTTPException(status_code=401, detail="Invalid credentials") + + token = create_access_token({"email": data.email, "role": row[1]}) return {"access_token": token} \ No newline at end of file diff --git a/gateway/routers/route.py b/server/gateway/routers/route.py similarity index 95% rename from gateway/routers/route.py rename to server/gateway/routers/route.py index 9feb94d..f51661f 100644 --- a/gateway/routers/route.py +++ b/server/gateway/routers/route.py @@ -1,18 +1,18 @@ -from fastapi import APIRouter -from pydantic import BaseModel - -router = APIRouter() - -class RouteRequest(BaseModel): - start: str - end: str - battery_level: int - -@router.post("/") -def calculate_route(data: RouteRequest): - return { - "start": data.start, - "end": data.end, - "estimated_time": "2h 30m", - "stations_on_route": [] +from fastapi import APIRouter +from pydantic import BaseModel + +router = APIRouter() + +class RouteRequest(BaseModel): + start: str + end: str + battery_level: int + +@router.post("/") +def calculate_route(data: RouteRequest): + return { + "start": data.start, + "end": data.end, + "estimated_time": "2h 30m", + "stations_on_route": [] } \ No newline at end of file diff --git a/gateway/routers/station.py b/server/gateway/routers/station.py similarity index 96% rename from gateway/routers/station.py rename to server/gateway/routers/station.py index b7f47dd..7554805 100644 --- a/gateway/routers/station.py +++ b/server/gateway/routers/station.py @@ -1,34 +1,34 @@ -from fastapi import APIRouter -from pydantic import BaseModel -import psycopg2 - -router = APIRouter() - -conn = psycopg2.connect(dbname="evroutesstations", user="roki", password="roki", host="localhost") -cursor = conn.cursor() - -class Station(BaseModel): - name: str - latitude: float - longitude: float - connector_type: str - power_kW: int - working_hours: str - -@router.post("/") -def add_station(station: Station): - cursor.execute( - """ - INSERT INTO stations (name, latitude, longitude, connector_type, power_kW, working_hours) - VALUES (%s, %s, %s, %s, %s, %s) - """, - (station.name, station.latitude, station.longitude, station.connector_type, station.power_kW, station.working_hours) - ) - conn.commit() - return {"status": "ok"} - -@router.get("/") -def get_stations(): - cursor.execute("SELECT * FROM stations") - rows = cursor.fetchall() +from fastapi import APIRouter +from pydantic import BaseModel +import psycopg2 + +router = APIRouter() + +conn = psycopg2.connect(dbname="evroutesstations", user="roki", password="roki", host="localhost") +cursor = conn.cursor() + +class Station(BaseModel): + name: str + latitude: float + longitude: float + connector_type: str + power_kW: int + working_hours: str + +@router.post("/") +def add_station(station: Station): + cursor.execute( + """ + INSERT INTO stations (name, latitude, longitude, connector_type, power_kW, working_hours) + VALUES (%s, %s, %s, %s, %s, %s) + """, + (station.name, station.latitude, station.longitude, station.connector_type, station.power_kW, station.working_hours) + ) + conn.commit() + return {"status": "ok"} + +@router.get("/") +def get_stations(): + cursor.execute("SELECT * FROM stations") + rows = cursor.fetchall() return rows \ No newline at end of file diff --git a/gateway/routers/user.py b/server/gateway/routers/user.py similarity index 96% rename from gateway/routers/user.py rename to server/gateway/routers/user.py index a18d363..3821aed 100644 --- a/gateway/routers/user.py +++ b/server/gateway/routers/user.py @@ -1,36 +1,36 @@ -from fastapi import APIRouter -from pydantic import BaseModel -import psycopg2 - -router = APIRouter() - -conn = psycopg2.connect(dbname="evroutesuserinfo", user="roki", password="roki", host="localhost") -cursor = conn.cursor() - -class UserData(BaseModel): - email: str - car_model: str - battery_capacity: float - connector_type: str - -@router.post("/") -def save_user(data: UserData): - cursor.execute( - """ - INSERT INTO users (email, car_model, battery_capacity, connector_type) - VALUES (%s, %s, %s, %s) - ON CONFLICT (email) DO UPDATE SET - car_model = EXCLUDED.car_model, - battery_capacity = EXCLUDED.battery_capacity, - connector_type = EXCLUDED.connector_type - """, - (data.email, data.car_model, data.battery_capacity, data.connector_type) - ) - conn.commit() - return {"status": "saved"} - -@router.get("/{email}") -def get_user(email: str): - cursor.execute("SELECT * FROM users WHERE email = %s", (email,)) - row = cursor.fetchone() +from fastapi import APIRouter +from pydantic import BaseModel +import psycopg2 + +router = APIRouter() + +conn = psycopg2.connect(dbname="evroutesuserinfo", user="roki", password="roki", host="localhost") +cursor = conn.cursor() + +class UserData(BaseModel): + email: str + car_model: str + battery_capacity: float + connector_type: str + +@router.post("/") +def save_user(data: UserData): + cursor.execute( + """ + INSERT INTO users (email, car_model, battery_capacity, connector_type) + VALUES (%s, %s, %s, %s) + ON CONFLICT (email) DO UPDATE SET + car_model = EXCLUDED.car_model, + battery_capacity = EXCLUDED.battery_capacity, + connector_type = EXCLUDED.connector_type + """, + (data.email, data.car_model, data.battery_capacity, data.connector_type) + ) + conn.commit() + return {"status": "saved"} + +@router.get("/{email}") +def get_user(email: str): + cursor.execute("SELECT * FROM users WHERE email = %s", (email,)) + row = cursor.fetchone() return row \ No newline at end of file diff --git a/gateway/utils.py b/server/gateway/utils.py similarity index 100% rename from gateway/utils.py rename to server/gateway/utils.py diff --git a/route_service/.dockerignore b/server/route_service/.dockerignore similarity index 100% rename from route_service/.dockerignore rename to server/route_service/.dockerignore diff --git a/route_service/.gitkeep b/server/route_service/.gitkeep similarity index 100% rename from route_service/.gitkeep rename to server/route_service/.gitkeep diff --git a/route_service/Dockerfile b/server/route_service/Dockerfile similarity index 100% rename from route_service/Dockerfile rename to server/route_service/Dockerfile diff --git a/route_service/requirements.txt b/server/route_service/requirements.txt similarity index 100% rename from route_service/requirements.txt rename to server/route_service/requirements.txt diff --git a/route_service/src/config.py b/server/route_service/src/config.py similarity index 100% rename from route_service/src/config.py rename to server/route_service/src/config.py diff --git a/route_service/src/main.py b/server/route_service/src/main.py similarity index 100% rename from route_service/src/main.py rename to server/route_service/src/main.py diff --git a/station_service/.dockerignore b/server/station_service/.dockerignore similarity index 100% rename from station_service/.dockerignore rename to server/station_service/.dockerignore diff --git a/station_service/.gitkeep b/server/station_service/.gitkeep similarity index 100% rename from station_service/.gitkeep rename to server/station_service/.gitkeep diff --git a/station_service/Dockerfile b/server/station_service/Dockerfile similarity index 100% rename from station_service/Dockerfile rename to server/station_service/Dockerfile diff --git a/station_service/requirements.txt b/server/station_service/requirements.txt similarity index 100% rename from station_service/requirements.txt rename to server/station_service/requirements.txt diff --git a/station_service/src/config.py b/server/station_service/src/config.py similarity index 100% rename from station_service/src/config.py rename to server/station_service/src/config.py diff --git a/station_service/src/main.py b/server/station_service/src/main.py similarity index 100% rename from station_service/src/main.py rename to server/station_service/src/main.py diff --git a/user_service/.dockerignore b/server/user_service/.dockerignore similarity index 100% rename from user_service/.dockerignore rename to server/user_service/.dockerignore diff --git a/user_service/.gitkeep b/server/user_service/.gitkeep similarity index 100% rename from user_service/.gitkeep rename to server/user_service/.gitkeep diff --git a/user_service/Dockerfile b/server/user_service/Dockerfile similarity index 100% rename from user_service/Dockerfile rename to server/user_service/Dockerfile diff --git a/user_service/requirements.txt b/server/user_service/requirements.txt similarity index 100% rename from user_service/requirements.txt rename to server/user_service/requirements.txt diff --git a/user_service/src/config.py b/server/user_service/src/config.py similarity index 100% rename from user_service/src/config.py rename to server/user_service/src/config.py diff --git a/user_service/src/main.py b/server/user_service/src/main.py similarity index 100% rename from user_service/src/main.py rename to server/user_service/src/main.py From a0cf98c5a976d076c9e2df83223cfdcb2ad81393 Mon Sep 17 00:00:00 2001 From: ZhuchkaTrilesix Date: Fri, 23 May 2025 12:17:39 +0300 Subject: [PATCH 05/14] docker for services --- server/auth_service/Dockerfile | 2 +- server/data_service/Dockerfile | 2 +- server/docker-compose.local.yml | 68 +++++++++++++++++++++++++++---- server/route_service/Dockerfile | 2 +- server/station_service/Dockerfile | 2 +- server/user_service/Dockerfile | 2 +- 6 files changed, 66 insertions(+), 12 deletions(-) diff --git a/server/auth_service/Dockerfile b/server/auth_service/Dockerfile index cafbaf3..c41bb0c 100644 --- a/server/auth_service/Dockerfile +++ b/server/auth_service/Dockerfile @@ -11,4 +11,4 @@ COPY . . WORKDIR /app/src -CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8001", "--reload"] \ No newline at end of file +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8002", "--reload"] \ No newline at end of file diff --git a/server/data_service/Dockerfile b/server/data_service/Dockerfile index cafbaf3..79d41a0 100644 --- a/server/data_service/Dockerfile +++ b/server/data_service/Dockerfile @@ -11,4 +11,4 @@ COPY . . WORKDIR /app/src -CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8001", "--reload"] \ No newline at end of file +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8003", "--reload"] \ No newline at end of file diff --git a/server/docker-compose.local.yml b/server/docker-compose.local.yml index dcca6db..81d30d4 100644 --- a/server/docker-compose.local.yml +++ b/server/docker-compose.local.yml @@ -1,17 +1,71 @@ version: '3.8' services: - api: - build: . - command: uvicorn main:app --host 0.0.0.0 --port 8000 --reload - volumes: - - .:/code + gateway: + build: ./ + container_name: gateway + ports: + - "8001:8001" + depends_on: + - mongodb + environment: + DATABASE_URL: postgresql+asyncpg://fastapi:secret@postgres:5432/fastapi_dev + restart: unless-stopped + + auth_service: + build: ./ + container_name: auth_service + ports: + - "8002:8002" + depends_on: + - mongodb + environment: + DATABASE_URL: postgresql+asyncpg://fastapi:secret@postgres:5432/fastapi_dev + restart: unless-stopped + + data_service: + build: ./ + container_name: data_service + ports: + - "8003:8003" + depends_on: + - mongodb + environment: + DATABASE_URL: postgresql+asyncpg://fastapi:secret@postgres:5432/fastapi_dev + restart: unless-stopped + + route_service: + build: ./ + container_name: route_service ports: - - "8000:8000" + - "8004:8004" + depends_on: + - mongodb environment: DATABASE_URL: postgresql+asyncpg://fastapi:secret@postgres:5432/fastapi_dev + restart: unless-stopped + + station_service: + build: ./ + container_name: station_service + ports: + - "8005:8005" depends_on: - - postgres + - mongodb + environment: + DATABASE_URL: postgresql+asyncpg://fastapi:secret@postgres:5432/fastapi_dev + restart: unless-stopped + + user_service: + build: ./ + container_name: user_service + ports: + - "8006:8006" + depends_on: + - mongodb + environment: + DATABASE_URL: postgresql+asyncpg://fastapi:secret@postgres:5432/fastapi_dev + restart: unless-stopped postgres: image: postgres:15-alpine diff --git a/server/route_service/Dockerfile b/server/route_service/Dockerfile index cafbaf3..e132f97 100644 --- a/server/route_service/Dockerfile +++ b/server/route_service/Dockerfile @@ -11,4 +11,4 @@ COPY . . WORKDIR /app/src -CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8001", "--reload"] \ No newline at end of file +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8004", "--reload"] \ No newline at end of file diff --git a/server/station_service/Dockerfile b/server/station_service/Dockerfile index cafbaf3..70d0a72 100644 --- a/server/station_service/Dockerfile +++ b/server/station_service/Dockerfile @@ -11,4 +11,4 @@ COPY . . WORKDIR /app/src -CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8001", "--reload"] \ No newline at end of file +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8005", "--reload"] \ No newline at end of file diff --git a/server/user_service/Dockerfile b/server/user_service/Dockerfile index cafbaf3..e00086c 100644 --- a/server/user_service/Dockerfile +++ b/server/user_service/Dockerfile @@ -11,4 +11,4 @@ COPY . . WORKDIR /app/src -CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8001", "--reload"] \ No newline at end of file +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8006", "--reload"] \ No newline at end of file From e1a1bfcf49a653adfabdac7ee643df7b606573bc Mon Sep 17 00:00:00 2001 From: ZhuchkaTrilesix Date: Fri, 23 May 2025 12:20:16 +0300 Subject: [PATCH 06/14] fix --- server/docker-compose.local.yml | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/server/docker-compose.local.yml b/server/docker-compose.local.yml index 81d30d4..8e4811b 100644 --- a/server/docker-compose.local.yml +++ b/server/docker-compose.local.yml @@ -2,73 +2,74 @@ version: '3.8' services: gateway: - build: ./ + build: ./gateway container_name: gateway ports: - "8001:8001" depends_on: - - mongodb + - postgres environment: DATABASE_URL: postgresql+asyncpg://fastapi:secret@postgres:5432/fastapi_dev restart: unless-stopped auth_service: - build: ./ + build: ./auth_service container_name: auth_service ports: - "8002:8002" depends_on: - - mongodb + - postgres environment: DATABASE_URL: postgresql+asyncpg://fastapi:secret@postgres:5432/fastapi_dev restart: unless-stopped data_service: - build: ./ + build: ./data_service container_name: data_service ports: - "8003:8003" depends_on: - - mongodb + - postgres environment: DATABASE_URL: postgresql+asyncpg://fastapi:secret@postgres:5432/fastapi_dev restart: unless-stopped route_service: - build: ./ + build: ./route_service container_name: route_service ports: - "8004:8004" depends_on: - - mongodb + - postgres environment: DATABASE_URL: postgresql+asyncpg://fastapi:secret@postgres:5432/fastapi_dev restart: unless-stopped station_service: - build: ./ + build: ./station_service container_name: station_service ports: - "8005:8005" depends_on: - - mongodb + - postgres environment: DATABASE_URL: postgresql+asyncpg://fastapi:secret@postgres:5432/fastapi_dev restart: unless-stopped user_service: - build: ./ + build: ./user_service container_name: user_service ports: - "8006:8006" depends_on: - - mongodb + - postgres environment: DATABASE_URL: postgresql+asyncpg://fastapi:secret@postgres:5432/fastapi_dev restart: unless-stopped postgres: image: postgres:15-alpine + container_name: postgres environment: POSTGRES_USER: fastapi POSTGRES_PASSWORD: secret From 7a4abc2d306827ecf6342f5541321a745ef8caa2 Mon Sep 17 00:00:00 2001 From: ZhuchkaTrilesix Date: Fri, 23 May 2025 12:35:04 +0300 Subject: [PATCH 07/14] configuration/ref --- server/{gateway/routers => }/__init__.py | 0 server/auth_service/src/config.py | 2 ++ server/data_service/src/config.py | 2 ++ server/gateway/.dockerignire | 3 +++ server/gateway/.gitkeep | 0 server/gateway/src/__init__.py | 0 server/gateway/src/config.py | 10 ++++++++++ server/gateway/src/dependencies/__init__.py | 0 server/gateway/{ => src}/dependencies/auth.py | 0 server/gateway/{ => src}/main.py | 5 +++-- server/gateway/src/routers/__init__.py | 0 server/gateway/{ => src}/routers/auth.py | 0 server/gateway/{ => src}/routers/route.py | 0 server/gateway/{ => src}/routers/station.py | 0 server/gateway/{ => src}/routers/user.py | 0 server/gateway/{ => src}/utils.py | 0 server/route_service/src/config.py | 2 ++ server/station_service/src/config.py | 2 ++ server/user_service/src/config.py | 2 ++ 19 files changed, 26 insertions(+), 2 deletions(-) rename server/{gateway/routers => }/__init__.py (100%) create mode 100644 server/gateway/.dockerignire create mode 100644 server/gateway/.gitkeep create mode 100644 server/gateway/src/__init__.py create mode 100644 server/gateway/src/config.py create mode 100644 server/gateway/src/dependencies/__init__.py rename server/gateway/{ => src}/dependencies/auth.py (100%) rename server/gateway/{ => src}/main.py (89%) create mode 100644 server/gateway/src/routers/__init__.py rename server/gateway/{ => src}/routers/auth.py (100%) rename server/gateway/{ => src}/routers/route.py (100%) rename server/gateway/{ => src}/routers/station.py (100%) rename server/gateway/{ => src}/routers/user.py (100%) rename server/gateway/{ => src}/utils.py (100%) diff --git a/server/gateway/routers/__init__.py b/server/__init__.py similarity index 100% rename from server/gateway/routers/__init__.py rename to server/__init__.py diff --git a/server/auth_service/src/config.py b/server/auth_service/src/config.py index f3309f5..66fc382 100644 --- a/server/auth_service/src/config.py +++ b/server/auth_service/src/config.py @@ -6,3 +6,5 @@ class CfgBase(ABC): dict: callable = asdict +class PostgresCfg(CfgBase): + url: str = os.getenv("DATABASE_URL") \ No newline at end of file diff --git a/server/data_service/src/config.py b/server/data_service/src/config.py index f3309f5..66fc382 100644 --- a/server/data_service/src/config.py +++ b/server/data_service/src/config.py @@ -6,3 +6,5 @@ class CfgBase(ABC): dict: callable = asdict +class PostgresCfg(CfgBase): + url: str = os.getenv("DATABASE_URL") \ No newline at end of file diff --git a/server/gateway/.dockerignire b/server/gateway/.dockerignire new file mode 100644 index 0000000..74a9069 --- /dev/null +++ b/server/gateway/.dockerignire @@ -0,0 +1,3 @@ +.venv +.idea +__pycache__ \ No newline at end of file diff --git a/server/gateway/.gitkeep b/server/gateway/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/server/gateway/src/__init__.py b/server/gateway/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/server/gateway/src/config.py b/server/gateway/src/config.py new file mode 100644 index 0000000..66fc382 --- /dev/null +++ b/server/gateway/src/config.py @@ -0,0 +1,10 @@ +import os +from abc import ABC +from dataclasses import asdict, dataclass + + +class CfgBase(ABC): + dict: callable = asdict + +class PostgresCfg(CfgBase): + url: str = os.getenv("DATABASE_URL") \ No newline at end of file diff --git a/server/gateway/src/dependencies/__init__.py b/server/gateway/src/dependencies/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/server/gateway/dependencies/auth.py b/server/gateway/src/dependencies/auth.py similarity index 100% rename from server/gateway/dependencies/auth.py rename to server/gateway/src/dependencies/auth.py diff --git a/server/gateway/main.py b/server/gateway/src/main.py similarity index 89% rename from server/gateway/main.py rename to server/gateway/src/main.py index f9fe882..4e4f0ef 100644 --- a/server/gateway/main.py +++ b/server/gateway/src/main.py @@ -1,10 +1,11 @@ -from fastapi import FastAPI, Depends, HTTPException +from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from contextlib import asynccontextmanager import logging import uvicorn -from routers import auth, user, station, route +from server.gateway.src.routers import auth, route +from server.gateway.src.routers import user, station logging.basicConfig( level=logging.INFO, diff --git a/server/gateway/src/routers/__init__.py b/server/gateway/src/routers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/server/gateway/routers/auth.py b/server/gateway/src/routers/auth.py similarity index 100% rename from server/gateway/routers/auth.py rename to server/gateway/src/routers/auth.py diff --git a/server/gateway/routers/route.py b/server/gateway/src/routers/route.py similarity index 100% rename from server/gateway/routers/route.py rename to server/gateway/src/routers/route.py diff --git a/server/gateway/routers/station.py b/server/gateway/src/routers/station.py similarity index 100% rename from server/gateway/routers/station.py rename to server/gateway/src/routers/station.py diff --git a/server/gateway/routers/user.py b/server/gateway/src/routers/user.py similarity index 100% rename from server/gateway/routers/user.py rename to server/gateway/src/routers/user.py diff --git a/server/gateway/utils.py b/server/gateway/src/utils.py similarity index 100% rename from server/gateway/utils.py rename to server/gateway/src/utils.py diff --git a/server/route_service/src/config.py b/server/route_service/src/config.py index f3309f5..66fc382 100644 --- a/server/route_service/src/config.py +++ b/server/route_service/src/config.py @@ -6,3 +6,5 @@ class CfgBase(ABC): dict: callable = asdict +class PostgresCfg(CfgBase): + url: str = os.getenv("DATABASE_URL") \ No newline at end of file diff --git a/server/station_service/src/config.py b/server/station_service/src/config.py index f3309f5..66fc382 100644 --- a/server/station_service/src/config.py +++ b/server/station_service/src/config.py @@ -6,3 +6,5 @@ class CfgBase(ABC): dict: callable = asdict +class PostgresCfg(CfgBase): + url: str = os.getenv("DATABASE_URL") \ No newline at end of file diff --git a/server/user_service/src/config.py b/server/user_service/src/config.py index f3309f5..66fc382 100644 --- a/server/user_service/src/config.py +++ b/server/user_service/src/config.py @@ -6,3 +6,5 @@ class CfgBase(ABC): dict: callable = asdict +class PostgresCfg(CfgBase): + url: str = os.getenv("DATABASE_URL") \ No newline at end of file From cdcf6a5295645ab4e64ccc92ffbd7bafa0f86de4 Mon Sep 17 00:00:00 2001 From: Slava Date: Sat, 24 May 2025 13:34:58 +0300 Subject: [PATCH 08/14] WIP --- server/data_service/Dockerfile | 6 ++- server/data_service/requirements.txt | 42 +++++++++++++++++ server/data_service/src/EV_cars.py | 47 ++++++++++++++++++++ server/data_service/src/database/__init__.py | 0 server/data_service/src/database/cruds.py | 11 +++++ server/data_service/src/database/database.py | 23 ++++++++++ server/data_service/src/database/init_db.py | 10 +++++ server/data_service/src/database/models.py | 14 ++++++ server/data_service/src/database/schemas.py | 7 +++ server/data_service/src/main.py | 13 ++++++ 10 files changed, 171 insertions(+), 2 deletions(-) create mode 100644 server/data_service/src/EV_cars.py create mode 100644 server/data_service/src/database/__init__.py create mode 100644 server/data_service/src/database/cruds.py create mode 100644 server/data_service/src/database/database.py create mode 100644 server/data_service/src/database/init_db.py create mode 100644 server/data_service/src/database/models.py create mode 100644 server/data_service/src/database/schemas.py diff --git a/server/data_service/Dockerfile b/server/data_service/Dockerfile index 79d41a0..f145473 100644 --- a/server/data_service/Dockerfile +++ b/server/data_service/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.13.3-slim +FROM python:3.12.3-slim WORKDIR /app @@ -10,5 +10,7 @@ RUN pip install --no-cache-dir fastapi[standard] && \ COPY . . WORKDIR /app/src +ENV DATABASE_URL=postgresql+asyncpg://fastapi:secret@postgres:5432/fastapi_dev -CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8003", "--reload"] \ No newline at end of file +CMD ["python", "main.py"] +#CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8003", "--reload"] \ No newline at end of file diff --git a/server/data_service/requirements.txt b/server/data_service/requirements.txt index e69de29..46b7e34 100644 --- a/server/data_service/requirements.txt +++ b/server/data_service/requirements.txt @@ -0,0 +1,42 @@ +annotated-types==0.7.0 +anyio==4.9.0 +asyncpg==0.30.0 +certifi==2025.4.26 +click==8.1.8 +colorama==0.4.6 +dnspython==2.7.0 +email_validator==2.2.0 +fastapi==0.115.12 +fastapi-cli==0.0.7 +greenlet==3.2.2 +h11==0.16.0 +httpcore==1.0.9 +httptools==0.6.4 +httpx==0.28.1 +idna==3.10 +Jinja2==3.1.6 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +pydantic==2.11.5 +pydantic_core==2.33.2 +Pygments==2.19.1 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +rich==14.0.0 +rich-toolkit==0.14.6 +shellingham==1.5.4 +sniffio==1.3.1 +SQLAlchemy==2.0.41 +starlette==0.46.2 +typer==0.15.4 +typing-inspection==0.4.1 +typing_extensions==4.13.2 +uvicorn==0.34.2 +watchfiles==1.0.5 +websockets==15.0.1 +psycopg2-binary +beautifulsoup4 +requests +SQLAlchemy \ No newline at end of file diff --git a/server/data_service/src/EV_cars.py b/server/data_service/src/EV_cars.py new file mode 100644 index 0000000..a71f110 --- /dev/null +++ b/server/data_service/src/EV_cars.py @@ -0,0 +1,47 @@ +from bs4 import BeautifulSoup +from database.schemas import CarCreate +import requests + +def parse_ev_cars() -> list: + result = [] + + for page_idx in range(0, 20): + url_page = f"https://ev-database.org/#group=vehicle-group&rs-pr=10000_100000&rs-er=0_1000&rs-ld=0_1000&rs-ac=2_23&rs-dcfc=0_300&rs-ub=10_200&rs-tw=0_2500&rs-ef=100_350&rs-sa=-1_5&rs-w=1000_3500&rs-c=0_5000&rs-y=2010_2030&s=1&p={page_idx}-50" + + headers = { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36' + } + + try: + response = requests.get(url_page, headers=headers) + soup = BeautifulSoup(response.text, 'html.parser') + + cars_list = soup.find_all('div', {'class': 'list-item', 'data-jplist-item': ''}) + + for idx, car in enumerate(cars_list): + title_link = car.find('a', class_='title') + if title_link: + spans = title_link.find_all('span') + full_name = ' '.join(span.text.strip() for span in spans) + else: + print(f'Отсуствует название для {idx + 1} машины') + + specs = car.find('div', class_='specs') + + consumpting = specs.find('div', {'data-tooltip': "Efficiency under standardized conditions"}).find('span', class_='efficiency').text.strip() + battery_capacity = specs.find('div', {'data-tooltip': "Useable battery capacity."}).find('span', class_='battery_p').text.strip() + hidden_info = car.find('div', class_='hidden') + type_charger = hidden_info.find('span', attrs={'title': lambda x: x and 'plug' in x}).text.strip() + + result.append(CarCreate(name=full_name, + battery_capacity=battery_capacity, + consumpting=consumpting, + type_charger=type_charger)) + except Exception as e: + print(f"Ошибка на странице {page_idx}: {e}") + + print(f"Была собрана информация о {len(cars_list)} автомобилях") + return result + +if __name__ == "__main__": + print(parse_ev_cars()) \ No newline at end of file diff --git a/server/data_service/src/database/__init__.py b/server/data_service/src/database/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/server/data_service/src/database/cruds.py b/server/data_service/src/database/cruds.py new file mode 100644 index 0000000..6775c4b --- /dev/null +++ b/server/data_service/src/database/cruds.py @@ -0,0 +1,11 @@ +from database.schemas import Carpy +from database.models import Car + +from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy.future import select + +async def push_cars_data(db: AsyncSession, car: Carpy) -> Car: + new_car = Car(id=car.id, ) + db.add(new_car) + await db.flush() + return new_car \ No newline at end of file diff --git a/server/data_service/src/database/database.py b/server/data_service/src/database/database.py new file mode 100644 index 0000000..77ce6f2 --- /dev/null +++ b/server/data_service/src/database/database.py @@ -0,0 +1,23 @@ +from typing import Iterator, Any, AsyncGenerator +from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker +from config import postgres + +engine = create_async_engine( + url=postgres.url, + future=True, + echo=False +) + +async_session = async_sessionmaker(engine, expire_on_commit=False, class_=AsyncSession) + + +async def get_db() -> AsyncGenerator[AsyncSession, None]: + session: AsyncSession = async_session() + try: + yield session + await session.commit() + except Exception as exc: + await session.rollback() + raise exc + finally: + await session.close() \ No newline at end of file diff --git a/server/data_service/src/database/init_db.py b/server/data_service/src/database/init_db.py new file mode 100644 index 0000000..6eeb2e8 --- /dev/null +++ b/server/data_service/src/database/init_db.py @@ -0,0 +1,10 @@ +import asyncio +from database.database import engine +from models import Base + +async def init_models(): + async with engine.begin() as conn: + await conn.run_sync(Base.metadata.create_all) + +if __name__ == "__main__": + asyncio.run(init_models()) \ No newline at end of file diff --git a/server/data_service/src/database/models.py b/server/data_service/src/database/models.py new file mode 100644 index 0000000..5f769ad --- /dev/null +++ b/server/data_service/src/database/models.py @@ -0,0 +1,14 @@ +from sqlalchemy import String, Integer, JSON +from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column + +class Base(DeclarativeBase): + pass + +class Car(Base): + __tablename__ = "cars" + + id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) + name: Mapped[str] = mapped_column(String, nullable=False) + battery_capacity: Mapped[str] = mapped_column(String, nullable=False) + consumpting: Mapped[str] = mapped_column(String, nullable=False) + type_charger: Mapped[str] = mapped_column(String, nullable=False) \ No newline at end of file diff --git a/server/data_service/src/database/schemas.py b/server/data_service/src/database/schemas.py new file mode 100644 index 0000000..46d4a6a --- /dev/null +++ b/server/data_service/src/database/schemas.py @@ -0,0 +1,7 @@ +from pydantic import BaseModel + +class CarCreate(BaseModel): + name: str + battery_capacity: str + consumpting: str + type_charger: str \ No newline at end of file diff --git a/server/data_service/src/main.py b/server/data_service/src/main.py index e69de29..1319d94 100644 --- a/server/data_service/src/main.py +++ b/server/data_service/src/main.py @@ -0,0 +1,13 @@ +from fastapi import FastAPI, Depends +from sqlalchemy.ext.asyncio import AsyncSession +from database.database import get_db +from database.models import Base +from EV_cars import parse_ev_cars + +#TODO Base.metadata.create_all() я не помню где и как в этой структуре делать + +app = FastAPI() + +@app.on_event("startup") +async def startup(): + await init_models() \ No newline at end of file From fc648a96b4124ffb4f9ee2a511913c839f189491 Mon Sep 17 00:00:00 2001 From: OrusskiyVR Date: Mon, 26 May 2025 10:04:21 +0300 Subject: [PATCH 09/14] WIP --- server/data_service/src/database/cruds.py | 28 ++++++++++++++++++--- server/data_service/src/database/schemas.py | 7 ++++++ server/data_service/src/main.py | 23 +++++++++++++---- 3 files changed, 49 insertions(+), 9 deletions(-) diff --git a/server/data_service/src/database/cruds.py b/server/data_service/src/database/cruds.py index 6775c4b..c91c6b1 100644 --- a/server/data_service/src/database/cruds.py +++ b/server/data_service/src/database/cruds.py @@ -1,11 +1,31 @@ -from database.schemas import Carpy +from database.schemas import CarCreate, CarGet from database.models import Car from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.future import select -async def push_cars_data(db: AsyncSession, car: Carpy) -> Car: - new_car = Car(id=car.id, ) +async def add_car(db: AsyncSession, car: CarCreate) -> Car: + new_car = Car(name=car.name, + battery_capacity=car.battery_capacity, + consumpting=car.consumpting, + type_charger=car.type_charger) db.add(new_car) await db.flush() - return new_car \ No newline at end of file + return new_car + +async def add_cars(db: AsyncSession, cars: list[CarCreate]) -> list[int]: + car_objects = [ + Car(name=car.name, + battery_capacity=car.battery_capacity, + consumpting=car.consumpting, + type_charger=car.type_charger + ) + for car in cars + ] + + db.add_all(car_objects) + await db.flush() + return car_objects + +async def get_car(db: AsyncSession) -> CarGet: + pass diff --git a/server/data_service/src/database/schemas.py b/server/data_service/src/database/schemas.py index 46d4a6a..463b68b 100644 --- a/server/data_service/src/database/schemas.py +++ b/server/data_service/src/database/schemas.py @@ -1,6 +1,13 @@ from pydantic import BaseModel class CarCreate(BaseModel): + name: str + battery_capacity: str + consumpting: str + type_charger: str + +class CarGet(BaseModel): + id: int name: str battery_capacity: str consumpting: str diff --git a/server/data_service/src/main.py b/server/data_service/src/main.py index 1319d94..1b94965 100644 --- a/server/data_service/src/main.py +++ b/server/data_service/src/main.py @@ -3,11 +3,24 @@ from database.database import get_db from database.models import Base from EV_cars import parse_ev_cars +from database.init_db import init_models +from contextlib import asynccontextmanager +from database import cruds +from database.schemas import CarCreate -#TODO Base.metadata.create_all() я не помню где и как в этой структуре делать -app = FastAPI() +@asynccontextmanager +async def lifespan(app: FastAPI): + try: + print("Starting up") + await init_models() # Код стартапа + yield + finally: + print("Shutting down...") -@app.on_event("startup") -async def startup(): - await init_models() \ No newline at end of file +app = FastAPI(lifespan=lifespan) + + +@app.post("/cars", response_model=CarCreate) +async def add_car(car: CarCreate, db: AsyncSession = Depends(get_db)): + return await cruds.add_car(db, car) \ No newline at end of file From 415cf05fe48ab2370dfdfc94ea6badbe41fedc15 Mon Sep 17 00:00:00 2001 From: OrusskiyVR Date: Mon, 26 May 2025 10:22:36 +0300 Subject: [PATCH 10/14] WIP --- server/data_service/src/database/cruds.py | 15 +++++++++++++-- server/data_service/src/main.py | 19 +++++++++++++++---- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/server/data_service/src/database/cruds.py b/server/data_service/src/database/cruds.py index c91c6b1..b66b463 100644 --- a/server/data_service/src/database/cruds.py +++ b/server/data_service/src/database/cruds.py @@ -27,5 +27,16 @@ async def add_cars(db: AsyncSession, cars: list[CarCreate]) -> list[int]: await db.flush() return car_objects -async def get_car(db: AsyncSession) -> CarGet: - pass +async def get_car_by_name(db: AsyncSession, name: str) -> CarGet | None: + stmt = select(Car).where(Car.name == name) + + result = await db.execute(stmt) + return result.scalar_one_or_none() + + +async def get_car(db: AsyncSession, id: int) -> CarGet | None: + stmt = select(Car).where(Car.id == id) + + result = await db.execute(stmt) + return result.scalar_one_or_none() + diff --git a/server/data_service/src/main.py b/server/data_service/src/main.py index 1b94965..088e9ee 100644 --- a/server/data_service/src/main.py +++ b/server/data_service/src/main.py @@ -1,12 +1,11 @@ from fastapi import FastAPI, Depends from sqlalchemy.ext.asyncio import AsyncSession from database.database import get_db -from database.models import Base from EV_cars import parse_ev_cars from database.init_db import init_models from contextlib import asynccontextmanager from database import cruds -from database.schemas import CarCreate +from database.schemas import CarCreate, CarGet @asynccontextmanager @@ -21,6 +20,18 @@ async def lifespan(app: FastAPI): app = FastAPI(lifespan=lifespan) -@app.post("/cars", response_model=CarCreate) +@app.post("/car", response_model=CarCreate) async def add_car(car: CarCreate, db: AsyncSession = Depends(get_db)): - return await cruds.add_car(db, car) \ No newline at end of file + return await cruds.add_car(db, car) + +@app.post("/cars", response_model=list[CarCreate]) +async def add_cars(cars: list[CarCreate], db: AsyncSession = Depends(get_db)): + return await cruds.add_cars(db, cars) + +@app.get("/car", response_model=CarGet) +async def get_car_by_name(name: str, db: AsyncSession = Depends(get_db)): + return await cruds.get_car_by_name(db, name) + +@app.get("/car/{car_id}", response_model=CarGet) +async def get_car(car_id: int, db: AsyncSession = Depends(get_db)): + return await cruds.get_car(db, car_id) \ No newline at end of file From 61516d926e6e61a45c320a8e27cef4cfc9eb17b3 Mon Sep 17 00:00:00 2001 From: OrusskiyVR Date: Mon, 26 May 2025 13:08:12 +0300 Subject: [PATCH 11/14] WIP --- server/data_service/Dockerfile | 4 +- server/data_service/src/database/cruds.py | 9 ++- server/data_service/src/database/database.py | 4 +- server/data_service/src/database/init_db.py | 2 +- server/data_service/src/database/models.py | 2 +- server/data_service/src/main.py | 18 ++++-- server/station_service/database/__init__.py | 0 server/station_service/database/cruds.py | 35 +++++++++++ server/station_service/database/database.py | 22 +++++++ server/station_service/database/init_db.py | 10 +++ server/station_service/database/models.py | 15 +++++ server/station_service/database/schemas.py | 16 +++++ server/station_service/src/main.py | 37 +++++++++++ server/station_service/src/stations.py | 65 ++++++++++++++++++++ 14 files changed, 225 insertions(+), 14 deletions(-) create mode 100644 server/station_service/database/__init__.py create mode 100644 server/station_service/database/cruds.py create mode 100644 server/station_service/database/database.py create mode 100644 server/station_service/database/init_db.py create mode 100644 server/station_service/database/models.py create mode 100644 server/station_service/database/schemas.py create mode 100644 server/station_service/src/stations.py diff --git a/server/data_service/Dockerfile b/server/data_service/Dockerfile index f145473..e8c59ce 100644 --- a/server/data_service/Dockerfile +++ b/server/data_service/Dockerfile @@ -10,7 +10,5 @@ RUN pip install --no-cache-dir fastapi[standard] && \ COPY . . WORKDIR /app/src -ENV DATABASE_URL=postgresql+asyncpg://fastapi:secret@postgres:5432/fastapi_dev -CMD ["python", "main.py"] -#CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8003", "--reload"] \ No newline at end of file +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8003", "--reload"] \ No newline at end of file diff --git a/server/data_service/src/database/cruds.py b/server/data_service/src/database/cruds.py index b66b463..70aa228 100644 --- a/server/data_service/src/database/cruds.py +++ b/server/data_service/src/database/cruds.py @@ -13,7 +13,7 @@ async def add_car(db: AsyncSession, car: CarCreate) -> Car: await db.flush() return new_car -async def add_cars(db: AsyncSession, cars: list[CarCreate]) -> list[int]: +async def add_cars(db: AsyncSession, cars: list[CarCreate]) -> list[Car]: car_objects = [ Car(name=car.name, battery_capacity=car.battery_capacity, @@ -27,13 +27,18 @@ async def add_cars(db: AsyncSession, cars: list[CarCreate]) -> list[int]: await db.flush() return car_objects +async def get_all_cars(db: AsyncSession) -> list[CarGet]: + stmt = select(Car) + + result = await db.execute(stmt) + return result.scalars().all() + async def get_car_by_name(db: AsyncSession, name: str) -> CarGet | None: stmt = select(Car).where(Car.name == name) result = await db.execute(stmt) return result.scalar_one_or_none() - async def get_car(db: AsyncSession, id: int) -> CarGet | None: stmt = select(Car).where(Car.id == id) diff --git a/server/data_service/src/database/database.py b/server/data_service/src/database/database.py index 77ce6f2..a61a9fe 100644 --- a/server/data_service/src/database/database.py +++ b/server/data_service/src/database/database.py @@ -1,9 +1,9 @@ from typing import Iterator, Any, AsyncGenerator from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker -from config import postgres +from config import PostgresCfg engine = create_async_engine( - url=postgres.url, + url=PostgresCfg.url, future=True, echo=False ) diff --git a/server/data_service/src/database/init_db.py b/server/data_service/src/database/init_db.py index 6eeb2e8..65ccf55 100644 --- a/server/data_service/src/database/init_db.py +++ b/server/data_service/src/database/init_db.py @@ -1,6 +1,6 @@ import asyncio from database.database import engine -from models import Base +from database.models import Base async def init_models(): async with engine.begin() as conn: diff --git a/server/data_service/src/database/models.py b/server/data_service/src/database/models.py index 5f769ad..4f80825 100644 --- a/server/data_service/src/database/models.py +++ b/server/data_service/src/database/models.py @@ -8,7 +8,7 @@ class Car(Base): __tablename__ = "cars" id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) - name: Mapped[str] = mapped_column(String, nullable=False) + name: Mapped[str] = mapped_column(String, index=True, nullable=False) battery_capacity: Mapped[str] = mapped_column(String, nullable=False) consumpting: Mapped[str] = mapped_column(String, nullable=False) type_charger: Mapped[str] = mapped_column(String, nullable=False) \ No newline at end of file diff --git a/server/data_service/src/main.py b/server/data_service/src/main.py index 088e9ee..95d73e2 100644 --- a/server/data_service/src/main.py +++ b/server/data_service/src/main.py @@ -1,4 +1,4 @@ -from fastapi import FastAPI, Depends +from fastapi import FastAPI, Depends, status from sqlalchemy.ext.asyncio import AsyncSession from database.database import get_db from EV_cars import parse_ev_cars @@ -12,7 +12,11 @@ async def lifespan(app: FastAPI): try: print("Starting up") - await init_models() # Код стартапа + app.add_middleware( + allow_methods=["GET", "POST"], + allow_origins=["*"] + ) + await init_models() yield finally: print("Shutting down...") @@ -20,11 +24,11 @@ async def lifespan(app: FastAPI): app = FastAPI(lifespan=lifespan) -@app.post("/car", response_model=CarCreate) +@app.post("/car", response_model=CarCreate, status_code=status.HTTP_201_CREATED) async def add_car(car: CarCreate, db: AsyncSession = Depends(get_db)): return await cruds.add_car(db, car) -@app.post("/cars", response_model=list[CarCreate]) +@app.post("/cars", response_model=list[CarCreate], status_code=status.HTTP_201_CREATED) async def add_cars(cars: list[CarCreate], db: AsyncSession = Depends(get_db)): return await cruds.add_cars(db, cars) @@ -34,4 +38,8 @@ async def get_car_by_name(name: str, db: AsyncSession = Depends(get_db)): @app.get("/car/{car_id}", response_model=CarGet) async def get_car(car_id: int, db: AsyncSession = Depends(get_db)): - return await cruds.get_car(db, car_id) \ No newline at end of file + return await cruds.get_car(db, car_id) + +@app.get("/cars", response_model=CarGet) +async def get_cars(db: AsyncSession = Depends(get_db)): + return await cruds.get_all_cars(db) \ No newline at end of file diff --git a/server/station_service/database/__init__.py b/server/station_service/database/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/server/station_service/database/cruds.py b/server/station_service/database/cruds.py new file mode 100644 index 0000000..61a0e88 --- /dev/null +++ b/server/station_service/database/cruds.py @@ -0,0 +1,35 @@ +from database.schemas import StationCreate, StationGet +from database.models import Station + +from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy.future import select + +async def add_station(db: AsyncSession, station: StationCreate) -> Station: + new_station = Station(name=station.name, + latitude=station.latitude, + longtitude=station.longtitude, + connection_type=station.connection_type, + power_kw=station.power_kw) + db.add(new_station) + await db.flush() + return new_station + +async def add_stations(db: AsyncSession, stations: list[StationCreate]) -> list[Station]: + station_objects = [ + Station(name=station.name, + latitude=station.latitude, + longtitude=station.longtitude, + connection_type=station.connection_type, + power_kw=station.power_kw) + for station in stations + ] + + db.add_all(station_objects) + await db.flush() + return station_objects + +async def get_all_stations(db: AsyncSession) -> list[StationGet]: + stmt = select(Station) + + result = await db.execute(stmt) + return result.scalars().all() diff --git a/server/station_service/database/database.py b/server/station_service/database/database.py new file mode 100644 index 0000000..4db193e --- /dev/null +++ b/server/station_service/database/database.py @@ -0,0 +1,22 @@ +from typing import AsyncGenerator +from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker +from config import PostgresCfg + +engine = create_async_engine( + url=PostgresCfg.url, + future=True, + echo=False +) + +async_session = async_sessionmaker(engine, expire_on_commit=False, class_=AsyncSession) + +async def get_db() -> AsyncGenerator[AsyncSession, None]: + session: AsyncSession = async_session() + try: + yield session + await session.commit() + except Exception as exc: + await session.rollback() + raise exc + finally: + await session.close() \ No newline at end of file diff --git a/server/station_service/database/init_db.py b/server/station_service/database/init_db.py new file mode 100644 index 0000000..65ccf55 --- /dev/null +++ b/server/station_service/database/init_db.py @@ -0,0 +1,10 @@ +import asyncio +from database.database import engine +from database.models import Base + +async def init_models(): + async with engine.begin() as conn: + await conn.run_sync(Base.metadata.create_all) + +if __name__ == "__main__": + asyncio.run(init_models()) \ No newline at end of file diff --git a/server/station_service/database/models.py b/server/station_service/database/models.py new file mode 100644 index 0000000..3dcd8af --- /dev/null +++ b/server/station_service/database/models.py @@ -0,0 +1,15 @@ +from sqlalchemy import String, Integer, Float +from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column + +class Base(DeclarativeBase): + pass + +class Station(Base): + __tablename__ = "stations" + + id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) + name: Mapped[str] = mapped_column(String, index=True, nullable=False) + latitude: Mapped[float] = mapped_column(Float, index=True, nullable=False) + longtitude: Mapped[float] = mapped_column(Float, index=True, nullable=False) + connection_type: Mapped[str] = mapped_column(String, index=True, nullable=False) + power_kw: Mapped[int] = mapped_column(Integer, nullable=False) \ No newline at end of file diff --git a/server/station_service/database/schemas.py b/server/station_service/database/schemas.py new file mode 100644 index 0000000..5db06f0 --- /dev/null +++ b/server/station_service/database/schemas.py @@ -0,0 +1,16 @@ +from pydantic import BaseModel + +class StationCreate(BaseModel): + name: str + latitude: float + longtitude: float + connection_type: str + power_kw: int + +class StationGet(BaseModel): + id: int + name: str + latitude: float + longtitude: float + connection_type: str + power_kw: int \ No newline at end of file diff --git a/server/station_service/src/main.py b/server/station_service/src/main.py index e69de29..416c618 100644 --- a/server/station_service/src/main.py +++ b/server/station_service/src/main.py @@ -0,0 +1,37 @@ +from fastapi import FastAPI, Depends, status +from sqlalchemy.ext.asyncio import AsyncSession +from database.database import get_db +from stations import get_all_stations +from database.init_db import init_models +from contextlib import asynccontextmanager +from database import cruds +from database.schemas import StationCreate, StationGet + + +@asynccontextmanager +async def lifespan(app: FastAPI): + try: + print("Starting up") + app.add_middleware( + allow_methods=["GET", "POST"], + allow_origins=["*"] + ) + await init_models() + yield + finally: + print("Shutting down...") + +app = FastAPI(lifespan=lifespan) + + +@app.post("/station", response_model=StationCreate, status_code=status.HTTP_201_CREATED) +async def add_station(station: StationCreate, db: AsyncSession = Depends(get_db)): + return await cruds.add_station(db, station) + +@app.post("/stations", response_model=list[StationCreate], status_code=status.HTTP_201_CREATED) +async def add_statios(stations: list[StationCreate], db: AsyncSession = Depends(get_db)): + return await cruds.add_stations(db, stations) + +@app.get("/stations", response_model=StationGet) +async def get_stations(db: AsyncSession = Depends(get_db)): + return await cruds.get_all_stations(db) \ No newline at end of file diff --git a/server/station_service/src/stations.py b/server/station_service/src/stations.py new file mode 100644 index 0000000..330bbb5 --- /dev/null +++ b/server/station_service/src/stations.py @@ -0,0 +1,65 @@ +import requests +from database.schemas import StationCreate +import os + +API_BASE_URL = "https://api.openchargemap.io/v3" +API_KEY = os.getenv("API_KEY") + +URL_PARAMS = { + 'output': 'json', + 'key': API_KEY + } + +def get_ref_data(): + new_url = f"{API_BASE_URL}/referencedata?{'&'.join(f'{key}={value}' for key, value in URL_PARAMS.items())}" + response = requests.get(new_url) + response.raise_for_status() + data = response.json() + + conn_types = data['ConnectionTypes'] + countries = data['Countries'] + return conn_types, countries + +def get_good_charg_ids(): + GOOD_CHARG = ['CHAdeMO', 'CCS (Type 1)', 'CCS (Type 2)'] + + pass + +def get_all_info_by_country(country_id: int): + result = [] + new_url = f"{API_BASE_URL}/poi?{'&'.join(f'{key}={value}' for key, value in URL_PARAMS.items())}&countryid={country_id}&connectiontypeid=2, 32, 33" + response = requests.get(new_url) + response.raise_for_status() + data = response.json() + for station in data: + result.append(get_use_info_from_json(station=station)) + return result + + +def get_use_info_from_json(station: dict) -> StationCreate: + address_info = station['AddressInfo'] + name = address_info['Title'] + latitude = address_info['Latitude'] + longtitude = address_info['Longitude'] + connections_info = station['Connections'] + + for connection in connections_info: + connection_type = connection['ConnectionType'] + power_kw = connection['PowerKW'] + + return StationCreate(name=name, + latitude=latitude, + longtitude=longtitude, + connection_type=connection_type, + power_kw=power_kw + ) + +def all_stations_info(): + stations = [] + + for country_id in range(251): + country_stations = get_all_info_by_country(country_id=country_id) + stations.append(country_stations) + return stations + +stations = all_stations_info() \ No newline at end of file From 1ecf9f3a41b5c1a515f32a5a44f0f98dde391b18 Mon Sep 17 00:00:00 2001 From: OrusskiyVR Date: Mon, 26 May 2025 15:22:53 +0300 Subject: [PATCH 12/14] WIP --- server/auth_service/.dockerignore | 3 -- server/auth_service/Dockerfile | 14 ------ server/auth_service/src/config.py | 10 ----- server/auth_service/src/main.py | 0 server/data_service/src/database/models.py | 2 +- .../{ => src}/database/__init__.py | 0 .../{ => src}/database/cruds.py | 0 .../{ => src}/database/database.py | 0 .../{ => src}/database/init_db.py | 0 .../{ => src}/database/models.py | 0 .../{ => src}/database/schemas.py | 0 .../src/database/__init__.py} | 0 server/user_service/src/database/cruds.py | 45 +++++++++++++++++++ server/user_service/src/database/database.py | 23 ++++++++++ .../src/database/init_db.py} | 0 server/user_service/src/database/models.py | 13 ++++++ server/user_service/src/database/schemas.py | 16 +++++++ server/user_service/src/main.py | 37 +++++++++++++++ 18 files changed, 135 insertions(+), 28 deletions(-) delete mode 100644 server/auth_service/.dockerignore delete mode 100644 server/auth_service/Dockerfile delete mode 100644 server/auth_service/src/config.py delete mode 100644 server/auth_service/src/main.py rename server/station_service/{ => src}/database/__init__.py (100%) rename server/station_service/{ => src}/database/cruds.py (100%) rename server/station_service/{ => src}/database/database.py (100%) rename server/station_service/{ => src}/database/init_db.py (100%) rename server/station_service/{ => src}/database/models.py (100%) rename server/station_service/{ => src}/database/schemas.py (100%) rename server/{auth_service/.gitkeep => user_service/src/database/__init__.py} (100%) create mode 100644 server/user_service/src/database/cruds.py create mode 100644 server/user_service/src/database/database.py rename server/{auth_service/requirements.txt => user_service/src/database/init_db.py} (100%) create mode 100644 server/user_service/src/database/models.py create mode 100644 server/user_service/src/database/schemas.py diff --git a/server/auth_service/.dockerignore b/server/auth_service/.dockerignore deleted file mode 100644 index e31985f..0000000 --- a/server/auth_service/.dockerignore +++ /dev/null @@ -1,3 +0,0 @@ -.venv -__pycache__ -.idea \ No newline at end of file diff --git a/server/auth_service/Dockerfile b/server/auth_service/Dockerfile deleted file mode 100644 index c41bb0c..0000000 --- a/server/auth_service/Dockerfile +++ /dev/null @@ -1,14 +0,0 @@ -FROM python:3.13.3-slim - -WORKDIR /app - -COPY requirements.txt . - -RUN pip install --no-cache-dir fastapi[standard] && \ - pip install --no-cache-dir -r requirements.txt - -COPY . . - -WORKDIR /app/src - -CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8002", "--reload"] \ No newline at end of file diff --git a/server/auth_service/src/config.py b/server/auth_service/src/config.py deleted file mode 100644 index 66fc382..0000000 --- a/server/auth_service/src/config.py +++ /dev/null @@ -1,10 +0,0 @@ -import os -from abc import ABC -from dataclasses import asdict, dataclass - - -class CfgBase(ABC): - dict: callable = asdict - -class PostgresCfg(CfgBase): - url: str = os.getenv("DATABASE_URL") \ No newline at end of file diff --git a/server/auth_service/src/main.py b/server/auth_service/src/main.py deleted file mode 100644 index e69de29..0000000 diff --git a/server/data_service/src/database/models.py b/server/data_service/src/database/models.py index 4f80825..1b00c9a 100644 --- a/server/data_service/src/database/models.py +++ b/server/data_service/src/database/models.py @@ -1,4 +1,4 @@ -from sqlalchemy import String, Integer, JSON +from sqlalchemy import String from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column class Base(DeclarativeBase): diff --git a/server/station_service/database/__init__.py b/server/station_service/src/database/__init__.py similarity index 100% rename from server/station_service/database/__init__.py rename to server/station_service/src/database/__init__.py diff --git a/server/station_service/database/cruds.py b/server/station_service/src/database/cruds.py similarity index 100% rename from server/station_service/database/cruds.py rename to server/station_service/src/database/cruds.py diff --git a/server/station_service/database/database.py b/server/station_service/src/database/database.py similarity index 100% rename from server/station_service/database/database.py rename to server/station_service/src/database/database.py diff --git a/server/station_service/database/init_db.py b/server/station_service/src/database/init_db.py similarity index 100% rename from server/station_service/database/init_db.py rename to server/station_service/src/database/init_db.py diff --git a/server/station_service/database/models.py b/server/station_service/src/database/models.py similarity index 100% rename from server/station_service/database/models.py rename to server/station_service/src/database/models.py diff --git a/server/station_service/database/schemas.py b/server/station_service/src/database/schemas.py similarity index 100% rename from server/station_service/database/schemas.py rename to server/station_service/src/database/schemas.py diff --git a/server/auth_service/.gitkeep b/server/user_service/src/database/__init__.py similarity index 100% rename from server/auth_service/.gitkeep rename to server/user_service/src/database/__init__.py diff --git a/server/user_service/src/database/cruds.py b/server/user_service/src/database/cruds.py new file mode 100644 index 0000000..16ab3ae --- /dev/null +++ b/server/user_service/src/database/cruds.py @@ -0,0 +1,45 @@ +from database.schemas import UserCreate, UserGet, UserInDB +from database.models import User +from passlib.context import CryptContext +from fastapi import HTTPException +from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy.future import select + +pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") +async def add_user(db: AsyncSession, user: UserCreate) -> UserGet: + hashed_password = pwd_context.hash(user.password.get_secret_value()) + + new_user = UserInDB(login=user.login, + password_hash=hashed_password, + car_id=user.car_id + ) + + db.add(new_user) + db.flush() + return UserGet(id=new_user.id, + login=new_user.login, + car_id=new_user.car_id) + +async def get_user(db: AsyncSession, login: str) -> UserGet | None: + stmt = select(User).where(User.login == login) + result = await db.execute(stmt) + + return result.scalar_one_or_none() + +async def update_user_car(db: AsyncSession, login: int, new_car: int | None) -> UserGet: + query = select(UserInDB).where(UserInDB.login == login) + result = await db.execute(statement=query) + user = result.scalar_one_or_none() + + if not user: + raise HTTPException(status_code=404, detail="Пользователь не найден") + + user.car_id = new_car + + await db.flush() + + return UserGet( + id=user.id, + login=user.login, + car_id=user.car_id + ) \ No newline at end of file diff --git a/server/user_service/src/database/database.py b/server/user_service/src/database/database.py new file mode 100644 index 0000000..4c5a833 --- /dev/null +++ b/server/user_service/src/database/database.py @@ -0,0 +1,23 @@ +from typing import AsyncGenerator +from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker +from config import PostgresCfg + +engine = create_async_engine( + url=PostgresCfg.url, + future=True, + echo=False +) + +async_session = async_sessionmaker(engine, expire_on_commit=False, class_=AsyncSession) + + +async def get_db() -> AsyncGenerator[AsyncSession, None]: + session: AsyncSession = async_session() + try: + yield session + await session.commit() + except Exception as exc: + await session.rollback() + raise exc + finally: + await session.close() \ No newline at end of file diff --git a/server/auth_service/requirements.txt b/server/user_service/src/database/init_db.py similarity index 100% rename from server/auth_service/requirements.txt rename to server/user_service/src/database/init_db.py diff --git a/server/user_service/src/database/models.py b/server/user_service/src/database/models.py new file mode 100644 index 0000000..3af768e --- /dev/null +++ b/server/user_service/src/database/models.py @@ -0,0 +1,13 @@ +from sqlalchemy import String, Integer +from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship + +class Base(DeclarativeBase): + pass + +class User(Base): + __tablename__ = "users" + + id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) + login: Mapped[str] = mapped_column(String, unique=True, index=True, nullable=False) + password_hash: Mapped[str] = mapped_column(String(255), nullable=False) + car_id: Mapped[int | None ] = mapped_column(Integer, nullable=True) \ No newline at end of file diff --git a/server/user_service/src/database/schemas.py b/server/user_service/src/database/schemas.py new file mode 100644 index 0000000..ab02cf5 --- /dev/null +++ b/server/user_service/src/database/schemas.py @@ -0,0 +1,16 @@ +from pydantic import BaseModel, SecretStr + +class UserCreate(BaseModel): + login: str + password: SecretStr + car_id: int | None + +class UserInDB(BaseModel): + login: str + password_hash: str + car_id: int | None + +class UserGet(BaseModel): + id: int + login: str + car_id: str | None \ No newline at end of file diff --git a/server/user_service/src/main.py b/server/user_service/src/main.py index e69de29..51bb5e5 100644 --- a/server/user_service/src/main.py +++ b/server/user_service/src/main.py @@ -0,0 +1,37 @@ +from fastapi import FastAPI, Depends, status +from sqlalchemy.ext.asyncio import AsyncSession +from database.database import get_db +from database.init_db import init_models +from contextlib import asynccontextmanager +from database import cruds +from database.schemas import UserCreate, UserGet + + +@asynccontextmanager +async def lifespan(app: FastAPI): + try: + print("Starting up") + app.add_middleware( + allow_methods=["GET", "POST", "PATCH"], + allow_origins=["*"] + ) + await init_models() + yield + finally: + print("Shutting down...") + +app = FastAPI(lifespan=lifespan) + +@app.post("/register", response_model=UserGet, status_code=status.HTTP_201_CREATED) +async def add_user(user: UserCreate, db: AsyncSession = Depends(get_db)) + return await cruds.add_user(db, user) + +#@app.post("/login") + +@app.get("/user", response_model=UserGet) +async def get_user(login: str, db: AsyncSession = Depends(get_db)): + return await cruds.get_user(db, login) + +@app.patch("/user/car", response_model=UserGet) +async def update_car(login: str, new_car: int, db: AsyncSession = Depends(get_db)): + return await cruds.update_user_car(db, login, new_car) From 71d7a587ddd5579907d4e892e9f5a238aec0c359 Mon Sep 17 00:00:00 2001 From: OrusskiyVR Date: Mon, 26 May 2025 16:13:28 +0300 Subject: [PATCH 13/14] WIP auth service --- server/user_service/src/auth/auth.py | 27 +++++++++++++++++++++ server/user_service/src/database/cruds.py | 11 ++++++--- server/user_service/src/database/schemas.py | 17 ++++++++++++- server/user_service/src/main.py | 19 +++++++++++---- 4 files changed, 64 insertions(+), 10 deletions(-) create mode 100644 server/user_service/src/auth/auth.py diff --git a/server/user_service/src/auth/auth.py b/server/user_service/src/auth/auth.py new file mode 100644 index 0000000..4631314 --- /dev/null +++ b/server/user_service/src/auth/auth.py @@ -0,0 +1,27 @@ +from passlib.context import CryptContext +from sqlalchemy.ext.asyncio import AsyncSession +from datetime import datetime, timedelta, timezone + +from jose import jwt + +from database.schemas import UserInDB, TokenData +from database import cruds + +pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") + +class AuthService: + def __init__(self, secret_key: str, algorithm: str = "HS256"): + self.SECRET_KEY = secret_key + self.ALGORITHM = algorithm + + async def authenticate_user(self, db: AsyncSession, login: str, password: str) -> UserInDB | None: + user = await cruds.get_user(db, login) + if not user or not pwd_context.verify(password, user.password_hash): + return None + return user + + def create_access_token(self, data: TokenData, expires_delta: timedelta) -> str: + to_encode = data.model_dump() + expire = datetime.now(timezone.utc) + expires_delta + to_encode.update({"exp": expire}) + return jwt.encode(to_encode, self.SECRET_KEY, algorithm=self.ALGORITHM) \ No newline at end of file diff --git a/server/user_service/src/database/cruds.py b/server/user_service/src/database/cruds.py index 16ab3ae..146da56 100644 --- a/server/user_service/src/database/cruds.py +++ b/server/user_service/src/database/cruds.py @@ -20,10 +20,15 @@ async def add_user(db: AsyncSession, user: UserCreate) -> UserGet: login=new_user.login, car_id=new_user.car_id) -async def get_user(db: AsyncSession, login: str) -> UserGet | None: + +async def get_db_user(db: AsyncSession, login: str) -> UserInDB | None: stmt = select(User).where(User.login == login) result = await db.execute(stmt) + return result.scalar_one_or_none() +async def get_user(db: AsyncSession, login: str) -> UserGet | None: + stmt = select(User).where(User.login == login) + result = await db.execute(stmt) return result.scalar_one_or_none() async def update_user_car(db: AsyncSession, login: int, new_car: int | None) -> UserGet: @@ -35,11 +40,9 @@ async def update_user_car(db: AsyncSession, login: int, new_car: int | None) -> raise HTTPException(status_code=404, detail="Пользователь не найден") user.car_id = new_car - await db.flush() return UserGet( id=user.id, login=user.login, - car_id=user.car_id - ) \ No newline at end of file + car_id=user.car_id) \ No newline at end of file diff --git a/server/user_service/src/database/schemas.py b/server/user_service/src/database/schemas.py index ab02cf5..9747e83 100644 --- a/server/user_service/src/database/schemas.py +++ b/server/user_service/src/database/schemas.py @@ -1,4 +1,9 @@ from pydantic import BaseModel, SecretStr +from datetime import datetime + +class UserLogin(BaseModel): + login: str + password: SecretStr class UserCreate(BaseModel): login: str @@ -13,4 +18,14 @@ class UserInDB(BaseModel): class UserGet(BaseModel): id: int login: str - car_id: str | None \ No newline at end of file + car_id: str | None + +class TokenData(BaseModel): + sub: str + exp: datetime | None = None + scopes: list[str] = [] + + class Config: + json_encoders = { + datetime: lambda v: v.timestamp() # Для корректной сериализации в JSON + } \ No newline at end of file diff --git a/server/user_service/src/main.py b/server/user_service/src/main.py index 51bb5e5..159d648 100644 --- a/server/user_service/src/main.py +++ b/server/user_service/src/main.py @@ -1,11 +1,11 @@ -from fastapi import FastAPI, Depends, status +from fastapi import FastAPI, Depends, status, HTTPException from sqlalchemy.ext.asyncio import AsyncSession from database.database import get_db from database.init_db import init_models from contextlib import asynccontextmanager from database import cruds -from database.schemas import UserCreate, UserGet - +from database.schemas import UserCreate, UserGet, UserLogin +from auth.auth import authenticate_user, create_access_token @asynccontextmanager async def lifespan(app: FastAPI): @@ -23,10 +23,19 @@ async def lifespan(app: FastAPI): app = FastAPI(lifespan=lifespan) @app.post("/register", response_model=UserGet, status_code=status.HTTP_201_CREATED) -async def add_user(user: UserCreate, db: AsyncSession = Depends(get_db)) +async def add_user(user: UserCreate, db: AsyncSession = Depends(get_db)): return await cruds.add_user(db, user) -#@app.post("/login") +@app.post("/login", response_model=UserGet) +async def login(user_data: UserLogin, db: AsyncSession = Depends(get_db)): + user = await authenticate_user(db, user_data.login, user_data.password) + if not user: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Incorrect login or password", + ) + access_token = create_access_token(data={"sub": user.login}) + return {"access_token": access_token, "token_type": "bearer"} @app.get("/user", response_model=UserGet) async def get_user(login: str, db: AsyncSession = Depends(get_db)): From 773b42f8c2458a38997ba40e10665009694fb8ed Mon Sep 17 00:00:00 2001 From: OrusskiyVR Date: Mon, 26 May 2025 16:36:59 +0300 Subject: [PATCH 14/14] WIP --- server/data_service/src/main.py | 2 +- server/gateway/src/dependencies/__init__.py | 0 server/gateway/src/dependencies/auth.py | 18 ------------- server/gateway/src/utils.py | 30 ++++++++++++++++++++- server/station_service/src/main.py | 2 +- server/user_service/src/database/cruds.py | 22 +++++++-------- server/user_service/src/database/schemas.py | 2 -- server/user_service/src/main.py | 4 +-- 8 files changed, 44 insertions(+), 36 deletions(-) delete mode 100644 server/gateway/src/dependencies/__init__.py delete mode 100644 server/gateway/src/dependencies/auth.py diff --git a/server/data_service/src/main.py b/server/data_service/src/main.py index 95d73e2..017a67b 100644 --- a/server/data_service/src/main.py +++ b/server/data_service/src/main.py @@ -21,7 +21,7 @@ async def lifespan(app: FastAPI): finally: print("Shutting down...") -app = FastAPI(lifespan=lifespan) +app = FastAPI(lifespan=lifespan, title="EV Route Car Service") @app.post("/car", response_model=CarCreate, status_code=status.HTTP_201_CREATED) diff --git a/server/gateway/src/dependencies/__init__.py b/server/gateway/src/dependencies/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/server/gateway/src/dependencies/auth.py b/server/gateway/src/dependencies/auth.py deleted file mode 100644 index 2cf0c58..0000000 --- a/server/gateway/src/dependencies/auth.py +++ /dev/null @@ -1,18 +0,0 @@ -from fastapi import Depends, HTTPException -from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials -import jwt - -SECRET_KEY = "secret" -ALGORITHM = "HS256" - -security = HTTPBearer() - -def get_current_user(credentials: HTTPAuthorizationCredentials = Depends(security)): - token = credentials.credentials - try: - payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) - return payload - except jwt.ExpiredSignatureError: - raise HTTPException(status_code=401, detail="Token expired") - except jwt.InvalidTokenError: - raise HTTPException(status_code=401, detail="Invalid token") \ No newline at end of file diff --git a/server/gateway/src/utils.py b/server/gateway/src/utils.py index 28442a7..d77654c 100644 --- a/server/gateway/src/utils.py +++ b/server/gateway/src/utils.py @@ -17,4 +17,32 @@ def create_access_token(data: dict, expires_delta: timedelta = timedelta(hours=1 return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) def decode_token(token: str): - return jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) \ No newline at end of file + return jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) + + +# async def authenticate_user(self, db: AsyncSession, login: str, password: str) -> UserInDB | None: +# user = await cruds.get_user(db, login) +# if not user or not pwd_context.verify(password, user.password_hash): +# return None +# return user + +# def create_access_token(self, data: TokenData, expires_delta: timedelta) -> str: +# to_encode = data.model_dump() +# expire = datetime.now(timezone.utc) + expires_delta +# to_encode.update({"exp": expire}) +# return jwt.encode(to_encode, self.SECRET_KEY, algorithm=self.ALGORITHM) + +# SECRET_KEY = "secret" +# ALGORITHM = "HS256" + +# security = HTTPBearer() + +# def get_current_user(credentials: HTTPAuthorizationCredentials = Depends(security)): +# token = credentials.credentials +# try: +# payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) +# return payload +# except jwt.ExpiredSignatureError: +# raise HTTPException(status_code=401, detail="Token expired") +# except jwt.InvalidTokenError: +# raise HTTPException(status_code=401, detail="Invalid token") \ No newline at end of file diff --git a/server/station_service/src/main.py b/server/station_service/src/main.py index 416c618..33d639a 100644 --- a/server/station_service/src/main.py +++ b/server/station_service/src/main.py @@ -21,7 +21,7 @@ async def lifespan(app: FastAPI): finally: print("Shutting down...") -app = FastAPI(lifespan=lifespan) +app = FastAPI(lifespan=lifespan, title="EV Route Station Service") @app.post("/station", response_model=StationCreate, status_code=status.HTTP_201_CREATED) diff --git a/server/user_service/src/database/cruds.py b/server/user_service/src/database/cruds.py index 146da56..1b7140b 100644 --- a/server/user_service/src/database/cruds.py +++ b/server/user_service/src/database/cruds.py @@ -4,22 +4,22 @@ from fastapi import HTTPException from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.future import select +from sqlalchemy.exc import IntegrityError pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") async def add_user(db: AsyncSession, user: UserCreate) -> UserGet: hashed_password = pwd_context.hash(user.password.get_secret_value()) - new_user = UserInDB(login=user.login, - password_hash=hashed_password, - car_id=user.car_id - ) - - db.add(new_user) - db.flush() - return UserGet(id=new_user.id, - login=new_user.login, - car_id=new_user.car_id) - + password_hash=hashed_password) + try: + db.add(new_user) + db.flush() + return UserGet(id=new_user.id, + login=new_user.login, + car_id=new_user.car_id) + except IntegrityError as ie: + await db.rollback() + raise HTTPException(status_code=400, detail=f"Логин уже занят: {str(ie)}") async def get_db_user(db: AsyncSession, login: str) -> UserInDB | None: stmt = select(User).where(User.login == login) diff --git a/server/user_service/src/database/schemas.py b/server/user_service/src/database/schemas.py index 9747e83..1612a95 100644 --- a/server/user_service/src/database/schemas.py +++ b/server/user_service/src/database/schemas.py @@ -8,12 +8,10 @@ class UserLogin(BaseModel): class UserCreate(BaseModel): login: str password: SecretStr - car_id: int | None class UserInDB(BaseModel): login: str password_hash: str - car_id: int | None class UserGet(BaseModel): id: int diff --git a/server/user_service/src/main.py b/server/user_service/src/main.py index 159d648..ba3ee38 100644 --- a/server/user_service/src/main.py +++ b/server/user_service/src/main.py @@ -20,7 +20,7 @@ async def lifespan(app: FastAPI): finally: print("Shutting down...") -app = FastAPI(lifespan=lifespan) +app = FastAPI(lifespan=lifespan, title="EV Route User Service") @app.post("/register", response_model=UserGet, status_code=status.HTTP_201_CREATED) async def add_user(user: UserCreate, db: AsyncSession = Depends(get_db)): @@ -41,6 +41,6 @@ async def login(user_data: UserLogin, db: AsyncSession = Depends(get_db)): async def get_user(login: str, db: AsyncSession = Depends(get_db)): return await cruds.get_user(db, login) -@app.patch("/user/car", response_model=UserGet) +@app.patch("/user/car", response_model=UserGet, status_code=status.HTTP_202_ACCEPTED) async def update_car(login: str, new_car: int, db: AsyncSession = Depends(get_db)): return await cruds.update_user_car(db, login, new_car)