diff --git a/.gitignore b/.gitignore index 85ae518..f3ea9b3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ .vscode /backend/pyrightconfig.json /backend/.python-version +/backend/.env +/backend/app/.env diff --git a/backend/app/.env-example b/backend/app/.env-example new file mode 100644 index 0000000..1187c85 --- /dev/null +++ b/backend/app/.env-example @@ -0,0 +1,5 @@ +POSTGRES_USER= +POSTGRES_PASSWORD= +POSTGRES_DB= +POSTGRES_HOST= +POSTGRES_PORT= diff --git a/backend/app/db_engine.py b/backend/app/db_engine.py new file mode 100644 index 0000000..e576a86 --- /dev/null +++ b/backend/app/db_engine.py @@ -0,0 +1,42 @@ +from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker +from sqlalchemy.orm import DeclarativeBase +import dotenv +import os + +dotenv.load_dotenv() + +PG_USER = dotenv.get_key(".env", "PG_USER") +PG_PASSWORD = dotenv.get_key(".env", "PG_PASSWORD") +PG_HOST = dotenv.get_key(".env", "PG_HOST") +PG_PORT = dotenv.get_key(".env", "PG_PORT") +PG_DB = dotenv.get_key(".env", "PG_DB") + +DATABASE_URL = ( + f"postgresql+asyncpg://{PG_USER}:{PG_PASSWORD}@{PG_HOST}:{PG_PORT}/{PG_DB}" +) + +engine = create_async_engine( + DATABASE_URL, + echo=False, + future=True, +) + +async_session_maker = async_sessionmaker( + engine, + class_=AsyncSession, + expire_on_commit=False, +) + + +class Base(DeclarativeBase): + pass + +async def get_async_session(): + async with async_session_maker() as session: + yield session + + +async def init_db(): + async with engine.begin() as conn: + await conn.run_sync(Base.metadata.create_all) + diff --git a/backend/app/models/guestbook.py b/backend/app/models/guestbook.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/app/models/posts.py b/backend/app/models/posts.py new file mode 100644 index 0000000..1b6c257 --- /dev/null +++ b/backend/app/models/posts.py @@ -0,0 +1,15 @@ +from sqlalchemy import Column, Integer, String, ForeignKey, DateTime +from sqlalchemy.orm import relationship + +from app.db_engine import Base + + +class Post(Base): + __tablename__ = "posts" + + id = Column(Integer, primary_key=True, index=True, autoincrement=True, nullable=False) + title = Column(String, index=True, nullable=False) + content = Column(String, index=True, nullable=False) + images = Column(String, index=True) + updated_at = Column(DateTime, index=True) + diff --git a/backend/docker-compose.yml b/backend/docker-compose.yml new file mode 100644 index 0000000..a2e7d61 --- /dev/null +++ b/backend/docker-compose.yml @@ -0,0 +1,36 @@ +services: + postgres: + image: postgres:18-alpine3.22 + ports: + - "5432:5432" + environment: + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_DB: ${POSTGRES_DB} + volumes: + - postgres:/var/lib/postgresql/data + networks: + - backend + + backend: + build: . + ports: + - "8000:8000" + environment: + - POSTGRES_USER=${POSTGRES_USER} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + - POSTGRES_DB=${POSTGRES_DB} + - POSTGRES_HOST=${POSTGRES_HOST} + - POSTGRES_PORT=${POSTGRES_PORT} + depends_on: + - postgres + networks: + - backend + +volumes: + postgres: + driver: local + +networks: + backend: + driver: bridge diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 343a26b..dc7b911 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -5,7 +5,9 @@ description = "Add your description here" readme = "README.md" requires-python = ">=3.13" dependencies = [ + "asyncpg>=0.30.0", "fastapi>=0.121.3", + "python-dotenv>=1.2.1", "sqlalchemy>=2.0.44", "uvicorn>=0.38.0", ] diff --git a/backend/uv.lock b/backend/uv.lock index f3440a0..eead47c 100644 --- a/backend/uv.lock +++ b/backend/uv.lock @@ -33,19 +33,39 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/15/b3/9b1a8074496371342ec1e796a96f99c82c945a339cd81a8e73de28b4cf9e/anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc", size = 109097, upload-time = "2025-09-23T09:19:10.601Z" }, ] +[[package]] +name = "asyncpg" +version = "0.30.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2f/4c/7c991e080e106d854809030d8584e15b2e996e26f16aee6d757e387bc17d/asyncpg-0.30.0.tar.gz", hash = "sha256:c551e9928ab6707602f44811817f82ba3c446e018bfe1d3abecc8ba5f3eac851", size = 957746, upload-time = "2024-10-20T00:30:41.127Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/22/e20602e1218dc07692acf70d5b902be820168d6282e69ef0d3cb920dc36f/asyncpg-0.30.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:05b185ebb8083c8568ea8a40e896d5f7af4b8554b64d7719c0eaa1eb5a5c3a70", size = 670373, upload-time = "2024-10-20T00:29:55.165Z" }, + { url = "https://files.pythonhosted.org/packages/3d/b3/0cf269a9d647852a95c06eb00b815d0b95a4eb4b55aa2d6ba680971733b9/asyncpg-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c47806b1a8cbb0a0db896f4cd34d89942effe353a5035c62734ab13b9f938da3", size = 634745, upload-time = "2024-10-20T00:29:57.14Z" }, + { url = "https://files.pythonhosted.org/packages/8e/6d/a4f31bf358ce8491d2a31bfe0d7bcf25269e80481e49de4d8616c4295a34/asyncpg-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b6fde867a74e8c76c71e2f64f80c64c0f3163e687f1763cfaf21633ec24ec33", size = 3512103, upload-time = "2024-10-20T00:29:58.499Z" }, + { url = "https://files.pythonhosted.org/packages/96/19/139227a6e67f407b9c386cb594d9628c6c78c9024f26df87c912fabd4368/asyncpg-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46973045b567972128a27d40001124fbc821c87a6cade040cfcd4fa8a30bcdc4", size = 3592471, upload-time = "2024-10-20T00:30:00.354Z" }, + { url = "https://files.pythonhosted.org/packages/67/e4/ab3ca38f628f53f0fd28d3ff20edff1c975dd1cb22482e0061916b4b9a74/asyncpg-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9110df111cabc2ed81aad2f35394a00cadf4f2e0635603db6ebbd0fc896f46a4", size = 3496253, upload-time = "2024-10-20T00:30:02.794Z" }, + { url = "https://files.pythonhosted.org/packages/ef/5f/0bf65511d4eeac3a1f41c54034a492515a707c6edbc642174ae79034d3ba/asyncpg-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:04ff0785ae7eed6cc138e73fc67b8e51d54ee7a3ce9b63666ce55a0bf095f7ba", size = 3662720, upload-time = "2024-10-20T00:30:04.501Z" }, + { url = "https://files.pythonhosted.org/packages/e7/31/1513d5a6412b98052c3ed9158d783b1e09d0910f51fbe0e05f56cc370bc4/asyncpg-0.30.0-cp313-cp313-win32.whl", hash = "sha256:ae374585f51c2b444510cdf3595b97ece4f233fde739aa14b50e0d64e8a7a590", size = 560404, upload-time = "2024-10-20T00:30:06.537Z" }, + { url = "https://files.pythonhosted.org/packages/c8/a4/cec76b3389c4c5ff66301cd100fe88c318563ec8a520e0b2e792b5b84972/asyncpg-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:f59b430b8e27557c3fb9869222559f7417ced18688375825f8f12302c34e915e", size = 621623, upload-time = "2024-10-20T00:30:09.024Z" }, +] + [[package]] name = "backend" version = "0.1.0" source = { virtual = "." } dependencies = [ + { name = "asyncpg" }, { name = "fastapi" }, + { name = "python-dotenv" }, { name = "sqlalchemy" }, { name = "uvicorn" }, ] [package.metadata] requires-dist = [ + { name = "asyncpg", specifier = ">=0.30.0" }, { name = "fastapi", specifier = ">=0.121.3" }, + { name = "python-dotenv", specifier = ">=1.2.1" }, { name = "sqlalchemy", specifier = ">=2.0.44" }, { name = "uvicorn", specifier = ">=0.38.0" }, ] @@ -200,6 +220,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, ] +[[package]] +name = "python-dotenv" +version = "1.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221, upload-time = "2025-10-26T15:12:10.434Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230, upload-time = "2025-10-26T15:12:09.109Z" }, +] + [[package]] name = "sniffio" version = "1.3.1"