diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 47c5c20..f9ca4d3 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -11,18 +11,12 @@ jobs: - name: Setup SSH key run: | - echo "${{ secrets.SSH_PRIVATE_KEY }}" > /tmp/id_fin - chmod 600 /tmp/id_fin - - - name: Create inventory - run: echo "${{ secrets.INVENTORY }}" > inventory.ini - - - name: Create secrets.yml - run: echo "${{ secrets.SECRETS }}" > secrets.yml - + echo "${{ secrets.SSH_PRIVATE_KEY }}" > /tmp/id_fin + chmod 600 /tmp/id_fin +#ссылка на репо - name: Create .env file run: echo "${{ secrets.RUNNER_ENV }}" > .env - +#env для runners - name: Checkout only deploy.yml uses: actions/checkout@v4 with: @@ -30,7 +24,9 @@ jobs: ref: main path: tmp-repo sparse-checkout: | - ansible/deploy.yml + ansible/deploy.yml + ansible/inventory.ini + ansible/secrets.yml - name: Run Ansible playbook run: ansible-playbook -i inventory.ini tmp-repo/ansible/deploy.yml -e @secrets.yml -e env_file="$(pwd)/.env" @@ -46,10 +42,10 @@ jobs: - name: Create .env file run: echo "${{ secrets.WEDDING_SITE_ENV }}" > .env - +#env для проекта - name: Build image run: docker build -t back:latest -f docker/dockerfile . - + - name: Start docker-compose run: docker compose --env-file .env -f docker/docker-compose.yaml up -d \ No newline at end of file diff --git a/.gitignore b/.gitignore index 48e9c9b..1b4d44e 100644 --- a/.gitignore +++ b/.gitignore @@ -22,7 +22,6 @@ hint.py #env *.env -secrets.yml #db *.db versions/ diff --git a/ansible/deploy.yml b/ansible/deploy.yml index 42bb1e3..9879a7f 100644 --- a/ansible/deploy.yml +++ b/ansible/deploy.yml @@ -31,11 +31,6 @@ group: root mode: '0600' - - name: Download nginx - shell: wget -O /opt/infra/nginx.yaml "{{ URL for docker-compose nginx }}" #перенести докер в pipeline и выкачать страницы для запуска с созданием loacations в nginx - args: - creates: /opt/infra/nginx.yaml - - name: Download node-docker.yaml shell: wget -O /opt/infra/node-docker.yaml "{{ gitea_instance_url }}" args: @@ -43,6 +38,3 @@ - name: Start node-docker shell: docker-compose -f /opt/infra/node-docker.yaml up -d - - - name: Start nginx - shell: docker-compose -f /opt/infra/nginx.yaml up -d diff --git a/ansible/inventory.ini b/ansible/inventory.ini new file mode 100644 index 0000000..b733863 --- /dev/null +++ b/ansible/inventory.ini @@ -0,0 +1,2 @@ +[servers] +myserver ansible_host=38.244.136.102 ansible_user=root \ No newline at end of file diff --git a/ansible/inventory_example b/ansible/inventory_example deleted file mode 100644 index a66718e..0000000 --- a/ansible/inventory_example +++ /dev/null @@ -1,2 +0,0 @@ -[servers] -myserver host=... ansible_user=... \ No newline at end of file diff --git a/ansible/secrets.yml b/ansible/secrets.yml new file mode 100644 index 0000000..9844510 --- /dev/null +++ b/ansible/secrets.yml @@ -0,0 +1 @@ +gitea_instance_url: https://git.homyk.space/MH.Dmitrii/wedding-site/src/branch/main/docker/gitea_runners.git diff --git a/ansible/secrets_example b/ansible/secrets_example deleted file mode 100644 index 82df564..0000000 --- a/ansible/secrets_example +++ /dev/null @@ -1 +0,0 @@ -gitea_instance_url: ... \ No newline at end of file diff --git a/docker/caddy/caddy.conf b/docker/caddy/caddy.conf new file mode 100644 index 0000000..98cb047 --- /dev/null +++ b/docker/caddy/caddy.conf @@ -0,0 +1,18 @@ +example.com { + + encode gzip + + # --- API --- + handle_path /api/* { + reverse_proxy backend:8000 + } + + # --- Frontend --- + root * /var/www/site + file_server + + log { + output stdout + format console + } +} \ No newline at end of file diff --git a/docker/caddy/caddy.yaml b/docker/caddy/caddy.yaml new file mode 100644 index 0000000..ebed74d --- /dev/null +++ b/docker/caddy/caddy.yaml @@ -0,0 +1,19 @@ +services: + caddy: + image: caddy: + restart: unless-stopped + cap_add: + - NET_ADMIN + ports: + - "80:80" + - "443:443" + - "443:443/udp" + volumes: + - $PWD/conf:/etc/caddy + - $PWD/site:/srv + - caddy_data:/data + - caddy_config:/config + +volumes: + caddy_data: + caddy_config: diff --git a/docker/nginx/nginx.yaml b/docker/nginx/nginx.yaml deleted file mode 100644 index 5bb32be..0000000 --- a/docker/nginx/nginx.yaml +++ /dev/null @@ -1,8 +0,0 @@ -web: - image: nginx - volumes: - - ./templates:/etc/nginx/templates - - ./confs:/etc/nginx/conf.d - ports: - - "80:80" - - "443:443" \ No newline at end of file diff --git a/makefile b/makefile index dedc97f..b93afc6 100644 --- a/makefile +++ b/makefile @@ -2,9 +2,9 @@ VENV=source ./.venv/bin/activate; ALEMBIC=alembic -c ./server/backend/database/alembic/alembic.ini .PHONY: run run_debug migrate_head migrate_down migrate_history migrate_current migrate run: - $(VENV) python run.py + $(VENV) python run.py --user_name admin run_debug: - $(VENV) python run.py --mode debug + $(VENV) python run.py --mode debug --user_name admin migrate_head: $(VENV) $(ALEMBIC) upgrade head migrate_down: diff --git a/run.py b/run.py index 442c535..f0c9763 100644 --- a/run.py +++ b/run.py @@ -1,4 +1,7 @@ from server.backend.schema.pydantic import settings +from server.backend.database.db import create_user, list_users +from server.backend.schema.pydantic import UserCreate +import asyncio import uvicorn def start(log_level:str): if __name__ == "__main__": @@ -11,18 +14,33 @@ def start(log_level:str): access_log=True ) import argparse -parser = argparse.ArgumentParser(description="logging") +parser = argparse.ArgumentParser(description="logging and admin creation") parser.add_argument( "--mode", choices=["debug","info"], default="info", help="Режим логирования (по умолчанию: info)" ) +parser.add_argument( + "--user_name", + type=str, + required=True, + help="Создание первого пользователя)" +) args = parser.parse_args() -match args.mode: - case "debug": - print("Режим:", args.mode) - start(args.mode) - case "info": - print("Режим:", args.mode) - start(args.mode) \ No newline at end of file +async def arguments(args): + admin_user = { + "code": "123456", + "name": args.user_name, + "surname": args.user_name, + "admin": True + } + users = await list_users() + label = any(u.admin for u in users) + if not label: + await create_user(UserCreate(**admin_user)) + match args.mode: + case "debug" | "info": + print("Режим:", args.mode) + start(args.mode) +asyncio.run(arguments(args)) diff --git a/server/backend/auth/JWT.py b/server/backend/auth/JWT.py index 697d0d0..6084b2b 100644 --- a/server/backend/auth/JWT.py +++ b/server/backend/auth/JWT.py @@ -6,8 +6,7 @@ from server.backend.schema.pydantic import settings def signJWT(user_info: dict) -> str: payload = { - "user_id": user_info.id, - "admin":user_info.admin, + "user_id":user_info.id, "expires": time.time() + settings.ACCESS_TOKEN_EXPIRE_SECONDS } token = pyjwt.encode(payload, settings.SECRET_KEY, algorithm=settings.ALGORITHM) diff --git a/server/backend/database/db.py b/server/backend/database/db.py index 1b7d7ed..2ebe47a 100644 --- a/server/backend/database/db.py +++ b/server/backend/database/db.py @@ -38,15 +38,19 @@ class User(Base): async def create_user(user_info): async with AsyncSessionLocal() as session: - user_data = user_info.dict(exclude_unset=True) - new_user = User(**user_data) - session.add(new_user) - await session.commit() - await session.refresh(new_user) + result = await session.execute(select(User).where(User.code==user_info.code)) + user = result.scalar_one_or_none() + if user == None: + user_data = user_info.dict(exclude_unset=True) + new_user = User(**user_data) + session.add(new_user) + await session.commit() + await session.refresh(new_user) + return user async def update_user(user_info): async with AsyncSessionLocal() as session: - result = await session.execute(select(User).where(User.code==user_info.code)) + result = await session.execute(select(User).where(User.id==user_info.id)) user = result.scalar_one_or_none() if user: update_data = user_info.dict(exclude_unset=True) @@ -64,13 +68,29 @@ async def list_users(): return users else: return None - +async def list_user(id): + async with AsyncSessionLocal() as session: + result = await session.execute(select(User).where(User.id == id)) + user = result.scalar_one_or_none() + if user: + return user + else: + return None +async def list_user_by_code(code): + async with AsyncSessionLocal() as session: + result = await session.execute(select(User).where(User.code == code)) + user = result.scalar_one_or_none() + if user: + return user + else: + return None async def login_user(code): async with AsyncSessionLocal() as session: result = await session.execute(select(User).where(User.code == code.code)) user = result.scalar_one_or_none() if user: user.last_login=datetime.now(timezone.utc) + user.activated=True await session.commit() return user else: diff --git a/server/backend/endpoints/endpoints.py b/server/backend/endpoints/endpoints.py index 65c52cf..6206b85 100644 --- a/server/backend/endpoints/endpoints.py +++ b/server/backend/endpoints/endpoints.py @@ -18,9 +18,27 @@ async def check_roles(user=Depends(get_current_user)): return user @api.post("/update", response_model=pydantic.UserUpdate) -async def update_user(data: pydantic.UserUpdate,user=Depends(get_current_user)): - data = await db.update_user(data) - return data +async def update_user(data: pydantic.UserUpdate, user=Depends(get_current_user)): + user_check = await db.list_user(user["user_id"]) + if not user_check: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found") + if not user_check.admin: + if data.code != user_check.code: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="Ordinary users cannot change their code" + ) + if user_check.admin: + if data.code != user_check.code: + existing_user = await db.list_user_by_code(data.code) + if existing_user and existing_user.id != user_check.id: + raise HTTPException( + status_code=status.HTTP_409_CONFLICT, + detail="Code already exists for another user" + ) + updated_data = data.copy(update={"id": user_check.id}) + updated_data = await db.update_user(updated_data) + return updated_data @api.post("/create", response_model=pydantic.UserAccess) async def create_user(user_info: pydantic.UserCreate,user=Depends(check_roles)): diff --git a/server/backend/schema/pydantic.py b/server/backend/schema/pydantic.py index d7ae0bc..ef02553 100644 --- a/server/backend/schema/pydantic.py +++ b/server/backend/schema/pydantic.py @@ -23,9 +23,6 @@ class UserOut(BaseModel): name: NameStr = Field(..., description="Name of the guest") surname: NameStr = Field(..., description="Surname of the guest") -class UserCreate(UserAccess): - pass - class UserUpdate(UserAccess): name: NameStr = Field(..., description="Name of the guest") surname: NameStr = Field(..., description="Surname of the guest") @@ -35,6 +32,8 @@ class UserUpdate(UserAccess): alco: bool = Field(False, description="if the guest will drink alco or not") types_of_alco: str = Field("", description="types of alco") +class UserCreate(UserUpdate): + admin:bool = Field(False, description="Admin privilegies") class Settings(BaseSettings): DIR:str PORT:int