72
Dockerfile
72
Dockerfile
@@ -1,70 +1,22 @@
|
|||||||
# --- Base Stage ---
|
FROM node:18-alpine
|
||||||
FROM node:18 AS base
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Copy shared package.json and install dependencies
|
# Copy package files
|
||||||
COPY package.json package-lock.json ./
|
COPY package*.json ./
|
||||||
COPY maxun-core/package.json ./maxun-core/package.json
|
|
||||||
RUN npm install
|
|
||||||
|
|
||||||
# --- Backend Build Stage ---
|
|
||||||
FROM base AS backend-build
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Copy TypeScript configs
|
|
||||||
COPY tsconfig*.json ./
|
|
||||||
COPY server/tsconfig.json ./server/
|
|
||||||
|
|
||||||
# Copy ALL source code (both frontend and backend)
|
|
||||||
COPY src ./src
|
|
||||||
# Copy backend code and maxun-core
|
|
||||||
COPY server/src ./server/src
|
|
||||||
COPY maxun-core ./maxun-core
|
COPY maxun-core ./maxun-core
|
||||||
|
|
||||||
# Install TypeScript globally and build
|
# Install dependencies
|
||||||
RUN npm install -g typescript
|
RUN npm install
|
||||||
RUN npm run build:server
|
|
||||||
|
|
||||||
# --- Frontend Build Stage ---
|
# Copy frontend source code and config
|
||||||
FROM base AS frontend-build
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Copy frontend code and configs
|
|
||||||
COPY src ./src
|
COPY src ./src
|
||||||
COPY index.html ./index.html
|
COPY index.html ./
|
||||||
COPY public ./public
|
|
||||||
COPY vite.config.js ./
|
COPY vite.config.js ./
|
||||||
COPY tsconfig.json ./
|
COPY tsconfig.json ./
|
||||||
|
|
||||||
# Build frontend
|
# Expose the frontend port
|
||||||
RUN npm run build
|
EXPOSE 5173
|
||||||
|
|
||||||
# --- Production Stage ---
|
# Start the frontend using the client script
|
||||||
FROM nginx:alpine AS production
|
CMD ["npm", "run", "client", "--", "--host"]
|
||||||
|
|
||||||
# Install Node.js in the production image
|
|
||||||
RUN apk add --update nodejs npm
|
|
||||||
|
|
||||||
# Copy nginx configuration
|
|
||||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
|
||||||
|
|
||||||
# Copy built frontend
|
|
||||||
COPY --from=frontend-build /app/build /usr/share/nginx/html
|
|
||||||
COPY --from=frontend-build /app/public/img /usr/share/nginx/html/img
|
|
||||||
|
|
||||||
# Copy built backend and its dependencies
|
|
||||||
WORKDIR /app
|
|
||||||
COPY --from=backend-build /app/package*.json ./
|
|
||||||
COPY --from=backend-build /app/server/dist ./server/dist
|
|
||||||
COPY --from=backend-build /app/maxun-core ./maxun-core
|
|
||||||
COPY --from=backend-build /app/node_modules ./node_modules
|
|
||||||
|
|
||||||
# Copy start script
|
|
||||||
COPY docker-entrypoint.sh /
|
|
||||||
RUN chmod +x /docker-entrypoint.sh
|
|
||||||
|
|
||||||
EXPOSE 80 8080
|
|
||||||
|
|
||||||
# Start both nginx and node server
|
|
||||||
ENTRYPOINT ["/docker-entrypoint.sh"]
|
|
||||||
|
|
||||||
10
README.md
10
README.md
@@ -25,13 +25,15 @@ Maxun lets you train a robot in 2 minutes and scrape the web on auto-pilot. Web
|
|||||||
|
|
||||||
<img src="https://static.scarf.sh/a.png?x-pxid=c12a77cc-855e-4602-8a0f-614b2d0da56a" />
|
<img src="https://static.scarf.sh/a.png?x-pxid=c12a77cc-855e-4602-8a0f-614b2d0da56a" />
|
||||||
|
|
||||||
# Installation
|
# Local Setup
|
||||||
### Docker
|
### Docker
|
||||||
⚠️ Work In Progress. Will be available by EOD.
|
```
|
||||||
|
docker-compose up -d --build
|
||||||
|
```
|
||||||
|
|
||||||
### Local Setup
|
### Without Docker
|
||||||
1. Ensure you have Node.js, PostgreSQL, MinIO and Redis installed on your system.
|
1. Ensure you have Node.js, PostgreSQL, MinIO and Redis installed on your system.
|
||||||
2. Run the commands below:
|
2. Run the commands below
|
||||||
```
|
```
|
||||||
git clone https://github.com/getmaxun/maxun
|
git clone https://github.com/getmaxun/maxun
|
||||||
|
|
||||||
|
|||||||
@@ -1,30 +1,31 @@
|
|||||||
version: '3.8'
|
version: '3.8'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
app:
|
postgres:
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
target: production
|
|
||||||
env_file: .env
|
|
||||||
ports:
|
|
||||||
- "5173:80"
|
|
||||||
- "8080:8080"
|
|
||||||
depends_on:
|
|
||||||
- db
|
|
||||||
- minio
|
|
||||||
- redis
|
|
||||||
|
|
||||||
db:
|
|
||||||
image: postgres:13
|
image: postgres:13
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_DB: ${DB_NAME}
|
|
||||||
POSTGRES_USER: ${DB_USER}
|
POSTGRES_USER: ${DB_USER}
|
||||||
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||||
|
POSTGRES_DB: ${DB_NAME}
|
||||||
ports:
|
ports:
|
||||||
- "5432:5432"
|
- "5432:5432"
|
||||||
volumes:
|
volumes:
|
||||||
- postgres_data:/var/lib/postgresql/data
|
- postgres_data:/var/lib/postgresql/data
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:6
|
||||||
|
environment:
|
||||||
|
REDIS_HOST: ${REDIS_HOST}
|
||||||
|
REDIS_PORT: ${REDIS_PORT}
|
||||||
|
ports:
|
||||||
|
- "6379:6379"
|
||||||
|
volumes:
|
||||||
|
- redis_data:/data
|
||||||
|
|
||||||
minio:
|
minio:
|
||||||
image: minio/minio
|
image: minio/minio
|
||||||
@@ -37,15 +38,41 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- minio_data:/data
|
- minio_data:/data
|
||||||
|
|
||||||
redis:
|
backend:
|
||||||
image: redis:6
|
build:
|
||||||
environment:
|
context: .
|
||||||
- REDIS_HOST=redis
|
dockerfile: server/Dockerfile
|
||||||
- REDIS_PORT=6379
|
|
||||||
ports:
|
ports:
|
||||||
- "6379:6379"
|
- "8080:8080"
|
||||||
|
env_file: .env
|
||||||
|
environment:
|
||||||
|
# to ensure Playwright works in Docker
|
||||||
|
PLAYWRIGHT_BROWSERS_PATH: /ms-playwright
|
||||||
|
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 0
|
||||||
|
# DEBUG: pw:api
|
||||||
|
# PWDEBUG: 1 # Enables debugging
|
||||||
|
CHROMIUM_FLAGS: '--disable-gpu --no-sandbox --headless=new'
|
||||||
volumes:
|
volumes:
|
||||||
- redis_data:/data
|
# - /tmp/.X11-unix:/tmp/.X11-unix
|
||||||
|
- /var/run/dbus:/var/run/dbus # Add this for D-Bus support
|
||||||
|
security_opt:
|
||||||
|
- seccomp=unconfined # This might help with browser sandbox issues
|
||||||
|
# Increase shared memory size for Chromium
|
||||||
|
shm_size: '2gb'
|
||||||
|
depends_on:
|
||||||
|
- postgres
|
||||||
|
- redis
|
||||||
|
- minio
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
ports:
|
||||||
|
- "5173:5173"
|
||||||
|
env_file: .env
|
||||||
|
depends_on:
|
||||||
|
- backend
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
postgres_data:
|
postgres_data:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
# Start backend server
|
# Start backend server
|
||||||
cd /app && npm run start:server &
|
cd /app && npm run start:server -- --host 0.0.0.0 &
|
||||||
|
|
||||||
# Start nginx
|
# Start nginx
|
||||||
nginx -g 'daemon off;'
|
nginx -g 'daemon off;'
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
name="description"
|
name="description"
|
||||||
content="Web site created using Vite"
|
content="Web site created using Vite"
|
||||||
/>
|
/>
|
||||||
<link rel="icon" type="image/png" href="public/img/maxunlogo.png">
|
<link rel="icon" type="image/png" href="img/maxunlogo.png">
|
||||||
<title>Maxun | Open Source No Code Web Data Extraction Platform</title>
|
<title>Maxun | Open Source No Code Web Data Extraction Platform</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
32
nginx.conf
32
nginx.conf
@@ -1,17 +1,45 @@
|
|||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
root /usr/share/nginx/html;
|
root /usr/share/nginx/html;
|
||||||
try_files $uri $uri/ /index.html;
|
try_files $uri $uri/ /index.html;
|
||||||
}
|
}
|
||||||
|
|
||||||
location /api {
|
location /api {
|
||||||
proxy_pass http://127.0.0.1:8080;
|
proxy_pass http://localhost:8080;
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
proxy_set_header Connection 'upgrade';
|
proxy_set_header Connection 'upgrade';
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
proxy_cache_bypass $http_upgrade;
|
proxy_cache_bypass $http_upgrade;
|
||||||
|
|
||||||
|
# Add timeout configurations
|
||||||
|
proxy_connect_timeout 60s;
|
||||||
|
proxy_send_timeout 60s;
|
||||||
|
proxy_read_timeout 60s;
|
||||||
|
|
||||||
|
# Add error handling
|
||||||
|
proxy_intercept_errors on;
|
||||||
|
error_page 502 503 504 /50x.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ ^/(record|workflow|storage|auth|integration|proxy|api-docs) {
|
||||||
|
proxy_pass http://localhost:8080;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection 'keep-alive'; # Ensure connections remain open
|
||||||
|
proxy_set_header Connection 'upgrade';
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_cache_bypass $http_upgrade;
|
||||||
|
|
||||||
|
# Timeout configurations
|
||||||
|
proxy_connect_timeout 60s;
|
||||||
|
proxy_send_timeout 60s;
|
||||||
|
proxy_read_timeout 60s;
|
||||||
|
|
||||||
|
# Error handling for these routes
|
||||||
|
proxy_intercept_errors on;
|
||||||
|
error_page 502 503 504 /50x.html;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -73,11 +73,11 @@
|
|||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "concurrently -k \"npm run server\" \"npm run client\"",
|
"start": "concurrently -k \"npm run server\" \"npm run client\"",
|
||||||
"server": "./node_modules/.bin/nodemon server/src/server.ts",
|
"server": "cross-env NODE_OPTIONS='--max-old-space-size=8000' nodemon server/src/server.ts",
|
||||||
"client": "vite",
|
"client": "vite",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"build:server": "tsc -p server/tsconfig.json",
|
"build:server": "tsc -p server/tsconfig.json",
|
||||||
"start:server": "node server/dist/server/src/server.js",
|
"start:server": "cross-env NODE_OPTIONS='--max-old-space-size=8000' server/dist/server/src/server.js",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"lint": "./node_modules/.bin/eslint ."
|
"lint": "./node_modules/.bin/eslint ."
|
||||||
},
|
},
|
||||||
@@ -101,6 +101,7 @@
|
|||||||
"@vitejs/plugin-react": "^4.3.3",
|
"@vitejs/plugin-react": "^4.3.3",
|
||||||
"ajv": "^8.8.2",
|
"ajv": "^8.8.2",
|
||||||
"concurrently": "^7.0.0",
|
"concurrently": "^7.0.0",
|
||||||
|
"cross-env": "^7.0.3",
|
||||||
"nodemon": "^2.0.15",
|
"nodemon": "^2.0.15",
|
||||||
"ts-node": "^10.4.0",
|
"ts-node": "^10.4.0",
|
||||||
"vite": "^5.4.10"
|
"vite": "^5.4.10"
|
||||||
|
|||||||
83
server/Dockerfile
Normal file
83
server/Dockerfile
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
FROM mcr.microsoft.com/playwright:v1.40.0-jammy
|
||||||
|
|
||||||
|
# Set working directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install node dependencies
|
||||||
|
COPY package*.json ./
|
||||||
|
COPY maxun-core ./maxun-core
|
||||||
|
COPY src ./src
|
||||||
|
COPY server ./server
|
||||||
|
COPY tsconfig.json ./
|
||||||
|
COPY server/tsconfig.json ./server/
|
||||||
|
COPY server/start.sh ./
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
# Install Playwright browsers and dependencies
|
||||||
|
RUN npx playwright install --with-deps chromium
|
||||||
|
|
||||||
|
# Install xvfb for display support
|
||||||
|
#RUN apt-get update && apt-get install -y xvfb
|
||||||
|
|
||||||
|
# RUN apt-get update && apt-get install -y \
|
||||||
|
# libgbm-dev \
|
||||||
|
# libxkbcommon-x11-0 \
|
||||||
|
# libxcomposite1 \
|
||||||
|
# libxdamage1 \
|
||||||
|
# libxrandr2 \
|
||||||
|
# libxshmfence1 \
|
||||||
|
# libxtst6 \
|
||||||
|
# libnss3 \
|
||||||
|
# libatk1.0-0 \
|
||||||
|
# libatk-bridge2.0-0 \
|
||||||
|
# libdrm2 \
|
||||||
|
# libxcb1 \
|
||||||
|
# libxkbcommon0 \
|
||||||
|
# fonts-noto-color-emoji \
|
||||||
|
# fonts-unifont \
|
||||||
|
# libpango-1.0-0 \
|
||||||
|
# libcairo2 \
|
||||||
|
# libasound2 \
|
||||||
|
# libglib2.0-0 \
|
||||||
|
# libdbus-1-3 \
|
||||||
|
# && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Create and set permissions for chrome directories
|
||||||
|
# Create the Chromium data directory with necessary permissions
|
||||||
|
RUN mkdir -p /tmp/chromium-data-dir && \
|
||||||
|
chmod -R 777 /tmp/chromium-data-dir
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
libgbm-dev \
|
||||||
|
libnss3 \
|
||||||
|
libatk1.0-0 \
|
||||||
|
libatk-bridge2.0-0 \
|
||||||
|
libdrm2 \
|
||||||
|
libxkbcommon0 \
|
||||||
|
libglib2.0-0 \
|
||||||
|
libdbus-1-3 \
|
||||||
|
libx11-xcb1 \
|
||||||
|
libxcb1 \
|
||||||
|
libxcomposite1 \
|
||||||
|
libxcursor1 \
|
||||||
|
libxdamage1 \
|
||||||
|
libxext6 \
|
||||||
|
libxi6 \
|
||||||
|
libxtst6 \
|
||||||
|
&& rm -rf /var/lib/apt/lists/* \
|
||||||
|
&& mkdir -p /tmp/.X11-unix && chmod 1777 /tmp/.X11-unix
|
||||||
|
|
||||||
|
# Add a dbus configuration to prevent connection errors
|
||||||
|
# RUN mkdir -p /var/run/dbus
|
||||||
|
|
||||||
|
# Make the script executable
|
||||||
|
RUN chmod +x ./start.sh
|
||||||
|
|
||||||
|
# Expose the backend port
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
# Start the backend using the start script
|
||||||
|
CMD ["./start.sh"]
|
||||||
@@ -464,13 +464,7 @@ async function createWorkflowAndStoreMetadata(id: string, userId: string) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const browserId = createRemoteBrowserForRun({
|
const browserId = createRemoteBrowserForRun(userId);
|
||||||
browser: chromium,
|
|
||||||
launchOptions: {
|
|
||||||
headless: true,
|
|
||||||
proxy: proxyOptions.server ? proxyOptions : undefined,
|
|
||||||
}
|
|
||||||
}, userId);
|
|
||||||
|
|
||||||
const runId = uuid();
|
const runId = uuid();
|
||||||
|
|
||||||
@@ -656,7 +650,7 @@ export async function handleRunRecording(id: string, userId: string) {
|
|||||||
throw new Error('browserId or runId or userId is undefined');
|
throw new Error('browserId or runId or userId is undefined');
|
||||||
}
|
}
|
||||||
|
|
||||||
const socket = io(`http://localhost:8080/${browserId}`, {
|
const socket = io(`${process.env.BACKEND_URL}/${browserId}`, {
|
||||||
transports: ['websocket'],
|
transports: ['websocket'],
|
||||||
rejectUnauthorized: false
|
rejectUnauthorized: false
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
Browser,
|
Browser,
|
||||||
CDPSession,
|
CDPSession,
|
||||||
BrowserContext,
|
BrowserContext,
|
||||||
|
chromium,
|
||||||
} from 'playwright';
|
} from 'playwright';
|
||||||
import { Socket } from "socket.io";
|
import { Socket } from "socket.io";
|
||||||
import { PlaywrightBlocker } from '@cliqz/adblocker-playwright';
|
import { PlaywrightBlocker } from '@cliqz/adblocker-playwright';
|
||||||
@@ -91,8 +92,39 @@ export class RemoteBrowser {
|
|||||||
* @param options remote browser options to be used when launching the browser
|
* @param options remote browser options to be used when launching the browser
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
public initialize = async (options: RemoteBrowserOptions, userId: string): Promise<void> => {
|
public initialize = async (userId: string): Promise<void> => {
|
||||||
this.browser = <Browser>(await options.browser.launch(options.launchOptions));
|
// const launchOptions = {
|
||||||
|
// headless: true,
|
||||||
|
// proxy: options.launchOptions?.proxy,
|
||||||
|
// chromiumSandbox: false,
|
||||||
|
// args: [
|
||||||
|
// '--no-sandbox',
|
||||||
|
// '--disable-setuid-sandbox',
|
||||||
|
// '--headless=new',
|
||||||
|
// '--disable-gpu',
|
||||||
|
// '--disable-dev-shm-usage',
|
||||||
|
// '--disable-software-rasterizer',
|
||||||
|
// '--in-process-gpu',
|
||||||
|
// '--disable-infobars',
|
||||||
|
// '--single-process',
|
||||||
|
// '--no-zygote',
|
||||||
|
// '--disable-notifications',
|
||||||
|
// '--disable-extensions',
|
||||||
|
// '--disable-background-timer-throttling',
|
||||||
|
// ...(options.launchOptions?.args || [])
|
||||||
|
// ],
|
||||||
|
// env: {
|
||||||
|
// ...process.env,
|
||||||
|
// CHROMIUM_FLAGS: '--disable-gpu --no-sandbox --headless=new'
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
// console.log('Launch options before:', options.launchOptions);
|
||||||
|
// this.browser = <Browser>(await options.browser.launch(launchOptions));
|
||||||
|
|
||||||
|
// console.log('Launch options after:', options.launchOptions)
|
||||||
|
this.browser = <Browser>(await chromium.launch({
|
||||||
|
headless: true,
|
||||||
|
}));
|
||||||
const proxyConfig = await getDecryptedProxyConfig(userId);
|
const proxyConfig = await getDecryptedProxyConfig(userId);
|
||||||
let proxyOptions: { server: string, username?: string, password?: string } = { server: '' };
|
let proxyOptions: { server: string, username?: string, password?: string } = { server: '' };
|
||||||
if (proxyConfig.proxy_url) {
|
if (proxyConfig.proxy_url) {
|
||||||
@@ -107,6 +139,16 @@ export class RemoteBrowser {
|
|||||||
const contextOptions: any = {
|
const contextOptions: any = {
|
||||||
viewport: { height: 400, width: 900 },
|
viewport: { height: 400, width: 900 },
|
||||||
// recordVideo: { dir: 'videos/' }
|
// recordVideo: { dir: 'videos/' }
|
||||||
|
// Force reduced motion to prevent animation issues
|
||||||
|
reducedMotion: 'reduce',
|
||||||
|
// Force JavaScript to be enabled
|
||||||
|
javaScriptEnabled: true,
|
||||||
|
// Set a reasonable timeout
|
||||||
|
timeout: 50000,
|
||||||
|
// Disable hardware acceleration
|
||||||
|
forcedColors: 'none',
|
||||||
|
isMobile: false,
|
||||||
|
hasTouch: false
|
||||||
};
|
};
|
||||||
|
|
||||||
if (proxyOptions.server) {
|
if (proxyOptions.server) {
|
||||||
@@ -116,9 +158,17 @@ export class RemoteBrowser {
|
|||||||
password: proxyOptions.password ? proxyOptions.password : undefined,
|
password: proxyOptions.password ? proxyOptions.password : undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
const browserUserAgent = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.5481.38 Safari/537.36";
|
||||||
|
|
||||||
|
|
||||||
|
contextOptions.userAgent = browserUserAgent;
|
||||||
this.context = await this.browser.newContext(contextOptions);
|
this.context = await this.browser.newContext(contextOptions);
|
||||||
|
console.log(`Context from initialize: ${JSON.stringify(this.context)}`)
|
||||||
this.currentPage = await this.context.newPage();
|
this.currentPage = await this.context.newPage();
|
||||||
|
console.log(`CPage from initialize: ${JSON.stringify(this.currentPage)}`)
|
||||||
|
// await this.currentPage.setExtraHTTPHeaders({
|
||||||
|
// 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'
|
||||||
|
// });
|
||||||
const blocker = await PlaywrightBlocker.fromPrebuiltAdsAndTracking(fetch);
|
const blocker = await PlaywrightBlocker.fromPrebuiltAdsAndTracking(fetch);
|
||||||
await blocker.enableBlockingInPage(this.currentPage);
|
await blocker.enableBlockingInPage(this.currentPage);
|
||||||
this.client = await this.currentPage.context().newCDPSession(this.currentPage);
|
this.client = await this.currentPage.context().newCDPSession(this.currentPage);
|
||||||
@@ -331,6 +381,9 @@ export class RemoteBrowser {
|
|||||||
await this.stopScreencast();
|
await this.stopScreencast();
|
||||||
const newPage = options ? await this.browser?.newPage(options)
|
const newPage = options ? await this.browser?.newPage(options)
|
||||||
: await this.browser?.newPage();
|
: await this.browser?.newPage();
|
||||||
|
await newPage?.setExtraHTTPHeaders({
|
||||||
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'
|
||||||
|
});
|
||||||
|
|
||||||
await this.currentPage?.close();
|
await this.currentPage?.close();
|
||||||
this.currentPage = newPage;
|
this.currentPage = newPage;
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import logger from "../logger";
|
|||||||
* @returns string
|
* @returns string
|
||||||
* @category BrowserManagement-Controller
|
* @category BrowserManagement-Controller
|
||||||
*/
|
*/
|
||||||
export const initializeRemoteBrowserForRecording = (options: RemoteBrowserOptions, userId: string): string => {
|
export const initializeRemoteBrowserForRecording = (userId: string): string => {
|
||||||
const id = getActiveBrowserId() || uuid();
|
const id = getActiveBrowserId() || uuid();
|
||||||
createSocketConnection(
|
createSocketConnection(
|
||||||
io.of(id),
|
io.of(id),
|
||||||
@@ -34,7 +34,7 @@ export const initializeRemoteBrowserForRecording = (options: RemoteBrowserOption
|
|||||||
} else {
|
} else {
|
||||||
const browserSession = new RemoteBrowser(socket);
|
const browserSession = new RemoteBrowser(socket);
|
||||||
browserSession.interpreter.subscribeToPausing();
|
browserSession.interpreter.subscribeToPausing();
|
||||||
await browserSession.initialize(options, userId);
|
await browserSession.initialize(userId);
|
||||||
await browserSession.registerEditorEvents();
|
await browserSession.registerEditorEvents();
|
||||||
await browserSession.subscribeToScreencast();
|
await browserSession.subscribeToScreencast();
|
||||||
browserPool.addRemoteBrowser(id, browserSession, true);
|
browserPool.addRemoteBrowser(id, browserSession, true);
|
||||||
@@ -52,13 +52,13 @@ export const initializeRemoteBrowserForRecording = (options: RemoteBrowserOption
|
|||||||
* @returns string
|
* @returns string
|
||||||
* @category BrowserManagement-Controller
|
* @category BrowserManagement-Controller
|
||||||
*/
|
*/
|
||||||
export const createRemoteBrowserForRun = (options: RemoteBrowserOptions, userId: string): string => {
|
export const createRemoteBrowserForRun = (userId: string): string => {
|
||||||
const id = uuid();
|
const id = uuid();
|
||||||
createSocketConnectionForRun(
|
createSocketConnectionForRun(
|
||||||
io.of(id),
|
io.of(id),
|
||||||
async (socket: Socket) => {
|
async (socket: Socket) => {
|
||||||
const browserSession = new RemoteBrowser(socket);
|
const browserSession = new RemoteBrowser(socket);
|
||||||
await browserSession.initialize(options, userId);
|
await browserSession.initialize(userId);
|
||||||
browserPool.addRemoteBrowser(id, browserSession, true);
|
browserPool.addRemoteBrowser(id, browserSession, true);
|
||||||
socket.emit('ready-for-run');
|
socket.emit('ready-for-run');
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ interface AuthenticatedRequest extends Request {
|
|||||||
}
|
}
|
||||||
|
|
||||||
router.post('/register', async (req, res) => {
|
router.post('/register', async (req, res) => {
|
||||||
|
console.log('Received request at /auth/register');
|
||||||
|
console.log('Received body:', req.body);
|
||||||
try {
|
try {
|
||||||
const { email, password } = req.body
|
const { email, password } = req.body
|
||||||
|
|
||||||
@@ -25,7 +27,21 @@ router.post('/register', async (req, res) => {
|
|||||||
|
|
||||||
const hashedPassword = await hashPassword(password)
|
const hashedPassword = await hashPassword(password)
|
||||||
|
|
||||||
const user = await User.create({ email, password: hashedPassword });
|
let user: any;
|
||||||
|
|
||||||
|
try {
|
||||||
|
user = await User.create({ email, password: hashedPassword });
|
||||||
|
} catch (
|
||||||
|
error: any
|
||||||
|
) {
|
||||||
|
console.log(`Could not create user - ${error}`)
|
||||||
|
return res.status(500).send(`Could not create user - ${error.message}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!process.env.JWT_SECRET) {
|
||||||
|
console.log('JWT_SECRET is not defined in the environment');
|
||||||
|
return res.status(500).send('Internal Server Error');
|
||||||
|
}
|
||||||
|
|
||||||
const token = jwt.sign({ id: user.id }, process.env.JWT_SECRET as string, { expiresIn: '12h' });
|
const token = jwt.sign({ id: user.id }, process.env.JWT_SECRET as string, { expiresIn: '12h' });
|
||||||
user.password = undefined as unknown as string
|
user.password = undefined as unknown as string
|
||||||
@@ -40,8 +56,10 @@ router.post('/register', async (req, res) => {
|
|||||||
registeredAt: new Date().toISOString()
|
registeredAt: new Date().toISOString()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
console.log(`User registered - ${user.email}`)
|
||||||
res.json(user)
|
res.json(user)
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
console.log(`Could not register user - ${error}`)
|
||||||
res.status(500).send(`Could not register user - ${error.message}`)
|
res.status(500).send(`Could not register user - ${error.message}`)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -11,14 +11,14 @@ import {
|
|||||||
stopRunningInterpretation,
|
stopRunningInterpretation,
|
||||||
getRemoteBrowserCurrentUrl, getRemoteBrowserCurrentTabs,
|
getRemoteBrowserCurrentUrl, getRemoteBrowserCurrentTabs,
|
||||||
} from '../browser-management/controller'
|
} from '../browser-management/controller'
|
||||||
import { chromium } from 'playwright-extra';
|
import { chromium } from 'playwright';
|
||||||
import stealthPlugin from 'puppeteer-extra-plugin-stealth';
|
import stealthPlugin from 'puppeteer-extra-plugin-stealth';
|
||||||
import logger from "../logger";
|
import logger from "../logger";
|
||||||
import { getDecryptedProxyConfig } from './proxy';
|
import { getDecryptedProxyConfig } from './proxy';
|
||||||
import { requireSignIn } from '../middlewares/auth';
|
import { requireSignIn } from '../middlewares/auth';
|
||||||
|
|
||||||
export const router = Router();
|
export const router = Router();
|
||||||
chromium.use(stealthPlugin());
|
// chromium.use(stealthPlugin());
|
||||||
|
|
||||||
|
|
||||||
export interface AuthenticatedRequest extends Request {
|
export interface AuthenticatedRequest extends Request {
|
||||||
@@ -56,13 +56,8 @@ router.get('/start', requireSignIn, async (req: AuthenticatedRequest, res: Respo
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const id = initializeRemoteBrowserForRecording({
|
const id = initializeRemoteBrowserForRecording(req.user.id);
|
||||||
browser: chromium,
|
console.log('id start:', id);
|
||||||
launchOptions: {
|
|
||||||
headless: true,
|
|
||||||
proxy: proxyOptions.server ? proxyOptions : undefined,
|
|
||||||
}
|
|
||||||
}, req.user.id);
|
|
||||||
return res.send(id);
|
return res.send(id);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -74,10 +69,8 @@ router.post('/start', requireSignIn, (req: AuthenticatedRequest, res:Response) =
|
|||||||
if (!req.user) {
|
if (!req.user) {
|
||||||
return res.status(401).send('User not authenticated');
|
return res.status(401).send('User not authenticated');
|
||||||
}
|
}
|
||||||
const id = initializeRemoteBrowserForRecording({
|
const id = initializeRemoteBrowserForRecording(req.user.id);
|
||||||
browser: chromium,
|
console.log('id start POST:', id);
|
||||||
launchOptions: req.body,
|
|
||||||
}, req.user.id);
|
|
||||||
return res.send(id);
|
return res.send(id);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -158,13 +158,7 @@ router.put('/runs/:id', requireSignIn, async (req: AuthenticatedRequest, res) =>
|
|||||||
|
|
||||||
console.log(`Proxy config for run: ${JSON.stringify(proxyOptions)}`)
|
console.log(`Proxy config for run: ${JSON.stringify(proxyOptions)}`)
|
||||||
|
|
||||||
const id = createRemoteBrowserForRun({
|
const id = createRemoteBrowserForRun(req.user.id);
|
||||||
browser: chromium,
|
|
||||||
launchOptions: {
|
|
||||||
headless: true,
|
|
||||||
proxy: proxyOptions.server ? proxyOptions : undefined,
|
|
||||||
}
|
|
||||||
}, req.user.id);
|
|
||||||
|
|
||||||
const runId = uuid();
|
const runId = uuid();
|
||||||
|
|
||||||
|
|||||||
@@ -39,8 +39,8 @@ export const io = new Server(server);
|
|||||||
*/
|
*/
|
||||||
export const browserPool = new BrowserPool();
|
export const browserPool = new BrowserPool();
|
||||||
|
|
||||||
app.use(bodyParser.json({ limit: '10mb' }))
|
// app.use(bodyParser.json({ limit: '10mb' }))
|
||||||
app.use(bodyParser.urlencoded({ extended: true, limit: '10mb', parameterLimit: 9000 }));
|
// app.use(bodyParser.urlencoded({ extended: true, limit: '10mb', parameterLimit: 9000 }));
|
||||||
// parse cookies - "cookie" is true in csrfProtection
|
// parse cookies - "cookie" is true in csrfProtection
|
||||||
app.use(cookieParser())
|
app.use(cookieParser())
|
||||||
|
|
||||||
@@ -62,42 +62,60 @@ readdirSync(path.join(__dirname, 'api')).forEach((r) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Check if we're running in production or development
|
|
||||||
const isProduction = process.env.NODE_ENV === 'production';
|
const isProduction = process.env.NODE_ENV === 'production';
|
||||||
const workerPath = path.resolve(__dirname, isProduction ? './worker.js' : '/worker.ts');
|
const workerPath = path.resolve(__dirname, isProduction ? './worker.js' : './worker.ts');
|
||||||
|
|
||||||
// Fork the worker process
|
let workerProcess: any;
|
||||||
const workerProcess = fork(workerPath, [], {
|
if (!isProduction) {
|
||||||
execArgv: isProduction ? ['--inspect=8081'] : ['--inspect=5859'],
|
workerProcess = fork(workerPath, [], {
|
||||||
});
|
execArgv: ['--inspect=5859'],
|
||||||
|
});
|
||||||
workerProcess.on('message', (message) => {
|
workerProcess.on('message', (message: any) => {
|
||||||
console.log(`Message from worker: ${message}`);
|
console.log(`Message from worker: ${message}`);
|
||||||
});
|
});
|
||||||
workerProcess.on('error', (error) => {
|
workerProcess.on('error', (error: any) => {
|
||||||
console.error(`Error in worker: ${error}`);
|
console.error(`Error in worker: ${error}`);
|
||||||
});
|
});
|
||||||
workerProcess.on('exit', (code) => {
|
workerProcess.on('exit', (code: any) => {
|
||||||
console.log(`Worker exited with code: ${code}`);
|
console.log(`Worker exited with code: ${code}`);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
app.get('/', function (req, res) {
|
app.get('/', function (req, res) {
|
||||||
capture(
|
capture(
|
||||||
'maxun-oss-server-run', {
|
'maxun-oss-server-run', {
|
||||||
event: 'server_started',
|
event: 'server_started',
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
return res.send('Maxun server started 🚀');
|
return res.send('Maxun server started 🚀');
|
||||||
});
|
});
|
||||||
|
|
||||||
server.listen(SERVER_PORT, async () => {
|
// Add CORS headers
|
||||||
await connectDB();
|
app.use((req, res, next) => {
|
||||||
await syncDB();
|
res.header('Access-Control-Allow-Origin', '*');
|
||||||
logger.log('info', `Server listening on port ${SERVER_PORT}`);
|
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
|
||||||
|
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
|
||||||
|
if (req.method === 'OPTIONS') {
|
||||||
|
return res.sendStatus(200);
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
|
server.listen(SERVER_PORT, '0.0.0.0', async () => {
|
||||||
|
try {
|
||||||
|
await connectDB();
|
||||||
|
await syncDB();
|
||||||
|
logger.log('info', `Server listening on port ${SERVER_PORT}`);
|
||||||
|
} catch (error: any) {
|
||||||
|
logger.log('error', `Failed to connect to the database: ${error.message}`);
|
||||||
|
process.exit(1); // Exit the process if DB connection fails
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
process.on('SIGINT', () => {
|
process.on('SIGINT', () => {
|
||||||
console.log('Main app shutting down...');
|
console.log('Main app shutting down...');
|
||||||
workerProcess.kill();
|
if (!isProduction) {
|
||||||
|
workerProcess.kill();
|
||||||
|
}
|
||||||
process.exit();
|
process.exit();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,10 +3,15 @@ import dotenv from 'dotenv';
|
|||||||
import setupAssociations from '../models/associations';
|
import setupAssociations from '../models/associations';
|
||||||
|
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
const sequelize = new Sequelize(
|
|
||||||
`postgresql://${process.env.DB_USER}:${process.env.DB_PASSWORD}@${process.env.DB_HOST}:${process.env.DB_PORT}/${process.env.DB_NAME}`,
|
const databaseUrl = `postgresql://${process.env.DB_USER}:${process.env.DB_PASSWORD}@${process.env.DB_HOST}:${process.env.DB_PORT}/${process.env.DB_NAME}`;
|
||||||
|
|
||||||
|
// Extract the hostname using the URL constructor
|
||||||
|
const host = new URL(databaseUrl).hostname;
|
||||||
|
|
||||||
|
const sequelize = new Sequelize(databaseUrl,
|
||||||
{
|
{
|
||||||
host: process.env.DB_HOST,
|
host,
|
||||||
dialect: 'postgres',
|
dialect: 'postgres',
|
||||||
logging: false,
|
logging: false,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ const options = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
apis: [path.join(__dirname, '../api/*.ts')],
|
apis: process.env.NODE_ENV === 'production' ? [path.join(__dirname, '../api/*.js')] : [path.join(__dirname, '../api/*.ts')]
|
||||||
};
|
};
|
||||||
|
|
||||||
const swaggerSpec = swaggerJSDoc(options);
|
const swaggerSpec = swaggerJSDoc(options);
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export const encrypt = (text: string): string => {
|
|||||||
|
|
||||||
export const decrypt = (encryptedText: string): string => {
|
export const decrypt = (encryptedText: string): string => {
|
||||||
const [iv, encrypted] = encryptedText.split(':');
|
const [iv, encrypted] = encryptedText.split(':');
|
||||||
const algorithm = getEnvVariable('ALGORITHM');
|
const algorithm = "aes-256-cbc";
|
||||||
const key = Buffer.from(getEnvVariable('ENCRYPTION_KEY'), 'hex');
|
const key = Buffer.from(getEnvVariable('ENCRYPTION_KEY'), 'hex');
|
||||||
const decipher = crypto.createDecipheriv(algorithm, key, Buffer.from(iv, 'hex'));
|
const decipher = crypto.createDecipheriv(algorithm, key, Buffer.from(iv, 'hex'));
|
||||||
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
|
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
|
||||||
|
|||||||
@@ -40,13 +40,7 @@ async function createWorkflowAndStoreMetadata(id: string, userId: string) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const browserId = createRemoteBrowserForRun({
|
const browserId = createRemoteBrowserForRun( userId);
|
||||||
browser: chromium,
|
|
||||||
launchOptions: {
|
|
||||||
headless: true,
|
|
||||||
proxy: proxyOptions.server ? proxyOptions : undefined,
|
|
||||||
}
|
|
||||||
}, userId);
|
|
||||||
const runId = uuid();
|
const runId = uuid();
|
||||||
|
|
||||||
const run = await Run.create({
|
const run = await Run.create({
|
||||||
@@ -229,7 +223,7 @@ export async function handleRunRecording(id: string, userId: string) {
|
|||||||
throw new Error('browserId or runId or userId is undefined');
|
throw new Error('browserId or runId or userId is undefined');
|
||||||
}
|
}
|
||||||
|
|
||||||
const socket = io(`http://localhost:8080/${browserId}`, {
|
const socket = io(`${process.env.BACKEND_URL}/${browserId}`, {
|
||||||
transports: ['websocket'],
|
transports: ['websocket'],
|
||||||
rejectUnauthorized: false
|
rejectUnauthorized: false
|
||||||
});
|
});
|
||||||
|
|||||||
10
server/start.sh
Normal file
10
server/start.sh
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Start Xvfb in the background with the desired dimensions
|
||||||
|
#Xvfb :0 -screen 0 900x400x24 &
|
||||||
|
|
||||||
|
# Wait for Xvfb to start
|
||||||
|
#sleep 2
|
||||||
|
|
||||||
|
# Execute the Node.js application
|
||||||
|
exec npm run server
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es2018",
|
"target": "es2020",
|
||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
"outDir": "./dist",
|
"outDir": "./dist",
|
||||||
"rootDir": "../",
|
"rootDir": "../",
|
||||||
@@ -21,12 +21,12 @@
|
|||||||
"include": [
|
"include": [
|
||||||
"src/**/*",
|
"src/**/*",
|
||||||
"../src/shared/**/*",
|
"../src/shared/**/*",
|
||||||
"../src/helpers/**/*"
|
"../src/helpers/**/*",
|
||||||
],
|
],
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"node_modules",
|
"node_modules",
|
||||||
"../src/components/**/*", // Exclude frontend components
|
"../src/components/**/*", // Exclude frontend components
|
||||||
"../src/pages/**/*", // Exclude frontend pages
|
"../src/pages/**/*", // Exclude frontend pages
|
||||||
"../src/app/**/*" // Exclude other frontend-specific code
|
"../src/app/**/*", // Exclude other frontend-specific code
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { default as axios } from "axios";
|
import { default as axios } from "axios";
|
||||||
|
import { apiUrl } from "../apiConfig"
|
||||||
|
|
||||||
export const getUserById = async (userId: string) => {
|
export const getUserById = async (userId: string) => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(`http://localhost:8080/auth/user/${userId}`);
|
const response = await axios.get(`${apiUrl}/auth/user/${userId}`);
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
return response.data;
|
return response.data;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { default as axios } from "axios";
|
import { default as axios } from "axios";
|
||||||
|
import { apiUrl } from "../apiConfig";
|
||||||
|
|
||||||
export const handleUploadCredentials = async (fileName: string, credentials: any, spreadsheetId: string, range: string): Promise<boolean> => {
|
export const handleUploadCredentials = async (fileName: string, credentials: any, spreadsheetId: string, range: string): Promise<boolean> => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.post('http://localhost:8080/integration/upload-credentials', { fileName, credentials: JSON.parse(credentials), spreadsheetId, range });
|
const response = await axios.post(`${apiUrl}/integration/upload-credentials`, { fileName, credentials: JSON.parse(credentials), spreadsheetId, range });
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
return response.data;
|
return response.data;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { default as axios } from "axios";
|
import { default as axios } from "axios";
|
||||||
|
import { apiUrl } from "../apiConfig";
|
||||||
|
|
||||||
export const sendProxyConfig = async (proxyConfig: { server_url: string, username?: string, password?: string }): Promise<boolean> => {
|
export const sendProxyConfig = async (proxyConfig: { server_url: string, username?: string, password?: string }): Promise<boolean> => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.post(`http://localhost:8080/proxy/config`, proxyConfig);
|
const response = await axios.post(`${apiUrl}/proxy/config`, proxyConfig);
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
return response.data;
|
return response.data;
|
||||||
} else {
|
} else {
|
||||||
@@ -16,7 +17,7 @@ export const sendProxyConfig = async (proxyConfig: { server_url: string, usernam
|
|||||||
|
|
||||||
export const getProxyConfig = async (): Promise<{ proxy_url: string, auth: boolean }> => {
|
export const getProxyConfig = async (): Promise<{ proxy_url: string, auth: boolean }> => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(`http://localhost:8080/proxy/config`);
|
const response = await axios.get(`${apiUrl}/proxy/config`);
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
return response.data;
|
return response.data;
|
||||||
} else {
|
} else {
|
||||||
@@ -30,7 +31,7 @@ export const getProxyConfig = async (): Promise<{ proxy_url: string, auth: boole
|
|||||||
|
|
||||||
export const testProxyConfig = async (): Promise<{ success: boolean }> => {
|
export const testProxyConfig = async (): Promise<{ success: boolean }> => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(`http://localhost:8080/proxy/test`);
|
const response = await axios.get(`${apiUrl}/proxy/test`);
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
return response.data;
|
return response.data;
|
||||||
} else {
|
} else {
|
||||||
@@ -44,7 +45,7 @@ export const testProxyConfig = async (): Promise<{ success: boolean }> => {
|
|||||||
|
|
||||||
export const deleteProxyConfig = async (): Promise<boolean> => {
|
export const deleteProxyConfig = async (): Promise<boolean> => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.delete(`http://localhost:8080/proxy/config`);
|
const response = await axios.delete(`${apiUrl}/proxy/config`);
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
return response.data;
|
return response.data;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { default as axios, AxiosResponse } from "axios";
|
import { default as axios, AxiosResponse } from "axios";
|
||||||
|
import { apiUrl } from "../apiConfig";
|
||||||
|
|
||||||
export const startRecording = async() : Promise<string> => {
|
export const startRecording = async() : Promise<string> => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get('http://localhost:8080/record/start')
|
const response = await axios.get(`${apiUrl}/record/start`)
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
return response.data;
|
return response.data;
|
||||||
} else {
|
} else {
|
||||||
@@ -14,7 +15,7 @@ export const startRecording = async() : Promise<string> => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const stopRecording = async (id: string): Promise<void> => {
|
export const stopRecording = async (id: string): Promise<void> => {
|
||||||
await axios.get(`http://localhost:8080/record/stop/${id}`)
|
await axios.get(`${apiUrl}/record/stop/${id}`)
|
||||||
.then((response : AxiosResponse<boolean>) => {
|
.then((response : AxiosResponse<boolean>) => {
|
||||||
})
|
})
|
||||||
.catch((error: any) => {
|
.catch((error: any) => {
|
||||||
@@ -23,7 +24,7 @@ export const stopRecording = async (id: string): Promise<void> => {
|
|||||||
|
|
||||||
export const getActiveBrowserId = async(): Promise<string> => {
|
export const getActiveBrowserId = async(): Promise<string> => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get('http://localhost:8080/record/active');
|
const response = await axios.get(`${apiUrl}/record/active`);
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
return response.data;
|
return response.data;
|
||||||
} else {
|
} else {
|
||||||
@@ -36,7 +37,7 @@ export const getActiveBrowserId = async(): Promise<string> => {
|
|||||||
|
|
||||||
export const interpretCurrentRecording = async(): Promise<boolean> => {
|
export const interpretCurrentRecording = async(): Promise<boolean> => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get('http://localhost:8080/record/interpret');
|
const response = await axios.get(`${apiUrl}/record/interpret`);
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
@@ -50,7 +51,7 @@ export const interpretCurrentRecording = async(): Promise<boolean> => {
|
|||||||
|
|
||||||
export const stopCurrentInterpretation = async(): Promise<void> => {
|
export const stopCurrentInterpretation = async(): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get('http://localhost:8080/record/interpret/stop');
|
const response = await axios.get(`${apiUrl}/record/interpret/stop`);
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
@@ -63,7 +64,7 @@ export const stopCurrentInterpretation = async(): Promise<void> => {
|
|||||||
|
|
||||||
export const getCurrentUrl = async (): Promise<string | null> => {
|
export const getCurrentUrl = async (): Promise<string | null> => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get('http://localhost:8080/record/active/url');
|
const response = await axios.get(`${apiUrl}/record/active/url`);
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
return response.data;
|
return response.data;
|
||||||
} else {
|
} else {
|
||||||
@@ -77,7 +78,7 @@ export const getCurrentUrl = async (): Promise<string | null> => {
|
|||||||
|
|
||||||
export const getCurrentTabs = async (): Promise<string[] | null> => {
|
export const getCurrentTabs = async (): Promise<string[] | null> => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get('http://localhost:8080/record/active/tabs');
|
const response = await axios.get(`${apiUrl}/record/active/tabs`);
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
return response.data;
|
return response.data;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -3,10 +3,11 @@ import { WorkflowFile } from "maxun-core";
|
|||||||
import { RunSettings } from "../components/molecules/RunSettings";
|
import { RunSettings } from "../components/molecules/RunSettings";
|
||||||
import { ScheduleSettings } from "../components/molecules/ScheduleSettings";
|
import { ScheduleSettings } from "../components/molecules/ScheduleSettings";
|
||||||
import { CreateRunResponse, ScheduleRunResponse } from "../pages/MainPage";
|
import { CreateRunResponse, ScheduleRunResponse } from "../pages/MainPage";
|
||||||
|
import { apiUrl } from "../apiConfig";
|
||||||
|
|
||||||
export const getStoredRecordings = async (): Promise<string[] | null> => {
|
export const getStoredRecordings = async (): Promise<string[] | null> => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get('http://localhost:8080/storage/recordings');
|
const response = await axios.get(`${apiUrl}/storage/recordings`);
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
return response.data;
|
return response.data;
|
||||||
} else {
|
} else {
|
||||||
@@ -20,7 +21,7 @@ export const getStoredRecordings = async (): Promise<string[] | null> => {
|
|||||||
|
|
||||||
export const getStoredRuns = async (): Promise<string[] | null> => {
|
export const getStoredRuns = async (): Promise<string[] | null> => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get('http://localhost:8080/storage/runs');
|
const response = await axios.get(`${apiUrl}/storage/runs`);
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
return response.data;
|
return response.data;
|
||||||
} else {
|
} else {
|
||||||
@@ -34,7 +35,7 @@ export const getStoredRuns = async (): Promise<string[] | null> => {
|
|||||||
|
|
||||||
export const getStoredRecording = async (id: string) => {
|
export const getStoredRecording = async (id: string) => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(`http://localhost:8080/storage/recordings/${id}`);
|
const response = await axios.get(`${apiUrl}/storage/recordings/${id}`);
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
return response.data;
|
return response.data;
|
||||||
} else {
|
} else {
|
||||||
@@ -48,7 +49,7 @@ export const getStoredRecording = async (id: string) => {
|
|||||||
|
|
||||||
export const deleteRecordingFromStorage = async (id: string): Promise<boolean> => {
|
export const deleteRecordingFromStorage = async (id: string): Promise<boolean> => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.delete(`http://localhost:8080/storage/recordings/${id}`);
|
const response = await axios.delete(`${apiUrl}/storage/recordings/${id}`);
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
return response.data;
|
return response.data;
|
||||||
} else {
|
} else {
|
||||||
@@ -62,7 +63,7 @@ export const deleteRecordingFromStorage = async (id: string): Promise<boolean> =
|
|||||||
|
|
||||||
export const deleteRunFromStorage = async (id: string): Promise<boolean> => {
|
export const deleteRunFromStorage = async (id: string): Promise<boolean> => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.delete(`http://localhost:8080/storage/runs/${id}`);
|
const response = await axios.delete(`${apiUrl}/storage/runs/${id}`);
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
return response.data;
|
return response.data;
|
||||||
} else {
|
} else {
|
||||||
@@ -76,7 +77,7 @@ export const deleteRunFromStorage = async (id: string): Promise<boolean> => {
|
|||||||
|
|
||||||
export const editRecordingFromStorage = async (browserId: string, id: string): Promise<WorkflowFile | null> => {
|
export const editRecordingFromStorage = async (browserId: string, id: string): Promise<WorkflowFile | null> => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.put(`http://localhost:8080/workflow/${browserId}/${id}`);
|
const response = await axios.put(`${apiUrl}/workflow/${browserId}/${id}`);
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
return response.data;
|
return response.data;
|
||||||
} else {
|
} else {
|
||||||
@@ -91,7 +92,7 @@ export const editRecordingFromStorage = async (browserId: string, id: string): P
|
|||||||
export const createRunForStoredRecording = async (id: string, settings: RunSettings): Promise<CreateRunResponse> => {
|
export const createRunForStoredRecording = async (id: string, settings: RunSettings): Promise<CreateRunResponse> => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.put(
|
const response = await axios.put(
|
||||||
`http://localhost:8080/storage/runs/${id}`,
|
`${apiUrl}/storage/runs/${id}`,
|
||||||
{ ...settings });
|
{ ...settings });
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
return response.data;
|
return response.data;
|
||||||
@@ -106,7 +107,7 @@ export const createRunForStoredRecording = async (id: string, settings: RunSetti
|
|||||||
|
|
||||||
export const interpretStoredRecording = async (id: string): Promise<boolean> => {
|
export const interpretStoredRecording = async (id: string): Promise<boolean> => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.post(`http://localhost:8080/storage/runs/run/${id}`);
|
const response = await axios.post(`${apiUrl}/storage/runs/run/${id}`);
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
return response.data;
|
return response.data;
|
||||||
} else {
|
} else {
|
||||||
@@ -120,7 +121,7 @@ export const interpretStoredRecording = async (id: string): Promise<boolean> =>
|
|||||||
|
|
||||||
export const notifyAboutAbort = async (id: string): Promise<boolean> => {
|
export const notifyAboutAbort = async (id: string): Promise<boolean> => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.post(`http://localhost:8080/storage/runs/abort/${id}`);
|
const response = await axios.post(`${apiUrl}/storage/runs/abort/${id}`);
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
return response.data;
|
return response.data;
|
||||||
} else {
|
} else {
|
||||||
@@ -135,7 +136,7 @@ export const notifyAboutAbort = async (id: string): Promise<boolean> => {
|
|||||||
export const scheduleStoredRecording = async (id: string, settings: ScheduleSettings): Promise<ScheduleRunResponse> => {
|
export const scheduleStoredRecording = async (id: string, settings: ScheduleSettings): Promise<ScheduleRunResponse> => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.put(
|
const response = await axios.put(
|
||||||
`http://localhost:8080/storage/schedule/${id}`,
|
`${apiUrl}/storage/schedule/${id}`,
|
||||||
{ ...settings });
|
{ ...settings });
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
return response.data;
|
return response.data;
|
||||||
@@ -150,7 +151,7 @@ export const scheduleStoredRecording = async (id: string, settings: ScheduleSett
|
|||||||
|
|
||||||
export const getSchedule = async (id: string) => {
|
export const getSchedule = async (id: string) => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(`http://localhost:8080/storage/schedule/${id}`);
|
const response = await axios.get(`${apiUrl}/storage/schedule/${id}`);
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
return response.data.schedule;
|
return response.data.schedule;
|
||||||
} else {
|
} else {
|
||||||
@@ -164,7 +165,7 @@ export const getSchedule = async (id: string) => {
|
|||||||
|
|
||||||
export const deleteSchedule = async (id: string): Promise<boolean> => {
|
export const deleteSchedule = async (id: string): Promise<boolean> => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.delete(`http://localhost:8080/storage/schedule/${id}`);
|
const response = await axios.delete(`${apiUrl}/storage/schedule/${id}`);
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
return response.data;
|
return response.data;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import { WhereWhatPair, WorkflowFile } from "maxun-core";
|
import { WhereWhatPair, WorkflowFile } from "maxun-core";
|
||||||
import { emptyWorkflow } from "../shared/constants";
|
import { emptyWorkflow } from "../shared/constants";
|
||||||
import { default as axios, AxiosResponse } from "axios";
|
import { default as axios, AxiosResponse } from "axios";
|
||||||
|
import { apiUrl } from "../apiConfig";
|
||||||
|
|
||||||
export const getActiveWorkflow = async(id: string) : Promise<WorkflowFile> => {
|
export const getActiveWorkflow = async(id: string) : Promise<WorkflowFile> => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(`http://localhost:8080/workflow/${id}`)
|
const response = await axios.get(`${apiUrl}/workflow/${id}`)
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
return response.data;
|
return response.data;
|
||||||
} else {
|
} else {
|
||||||
@@ -18,7 +19,7 @@ export const getActiveWorkflow = async(id: string) : Promise<WorkflowFile> => {
|
|||||||
|
|
||||||
export const getParamsOfActiveWorkflow = async(id: string) : Promise<string[]|null> => {
|
export const getParamsOfActiveWorkflow = async(id: string) : Promise<string[]|null> => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(`http://localhost:8080/workflow/params/${id}`)
|
const response = await axios.get(`${apiUrl}/workflow/params/${id}`)
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
return response.data;
|
return response.data;
|
||||||
} else {
|
} else {
|
||||||
@@ -32,7 +33,7 @@ export const getParamsOfActiveWorkflow = async(id: string) : Promise<string[]|nu
|
|||||||
|
|
||||||
export const deletePair = async(index: number): Promise<WorkflowFile> => {
|
export const deletePair = async(index: number): Promise<WorkflowFile> => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.delete(`http://localhost:8080/workflow/pair/${index}`);
|
const response = await axios.delete(`${apiUrl}/workflow/pair/${index}`);
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
return response.data;
|
return response.data;
|
||||||
} else {
|
} else {
|
||||||
@@ -46,7 +47,7 @@ export const deletePair = async(index: number): Promise<WorkflowFile> => {
|
|||||||
|
|
||||||
export const AddPair = async(index: number, pair: WhereWhatPair): Promise<WorkflowFile> => {
|
export const AddPair = async(index: number, pair: WhereWhatPair): Promise<WorkflowFile> => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.post(`http://localhost:8080/workflow/pair/${index}`, {
|
const response = await axios.post(`${apiUrl}/workflow/pair/${index}`, {
|
||||||
pair,
|
pair,
|
||||||
}, {headers: {'Content-Type': 'application/json'}});
|
}, {headers: {'Content-Type': 'application/json'}});
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
@@ -62,7 +63,7 @@ export const AddPair = async(index: number, pair: WhereWhatPair): Promise<Workfl
|
|||||||
|
|
||||||
export const UpdatePair = async(index: number, pair: WhereWhatPair): Promise<WorkflowFile> => {
|
export const UpdatePair = async(index: number, pair: WhereWhatPair): Promise<WorkflowFile> => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.put(`http://localhost:8080/workflow/pair/${index}`, {
|
const response = await axios.put(`${apiUrl}/workflow/pair/${index}`, {
|
||||||
pair,
|
pair,
|
||||||
}, {headers: {'Content-Type': 'application/json'}});
|
}, {headers: {'Content-Type': 'application/json'}});
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
|
|||||||
1
src/apiConfig.js
Normal file
1
src/apiConfig.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export const apiUrl = import.meta.env.VITE_BACKEND_URL;
|
||||||
@@ -6,6 +6,7 @@ import TextField from "@mui/material/TextField";
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { useGlobalInfoStore } from '../../context/globalInfo';
|
import { useGlobalInfoStore } from '../../context/globalInfo';
|
||||||
import { getStoredRecording } from '../../api/storage';
|
import { getStoredRecording } from '../../api/storage';
|
||||||
|
import { apiUrl } from '../../apiConfig.js';
|
||||||
interface IntegrationProps {
|
interface IntegrationProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
handleStart: (data: IntegrationSettings) => void;
|
handleStart: (data: IntegrationSettings) => void;
|
||||||
@@ -32,12 +33,12 @@ export const IntegrationSettingsModal = ({ isOpen, handleStart, handleClose }: I
|
|||||||
const [recording, setRecording] = useState<any>(null);
|
const [recording, setRecording] = useState<any>(null);
|
||||||
|
|
||||||
const authenticateWithGoogle = () => {
|
const authenticateWithGoogle = () => {
|
||||||
window.location.href = `http://localhost:8080/auth/google?robotId=${recordingId}`;
|
window.location.href = `${apiUrl}/auth/google?robotId=${recordingId}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOAuthCallback = async () => {
|
const handleOAuthCallback = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(`http://localhost:8080/auth/google/callback`);
|
const response = await axios.get(`${apiUrl}/auth/google/callback`);
|
||||||
const { google_sheet_email, files } = response.data;
|
const { google_sheet_email, files } = response.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setError('Error authenticating with Google');
|
setError('Error authenticating with Google');
|
||||||
@@ -46,7 +47,7 @@ export const IntegrationSettingsModal = ({ isOpen, handleStart, handleClose }: I
|
|||||||
|
|
||||||
const fetchSpreadsheetFiles = async () => {
|
const fetchSpreadsheetFiles = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(`http://localhost:8080/auth/gsheets/files?robotId=${recordingId}`, {
|
const response = await axios.get(`${apiUrl}/auth/gsheets/files?robotId=${recordingId}`, {
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
});
|
});
|
||||||
setSpreadsheets(response.data);
|
setSpreadsheets(response.data);
|
||||||
@@ -66,7 +67,7 @@ export const IntegrationSettingsModal = ({ isOpen, handleStart, handleClose }: I
|
|||||||
const updateGoogleSheetId = async () => {
|
const updateGoogleSheetId = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.post(
|
const response = await axios.post(
|
||||||
`http://localhost:8080/auth/gsheets/update`,
|
`${apiUrl}/auth/gsheets/update`,
|
||||||
{ spreadsheetId: settings.spreadsheetId, spreadsheetName: settings.spreadsheetName, robotId: recordingId },
|
{ spreadsheetId: settings.spreadsheetId, spreadsheetName: settings.spreadsheetName, robotId: recordingId },
|
||||||
{ withCredentials: true }
|
{ withCredentials: true }
|
||||||
);
|
);
|
||||||
@@ -79,7 +80,7 @@ export const IntegrationSettingsModal = ({ isOpen, handleStart, handleClose }: I
|
|||||||
const removeIntegration = async () => {
|
const removeIntegration = async () => {
|
||||||
try {
|
try {
|
||||||
await axios.post(
|
await axios.post(
|
||||||
`http://localhost:8080/auth/gsheets/remove`,
|
`${apiUrl}/auth/gsheets/remove`,
|
||||||
{ robotId: recordingId },
|
{ robotId: recordingId },
|
||||||
{ withCredentials: true }
|
{ withCredentials: true }
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { useNavigate } from 'react-router-dom';
|
|||||||
import { AuthContext } from '../../context/auth';
|
import { AuthContext } from '../../context/auth';
|
||||||
import { SaveRecording } from '../molecules/SaveRecording';
|
import { SaveRecording } from '../molecules/SaveRecording';
|
||||||
import DiscordIcon from '../atoms/DiscordIcon';
|
import DiscordIcon from '../atoms/DiscordIcon';
|
||||||
|
import { apiUrl } from '../../apiConfig';
|
||||||
|
|
||||||
interface NavBarProps {
|
interface NavBarProps {
|
||||||
recordingName: string;
|
recordingName: string;
|
||||||
@@ -34,7 +35,7 @@ export const NavBar: React.FC<NavBarProps> = ({ recordingName, isRecording }) =>
|
|||||||
const logout = async () => {
|
const logout = async () => {
|
||||||
dispatch({ type: 'LOGOUT' });
|
dispatch({ type: 'LOGOUT' });
|
||||||
window.localStorage.removeItem('user');
|
window.localStorage.removeItem('user');
|
||||||
const { data } = await axios.get('http://localhost:8080/auth/logout');
|
const { data } = await axios.get(`${apiUrl}/auth/logout`);
|
||||||
notify('success', data.message);
|
notify('success', data.message);
|
||||||
navigate('/login');
|
navigate('/login');
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import { ContentCopy, Visibility, Delete } from '@mui/icons-material';
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { useGlobalInfoStore } from '../../context/globalInfo';
|
import { useGlobalInfoStore } from '../../context/globalInfo';
|
||||||
|
import { apiUrl } from '../../apiConfig';
|
||||||
|
|
||||||
const Container = styled(Box)`
|
const Container = styled(Box)`
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -38,7 +39,7 @@ const ApiKeyManager = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchApiKey = async () => {
|
const fetchApiKey = async () => {
|
||||||
try {
|
try {
|
||||||
const { data } = await axios.get('http://localhost:8080/auth/api-key');
|
const { data } = await axios.get(`${apiUrl}/auth/api-key`);
|
||||||
setApiKey(data.api_key);
|
setApiKey(data.api_key);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
notify('error', `Failed to fetch API Key - ${error.message}`);
|
notify('error', `Failed to fetch API Key - ${error.message}`);
|
||||||
@@ -53,7 +54,7 @@ const ApiKeyManager = () => {
|
|||||||
const generateApiKey = async () => {
|
const generateApiKey = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
const { data } = await axios.post('http://localhost:8080/auth/generate-api-key');
|
const { data } = await axios.post(`${apiUrl}/auth/generate-api-key`);
|
||||||
setApiKey(data.api_key);
|
setApiKey(data.api_key);
|
||||||
notify('success', `Generated API Key successfully`);
|
notify('success', `Generated API Key successfully`);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
@@ -66,7 +67,7 @@ const ApiKeyManager = () => {
|
|||||||
const deleteApiKey = async () => {
|
const deleteApiKey = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
await axios.delete('http://localhost:8080/auth/delete-api-key');
|
await axios.delete(`${apiUrl}/auth/delete-api-key`);
|
||||||
setApiKey(null);
|
setApiKey(null);
|
||||||
notify('success', 'API Key deleted successfully');
|
notify('success', 'API Key deleted successfully');
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import React, { useCallback, useEffect, useState } from 'react';
|
|||||||
import { useSocketStore } from '../../context/socket';
|
import { useSocketStore } from '../../context/socket';
|
||||||
import { Button } from '@mui/material';
|
import { Button } from '@mui/material';
|
||||||
import Canvas from "../atoms/canvas";
|
import Canvas from "../atoms/canvas";
|
||||||
import { useBrowserDimensionsStore } from "../../context/browserDimensions";
|
|
||||||
import { Highlighter } from "../atoms/Highlighter";
|
import { Highlighter } from "../atoms/Highlighter";
|
||||||
import { GenericModal } from '../atoms/GenericModal';
|
import { GenericModal } from '../atoms/GenericModal';
|
||||||
import { useActionContext } from '../../context/browserActions';
|
import { useActionContext } from '../../context/browserActions';
|
||||||
@@ -66,7 +65,6 @@ export const BrowserWindow = () => {
|
|||||||
|
|
||||||
const { socket } = useSocketStore();
|
const { socket } = useSocketStore();
|
||||||
const { notify } = useGlobalInfoStore();
|
const { notify } = useGlobalInfoStore();
|
||||||
//const { width, height } = useBrowserDimensionsStore();
|
|
||||||
const { getText, getList, paginationMode, paginationType, limitMode } = useActionContext();
|
const { getText, getList, paginationMode, paginationType, limitMode } = useActionContext();
|
||||||
const { addTextStep, addListStep } = useBrowserSteps();
|
const { addTextStep, addListStep } = useBrowserSteps();
|
||||||
|
|
||||||
@@ -405,4 +403,4 @@ const modalStyle = {
|
|||||||
height: 'fit-content',
|
height: 'fit-content',
|
||||||
display: 'block',
|
display: 'block',
|
||||||
padding: '20px',
|
padding: '20px',
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import Tab from '@mui/material/Tab';
|
|||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
import { Paper, Button } from "@mui/material";
|
import { Paper, Button } from "@mui/material";
|
||||||
import { AutoAwesome, FormatListBulleted, VpnKey, Usb, Article, Link, CloudQueue } from "@mui/icons-material";
|
import { AutoAwesome, FormatListBulleted, VpnKey, Usb, Article, Link, CloudQueue } from "@mui/icons-material";
|
||||||
|
import { apiUrl } from "../../apiConfig";
|
||||||
|
|
||||||
interface MainMenuProps {
|
interface MainMenuProps {
|
||||||
value: string;
|
value: string;
|
||||||
@@ -86,7 +87,7 @@ export const MainMenu = ({ value = 'recordings', handleChangeContent }: MainMenu
|
|||||||
</Tabs>
|
</Tabs>
|
||||||
<hr />
|
<hr />
|
||||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: '1rem', textAlign: 'left' }}>
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: '1rem', textAlign: 'left' }}>
|
||||||
<Button href="http://localhost:8080/api-docs/" target="_blank" rel="noopener noreferrer" sx={buttonStyles} startIcon={<Article />}>
|
<Button href={`${apiUrl}/api-docs/`} target="_blank" rel="noopener noreferrer" sx={buttonStyles} startIcon={<Article />}>
|
||||||
API Docs
|
API Docs
|
||||||
</Button>
|
</Button>
|
||||||
<Button href="https://forms.gle/hXjgqDvkEhPcaBW76" target="_blank" rel="noopener noreferrer" sx={buttonStyles} startIcon={<CloudQueue />}>
|
<Button href="https://forms.gle/hXjgqDvkEhPcaBW76" target="_blank" rel="noopener noreferrer" sx={buttonStyles} startIcon={<CloudQueue />}>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useReducer, createContext, useEffect } from 'react';
|
import { useReducer, createContext, useEffect } from 'react';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { apiUrl } from "../apiConfig";
|
||||||
|
|
||||||
interface AuthProviderProps {
|
interface AuthProviderProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
@@ -65,7 +66,7 @@ const AuthProvider = ({ children }: AuthProviderProps) => {
|
|||||||
if (res.status === 401 && res.config && !res.config.__isRetryRequest) {
|
if (res.status === 401 && res.config && !res.config.__isRetryRequest) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios
|
axios
|
||||||
.get('http://localhost:8080/auth/logout')
|
.get(`${apiUrl}/auth/logout`)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
console.log('/401 error > logout');
|
console.log('/401 error > logout');
|
||||||
dispatch({ type: 'LOGOUT' });
|
dispatch({ type: 'LOGOUT' });
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import React, { createContext, useCallback, useContext, useMemo, useState } from 'react';
|
import React, { createContext, useCallback, useContext, useMemo, useState } from 'react';
|
||||||
import { io, Socket } from 'socket.io-client';
|
import { io, Socket } from 'socket.io-client';
|
||||||
|
import { apiUrl } from "../apiConfig";
|
||||||
|
|
||||||
const SERVER_ENDPOINT = 'http://localhost:8080';
|
const SERVER_ENDPOINT = apiUrl;
|
||||||
|
|
||||||
interface SocketState {
|
interface SocketState {
|
||||||
socket: Socket | null;
|
socket: Socket | null;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
CircularProgress,
|
CircularProgress,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { useGlobalInfoStore } from "../context/globalInfo";
|
import { useGlobalInfoStore } from "../context/globalInfo";
|
||||||
|
import { apiUrl } from "../apiConfig";
|
||||||
|
|
||||||
const Login = () => {
|
const Login = () => {
|
||||||
const [form, setForm] = useState({
|
const [form, setForm] = useState({
|
||||||
@@ -40,7 +41,7 @@ const Login = () => {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
const { data } = await axios.post(`http://localhost:8080/auth/login`, { email, password });
|
const { data } = await axios.post(`${apiUrl}/auth/login`, { email, password });
|
||||||
dispatch({ type: 'LOGIN', payload: data });
|
dispatch({ type: 'LOGIN', payload: data });
|
||||||
notify('success', 'Welcome to Maxun!');
|
notify('success', 'Welcome to Maxun!');
|
||||||
window.localStorage.setItem('user', JSON.stringify(data));
|
window.localStorage.setItem('user', JSON.stringify(data));
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { RunSettings } from "../components/molecules/RunSettings";
|
|||||||
import { ScheduleSettings } from "../components/molecules/ScheduleSettings";
|
import { ScheduleSettings } from "../components/molecules/ScheduleSettings";
|
||||||
import { IntegrationSettings } from "../components/molecules/IntegrationSettings";
|
import { IntegrationSettings } from "../components/molecules/IntegrationSettings";
|
||||||
import { RobotSettings } from "../components/molecules/RobotSettings";
|
import { RobotSettings } from "../components/molecules/RobotSettings";
|
||||||
|
import { apiUrl } from "../apiConfig";
|
||||||
|
|
||||||
interface MainPageProps {
|
interface MainPageProps {
|
||||||
handleEditRecording: (id: string, fileName: string) => void;
|
handleEditRecording: (id: string, fileName: string) => void;
|
||||||
@@ -88,7 +89,7 @@ export const MainPage = ({ handleEditRecording }: MainPageProps) => {
|
|||||||
createRunForStoredRecording(runningRecordingId, settings).then(({ browserId, runId }: CreateRunResponse) => {
|
createRunForStoredRecording(runningRecordingId, settings).then(({ browserId, runId }: CreateRunResponse) => {
|
||||||
setIds({ browserId, runId });
|
setIds({ browserId, runId });
|
||||||
const socket =
|
const socket =
|
||||||
io(`http://localhost:8080/${browserId}`, {
|
io(`${apiUrl}/${browserId}`, {
|
||||||
transports: ["websocket"],
|
transports: ["websocket"],
|
||||||
rejectUnauthorized: false
|
rejectUnauthorized: false
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import axios from 'axios';
|
|||||||
import { AuthContext } from '../context/auth';
|
import { AuthContext } from '../context/auth';
|
||||||
import { TextField, Button, CircularProgress, Typography, Box, Container } from '@mui/material';
|
import { TextField, Button, CircularProgress, Typography, Box, Container } from '@mui/material';
|
||||||
import { useGlobalInfoStore } from "../context/globalInfo";
|
import { useGlobalInfoStore } from "../context/globalInfo";
|
||||||
|
import { apiUrl } from "../apiConfig";
|
||||||
|
|
||||||
const Register = () => {
|
const Register = () => {
|
||||||
const [form, setForm] = useState({
|
const [form, setForm] = useState({
|
||||||
@@ -31,7 +32,7 @@ const Register = () => {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
const { data } = await axios.post('http://localhost:8080/auth/register', {
|
const { data } = await axios.post(`${apiUrl}/auth/register`, {
|
||||||
email,
|
email,
|
||||||
password,
|
password,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,13 +9,14 @@
|
|||||||
"strict": true,
|
"strict": true,
|
||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
"noFallthroughCasesInSwitch": true,
|
"noFallthroughCasesInSwitch": true,
|
||||||
"module": "commonjs",
|
"module": "esnext",
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"jsx": "react-jsx",
|
"jsx": "react-jsx",
|
||||||
|
"types": ["vite/client"],
|
||||||
"outDir": "./build"
|
"outDir": "./build"
|
||||||
},
|
},
|
||||||
"include": ["src"]
|
"include": ["src", "vite-env.d.ts"]
|
||||||
}
|
}
|
||||||
|
|||||||
7
vite-env.d.ts
vendored
Normal file
7
vite-env.d.ts
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
interface ImportMetaEnv {
|
||||||
|
readonly VITE_BACKEND_URL: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ImportMeta {
|
||||||
|
readonly env: ImportMetaEnv;
|
||||||
|
}
|
||||||
@@ -3,6 +3,9 @@ import react from '@vitejs/plugin-react';
|
|||||||
|
|
||||||
export default defineConfig(() => {
|
export default defineConfig(() => {
|
||||||
return {
|
return {
|
||||||
|
define: {
|
||||||
|
'import.meta.env.VITE_BACKEND_URL': JSON.stringify(process.env.VITE_BACKEND_URL),
|
||||||
|
},
|
||||||
build: {
|
build: {
|
||||||
outDir: 'build',
|
outDir: 'build',
|
||||||
manifest: true,
|
manifest: true,
|
||||||
|
|||||||
Reference in New Issue
Block a user