diff --git a/README.md b/README.md index aad5dc2f..67638748 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@

- +
Maxun
@@ -18,7 +18,8 @@ Maxun lets you train a robot in 2 minutes and scrape the web on auto-pilot. Web Website | Discord | Twitter | - Join Maxun Cloud + Join Maxun Cloud | + Watch Tutorials

![maxun_demo](https://github.com/user-attachments/assets/a61ba670-e56a-4ae1-9681-0b4bd6ba9cdc) @@ -109,6 +110,17 @@ BYOP (Bring Your Own Proxy) lets you connect external proxies to bypass anti-bot # Cloud We offer a managed cloud version to run Maxun without having to manage the infrastructure and extract data at scale. Maxun cloud also deals with anti-bot detection, huge proxy network with automatic proxy rotation, and CAPTCHA solving. If this interests you, [join the cloud waitlist](https://docs.google.com/forms/d/e/1FAIpQLSdbD2uhqC4sbg4eLZ9qrFbyrfkXZ2XsI6dQ0USRCQNZNn5pzg/viewform) as we launch soon. +# Screenshots +![Maxun PH Launch (1)-1-1](https://github.com/user-attachments/assets/d7c75fa2-2bbc-47bb-a5f6-0ee6c162f391) +![Maxun PH Launch (1)-2-1](https://github.com/user-attachments/assets/d85a3ec7-8ce8-4daa-89aa-52d9617e227a) +![Maxun PH Launch (1)-3-1](https://github.com/user-attachments/assets/4bd5a0b4-485d-44f4-a487-edd9afc18b11) +![Maxun PH Launch (1)-4-1](https://github.com/user-attachments/assets/78140675-a6b6-49b2-981f-6a3d9a32b0b9) +![Maxun PH Launch (1)-5-1](https://github.com/user-attachments/assets/d9fe8519-c81c-4e45-92f2-b2939bf24192) +![Maxun PH Launch (1)-6-1](https://github.com/user-attachments/assets/c26e9ae3-c3da-4280-826a-c7cdf913fb93) +![Maxun PH Launch (1)-7-1](https://github.com/user-attachments/assets/fd7196f4-a6dc-4c4c-9c76-fdd93fac8247) +![Maxun PH Launch (1)-8-1](https://github.com/user-attachments/assets/16ee4a71-772a-49ae-a0e5-cb0529519bda) +![Maxun PH Launch (1)-9-1](https://github.com/user-attachments/assets/160f46fa-0357-4c1b-ba50-b4fe64453bb7) + # Note This project is in early stages of development. Your feedback is very important for us - we're actively working to improve the product. Drop anonymous feedback here. diff --git a/docker-compose.yml b/docker-compose.yml index 50983684..1aeefd26 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -52,9 +52,6 @@ services: # DEBUG: pw:api # PWDEBUG: 1 # Enables debugging CHROMIUM_FLAGS: '--disable-gpu --no-sandbox --headless=new' - volumes: - # - /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 @@ -63,6 +60,10 @@ services: - postgres - redis - minio + volumes: + - ./server:/app/server # Mount server source code for hot reloading + - ./maxun-core:/app/maxun-core # Mount maxun-core for any shared code updates + - /var/run/dbus:/var/run/dbus frontend: build: @@ -71,6 +72,9 @@ services: ports: - "5173:5173" env_file: .env + volumes: + - ./:/app # Mount entire frontend app directory for hot reloading + - /app/node_modules # Anonymous volume to prevent overwriting node_modules depends_on: - backend diff --git a/index.html b/index.html index 81e42f02..c1ebd718 100644 --- a/index.html +++ b/index.html @@ -8,7 +8,7 @@ name="description" content="Web site created using Vite" /> - + Maxun | Open Source No Code Web Data Extraction Platform diff --git a/server/src/api/record.ts b/server/src/api/record.ts index 610f1825..b55f06bc 100644 --- a/server/src/api/record.ts +++ b/server/src/api/record.ts @@ -650,7 +650,7 @@ export async function handleRunRecording(id: string, userId: string) { throw new Error('browserId or runId or userId is undefined'); } - const socket = io(`${process.env.BACKEND_URL}/${browserId}`, { + const socket = io(`${process.env.BACKEND_URL ? process.env.BACKEND_URL : 'http://localhost:8080'}/${browserId}`, { transports: ['websocket'], rejectUnauthorized: false }); diff --git a/server/src/storage/mino.ts b/server/src/storage/mino.ts index 3b83e386..04efaf8c 100644 --- a/server/src/storage/mino.ts +++ b/server/src/storage/mino.ts @@ -2,7 +2,7 @@ import { Client } from 'minio'; import Run from '../models/Run'; const minioClient = new Client({ - endPoint: process.env.MINIO_ENDPOINT || 'localhost', + endPoint: process.env.MINIO_ENDPOINT ? process.env.MINIO_ENDPOINT : 'localhost', port: parseInt(process.env.MINIO_PORT || '9000'), useSSL: false, accessKey: process.env.MINIO_ACCESS_KEY || 'minio-access-key', @@ -108,7 +108,8 @@ class BinaryOutputService { await this.uploadBinaryOutputToMinioBucket(run, minioKey, binaryData); // Construct the public URL for the uploaded object - const publicUrl = `http://${process.env.MINIO_ENDPOINT}:${process.env.MINIO_PORT}/${this.bucketName}/${minioKey}`; + // todo: use minio endpoint + const publicUrl = `http://localhost:${process.env.MINIO_PORT}/${this.bucketName}/${minioKey}`; // Save the public URL in the result object uploadedBinaryOutput[key] = publicUrl; diff --git a/server/src/workflow-management/scheduler/index.ts b/server/src/workflow-management/scheduler/index.ts index cde08a29..a94c6d16 100644 --- a/server/src/workflow-management/scheduler/index.ts +++ b/server/src/workflow-management/scheduler/index.ts @@ -223,7 +223,7 @@ export async function handleRunRecording(id: string, userId: string) { throw new Error('browserId or runId or userId is undefined'); } - const socket = io(`${process.env.BACKEND_URL}/${browserId}`, { + const socket = io(`${process.env.BACKEND_URL ? process.env.BACKEND_URL : 'http://localhost:8080'}/${browserId}`, { transports: ['websocket'], rejectUnauthorized: false }); diff --git a/src/apiConfig.js b/src/apiConfig.js index 8661491e..9cf20e94 100644 --- a/src/apiConfig.js +++ b/src/apiConfig.js @@ -1 +1 @@ -export const apiUrl = import.meta.env.VITE_BACKEND_URL; \ No newline at end of file +export const apiUrl = import.meta.env.VITE_BACKEND_URL ? import.meta.env.VITE_BACKEND_URL : 'http://localhost:8080' \ No newline at end of file diff --git a/public/img/maxunlogo.png b/src/assets/maxunlogo.png similarity index 100% rename from public/img/maxunlogo.png rename to src/assets/maxunlogo.png diff --git a/src/components/molecules/ActionDescriptionBox.tsx b/src/components/molecules/ActionDescriptionBox.tsx index 4efdb32e..cad962c7 100644 --- a/src/components/molecules/ActionDescriptionBox.tsx +++ b/src/components/molecules/ActionDescriptionBox.tsx @@ -2,6 +2,7 @@ import React from 'react'; import styled from 'styled-components'; import { Typography, FormControlLabel, Checkbox, Box } from '@mui/material'; import { useActionContext } from '../../context/browserActions'; +import MaxunLogo from "../../assets/maxunlogo.png"; const CustomBoxContainer = styled.div` position: relative; @@ -110,7 +111,7 @@ const ActionDescriptionBox = () => { return ( - + {renderActionDescription()} diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 8dd678c5..69fba352 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -10,6 +10,7 @@ import { AuthContext } from '../../context/auth'; import { SaveRecording } from '../molecules/SaveRecording'; import DiscordIcon from '../atoms/DiscordIcon'; import { apiUrl } from '../../apiConfig'; +import MaxunLogo from "../../assets/maxunlogo.png"; interface NavBarProps { recordingName: string; @@ -55,7 +56,7 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => display: 'flex', justifyContent: 'flex-start', }}> - +
Maxun
{ diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index f2871aea..a11989bd 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -1,4 +1,4 @@ -import React, { useState, useCallback, useEffect } from 'react'; +import React, { useState, useCallback, useEffect, useMemo } from 'react'; import { Button, Paper, Box, TextField, IconButton } from "@mui/material"; import EditIcon from '@mui/icons-material/Edit'; import TextFieldsIcon from '@mui/icons-material/TextFields'; @@ -373,6 +373,20 @@ export const RightSidePanel: React.FC = ({ onFinishCapture stopGetScreenshot(); }; + const isConfirmCaptureDisabled = useMemo(() => { + // Check if we are in the initial stage and if there are no browser steps or no valid list selectors with fields + if (captureStage !== 'initial') return false; + + const hasValidListSelector = browserSteps.some(step => + step.type === 'list' && + step.listSelector && + Object.keys(step.fields).length > 0 + ); + + // Disable the button if there are no valid list selectors or if there are unconfirmed list text fields + return !hasValidListSelector || hasUnconfirmedListTextFields; + }, [captureStage, browserSteps, hasUnconfirmedListTextFields]); + return ( {/* @@ -387,7 +401,7 @@ export const RightSidePanel: React.FC = ({ onFinishCapture