Deploy to production #68
			
				
			
		
		
		
	| 
						 | 
					@ -117,7 +117,7 @@ export default function MailsConfig() {
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const handleAdd = async (mail: string) => {
 | 
					  const handleAdd = async (mail: string) => {
 | 
				
			||||||
    const newMails = [...mails, mail];
 | 
					    const newMails = [...mails, mail.trim()];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    open();
 | 
					    open();
 | 
				
			||||||
    const response = await upsertConfig({
 | 
					    const response = await upsertConfig({
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1 +1 @@
 | 
				
			||||||
{"createdAt":1748224707802}
 | 
					{"createdAt":1748825131474}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,31 @@
 | 
				
			||||||
 | 
					import { Injectable } from '@nestjs/common';
 | 
				
			||||||
 | 
					import { ConfigService } from '@nestjs/config';
 | 
				
			||||||
 | 
					import axios from 'axios';
 | 
				
			||||||
 | 
					import { BidsService } from '../services/bids.service';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Injectable()
 | 
				
			||||||
 | 
					export class HotItemApi {
 | 
				
			||||||
 | 
					  constructor(
 | 
				
			||||||
 | 
					    private readonly bidsService: BidsService,
 | 
				
			||||||
 | 
					    private readonly configService: ConfigService,
 | 
				
			||||||
 | 
					  ) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  listHotItem = async () => {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const res = await axios({
 | 
				
			||||||
 | 
					        method: 'GET',
 | 
				
			||||||
 | 
					        baseURL: this.configService.get('NEW_ITEM_BASE_URL'),
 | 
				
			||||||
 | 
					        url: '/disti/api/hotitem',
 | 
				
			||||||
 | 
					        headers: {
 | 
				
			||||||
 | 
					          //   ...axios.defaults.headers.common,
 | 
				
			||||||
 | 
					          'Content-Type': 'application/json',
 | 
				
			||||||
 | 
					          Authorization: 'Bearer ' + this.configService.get('NEW_ITEM_TOKEN'),
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return res.data.data || [];
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      return [];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -33,6 +33,7 @@ import { Config } from './entities/configs.entity';
 | 
				
			||||||
import { AdminConfigsController } from './controllers/admin/admin-configs.controller';
 | 
					import { AdminConfigsController } from './controllers/admin/admin-configs.controller';
 | 
				
			||||||
import { BidMetadatasService } from './services/bid-metadatas.service';
 | 
					import { BidMetadatasService } from './services/bid-metadatas.service';
 | 
				
			||||||
import { BidMetadata } from './entities/bid-metadata.entity';
 | 
					import { BidMetadata } from './entities/bid-metadata.entity';
 | 
				
			||||||
 | 
					import { HotItemApi } from './apis/hot-item.api';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Module({
 | 
					@Module({
 | 
				
			||||||
  imports: [
 | 
					  imports: [
 | 
				
			||||||
| 
						 | 
					@ -76,6 +77,7 @@ import { BidMetadata } from './entities/bid-metadata.entity';
 | 
				
			||||||
    TasksService,
 | 
					    TasksService,
 | 
				
			||||||
    ConfigsService,
 | 
					    ConfigsService,
 | 
				
			||||||
    BidMetadatasService,
 | 
					    BidMetadatasService,
 | 
				
			||||||
 | 
					    HotItemApi,
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
  exports: [
 | 
					  exports: [
 | 
				
			||||||
    BotTelegramApi,
 | 
					    BotTelegramApi,
 | 
				
			||||||
| 
						 | 
					@ -83,6 +85,7 @@ import { BidMetadata } from './entities/bid-metadata.entity';
 | 
				
			||||||
    BidsService,
 | 
					    BidsService,
 | 
				
			||||||
    ConfigsService,
 | 
					    ConfigsService,
 | 
				
			||||||
    DashboardService,
 | 
					    DashboardService,
 | 
				
			||||||
 | 
					    HotItemApi,
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class BidsModule {}
 | 
					export class BidsModule {}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -98,7 +98,8 @@ export class ImapService {
 | 
				
			||||||
            const result = verifyCode(emailContent);
 | 
					            const result = verifyCode(emailContent);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (!result) {
 | 
					            if (!result) {
 | 
				
			||||||
              throw new Error('fetchLatestEmail: Name or Code is empty');
 | 
					              console.log('fetchLatestEmail: Name or Code is empty');
 | 
				
			||||||
 | 
					              return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const { code, name } = result;
 | 
					            const { code, name } = result;
 | 
				
			||||||
| 
						 | 
					@ -108,7 +109,8 @@ export class ImapService {
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (!webBid) {
 | 
					            if (!webBid) {
 | 
				
			||||||
              throw new Error('Not found web bid');
 | 
					              console.log('Not found web bid');
 | 
				
			||||||
 | 
					              return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // send message event
 | 
					            // send message event
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,15 +1,17 @@
 | 
				
			||||||
import { Injectable } from '@nestjs/common';
 | 
					import { Bid } from '@/modules/bids/entities/bid.entity';
 | 
				
			||||||
import { MailerService } from '@nestjs-modules/mailer';
 | 
					 | 
				
			||||||
import { ScrapItem } from '@/modules/scraps/entities/scrap-item.entity';
 | 
					import { ScrapItem } from '@/modules/scraps/entities/scrap-item.entity';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  extractDomain,
 | 
					  extractDomain,
 | 
				
			||||||
  extractDomainSmart,
 | 
					  extractDomainSmart,
 | 
				
			||||||
  formatEndTime,
 | 
					  formatEndTime,
 | 
				
			||||||
 | 
					  isHotItemFn,
 | 
				
			||||||
  isTimeReached,
 | 
					  isTimeReached,
 | 
				
			||||||
} from '@/ultils';
 | 
					} from '@/ultils';
 | 
				
			||||||
import { Bid } from '@/modules/bids/entities/bid.entity';
 | 
					import { MailerService } from '@nestjs-modules/mailer';
 | 
				
			||||||
import { InjectQueue } from '@nestjs/bull';
 | 
					import { InjectQueue } from '@nestjs/bull';
 | 
				
			||||||
 | 
					import { Injectable } from '@nestjs/common';
 | 
				
			||||||
import { Queue } from 'bull';
 | 
					import { Queue } from 'bull';
 | 
				
			||||||
 | 
					import * as _ from 'lodash';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Injectable()
 | 
					@Injectable()
 | 
				
			||||||
export class MailsService {
 | 
					export class MailsService {
 | 
				
			||||||
| 
						 | 
					@ -52,17 +54,26 @@ export class MailsService {
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  generateProductTableHTML(products: ScrapItem[]): string {
 | 
					  generateProductTableHTML(
 | 
				
			||||||
 | 
					    products: ScrapItem[],
 | 
				
			||||||
 | 
					    hotItems: { name: string }[],
 | 
				
			||||||
 | 
					  ): {
 | 
				
			||||||
 | 
					    html: string;
 | 
				
			||||||
 | 
					    hasHotItem: boolean;
 | 
				
			||||||
 | 
					  } {
 | 
				
			||||||
    const from = process.env.MAIL_USER || 'no-reply@example.com';
 | 
					    const from = process.env.MAIL_USER || 'no-reply@example.com';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let hasHotItem = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!products.length) {
 | 
					    if (!products.length) {
 | 
				
			||||||
      return `
 | 
					      return {
 | 
				
			||||||
 | 
					        html: `
 | 
				
			||||||
    <!DOCTYPE html>
 | 
					    <!DOCTYPE html>
 | 
				
			||||||
    <html lang="en">
 | 
					    <html lang="en">
 | 
				
			||||||
    <head>
 | 
					    <head>
 | 
				
			||||||
      <meta charset="UTF-8" />
 | 
					      <meta charset="UTF-8" />
 | 
				
			||||||
      <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
 | 
					      <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
 | 
				
			||||||
      <title>Products</title>
 | 
					      <title>Product Listing</title>
 | 
				
			||||||
    </head>
 | 
					    </head>
 | 
				
			||||||
    <body style="font-family: sans-serif; background: #f8f9fa; padding: 20px;">
 | 
					    <body style="font-family: sans-serif; background: #f8f9fa; padding: 20px;">
 | 
				
			||||||
      <h2 style="text-align: center; color: #333;">Product Listing</h2>
 | 
					      <h2 style="text-align: center; color: #333;">Product Listing</h2>
 | 
				
			||||||
| 
						 | 
					@ -70,33 +81,75 @@ export class MailsService {
 | 
				
			||||||
      <p style="text-align: center; color: #999; font-size: 12px; margin-top: 40px;">From: ${from}</p>
 | 
					      <p style="text-align: center; color: #999; font-size: 12px; margin-top: 40px;">From: ${from}</p>
 | 
				
			||||||
    </body>
 | 
					    </body>
 | 
				
			||||||
    </html>
 | 
					    </html>
 | 
				
			||||||
    `;
 | 
					    `,
 | 
				
			||||||
 | 
					        hasHotItem,
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const rows = products
 | 
					    // Decorate products with isHotItem
 | 
				
			||||||
      .map(
 | 
					    const decorated = products.map((p) => {
 | 
				
			||||||
        (p) => `
 | 
					      const isHotItem = hotItems.some((obj) =>
 | 
				
			||||||
      <tr>
 | 
					        p.name.toLowerCase().includes(obj.name.toLowerCase()),
 | 
				
			||||||
        <td><img src="${p.image_url}" alt="Product Image" style="height: 40px; object-fit: contain; border-radius: 4px;" /></td>
 | 
					      );
 | 
				
			||||||
        <td>${p.name}</td>
 | 
					      if (isHotItem) hasHotItem = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return {
 | 
				
			||||||
 | 
					        ...p,
 | 
				
			||||||
 | 
					        isHotItem,
 | 
				
			||||||
 | 
					        isNew:
 | 
				
			||||||
 | 
					          new Date(p.created_at).getTime() === new Date(p.updated_at).getTime(),
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Separate hot and non-hot items, preserving original order
 | 
				
			||||||
 | 
					    const hotItemsFirst = [
 | 
				
			||||||
 | 
					      ...decorated.filter((p) => p.isHotItem),
 | 
				
			||||||
 | 
					      ...decorated.filter((p) => !p.isHotItem),
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const rows = hotItemsFirst
 | 
				
			||||||
 | 
					      .map((p) => {
 | 
				
			||||||
 | 
					        const isHotItem = isHotItemFn(p, hotItems);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (isHotItem) {
 | 
				
			||||||
 | 
					          hasHotItem = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const isNew =
 | 
				
			||||||
 | 
					          new Date(p.created_at).getTime() === new Date(p.updated_at).getTime();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const nameStyle = isNew ? 'color: #2f9e44; font-weight: bold;' : '';
 | 
				
			||||||
 | 
					        const namePrefix = isHotItem
 | 
				
			||||||
 | 
					          ? '<span style="font-weight: bold; color: #e03131;">[HOT ITEM]</span> '
 | 
				
			||||||
 | 
					          : '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return `
 | 
				
			||||||
 | 
					        <tr ${isHotItem ? 'style="background-color: #ff848496;margin-bottom: 4px;"' : 'margin-bottom: 4px;'}>
 | 
				
			||||||
 | 
					          <td style="display:flex;max-width: 60px;"><img src="${p.image_url}" alt="Product Image" style="height: 60px !important; object-fit: contain; border-radius: 4px;" /></td>
 | 
				
			||||||
 | 
					          <td style="${nameStyle}">${namePrefix}${p.name}</td>
 | 
				
			||||||
          <td style="font-weight: bold; color: #e03131;">${p.current_price ? '$' + p.current_price : 'None'}</td>
 | 
					          <td style="font-weight: bold; color: #e03131;">${p.current_price ? '$' + p.current_price : 'None'}</td>
 | 
				
			||||||
          <td><a href="${p.url}" target="_blank" style="color: #007bff;">View</a></td>
 | 
					          <td><a href="${p.url}" target="_blank" style="color: #007bff;">View</a></td>
 | 
				
			||||||
          <td>${extractDomainSmart(p.scrap_config.web_bid.origin_url)}</td>
 | 
					          <td>${extractDomainSmart(p.scrap_config.web_bid.origin_url)}</td>
 | 
				
			||||||
        </tr>
 | 
					        </tr>
 | 
				
			||||||
    `,
 | 
					      `;
 | 
				
			||||||
      )
 | 
					      })
 | 
				
			||||||
      .join('');
 | 
					      .join('');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return `
 | 
					    const title = hasHotItem
 | 
				
			||||||
 | 
					      ? '<span style="font-weight: bold; color: #e03131;">[HOT ITEMS]</span> Product Listing'
 | 
				
			||||||
 | 
					      : 'Product Listing';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      html: `
 | 
				
			||||||
  <!DOCTYPE html>
 | 
					  <!DOCTYPE html>
 | 
				
			||||||
  <html lang="en">
 | 
					  <html lang="en">
 | 
				
			||||||
  <head>
 | 
					  <head>
 | 
				
			||||||
    <meta charset="UTF-8" />
 | 
					    <meta charset="UTF-8" />
 | 
				
			||||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
 | 
					    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
 | 
				
			||||||
    <title>Products</title>
 | 
					    <title>${title}</title>
 | 
				
			||||||
  </head>
 | 
					  </head>
 | 
				
			||||||
  <body style="font-family: sans-serif; background: #f8f9fa; padding: 20px;">
 | 
					  <body style="font-family: sans-serif; background: #f8f9fa; padding: 20px;">
 | 
				
			||||||
    <h2 style="text-align: center; color: #333;">Product Listing</h2>
 | 
					    <h2 style="text-align: center; color: #333;">${title}</h2>
 | 
				
			||||||
    <div style="overflow-x: auto;">
 | 
					    <div style="overflow-x: auto;">
 | 
				
			||||||
      <table style="width: 100%; min-width: 500px; border-collapse: collapse; background: #fff; border-radius: 6px; box-shadow: 0 2px 6px rgba(0,0,0,0.05);">
 | 
					      <table style="width: 100%; min-width: 500px; border-collapse: collapse; background: #fff; border-radius: 6px; box-shadow: 0 2px 6px rgba(0,0,0,0.05);">
 | 
				
			||||||
        <thead style="background: #f1f3f5;">
 | 
					        <thead style="background: #f1f3f5;">
 | 
				
			||||||
| 
						 | 
					@ -116,7 +169,9 @@ export class MailsService {
 | 
				
			||||||
    <p style="text-align: center; color: #999; font-size: 12px; margin-top: 40px;">From: ${from}</p>
 | 
					    <p style="text-align: center; color: #999; font-size: 12px; margin-top: 40px;">From: ${from}</p>
 | 
				
			||||||
  </body>
 | 
					  </body>
 | 
				
			||||||
  </html>
 | 
					  </html>
 | 
				
			||||||
  `;
 | 
					  `,
 | 
				
			||||||
 | 
					      hasHotItem,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getAuctionStatusEmailContent(bid: Bid): string {
 | 
					  getAuctionStatusEmailContent(bid: Bid): string {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,10 +3,11 @@ import { DashboardService } from '@/modules/bids/services/dashboard.service';
 | 
				
			||||||
import { MailsService } from '@/modules/mails/services/mails.service';
 | 
					import { MailsService } from '@/modules/mails/services/mails.service';
 | 
				
			||||||
import { delay } from '@/ultils';
 | 
					import { delay } from '@/ultils';
 | 
				
			||||||
import { Injectable, Logger } from '@nestjs/common';
 | 
					import { Injectable, Logger } from '@nestjs/common';
 | 
				
			||||||
import { Cron } from '@nestjs/schedule';
 | 
					import { Cron, CronExpression } from '@nestjs/schedule';
 | 
				
			||||||
import * as moment from 'moment';
 | 
					import * as moment from 'moment';
 | 
				
			||||||
import { Between } from 'typeorm';
 | 
					import { Between } from 'typeorm';
 | 
				
			||||||
import { ScrapItemsService } from './scrap-item-config.service';
 | 
					import { ScrapItemsService } from './scrap-item-config.service';
 | 
				
			||||||
 | 
					import { HotItemApi } from '@/modules/bids/apis/hot-item.api';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Injectable()
 | 
					@Injectable()
 | 
				
			||||||
export class TasksService {
 | 
					export class TasksService {
 | 
				
			||||||
| 
						 | 
					@ -17,6 +18,7 @@ export class TasksService {
 | 
				
			||||||
    private readonly mailsService: MailsService,
 | 
					    private readonly mailsService: MailsService,
 | 
				
			||||||
    private readonly configsSerivce: ConfigsService,
 | 
					    private readonly configsSerivce: ConfigsService,
 | 
				
			||||||
    private readonly dashboardService: DashboardService,
 | 
					    private readonly dashboardService: DashboardService,
 | 
				
			||||||
 | 
					    private readonly hotItemApi: HotItemApi,
 | 
				
			||||||
  ) {}
 | 
					  ) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async runProcessAndSendReport(processName: string) {
 | 
					  async runProcessAndSendReport(processName: string) {
 | 
				
			||||||
| 
						 | 
					@ -71,13 +73,20 @@ export class TasksService {
 | 
				
			||||||
          updated_at: Between(startOfDay, endOfDay),
 | 
					          updated_at: Between(startOfDay, endOfDay),
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        relations: { scrap_config: { web_bid: true } },
 | 
					        relations: { scrap_config: { web_bid: true } },
 | 
				
			||||||
        order: { updated_at: 'DESC' },
 | 
					        order: { updated_at: 'ASC' },
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const hotItems = await this.hotItemApi.listHotItem();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const { hasHotItem, html } = this.mailsService.generateProductTableHTML(
 | 
				
			||||||
 | 
					        data,
 | 
				
			||||||
 | 
					        hotItems,
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      await this.mailsService.sendHtmlMailJob({
 | 
					      await this.mailsService.sendHtmlMailJob({
 | 
				
			||||||
        to: mails,
 | 
					        to: mails,
 | 
				
			||||||
        subject: `Auction Items Matching Your Keywords – Daily Update ${moment().format('YYYY-MM-DD HH:mm')}`,
 | 
					        subject: `${hasHotItem ? '[HOT ITEMS] ' : ''}Auction Items Matching Your Keywords – Daily Update ${moment().format('YYYY-MM-DD HH:mm')}`,
 | 
				
			||||||
        html: this.mailsService.generateProductTableHTML(data),
 | 
					        html: html,
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      console.log('Report mail sent successfully.');
 | 
					      console.log('Report mail sent successfully.');
 | 
				
			||||||
| 
						 | 
					@ -86,7 +95,8 @@ export class TasksService {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Cron('58 5 * * *')
 | 
					  // @Cron('59 5 * * *')
 | 
				
			||||||
 | 
					  // @Cron(CronExpression.EVERY_MINUTE)
 | 
				
			||||||
  async handleScraps() {
 | 
					  async handleScraps() {
 | 
				
			||||||
    const processName = 'scrape-data-keyword';
 | 
					    const processName = 'scrape-data-keyword';
 | 
				
			||||||
    await this.runProcessAndSendReport(processName);
 | 
					    await this.runProcessAndSendReport(processName);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,5 @@
 | 
				
			||||||
import { Bid } from '@/modules/bids/entities/bid.entity';
 | 
					import { Bid } from '@/modules/bids/entities/bid.entity';
 | 
				
			||||||
 | 
					import { ScrapItem } from '@/modules/scraps/entities/scrap-item.entity';
 | 
				
			||||||
import * as moment from 'moment';
 | 
					import * as moment from 'moment';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function extractModelId(url: string): string | null {
 | 
					export function extractModelId(url: string): string | null {
 | 
				
			||||||
| 
						 | 
					@ -215,3 +216,9 @@ export function formatEndTime(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const delay = (ms: number) =>
 | 
					export const delay = (ms: number) =>
 | 
				
			||||||
  new Promise((resolve) => setTimeout(resolve, ms));
 | 
					  new Promise((resolve) => setTimeout(resolve, ms));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const isHotItemFn = (item: ScrapItem, hotItems: any[]) => {
 | 
				
			||||||
 | 
					  return hotItems.some((obj) =>
 | 
				
			||||||
 | 
					    item.name.toLowerCase().includes(obj.name.toLowerCase()),
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -22,6 +22,7 @@ const init = async () => {
 | 
				
			||||||
      try {
 | 
					      try {
 | 
				
			||||||
        await model.action();
 | 
					        await model.action();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        console.log(`Loaded model [${model?.web_bid?.origin_url || ""}]`);
 | 
				
			||||||
        for (const key of Object.keys(model.results)) {
 | 
					        for (const key of Object.keys(model.results)) {
 | 
				
			||||||
          const dataArray = model.results[key];
 | 
					          const dataArray = model.results[key];
 | 
				
			||||||
          const result = await upsertScapeItems(dataArray);
 | 
					          const result = await upsertScapeItems(dataArray);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,25 +1,12 @@
 | 
				
			||||||
import axios from "axios";
 | 
					import axios from "axios";
 | 
				
			||||||
import browser from "../system/browser.js";
 | 
					import { extractModelId } from "../system/ultils.js";
 | 
				
			||||||
import { extractModelId, extractNumber } from "../system/ultils.js";
 | 
					 | 
				
			||||||
import { ScrapModel } from "./scrap-model.js";
 | 
					import { ScrapModel } from "./scrap-model.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class AllbidsScrapModel extends ScrapModel {
 | 
					export class AllbidsScrapModel extends ScrapModel {
 | 
				
			||||||
  action = async () => {
 | 
					  action = async () => {
 | 
				
			||||||
    const urlsData = this.extractUrls();
 | 
					    const urlsData = this.extractUrls();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    console.log({ urlsData });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for (let item of urlsData) {
 | 
					    for (let item of urlsData) {
 | 
				
			||||||
      // await this.page.goto(item.url);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      // const data = await this.getItemsInHtml(item);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      // const results = this.filterItemByKeyword(item.keyword, data);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      // this.results[item.keyword] = results;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      // console.log({ results: this.results });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      const { data } = await axios({
 | 
					      const { data } = await axios({
 | 
				
			||||||
        url: item.url,
 | 
					        url: item.url,
 | 
				
			||||||
        method: "POST",
 | 
					        method: "POST",
 | 
				
			||||||
| 
						 | 
					@ -37,14 +24,14 @@ export class AllbidsScrapModel extends ScrapModel {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const auctions = data?.auctions || [];
 | 
					      const auctions = data?.auctions || [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const mappedData = auctions.map((item) => {
 | 
					      const mappedData = auctions.map((auction) => {
 | 
				
			||||||
        return {
 | 
					        return {
 | 
				
			||||||
          url: item.AucDetailsUrlLink,
 | 
					          url: auction.AucDetailsUrlLink,
 | 
				
			||||||
          image_url: item.aucThumbnailUrl,
 | 
					          image_url: auction.aucThumbnailUrl,
 | 
				
			||||||
          name: item.aucTitle,
 | 
					          name: auction.aucTitle,
 | 
				
			||||||
          keyword: data.keyword,
 | 
					          keyword: item.keyword,
 | 
				
			||||||
          model: extractModelId(item.AucDetailsUrlLink),
 | 
					          model: extractModelId(auction.AucDetailsUrlLink),
 | 
				
			||||||
          current_price: item.aucCurrentBid,
 | 
					          current_price: auction.aucCurrentBid,
 | 
				
			||||||
          scrap_config_id: this.scrap_config_id,
 | 
					          scrap_config_id: this.scrap_config_id,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue