Compare commits
4 Commits
7fa1901e5f
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ce029534e | ||
| f98e5c88fe | |||
|
|
6498dfa4f4 | ||
|
|
1fa914900d |
1
.postcssrc
Normal file
1
.postcssrc
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
@@ -8,15 +8,22 @@ COPY package*.json ./
|
|||||||
# Install dependencies
|
# Install dependencies
|
||||||
RUN npm install --legacy-peer-deps
|
RUN npm install --legacy-peer-deps
|
||||||
|
|
||||||
# Copy frontend source code and config
|
# Copy all configuration files first
|
||||||
COPY src ./src
|
|
||||||
COPY public ./public
|
|
||||||
COPY index.html ./
|
|
||||||
COPY vite.config.js ./
|
COPY vite.config.js ./
|
||||||
COPY tsconfig.json ./
|
COPY tsconfig.json ./
|
||||||
|
COPY index.html ./
|
||||||
|
COPY vite-env.d.ts ./
|
||||||
|
COPY postcss.config.mjs ./
|
||||||
|
|
||||||
|
# Copy frontend source code
|
||||||
|
COPY src ./src
|
||||||
|
COPY public ./public
|
||||||
|
|
||||||
|
# Build production bundle
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
# Expose the frontend port
|
# Expose the frontend port
|
||||||
EXPOSE ${FRONTEND_PORT:-5173}
|
EXPOSE ${FRONTEND_PORT:-5173}
|
||||||
|
|
||||||
# Start the frontend using the client script
|
# Serve static files using built-in Vite preview
|
||||||
CMD ["npm", "run", "client", "--", "--host"]
|
CMD ["npm", "run", "preview", "--", "--host", "0.0.0.0", "--port", "5173"]
|
||||||
25
README_RU.md
Normal file
25
README_RU.md
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# Dorod Parser
|
||||||
|
|
||||||
|
**Визуальный веб-скрапер без кода с AI-интеграцией**
|
||||||
|
|
||||||
|
## Возможности
|
||||||
|
|
||||||
|
- **Recorder Mode** - записывай действия мышкой, создавай роботов
|
||||||
|
- **AI Mode** - описывай что нужно извлечь естественным языком
|
||||||
|
- **Динамические сайты** - адаптируется к изменениям структуры
|
||||||
|
- **Экспорт** - Google Sheets, Airtable, JSON, CSV
|
||||||
|
- **Self-hosted** - разворачивай на своих серверах через Docker
|
||||||
|
- **REST API** - программное управление через SDK
|
||||||
|
|
||||||
|
## Технологии
|
||||||
|
|
||||||
|
- **Frontend:** React + Vite (порт 3030)
|
||||||
|
- **Backend:** Node.js + Playwright
|
||||||
|
- **Хранилище:** PostgreSQL + MinIO + Redis
|
||||||
|
- **Лицензия:** AGPLv3
|
||||||
|
|
||||||
|
|
||||||
|
docker-compose up
|
||||||
|
\\\
|
||||||
|
|
||||||
|
Открой: **http://localhost:3030**
|
||||||
@@ -7,7 +7,7 @@ services:
|
|||||||
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||||
POSTGRES_DB: ${DB_NAME}
|
POSTGRES_DB: ${DB_NAME}
|
||||||
ports:
|
ports:
|
||||||
- "${DB_PORT:-5432}:${DB_PORT:-5432}"
|
- "5433:5432"
|
||||||
volumes:
|
volumes:
|
||||||
- postgres_data:/var/lib/postgresql/data
|
- postgres_data:/var/lib/postgresql/data
|
||||||
healthcheck:
|
healthcheck:
|
||||||
@@ -22,10 +22,10 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
MINIO_ROOT_USER: ${MINIO_ACCESS_KEY}
|
MINIO_ROOT_USER: ${MINIO_ACCESS_KEY}
|
||||||
MINIO_ROOT_PASSWORD: ${MINIO_SECRET_KEY}
|
MINIO_ROOT_PASSWORD: ${MINIO_SECRET_KEY}
|
||||||
command: server /data --console-address :${MINIO_CONSOLE_PORT:-9001}
|
command: server /data --console-address :9001
|
||||||
ports:
|
ports:
|
||||||
- "${MINIO_PORT:-9000}:${MINIO_PORT:-9000}" # API port
|
- "9020:9000" # API port
|
||||||
- "${MINIO_CONSOLE_PORT:-9001}:${MINIO_CONSOLE_PORT:-9001}" # WebUI port
|
- "9021:9001" # WebUI port
|
||||||
volumes:
|
volumes:
|
||||||
- minio_data:/data
|
- minio_data:/data
|
||||||
|
|
||||||
@@ -35,8 +35,7 @@ services:
|
|||||||
# dockerfile: Dockerfile.backend
|
# dockerfile: Dockerfile.backend
|
||||||
image: getmaxun/maxun-backend:latest
|
image: getmaxun/maxun-backend:latest
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
network_mode: "host"
|
||||||
- "${BACKEND_PORT:-8080}:${BACKEND_PORT:-8080}"
|
|
||||||
env_file: .env
|
env_file: .env
|
||||||
environment:
|
environment:
|
||||||
BACKEND_URL: ${BACKEND_URL}
|
BACKEND_URL: ${BACKEND_URL}
|
||||||
@@ -65,12 +64,14 @@ services:
|
|||||||
# dockerfile: Dockerfile.frontend
|
# dockerfile: Dockerfile.frontend
|
||||||
image: getmaxun/maxun-frontend:latest
|
image: getmaxun/maxun-frontend:latest
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
network_mode: "host"
|
||||||
- "${FRONTEND_PORT:-5173}:${FRONTEND_PORT:-5173}"
|
|
||||||
env_file: .env
|
env_file: .env
|
||||||
environment:
|
environment:
|
||||||
PUBLIC_URL: ${PUBLIC_URL}
|
PUBLIC_URL: http://localhost:5174
|
||||||
BACKEND_URL: ${BACKEND_URL}
|
BACKEND_URL: http://localhost:8081
|
||||||
|
VITE_BACKEND_URL: http://localhost:8081
|
||||||
|
VITE_PUBLIC_URL: http://localhost:5174
|
||||||
|
command: sh -c "npm run client"
|
||||||
depends_on:
|
depends_on:
|
||||||
- backend
|
- backend
|
||||||
|
|
||||||
@@ -79,11 +80,11 @@ services:
|
|||||||
context: .
|
context: .
|
||||||
dockerfile: browser/Dockerfile
|
dockerfile: browser/Dockerfile
|
||||||
args:
|
args:
|
||||||
BROWSER_WS_PORT: ${BROWSER_WS_PORT:-3001}
|
BROWSER_WS_PORT: 3001
|
||||||
BROWSER_HEALTH_PORT: ${BROWSER_HEALTH_PORT:-3002}
|
BROWSER_HEALTH_PORT: 3002
|
||||||
ports:
|
ports:
|
||||||
- "${BROWSER_WS_PORT:-3001}:${BROWSER_WS_PORT:-3001}"
|
- "3011:3001"
|
||||||
- "${BROWSER_HEALTH_PORT:-3002}:${BROWSER_HEALTH_PORT:-3002}"
|
- "3012:3002"
|
||||||
environment:
|
environment:
|
||||||
- NODE_ENV=production
|
- NODE_ENV=production
|
||||||
- DEBUG=pw:browser*
|
- DEBUG=pw:browser*
|
||||||
|
|||||||
@@ -54,7 +54,7 @@
|
|||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"loglevel": "^1.8.0",
|
"loglevel": "^1.8.0",
|
||||||
"loglevel-plugin-remote": "^0.6.8",
|
"loglevel-plugin-remote": "^0.6.8",
|
||||||
"Dorod Parser-core": "^0.0.31",
|
"maxun-core": "^0.0.31",
|
||||||
"minio": "^8.0.1",
|
"minio": "^8.0.1",
|
||||||
"moment-timezone": "^0.5.45",
|
"moment-timezone": "^0.5.45",
|
||||||
"node-cron": "^3.0.3",
|
"node-cron": "^3.0.3",
|
||||||
|
|||||||
1
postcss.config.cjs
Normal file
1
postcss.config.cjs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
module.exports = {};
|
||||||
468
public/locales/ru.json
Normal file
468
public/locales/ru.json
Normal file
@@ -0,0 +1,468 @@
|
|||||||
|
{
|
||||||
|
"login": {
|
||||||
|
"title": "С возвращением!",
|
||||||
|
"email": "Введите рабочий email",
|
||||||
|
"password": "Пароль",
|
||||||
|
"button": "Войти",
|
||||||
|
"loading": "Загрузка",
|
||||||
|
"register_prompt": "Нет аккаунта?",
|
||||||
|
"register_link": "Зарегистрироваться",
|
||||||
|
"welcome_notification": "Добро пожаловать в Dorod Parser!",
|
||||||
|
"validation": {
|
||||||
|
"required_fields": "Email и пароль обязательны",
|
||||||
|
"password_length": "Пароль должен быть не менее 6 символов"
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"user_not_found": "Пользователь не существует",
|
||||||
|
"invalid_credentials": "Неверный email или пароль",
|
||||||
|
"server_error": "Ошибка входа. Попробуйте позже",
|
||||||
|
"generic": "Произошла ошибка. Попробуйте снова"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"register": {
|
||||||
|
"title": "Создать аккаунт",
|
||||||
|
"email": "Введите рабочий email",
|
||||||
|
"password": "Пароль",
|
||||||
|
"button": "Зарегистрироваться",
|
||||||
|
"loading": "Загрузка",
|
||||||
|
"register_prompt": "Уже есть аккаунт?",
|
||||||
|
"login_link": "Войти",
|
||||||
|
"welcome_notification": "Добро пожаловать в Dorod Parser!",
|
||||||
|
"validation": {
|
||||||
|
"email_required": "Email обязателен",
|
||||||
|
"password_requirements": "Пароль должен быть не менее 6 символов"
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"user_exists": "Пользователь с таким email уже существует",
|
||||||
|
"creation_failed": "Не удалось создать аккаунт",
|
||||||
|
"server_error": "Ошибка сервера",
|
||||||
|
"generic": "Регистрация не удалась. Попробуйте снова"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"recordingtable":{
|
||||||
|
"run": "Запустить",
|
||||||
|
"name": "Название",
|
||||||
|
"schedule": "Расписание",
|
||||||
|
"integrate": "Интеграция",
|
||||||
|
"settings": "Настройки",
|
||||||
|
"options": "Опции",
|
||||||
|
"heading":"Мои роботы",
|
||||||
|
"new":"Создать робота",
|
||||||
|
"deleteModalText": "Вы уверены, что хотите удалить этого робота?",
|
||||||
|
"delete": "Удалить",
|
||||||
|
"cancel": "Отмена",
|
||||||
|
"deleteSuccess": "Робот успешно удален",
|
||||||
|
"deleteFailed": "Не удалось удалить робота",
|
||||||
|
"search": "Поиск роботов..."
|
||||||
|
},
|
||||||
|
"mainmenu": {
|
||||||
|
"recordings": "Роботы",
|
||||||
|
"runs": "Запуски",
|
||||||
|
"proxy": "Прокси",
|
||||||
|
"apikey": "API ключ",
|
||||||
|
"feedback": "Присоединиться к Maxun Cloud",
|
||||||
|
"apidocs": "Веб-сайт в API"
|
||||||
|
},
|
||||||
|
"recordingpage": {
|
||||||
|
"stopRecording": "Остановить запись",
|
||||||
|
"recording": "Запись...",
|
||||||
|
"extract": "Извлечь",
|
||||||
|
"actions": "Действия",
|
||||||
|
"goto": "Перейти",
|
||||||
|
"click": "Клик",
|
||||||
|
"type": "Ввод",
|
||||||
|
"scroll": "Прокрутка",
|
||||||
|
"hover": "Наведение",
|
||||||
|
"select": "Выбор",
|
||||||
|
"clear": "Очистить",
|
||||||
|
"press": "Нажать",
|
||||||
|
"upload": "Загрузить",
|
||||||
|
"download": "Скачать",
|
||||||
|
"screenshot": "Скриншот",
|
||||||
|
"wait": "Ожидание",
|
||||||
|
"refresh": "Обновить",
|
||||||
|
"back": "Назад",
|
||||||
|
"forward": "Вперед",
|
||||||
|
"close": "Закрыть",
|
||||||
|
"newTab": "Новая вкладка",
|
||||||
|
"switchTab": "Переключить вкладку",
|
||||||
|
"pagination": "Пагинация",
|
||||||
|
"captcha": "Капча",
|
||||||
|
"saveRecording": "Сохранить запись",
|
||||||
|
"discardRecording": "Отменить запись",
|
||||||
|
"playRecording": "Воспроизвести",
|
||||||
|
"editStep": "Редактировать шаг",
|
||||||
|
"deleteStep": "Удалить шаг",
|
||||||
|
"addStep": "Добавить шаг",
|
||||||
|
"recordingName": "Название записи",
|
||||||
|
"enterUrl": "Введите URL",
|
||||||
|
"startRecording": "Начать запись",
|
||||||
|
"noSteps": "Нет записанных действий",
|
||||||
|
"error": {
|
||||||
|
"recording_failed": "Ошибка записи",
|
||||||
|
"save_failed": "Не удалось сохранить",
|
||||||
|
"invalid_url": "Неверный URL"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"robotSettings": {
|
||||||
|
"title": "Настройки робота",
|
||||||
|
"name": "Название",
|
||||||
|
"description": "Описание",
|
||||||
|
"url": "Начальный URL",
|
||||||
|
"proxy": "Прокси",
|
||||||
|
"useProxy": "Использовать прокси",
|
||||||
|
"proxyUrl": "URL прокси",
|
||||||
|
"userAgent": "User Agent",
|
||||||
|
"customHeaders": "Дополнительные заголовки",
|
||||||
|
"headerName": "Название",
|
||||||
|
"headerValue": "Значение",
|
||||||
|
"addHeader": "Добавить заголовок",
|
||||||
|
"timeout": "Тайм-аут (мс)",
|
||||||
|
"retries": "Повторные попытки",
|
||||||
|
"screenshot": "Делать скриншоты",
|
||||||
|
"notifications": "Уведомления",
|
||||||
|
"emailNotifications": "Email уведомления",
|
||||||
|
"webhookUrl": "Webhook URL",
|
||||||
|
"save": "Сохранить",
|
||||||
|
"cancel": "Отмена",
|
||||||
|
"advanced": "Расширенные настройки",
|
||||||
|
"basic": "Основные настройки"
|
||||||
|
},
|
||||||
|
"schedule": {
|
||||||
|
"title": "Настройка расписания",
|
||||||
|
"enabled": "Включить расписание",
|
||||||
|
"frequency": "Частота",
|
||||||
|
"hourly": "Каждый час",
|
||||||
|
"daily": "Ежедневно",
|
||||||
|
"weekly": "Еженедельно",
|
||||||
|
"monthly": "Ежемесячно",
|
||||||
|
"custom": "Настроить",
|
||||||
|
"time": "Время",
|
||||||
|
"timezone": "Часовой пояс",
|
||||||
|
"days": "Дни недели",
|
||||||
|
"monday": "Понедельник",
|
||||||
|
"tuesday": "Вторник",
|
||||||
|
"wednesday": "Среда",
|
||||||
|
"thursday": "Четверг",
|
||||||
|
"friday": "Пятница",
|
||||||
|
"saturday": "Суббота",
|
||||||
|
"sunday": "Воскресенье",
|
||||||
|
"nextRun": "Следующий запуск",
|
||||||
|
"lastRun": "Последний запуск",
|
||||||
|
"save": "Сохранить расписание",
|
||||||
|
"cancel": "Отмена"
|
||||||
|
},
|
||||||
|
"integrate": {
|
||||||
|
"title": "Интеграции",
|
||||||
|
"export": "Экспорт данных",
|
||||||
|
"googleSheets": "Google Sheets",
|
||||||
|
"airtable": "Airtable",
|
||||||
|
"webhook": "Webhook",
|
||||||
|
"api": "REST API",
|
||||||
|
"connect": "Подключить",
|
||||||
|
"disconnect": "Отключить",
|
||||||
|
"connected": "Подключено",
|
||||||
|
"notConnected": "Не подключено",
|
||||||
|
"testConnection": "Проверить подключение",
|
||||||
|
"exportFormat": "Формат экспорта",
|
||||||
|
"json": "JSON",
|
||||||
|
"csv": "CSV",
|
||||||
|
"excel": "Excel",
|
||||||
|
"save": "Сохранить настройки"
|
||||||
|
},
|
||||||
|
"runs": {
|
||||||
|
"title": "История запусков",
|
||||||
|
"status": "Статус",
|
||||||
|
"success": "Успешно",
|
||||||
|
"failed": "Ошибка",
|
||||||
|
"running": "Выполняется",
|
||||||
|
"pending": "Ожидание",
|
||||||
|
"cancelled": "Отменено",
|
||||||
|
"startTime": "Время начала",
|
||||||
|
"endTime": "Время окончания",
|
||||||
|
"duration": "Длительность",
|
||||||
|
"recordsExtracted": "Извлечено записей",
|
||||||
|
"viewDetails": "Подробности",
|
||||||
|
"viewLogs": "Логи",
|
||||||
|
"downloadData": "Скачать данные",
|
||||||
|
"retry": "Повторить",
|
||||||
|
"cancel": "Отменить",
|
||||||
|
"noRuns": "Нет запусков"
|
||||||
|
},
|
||||||
|
"extraction": {
|
||||||
|
"title": "Извлечение данных",
|
||||||
|
"addField": "Добавить поле",
|
||||||
|
"fieldName": "Название поля",
|
||||||
|
"selector": "Селектор",
|
||||||
|
"type": "Тип",
|
||||||
|
"text": "Текст",
|
||||||
|
"attribute": "Атрибут",
|
||||||
|
"html": "HTML",
|
||||||
|
"link": "Ссылка",
|
||||||
|
"image": "Изображение",
|
||||||
|
"list": "Список",
|
||||||
|
"table": "Таблица",
|
||||||
|
"required": "Обязательное",
|
||||||
|
"optional": "Опциональное",
|
||||||
|
"removeField": "Удалить поле",
|
||||||
|
"testExtraction": "Тестировать",
|
||||||
|
"preview": "Предпросмотр",
|
||||||
|
"noData": "Нет данных"
|
||||||
|
},
|
||||||
|
"ai": {
|
||||||
|
"title": "AI режим",
|
||||||
|
"prompt": "Опишите, что нужно извлечь",
|
||||||
|
"example": "Например: извлеките все названия товаров, цены и артикулы",
|
||||||
|
"generate": "Сгенерировать",
|
||||||
|
"generating": "Генерация...",
|
||||||
|
"apply": "Применить",
|
||||||
|
"edit": "Редактировать",
|
||||||
|
"model": "Модель AI",
|
||||||
|
"temperature": "Температура",
|
||||||
|
"maxTokens": "Макс. токенов",
|
||||||
|
"error": {
|
||||||
|
"generation_failed": "Не удалось сгенерировать",
|
||||||
|
"api_error": "Ошибка API",
|
||||||
|
"invalid_prompt": "Неверный промпт"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pagination": {
|
||||||
|
"title": "Настройка пагинации",
|
||||||
|
"enabled": "Включить пагинацию",
|
||||||
|
"type": "Тип",
|
||||||
|
"button": "Кнопка",
|
||||||
|
"scroll": "Прокрутка",
|
||||||
|
"url": "URL паттерн",
|
||||||
|
"nextButton": "Селектор кнопки 'Далее'",
|
||||||
|
"maxPages": "Макс. страниц",
|
||||||
|
"waitTime": "Время ожидания (мс)",
|
||||||
|
"stopCondition": "Условие остановки",
|
||||||
|
"noMorePages": "Нет больше страниц",
|
||||||
|
"maxPagesReached": "Достигнут лимит страниц",
|
||||||
|
"custom": "Настроить"
|
||||||
|
},
|
||||||
|
"captcha": {
|
||||||
|
"title": "Обработка капчи",
|
||||||
|
"enabled": "Включить решение капчи",
|
||||||
|
"service": "Сервис",
|
||||||
|
"twoCaptcha": "2Captcha",
|
||||||
|
"antiCaptcha": "AntiCaptcha",
|
||||||
|
"manual": "Вручную",
|
||||||
|
"apiKey": "API ключ",
|
||||||
|
"timeout": "Тайм-аут (сек)",
|
||||||
|
"test": "Тестировать",
|
||||||
|
"save": "Сохранить"
|
||||||
|
},
|
||||||
|
"proxy": {
|
||||||
|
"title": "Настройки прокси",
|
||||||
|
"enabled": "Использовать прокси",
|
||||||
|
"type": "Тип",
|
||||||
|
"http": "HTTP",
|
||||||
|
"https": "HTTPS",
|
||||||
|
"socks4": "SOCKS4",
|
||||||
|
"socks5": "SOCKS5",
|
||||||
|
"host": "Хост",
|
||||||
|
"port": "Порт",
|
||||||
|
"username": "Имя пользователя",
|
||||||
|
"password": "Пароль",
|
||||||
|
"testConnection": "Проверить подключение",
|
||||||
|
"success": "Прокси работает",
|
||||||
|
"failed": "Ошибка подключения"
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"title": "Настройки",
|
||||||
|
"account": "Аккаунт",
|
||||||
|
"profile": "Профиль",
|
||||||
|
"security": "Безопасность",
|
||||||
|
"notifications": "Уведомления",
|
||||||
|
"api": "API ключи",
|
||||||
|
"billing": "Биллинг",
|
||||||
|
"theme": "Тема",
|
||||||
|
"language": "Язык",
|
||||||
|
"light": "Светлая",
|
||||||
|
"dark": "Темная",
|
||||||
|
"auto": "Авто",
|
||||||
|
"save": "Сохранить",
|
||||||
|
"cancel": "Отмена",
|
||||||
|
"logout": "Выйти"
|
||||||
|
},
|
||||||
|
"profile": {
|
||||||
|
"title": "Профиль",
|
||||||
|
"name": "Имя",
|
||||||
|
"email": "Email",
|
||||||
|
"company": "Компания",
|
||||||
|
"website": "Веб-сайт",
|
||||||
|
"avatar": "Аватар",
|
||||||
|
"changeAvatar": "Изменить аватар",
|
||||||
|
"save": "Сохранить",
|
||||||
|
"updated": "Профиль обновлен"
|
||||||
|
},
|
||||||
|
"security": {
|
||||||
|
"title": "Безопасность",
|
||||||
|
"changePassword": "Изменить пароль",
|
||||||
|
"currentPassword": "Текущий пароль",
|
||||||
|
"newPassword": "Новый пароль",
|
||||||
|
"confirmPassword": "Подтвердите пароль",
|
||||||
|
"twoFactor": "Двухфакторная аутентификация",
|
||||||
|
"enable2FA": "Включить 2FA",
|
||||||
|
"disable2FA": "Отключить 2FA",
|
||||||
|
"apiKeys": "API ключи",
|
||||||
|
"generateKey": "Сгенерировать ключ",
|
||||||
|
"revokeKey": "Отозвать ключ",
|
||||||
|
"save": "Сохранить"
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"title": "Уведомления",
|
||||||
|
"email": "Email уведомления",
|
||||||
|
"runComplete": "Завершение запуска",
|
||||||
|
"runFailed": "Ошибка запуска",
|
||||||
|
"dailyReport": "Ежедневный отчет",
|
||||||
|
"weeklyReport": "Еженедельный отчет",
|
||||||
|
"webhook": "Webhook уведомления",
|
||||||
|
"webhookUrl": "Webhook URL",
|
||||||
|
"testWebhook": "Тестировать",
|
||||||
|
"save": "Сохранить"
|
||||||
|
},
|
||||||
|
"common": {
|
||||||
|
"save": "Сохранить",
|
||||||
|
"cancel": "Отмена",
|
||||||
|
"delete": "Удалить",
|
||||||
|
"edit": "Редактировать",
|
||||||
|
"create": "Создать",
|
||||||
|
"update": "Обновить",
|
||||||
|
"close": "Закрыть",
|
||||||
|
"back": "Назад",
|
||||||
|
"next": "Далее",
|
||||||
|
"previous": "Назад",
|
||||||
|
"finish": "Завершить",
|
||||||
|
"loading": "Загрузка...",
|
||||||
|
"saving": "Сохранение...",
|
||||||
|
"deleting": "Удаление...",
|
||||||
|
"success": "Успешно",
|
||||||
|
"error": "Ошибка",
|
||||||
|
"warning": "Предупреждение",
|
||||||
|
"info": "Информация",
|
||||||
|
"confirm": "Подтвердить",
|
||||||
|
"yes": "Да",
|
||||||
|
"no": "Нет",
|
||||||
|
"ok": "ОК",
|
||||||
|
"search": "Поиск",
|
||||||
|
"filter": "Фильтр",
|
||||||
|
"sort": "Сортировка",
|
||||||
|
"export": "Экспорт",
|
||||||
|
"import": "Импорт",
|
||||||
|
"download": "Скачать",
|
||||||
|
"upload": "Загрузить",
|
||||||
|
"copy": "Копировать",
|
||||||
|
"paste": "Вставить",
|
||||||
|
"cut": "Вырезать",
|
||||||
|
"select_all": "Выбрать все",
|
||||||
|
"clear_all": "Очистить все",
|
||||||
|
"refresh": "Обновить",
|
||||||
|
"settings": "Настройки",
|
||||||
|
"help": "Помощь",
|
||||||
|
"about": "О программе",
|
||||||
|
"version": "Версия",
|
||||||
|
"copyright": " 2026 Dorod Parser"
|
||||||
|
},
|
||||||
|
"errors": {
|
||||||
|
"generic": "Произошла ошибка",
|
||||||
|
"network": "Ошибка сети",
|
||||||
|
"timeout": "Превышен тайм-аут",
|
||||||
|
"not_found": "Не найдено",
|
||||||
|
"unauthorized": "Не авторизован",
|
||||||
|
"forbidden": "Доступ запрещен",
|
||||||
|
"server_error": "Ошибка сервера",
|
||||||
|
"validation": "Ошибка валидации",
|
||||||
|
"try_again": "Попробуйте снова"
|
||||||
|
},
|
||||||
|
"success": {
|
||||||
|
"saved": "Сохранено",
|
||||||
|
"deleted": "Удалено",
|
||||||
|
"updated": "Обновлено",
|
||||||
|
"created": "Создано",
|
||||||
|
"sent": "Отправлено",
|
||||||
|
"copied": "Скопировано"
|
||||||
|
},
|
||||||
|
"robotCreate": {
|
||||||
|
"title": "Создать нового робота",
|
||||||
|
"tabs": {
|
||||||
|
"extract": "Извлечь",
|
||||||
|
"scrape": "Скрейпинг",
|
||||||
|
"crawl": "Обход",
|
||||||
|
"search": "Поиск"
|
||||||
|
},
|
||||||
|
"chooseMode": "Выберите способ создания",
|
||||||
|
"modes": {
|
||||||
|
"recorder": {
|
||||||
|
"title": "Режим записи",
|
||||||
|
"description": "Запишите свои действия в рабочий процесс"
|
||||||
|
},
|
||||||
|
"ai": {
|
||||||
|
"title": "AI режим",
|
||||||
|
"description": "Опишите задачу. Он создаст её для вас",
|
||||||
|
"label": "Beta"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"extract": {
|
||||||
|
"description": "Извлекайте структурированные данные с веб-сайтов используя AI или записывайте свой собственный процесс извлечения",
|
||||||
|
"websiteUrl": "URL веб-сайта",
|
||||||
|
"websiteUrlOptional": "URL веб-сайта (необязательно)",
|
||||||
|
"websiteUrlPlaceholder": "Например: https://www.ycombinator.com/companies/",
|
||||||
|
"startRecording": "Начать запись",
|
||||||
|
"starting": "Запуск...",
|
||||||
|
"name": "Название",
|
||||||
|
"namePlaceholder": "Название",
|
||||||
|
"aiPrompt": "Промпт для извлечения",
|
||||||
|
"aiPromptPlaceholder": "Например: Извлечь первые 15 названий компаний, описания и информацию о партиях",
|
||||||
|
"aiExample": "Например: 'Извлечь названия продуктов, цены и рейтинги'",
|
||||||
|
"llmProvider": "LLM Провайдер",
|
||||||
|
"llmProviderOllama": "Ollama (Локально)",
|
||||||
|
"llmProviderAnthropic": "Anthropic (Claude)",
|
||||||
|
"llmProviderOpenAI": "OpenAI (GPT-4)",
|
||||||
|
"model": "Модель",
|
||||||
|
"modelDefault": "По умолчанию (llama3.2-vision)",
|
||||||
|
"ollamaBaseUrl": "Ollama Base URL (необязательно)",
|
||||||
|
"generate": "Создать робота",
|
||||||
|
"generating": "Создание...",
|
||||||
|
"createAndRun": "Создать и запустить робота",
|
||||||
|
"creatingAndRunning": "Создание и запуск...",
|
||||||
|
"apiKey": "API ключ (необязательно, если установлен в .env)",
|
||||||
|
"apiKeyPlaceholder": "API ключ"
|
||||||
|
},
|
||||||
|
"scrape": {
|
||||||
|
"description": "Скрейпить весь контент страницы в различных форматах",
|
||||||
|
"websiteUrl": "URL веб-сайта",
|
||||||
|
"robotName": "Название робота (необязательно)",
|
||||||
|
"outputFormats": "Форматы вывода",
|
||||||
|
"createRobot": "Создать робота",
|
||||||
|
"creating": "Создание..."
|
||||||
|
},
|
||||||
|
"crawl": {
|
||||||
|
"description": "Обходить веб-сайт и извлекать данные с нескольких страниц",
|
||||||
|
"websiteUrl": "URL веб-сайта",
|
||||||
|
"robotName": "Название робота (необязательно)",
|
||||||
|
"maxPages": "Максимум страниц для обхода",
|
||||||
|
"maxDepth": "Максимальная глубина обхода",
|
||||||
|
"includePaths": "Включить пути (через запятую, необязательно)",
|
||||||
|
"excludePaths": "Исключить пути (через запятую, необязательно)",
|
||||||
|
"createRobot": "Создать робота",
|
||||||
|
"creating": "Создание...",
|
||||||
|
"advancedOptions": "Дополнительные опции"
|
||||||
|
},
|
||||||
|
"search": {
|
||||||
|
"description": "Искать информацию в интернете используя AI",
|
||||||
|
"query": "Поисковой запрос",
|
||||||
|
"queryPlaceholder": "О чём вы хотите узнать?",
|
||||||
|
"robotName": "Название робота (необязательно)",
|
||||||
|
"createRobot": "Создать поискового робота",
|
||||||
|
"creating": "Создание..."
|
||||||
|
},
|
||||||
|
"errors": {
|
||||||
|
"urlRequired": "URL обязателен",
|
||||||
|
"queryRequired": "Поисковой запрос обязателен",
|
||||||
|
"failedToStart": "Не удалось начать запись. Попробуйте снова",
|
||||||
|
"failedToCreate": "Не удалось создать робота"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -364,6 +364,14 @@ export const NavBar: React.FC<NavBarProps> = ({
|
|||||||
>
|
>
|
||||||
Türkçe
|
Türkçe
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
<MenuItem
|
||||||
|
onClick={() => {
|
||||||
|
changeLanguage("ru");
|
||||||
|
handleMenuClose();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Русский
|
||||||
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
window.open('https://docs.maxun.dev/development/i18n', '_blank');
|
window.open('https://docs.maxun.dev/development/i18n', '_blank');
|
||||||
@@ -468,6 +476,14 @@ export const NavBar: React.FC<NavBarProps> = ({
|
|||||||
>
|
>
|
||||||
Türkçe
|
Türkçe
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
<MenuItem
|
||||||
|
onClick={() => {
|
||||||
|
changeLanguage("ru");
|
||||||
|
handleMenuClose();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Русский
|
||||||
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
window.open('https://docs.maxun.dev/development/i18n', '_blank');
|
window.open('https://docs.maxun.dev/development/i18n', '_blank');
|
||||||
|
|||||||
@@ -276,7 +276,7 @@ const RobotCreate: React.FC = () => {
|
|||||||
<ArrowBack />
|
<ArrowBack />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<Typography variant="h5" component="h1">
|
<Typography variant="h5" component="h1">
|
||||||
Create New Robot
|
{t('robotCreate.title')}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
@@ -299,10 +299,10 @@ const RobotCreate: React.FC = () => {
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Tab label="Extract" id="extract-robot" aria-controls="extract-robot" />
|
<Tab label={t('robotCreate.tabs.extract')} id="extract-robot" aria-controls="extract-robot" />
|
||||||
<Tab label="Scrape" id="scrape-robot" aria-controls="scrape-robot" />
|
<Tab label={t('robotCreate.tabs.scrape')} id="scrape-robot" aria-controls="scrape-robot" />
|
||||||
<Tab label="Crawl" id="crawl-robot" aria-controls="crawl-robot" />
|
<Tab label={t('robotCreate.tabs.crawl')} id="crawl-robot" aria-controls="crawl-robot" />
|
||||||
<Tab label="Search" id="search-robot" aria-controls="search-robot" />
|
<Tab label={t('robotCreate.tabs.search')} id="search-robot" aria-controls="search-robot" />
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
@@ -321,11 +321,11 @@ const RobotCreate: React.FC = () => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<Typography variant="body2" color="text.secondary" mb={3}>
|
<Typography variant="body2" color="text.secondary" mb={3}>
|
||||||
Extract structured data from websites using AI or record your own extraction workflow.
|
{t('robotCreate.extract.description')}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Box sx={{ width: '100%', maxWidth: 700, mb: 3 }}>
|
<Box sx={{ width: '100%', maxWidth: 700, mb: 3 }}>
|
||||||
<Typography variant="subtitle1" gutterBottom sx={{ mb: 2 }} color="text.secondary">
|
<Typography variant="subtitle1" gutterBottom sx={{ mb: 2 }} color="text.secondary">
|
||||||
Choose How to Build
|
{t('robotCreate.chooseMode')}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Box sx={{ display: 'flex', gap: 2 }}>
|
<Box sx={{ display: 'flex', gap: 2 }}>
|
||||||
@@ -345,10 +345,10 @@ const RobotCreate: React.FC = () => {
|
|||||||
<CardContent sx={{ textAlign: 'center', py: 3, color:"text.secondary" }}>
|
<CardContent sx={{ textAlign: 'center', py: 3, color:"text.secondary" }}>
|
||||||
<HighlightAlt sx={{ fontSize: 32, mb: 1 }} />
|
<HighlightAlt sx={{ fontSize: 32, mb: 1 }} />
|
||||||
<Typography variant="h6" gutterBottom>
|
<Typography variant="h6" gutterBottom>
|
||||||
Recorder Mode
|
{t('robotCreate.modes.recorder.title')}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body2">
|
<Typography variant="body2">
|
||||||
Record your actions into a workflow.
|
{t('robotCreate.modes.recorder.description')}
|
||||||
</Typography>
|
</Typography>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -380,16 +380,16 @@ const RobotCreate: React.FC = () => {
|
|||||||
fontSize: '0.7rem',
|
fontSize: '0.7rem',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Beta
|
{t('robotCreate.modes.ai.label')}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<CardContent sx={{ textAlign: 'center', py: 3, color:"text.secondary" }}>
|
<CardContent sx={{ textAlign: 'center', py: 3, color:"text.secondary" }}>
|
||||||
<AutoAwesome sx={{ fontSize: 32, mb: 1 }} />
|
<AutoAwesome sx={{ fontSize: 32, mb: 1 }} />
|
||||||
<Typography variant="h6" gutterBottom>
|
<Typography variant="h6" gutterBottom>
|
||||||
AI Mode
|
{t('robotCreate.modes.ai.title')}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body2">
|
<Typography variant="body2">
|
||||||
Describe the task. It builds it for you.
|
{t('robotCreate.modes.ai.description')}
|
||||||
</Typography>
|
</Typography>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -399,45 +399,45 @@ const RobotCreate: React.FC = () => {
|
|||||||
<Box sx={{ width: '100%', maxWidth: 700 }}>
|
<Box sx={{ width: '100%', maxWidth: 700 }}>
|
||||||
<Box sx={{ mb: 3 }}>
|
<Box sx={{ mb: 3 }}>
|
||||||
<TextField
|
<TextField
|
||||||
placeholder="Name"
|
placeholder={t('robotCreate.extract.namePlaceholder')}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
fullWidth
|
fullWidth
|
||||||
value={extractRobotName}
|
value={extractRobotName}
|
||||||
onChange={(e) => setExtractRobotName(e.target.value)}
|
onChange={(e) => setExtractRobotName(e.target.value)}
|
||||||
label="Name"
|
label={t('robotCreate.extract.name')}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box sx={{ mb: 3 }}>
|
<Box sx={{ mb: 3 }}>
|
||||||
<TextField
|
<TextField
|
||||||
placeholder="Example: Extract first 15 company names, descriptions, and batch information"
|
placeholder={t('robotCreate.extract.aiPromptPlaceholder')}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
fullWidth
|
fullWidth
|
||||||
multiline
|
multiline
|
||||||
rows={3}
|
rows={3}
|
||||||
value={aiPrompt}
|
value={aiPrompt}
|
||||||
onChange={(e) => setAiPrompt(e.target.value)}
|
onChange={(e) => setAiPrompt(e.target.value)}
|
||||||
label="Extraction Prompt"
|
label={t('robotCreate.extract.aiPrompt')}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box sx={{ mb: 3 }}>
|
<Box sx={{ mb: 3 }}>
|
||||||
<TextField
|
<TextField
|
||||||
placeholder="Example: https://www.ycombinator.com/companies/"
|
placeholder={t('robotCreate.extract.websiteUrlPlaceholder')}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
fullWidth
|
fullWidth
|
||||||
value={url}
|
value={url}
|
||||||
onChange={(e) => setUrl(e.target.value)}
|
onChange={(e) => setUrl(e.target.value)}
|
||||||
label="Website URL (Optional)"
|
label={t('robotCreate.extract.websiteUrlOptional')}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box sx={{ display: 'flex', gap: 2, mb: 3 }}>
|
<Box sx={{ display: 'flex', gap: 2, mb: 3 }}>
|
||||||
<FormControl sx={{ flex: 1 }}>
|
<FormControl sx={{ flex: 1 }}>
|
||||||
<InputLabel>LLM Provider</InputLabel>
|
<InputLabel>{t('robotCreate.extract.llmProvider')}</InputLabel>
|
||||||
<Select
|
<Select
|
||||||
value={llmProvider}
|
value={llmProvider}
|
||||||
label="LLM Provider"
|
label={t('robotCreate.extract.llmProvider')}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const provider = e.target.value as 'anthropic' | 'openai' | 'ollama';
|
const provider = e.target.value as 'anthropic' | 'openai' | 'ollama';
|
||||||
setLlmProvider(provider);
|
setLlmProvider(provider);
|
||||||
@@ -449,22 +449,22 @@ const RobotCreate: React.FC = () => {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<MenuItem value="ollama">Ollama (Local)</MenuItem>
|
<MenuItem value="ollama">{t('robotCreate.extract.llmProviderOllama')}</MenuItem>
|
||||||
<MenuItem value="anthropic">Anthropic (Claude)</MenuItem>
|
<MenuItem value="anthropic">{t('robotCreate.extract.llmProviderAnthropic')}</MenuItem>
|
||||||
<MenuItem value="openai">OpenAI (GPT-4)</MenuItem>
|
<MenuItem value="openai">{t('robotCreate.extract.llmProviderOpenAI')}</MenuItem>
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormControl sx={{ flex: 1 }}>
|
<FormControl sx={{ flex: 1 }}>
|
||||||
<InputLabel>Model</InputLabel>
|
<InputLabel>{t('robotCreate.extract.model')}</InputLabel>
|
||||||
<Select
|
<Select
|
||||||
value={llmModel}
|
value={llmModel}
|
||||||
label="Model"
|
label={t('robotCreate.extract.model')}
|
||||||
onChange={(e) => setLlmModel(e.target.value)}
|
onChange={(e) => setLlmModel(e.target.value)}
|
||||||
>
|
>
|
||||||
{llmProvider === 'ollama' ? (
|
{llmProvider === 'ollama' ? (
|
||||||
[
|
[
|
||||||
<MenuItem key="default" value="default">Default (llama3.2-vision)</MenuItem>,
|
<MenuItem key="default" value="default">{t('robotCreate.extract.modelDefault')}</MenuItem>,
|
||||||
<MenuItem key="llama3.2-vision" value="llama3.2-vision">llama3.2-vision</MenuItem>,
|
<MenuItem key="llama3.2-vision" value="llama3.2-vision">llama3.2-vision</MenuItem>,
|
||||||
<MenuItem key="llama3.2" value="llama3.2">llama3.2</MenuItem>
|
<MenuItem key="llama3.2" value="llama3.2">llama3.2</MenuItem>
|
||||||
]
|
]
|
||||||
@@ -495,7 +495,7 @@ const RobotCreate: React.FC = () => {
|
|||||||
type="password"
|
type="password"
|
||||||
value={llmApiKey}
|
value={llmApiKey}
|
||||||
onChange={(e) => setLlmApiKey(e.target.value)}
|
onChange={(e) => setLlmApiKey(e.target.value)}
|
||||||
label="API Key (Optional if set in .env)"
|
label={t('robotCreate.extract.apiKey')}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
@@ -508,7 +508,7 @@ const RobotCreate: React.FC = () => {
|
|||||||
fullWidth
|
fullWidth
|
||||||
value={llmBaseUrl}
|
value={llmBaseUrl}
|
||||||
onChange={(e) => setLlmBaseUrl(e.target.value)}
|
onChange={(e) => setLlmBaseUrl(e.target.value)}
|
||||||
label="Ollama Base URL (Optional)"
|
label={t('robotCreate.extract.ollamaBaseUrl')}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
@@ -626,7 +626,7 @@ const RobotCreate: React.FC = () => {
|
|||||||
}}
|
}}
|
||||||
startIcon={isLoading ? <CircularProgress size={20} color="inherit" /> : null}
|
startIcon={isLoading ? <CircularProgress size={20} color="inherit" /> : null}
|
||||||
>
|
>
|
||||||
{isLoading ? 'Creating & Running...' : 'Create & Run Robot'}
|
{isLoading ? t('robotCreate.extract.creatingAndRunning') : t('robotCreate.extract.createAndRun')}
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
@@ -635,12 +635,12 @@ const RobotCreate: React.FC = () => {
|
|||||||
<>
|
<>
|
||||||
<Box sx={{ width: '100%', maxWidth: 700, mb: 3 }}>
|
<Box sx={{ width: '100%', maxWidth: 700, mb: 3 }}>
|
||||||
<TextField
|
<TextField
|
||||||
placeholder="Example: https://www.ycombinator.com/companies/"
|
placeholder={t('robotCreate.extract.websiteUrlPlaceholder')}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
fullWidth
|
fullWidth
|
||||||
value={url}
|
value={url}
|
||||||
onChange={(e) => setUrl(e.target.value)}
|
onChange={(e) => setUrl(e.target.value)}
|
||||||
label="Website URL"
|
label={t('robotCreate.extract.websiteUrl')}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{ width: '100%', maxWidth: 700 }}>
|
<Box sx={{ width: '100%', maxWidth: 700 }}>
|
||||||
@@ -658,7 +658,7 @@ const RobotCreate: React.FC = () => {
|
|||||||
}}
|
}}
|
||||||
startIcon={isLoading ? <CircularProgress size={20} color="inherit" /> : null}
|
startIcon={isLoading ? <CircularProgress size={20} color="inherit" /> : null}
|
||||||
>
|
>
|
||||||
{isLoading ? 'Starting...' : 'Start Recording'}
|
{isLoading ? t('robotCreate.extract.starting') : t('robotCreate.extract.startRecording')}
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ i18n
|
|||||||
.init({
|
.init({
|
||||||
fallbackLng: 'en',
|
fallbackLng: 'en',
|
||||||
debug: import.meta.env.DEV,
|
debug: import.meta.env.DEV,
|
||||||
supportedLngs: ['en', 'es', 'ja', 'zh','de', 'tr'],
|
supportedLngs: ['en', 'es', 'ja', 'zh','de', 'tr', 'ru'],
|
||||||
interpolation: {
|
interpolation: {
|
||||||
escapeValue: false, // React already escapes
|
escapeValue: false, // React already escapes
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -11,9 +11,14 @@ export default defineConfig(() => {
|
|||||||
'import.meta.env.VITE_BACKEND_URL': JSON.stringify(process.env.VITE_BACKEND_URL),
|
'import.meta.env.VITE_BACKEND_URL': JSON.stringify(process.env.VITE_BACKEND_URL),
|
||||||
'import.meta.env.VITE_PUBLIC_URL': JSON.stringify(publicUrl),
|
'import.meta.env.VITE_PUBLIC_URL': JSON.stringify(publicUrl),
|
||||||
},
|
},
|
||||||
|
css: {
|
||||||
|
postcss: {
|
||||||
|
plugins: [], // Empty plugins array - NO PostCSS processing
|
||||||
|
},
|
||||||
|
},
|
||||||
server: {
|
server: {
|
||||||
host: new URL(publicUrl).hostname,
|
host: '0.0.0.0', // Listen on all interfaces for Docker
|
||||||
port: parseInt(new URL(publicUrl).port),
|
port: 5174,
|
||||||
},
|
},
|
||||||
build: {
|
build: {
|
||||||
outDir: 'build',
|
outDir: 'build',
|
||||||
|
|||||||
Reference in New Issue
Block a user