From 45fec68d9260e1a1fae95c63a26e3203fb8c0137 Mon Sep 17 00:00:00 2001 From: nkhangg Date: Mon, 24 Mar 2025 15:20:47 +0700 Subject: [PATCH 1/6] update title login --- auto-bid-admin/src/pages/login.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auto-bid-admin/src/pages/login.tsx b/auto-bid-admin/src/pages/login.tsx index 22faa56..1c6fd88 100644 --- a/auto-bid-admin/src/pages/login.tsx +++ b/auto-bid-admin/src/pages/login.tsx @@ -34,7 +34,7 @@ export default function Login() { - Login to KTQ Admin + Login to Bid System From 7445cd5032a02b0f3916416054fe8b267583641a Mon Sep 17 00:00:00 2001 From: nkhangg Date: Mon, 24 Mar 2025 16:21:13 +0700 Subject: [PATCH 2/6] upload dev env --- auto-bid-admin/.env.dev | 2 ++ auto-bid-server/.env.dev | 30 ++++++++++++++++++++++++++++++ auto-bid-tool/.env.dev | 3 +++ auto-bid-tool/.gitignore | 3 --- 4 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 auto-bid-admin/.env.dev create mode 100644 auto-bid-server/.env.dev create mode 100644 auto-bid-tool/.env.dev diff --git a/auto-bid-admin/.env.dev b/auto-bid-admin/.env.dev new file mode 100644 index 0000000..b4a0abd --- /dev/null +++ b/auto-bid-admin/.env.dev @@ -0,0 +1,2 @@ +VITE_BASE_URL = 'http://localhost:4000/api/v1/admin/' +VITE_SOCKET_URL = 'http://localhost:4000' \ No newline at end of file diff --git a/auto-bid-server/.env.dev b/auto-bid-server/.env.dev new file mode 100644 index 0000000..3575351 --- /dev/null +++ b/auto-bid-server/.env.dev @@ -0,0 +1,30 @@ +DB_HOST=127.0.0.1 +# DB_HOST=127.0.0.1 +DB_USERNAME=root +DB_PASSWORD=123 + +ENVIRONMENT='dev' + +DB_PORT=3306 +DB_NAME=auto-bids + +PORT = 4000 + + + +APP_PATH = 'http://localhost:4000' + +CORS = "http://localhost:5173, http://localhost:3000" + + +SECRET_KEY = "kgmwljwekqiq25232mdmsgnekwhlwekmglkwjqjwqw" + + +# DEV GROUP +TELEGRAM_BOT_TOKEN = "7963294152:AAE8b9AbsyLYzpeJbUMIcelVWlCBN5mLJ2o" +CHAT_ID = "-1002593407119" + +# Bid histories GROUP +# TELEGRAM_BOT_TOKEN = "7533631751:AAEfE7Ei015U1sSsYPSAYwbYXWFl5D7y_18" +# CHAT_ID = "-1002535794248" + diff --git a/auto-bid-tool/.env.dev b/auto-bid-tool/.env.dev new file mode 100644 index 0000000..430c5e8 --- /dev/null +++ b/auto-bid-tool/.env.dev @@ -0,0 +1,3 @@ +ENVIRONMENT = 'prod' +SOCKET_URL = 'http://localhost:4000' +BASE_URL = 'http://localhost:4000/api/v1/' \ No newline at end of file diff --git a/auto-bid-tool/.gitignore b/auto-bid-tool/.gitignore index 1b9e5a5..92344c3 100644 --- a/auto-bid-tool/.gitignore +++ b/auto-bid-tool/.gitignore @@ -40,9 +40,6 @@ lerna-debug.log* # dotenv environment variable files .env -.env.development.local -.env.test.local -.env.production.local .env.local # temp directory From 614b8ca77cea64c0a6b30cc6fee80a0a023c5181 Mon Sep 17 00:00:00 2001 From: nkhangg Date: Tue, 25 Mar 2025 09:05:13 +0700 Subject: [PATCH 3/6] update browser args --- .../src/components/admin/admin-modal.tsx | 14 +++++++++-- .../admin/grant-new-password-modal.tsx | 11 ++++++-- .../src/components/bid/bid-modal.tsx | 14 +++++++++-- .../show-histories-bid-grays-api-modal.tsx | 10 ++++++-- auto-bid-admin/src/components/user-menu.tsx | 12 +++++++-- .../components/web-bid/web-account-modal.tsx | 13 ++++++++-- .../src/components/web-bid/web-bid-modal.tsx | 13 ++++++++-- .../1742778498009-create-admin-table.ts | 5 ++++ .../modules/admins/entities/admin.entity.ts | 5 +++- auto-bid-tool/system/browser.js | 25 ++++++++++++++----- 10 files changed, 101 insertions(+), 21 deletions(-) diff --git a/auto-bid-admin/src/components/admin/admin-modal.tsx b/auto-bid-admin/src/components/admin/admin-modal.tsx index 0fa57c2..c65a52f 100644 --- a/auto-bid-admin/src/components/admin/admin-modal.tsx +++ b/auto-bid-admin/src/components/admin/admin-modal.tsx @@ -1,10 +1,10 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import { Badge, Box, Button, Modal, ModalProps, PasswordInput, Text, TextInput, Tooltip } from '@mantine/core'; +import { Badge, Box, Button, LoadingOverlay, Modal, ModalProps, PasswordInput, Text, TextInput, Tooltip } from '@mantine/core'; import { useForm, zodResolver } from '@mantine/form'; import { useDisclosure } from '@mantine/hooks'; import { IconPlus } from '@tabler/icons-react'; import _ from 'lodash'; -import { useEffect, useRef } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { z } from 'zod'; import { createAdmin, updateAdmin } from '../../apis/admin'; import { useConfirmStore } from '../../lib/zustand/use-confirm'; @@ -40,6 +40,8 @@ export default function AdminModal({ data, onUpdated, ...props }: IAdminModelPro validate: zodResolver(data ? updateSchema : createSchema), }); + const [loading, setLoading] = useState(false); + const [opened, { open, close }] = useDisclosure(false); const { deletePermission, setPermissions, permissions, basePermission } = usePermissionStore(); @@ -54,7 +56,9 @@ export default function AdminModal({ data, onUpdated, ...props }: IAdminModelPro title: 'Update ?', message: `This account will be update`, handleOk: async () => { + setLoading(true); const result = await updateAdmin(values); + setLoading(false); if (!result) return; @@ -72,7 +76,10 @@ export default function AdminModal({ data, onUpdated, ...props }: IAdminModelPro } else { const { confirmPassword, ...newValues } = values; + setLoading(true); + const result = await createAdmin(newValues as Omit); + setLoading(false); if (!result) return; @@ -110,6 +117,7 @@ export default function AdminModal({ data, onUpdated, ...props }: IAdminModelPro return ( + + ); } diff --git a/auto-bid-admin/src/components/admin/grant-new-password-modal.tsx b/auto-bid-admin/src/components/admin/grant-new-password-modal.tsx index 61a10e2..20cd805 100644 --- a/auto-bid-admin/src/components/admin/grant-new-password-modal.tsx +++ b/auto-bid-admin/src/components/admin/grant-new-password-modal.tsx @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import { Button, Modal, ModalProps, PasswordInput } from '@mantine/core'; +import { Button, LoadingOverlay, Modal, ModalProps, PasswordInput } from '@mantine/core'; import { useForm, zodResolver } from '@mantine/form'; -import { useEffect } from 'react'; +import { useEffect, useState } from 'react'; import { z } from 'zod'; import { grantNewPasswordAdmin } from '../../apis/admin'; import { useConfirmStore } from '../../lib/zustand/use-confirm'; @@ -26,6 +26,8 @@ export default function GrantNewPasswordModal({ data, onUpdated, ...props }: IAd validate: zodResolver(schema), }); + const [loading, setLoading] = useState(false); + const { setConfirm } = useConfirmStore(); const handleSubmit = async (values: typeof form.values) => { @@ -34,10 +36,12 @@ export default function GrantNewPasswordModal({ data, onUpdated, ...props }: IAd title: 'Update ?', message: `This account will be update`, handleOk: async () => { + setLoading(true); const result = await grantNewPasswordAdmin({ id: data.id, password: values.password, }); + setLoading(false); if (!result) return; @@ -73,6 +77,7 @@ export default function GrantNewPasswordModal({ data, onUpdated, ...props }: IAd return ( + + ); } diff --git a/auto-bid-admin/src/components/bid/bid-modal.tsx b/auto-bid-admin/src/components/bid/bid-modal.tsx index 856eb5a..141dcdd 100644 --- a/auto-bid-admin/src/components/bid/bid-modal.tsx +++ b/auto-bid-admin/src/components/bid/bid-modal.tsx @@ -1,8 +1,8 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import { Button, Modal, ModalProps, NumberInput, TextInput } from '@mantine/core'; +import { Button, LoadingOverlay, Modal, ModalProps, NumberInput, TextInput } from '@mantine/core'; import { useForm, zodResolver } from '@mantine/form'; import _ from 'lodash'; -import { useEffect, useRef } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { z } from 'zod'; import { createBid, updateBid } from '../../apis/bid'; import { useConfirmStore } from '../../lib/zustand/use-confirm'; @@ -28,13 +28,17 @@ export default function BidModal({ data, onUpdated, ...props }: IBidModelProps) const { setConfirm } = useConfirmStore(); + const [loading, setLoading] = useState(false); + const handleSubmit = async (values: typeof form.values) => { if (data) { setConfirm({ title: 'Update ?', message: `This product will be update`, handleOk: async () => { + setLoading(true); const result = await updateBid(values); + setLoading(false); if (!result) return; @@ -52,8 +56,11 @@ export default function BidModal({ data, onUpdated, ...props }: IBidModelProps) } else { const { url, max_price, plus_price } = values; + setLoading(true); const result = await createBid({ url, max_price, plus_price } as IBid); + setLoading(false); + if (!result) return; props.onClose(); @@ -83,6 +90,7 @@ export default function BidModal({ data, onUpdated, ...props }: IBidModelProps) return ( + + ); } diff --git a/auto-bid-admin/src/components/bid/show-histories-bid-grays-api-modal.tsx b/auto-bid-admin/src/components/bid/show-histories-bid-grays-api-modal.tsx index 62917e5..04ad1ee 100644 --- a/auto-bid-admin/src/components/bid/show-histories-bid-grays-api-modal.tsx +++ b/auto-bid-admin/src/components/bid/show-histories-bid-grays-api-modal.tsx @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import { Modal, ModalProps, Table } from '@mantine/core'; +import { LoadingOverlay, Modal, ModalProps, Table } from '@mantine/core'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { getDetailBidHistories } from '../../apis/bid-histories'; import { extractNumber } from '../../lib/table/ultils'; @@ -14,6 +14,8 @@ export interface IShowHistoriesBidGraysApiModalProps extends ModalProps { export default function ShowHistoriesBidGraysApiModal({ data, onUpdated, ...props }: IShowHistoriesBidGraysApiModalProps) { const [histories, setHistories] = useState[]>([]); + const [loading, setLoading] = useState(false); + const rows = useMemo(() => { return histories.map((element) => ( @@ -33,7 +35,9 @@ export default function ShowHistoriesBidGraysApiModal({ data, onUpdated, ...prop return; } + setLoading(true); const response = await getDetailBidHistories(data?.lot_id); + setLoading(false); if (response.data && response.data) { setHistories(response.data); @@ -45,7 +49,7 @@ export default function ShowHistoriesBidGraysApiModal({ data, onUpdated, ...prop }, [handleCallApi]); return ( - BIDDING HISTORY} centered> + BIDDING HISTORY} centered> @@ -68,6 +72,8 @@ export default function ShowHistoriesBidGraysApiModal({ data, onUpdated, ...prop )}
+ +
); } diff --git a/auto-bid-admin/src/components/user-menu.tsx b/auto-bid-admin/src/components/user-menu.tsx index 61975c1..8ca1640 100644 --- a/auto-bid-admin/src/components/user-menu.tsx +++ b/auto-bid-admin/src/components/user-menu.tsx @@ -1,4 +1,4 @@ -import { Avatar, Button, Menu, Modal, PasswordInput } from '@mantine/core'; +import { Avatar, Button, LoadingOverlay, Menu, Modal, PasswordInput } from '@mantine/core'; import { useForm, zodResolver } from '@mantine/form'; import { useDisclosure } from '@mantine/hooks'; import { IconLogout, IconSettings, IconUser } from '@tabler/icons-react'; @@ -7,6 +7,7 @@ import { z } from 'zod'; import { changePassword, logout } from '../apis/auth'; import { useConfirmStore } from '../lib/zustand/use-confirm'; import Links from '../system/links'; +import { useState } from 'react'; const schema = z .object({ @@ -24,6 +25,8 @@ export default function UserMenu() { const { setConfirm } = useConfirmStore(); + const [loading, setLoading] = useState(false); + const navigate = useNavigate(); const form = useForm({ initialValues: { @@ -59,11 +62,14 @@ export default function UserMenu() { message: 'This account will change password !', okButton: { value: 'Sure' }, handleOk: async () => { + setLoading(true); const data = await changePassword({ newPassword: values.newPassword, password: values.currentPassword, }); + setLoading(false); + if (data && data.data) { navigate(Links.LOGIN); close(); @@ -95,7 +101,7 @@ export default function UserMenu() { - +
@@ -104,6 +110,8 @@ export default function UserMenu() { Change + +
); diff --git a/auto-bid-admin/src/components/web-bid/web-account-modal.tsx b/auto-bid-admin/src/components/web-bid/web-account-modal.tsx index a0eae98..f2a35d2 100644 --- a/auto-bid-admin/src/components/web-bid/web-account-modal.tsx +++ b/auto-bid-admin/src/components/web-bid/web-account-modal.tsx @@ -1,8 +1,8 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import { Button, Modal, ModalProps, PasswordInput, TextInput } from '@mantine/core'; +import { Button, LoadingOverlay, Modal, ModalProps, PasswordInput, TextInput } from '@mantine/core'; import { useForm, zodResolver } from '@mantine/form'; import _ from 'lodash'; -import { useEffect, useRef } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { z } from 'zod'; import { updateWebBid } from '../../apis/web-bid'; import { useConfirmStore } from '../../lib/zustand/use-confirm'; @@ -22,6 +22,8 @@ export default function WebAccountModal({ data, onUpdated, ...props }: IWebBidMo validate: zodResolver(schema), }); + const [loading, setLoading] = useState(false); + const prevData = useRef(data); const { setConfirm } = useConfirmStore(); @@ -32,7 +34,9 @@ export default function WebAccountModal({ data, onUpdated, ...props }: IWebBidMo title: 'Update ?', message: `This account will be update`, handleOk: async () => { + setLoading(true); const result = await updateWebBid(values); + setLoading(false); if (!result) return; @@ -48,7 +52,9 @@ export default function WebAccountModal({ data, onUpdated, ...props }: IWebBidMo }, }); } else { + setLoading(true); const result = await updateWebBid(values); + setLoading(false); if (!result) return; @@ -79,6 +85,7 @@ export default function WebAccountModal({ data, onUpdated, ...props }: IWebBidMo return ( + + ); } diff --git a/auto-bid-admin/src/components/web-bid/web-bid-modal.tsx b/auto-bid-admin/src/components/web-bid/web-bid-modal.tsx index 30f0897..a9eee58 100644 --- a/auto-bid-admin/src/components/web-bid/web-bid-modal.tsx +++ b/auto-bid-admin/src/components/web-bid/web-bid-modal.tsx @@ -1,8 +1,8 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import { Button, Modal, ModalProps, TextInput } from '@mantine/core'; +import { Button, LoadingOverlay, Modal, ModalProps, TextInput } from '@mantine/core'; import { useForm, zodResolver } from '@mantine/form'; import _ from 'lodash'; -import { useEffect, useRef } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { z } from 'zod'; import { createWebBid, updateWebBid } from '../../apis/web-bid'; import { useConfirmStore } from '../../lib/zustand/use-confirm'; @@ -22,6 +22,8 @@ export default function WebBidModal({ data, onUpdated, ...props }: IWebBidModelP validate: zodResolver(z.object(schema)), }); + const [loading, setLoading] = useState(false); + const prevData = useRef(data); const { setConfirm } = useConfirmStore(); @@ -32,7 +34,9 @@ export default function WebBidModal({ data, onUpdated, ...props }: IWebBidModelP title: 'Update ?', message: `This web will be update`, handleOk: async () => { + setLoading(true); const result = await updateWebBid(values); + setLoading(false); if (!result) return; @@ -50,7 +54,9 @@ export default function WebBidModal({ data, onUpdated, ...props }: IWebBidModelP } else { const { url, origin_url } = values; + setLoading(true); const result = await createWebBid({ url, origin_url } as IWebBid); + setLoading(false); if (!result) return; @@ -88,6 +94,7 @@ export default function WebBidModal({ data, onUpdated, ...props }: IWebBidModelP return ( + + ); } diff --git a/auto-bid-server/src/migrations/1742778498009-create-admin-table.ts b/auto-bid-server/src/migrations/1742778498009-create-admin-table.ts index 3d82f86..20c08de 100644 --- a/auto-bid-server/src/migrations/1742778498009-create-admin-table.ts +++ b/auto-bid-server/src/migrations/1742778498009-create-admin-table.ts @@ -2,6 +2,11 @@ import { MigrationInterface, QueryRunner } from 'typeorm'; export class CreateAdminTable1742778498009 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise { + // { + // 'username': 'admin', + // 'password': 'Admin@123' + // } + await queryRunner.query(` INSERT INTO admins (email, username, password, is_system_account) VALUES ('admin@gmail.com', 'admin', '$2b$10$eF7K4Msw32e5ZC2cU78KgOqxMJygQcPDt5xXZP29inBBIV9KEsoyO', 1); diff --git a/auto-bid-server/src/modules/admins/entities/admin.entity.ts b/auto-bid-server/src/modules/admins/entities/admin.entity.ts index 745bd7f..7644bb2 100644 --- a/auto-bid-server/src/modules/admins/entities/admin.entity.ts +++ b/auto-bid-server/src/modules/admins/entities/admin.entity.ts @@ -24,6 +24,9 @@ export default class Admin extends Timestamp { @Column({ type: 'boolean', default: false }) is_system_account: boolean; - @ManyToMany(() => Permission, (permission) => permission.admins) + @ManyToMany(() => Permission, (permission) => permission.admins, { + cascade: true, + onDelete: 'CASCADE', + }) permissions: Permission[]; } diff --git a/auto-bid-tool/system/browser.js b/auto-bid-tool/system/browser.js index 66c230e..95d3a7d 100644 --- a/auto-bid-tool/system/browser.js +++ b/auto-bid-tool/system/browser.js @@ -5,19 +5,32 @@ import StealthPlugin from 'puppeteer-extra-plugin-stealth'; puppeteer.use(StealthPlugin()); const browser = await puppeteer.launch({ - headless: process.env.ENVIRONMENT === 'prod' ? true : false, + headless: process.env.ENVIRONMENT === 'prod' ? 'new' : false, // userDataDir: CONSTANTS.PROFILE_PATH, // Thư mục lưu profile timeout: 60000, args: [ '--no-sandbox', '--disable-setuid-sandbox', - '--disable-backgrounding-occluded-windows', - '--disable-renderer-backgrounding', - '--disable-ipc-flooding-protection', - '--disable-features=CalculateNativeWinOcclusion,AudioServiceOutOfProcess', - '--disable-background-timer-throttling', + '--disable-dev-shm-usage', + '--disable-gpu', + '--disable-software-rasterizer', + '--disable-background-networking', + '--disable-sync', + '--mute-audio', + '--no-first-run', + '--no-default-browser-check', '--ignore-certificate-errors', '--start-maximized', + '--disable-site-isolation-trials', // Tắt sandbox riêng cho từng site + '--memory-pressure-off', // Tắt cơ chế bảo vệ bộ nhớ + '--disk-cache-size=0', // Không dùng cache để giảm bộ nhớ + '--enable-low-end-device-mode', // Kích hoạt chế độ tiết kiệm RAM + '--disable-best-effort-tasks', // Tắt tác vụ không quan trọng + '--disable-accelerated-2d-canvas', // Không dùng GPU để vẽ canvas + '--disable-threaded-animation', // Giảm animation chạy trên nhiều thread + '--disable-threaded-scrolling', // Tắt cuộn trang đa luồng + '--disable-logging', // Tắt log debug + '--blink-settings=imagesEnabled=false', // Không tải hình ảnh ], }); From 1fc6e75be205c5da6100cc16fc729a25a0e16959 Mon Sep 17 00:00:00 2001 From: nkhangg Date: Tue, 25 Mar 2025 10:53:13 +0700 Subject: [PATCH 4/6] update worktracking --- auto-bid-tool/index.js | 128 +++++++++++++----- auto-bid-tool/models/bid.js | 5 +- .../models/grays.com/grays-api-bid.js | 5 +- auto-bid-tool/package-lock.json | 28 ++++ auto-bid-tool/package.json | 1 + auto-bid-tool/service/app-service.js | 1 + auto-bid-tool/system/browser.js | 5 +- 7 files changed, 134 insertions(+), 39 deletions(-) diff --git a/auto-bid-tool/index.js b/auto-bid-tool/index.js index 435e28f..96f1e8d 100644 --- a/auto-bid-tool/index.js +++ b/auto-bid-tool/index.js @@ -5,14 +5,11 @@ import { createApiBid, createBidProduct, deleteProfile, shouldUpdateProductTab } import browser from './system/browser.js'; import configs from './system/config.js'; import { delay, isTimeReached, safeClosePage } from './system/utils.js'; +import pLimit from 'p-limit'; let MANAGER_BIDS = []; -let _INTERVAL_TRACKING_ID = null; -let _CLEAR_LAZY_TAB_ID = null; -let _WORK_TRACKING_ID = null; - -global.IS_CLEANING = false; +const activeTasks = new Set(); const handleUpdateProductTabs = (data) => { if (!Array.isArray(data)) { @@ -181,58 +178,125 @@ const tracking = async () => { } }; -const clearLazyTab = async () => { - if (!global.IS_CLEANING) return; +// const clearLazyTab = async () => { +// if (!browser) { +// console.warn('⚠️ Browser is not available or disconnected.'); +// return; +// } +// try { +// const pages = await browser.pages(); + +// // Lấy danh sách URL từ flattenedArray +// const activeUrls = _.flatMap(MANAGER_BIDS, (item) => [item.url, ...item.children.map((child) => child.url)]).filter(Boolean); // Lọc bỏ null hoặc undefined + +// console.log( +// '🔍 Page URLs:', +// pages.map((page) => page.url()), +// ); + +// for (const page of pages) { +// const pageUrl = page.url(); + +// // 🔥 Bỏ qua tab 'about:blank' hoặc tab không có URL +// if (!pageUrl || pageUrl === 'about:blank') continue; + +// if (!activeUrls.includes(pageUrl)) { +// if (!page.isClosed() && browser.isConnected()) { +// try { +// await page.close(); +// console.log(`🛑 Closing unused tab: ${pageUrl}`); +// } catch (err) { +// console.warn(`⚠️ Error closing tab ${pageUrl}:`, err.message); +// } +// } +// } +// } +// } catch (err) { +// console.error('❌ Error in clearLazyTab:', err.message); +// } +// }; + +// const workTracking = async () => { +// try { +// const activeData = _.flatMap(MANAGER_BIDS, (item) => [item, ...item.children]); + +// for (const item of activeData) { +// if (item.page_context && !item.page_context.isClosed()) { +// item.handleTakeWorkSnapshot(); +// } +// } +// } catch (error) { +// console.log('Lỗi rồi:', error); +// } +// }; + +const clearLazyTab = async () => { if (!browser) { console.warn('⚠️ Browser is not available or disconnected.'); return; } try { - const pages = await browser.pages(); + const contexts = browser.contexts(); // Lấy tất cả contexts - // Lấy danh sách URL từ flattenedArray - const activeUrls = _.flatMap(MANAGER_BIDS, (item) => [item.url, ...item.children.map((child) => child.url)]).filter(Boolean); // Lọc bỏ null hoặc undefined + for (const context of contexts) { + const pages = context.pages(); // Lấy tất cả pages trong context - console.log( - '🔍 Page URLs:', - pages.map((page) => page.url()), - ); + // Lấy danh sách URL từ flattenedArray + const activeUrls = _.flatMap(MANAGER_BIDS, (item) => [item.url, ...item.children.map((child) => child.url)]).filter(Boolean); - for (const page of pages) { - const pageUrl = page.url(); + for (const page of pages) { + const pageUrl = page.url(); - // 🔥 Bỏ qua tab 'about:blank' hoặc tab không có URL - if (!pageUrl || pageUrl === 'about:blank') continue; + if (!pageUrl || pageUrl === 'about:blank') continue; - if (!activeUrls.includes(pageUrl)) { - if (!page.isClosed() && browser.isConnected()) { - try { - await page.close(); - console.log(`🛑 Closing unused tab: ${pageUrl}`); - } catch (err) { - console.warn(`⚠️ Error closing tab ${pageUrl}:`, err.message); + if (!activeUrls.includes(pageUrl)) { + if (!page.isClosed()) { + try { + await page.close(); + console.log(`🛑 Closing unused tab: ${pageUrl}`); + } catch (err) { + console.warn(`⚠️ Error closing tab ${pageUrl}:`, err.message); + } } } } + + // Nếu context không còn page nào thì đóng luôn + if (context.pages().length === 0) { + await context.close(); + console.log(`🗑️ Closing unused context`); + } } } catch (err) { console.error('❌ Error in clearLazyTab:', err.message); } }; - const workTracking = async () => { try { const activeData = _.flatMap(MANAGER_BIDS, (item) => [item, ...item.children]); + const limit = pLimit(5); - for (const item of activeData) { - if (item.page_context && !item.page_context.isClosed()) { - item.handleTakeWorkSnapshot(); - } - } + await Promise.allSettled( + activeData + .filter((item) => item.page_context && !item.page_context.isClosed()) + .filter((item) => !activeTasks.has(item.id)) + .map((item) => + limit(async () => { + activeTasks.add(item.id); + try { + await item.handleTakeWorkSnapshot(); + } catch (error) { + console.error(`[❌ ERROR] Snapshot failed for Product ID: ${item.id}`, error); + } finally { + activeTasks.delete(item.id); + } + }), + ), + ); } catch (error) { - console.log('Lỗi rồi:', error); + console.error(`[❌ ERROR] Work tracking failed: ${error.message}\n`, error.stack); } }; diff --git a/auto-bid-tool/models/bid.js b/auto-bid-tool/models/bid.js index 1f2111e..08164a7 100644 --- a/auto-bid-tool/models/bid.js +++ b/auto-bid-tool/models/bid.js @@ -19,10 +19,11 @@ export class Bid { if (!this.page_context) return; try { - console.log(`✅ Page loaded. Taking snapshot for Product ID: ${this.id}`); + await this.page_context.waitForSelector('#pageContainer', { timeout: 10000 }); + console.log(`✅ Page fully loaded. Taking snapshot for Product ID: ${this.id}`); takeSnapshot(this.page_context, this, 'working', CONSTANTS.TYPE_IMAGE.WORK); } catch (error) { console.error(`❌ Error taking snapshot for Product ID: ${this.id}:`, error.message); } - }, 500); + }, 1000); } diff --git a/auto-bid-tool/models/grays.com/grays-api-bid.js b/auto-bid-tool/models/grays.com/grays-api-bid.js index 370d69a..3da2809 100644 --- a/auto-bid-tool/models/grays.com/grays-api-bid.js +++ b/auto-bid-tool/models/grays.com/grays-api-bid.js @@ -134,14 +134,12 @@ export class GrayApiBid extends ApiBid { // 🔍 Check if already logged in (login input should not be visible) if (!(await page.$('input[name="username"]')) || fs.existsSync(filePath)) { console.log('✅ Already logged in, skipping login.'); - global.IS_CLEANING = true; this.retry_login = 0; // Reset retry count return; } console.log('🔑 Starting login process...'); - global.IS_CLEANING = false; try { await page.type('input[name="username"]', this.username, { delay: 100 }); @@ -156,7 +154,6 @@ export class GrayApiBid extends ApiBid { if (!(await page.$('input[name="username"]'))) { console.log('✅ Login successful!'); this.retry_login = 0; // Reset retry count after success - global.IS_CLEANING = true; return; } @@ -211,7 +208,7 @@ export class GrayApiBid extends ApiBid { }); page.on('load', async () => { - console.log('🔄 Trang đã reload, khởi động lại polling...'); + console.log('🔄 The page has reloaded, restarting polling...'); // await takeSnapshot(this.page_context, this, 'working', CONSTANTS.TYPE_IMAGE.WORK); diff --git a/auto-bid-tool/package-lock.json b/auto-bid-tool/package-lock.json index 9ce2e4f..2414118 100644 --- a/auto-bid-tool/package-lock.json +++ b/auto-bid-tool/package-lock.json @@ -12,6 +12,7 @@ "axios": "^1.8.2", "dotenv": "^16.4.7", "lodash": "^4.17.21", + "p-limit": "^6.2.0", "puppeteer": "^24.4.0", "puppeteer-extra": "^3.3.6", "puppeteer-extra-plugin-stealth": "^2.11.2", @@ -1294,6 +1295,21 @@ "wrappy": "1" } }, + "node_modules/p-limit": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-6.2.0.tgz", + "integrity": "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA==", + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/pac-proxy-agent": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", @@ -1992,6 +2008,18 @@ "fd-slicer": "~1.1.0" } }, + "node_modules/yocto-queue": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.1.tgz", + "integrity": "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==", + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/zod": { "version": "3.24.2", "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.2.tgz", diff --git a/auto-bid-tool/package.json b/auto-bid-tool/package.json index cbe78b9..9776209 100644 --- a/auto-bid-tool/package.json +++ b/auto-bid-tool/package.json @@ -15,6 +15,7 @@ "axios": "^1.8.2", "dotenv": "^16.4.7", "lodash": "^4.17.21", + "p-limit": "^6.2.0", "puppeteer": "^24.4.0", "puppeteer-extra": "^3.3.6", "puppeteer-extra-plugin-stealth": "^2.11.2", diff --git a/auto-bid-tool/service/app-service.js b/auto-bid-tool/service/app-service.js index f32fd42..f01c4b8 100644 --- a/auto-bid-tool/service/app-service.js +++ b/auto-bid-tool/service/app-service.js @@ -5,6 +5,7 @@ import configs from '../system/config.js'; import CONSTANTS from '../system/constants.js'; import { sanitizeFileName } from '../system/utils.js'; import * as fs from 'fs'; +import _ from 'lodash'; const ONE_MINUTE = 60 * 1000; diff --git a/auto-bid-tool/system/browser.js b/auto-bid-tool/system/browser.js index 95d3a7d..00114d6 100644 --- a/auto-bid-tool/system/browser.js +++ b/auto-bid-tool/system/browser.js @@ -30,7 +30,10 @@ const browser = await puppeteer.launch({ '--disable-threaded-animation', // Giảm animation chạy trên nhiều thread '--disable-threaded-scrolling', // Tắt cuộn trang đa luồng '--disable-logging', // Tắt log debug - '--blink-settings=imagesEnabled=false', // Không tải hình ảnh + '--blink-settings=imagesEnabled=false', // Không tải hình ảnh, + '--disable-background-timer-throttling', // Tránh việc throttling các timer khi chạy nền. + '--disable-webrtc', + '--disable-ipc-flooding-protection', // Nếu có extension cần IPC, cái này giúp tối ưu. ], }); From add0cf0263e1c0dc62ccb4ad7c29dd884c14ed73 Mon Sep 17 00:00:00 2001 From: nkhangg Date: Tue, 25 Mar 2025 13:19:04 +0700 Subject: [PATCH 5/6] update tracking fn --- auto-bid-tool/index.js | 193 ++++++++++-------- auto-bid-tool/models/bid.js | 2 +- .../models/grays.com/grays-product-bid.js | 9 - 3 files changed, 111 insertions(+), 93 deletions(-) diff --git a/auto-bid-tool/index.js b/auto-bid-tool/index.js index 96f1e8d..979e7e2 100644 --- a/auto-bid-tool/index.js +++ b/auto-bid-tool/index.js @@ -46,22 +46,29 @@ const handleUpdateProductTabs = (data) => { }; // const tracking = async () => { -// if (_INTERVAL_TRACKING_ID) { -// clearInterval(_INTERVAL_TRACKING_ID); -// _INTERVAL_TRACKING_ID = null; -// } +// console.log('🚀 Tracking process started...'); -// _INTERVAL_TRACKING_ID = setInterval(async () => { +// while (true) { +// console.log('🔍 Scanning active bids...'); // const productTabs = _.flatMap(MANAGER_BIDS, 'children'); +// for (const apiBid of MANAGER_BIDS) { +// if (apiBid.page_context) continue; + +// console.log(`🎧 Listening to events for API Bid ID: ${apiBid.id}`); +// await apiBid.listen_events(); +// } + // for (const productTab of productTabs) { +// console.log(`📌 Processing Product ID: ${productTab.id}`); + // // Tìm parent context nếu chưa có // if (!productTab.parent_browser_context) { // const parent = _.find(MANAGER_BIDS, { id: productTab.web_bid.id }); // productTab.parent_browser_context = parent?.browser_context; // if (!productTab.parent_browser_context) { -// console.log(`🔄 Waiting for parent process to start... (Product ID: ${productTab.id})`); +// console.log(`⏳ Waiting for parent process to start... (Product ID: ${productTab.id})`); // continue; // } // } @@ -74,6 +81,7 @@ const handleUpdateProductTabs = (data) => { // // Nếu URL thay đổi, điều hướng đến URL mới // if (productTab.page_context.url() !== productTab.url) { +// console.log(`🔄 Redirecting to new URL for Product ID: ${productTab.id}`); // await productTab.gotoLink(); // } @@ -87,7 +95,7 @@ const handleUpdateProductTabs = (data) => { // // Nếu chưa có first_bid (trạng thái chưa đặt giá) // if (!productTab.first_bid) { -// console.log(`🎯 Tracking out-bid event for Product ID: ${productTab.id}`); +// console.log(`🎯 Waiting for first bid for Product ID: ${productTab.id}`); // continue; // } @@ -100,84 +108,18 @@ const handleUpdateProductTabs = (data) => { // console.log(`🚀 Executing action for Product ID: ${productTab.id}`); // await productTab.action(); // } -// }, configs.AUTO_TRACKING_DELAY); + +// console.log('🧹 Cleaning up unused tabs...'); +// await clearLazyTab(); + +// console.log('📊 Tracking work status...'); +// workTracking(); + +// console.log(`⏳ Waiting ${configs.AUTO_TRACKING_DELAY / 1000} seconds before the next iteration...`); +// await delay(configs.AUTO_TRACKING_DELAY); +// } // }; -const tracking = async () => { - console.log('🚀 Tracking process started...'); - - while (true) { - console.log('🔍 Scanning active bids...'); - const productTabs = _.flatMap(MANAGER_BIDS, 'children'); - - for (const apiBid of MANAGER_BIDS) { - if (apiBid.page_context) continue; - - console.log(`🎧 Listening to events for API Bid ID: ${apiBid.id}`); - await apiBid.listen_events(); - } - - for (const productTab of productTabs) { - console.log(`📌 Processing Product ID: ${productTab.id}`); - - // Tìm parent context nếu chưa có - if (!productTab.parent_browser_context) { - const parent = _.find(MANAGER_BIDS, { id: productTab.web_bid.id }); - productTab.parent_browser_context = parent?.browser_context; - - if (!productTab.parent_browser_context) { - console.log(`⏳ Waiting for parent process to start... (Product ID: ${productTab.id})`); - continue; - } - } - - // Kết nối Puppeteer nếu chưa có page_context - if (!productTab.page_context) { - console.log(`🔌 Connecting to page for Product ID: ${productTab.id}`); - await productTab.puppeteer_connect(); - } - - // Nếu URL thay đổi, điều hướng đến URL mới - if (productTab.page_context.url() !== productTab.url) { - console.log(`🔄 Redirecting to new URL for Product ID: ${productTab.id}`); - await productTab.gotoLink(); - } - - // Kiểm tra nếu cần cập nhật trước khi gọi update() - if (shouldUpdateProductTab(productTab)) { - console.log(`🔄 Updating Product ID: ${productTab.id}...`); - await productTab.update(); - } else { - console.log(`⏳ Product ID: ${productTab.id} was updated recently. Skipping update.`); - } - - // Nếu chưa có first_bid (trạng thái chưa đặt giá) - if (!productTab.first_bid) { - console.log(`🎯 Waiting for first bid for Product ID: ${productTab.id}`); - continue; - } - - // Nếu chưa đến giờ bid - if (productTab.start_bid_time && !isTimeReached(productTab.start_bid_time)) { - console.log(`⏳ Not yet time to bid. Skipping Product ID: ${productTab.id}`); - continue; - } - - console.log(`🚀 Executing action for Product ID: ${productTab.id}`); - await productTab.action(); - } - - console.log('🧹 Cleaning up unused tabs...'); - await clearLazyTab(); - - console.log('📊 Tracking work status...'); - workTracking(); - - console.log(`⏳ Waiting ${configs.AUTO_TRACKING_DELAY / 1000} seconds before the next iteration...`); - await delay(configs.AUTO_TRACKING_DELAY); - } -}; - // const clearLazyTab = async () => { // if (!browser) { // console.warn('⚠️ Browser is not available or disconnected.'); @@ -231,6 +173,91 @@ const tracking = async () => { // } // }; +const tracking = async () => { + console.log('🚀 Tracking process started...'); + + while (true) { + try { + console.log('🔍 Scanning active bids...'); + const productTabs = _.flatMap(MANAGER_BIDS, 'children'); + + // Lắng nghe sự kiện của API bids (chạy song song) + await Promise.allSettled( + MANAGER_BIDS.filter((bid) => !bid.page_context).map((apiBid) => { + console.log(`🎧 Listening to events for API Bid ID: ${apiBid.id}`); + return apiBid.listen_events(); + }), + ); + + // Duyệt qua từng productTab + await Promise.allSettled( + productTabs.map(async (productTab) => { + console.log(`📌 Processing Product ID: ${productTab.id}`); + + // Xác định parent context + if (!productTab.parent_browser_context) { + const parent = _.find(MANAGER_BIDS, { id: productTab.web_bid.id }); + productTab.parent_browser_context = parent?.browser_context; + if (!productTab.parent_browser_context) { + console.log(`⏳ Waiting for parent process... (Product ID: ${productTab.id})`); + return; + } + } + + // Kết nối Puppeteer nếu chưa có page_context + if (!productTab.page_context) { + console.log(`🔌 Connecting to page for Product ID: ${productTab.id}`); + await productTab.puppeteer_connect(); + } + + // Kiểm tra URL và điều hướng nếu cần + if (productTab.page_context.url() !== productTab.url) { + console.log(`🔄 Redirecting to new URL for Product ID: ${productTab.id}`); + await productTab.gotoLink(); + } + + // Cập nhật nếu cần thiết + if (shouldUpdateProductTab(productTab)) { + console.log(`🔄 Updating Product ID: ${productTab.id}...`); + await productTab.update(); + } else { + console.log(`⏳ Product ID: ${productTab.id} was updated recently. Skipping update.`); + } + + // Chờ first bid + if (!productTab.first_bid) { + console.log(`🎯 Waiting for first bid for Product ID: ${productTab.id}`); + return; + } + + // Kiểm tra thời gian bid + if (productTab.start_bid_time && !isTimeReached(productTab.start_bid_time)) { + console.log(`⏳ Not yet time to bid. Skipping Product ID: ${productTab.id}`); + return; + } + + // Thực thi hành động + console.log(`🚀 Executing action for Product ID: ${productTab.id}`); + await productTab.action(); + }), + ); + + // Dọn dẹp tab không dùng + console.log('🧹 Cleaning up unused tabs...'); + await clearLazyTab(); + + // Cập nhật trạng thái tracking + console.log('📊 Tracking work status...'); + workTracking(); + } catch (error) { + console.error('❌ Error in tracking loop:', error); + } + + console.log(`⏳ Waiting ${configs.AUTO_TRACKING_DELAY / 1000} seconds before the next iteration...`); + await delay(configs.AUTO_TRACKING_DELAY); + } +}; + const clearLazyTab = async () => { if (!browser) { console.warn('⚠️ Browser is not available or disconnected.'); diff --git a/auto-bid-tool/models/bid.js b/auto-bid-tool/models/bid.js index 08164a7..91053ec 100644 --- a/auto-bid-tool/models/bid.js +++ b/auto-bid-tool/models/bid.js @@ -19,7 +19,7 @@ export class Bid { if (!this.page_context) return; try { - await this.page_context.waitForSelector('#pageContainer', { timeout: 10000 }); + // await this.page_context.waitForSelector('#pageContainer', { timeout: 10000 }); console.log(`✅ Page fully loaded. Taking snapshot for Product ID: ${this.id}`); takeSnapshot(this.page_context, this, 'working', CONSTANTS.TYPE_IMAGE.WORK); } catch (error) { diff --git a/auto-bid-tool/models/grays.com/grays-product-bid.js b/auto-bid-tool/models/grays.com/grays-product-bid.js index 04153ad..aeb77ba 100644 --- a/auto-bid-tool/models/grays.com/grays-product-bid.js +++ b/auto-bid-tool/models/grays.com/grays-product-bid.js @@ -38,8 +38,6 @@ export class GraysProductBid extends ProductBid { }); if (!response.status) { - // await this.handleReturnProductPage(page); - // await safeClosePage(this); return { result: false, bid_price: 0 }; } @@ -95,7 +93,6 @@ export class GraysProductBid extends ProductBid { if (!close_time || new Date(close_time).getTime() <= new Date().getTime()) { console.log(`Product is close ${close_time} ❌`); - // await safeClosePage(this); return { result: true, close_time }; } @@ -126,10 +123,8 @@ export class GraysProductBid extends ProductBid { await takeSnapshot(page, this, 'bid-success', CONSTANTS.TYPE_IMAGE.SUCCESS); return true; } catch (error) { - console.log({ error: error.message }); console.log('❌ Timeout to loading'); await takeSnapshot(page, this, 'timeout to loading'); - // await safeClosePage(this); return false; } } @@ -140,8 +135,6 @@ export class GraysProductBid extends ProductBid { } async handleUpdateBid({ lot_id, close_time, name, current_price, reserve_price }) { - // if (close_time && this.close_time == close_time) return; - const response = await updateBid(this.id, { lot_id, close_time, name, current_price, reserve_price: Number(reserve_price) || 0 }); if (response) { @@ -236,7 +229,6 @@ export class GraysProductBid extends ProductBid { if (!resultPlaceBid) { console.log('❌ Error occurred while placing the bid.'); await takeSnapshot(page, this, 'place-bid-action'); - // await safeClosePage(this); return; } @@ -245,7 +237,6 @@ export class GraysProductBid extends ProductBid { await this.handleReturnProductPage(page); } catch (error) { console.error(`🚨 Error navigating the page: ${error.message}`); - // safeClosePage(this); } }; } From 1e214a81623737b2bcf7b7c466170e6d0a82c5de Mon Sep 17 00:00:00 2001 From: nkhangg Date: Tue, 25 Mar 2025 15:19:42 +0700 Subject: [PATCH 6/6] update --- auto-bid-tool/index.js | 184 ++++------------------------------ auto-bid-tool/system/utils.js | 4 +- 2 files changed, 23 insertions(+), 165 deletions(-) diff --git a/auto-bid-tool/index.js b/auto-bid-tool/index.js index 979e7e2..bf9c217 100644 --- a/auto-bid-tool/index.js +++ b/auto-bid-tool/index.js @@ -45,134 +45,6 @@ const handleUpdateProductTabs = (data) => { MANAGER_BIDS = newDataManager; }; -// const tracking = async () => { -// console.log('🚀 Tracking process started...'); - -// while (true) { -// console.log('🔍 Scanning active bids...'); -// const productTabs = _.flatMap(MANAGER_BIDS, 'children'); - -// for (const apiBid of MANAGER_BIDS) { -// if (apiBid.page_context) continue; - -// console.log(`🎧 Listening to events for API Bid ID: ${apiBid.id}`); -// await apiBid.listen_events(); -// } - -// for (const productTab of productTabs) { -// console.log(`📌 Processing Product ID: ${productTab.id}`); - -// // Tìm parent context nếu chưa có -// if (!productTab.parent_browser_context) { -// const parent = _.find(MANAGER_BIDS, { id: productTab.web_bid.id }); -// productTab.parent_browser_context = parent?.browser_context; - -// if (!productTab.parent_browser_context) { -// console.log(`⏳ Waiting for parent process to start... (Product ID: ${productTab.id})`); -// continue; -// } -// } - -// // Kết nối Puppeteer nếu chưa có page_context -// if (!productTab.page_context) { -// console.log(`🔌 Connecting to page for Product ID: ${productTab.id}`); -// await productTab.puppeteer_connect(); -// } - -// // Nếu URL thay đổi, điều hướng đến URL mới -// if (productTab.page_context.url() !== productTab.url) { -// console.log(`🔄 Redirecting to new URL for Product ID: ${productTab.id}`); -// await productTab.gotoLink(); -// } - -// // Kiểm tra nếu cần cập nhật trước khi gọi update() -// if (shouldUpdateProductTab(productTab)) { -// console.log(`🔄 Updating Product ID: ${productTab.id}...`); -// await productTab.update(); -// } else { -// console.log(`⏳ Product ID: ${productTab.id} was updated recently. Skipping update.`); -// } - -// // Nếu chưa có first_bid (trạng thái chưa đặt giá) -// if (!productTab.first_bid) { -// console.log(`🎯 Waiting for first bid for Product ID: ${productTab.id}`); -// continue; -// } - -// // Nếu chưa đến giờ bid -// if (productTab.start_bid_time && !isTimeReached(productTab.start_bid_time)) { -// console.log(`⏳ Not yet time to bid. Skipping Product ID: ${productTab.id}`); -// continue; -// } - -// console.log(`🚀 Executing action for Product ID: ${productTab.id}`); -// await productTab.action(); -// } - -// console.log('🧹 Cleaning up unused tabs...'); -// await clearLazyTab(); - -// console.log('📊 Tracking work status...'); -// workTracking(); - -// console.log(`⏳ Waiting ${configs.AUTO_TRACKING_DELAY / 1000} seconds before the next iteration...`); -// await delay(configs.AUTO_TRACKING_DELAY); -// } -// }; - -// const clearLazyTab = async () => { -// if (!browser) { -// console.warn('⚠️ Browser is not available or disconnected.'); -// return; -// } - -// try { -// const pages = await browser.pages(); - -// // Lấy danh sách URL từ flattenedArray -// const activeUrls = _.flatMap(MANAGER_BIDS, (item) => [item.url, ...item.children.map((child) => child.url)]).filter(Boolean); // Lọc bỏ null hoặc undefined - -// console.log( -// '🔍 Page URLs:', -// pages.map((page) => page.url()), -// ); - -// for (const page of pages) { -// const pageUrl = page.url(); - -// // 🔥 Bỏ qua tab 'about:blank' hoặc tab không có URL -// if (!pageUrl || pageUrl === 'about:blank') continue; - -// if (!activeUrls.includes(pageUrl)) { -// if (!page.isClosed() && browser.isConnected()) { -// try { -// await page.close(); -// console.log(`🛑 Closing unused tab: ${pageUrl}`); -// } catch (err) { -// console.warn(`⚠️ Error closing tab ${pageUrl}:`, err.message); -// } -// } -// } -// } -// } catch (err) { -// console.error('❌ Error in clearLazyTab:', err.message); -// } -// }; - -// const workTracking = async () => { -// try { -// const activeData = _.flatMap(MANAGER_BIDS, (item) => [item, ...item.children]); - -// for (const item of activeData) { -// if (item.page_context && !item.page_context.isClosed()) { -// item.handleTakeWorkSnapshot(); -// } -// } -// } catch (error) { -// console.log('Lỗi rồi:', error); -// } -// }; - const tracking = async () => { console.log('🚀 Tracking process started...'); @@ -189,8 +61,7 @@ const tracking = async () => { }), ); - // Duyệt qua từng productTab - await Promise.allSettled( + Promise.allSettled( productTabs.map(async (productTab) => { console.log(`📌 Processing Product ID: ${productTab.id}`); @@ -211,7 +82,7 @@ const tracking = async () => { } // Kiểm tra URL và điều hướng nếu cần - if (productTab.page_context.url() !== productTab.url) { + if ((await productTab.page_context.url()) !== productTab.url) { console.log(`🔄 Redirecting to new URL for Product ID: ${productTab.id}`); await productTab.gotoLink(); } @@ -244,7 +115,7 @@ const tracking = async () => { // Dọn dẹp tab không dùng console.log('🧹 Cleaning up unused tabs...'); - await clearLazyTab(); + clearLazyTab(); // Cập nhật trạng thái tracking console.log('📊 Tracking work status...'); @@ -265,41 +136,38 @@ const clearLazyTab = async () => { } try { - const contexts = browser.contexts(); // Lấy tất cả contexts + const pages = await browser.pages(); - for (const context of contexts) { - const pages = context.pages(); // Lấy tất cả pages trong context + // Lấy danh sách URL từ flattenedArray + const activeUrls = _.flatMap(MANAGER_BIDS, (item) => [item.url, ...item.children.map((child) => child.url)]).filter(Boolean); // Lọc bỏ null hoặc undefined - // Lấy danh sách URL từ flattenedArray - const activeUrls = _.flatMap(MANAGER_BIDS, (item) => [item.url, ...item.children.map((child) => child.url)]).filter(Boolean); + console.log( + '🔍 Page URLs:', + pages.map((page) => page.url()), + ); - for (const page of pages) { - const pageUrl = page.url(); + for (const page of pages) { + const pageUrl = page.url(); - if (!pageUrl || pageUrl === 'about:blank') continue; + // 🔥 Bỏ qua tab 'about:blank' hoặc tab không có URL + if (!pageUrl || pageUrl === 'about:blank') continue; - if (!activeUrls.includes(pageUrl)) { - if (!page.isClosed()) { - try { - await page.close(); - console.log(`🛑 Closing unused tab: ${pageUrl}`); - } catch (err) { - console.warn(`⚠️ Error closing tab ${pageUrl}:`, err.message); - } + if (!activeUrls.includes(pageUrl)) { + if (!page.isClosed() && browser.isConnected()) { + try { + await page.close(); + console.log(`🛑 Closing unused tab: ${pageUrl}`); + } catch (err) { + console.warn(`⚠️ Error closing tab ${pageUrl}:, err.message`); } } } - - // Nếu context không còn page nào thì đóng luôn - if (context.pages().length === 0) { - await context.close(); - console.log(`🗑️ Closing unused context`); - } } } catch (err) { console.error('❌ Error in clearLazyTab:', err.message); } }; + const workTracking = async () => { try { const activeData = _.flatMap(MANAGER_BIDS, (item) => [item, ...item.children]); @@ -344,8 +212,6 @@ const workTracking = async () => { console.log('📢 Bids Data:', data); handleUpdateProductTabs(data); - - // await Promise.all(MANAGER_BIDS.map((apiBid) => apiBid.listen_events())); }); socket.on('webUpdated', async (data) => { @@ -361,8 +227,6 @@ const workTracking = async () => { if (tabs.length <= 0) return; await Promise.all(tabs.map((tab) => safeClosePage(tab))); - - await Promise.all(MANAGER_BIDS.map((apiBid) => apiBid.listen_events())); } else { console.log('⚠️ No profile found to delete.'); } @@ -370,8 +234,4 @@ const workTracking = async () => { // AUTO TRACKING tracking(); - - // clearLazyTab(); - - // workTracking(); })(); diff --git a/auto-bid-tool/system/utils.js b/auto-bid-tool/system/utils.js index 01ca787..502940c 100644 --- a/auto-bid-tool/system/utils.js +++ b/auto-bid-tool/system/utils.js @@ -39,7 +39,7 @@ export const takeSnapshot = async (page, item, imageName, type = CONSTANTS.TYPE_ }); // Chụp ảnh màn hình và lưu vào filePath - await page.screenshot({ path: filePath, fullPage: true }); + await page.screenshot({ path: filePath }); console.log(`📸 Image saved at: ${filePath}`); @@ -49,8 +49,6 @@ export const takeSnapshot = async (page, item, imageName, type = CONSTANTS.TYPE_ } } catch (error) { console.log('Error when snapshot: ' + error.message); - } finally { - global.IS_CLEANING = true; } };