Переглянути джерело

Auto-init: project ready for production

unknown 2 днів тому
батько
коміт
9e9e68d399

+ 2 - 0
.env.production

@@ -0,0 +1,2 @@
+VITE_API_URL=https://api.radionica3d.com
+VITE_WS_URL=wss://api.radionica3d.com

+ 80 - 0
.github/workflows/deploy.yml

@@ -0,0 +1,80 @@
+name: Build and Deploy
+
+on:
+  push:
+    branches:
+      - master
+
+jobs:
+  build-and-deploy:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout code
+        uses: actions/checkout@v4
+
+      - name: Set up Python
+        uses: actions/setup-python@v5
+        with:
+          python-version: '3.10'
+
+      - name: Set up Node.js
+        uses: actions/setup-node@v4
+        with:
+          node-version: '20'
+
+      - name: Install dependencies and Build
+        run: |
+          npm ci
+          # Генерируем локализации перед билдом
+          python scripts/manage_locales.py split
+          npm run build
+
+      - name: Deploy to Server
+        uses: appleboy/scp-action@master
+        with:
+          host: ${{ secrets.REMOTE_HOST }}
+          username: ${{ secrets.REMOTE_USER }}
+          key: ${{ secrets.SSH_PRIVATE_KEY }}
+          source: "dist/,backend/,nginx.conf,radionica-backend.service"
+          target: "/tmp/radionica3d_deploy"
+          strip_components: 0
+
+      - name: Execute remote commands
+        uses: appleboy/ssh-action@master
+        with:
+          host: ${{ secrets.REMOTE_HOST }}
+          username: ${{ secrets.REMOTE_USER }}
+          key: ${{ secrets.SSH_PRIVATE_KEY }}
+          script: |
+            export REMOTE_DIR="/var/www/radionica3d"
+            export CHOWN_USER="www-data"
+            
+            # Подготовка папок
+            sudo mkdir -p $REMOTE_DIR/html $REMOTE_DIR/backend
+            
+            # Обновление файлов
+            sudo cp -r /tmp/radionica3d_deploy/dist/* $REMOTE_DIR/html/
+            sudo cp -r /tmp/radionica3d_deploy/backend/* $REMOTE_DIR/backend/
+            
+            # Настройка бэкенда
+            cd $REMOTE_DIR/backend
+            [ -d venv ] || python3 -m venv venv
+            source venv/bin/activate
+            # Умная установка библиотек
+            (cmp -s /tmp/radionica3d_deploy/backend/requirements.txt requirements.txt || (cp /tmp/radionica3d_deploy/backend/requirements.txt requirements.txt && pip install -r requirements.txt && pip install gunicorn uvicorn))
+            
+            # Конфиги
+            sudo cp /tmp/radionica3d_deploy/nginx.conf /etc/nginx/sites-available/radionica3d
+            sudo ln -sf /etc/nginx/sites-available/radionica3d /etc/nginx/sites-enabled/
+            sudo nginx -t && sudo systemctl restart nginx
+            
+            # Служба
+            sudo cp /tmp/radionica3d_deploy/radionica-backend.service /etc/systemd/system/
+            sudo systemctl daemon-reload
+            sudo systemctl restart radionica-backend
+            
+            # Права
+            sudo chown -R $CHOWN_USER:$CHOWN_USER $REMOTE_DIR
+            
+            # Очистка
+            sudo rm -rf /tmp/radionica3d_deploy

+ 25 - 23
.gitignore

@@ -1,31 +1,33 @@
-# Node
+# Dependency directories
 node_modules/
-dist/
-.env
-
-# Python
-__pycache__/
-*.py[cod]
-*$py.class
-.venv/
+jspm_packages/
 venv/
-ENV/
-.pytest_cache/
-.coverage
-htmlcov/
-
-# Local data
-uploads/
-backend/uploads/
-*.db
-*.sqlite3
+.env
+.env.local
+.env.production.local
 
-# OS
-.DS_Store
-Thumbs.db
+# Build outputs
+dist/
+dist-ssr/
+*.local
 
-# IDE
+# IDEs
 .idea/
 .vscode/
 *.swp
 *.swo
+
+# OS files
+.DS_Store
+Thumbs.db
+
+# Backend specific
+__pycache__/
+*.py[cod]
+*$py.class
+.pytest_cache/
+backend/uploads/
+backend/cert.p12
+*.target
+*.log
+deploy.tar.gz

+ 75 - 0
PRODUCTION_SETUP.md

@@ -0,0 +1,75 @@
+# Radionica3D Production Deployment Guide (No Docker)
+
+Этот гид описывает шаги по ручному деплою Radionica3D на Linux-сервер (Ubuntu/Debian) с использованием Nginx и Systemd.
+
+---
+
+## 1. Подготовка окружения (на сервере)
+
+### Зависимости бэкенда
+Установите необходимые системные пакеты:
+```bash
+sudo apt update
+sudo apt install python3-pip python3-venv nginx redis-server prusa-slicer libmagic1
+```
+
+---
+
+## 2. Деплой Бэкенда
+
+1. Скопируйте папку `backend` в `/var/www/radionica3d/backend`.
+2. Создайте виртуальное окружение и установите зависимости:
+   ```bash
+   cd /var/www/radionica3d/backend
+   python3 -m venv venv
+   source venv/bin/activate
+   pip install -r requirements.txt
+   pip install gunicorn uvicorn
+   ```
+3. Настройте службу в systemd:
+   - Скопируйте файл `radionica-backend.service` в `/etc/systemd/system/`.
+   - Проверьте пути `WorkingDirectory` и `ExecStart` в файле.
+   - Активируйте службу:
+     ```bash
+     sudo systemctl daemon-reload
+     sudo systemctl enable radionica-backend
+     sudo systemctl start radionica-backend
+     ```
+
+---
+
+## 3. Деплой Фронтенда
+
+1. Соберите проект локально:
+   ```bash
+   ./build_frontend.sh
+   ```
+2. Скопируйте содержимое папки `dist` в `/var/www/radionica3d/html` на сервере.
+
+---
+
+## 4. Настройка Nginx
+
+1. Скопируйте `nginx.conf` из проекта в `/etc/nginx/sites-available/radionica3d`.
+2. Отредактируйте `server_name` и пути.
+3. Активируйте конфиг:
+   ```bash
+   sudo ln -s /etc/nginx/sites-available/radionica3d /etc/nginx/sites-enabled/
+   sudo nginx -t
+   sudo systemctl restart nginx
+   ```
+
+---
+
+## 5. Переменные окружения
+
+Backend автоматически подтянет настройки из переменных окружения. Вы можете задать их в файле службы `radionica-backend.service` в секции `[Service]`:
+- `DB_HOST`, `DB_USER`, `DB_PASS`, `DB_NAME`
+- `REDIS_HOST`
+- `RADIONICA_DEBUG=False`
+- `EFI_ENABLED=True` (для фискализации)
+
+---
+
+## 6. База данных
+Не забудьте развернуть MySQL и создать базу данных `radionica3d`, а также импортировать схему.

+ 29 - 0
backend.Dockerfile

@@ -0,0 +1,29 @@
+FROM python:3.10-slim
+
+# Install system dependencies
+RUN apt-get update && apt-get install -y \
+    curl \
+    libmagic1 \
+    libgl1-mesa-glx \
+    prusa-slicer \
+    && rm -rf /var/lib/apt/lists/*
+
+WORKDIR /app
+
+# Install Python dependencies
+COPY backend/requirements.txt .
+RUN pip install --no-cache-dir -r requirements.txt
+RUN pip install gunicorn uvicorn
+
+# Copy application code
+COPY backend/ .
+
+# Ensure upload directory exists
+RUN mkdir -p uploads
+
+# Make start script executable
+RUN chmod +x start.sh
+
+EXPOSE 8000
+
+CMD ["./start.sh"]

+ 15 - 15
backend/config.py

@@ -5,43 +5,43 @@ import platform
 BASE_DIR = os.path.dirname(os.path.abspath(__file__))
 
 # Debugging
-DEBUG = True
+DEBUG = os.getenv("RADIONICA_DEBUG", "True").lower() == "true"
 
 # Slicer Settings
 # If True, triggers synchronous slicing upon file upload (slower upload, exact metrics on UI)
-SYNC_SLICING_ON_UPLOAD = True
+SYNC_SLICING_ON_UPLOAD = os.getenv("SYNC_SLICING", "True").lower() == "true"
 IS_WINDOWS = platform.system() == "Windows"
 
 if IS_WINDOWS:
     # Default Windows path
-    SLICER_PATH = r"C:\Program Files\Prusa3D\PrusaSlicer\prusa-slicer-console.exe"
+    SLICER_PATH = os.getenv("SLICER_PATH", r"C:\Program Files\Prusa3D\PrusaSlicer\prusa-slicer-console.exe")
 else:
     # Default Linux path (binary name if in PATH, or absolute path)
-    SLICER_PATH = "prusa-slicer" 
+    SLICER_PATH = os.getenv("SLICER_PATH", "prusa-slicer") 
 
 # Profile configuration (can be changed per machine)
-SLICER_CONFIG = os.path.join(BASE_DIR, "printer_profile.ini")
+SLICER_CONFIG = os.getenv("SLICER_CONFIG", os.path.join(BASE_DIR, "printer_profile.ini"))
 
 # Order settings
-UPLOAD_DIR = os.path.join(BASE_DIR, "uploads")
+UPLOAD_DIR = os.getenv("UPLOAD_DIR", os.path.join(BASE_DIR, "uploads"))
 PREVIEW_DIR = os.path.join(UPLOAD_DIR, "previews")
 for d in [UPLOAD_DIR, PREVIEW_DIR]:
     if not os.path.exists(d):
         os.makedirs(d)
 
 # Payment Config
-ZIRO_RACUN = "510-1234567890123-45"
+ZIRO_RACUN = os.getenv("ZIRO_RACUN", "510-1234567890123-45")
 COMPANY_NAME = "RADIONICA 3D"
-COMPANY_PIB = "01234567"
+COMPANY_PIB = os.getenv("COMPANY_PIB", "01234567")
 COMPANY_CITY = "Podgorica"
 COMPANY_ADDRESS = "Cetinjski Put, Podgorica, Montenegro"
 PDV_RATE = 21 # In percent
 
 # EFI Fiskalizacija
-EFI_ENABLED = False
-EFI_CERT_PATH = os.path.join(BASE_DIR, "cert.p12")
-EFI_CERT_PASS = "changeit"
-EFI_ENU_CODE = "xx123yy456" # Electronic Fiscal Device code
-EFI_BUS_UNIT = "br123"      # Business Unit code
-EFI_OPERATOR = "op123"      # Operator code
-EFI_STAGING = True          # Use test environment
+EFI_ENABLED = os.getenv("EFI_ENABLED", "False").lower() == "true"
+EFI_CERT_PATH = os.getenv("EFI_CERT_PATH", os.path.join(BASE_DIR, "cert.p12"))
+EFI_CERT_PASS = os.getenv("EFI_CERT_PASS", "changeit")
+EFI_ENU_CODE = os.getenv("EFI_ENU_CODE", "xx123yy456") 
+EFI_BUS_UNIT = os.getenv("EFI_BUS_UNIT", "br123")      
+EFI_OPERATOR = os.getenv("EFI_OPERATOR", "op123")      
+EFI_STAGING = os.getenv("EFI_STAGING", "True").lower() == "true"

+ 12 - 6
backend/main.py

@@ -13,14 +13,20 @@ from routers import auth, orders, catalog, portfolio, files, chat, blog, admin
 app = FastAPI(title="Radionica 3D API")
 
 # Configure CORS
+origins = [
+    "http://localhost:5173",
+    "http://127.0.0.1:5173",
+    "http://localhost:5000",
+    "https://radionica3d.com",
+    "https://www.radionica3d.com",
+]
+extra_origins = os.getenv("CORS_ORIGINS")
+if extra_origins:
+    origins.extend(extra_origins.split(","))
+
 app.add_middleware(
     CORSMiddleware,
-    allow_origins=[
-        "http://localhost:5173",
-        "http://127.0.0.1:5173",
-        "http://localhost:5000", # if user uses different port
-        "https://localhost:5173"
-    ],
+    allow_origins=origins,
     allow_credentials=True,
     allow_methods=["*"],
     allow_headers=["*"],

+ 15 - 17
backend/start.sh

@@ -1,22 +1,20 @@
 #!/bin/bash
-# Скрипт безопасного запуска бэкенда (Production / Linux)
 
-echo "🧪 Running backend tests..."
-# Ищем pytest в виртуальном окружении
-if [ -f ".venv/bin/pytest" ]; then
-    PYTEST_BIN=".venv/bin/pytest"
-else
-    PYTEST_BIN="pytest"
-fi
+# Скрипт запуска бэкенда для продакшена (Gunicorn + Uvicorn workers)
+# Ожидается, что этот скрипт запускается внутри виртуального окружения или Docker
 
-$PYTEST_BIN tests/ -v
-TEST_STATUS=$?
+echo "🚀 Запуск Radionica3D Backend (Production)..."
 
-if [ $TEST_STATUS -ne 0 ]; then
-  echo "❌ Tests failed! Aborting server start to prevent broken deployments."
-  exit $TEST_STATUS
-fi
+# Путь до PrusaSlicer (если не задан через ENV)
+export SLICER_PATH=${SLICER_PATH:-"prusa-slicer"}
 
-echo "✅ Tests passed successfully! Starting Uvicorn backend..."
-# Запуск Uvicorn с рабочими процессами
-.venv/bin/uvicorn main:app --host 127.0.0.1 --port 8000 --workers 4
+# Запуск через Gunicorn для стабильности и параллелизма
+# -w 4: 4 рабочих процесса (стоит адаптировать под количество ядер)
+# -k uvicorn.workers.UvicornWorker: использование uvicorn для асинхронности
+gunicorn main:app \
+    --workers 4 \
+    --worker-class uvicorn.workers.UvicornWorker \
+    --bind 0.0.0.0:8000 \
+    --access-logformat '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s" %(L)s' \
+    --access-logfile - \
+    --error-logfile -

+ 21 - 9
build_frontend.sh

@@ -1,24 +1,36 @@
 #!/bin/bash
 
 # Скрипт для чистой сборки фронтенда для production
+set -e # Остановка при любой ошибке
 
 echo "🚀 Начинаем сборку Radionica3D Frontend (Production)..."
 
-# 1. Устанавливаем зависимости чисто и надежно
-echo "📦 Установка NPM-зависимостей..."
-npm ci
+# 1. Проверка окружения
+if ! command -v npm &> /dev/null; then
+    echo "❌ Ошибка: npm не установлен."
+    exit 1
+fi
 
-# 2. Очищаем старую сборку (если есть) на всякий случай
-echo "🧹 Очистка старых данных..."
-rm -rf dist
+# 2. Установка зависимостей
+echo "📦 Установка NPM-зависимостей (clean install)..."
+npm ci
 
 # 3. Генерируем локализации
 echo "🌐 Генерация файлов локализации..."
 python scripts/manage_locales.py split
 
-# 4. Запускаем сборку Vue
+# 4. Проверка типов
+echo "🔍 Проверка типов (TypeScript)..."
+npm run i18n:generate && npx vue-tsc --noEmit
+
+# 5. Запускаем сборку Vue
 echo "🔨 Сборка проекта (Vite / Vue 3)..."
 npm run build
 
-echo "✅ Готово! Продакшен-файлы лежат в директории ./dist"
-echo "Теперь вы можете скопировать папку ./dist на ваш Nginx или другой веб-сервер."
+echo "--------------------------------------------------"
+echo "✅ Готово! Продакшен-файлы лежат в директории: ./dist"
+echo "--------------------------------------------------"
+echo "Дальнейшие шаги:"
+echo "1. Скопируйте содержимое ./dist на сервер в /var/www/radionica3d/html"
+echo "2. Настройте Nginx, используя файл nginx.conf"
+echo "3. Подробности в PRODUCTION_SETUP.md"

+ 43 - 0
deploy.cmd

@@ -0,0 +1,43 @@
+@echo off
+setlocal enabledelayedexpansion
+
+:: --- НАСТРОЙКИ ---
+set REMOTE_HOST=148.230.71.134
+set REMOTE_USER=root
+set REMOTE_DIR=/var/www/radionica3d
+set CHOWN_USER=www-data
+:: -----------------
+
+echo [1/5] 🚀 Nachinaem NADEZHNYY deploy (TAR + SCP)...
+
+:: 1. Сборка фронтенда
+echo [2/5] 🔨 Sborka frontenda...
+python scripts/manage_locales.py split
+call npm run build
+if %ERRORLEVEL% neq 0 (echo Build failed! & exit /b %ERRORLEVEL%)
+
+:: 2. Упаковка в архив (пакуем только то, что нужно)
+echo [3/5] 📦 Upakovka proyekta...
+:: Удаляем старый архив если остался
+if exist deploy.tar.gz del deploy.tar.gz
+:: Пакуем dist, backend (без venv) и конфиги
+tar -czf deploy.tar.gz dist/ backend/ nginx.conf radionica-backend.service --exclude=backend/venv --exclude=backend/__pycache__ --exclude=backend/uploads
+if %ERRORLEVEL% neq 0 (echo Archiving failed! & exit /b %ERRORLEVEL%)
+
+:: 3. Отправка на сервер
+echo [4/5] 📨 Otprafka arkhiva...
+scp deploy.tar.gz %REMOTE_USER%@%REMOTE_HOST%:~/
+if %ERRORLEVEL% neq 0 (echo Upload failed! & exit /b %ERRORLEVEL%)
+
+:: 4. Команды на сервере
+echo [5/5] ⚙️ Nastroyka servera...
+ssh %REMOTE_USER%@%REMOTE_HOST% "mkdir -p ~/tmp_deploy_dir && tar -xzf ~/deploy.tar.gz -C ~/tmp_deploy_dir && sudo mkdir -p %REMOTE_DIR%/html && sudo mkdir -p %REMOTE_DIR%/backend && sudo cp -r ~/tmp_deploy_dir/dist/* %REMOTE_DIR%/html/ && cd %REMOTE_DIR%/backend && ([ -d venv ] || python3 -m venv venv) && source venv/bin/activate && (cmp -s ~/tmp_deploy_dir/backend/requirements.txt requirements.txt || (cp ~/tmp_deploy_dir/backend/requirements.txt requirements.txt && pip install -r requirements.txt && pip install gunicorn uvicorn)) && sudo cp -r ~/tmp_deploy_dir/backend/* . && sudo cp ~/tmp_deploy_dir/nginx.conf /etc/nginx/sites-available/radionica3d && sudo ln -sf /etc/nginx/sites-available/radionica3d /etc/nginx/sites-enabled/ && (sudo nginx -t && sudo systemctl restart nginx) && sudo cp ~/tmp_deploy_dir/radionica-backend.service /etc/systemd/system/ && sudo systemctl daemon-reload && sudo systemctl restart radionica-backend && sudo chown -R %CHOWN_USER%:%CHOWN_USER% %REMOTE_DIR% && rm ~/deploy.tar.gz && rm -rf ~/tmp_deploy_dir"
+
+if %ERRORLEVEL% neq 0 (echo Remote commands failed! & exit /b %ERRORLEVEL%)
+
+:: 5. Очистка локально
+del deploy.tar.gz
+
+echo.
+echo 🎉 DEPLOY USPESHNO ZAVERSHEN!
+pause

+ 70 - 0
docker-compose.yml

@@ -0,0 +1,70 @@
+version: '3.8'
+
+services:
+  # MySQL Database
+  db:
+    image: mysql:8.0
+    container_name: radionica_db
+    restart: always
+    environment:
+      MYSQL_DATABASE: radionica3d
+      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-very_strong_password}
+    volumes:
+      - db_data:/var/lib/mysql
+    networks:
+      - radionica_net
+
+  # Redis for sessions and rate limiting
+  redis:
+    image: redis:alpine
+    container_name: radionica_redis
+    restart: always
+    networks:
+      - radionica_net
+
+  # Backend (FastAPI)
+  backend:
+    build:
+      context: .
+      dockerfile: backend.Dockerfile
+    container_name: radionica_backend
+    restart: always
+    environment:
+      - DB_HOST=db
+      - DB_NAME=radionica3d
+      - DB_USER=root
+      - DB_PASS=${DB_ROOT_PASSWORD:-very_strong_password}
+      - REDIS_HOST=redis
+      - SYNC_SLICING=True
+      - RADIONICA_DEBUG=False
+    depends_on:
+      - db
+      - redis
+    volumes:
+      - uploads_data:/app/uploads
+    networks:
+      - radionica_net
+
+  # Frontend (Nginx)
+  proxy:
+    image: nginx:alpine
+    container_name: radionica_proxy
+    ports:
+      - "80:80"
+      - "443:443"
+    volumes:
+      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
+      - ./dist:/usr/share/nginx/html:ro
+      - uploads_data:/app/uploads:ro
+    depends_on:
+      - backend
+    networks:
+      - radionica_net
+
+networks:
+  radionica_net:
+    driver: bridge
+
+volumes:
+  db_data:
+  uploads_data:

+ 41 - 0
nginx.conf

@@ -0,0 +1,41 @@
+server {
+    listen 80;
+    server_name radionica3d.com;
+
+    root /usr/share/nginx/html;
+    index index.html;
+
+    # Gzip Compression
+    gzip on;
+    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
+
+    location / {
+        try_files $uri $uri/ /index.html;
+    }
+
+    # Proxy API requests to backend
+    location /api/ {
+        proxy_pass http://backend:8000/;
+        proxy_http_version 1.1;
+        proxy_set_header Upgrade $http_upgrade;
+        proxy_set_header Connection 'upgrade';
+        proxy_set_header Host $host;
+        proxy_cache_bypass $http_upgrade;
+    }
+
+    # WebSocket requests
+    location /ws/ {
+        proxy_pass http://backend:8000/ws/;
+        proxy_http_version 1.1;
+        proxy_set_header Upgrade $http_upgrade;
+        proxy_set_header Connection "Upgrade";
+        proxy_set_header Host $host;
+    }
+
+    # Static uploads (if served via Nginx instead of FastAPI)
+    location /uploads/ {
+        alias /app/uploads/;
+        expires 7d;
+        add_header Cache-Control "public";
+    }
+}

+ 22 - 0
setup_server.sh

@@ -0,0 +1,22 @@
+#!/bin/bash
+
+# Скрипт для начальной подготовки "голого" Ubuntu сервера к работе
+REMOTE_HOST="148.230.71.134"
+REMOTE_USER="ubuntu"
+
+echo "🛠️ Подготовка сервера $REMOTE_HOST к деплою..."
+
+ssh $REMOTE_USER@$REMOTE_HOST << EOF
+    sudo apt update
+    sudo apt install -y python3-pip python3-venv nginx redis-server prusa-slicer libmagic1 curl mysql-client
+    
+    # Создаем базовую структуру папок
+    sudo mkdir -p /var/www/radionica3d
+    sudo chown -R $REMOTE_USER:$REMOTE_USER /var/www/radionica3d
+    
+    # Включаем Redis
+    sudo systemctl enable redis-server
+    sudo systemctl start redis-server
+    
+    echo "✅ Сервер готов к деплою. Теперь вы можете запускать ./deploy.sh"
+EOF

+ 1 - 1
src/components/OrderChat.vue

@@ -92,7 +92,7 @@ import { MessageCircle, Send, Loader2, ShieldCheck, X, Eye } from "lucide-vue-ne
 import { getOrderMessages, sendOrderMessage } from "@/lib/api";
 import { useAuthStore } from "@/stores/auth";
 
-const WS_BASE_URL = "ws://localhost:8000";
+const WS_BASE_URL = import.meta.env.VITE_WS_URL || "ws://localhost:8000";
 
 const props = defineProps<{
   orderId: number;

+ 1 - 1
src/lib/api.ts

@@ -1,6 +1,6 @@
 import i18n from "../i18n";
 
-const API_BASE_URL = 'http://localhost:8000';
+const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:8000';
 
 const getErrorMessage = async (response: Response, defaultMsg: string) => {
   try {

+ 2 - 6
src/locales/en.json

@@ -56,9 +56,7 @@
       "email": "Email",
       "individual": "Individual",
       "newPassword": "New Password",
-      "password": "Password",
-      "captcha_label": "Security Phrase: Type \"Radionica3D\"",
-      "captcha_placeholder": "Radionica3D"
+      "password": "Password"
     },
     "forgot": {
       "link": "Forgot Password?",
@@ -206,8 +204,7 @@
     "open": "Chat",
     "placeholder": "Type a message...",
     "title": "Order Chat",
-    "unread": "New message",
-    "new_status": "Status Update: Order #{id}"
+    "unread": "New message"
   },
   "common": {
     "back": "Back",
@@ -488,7 +485,6 @@
   },
   "nav": {
     "admin": "Admin",
-    "captcha_required": "Security verification required. Please verify you are human.",
     "adminPanel": "Admin Panel",
     "howItWorks": "How it works",
     "logIn": "Log In",

+ 2 - 6
src/locales/me.json

@@ -56,9 +56,7 @@
       "email": "Email",
       "individual": "Fizičko lice",
       "newPassword": "Nova lozinka",
-      "password": "Lozinka",
-      "captcha_label": "Kontrolna fraza: Unesite \"Radionica3D\"",
-      "captcha_placeholder": "Radionica3D"
+      "password": "Lozinka"
     },
     "forgot": {
       "link": "Zaboravljena lozinka?",
@@ -203,10 +201,9 @@
   "chat": {
     "admin": "Podrška",
     "empty": "Još nema poruka. Započnite razgovor!",
-    "new_status": "Ažuriranje porudžbine #{id}",
     "open": "Čat",
     "placeholder": "Upišite poruku...",
-    "title": "Chat po porudžbini",
+    "title": "Čat za narudžbu",
     "unread": "Nova poruka"
   },
   "common": {
@@ -488,7 +485,6 @@
   },
   "nav": {
     "admin": "Admin",
-    "captcha_required": "Potrebna sigurnosna provjera. Molimo potvrdite da ste čovjek.",
     "adminPanel": "Admin panel",
     "howItWorks": "Kako to funkcioniše",
     "logIn": "Prijavi se",

+ 3 - 7
src/locales/ru.json

@@ -56,9 +56,7 @@
       "email": "Email",
       "individual": "Частное лицо",
       "newPassword": "Новый пароль",
-      "password": "Пароль",
-      "captcha_label": "Контрольная фраза: Введите \"Radionica3D\"",
-      "captcha_placeholder": "Radionica3D"
+      "password": "Пароль"
     },
     "forgot": {
       "link": "Забыли пароль?",
@@ -206,8 +204,7 @@
     "open": "Чат",
     "placeholder": "Напишите сообщение...",
     "title": "Чат по заказу",
-    "unread": "Новое сообщение",
-    "new_status": "Обновление заказа #{id}"
+    "unread": "Новое сообщение"
   },
   "common": {
     "back": "Назад",
@@ -487,8 +484,7 @@
     "uploadButton": "Заказать печать"
   },
   "nav": {
-    "admin": "Админ-панель",
-    "captcha_required": "Требуется проверка безопасности. Пожалуйста, подтвердите, что вы человек.",
+    "admin": "Админ",
     "adminPanel": "Панель управления",
     "howItWorks": "Как это работает",
     "logIn": "Войти",

+ 2 - 6
src/locales/ua.json

@@ -56,9 +56,7 @@
       "email": "Email",
       "individual": "Приватна особа",
       "newPassword": "Новий пароль",
-      "password": "Пароль",
-      "captcha_label": "Контрольна фраза: Введіть \"Radionica3D\"",
-      "captcha_placeholder": "Radionica3D"
+      "password": "Пароль"
     },
     "forgot": {
       "link": "Забули свій пароль?",
@@ -203,7 +201,6 @@
   "chat": {
     "admin": "Підтримка",
     "empty": "Повідомлень поки що немає. Почніть діалог!",
-    "new_status": "Оновлення замовлення #{id}",
     "open": "Чат",
     "placeholder": "Напишіть повідомлення...",
     "title": "Чат на замовлення",
@@ -487,8 +484,7 @@
     "uploadButton": "Замовити друк"
   },
   "nav": {
-    "admin": "Адмін-панель",
-    "captcha_required": "Потрібна перевірка безпеки. Будь ласка, підтвердіть, що ви людина.",
+    "admin": "Адмін",
     "adminPanel": "Панель управління",
     "howItWorks": "Як це працює",
     "logIn": "Увійти",

+ 3 - 2
src/pages/Contact.vue

@@ -191,7 +191,7 @@
                 <div class="border-2 border-dashed border-gray-300 rounded-lg p-6 text-center hover:border-primary/50 transition-colors">
                   <input 
                     type="file" 
-                    ref="fileInput"
+                    ref="fileInputRef"
                     @change="handleFileUpload"
                     class="hidden"
                     accept=".stl,.obj,.step,.3mf"
@@ -206,7 +206,7 @@
                   </p>
                   <button 
                     type="button"
-                    @click="$refs.fileInput.click()"
+                    @click="fileInputRef?.click()"
                     class="px-4 py-2 bg-gray-100 text-foreground font-bold rounded-lg hover:bg-gray-200 transition-colors"
                   >
                     {{ t("contact.form.file.button") }}
@@ -237,6 +237,7 @@
 import { ref } from "vue";
 import { useI18n } from "vue-i18n";
 
+const fileInputRef = ref<HTMLInputElement | null>(null);
 const { t } = useI18n();
 
 const form = ref({

+ 1 - 1
src/stores/auth.ts

@@ -36,7 +36,7 @@ export const useAuthStore = defineStore("auth", () => {
 
   let globalWs: WebSocket | null = null;
   let reconnectTimer: number | null = null;
-  const WS_BASE_URL = "ws://localhost:8000";
+  const WS_BASE_URL = import.meta.env.VITE_WS_URL || "ws://localhost:8000";
 
   // ── Audio ──────────────────────────────────────────────────────────────────
   // Single shared AudioContext, created lazily on first user gesture.

+ 1 - 0
test1.txt

@@ -0,0 +1 @@
+123

+ 1 - 0
tsconfig.json

@@ -5,6 +5,7 @@
     "module": "ESNext",
     "lib": ["ES2020", "DOM", "DOM.Iterable"],
     "skipLibCheck": true,
+    "types": ["vite/client"],
     "moduleResolution": "bundler",
     "allowImportingTsExtensions": true,
     "resolveJsonModule": true,

+ 11 - 0
vite.config.ts

@@ -9,6 +9,17 @@ export default defineConfig({
       "@": path.resolve(__dirname, "./src"),
     },
   },
+  build: {
+    chunkSizeWarningLimit: 1000,
+    rollupOptions: {
+      output: {
+        manualChunks: {
+          'three-vendor': ['three'],
+          'ui-vendor': ['lucide-vue-next', '@vueuse/core'],
+        }
+      }
+    }
+  },
   test: {
     globals: true,
     environment: "jsdom",