diff --git a/backend/alembic/versions/e50a60c5d343_add_user_bio_and_avatar.py b/backend/alembic/versions/e50a60c5d343_add_user_bio_and_avatar.py new file mode 100644 index 0000000..fcf8fbe --- /dev/null +++ b/backend/alembic/versions/e50a60c5d343_add_user_bio_and_avatar.py @@ -0,0 +1,39 @@ +"""Add user bio and avatar + +Revision ID: e50a60c5d343 +Revises: b16ef2228130 +Create Date: 2025-12-20 06:44:30.761707 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +import sqlmodel + + +# revision identifiers, used by Alembic. +revision: str = 'e50a60c5d343' +down_revision: Union[str, Sequence[str], None] = 'b16ef2228130' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('user', schema=None) as batch_op: + batch_op.add_column(sa.Column('bio', sqlmodel.sql.sqltypes.AutoString(), nullable=True)) + batch_op.add_column(sa.Column('avatar', sqlmodel.sql.sqltypes.AutoString(), nullable=True)) + + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('user', schema=None) as batch_op: + batch_op.drop_column('avatar') + batch_op.drop_column('bio') + + # ### end Alembic commands ### diff --git a/docker-compose.hetzner.yml b/docker-compose.hetzner.yml deleted file mode 100644 index 7367a9a..0000000 --- a/docker-compose.hetzner.yml +++ /dev/null @@ -1,98 +0,0 @@ -version: '3.8' - -services: - backend: - build: ./backend - ports: - - "127.0.0.1:8020:8000" - volumes: - - ./backend:/app - - backend_data:/app/data - environment: - - DATABASE_URL=postgresql://elmeg:elmeg_password@db:5432/elmeg_db - - SECRET_KEY=${SECRET_KEY:-demo-secret-change-in-production} - command: uvicorn main:app --host 0.0.0.0 --port 8000 --root-path /api --proxy-headers --forwarded-allow-ips '*' - depends_on: - - db - restart: unless-stopped - healthcheck: - test: [ "CMD", "python", "-c", "import httpx; httpx.get('http://localhost:8000/docs').raise_for_status()" ] - interval: 30s - timeout: 10s - retries: 3 - networks: - - elmeg - - traefik - labels: - - "traefik.enable=true" - - "traefik.http.routers.elmeg-backend.rule=Host(`elmeg.xyz`) && PathPrefix(`/api`)" - - "traefik.http.routers.elmeg-backend.entrypoints=websecure" - - "traefik.http.routers.elmeg-backend.tls.certresolver=letsencrypt" - - "traefik.http.routers.elmeg-backend.priority=100" - - "traefik.http.middlewares.elmeg-strip.stripprefix.prefixes=/api" - - "traefik.http.routers.elmeg-backend.middlewares=elmeg-strip" - - "traefik.http.routers.elmeg-backend.service=elmeg-backend-svc" - - "traefik.http.services.elmeg-backend-svc.loadbalancer.server.port=8000" - - "traefik.docker.network=ersen_ersen-network" - # Direct routes for docs (no strip) - - "traefik.http.routers.elmeg-backend-docs.rule=Host(`elmeg.xyz`) && PathPrefix(`/docs`, `/openapi.json`)" - - "traefik.http.routers.elmeg-backend-docs.entrypoints=websecure" - - "traefik.http.routers.elmeg-backend-docs.tls.certresolver=letsencrypt" - - "traefik.http.routers.elmeg-backend-docs.priority=100" - - "traefik.http.routers.elmeg-backend-docs.service=elmeg-backend-svc" - - frontend: - build: - context: ./frontend - dockerfile: Dockerfile - ports: - - "127.0.0.1:3020:3000" - volumes: - - ./frontend:/app - - /app/node_modules - - /app/.next - environment: - - NEXT_PUBLIC_API_URL=https://elmeg.xyz/api - - INTERNAL_API_URL=http://backend:8000 - depends_on: - - backend - restart: unless-stopped - networks: - - elmeg - - traefik - labels: - - "traefik.enable=true" - - "traefik.http.routers.elmeg-frontend.rule=Host(`elmeg.xyz`) && !PathPrefix(`/api`, `/docs`, `/openapi.json`)" - - "traefik.http.routers.elmeg-frontend.entrypoints=websecure" - - "traefik.http.routers.elmeg-frontend.tls.certresolver=letsencrypt" - - "traefik.http.routers.elmeg-frontend.priority=50" - - "traefik.http.services.elmeg-frontend.loadbalancer.server.port=3000" - - "traefik.docker.network=ersen_ersen-network" - - db: - image: postgres:15-alpine - environment: - - POSTGRES_USER=elmeg - - POSTGRES_PASSWORD=elmeg_password - - POSTGRES_DB=elmeg_db - volumes: - - postgres_data:/var/lib/postgresql/data - restart: unless-stopped - healthcheck: - test: [ "CMD-SHELL", "pg_isready -U elmeg" ] - interval: 10s - timeout: 5s - retries: 5 - networks: - - elmeg - -volumes: - postgres_data: - backend_data: - - -networks: - elmeg: - traefik: - name: ersen_ersen-network - external: true diff --git a/traefik-routes.yml b/traefik-routes.yml new file mode 100644 index 0000000..7836ef5 --- /dev/null +++ b/traefik-routes.yml @@ -0,0 +1,77 @@ +http: + # Rate Limiting Middlewares + middlewares: + api-ratelimit: + rateLimit: + average: 100 # Average requests per second + burst: 200 # Maximum burst + period: 1m # Sliding window period + sourceCriterion: + ipStrategy: + depth: 1 # Use X-Forwarded-For header + api-ratelimit-strict: + rateLimit: + average: 10 # Strict limit for sensitive endpoints + burst: 20 + period: 1m + sourceCriterion: + ipStrategy: + depth: 1 + elmeg-strip: + stripPrefix: + prefixes: + - "/api" + + routers: + ersen-web: + rule: "Host(`ersen.xyz`) || Host(`www.ersen.xyz`)" + service: ersen-frontend + entryPoints: + - websecure + tls: + certResolver: letsencrypt + ersen-api: + rule: "Host(`api.ersen.xyz`)" + service: ersen-backend + entryPoints: + - websecure + middlewares: + - api-ratelimit + tls: + certResolver: letsencrypt + + elmeg-web: + rule: "Host(`elmeg.xyz`)" + service: elmeg-frontend + entryPoints: + - websecure + tls: + certResolver: letsencrypt + elmeg-api: + rule: "Host(`elmeg.xyz`) && PathPrefix(`/api`)" + service: elmeg-backend + priority: 100 + entryPoints: + - websecure + middlewares: + - elmeg-strip + tls: + certResolver: letsencrypt + + services: + ersen-frontend: + loadBalancer: + servers: + - url: "http://ersen-frontend-1:80" + ersen-backend: + loadBalancer: + servers: + - url: "http://ersen-backend-1:3000" + elmeg-frontend: + loadBalancer: + servers: + - url: "http://elmeg-demo-frontend-1:3000" + elmeg-backend: + loadBalancer: + servers: + - url: "http://elmeg-demo-backend-1:8000"