Spaces:
Paused
Paused
Vo Hoang Minh commited on
Commit ·
2661273
1
Parent(s): 7a2c594
up
Browse files- .gitignore +2 -0
- Dockerfile +81 -0
- README.md +1 -0
- entrypoint.sh +15 -0
- orbita.conf +25 -0
- package.json +23 -0
- pnpm-workspace.yaml +3 -0
- src/server.mjs +173 -0
- tsconfig.json +9 -0
.gitignore
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
node_modules/
|
| 2 |
+
pnpm-lock.yaml
|
Dockerfile
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Sử dụng Debian Slim để giảm kích thước image nhưng vẫn giữ sự tương thích
|
| 2 |
+
FROM node:22
|
| 3 |
+
|
| 4 |
+
RUN corepack enable && \
|
| 5 |
+
corepack prepare pnpm@latest --activate && \
|
| 6 |
+
apk add --no-cache curl && \
|
| 7 |
+
curl -fsSL https://bun.sh/install | bash && \
|
| 8 |
+
ln -s /root/.bun/bin/bun /usr/local/bin/bun
|
| 9 |
+
|
| 10 |
+
# Thiết lập môi trường
|
| 11 |
+
ENV DEBIAN_FRONTEND=noninteractive \
|
| 12 |
+
DISPLAY=:0
|
| 13 |
+
|
| 14 |
+
# Cài đặt dependencies cần thiết trong một lệnh duy nhất để giảm số lượng layer
|
| 15 |
+
RUN ln -fs /usr/share/zoneinfo/America/New_York /etc/localtime && \
|
| 16 |
+
apt-get update && \
|
| 17 |
+
apt-get install -y --no-install-recommends \
|
| 18 |
+
tzdata x11vnc xvfb zip wget curl psmisc \
|
| 19 |
+
gconf-service libasound2 libatk1.0-0 libatk-bridge2.0-0 \
|
| 20 |
+
libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 \
|
| 21 |
+
libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 \
|
| 22 |
+
libgtk-3-bin libnspr4 libpango-1.0-0 libpangocairo-1.0-0 \
|
| 23 |
+
libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 \
|
| 24 |
+
libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 \
|
| 25 |
+
libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates \
|
| 26 |
+
fonts-liberation libappindicator1 libnss3 lsb-release \
|
| 27 |
+
xdg-utils libgbm-dev nginx libcurl3-gnutls && \
|
| 28 |
+
apt-get clean && rm -rf /var/lib/apt/lists/*
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
RUN mkdir -p /home/orbita/.gologin/browser
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
# Tải và cài đặt Orbita Browser
|
| 35 |
+
RUN wget https://orbita-browser-linux.gologin.com/orbita-browser-latest.tar.gz -O /tmp/orbita-browser.tar.gz && \
|
| 36 |
+
tar -xzf /tmp/orbita-browser.tar.gz -C /home/orbita/.gologin && \
|
| 37 |
+
mv /home/orbita/.gologin/orbita-browser /home/orbita/.gologin/browser && \
|
| 38 |
+
rm -f /tmp/orbita-browser.tar.gz
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
# Cài đặt Node.js dependencies
|
| 42 |
+
WORKDIR /opt/orbita
|
| 43 |
+
|
| 44 |
+
COPY ./ /opt/orbita/
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
# Cấu hình Nginx
|
| 48 |
+
COPY orbita.conf /etc/nginx/conf.d/orbita.conf
|
| 49 |
+
RUN rm /etc/nginx/sites-enabled/default
|
| 50 |
+
|
| 51 |
+
# Cấp quyền truy cập cho nginx và các thư mục cần thiết
|
| 52 |
+
RUN chmod 777 /var/lib/nginx -R && \
|
| 53 |
+
chmod 777 /var/log -R && \
|
| 54 |
+
chmod 777 /run -R
|
| 55 |
+
|
| 56 |
+
# Cấu hình X11
|
| 57 |
+
RUN mkdir /tmp/.X11-unix && chmod 1777 /tmp/.X11-unix
|
| 58 |
+
|
| 59 |
+
# Copy các file ứng dụng
|
| 60 |
+
COPY entrypoint.sh /entrypoint.sh
|
| 61 |
+
# COPY fonts /home/orbita/.gologin/browser/fonts
|
| 62 |
+
|
| 63 |
+
RUN groupadd -r orbita && \
|
| 64 |
+
useradd -r -g orbita -s /bin/bash -G audio,video,sudo -p $(echo 1 | openssl passwd -1 -stdin) orbita && \
|
| 65 |
+
mkdir -p /home/orbita/Downloads /home/orbita/.gologin/browser/fonts && \
|
| 66 |
+
chown -R orbita:orbita /home/orbita && \
|
| 67 |
+
echo 'orbita ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
|
| 68 |
+
|
| 69 |
+
# Cấp quyền cho entrypoint.sh
|
| 70 |
+
RUN chmod 777 /entrypoint.sh
|
| 71 |
+
|
| 72 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
| 73 |
+
|
| 74 |
+
# bun install package.json
|
| 75 |
+
RUN bun install
|
| 76 |
+
|
| 77 |
+
# Chạy container với user orbita
|
| 78 |
+
USER root
|
| 79 |
+
|
| 80 |
+
# Mở cổng dịch vụ
|
| 81 |
+
EXPOSE 3000
|
README.md
CHANGED
|
@@ -5,6 +5,7 @@ colorFrom: green
|
|
| 5 |
colorTo: red
|
| 6 |
sdk: docker
|
| 7 |
pinned: false
|
|
|
|
| 8 |
---
|
| 9 |
|
| 10 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
| 5 |
colorTo: red
|
| 6 |
sdk: docker
|
| 7 |
pinned: false
|
| 8 |
+
app_port: 3000
|
| 9 |
---
|
| 10 |
|
| 11 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
entrypoint.sh
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
|
| 3 |
+
mkdir ~/.vnc
|
| 4 |
+
DISPLAY=:0
|
| 5 |
+
export DISPLAY=:0
|
| 6 |
+
echo $SCREEN_WIDTH
|
| 7 |
+
echo $SCREEN_HEIGHT
|
| 8 |
+
echo `echo $SCREEN_WIDTH`x`echo $SCREEN_HEIGHT`x16
|
| 9 |
+
|
| 10 |
+
cd /opt/orbita
|
| 11 |
+
Xvfb $DISPLAY -screen 0 `echo $SCREEN_WIDTH`x`echo $SCREEN_HEIGHT`x16 &
|
| 12 |
+
sleep 3
|
| 13 |
+
x11vnc -storepasswd 12345678 ~/.vnc/passwd
|
| 14 |
+
x11vnc -display $DISPLAY -bg -forever -usepw -quiet -rfbport 5901 -xkb
|
| 15 |
+
/usr/sbin/nginx -c /etc/nginx/nginx.conf
|
orbita.conf
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
server {
|
| 2 |
+
listen 3000 default_server;
|
| 3 |
+
|
| 4 |
+
server_name _;
|
| 5 |
+
|
| 6 |
+
location /json {
|
| 7 |
+
proxy_pass http://127.0.0.1:3500;
|
| 8 |
+
proxy_http_version 1.1;
|
| 9 |
+
proxy_set_header X-Real-IP $server_addr;
|
| 10 |
+
proxy_set_header X-Forwarded-for $server_addr;
|
| 11 |
+
proxy_set_header Host '127.0.0.1';
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
location /devtools {
|
| 17 |
+
proxy_pass http://127.0.0.1:3500;
|
| 18 |
+
proxy_http_version 1.1;
|
| 19 |
+
proxy_set_header Upgrade $http_upgrade;
|
| 20 |
+
proxy_set_header Connection "upgrade";
|
| 21 |
+
proxy_set_header X-Real-IP $server_addr;
|
| 22 |
+
proxy_set_header X-Forwarded-for $server_addr;
|
| 23 |
+
proxy_set_header Host '127.0.0.1';
|
| 24 |
+
}
|
| 25 |
+
}
|
package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "cloudbrowser",
|
| 3 |
+
"version": "1.0.0",
|
| 4 |
+
"description": "",
|
| 5 |
+
"main": "src/app.mjs",
|
| 6 |
+
"type": "module",
|
| 7 |
+
"scripts": {
|
| 8 |
+
"test": "echo \"Error: no test specified\" && exit 1",
|
| 9 |
+
"dev": "bun run --hot src/server.mjs"
|
| 10 |
+
},
|
| 11 |
+
"keywords": [],
|
| 12 |
+
"author": "",
|
| 13 |
+
"license": "ISC",
|
| 14 |
+
"packageManager": "pnpm@10.10.0",
|
| 15 |
+
"dependencies": {
|
| 16 |
+
"hono": "^4.7.10",
|
| 17 |
+
"puppeteer-core": "^21.0.0",
|
| 18 |
+
"gologin": "^1.0.51"
|
| 19 |
+
|
| 20 |
+
},
|
| 21 |
+
"devDependencies": {
|
| 22 |
+
}
|
| 23 |
+
}
|
pnpm-workspace.yaml
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
onlyBuiltDependencies:
|
| 2 |
+
- better-sqlite3
|
| 3 |
+
- sqlite3
|
src/server.mjs
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Hono } from "hono";
|
| 2 |
+
import puppeteer from "puppeteer-core";
|
| 3 |
+
import { default as GoLogin } from "gologin";
|
| 4 |
+
|
| 5 |
+
const app = new Hono();
|
| 6 |
+
const PORT = process.env.PORT || 3000;
|
| 7 |
+
|
| 8 |
+
// Tạo một instance GoLogin để quản lý profile
|
| 9 |
+
const GL = new GoLogin({
|
| 10 |
+
token:
|
| 11 |
+
process.env.GOLOGIN_TOKEN ||
|
| 12 |
+
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI2N2VlYWJmOGJkODY2YjdjM2Y2NmIzZjEiLCJ0eXBlIjoiZGV2Iiwiand0aWQiOiI2N2VlYWMyNjcwZTM0MDBhNWY2YjdkZmUifQ.tUvpgtJL0swAUinAx1XIeWt4OQMjBqszIciDPKoE9Nk", // Token từ GoLogin để xác thực
|
| 13 |
+
tmpdir: "./tmp", // Thư mục lưu trữ tạm thời
|
| 14 |
+
profileId: "67eeac2670e340a5f67b7c3f66b3f1",
|
| 15 |
+
});
|
| 16 |
+
|
| 17 |
+
// API endpoint để khởi tạo trình duyệt
|
| 18 |
+
app.post("/browser/start", async (c) => {
|
| 19 |
+
try {
|
| 20 |
+
// Tạo profile mới
|
| 21 |
+
|
| 22 |
+
// Khởi động trình duyệt với profile đã tạo
|
| 23 |
+
const wsUrl = await GL.start({
|
| 24 |
+
uploadCookiesToServer: true,
|
| 25 |
+
autoUpdateBrowser: false,
|
| 26 |
+
});
|
| 27 |
+
|
| 28 |
+
return c.json({
|
| 29 |
+
success: true,
|
| 30 |
+
profileId,
|
| 31 |
+
wsUrl,
|
| 32 |
+
});
|
| 33 |
+
} catch (error) {
|
| 34 |
+
console.error("Error starting browser:", error);
|
| 35 |
+
return c.json(
|
| 36 |
+
{
|
| 37 |
+
success: false,
|
| 38 |
+
error: error.message,
|
| 39 |
+
},
|
| 40 |
+
500
|
| 41 |
+
);
|
| 42 |
+
}
|
| 43 |
+
});
|
| 44 |
+
|
| 45 |
+
// API endpoint để điều hướng trình duyệt
|
| 46 |
+
app.post("/browser/navigate", async (c) => {
|
| 47 |
+
try {
|
| 48 |
+
const { profileId, url } = await c.req.json();
|
| 49 |
+
|
| 50 |
+
if (!profileId || !url) {
|
| 51 |
+
return c.json(
|
| 52 |
+
{
|
| 53 |
+
success: false,
|
| 54 |
+
error: "Profile ID and URL are required",
|
| 55 |
+
},
|
| 56 |
+
400
|
| 57 |
+
);
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
const wsUrl = await GL.getProfileStartupInfo(profileId);
|
| 61 |
+
|
| 62 |
+
// Kết nối đến trình duyệt đang chạy
|
| 63 |
+
const browser = await puppeteer.connect({
|
| 64 |
+
browserWSEndpoint: wsUrl.wsUrl,
|
| 65 |
+
ignoreHTTPSErrors: true,
|
| 66 |
+
});
|
| 67 |
+
|
| 68 |
+
// Mở tab mới
|
| 69 |
+
const page = await browser.newPage();
|
| 70 |
+
|
| 71 |
+
// Điều hướng đến URL
|
| 72 |
+
await page.goto(url, { waitUntil: "networkidle2" });
|
| 73 |
+
|
| 74 |
+
// Chụp ảnh màn hình
|
| 75 |
+
const screenshot = await page.screenshot({ encoding: "base64" });
|
| 76 |
+
|
| 77 |
+
return c.json({
|
| 78 |
+
success: true,
|
| 79 |
+
screenshot,
|
| 80 |
+
});
|
| 81 |
+
} catch (error) {
|
| 82 |
+
console.error("Error navigating browser:", error);
|
| 83 |
+
return c.json(
|
| 84 |
+
{
|
| 85 |
+
success: false,
|
| 86 |
+
error: error.message,
|
| 87 |
+
},
|
| 88 |
+
500
|
| 89 |
+
);
|
| 90 |
+
}
|
| 91 |
+
});
|
| 92 |
+
|
| 93 |
+
// API endpoint để đóng trình duyệt
|
| 94 |
+
app.post("/browser/stop", async (c) => {
|
| 95 |
+
try {
|
| 96 |
+
const { profileId } = await c.req.json();
|
| 97 |
+
|
| 98 |
+
if (!profileId) {
|
| 99 |
+
return c.json(
|
| 100 |
+
{
|
| 101 |
+
success: false,
|
| 102 |
+
error: "Profile ID is required",
|
| 103 |
+
},
|
| 104 |
+
400
|
| 105 |
+
);
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
// Dừng trình duyệt từ xa
|
| 109 |
+
await GL.stopRemote(profileId);
|
| 110 |
+
|
| 111 |
+
return c.json({
|
| 112 |
+
success: true,
|
| 113 |
+
message: "Browser stopped successfully",
|
| 114 |
+
});
|
| 115 |
+
} catch (error) {
|
| 116 |
+
console.error("Error stopping browser:", error);
|
| 117 |
+
return c.json(
|
| 118 |
+
{
|
| 119 |
+
success: false,
|
| 120 |
+
error: error.message,
|
| 121 |
+
},
|
| 122 |
+
500
|
| 123 |
+
);
|
| 124 |
+
}
|
| 125 |
+
});
|
| 126 |
+
|
| 127 |
+
// Trang chủ
|
| 128 |
+
app.get("/", (c) => {
|
| 129 |
+
return c.html(`
|
| 130 |
+
<!DOCTYPE html>
|
| 131 |
+
<html lang="en">
|
| 132 |
+
<head>
|
| 133 |
+
<meta charset="UTF-8">
|
| 134 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 135 |
+
<title>Cloud Browser API</title>
|
| 136 |
+
<style>
|
| 137 |
+
body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
|
| 138 |
+
h1 { color: #333; }
|
| 139 |
+
.endpoint { background: #f5f5f5; padding: 15px; margin: 20px 0; border-radius: 5px; }
|
| 140 |
+
code { background: #e0e0e0; padding: 2px 5px; border-radius: 3px; }
|
| 141 |
+
</style>
|
| 142 |
+
</head>
|
| 143 |
+
<body>
|
| 144 |
+
<h1>Cloud Browser API</h1>
|
| 145 |
+
<p>Sử dụng API này để khởi tạo và điều khiển trình duyệt từ xa.</p>
|
| 146 |
+
|
| 147 |
+
<div class="endpoint">
|
| 148 |
+
<h2>POST /browser/start</h2>
|
| 149 |
+
<p>Khởi động một trình duyệt mới và trả về profile ID và WebSocket URL.</p>
|
| 150 |
+
</div>
|
| 151 |
+
|
| 152 |
+
<div class="endpoint">
|
| 153 |
+
<h2>POST /browser/navigate</h2>
|
| 154 |
+
<p>Điều hướng trình duyệt đến URL xác định và trả về ảnh chụp màn hình.</p>
|
| 155 |
+
<p>Body: <code>{ "profileId": "...", "url": "https://example.com" }</code></p>
|
| 156 |
+
</div>
|
| 157 |
+
|
| 158 |
+
<div class="endpoint">
|
| 159 |
+
<h2>POST /browser/stop</h2>
|
| 160 |
+
<p>Đóng trình duyệt.</p>
|
| 161 |
+
<p>Body: <code>{ "profileId": "..." }</code></p>
|
| 162 |
+
</div>
|
| 163 |
+
</body>
|
| 164 |
+
</html>
|
| 165 |
+
`);
|
| 166 |
+
});
|
| 167 |
+
|
| 168 |
+
// Khởi động server
|
| 169 |
+
console.log(`Server is starting on port ${PORT}...`);
|
| 170 |
+
|
| 171 |
+
console.log(`Server running at http://localhost:${PORT}`);
|
| 172 |
+
|
| 173 |
+
export default app;
|
tsconfig.json
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"compilerOptions": {
|
| 3 |
+
"target": "ES2020",
|
| 4 |
+
"module": "NodeNext",
|
| 5 |
+
"moduleResolution": "NodeNext",
|
| 6 |
+
"esModuleInterop": true,
|
| 7 |
+
"strict": true
|
| 8 |
+
}
|
| 9 |
+
}
|