diff --git a/.gitignore b/.gitignore index b86d9c2..60b6d5f 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ dist/ uploads/ *.egg-info/ .DS_Store +.idea \ No newline at end of file diff --git a/backend/app/config.py b/backend/app/config.py index 3ab5cf6..6528fa5 100644 --- a/backend/app/config.py +++ b/backend/app/config.py @@ -15,6 +15,12 @@ class Settings(BaseSettings): MINIO_BUCKET: str = "filam3d" MINIO_SECURE: bool = False + JWT_SECRET: str = "change-me-in-production-please" + JWT_ALGORITHM: str = "HS256" + JWT_EXPIRE_HOURS: int = 24 + ADMIN_DEFAULT_EMAIL: str = "admin@filam3d.ru" + ADMIN_DEFAULT_PASSWORD: str = "admin123" + model_config = {"env_file": ["../.env", ".env"]} diff --git a/backend/app/main.py b/backend/app/main.py index dddeb82..7d41e72 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -6,10 +6,12 @@ from fastapi import FastAPI, Request from fastapi.middleware.cors import CORSMiddleware from sqlalchemy import select +from app.config import settings from app.database import async_session, engine, Base -from app.models import Material +from app.models import Material, AdminUser from app.seed.materials import MATERIALS -from app.routers import calculate, materials, orders, ai_advisor +from app.services.auth import hash_password +from app.routers import calculate, materials, orders, ai_advisor, admin # Configure logging logging.basicConfig( @@ -50,6 +52,22 @@ async def lifespan(app: FastAPI): else: logger.info("Materials already exist, skipping seed") + # Seed default admin user + logger.info("Checking admin user...") + async with async_session() as session: + result = await session.execute(select(AdminUser).limit(1)) + if result.scalar_one_or_none() is None: + admin_user = AdminUser( + email=settings.ADMIN_DEFAULT_EMAIL, + password_hash=hash_password(settings.ADMIN_DEFAULT_PASSWORD), + name="Admin", + ) + session.add(admin_user) + await session.commit() + logger.info("Default admin created: %s", settings.ADMIN_DEFAULT_EMAIL) + else: + logger.info("Admin user already exists, skipping") + logger.info("=== Application ready ===") yield logger.info("=== Application shutdown ===") @@ -82,6 +100,7 @@ app.include_router(calculate.router, prefix="/api") app.include_router(materials.router, prefix="/api") app.include_router(orders.router, prefix="/api") app.include_router(ai_advisor.router, prefix="/api") +app.include_router(admin.router, prefix="/api/admin") @app.get("/api/health") diff --git a/backend/app/models/__init__.py b/backend/app/models/__init__.py index 0c65c3e..8ec4548 100644 --- a/backend/app/models/__init__.py +++ b/backend/app/models/__init__.py @@ -1,5 +1,7 @@ from app.models.material import Material from app.models.calculation import Calculation from app.models.order import Order +from app.models.admin_user import AdminUser +from app.models.app_settings import AppSettings -__all__ = ["Material", "Calculation", "Order"] +__all__ = ["Material", "Calculation", "Order", "AdminUser", "AppSettings"] diff --git a/backend/requirements.txt b/backend/requirements.txt index 036699f..566aabc 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -11,3 +11,5 @@ python-multipart==0.0.20 httpx==0.28.1 minio==7.2.12 google-genai==1.14.0 +pyjwt==2.10.1 +bcrypt==4.2.1 diff --git a/docker-compose.yml b/docker-compose.yml index 0673d70..8eab17c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,7 +14,3 @@ services: MINIO_SECRET_KEY: SuperSecretPassword123! MINIO_BUCKET: ${MINIO_BUCKET:-filam3d} MINIO_SECURE: ${MINIO_SECURE:-false} - - frontend: - build: ./frontend - network_mode: host diff --git a/frontend/Dockerfile b/frontend/Dockerfile deleted file mode 100644 index 597940a..0000000 --- a/frontend/Dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -FROM node:20-alpine - -WORKDIR /app - -COPY package.json . -RUN npm install - -COPY . . - -EXPOSE 5173 - -CMD ["npm", "run", "dev"] diff --git a/frontend/index.html b/frontend/index.html index a759158..6ec7abe 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -3,10 +3,28 @@
+ + + + + + + + + + + + + + + + + + -Загрузите модель, выберите материал и получите мгновенный расчёт стоимости
-{{ store.error }}
-Загрузите модель, выберите материал и получите мгновенный расчёт
Загрузите модель и выберите материал для расчёта
+{{ store.error }}
+Загрузите модель и выберите материал для расчёта
+STL, 3MF или OBJ файл до 50 МБ. Drag-and-drop или выбор файла.
+Выберите материал, заполнение, высоту слоя. AI поможет с выбором.
+Детальная разбивка стоимости. Оформите заказ в два клика.
+{{ article.description }}
+