|
|
@@ -1,10 +1,13 @@
|
|
|
import { Request, Response } from 'express';
|
|
|
+import { v4 as uuidv4 } from 'uuid';
|
|
|
import { BaseEndpointComponent, EndpointMethod } from '../core/types';
|
|
|
import { logger } from '../../utils/logger';
|
|
|
import { ShopDatabase } from '../../database/Database';
|
|
|
+import { JobQueue } from '../../queue/JobQueue';
|
|
|
+import { ScraperJob } from '../../types';
|
|
|
|
|
|
export class CustomUrlsEndpoint extends BaseEndpointComponent {
|
|
|
- constructor(private db: ShopDatabase) {
|
|
|
+ constructor(private db: ShopDatabase, private jobQueue?: JobQueue) {
|
|
|
super();
|
|
|
}
|
|
|
|
|
|
@@ -16,7 +19,7 @@ export class CustomUrlsEndpoint extends BaseEndpointComponent {
|
|
|
this.addCustomUrl.bind(this),
|
|
|
{
|
|
|
summary: 'Add a custom URL to scrape for a shop',
|
|
|
- description: 'Adds a custom URL to be scraped for specific content type',
|
|
|
+ description: 'Adds a custom URL to be scraped for specific content type. The URL must be on the same domain and cannot be the root URL. Triggers immediate scraping via the job queue.',
|
|
|
tags: ['Custom URLs'],
|
|
|
parameters: [
|
|
|
{
|
|
|
@@ -63,7 +66,9 @@ export class CustomUrlsEndpoint extends BaseEndpointComponent {
|
|
|
enabled: { type: 'boolean' },
|
|
|
created_at: { type: 'string' }
|
|
|
}
|
|
|
- }
|
|
|
+ },
|
|
|
+ job_id: { type: 'string', description: 'ID of the scraping job (if queued)' },
|
|
|
+ scrape_status: { type: 'string', description: 'Status of the scraping job' }
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
@@ -284,6 +289,17 @@ export class CustomUrlsEndpoint extends BaseEndpointComponent {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ // Validate that the URL is not pointing to the root of the website
|
|
|
+ // Root URLs have no path or only "/" path
|
|
|
+ const urlPath = parsedUrl.pathname;
|
|
|
+ if (urlPath === '/' || urlPath === '') {
|
|
|
+ res.status(400).json({
|
|
|
+ error: 'URL cannot point to the root of the website. Please specify a specific page URL.',
|
|
|
+ example: `${parsedUrl.origin}/some-page`
|
|
|
+ });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
// Check if URL was already scraped
|
|
|
const alreadyScraped = this.db.isUrlAlreadyScraped(shop.id, url);
|
|
|
if (alreadyScraped) {
|
|
|
@@ -298,8 +314,25 @@ export class CustomUrlsEndpoint extends BaseEndpointComponent {
|
|
|
try {
|
|
|
const customUrl = this.db.createCustomUrl(shop.id, url, content_type);
|
|
|
|
|
|
+ // Trigger immediate scraping if job queue is available
|
|
|
+ let jobId: string | undefined;
|
|
|
+ if (this.jobQueue) {
|
|
|
+ const job: ScraperJob = {
|
|
|
+ id: uuidv4(),
|
|
|
+ sitemapUrl: shop.url, // Use shop URL for sitemap
|
|
|
+ status: 'pending',
|
|
|
+ createdAt: new Date(),
|
|
|
+ updatedAt: new Date(),
|
|
|
+ shopId: shop.id
|
|
|
+ };
|
|
|
+
|
|
|
+ this.jobQueue.addJob(job);
|
|
|
+ jobId = job.id;
|
|
|
+ logger.info(`Triggered scraping job ${job.id} for custom URL ${url} in shop ${shop.id}`);
|
|
|
+ }
|
|
|
+
|
|
|
res.status(201).json({
|
|
|
- message: 'Custom URL added successfully',
|
|
|
+ message: 'Custom URL added successfully' + (jobId ? ' and scraping started' : ''),
|
|
|
custom_url: {
|
|
|
id: customUrl.id,
|
|
|
shop_id: customUrl.shop_id,
|
|
|
@@ -307,7 +340,8 @@ export class CustomUrlsEndpoint extends BaseEndpointComponent {
|
|
|
content_type: customUrl.content_type,
|
|
|
enabled: customUrl.enabled,
|
|
|
created_at: customUrl.created_at
|
|
|
- }
|
|
|
+ },
|
|
|
+ ...(jobId && { job_id: jobId, scrape_status: 'queued' })
|
|
|
});
|
|
|
} catch (error: any) {
|
|
|
if (error.message?.includes('already been added')) {
|