This commit is contained in:
echo committed 2025-05-06 10:01:03 +02:00
commit 0fa3c7ac48
140 files changed
+24289

No files matched your search

+9
View File
@@ -0,0 +1,9 @@
import { AppService } from './app.service';
import { ScraperService } from './scraper/scraper.service';
export declare class AppController {
private readonly appService;
private readonly scraperService;
constructor(appService: AppService, scraperService: ScraperService);
getHello(): string;
triggerScrape(sourceId: number): Promise<void>;
}
+52
View File
@@ -0,0 +1,52 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AppController = void 0;
const common_1 = require("@nestjs/common");
const app_service_1 = require("./app.service");
const scraper_service_1 = require("./scraper/scraper.service");
let AppController = class AppController {
appService;
scraperService;
constructor(appService, scraperService) {
this.appService = appService;
this.scraperService = scraperService;
}
getHello() {
return this.appService.getHello();
}
async triggerScrape(sourceId) {
return this.scraperService.manualScrape(sourceId);
}
};
exports.AppController = AppController;
__decorate([
(0, common_1.Get)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", String)
], AppController.prototype, "getHello", null);
__decorate([
(0, common_1.Get)('scrape/:sourceId'),
__param(0, (0, common_1.Param)('sourceId', common_1.ParseIntPipe)),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Number]),
__metadata("design:returntype", Promise)
], AppController.prototype, "triggerScrape", null);
exports.AppController = AppController = __decorate([
(0, common_1.Controller)(),
__metadata("design:paramtypes", [app_service_1.AppService,
scraper_service_1.ScraperService])
], AppController);
//# sourceMappingURL=app.controller.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"app.controller.js","sourceRoot":"","sources":["../src/app.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAAsE;AACtE,+CAA2C;AAC3C,+DAA2D;AAGpD,IAAM,aAAa,GAAnB,MAAM,aAAa;IAEL;IACA;IAFnB,YACmB,UAAsB,EACtB,cAA8B;QAD9B,eAAU,GAAV,UAAU,CAAY;QACtB,mBAAc,GAAd,cAAc,CAAgB;IAC9C,CAAC;IAGJ,QAAQ;QACN,OAAO,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC;IACpC,CAAC;IAGK,AAAN,KAAK,CAAC,aAAa,CAAkC,QAAgB;QACnE,OAAO,IAAI,CAAC,cAAc,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;IACpD,CAAC;CACF,CAAA;AAfY,sCAAa;AAOxB;IADC,IAAA,YAAG,GAAE;;;;6CAGL;AAGK;IADL,IAAA,YAAG,EAAC,kBAAkB,CAAC;IACH,WAAA,IAAA,cAAK,EAAC,UAAU,EAAE,qBAAY,CAAC,CAAA;;;;kDAEnD;wBAdU,aAAa;IADzB,IAAA,mBAAU,GAAE;qCAGoB,wBAAU;QACN,gCAAc;GAHtC,aAAa,CAezB"}
+2
View File
@@ -0,0 +1,2 @@
export declare class AppModule {
}
+42
View File
@@ -0,0 +1,42 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AppModule = void 0;
const common_1 = require("@nestjs/common");
const schedule_1 = require("@nestjs/schedule");
const config_1 = require("@nestjs/config");
const app_controller_1 = require("./app.controller");
const app_service_1 = require("./app.service");
const prisma_module_1 = require("./prisma/prisma.module");
const product_module_1 = require("./product/product.module");
const scraper_module_1 = require("./scraper/scraper.module");
const price_module_1 = require("./price/price.module");
const source_module_1 = require("./source/source.module");
const user_module_1 = require("./user/user.module");
let AppModule = class AppModule {
};
exports.AppModule = AppModule;
exports.AppModule = AppModule = __decorate([
(0, common_1.Module)({
imports: [
config_1.ConfigModule.forRoot({
isGlobal: true,
}),
schedule_1.ScheduleModule.forRoot(),
prisma_module_1.PrismaModule,
product_module_1.ProductModule,
scraper_module_1.ScraperModule,
price_module_1.PriceModule,
source_module_1.SourceModule,
user_module_1.UserModule,
],
controllers: [app_controller_1.AppController],
providers: [app_service_1.AppService],
})
], AppModule);
//# sourceMappingURL=app.module.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"app.module.js","sourceRoot":"","sources":["../src/app.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,+CAAkD;AAClD,2CAA8C;AAC9C,qDAAiD;AACjD,+CAA2C;AAC3C,0DAAsD;AACtD,6DAAyD;AACzD,6DAAyD;AACzD,uDAAmD;AACnD,0DAAsD;AACtD,oDAAgD;AAkBzC,IAAM,SAAS,GAAf,MAAM,SAAS;CAAG,CAAA;AAAZ,8BAAS;oBAAT,SAAS;IAhBrB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE;YACP,qBAAY,CAAC,OAAO,CAAC;gBACnB,QAAQ,EAAE,IAAI;aACf,CAAC;YACF,yBAAc,CAAC,OAAO,EAAE;YACxB,4BAAY;YACZ,8BAAa;YACb,8BAAa;YACb,0BAAW;YACX,4BAAY;YACZ,wBAAU;SACX;QACD,WAAW,EAAE,CAAC,8BAAa,CAAC;QAC5B,SAAS,EAAE,CAAC,wBAAU,CAAC;KACxB,CAAC;GACW,SAAS,CAAG"}
+3
View File
@@ -0,0 +1,3 @@
export declare class AppService {
getHello(): string;
}
+20
View File
@@ -0,0 +1,20 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AppService = void 0;
const common_1 = require("@nestjs/common");
let AppService = class AppService {
getHello() {
return 'Hello World!';
}
};
exports.AppService = AppService;
exports.AppService = AppService = __decorate([
(0, common_1.Injectable)()
], AppService);
//# sourceMappingURL=app.service.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"app.service.js","sourceRoot":"","sources":["../src/app.service.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAA4C;AAGrC,IAAM,UAAU,GAAhB,MAAM,UAAU;IACrB,QAAQ;QACN,OAAO,cAAc,CAAC;IACxB,CAAC;CACF,CAAA;AAJY,gCAAU;qBAAV,UAAU;IADtB,IAAA,mBAAU,GAAE;GACA,UAAU,CAItB"}
+1
View File
@@ -0,0 +1 @@
export {};
+37
View File
@@ -0,0 +1,37 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const client_1 = require("@prisma/client");
const prisma = new client_1.PrismaClient();
async function main() {
try {
const sourcesToUpsert = [
{
name: 'Vero',
url: 'https://pricelist.vero.com.mk/89_2.html',
logo: 'vero-logo.png',
},
{
name: 'dim',
url: 'https://dim.marketceni.mk/',
logo: 'tinex-logo.png',
},
];
const upsertedSources = await Promise.all(sourcesToUpsert.map((source) => prisma.source.upsert({
where: { name: source.name },
update: {
url: source.url,
logo: source.logo,
},
create: source,
})));
console.log('Upserted sources:', upsertedSources);
}
catch (error) {
console.error('Error:', error);
}
finally {
await prisma.$disconnect();
}
}
main();
//# sourceMappingURL=create-source.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"create-source.js","sourceRoot":"","sources":["../src/create-source.ts"],"names":[],"mappings":";;AAAA,2CAA8C;AAE9C,MAAM,MAAM,GAAG,IAAI,qBAAY,EAAE,CAAC;AAElC,KAAK,UAAU,IAAI;IACjB,IAAI,CAAC;QAEH,MAAM,eAAe,GAAG;YACtB;gBACE,IAAI,EAAE,MAAM;gBACZ,GAAG,EAAE,yCAAyC;gBAC9C,IAAI,EAAE,eAAe;aACtB;YACD;gBACE,IAAI,EAAE,KAAK;gBACX,GAAG,EAAE,4BAA4B;gBACjC,IAAI,EAAE,gBAAgB;aACvB;SAGF,CAAC;QAGF,MAAM,eAAe,GAAG,MAAM,OAAO,CAAC,GAAG,CACvC,eAAe,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAC7B,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;YACnB,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE;YAC5B,MAAM,EAAE;gBACN,GAAG,EAAE,MAAM,CAAC,GAAG;gBACf,IAAI,EAAE,MAAM,CAAC,IAAI;aAClB;YACD,MAAM,EAAE,MAAM;SACf,CAAC,CACH,CACF,CAAC;QAEF,OAAO,CAAC,GAAG,CAAC,mBAAmB,EAAE,eAAe,CAAC,CAAC;IACpD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IACjC,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,CAAC,WAAW,EAAE,CAAC;IAC7B,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC"}
+1
View File
@@ -0,0 +1 @@
export {};
+15
View File
@@ -0,0 +1,15 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const core_1 = require("@nestjs/core");
const app_module_1 = require("./app.module");
async function bootstrap() {
const app = await core_1.NestFactory.create(app_module_1.AppModule);
app.enableCors({
origin: true,
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
credentials: true,
});
await app.listen(3001);
}
bootstrap();
//# sourceMappingURL=main.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";;AAAA,uCAA2C;AAC3C,6CAAyC;AAEzC,KAAK,UAAU,SAAS;IACtB,MAAM,GAAG,GAAG,MAAM,kBAAW,CAAC,MAAM,CAAC,sBAAS,CAAC,CAAC;IAGhD,GAAG,CAAC,UAAU,CAAC;QACb,MAAM,EAAE,IAAI;QACZ,OAAO,EAAE,gCAAgC;QACzC,WAAW,EAAE,IAAI;KAClB,CAAC,CAAC;IAEH,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;AACzB,CAAC;AACD,SAAS,EAAE,CAAC"}
+75
View File
@@ -0,0 +1,75 @@
import { PriceService } from './price.service';
export declare class PriceController {
private readonly priceService;
constructor(priceService: PriceService);
getPricesForProduct(id: string): Promise<({
source: {
id: number;
name: string;
url: string;
logo: string | null;
lastScraped: Date | null;
createdAt: Date;
updatedAt: Date;
};
} & {
id: number;
createdAt: Date;
sourceId: number;
regularPrice: number;
discountedPrice: number | null;
discountPercentage: number | null;
unitPrice: string | null;
promotionType: string | null;
promotionStart: Date | null;
promotionEnd: Date | null;
vatIncluded: boolean;
lastUpdated: Date;
productId: number;
})[]>;
comparePrices(ids: string): Promise<{
id: number;
name: string;
description: string | null;
category: string;
availability: boolean;
latestPrice: {
source: {
id: number;
name: string;
url: string;
logo: string | null;
lastScraped: Date | null;
createdAt: Date;
updatedAt: Date;
};
} & {
id: number;
createdAt: Date;
sourceId: number;
regularPrice: number;
discountedPrice: number | null;
discountPercentage: number | null;
unitPrice: string | null;
promotionType: string | null;
promotionStart: Date | null;
promotionEnd: Date | null;
vatIncluded: boolean;
lastUpdated: Date;
productId: number;
};
}[]>;
getPriceHistory(id: string, days?: string): Promise<{
productId: number;
history: unknown[];
priceRange: {
min: number;
max: number;
};
averagePrice: number;
discountStats: {
maxDiscount: number;
averageDiscount: number;
};
}>;
}
+61
View File
@@ -0,0 +1,61 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PriceController = void 0;
const common_1 = require("@nestjs/common");
const price_service_1 = require("./price.service");
let PriceController = class PriceController {
priceService;
constructor(priceService) {
this.priceService = priceService;
}
async getPricesForProduct(id) {
return this.priceService.getPricesForProduct(Number(id));
}
async comparePrices(ids) {
const productIds = ids.split(',').map(Number);
return this.priceService.comparePrices(productIds);
}
async getPriceHistory(id, days) {
return this.priceService.getPriceHistory(Number(id), days ? Number(days) : 30);
}
};
exports.PriceController = PriceController;
__decorate([
(0, common_1.Get)('product/:id'),
__param(0, (0, common_1.Param)('id')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String]),
__metadata("design:returntype", Promise)
], PriceController.prototype, "getPricesForProduct", null);
__decorate([
(0, common_1.Get)('compare/:ids'),
__param(0, (0, common_1.Param)('ids')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String]),
__metadata("design:returntype", Promise)
], PriceController.prototype, "comparePrices", null);
__decorate([
(0, common_1.Get)('history/:id'),
__param(0, (0, common_1.Param)('id')),
__param(1, (0, common_1.Query)('days')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, String]),
__metadata("design:returntype", Promise)
], PriceController.prototype, "getPriceHistory", null);
exports.PriceController = PriceController = __decorate([
(0, common_1.Controller)('prices'),
__metadata("design:paramtypes", [price_service_1.PriceService])
], PriceController);
//# sourceMappingURL=price.controller.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"price.controller.js","sourceRoot":"","sources":["../../src/price/price.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAA+D;AAC/D,mDAA+C;AAGxC,IAAM,eAAe,GAArB,MAAM,eAAe;IACG;IAA7B,YAA6B,YAA0B;QAA1B,iBAAY,GAAZ,YAAY,CAAc;IAAG,CAAC;IAGrD,AAAN,KAAK,CAAC,mBAAmB,CAAc,EAAU;QAC/C,OAAO,IAAI,CAAC,YAAY,CAAC,mBAAmB,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;IAC3D,CAAC;IAGK,AAAN,KAAK,CAAC,aAAa,CAAe,GAAW;QAC3C,MAAM,UAAU,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC9C,OAAO,IAAI,CAAC,YAAY,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;IACrD,CAAC;IAGK,AAAN,KAAK,CAAC,eAAe,CACN,EAAU,EACR,IAAa;QAE5B,OAAO,IAAI,CAAC,YAAY,CAAC,eAAe,CACtC,MAAM,CAAC,EAAE,CAAC,EACV,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CACzB,CAAC;IACJ,CAAC;CACF,CAAA;AAxBY,0CAAe;AAIpB;IADL,IAAA,YAAG,EAAC,aAAa,CAAC;IACQ,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;;;;0DAErC;AAGK;IADL,IAAA,YAAG,EAAC,cAAc,CAAC;IACC,WAAA,IAAA,cAAK,EAAC,KAAK,CAAC,CAAA;;;;oDAGhC;AAGK;IADL,IAAA,YAAG,EAAC,aAAa,CAAC;IAEhB,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;IACX,WAAA,IAAA,cAAK,EAAC,MAAM,CAAC,CAAA;;;;sDAMf;0BAvBU,eAAe;IAD3B,IAAA,mBAAU,EAAC,QAAQ,CAAC;qCAEwB,4BAAY;GAD5C,eAAe,CAwB3B"}
+2
View File
@@ -0,0 +1,2 @@
export declare class PriceModule {
}
+25
View File
@@ -0,0 +1,25 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PriceModule = void 0;
const common_1 = require("@nestjs/common");
const price_controller_1 = require("./price.controller");
const price_service_1 = require("./price.service");
const prisma_module_1 = require("../prisma/prisma.module");
let PriceModule = class PriceModule {
};
exports.PriceModule = PriceModule;
exports.PriceModule = PriceModule = __decorate([
(0, common_1.Module)({
imports: [prisma_module_1.PrismaModule],
controllers: [price_controller_1.PriceController],
providers: [price_service_1.PriceService],
exports: [price_service_1.PriceService],
})
], PriceModule);
//# sourceMappingURL=price.module.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"price.module.js","sourceRoot":"","sources":["../../src/price/price.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,yDAAqD;AACrD,mDAA+C;AAC/C,2DAAuD;AAQhD,IAAM,WAAW,GAAjB,MAAM,WAAW;CAAG,CAAA;AAAd,kCAAW;sBAAX,WAAW;IANvB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE,CAAC,4BAAY,CAAC;QACvB,WAAW,EAAE,CAAC,kCAAe,CAAC;QAC9B,SAAS,EAAE,CAAC,4BAAY,CAAC;QACzB,OAAO,EAAE,CAAC,4BAAY,CAAC;KACxB,CAAC;GACW,WAAW,CAAG"}
+75
View File
@@ -0,0 +1,75 @@
import { PrismaService } from '../prisma/prisma.service';
export declare class PriceService {
private prisma;
constructor(prisma: PrismaService);
getPricesForProduct(productId: number): Promise<({
source: {
id: number;
name: string;
url: string;
logo: string | null;
lastScraped: Date | null;
createdAt: Date;
updatedAt: Date;
};
} & {
id: number;
createdAt: Date;
sourceId: number;
regularPrice: number;
discountedPrice: number | null;
discountPercentage: number | null;
unitPrice: string | null;
promotionType: string | null;
promotionStart: Date | null;
promotionEnd: Date | null;
vatIncluded: boolean;
lastUpdated: Date;
productId: number;
})[]>;
comparePrices(productIds: number[]): Promise<{
id: number;
name: string;
description: string | null;
category: string;
availability: boolean;
latestPrice: {
source: {
id: number;
name: string;
url: string;
logo: string | null;
lastScraped: Date | null;
createdAt: Date;
updatedAt: Date;
};
} & {
id: number;
createdAt: Date;
sourceId: number;
regularPrice: number;
discountedPrice: number | null;
discountPercentage: number | null;
unitPrice: string | null;
promotionType: string | null;
promotionStart: Date | null;
promotionEnd: Date | null;
vatIncluded: boolean;
lastUpdated: Date;
productId: number;
};
}[]>;
getPriceHistory(productId: number, days?: number): Promise<{
productId: number;
history: unknown[];
priceRange: {
min: number;
max: number;
};
averagePrice: number;
discountStats: {
maxDiscount: number;
averageDiscount: number;
};
}>;
}
+92
View File
@@ -0,0 +1,92 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PriceService = void 0;
const common_1 = require("@nestjs/common");
const prisma_service_1 = require("../prisma/prisma.service");
let PriceService = class PriceService {
prisma;
constructor(prisma) {
this.prisma = prisma;
}
async getPricesForProduct(productId) {
return this.prisma.price.findMany({
where: { productId },
include: { source: true },
orderBy: { lastUpdated: 'desc' },
});
}
async comparePrices(productIds) {
const products = await this.prisma.product.findMany({
where: { id: { in: productIds } },
include: {
prices: {
include: { source: true },
orderBy: { lastUpdated: 'desc' },
take: 1,
},
},
});
return products.map((product) => ({
id: product.id,
name: product.name,
description: product.description,
category: product.category,
availability: product.availability,
latestPrice: product.prices[0] || null,
}));
}
async getPriceHistory(productId, days = 30) {
const startDate = new Date();
startDate.setDate(startDate.getDate() - days);
const prices = await this.prisma.price.findMany({
where: {
productId,
lastUpdated: { gte: startDate },
},
include: { source: true },
orderBy: { lastUpdated: 'asc' },
});
const priceHistory = prices.reduce((acc, price) => {
const date = price.lastUpdated.toISOString().split('T')[0];
if (!acc[date]) {
acc[date] = {
date,
regularPrice: price.regularPrice,
discountedPrice: price.discountedPrice,
source: price.source.name,
};
}
return acc;
}, {});
return {
productId,
history: Object.values(priceHistory),
priceRange: {
min: Math.min(...prices.map(p => p.discountedPrice || p.regularPrice)),
max: Math.max(...prices.map(p => p.regularPrice)),
},
averagePrice: prices.reduce((sum, p) => sum + p.regularPrice, 0) / prices.length,
discountStats: {
maxDiscount: Math.max(...prices.map(p => p.discountPercentage || 0)),
averageDiscount: prices.filter(p => p.discountPercentage)
.reduce((sum, p) => sum + (p.discountPercentage || 0), 0) /
prices.filter((p) => p.discountPercentage).length || 0,
},
};
}
};
exports.PriceService = PriceService;
exports.PriceService = PriceService = __decorate([
(0, common_1.Injectable)(),
__metadata("design:paramtypes", [prisma_service_1.PrismaService])
], PriceService);
//# sourceMappingURL=price.service.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"price.service.js","sourceRoot":"","sources":["../../src/price/price.service.ts"],"names":[],"mappings":"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"}
+2
View File
@@ -0,0 +1,2 @@
export declare class PrismaModule {
}
+21
View File
@@ -0,0 +1,21 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PrismaModule = void 0;
const common_1 = require("@nestjs/common");
const prisma_service_1 = require("./prisma.service");
let PrismaModule = class PrismaModule {
};
exports.PrismaModule = PrismaModule;
exports.PrismaModule = PrismaModule = __decorate([
(0, common_1.Module)({
providers: [prisma_service_1.PrismaService],
exports: [prisma_service_1.PrismaService],
})
], PrismaModule);
//# sourceMappingURL=prisma.module.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"prisma.module.js","sourceRoot":"","sources":["../../src/prisma/prisma.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,qDAAiD;AAM1C,IAAM,YAAY,GAAlB,MAAM,YAAY;CAAG,CAAA;AAAf,oCAAY;uBAAZ,YAAY;IAJxB,IAAA,eAAM,EAAC;QACN,SAAS,EAAE,CAAC,8BAAa,CAAC;QAC1B,OAAO,EAAE,CAAC,8BAAa,CAAC;KACzB,CAAC;GACW,YAAY,CAAG"}
+6
View File
@@ -0,0 +1,6 @@
import { OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
export declare class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
onModuleInit(): Promise<void>;
onModuleDestroy(): Promise<void>;
}
+24
View File
@@ -0,0 +1,24 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PrismaService = void 0;
const common_1 = require("@nestjs/common");
const client_1 = require("@prisma/client");
let PrismaService = class PrismaService extends client_1.PrismaClient {
async onModuleInit() {
await this.$connect();
}
async onModuleDestroy() {
await this.$disconnect();
}
};
exports.PrismaService = PrismaService;
exports.PrismaService = PrismaService = __decorate([
(0, common_1.Injectable)()
], PrismaService);
//# sourceMappingURL=prisma.service.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"prisma.service.js","sourceRoot":"","sources":["../../src/prisma/prisma.service.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAA2E;AAC3E,2CAA8C;AAGvC,IAAM,aAAa,GAAnB,MAAM,aAAc,SAAQ,qBAAY;IAC7C,KAAK,CAAC,YAAY;QAChB,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;IACxB,CAAC;IAED,KAAK,CAAC,eAAe;QACnB,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;IAC3B,CAAC;CACF,CAAA;AARY,sCAAa;wBAAb,aAAa;IADzB,IAAA,mBAAU,GAAE;GACA,aAAa,CAQzB"}
+154
View File
@@ -0,0 +1,154 @@
import { ProductService } from './product.service';
import { Prisma } from '@prisma/client';
export declare class ProductController {
private readonly productService;
constructor(productService: ProductService);
getProduct(id: string): Promise<({
prices: ({
source: {
id: number;
name: string;
url: string;
logo: string | null;
lastScraped: Date | null;
createdAt: Date;
updatedAt: Date;
};
} & {
id: number;
createdAt: Date;
sourceId: number;
regularPrice: number;
discountedPrice: number | null;
discountPercentage: number | null;
unitPrice: string | null;
promotionType: string | null;
promotionStart: Date | null;
promotionEnd: Date | null;
vatIncluded: boolean;
lastUpdated: Date;
productId: number;
})[];
} & {
id: number;
name: string;
createdAt: Date;
updatedAt: Date;
description: string | null;
category: string;
availability: boolean;
sourceProductId: string | null;
sourceId: number;
}) | null>;
getProducts(skip?: string, take?: string, search?: string, maxPrice?: string): Promise<({
prices: ({
source: {
id: number;
name: string;
url: string;
logo: string | null;
lastScraped: Date | null;
createdAt: Date;
updatedAt: Date;
};
} & {
id: number;
createdAt: Date;
sourceId: number;
regularPrice: number;
discountedPrice: number | null;
discountPercentage: number | null;
unitPrice: string | null;
promotionType: string | null;
promotionStart: Date | null;
promotionEnd: Date | null;
vatIncluded: boolean;
lastUpdated: Date;
productId: number;
})[];
} & {
id: number;
name: string;
createdAt: Date;
updatedAt: Date;
description: string | null;
category: string;
availability: boolean;
sourceProductId: string | null;
sourceId: number;
})[]>;
createProduct(data: Prisma.ProductCreateInput): Promise<{
prices: ({
source: {
id: number;
name: string;
url: string;
logo: string | null;
lastScraped: Date | null;
createdAt: Date;
updatedAt: Date;
};
} & {
id: number;
createdAt: Date;
sourceId: number;
regularPrice: number;
discountedPrice: number | null;
discountPercentage: number | null;
unitPrice: string | null;
promotionType: string | null;
promotionStart: Date | null;
promotionEnd: Date | null;
vatIncluded: boolean;
lastUpdated: Date;
productId: number;
})[];
} & {
id: number;
name: string;
createdAt: Date;
updatedAt: Date;
description: string | null;
category: string;
availability: boolean;
sourceProductId: string | null;
sourceId: number;
}>;
updateProduct(id: string, data: Prisma.ProductUpdateInput): Promise<{
prices: ({
source: {
id: number;
name: string;
url: string;
logo: string | null;
lastScraped: Date | null;
createdAt: Date;
updatedAt: Date;
};
} & {
id: number;
createdAt: Date;
sourceId: number;
regularPrice: number;
discountedPrice: number | null;
discountPercentage: number | null;
unitPrice: string | null;
promotionType: string | null;
promotionStart: Date | null;
promotionEnd: Date | null;
vatIncluded: boolean;
lastUpdated: Date;
productId: number;
})[];
} & {
id: number;
name: string;
createdAt: Date;
updatedAt: Date;
description: string | null;
category: string;
availability: boolean;
sourceProductId: string | null;
sourceId: number;
}>;
}
+94
View File
@@ -0,0 +1,94 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProductController = void 0;
const common_1 = require("@nestjs/common");
const product_service_1 = require("./product.service");
const client_1 = require("@prisma/client");
let ProductController = class ProductController {
productService;
constructor(productService) {
this.productService = productService;
}
async getProduct(id) {
return this.productService.getProduct(Number(id));
}
async getProducts(skip, take, search, maxPrice) {
if (search) {
return this.productService.searchProducts(search);
}
return this.productService.getProducts({
skip: skip ? Number(skip) : undefined,
take: take ? Number(take) : undefined,
where: maxPrice
? {
prices: {
some: {
regularPrice: {
lte: Number(maxPrice),
},
},
},
}
: undefined,
});
}
async createProduct(data) {
return this.productService.createProduct(data);
}
async updateProduct(id, data) {
return this.productService.updateProduct({
where: { id: Number(id) },
data,
});
}
};
exports.ProductController = ProductController;
__decorate([
(0, common_1.Get)(':id'),
__param(0, (0, common_1.Param)('id')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String]),
__metadata("design:returntype", Promise)
], ProductController.prototype, "getProduct", null);
__decorate([
(0, common_1.Get)(),
__param(0, (0, common_1.Query)('skip')),
__param(1, (0, common_1.Query)('take')),
__param(2, (0, common_1.Query)('search')),
__param(3, (0, common_1.Query)('maxPrice')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, String, String, String]),
__metadata("design:returntype", Promise)
], ProductController.prototype, "getProducts", null);
__decorate([
(0, common_1.Post)(),
__param(0, (0, common_1.Body)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", Promise)
], ProductController.prototype, "createProduct", null);
__decorate([
(0, common_1.Put)(':id'),
__param(0, (0, common_1.Param)('id')),
__param(1, (0, common_1.Body)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, Object]),
__metadata("design:returntype", Promise)
], ProductController.prototype, "updateProduct", null);
exports.ProductController = ProductController = __decorate([
(0, common_1.Controller)('products'),
__metadata("design:paramtypes", [product_service_1.ProductService])
], ProductController);
//# sourceMappingURL=product.controller.js.map
@@ -0,0 +1 @@
{"version":3,"file":"product.controller.js","sourceRoot":"","sources":["../../src/product/product.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAAgF;AAChF,uDAAmD;AACnD,2CAAwC;AAGjC,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IACC;IAA7B,YAA6B,cAA8B;QAA9B,mBAAc,GAAd,cAAc,CAAgB;IAAG,CAAC;IAGzD,AAAN,KAAK,CAAC,UAAU,CAAc,EAAU;QACtC,OAAO,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;IACpD,CAAC;IAGK,AAAN,KAAK,CAAC,WAAW,CACA,IAAa,EACb,IAAa,EACX,MAAe,EACb,QAAiB;QAEpC,IAAI,MAAM,EAAE,CAAC;YACX,OAAO,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;QACpD,CAAC;QAED,OAAO,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC;YACrC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;YACrC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;YACrC,KAAK,EAAE,QAAQ;gBACb,CAAC,CAAC;oBACE,MAAM,EAAE;wBACN,IAAI,EAAE;4BACJ,YAAY,EAAE;gCACZ,GAAG,EAAE,MAAM,CAAC,QAAQ,CAAC;6BACtB;yBACF;qBACF;iBACF;gBACH,CAAC,CAAC,SAAS;SACd,CAAC,CAAC;IACL,CAAC;IAGK,AAAN,KAAK,CAAC,aAAa,CAAS,IAA+B;QACzD,OAAO,IAAI,CAAC,cAAc,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;IACjD,CAAC;IAGK,AAAN,KAAK,CAAC,aAAa,CACJ,EAAU,EACf,IAA+B;QAEvC,OAAO,IAAI,CAAC,cAAc,CAAC,aAAa,CAAC;YACvC,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,EAAE;YACzB,IAAI;SACL,CAAC,CAAC;IACL,CAAC;CACF,CAAA;AAnDY,8CAAiB;AAItB;IADL,IAAA,YAAG,EAAC,KAAK,CAAC;IACO,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;;;;mDAE5B;AAGK;IADL,IAAA,YAAG,GAAE;IAEH,WAAA,IAAA,cAAK,EAAC,MAAM,CAAC,CAAA;IACb,WAAA,IAAA,cAAK,EAAC,MAAM,CAAC,CAAA;IACb,WAAA,IAAA,cAAK,EAAC,QAAQ,CAAC,CAAA;IACf,WAAA,IAAA,cAAK,EAAC,UAAU,CAAC,CAAA;;;;oDAqBnB;AAGK;IADL,IAAA,aAAI,GAAE;IACc,WAAA,IAAA,aAAI,GAAE,CAAA;;;;sDAE1B;AAGK;IADL,IAAA,YAAG,EAAC,KAAK,CAAC;IAER,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;IACX,WAAA,IAAA,aAAI,GAAE,CAAA;;;;sDAMR;4BAlDU,iBAAiB;IAD7B,IAAA,mBAAU,EAAC,UAAU,CAAC;qCAEwB,gCAAc;GADhD,iBAAiB,CAmD7B"}
+2
View File
@@ -0,0 +1,2 @@
export declare class ProductModule {
}
+24
View File
@@ -0,0 +1,24 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProductModule = void 0;
const common_1 = require("@nestjs/common");
const prisma_module_1 = require("../prisma/prisma.module");
const product_controller_1 = require("./product.controller");
const product_service_1 = require("./product.service");
let ProductModule = class ProductModule {
};
exports.ProductModule = ProductModule;
exports.ProductModule = ProductModule = __decorate([
(0, common_1.Module)({
imports: [prisma_module_1.PrismaModule],
controllers: [product_controller_1.ProductController],
providers: [product_service_1.ProductService],
})
], ProductModule);
//# sourceMappingURL=product.module.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"product.module.js","sourceRoot":"","sources":["../../src/product/product.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,2DAAuD;AACvD,6DAAyD;AACzD,uDAAmD;AAO5C,IAAM,aAAa,GAAnB,MAAM,aAAa;CAAG,CAAA;AAAhB,sCAAa;wBAAb,aAAa;IALzB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE,CAAC,4BAAY,CAAC;QACvB,WAAW,EAAE,CAAC,sCAAiB,CAAC;QAChC,SAAS,EAAE,CAAC,gCAAc,CAAC;KAC5B,CAAC;GACW,aAAa,CAAG"}
+200
View File
@@ -0,0 +1,200 @@
import { PrismaService } from '../prisma/prisma.service';
import { Prisma } from '@prisma/client';
export declare class ProductService {
private prisma;
constructor(prisma: PrismaService);
getProduct(id: number): Promise<({
prices: ({
source: {
id: number;
name: string;
url: string;
logo: string | null;
lastScraped: Date | null;
createdAt: Date;
updatedAt: Date;
};
} & {
id: number;
createdAt: Date;
sourceId: number;
regularPrice: number;
discountedPrice: number | null;
discountPercentage: number | null;
unitPrice: string | null;
promotionType: string | null;
promotionStart: Date | null;
promotionEnd: Date | null;
vatIncluded: boolean;
lastUpdated: Date;
productId: number;
})[];
} & {
id: number;
name: string;
createdAt: Date;
updatedAt: Date;
description: string | null;
category: string;
availability: boolean;
sourceProductId: string | null;
sourceId: number;
}) | null>;
getProducts(params: {
skip?: number;
take?: number;
cursor?: Prisma.ProductWhereUniqueInput;
where?: Prisma.ProductWhereInput;
orderBy?: Prisma.ProductOrderByWithRelationInput;
}): Promise<({
prices: ({
source: {
id: number;
name: string;
url: string;
logo: string | null;
lastScraped: Date | null;
createdAt: Date;
updatedAt: Date;
};
} & {
id: number;
createdAt: Date;
sourceId: number;
regularPrice: number;
discountedPrice: number | null;
discountPercentage: number | null;
unitPrice: string | null;
promotionType: string | null;
promotionStart: Date | null;
promotionEnd: Date | null;
vatIncluded: boolean;
lastUpdated: Date;
productId: number;
})[];
} & {
id: number;
name: string;
createdAt: Date;
updatedAt: Date;
description: string | null;
category: string;
availability: boolean;
sourceProductId: string | null;
sourceId: number;
})[]>;
searchProducts(query: string): Promise<({
prices: ({
source: {
id: number;
name: string;
url: string;
logo: string | null;
lastScraped: Date | null;
createdAt: Date;
updatedAt: Date;
};
} & {
id: number;
createdAt: Date;
sourceId: number;
regularPrice: number;
discountedPrice: number | null;
discountPercentage: number | null;
unitPrice: string | null;
promotionType: string | null;
promotionStart: Date | null;
promotionEnd: Date | null;
vatIncluded: boolean;
lastUpdated: Date;
productId: number;
})[];
} & {
id: number;
name: string;
createdAt: Date;
updatedAt: Date;
description: string | null;
category: string;
availability: boolean;
sourceProductId: string | null;
sourceId: number;
})[]>;
createProduct(data: Prisma.ProductCreateInput): Promise<{
prices: ({
source: {
id: number;
name: string;
url: string;
logo: string | null;
lastScraped: Date | null;
createdAt: Date;
updatedAt: Date;
};
} & {
id: number;
createdAt: Date;
sourceId: number;
regularPrice: number;
discountedPrice: number | null;
discountPercentage: number | null;
unitPrice: string | null;
promotionType: string | null;
promotionStart: Date | null;
promotionEnd: Date | null;
vatIncluded: boolean;
lastUpdated: Date;
productId: number;
})[];
} & {
id: number;
name: string;
createdAt: Date;
updatedAt: Date;
description: string | null;
category: string;
availability: boolean;
sourceProductId: string | null;
sourceId: number;
}>;
updateProduct(params: {
where: Prisma.ProductWhereUniqueInput;
data: Prisma.ProductUpdateInput;
}): Promise<{
prices: ({
source: {
id: number;
name: string;
url: string;
logo: string | null;
lastScraped: Date | null;
createdAt: Date;
updatedAt: Date;
};
} & {
id: number;
createdAt: Date;
sourceId: number;
regularPrice: number;
discountedPrice: number | null;
discountPercentage: number | null;
unitPrice: string | null;
promotionType: string | null;
promotionStart: Date | null;
promotionEnd: Date | null;
vatIncluded: boolean;
lastUpdated: Date;
productId: number;
})[];
} & {
id: number;
name: string;
createdAt: Date;
updatedAt: Date;
description: string | null;
category: string;
availability: boolean;
sourceProductId: string | null;
sourceId: number;
}>;
}
+94
View File
@@ -0,0 +1,94 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProductService = void 0;
const common_1 = require("@nestjs/common");
const prisma_service_1 = require("../prisma/prisma.service");
let ProductService = class ProductService {
prisma;
constructor(prisma) {
this.prisma = prisma;
}
async getProduct(id) {
return this.prisma.product.findUnique({
where: { id },
include: {
prices: {
include: { source: true },
orderBy: { lastUpdated: 'desc' }
}
}
});
}
async getProducts(params) {
const { skip, take, cursor, where, orderBy } = params;
return this.prisma.product.findMany({
skip,
take,
cursor,
where,
orderBy,
include: {
prices: {
include: { source: true },
orderBy: { lastUpdated: 'desc' },
take: 1
}
}
});
}
async searchProducts(query) {
return this.prisma.product.findMany({
where: {
OR: [
{ name: { contains: query, mode: 'insensitive' } },
{ description: { contains: query, mode: 'insensitive' } },
{ category: { contains: query, mode: 'insensitive' } }
]
},
include: {
prices: {
include: { source: true },
orderBy: { lastUpdated: 'desc' },
take: 1
}
}
});
}
async createProduct(data) {
return this.prisma.product.create({
data,
include: {
prices: {
include: { source: true }
}
}
});
}
async updateProduct(params) {
const { where, data } = params;
return this.prisma.product.update({
data,
where,
include: {
prices: {
include: { source: true }
}
}
});
}
};
exports.ProductService = ProductService;
exports.ProductService = ProductService = __decorate([
(0, common_1.Injectable)(),
__metadata("design:paramtypes", [prisma_service_1.PrismaService])
], ProductService);
//# sourceMappingURL=product.service.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"product.service.js","sourceRoot":"","sources":["../../src/product/product.service.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,6DAAyD;AAIlD,IAAM,cAAc,GAApB,MAAM,cAAc;IACL;IAApB,YAAoB,MAAqB;QAArB,WAAM,GAAN,MAAM,CAAe;IAAG,CAAC;IAE7C,KAAK,CAAC,UAAU,CAAC,EAAU;QACzB,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC;YACpC,KAAK,EAAE,EAAE,EAAE,EAAE;YACb,OAAO,EAAE;gBACP,MAAM,EAAE;oBACN,OAAO,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE;oBACzB,OAAO,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE;iBACjC;aACF;SACF,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,MAMjB;QACC,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC;QACtD,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC;YAClC,IAAI;YACJ,IAAI;YACJ,MAAM;YACN,KAAK;YACL,OAAO;YACP,OAAO,EAAE;gBACP,MAAM,EAAE;oBACN,OAAO,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE;oBACzB,OAAO,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE;oBAChC,IAAI,EAAE,CAAC;iBACR;aACF;SACF,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,KAAa;QAChC,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC;YAClC,KAAK,EAAE;gBACL,EAAE,EAAE;oBACF,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE,EAAE;oBAClD,EAAE,WAAW,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE,EAAE;oBACzD,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE,EAAE;iBACvD;aACF;YACD,OAAO,EAAE;gBACP,MAAM,EAAE;oBACN,OAAO,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE;oBACzB,OAAO,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE;oBAChC,IAAI,EAAE,CAAC;iBACR;aACF;SACF,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,IAA+B;QACjD,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;YAChC,IAAI;YACJ,OAAO,EAAE;gBACP,MAAM,EAAE;oBACN,OAAO,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE;iBAC1B;aACF;SACF,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,MAGnB;QACC,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC;QAC/B,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;YAChC,IAAI;YACJ,KAAK;YACL,OAAO,EAAE;gBACP,MAAM,EAAE;oBACN,OAAO,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE;iBAC1B;aACF;SACF,CAAC,CAAC;IACL,CAAC;CACF,CAAA;AApFY,wCAAc;yBAAd,cAAc;IAD1B,IAAA,mBAAU,GAAE;qCAEiB,8BAAa;GAD9B,cAAc,CAoF1B"}
+2
View File
@@ -0,0 +1,2 @@
export declare class ScraperModule {
}
+25
View File
@@ -0,0 +1,25 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ScraperModule = void 0;
const common_1 = require("@nestjs/common");
const axios_1 = require("@nestjs/axios");
const schedule_1 = require("@nestjs/schedule");
const scraper_service_1 = require("./scraper.service");
const prisma_module_1 = require("../prisma/prisma.module");
let ScraperModule = class ScraperModule {
};
exports.ScraperModule = ScraperModule;
exports.ScraperModule = ScraperModule = __decorate([
(0, common_1.Module)({
imports: [prisma_module_1.PrismaModule, axios_1.HttpModule, schedule_1.ScheduleModule.forRoot()],
providers: [scraper_service_1.ScraperService],
exports: [scraper_service_1.ScraperService],
})
], ScraperModule);
//# sourceMappingURL=scraper.module.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"scraper.module.js","sourceRoot":"","sources":["../../src/scraper/scraper.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,yCAA2C;AAC3C,+CAAkD;AAClD,uDAAmD;AACnD,2DAAuD;AAOhD,IAAM,aAAa,GAAnB,MAAM,aAAa;CAAG,CAAA;AAAhB,sCAAa;wBAAb,aAAa;IALzB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE,CAAC,4BAAY,EAAE,kBAAU,EAAE,yBAAc,CAAC,OAAO,EAAE,CAAC;QAC7D,SAAS,EAAE,CAAC,gCAAc,CAAC;QAC3B,OAAO,EAAE,CAAC,gCAAc,CAAC;KAC1B,CAAC;GACW,aAAa,CAAG"}
+17
View File
@@ -0,0 +1,17 @@
import { PrismaService } from '../prisma/prisma.service';
import { ConfigService } from '@nestjs/config';
export declare class ScraperService {
private prisma;
private config;
constructor(prisma: PrismaService, config: ConfigService);
private readonly logger;
private parsePrice;
private parseDate;
private parseDateRange;
private getTableSelector;
private calculateDiscountPercentage;
private parseProductRow;
scrapeAllSources(): Promise<void>;
scrapeProducts(sourceUrl: string, sourceId: number): Promise<void>;
manualScrape(sourceId: number): Promise<void>;
}
+245
View File
@@ -0,0 +1,245 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var ScraperService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ScraperService = void 0;
const common_1 = require("@nestjs/common");
const prisma_service_1 = require("../prisma/prisma.service");
const schedule_1 = require("@nestjs/schedule");
const cheerio = require("cheerio");
const axios_1 = require("axios");
const config_1 = require("@nestjs/config");
let ScraperService = ScraperService_1 = class ScraperService {
prisma;
config;
constructor(prisma, config) {
this.prisma = prisma;
this.config = config;
}
logger = new common_1.Logger(ScraperService_1.name);
parsePrice(price) {
const cleanPrice = price.replace(/[^\d.]/g, '');
return cleanPrice ? parseFloat(cleanPrice) : 0;
}
parseDate(dateStr) {
try {
const [day, month, year] = dateStr.split('/').map(Number);
return new Date(year, month - 1, day);
}
catch {
return null;
}
}
parseDateRange(dateRange) {
try {
const [startStr, endStr] = dateRange.split('-').map((s) => s.trim());
return {
start: this.parseDate(startStr),
end: this.parseDate(endStr),
};
}
catch {
return { start: null, end: null };
}
}
getTableSelector(sourceId) {
switch (sourceId) {
case 2:
return 'table:eq(2)';
case 12:
return '#product-table';
default:
throw new Error(`Unsupported source ID: ${sourceId}`);
}
}
calculateDiscountPercentage(regularPrice, discountedPrice) {
if (!discountedPrice || !regularPrice)
return null;
return Math.round(((regularPrice - discountedPrice) / regularPrice) * 100);
}
parseProductRow($, rowElement, sourceId) {
const cells = $(rowElement).find('td');
const getText = (index) => {
const cell = cells.eq(index);
return cell.text().trim();
};
if (sourceId === 2) {
const promotionPeriod = getText(9);
const { start, end } = this.parseDateRange(promotionPeriod);
return {
name: getText(0),
regularPrice: this.parsePrice(getText(1)),
unitPrice: getText(2) || null,
availability: getText(3).toLowerCase() === 'да',
description: getText(4) || '',
category: 'Uncategorized',
discountedPrice: this.parsePrice(getText(6)),
discountPercentage: parseFloat(getText(7)) || null,
promotionType: getText(8) || null,
promotionStart: start,
promotionEnd: end,
};
}
else if (sourceId === 12) {
const name = getText(1);
const regularPrice = this.parsePrice(getText(3));
const description = getText(2) || '';
const discountedPrice = this.parsePrice(getText(4)) || null;
return {
name,
regularPrice,
unitPrice: null,
availability: true,
description,
category: 'Uncategorized',
discountedPrice,
discountPercentage: this.calculateDiscountPercentage(regularPrice, discountedPrice),
promotionType: null,
promotionStart: null,
promotionEnd: null,
};
}
throw new Error(`Unsupported source ID: ${sourceId}`);
}
async scrapeAllSources() {
try {
const sources = await this.prisma.source.findMany();
for (const source of sources) {
try {
await this.scrapeProducts(source.url, source.id);
this.logger.log(`Successfully scraped data from source: ${source.name}`);
}
catch (error) {
this.logger.error(`Failed to scrape source ${source.name}:`, error);
continue;
}
}
}
catch (error) {
this.logger.error('Failed to fetch sources:', error);
}
}
async scrapeProducts(sourceUrl, sourceId) {
const config = {
headers: {
Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.5',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/117.0',
},
};
try {
this.logger.log(`Fetching data from URL: ${sourceUrl}`);
const response = await axios_1.default.get(sourceUrl, config);
const $ = cheerio.load(response.data);
const productTable = $(this.getTableSelector(sourceId));
if (!productTable.length) {
throw new Error('Product table not found');
}
const rows = productTable.find('tr').slice(1);
this.logger.log(`Found ${rows.length} product rows`);
let processedProducts = 0;
for (const row of rows.toArray()) {
try {
const scrapedProduct = this.parseProductRow($, row, sourceId);
if (!scrapedProduct.name)
continue;
this.logger.log(`Processing product: ${scrapedProduct.name}`);
const product = await this.prisma.product.upsert({
where: {
name_sourceId: {
name: scrapedProduct.name,
sourceId: sourceId,
},
},
create: {
name: scrapedProduct.name,
description: scrapedProduct.description,
category: scrapedProduct.category,
availability: scrapedProduct.availability,
sourceId: sourceId,
prices: {
create: {
regularPrice: scrapedProduct.regularPrice,
discountedPrice: scrapedProduct.discountedPrice,
discountPercentage: scrapedProduct.discountPercentage,
unitPrice: scrapedProduct.unitPrice,
promotionType: scrapedProduct.promotionType,
promotionStart: scrapedProduct.promotionStart,
promotionEnd: scrapedProduct.promotionEnd,
sourceId: sourceId,
},
},
},
update: {
availability: scrapedProduct.availability,
description: scrapedProduct.description,
category: scrapedProduct.category,
prices: {
create: {
regularPrice: scrapedProduct.regularPrice,
discountedPrice: scrapedProduct.discountedPrice,
discountPercentage: scrapedProduct.discountPercentage,
unitPrice: scrapedProduct.unitPrice,
promotionType: scrapedProduct.promotionType,
promotionStart: scrapedProduct.promotionStart,
promotionEnd: scrapedProduct.promotionEnd,
sourceId: sourceId,
},
},
},
});
processedProducts++;
this.logger.log(`Successfully processed product: ${product.name}`);
}
catch (error) {
if (error instanceof Error) {
this.logger.error(`Failed to process row: ${error.message}`);
}
else {
this.logger.error('Failed to process row: Unknown error');
}
}
}
this.logger.log(`Successfully processed ${processedProducts} products`);
}
catch (error) {
if (error instanceof Error) {
this.logger.error(`Failed to scrape products from source ${sourceId}: ${error.message}`);
}
else {
this.logger.error(`Failed to scrape products from source ${sourceId}: Unknown error`);
}
throw error;
}
}
async manualScrape(sourceId) {
const source = await this.prisma.source.findUnique({
where: { id: sourceId },
});
if (!source) {
throw new Error(`Source with ID ${sourceId} not found`);
}
return this.scrapeProducts(source.url, source.id);
}
};
exports.ScraperService = ScraperService;
__decorate([
(0, schedule_1.Cron)(schedule_1.CronExpression.EVERY_HOUR),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", Promise)
], ScraperService.prototype, "scrapeAllSources", null);
exports.ScraperService = ScraperService = ScraperService_1 = __decorate([
(0, common_1.Injectable)(),
__metadata("design:paramtypes", [prisma_service_1.PrismaService,
config_1.ConfigService])
], ScraperService);
//# sourceMappingURL=scraper.service.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"scraper.service.js","sourceRoot":"","sources":["../../src/scraper/scraper.service.ts"],"names":[],"mappings":"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 truncated
+68
View File
@@ -0,0 +1,68 @@
import { SourceService } from './source.service';
export declare class SourceController {
private readonly sourceService;
constructor(sourceService: SourceService);
getSources(): Promise<{
id: number;
name: string;
url: string;
logo: string | null;
lastScraped: Date | null;
createdAt: Date;
updatedAt: Date;
}[]>;
getSource(id: string): Promise<{
id: number;
name: string;
url: string;
logo: string | null;
lastScraped: Date | null;
createdAt: Date;
updatedAt: Date;
} | null>;
getProductsFromSource(id: string, skip?: string, take?: string): Promise<{
products: ({
prices: ({
source: {
id: number;
name: string;
url: string;
logo: string | null;
lastScraped: Date | null;
createdAt: Date;
updatedAt: Date;
};
} & {
id: number;
createdAt: Date;
sourceId: number;
regularPrice: number;
discountedPrice: number | null;
discountPercentage: number | null;
unitPrice: string | null;
promotionType: string | null;
promotionStart: Date | null;
promotionEnd: Date | null;
vatIncluded: boolean;
lastUpdated: Date;
productId: number;
})[];
} & {
id: number;
name: string;
createdAt: Date;
updatedAt: Date;
description: string | null;
category: string;
availability: boolean;
sourceProductId: string | null;
sourceId: number;
})[];
pagination: {
total: number;
skip: number;
take: number;
hasMore: boolean;
};
}>;
}
+60
View File
@@ -0,0 +1,60 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SourceController = void 0;
const common_1 = require("@nestjs/common");
const source_service_1 = require("./source.service");
let SourceController = class SourceController {
sourceService;
constructor(sourceService) {
this.sourceService = sourceService;
}
async getSources() {
return this.sourceService.getSources();
}
async getSource(id) {
return this.sourceService.getSource(Number(id));
}
async getProductsFromSource(id, skip, take) {
return this.sourceService.getProductsFromSource(Number(id), skip ? Number(skip) : undefined, take ? Number(take) : undefined);
}
};
exports.SourceController = SourceController;
__decorate([
(0, common_1.Get)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", Promise)
], SourceController.prototype, "getSources", null);
__decorate([
(0, common_1.Get)(':id'),
__param(0, (0, common_1.Param)('id')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String]),
__metadata("design:returntype", Promise)
], SourceController.prototype, "getSource", null);
__decorate([
(0, common_1.Get)(':id/products'),
__param(0, (0, common_1.Param)('id')),
__param(1, (0, common_1.Query)('skip')),
__param(2, (0, common_1.Query)('take')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, String, String]),
__metadata("design:returntype", Promise)
], SourceController.prototype, "getProductsFromSource", null);
exports.SourceController = SourceController = __decorate([
(0, common_1.Controller)('sources'),
__metadata("design:paramtypes", [source_service_1.SourceService])
], SourceController);
//# sourceMappingURL=source.controller.js.map
@@ -0,0 +1 @@
{"version":3,"file":"source.controller.js","sourceRoot":"","sources":["../../src/source/source.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAA+D;AAC/D,qDAAiD;AAG1C,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;IACE;IAA7B,YAA6B,aAA4B;QAA5B,kBAAa,GAAb,aAAa,CAAe;IAAG,CAAC;IAGvD,AAAN,KAAK,CAAC,UAAU;QACd,OAAO,IAAI,CAAC,aAAa,CAAC,UAAU,EAAE,CAAC;IACzC,CAAC;IAGK,AAAN,KAAK,CAAC,SAAS,CAAc,EAAU;QACrC,OAAO,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;IAClD,CAAC;IAGK,AAAN,KAAK,CAAC,qBAAqB,CACZ,EAAU,EACR,IAAa,EACb,IAAa;QAE5B,OAAO,IAAI,CAAC,aAAa,CAAC,qBAAqB,CAC7C,MAAM,CAAC,EAAE,CAAC,EACV,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,EAC/B,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAChC,CAAC;IACJ,CAAC;CACF,CAAA;AAzBY,4CAAgB;AAIrB;IADL,IAAA,YAAG,GAAE;;;;kDAGL;AAGK;IADL,IAAA,YAAG,EAAC,KAAK,CAAC;IACM,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;;;;iDAE3B;AAGK;IADL,IAAA,YAAG,EAAC,cAAc,CAAC;IAEjB,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;IACX,WAAA,IAAA,cAAK,EAAC,MAAM,CAAC,CAAA;IACb,WAAA,IAAA,cAAK,EAAC,MAAM,CAAC,CAAA;;;;6DAOf;2BAxBU,gBAAgB;IAD5B,IAAA,mBAAU,EAAC,SAAS,CAAC;qCAEwB,8BAAa;GAD9C,gBAAgB,CAyB5B"}
+2
View File
@@ -0,0 +1,2 @@
export declare class SourceModule {
}
+25
View File
@@ -0,0 +1,25 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SourceModule = void 0;
const common_1 = require("@nestjs/common");
const source_controller_1 = require("./source.controller");
const source_service_1 = require("./source.service");
const prisma_module_1 = require("../prisma/prisma.module");
let SourceModule = class SourceModule {
};
exports.SourceModule = SourceModule;
exports.SourceModule = SourceModule = __decorate([
(0, common_1.Module)({
imports: [prisma_module_1.PrismaModule],
controllers: [source_controller_1.SourceController],
providers: [source_service_1.SourceService],
exports: [source_service_1.SourceService],
})
], SourceModule);
//# sourceMappingURL=source.module.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"source.module.js","sourceRoot":"","sources":["../../src/source/source.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,2DAAuD;AACvD,qDAAiD;AACjD,2DAAuD;AAQhD,IAAM,YAAY,GAAlB,MAAM,YAAY;CAAG,CAAA;AAAf,oCAAY;uBAAZ,YAAY;IANxB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE,CAAC,4BAAY,CAAC;QACvB,WAAW,EAAE,CAAC,oCAAgB,CAAC;QAC/B,SAAS,EAAE,CAAC,8BAAa,CAAC;QAC1B,OAAO,EAAE,CAAC,8BAAa,CAAC;KACzB,CAAC;GACW,YAAY,CAAG"}
+68
View File
@@ -0,0 +1,68 @@
import { PrismaService } from '../prisma/prisma.service';
export declare class SourceService {
private prisma;
constructor(prisma: PrismaService);
getSources(): Promise<{
id: number;
name: string;
url: string;
logo: string | null;
lastScraped: Date | null;
createdAt: Date;
updatedAt: Date;
}[]>;
getSource(id: number): Promise<{
id: number;
name: string;
url: string;
logo: string | null;
lastScraped: Date | null;
createdAt: Date;
updatedAt: Date;
} | null>;
getProductsFromSource(sourceId: number, skip?: number, take?: number): Promise<{
products: ({
prices: ({
source: {
id: number;
name: string;
url: string;
logo: string | null;
lastScraped: Date | null;
createdAt: Date;
updatedAt: Date;
};
} & {
id: number;
createdAt: Date;
sourceId: number;
regularPrice: number;
discountedPrice: number | null;
discountPercentage: number | null;
unitPrice: string | null;
promotionType: string | null;
promotionStart: Date | null;
promotionEnd: Date | null;
vatIncluded: boolean;
lastUpdated: Date;
productId: number;
})[];
} & {
id: number;
name: string;
createdAt: Date;
updatedAt: Date;
description: string | null;
category: string;
availability: boolean;
sourceProductId: string | null;
sourceId: number;
})[];
pagination: {
total: number;
skip: number;
take: number;
hasMore: boolean;
};
}>;
}
+76
View File
@@ -0,0 +1,76 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SourceService = void 0;
const common_1 = require("@nestjs/common");
const prisma_service_1 = require("../prisma/prisma.service");
let SourceService = class SourceService {
prisma;
constructor(prisma) {
this.prisma = prisma;
}
async getSources() {
return this.prisma.source.findMany({
orderBy: { name: 'asc' },
});
}
async getSource(id) {
return this.prisma.source.findUnique({
where: { id },
});
}
async getProductsFromSource(sourceId, skip, take) {
const products = await this.prisma.product.findMany({
where: {
prices: {
some: {
sourceId,
},
},
},
include: {
prices: {
where: { sourceId },
orderBy: { lastUpdated: 'desc' },
take: 1,
include: { source: true },
},
},
skip,
take: take || 20,
orderBy: { name: 'asc' },
});
const total = await this.prisma.product.count({
where: {
prices: {
some: {
sourceId,
},
},
},
});
return {
products,
pagination: {
total,
skip: skip || 0,
take: take || 20,
hasMore: (skip || 0) + (take || 20) < total,
},
};
}
};
exports.SourceService = SourceService;
exports.SourceService = SourceService = __decorate([
(0, common_1.Injectable)(),
__metadata("design:paramtypes", [prisma_service_1.PrismaService])
], SourceService);
//# sourceMappingURL=source.service.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"source.service.js","sourceRoot":"","sources":["../../src/source/source.service.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,6DAAyD;AAGlD,IAAM,aAAa,GAAnB,MAAM,aAAa;IACJ;IAApB,YAAoB,MAAqB;QAArB,WAAM,GAAN,MAAM,CAAe;IAAG,CAAC;IAE7C,KAAK,CAAC,UAAU;QACd,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC;YACjC,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE;SACzB,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,EAAU;QACxB,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC;YACnC,KAAK,EAAE,EAAE,EAAE,EAAE;SACd,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,qBAAqB,CACzB,QAAgB,EAChB,IAAa,EACb,IAAa;QAEb,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC;YAClD,KAAK,EAAE;gBACL,MAAM,EAAE;oBACN,IAAI,EAAE;wBACJ,QAAQ;qBACT;iBACF;aACF;YACD,OAAO,EAAE;gBACP,MAAM,EAAE;oBACN,KAAK,EAAE,EAAE,QAAQ,EAAE;oBACnB,OAAO,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE;oBAChC,IAAI,EAAE,CAAC;oBACP,OAAO,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE;iBAC1B;aACF;YACD,IAAI;YACJ,IAAI,EAAE,IAAI,IAAI,EAAE;YAChB,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE;SACzB,CAAC,CAAC;QAGH,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;YAC5C,KAAK,EAAE;gBACL,MAAM,EAAE;oBACN,IAAI,EAAE;wBACJ,QAAQ;qBACT;iBACF;aACF;SACF,CAAC,CAAC;QAEH,OAAO;YACL,QAAQ;YACR,UAAU,EAAE;gBACV,KAAK;gBACL,IAAI,EAAE,IAAI,IAAI,CAAC;gBACf,IAAI,EAAE,IAAI,IAAI,EAAE;gBAChB,OAAO,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,GAAG,KAAK;aAC5C;SACF,CAAC;IACJ,CAAC;CACF,CAAA;AA9DY,sCAAa;wBAAb,aAAa;IADzB,IAAA,mBAAU,GAAE;qCAEiB,8BAAa;GAD9B,aAAa,CA8DzB"}
+1
View File
@@ -0,0 +1 @@
export {};
+79
View File
@@ -0,0 +1,79 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const client_1 = require("@prisma/client");
const prisma = new client_1.PrismaClient();
async function main() {
try {
const testSource = await prisma.source.upsert({
where: {
name: 'Test Source',
},
update: {
url: 'http://example.com',
},
create: {
name: 'Test Source',
url: 'http://example.com',
},
});
console.log('Test source created/updated successfully:', testSource);
const products = await Promise.all([
prisma.product.create({
data: {
name: 'iPhone 15',
description: 'Latest Apple smartphone',
category: 'Electronics',
sourceId: testSource.id,
prices: {
create: {
regularPrice: 999.99,
discountedPrice: 899.99,
discountPercentage: 10,
sourceId: testSource.id,
unitPrice: '$999.99/unit'
}
}
},
}),
prisma.product.create({
data: {
name: 'Samsung Galaxy S24',
description: 'Premium Android smartphone',
category: 'Electronics',
sourceId: testSource.id,
prices: {
create: {
regularPrice: 899.99,
sourceId: testSource.id,
unitPrice: '$899.99/unit'
}
}
},
}),
prisma.product.create({
data: {
name: 'Sony PlayStation 5',
description: 'Gaming console with advanced graphics',
category: 'Gaming',
sourceId: testSource.id,
prices: {
create: {
regularPrice: 499.99,
sourceId: testSource.id,
unitPrice: '$499.99/unit'
}
}
},
}),
]);
console.log('Sample products created successfully:', products);
}
catch (error) {
console.error('Error creating test data:', error);
}
finally {
await prisma.$disconnect();
}
}
main();
//# sourceMappingURL=test-db.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"test-db.js","sourceRoot":"","sources":["../src/test-db.ts"],"names":[],"mappings":";;AAAA,2CAA8C;AAE9C,MAAM,MAAM,GAAG,IAAI,qBAAY,EAAE,CAAC;AAElC,KAAK,UAAU,IAAI;IACjB,IAAI,CAAC;QAEH,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;YAC5C,KAAK,EAAE;gBACL,IAAI,EAAE,aAAa;aACpB;YACD,MAAM,EAAE;gBACN,GAAG,EAAE,oBAAoB;aAC1B;YACD,MAAM,EAAE;gBACN,IAAI,EAAE,aAAa;gBACnB,GAAG,EAAE,oBAAoB;aAC1B;SACF,CAAC,CAAC;QACH,OAAO,CAAC,GAAG,CAAC,2CAA2C,EAAE,UAAU,CAAC,CAAC;QAGrE,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACjC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;gBACpB,IAAI,EAAE;oBACJ,IAAI,EAAE,WAAW;oBACjB,WAAW,EAAE,yBAAyB;oBACtC,QAAQ,EAAE,aAAa;oBACvB,QAAQ,EAAE,UAAU,CAAC,EAAE;oBACvB,MAAM,EAAE;wBACN,MAAM,EAAE;4BACN,YAAY,EAAE,MAAM;4BACpB,eAAe,EAAE,MAAM;4BACvB,kBAAkB,EAAE,EAAE;4BACtB,QAAQ,EAAE,UAAU,CAAC,EAAE;4BACvB,SAAS,EAAE,cAAc;yBAC1B;qBACF;iBACF;aACF,CAAC;YACF,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;gBACpB,IAAI,EAAE;oBACJ,IAAI,EAAE,oBAAoB;oBAC1B,WAAW,EAAE,4BAA4B;oBACzC,QAAQ,EAAE,aAAa;oBACvB,QAAQ,EAAE,UAAU,CAAC,EAAE;oBACvB,MAAM,EAAE;wBACN,MAAM,EAAE;4BACN,YAAY,EAAE,MAAM;4BACpB,QAAQ,EAAE,UAAU,CAAC,EAAE;4BACvB,SAAS,EAAE,cAAc;yBAC1B;qBACF;iBACF;aACF,CAAC;YACF,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;gBACpB,IAAI,EAAE;oBACJ,IAAI,EAAE,oBAAoB;oBAC1B,WAAW,EAAE,uCAAuC;oBACpD,QAAQ,EAAE,QAAQ;oBAClB,QAAQ,EAAE,UAAU,CAAC,EAAE;oBACvB,MAAM,EAAE;wBACN,MAAM,EAAE;4BACN,YAAY,EAAE,MAAM;4BACpB,QAAQ,EAAE,UAAU,CAAC,EAAE;4BACvB,SAAS,EAAE,cAAc;yBAC1B;qBACF;iBACF;aACF,CAAC;SACH,CAAC,CAAC;QAEH,OAAO,CAAC,GAAG,CAAC,uCAAuC,EAAE,QAAQ,CAAC,CAAC;IACjE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,2BAA2B,EAAE,KAAK,CAAC,CAAC;IACpD,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,CAAC,WAAW,EAAE,CAAC;IAC7B,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC"}
+1
View File
@@ -0,0 +1 @@
export {};
+22
View File
@@ -0,0 +1,22 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const core_1 = require("@nestjs/core");
const app_module_1 = require("./app.module");
const scraper_service_1 = require("./scraper/scraper.service");
async function bootstrap() {
const app = await core_1.NestFactory.create(app_module_1.AppModule);
const scraperService = app.get(scraper_service_1.ScraperService);
try {
const sourceId = 2;
await scraperService.manualScrape(sourceId);
console.log('Scraping completed successfully');
}
catch (error) {
console.error('Scraping failed:', error);
}
finally {
await app.close();
}
}
bootstrap();
//# sourceMappingURL=test-scraper.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"test-scraper.js","sourceRoot":"","sources":["../src/test-scraper.ts"],"names":[],"mappings":";;AAAA,uCAA2C;AAC3C,6CAAyC;AACzC,+DAA2D;AAE3D,KAAK,UAAU,SAAS;IACtB,MAAM,GAAG,GAAG,MAAM,kBAAW,CAAC,MAAM,CAAC,sBAAS,CAAC,CAAC;IAChD,MAAM,cAAc,GAAG,GAAG,CAAC,GAAG,CAAC,gCAAc,CAAC,CAAC;IAE/C,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,CAAC,CAAC;QACnB,MAAM,cAAc,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;QAC5C,OAAO,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC;IACjD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,kBAAkB,EAAE,KAAK,CAAC,CAAC;IAC3C,CAAC;YAAS,CAAC;QACT,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC;IACpB,CAAC;AACH,CAAC;AAED,SAAS,EAAE,CAAC"}
+1
View File
@@ -0,0 +1 @@
{"fileNames":["../node_modules/typescript/lib/lib.es5.d.ts","../node_modules/typescript/lib/lib.es2015.d.ts","../node_modules/typescript/lib/lib.es2016.d.ts","../node_modules/typescript/lib/lib.es2017.d.ts","../node_modules/typescript/lib/lib.es2018.d.ts","../node_modules/typescript/lib/lib.es2019.d.ts","../node_modules/typescript/lib/lib.es2020.d.ts","../node_modules/typescript/lib/lib.es2021.d.ts","../node_modules/typescript/lib/lib.es2022.d.ts","../node_modules/typescript/lib/lib.es2023.d.ts","../node_modules/typescript/lib/lib.dom.d.ts","../node_modules/typescript/lib/lib.dom.iterable.d.ts","../node_modules/typescript/lib/lib.dom.asynciterable.d.ts","../node_modules/typescript/lib/lib.webworker.importscripts.d.ts","../node_modules/typescript/lib/lib.scripthost.d.ts","../node_modules/typescript/lib/lib.es2015.core.d.ts","../node_modules/typescript/lib/lib.es2015.collection.d.ts","../node_modules/typescript/lib/lib.es2015.generator.d.ts","../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../node_modules/typescript/lib/lib.es2015.promise.d.ts","../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../node_modules/typescript/lib/lib.es2016.intl.d.ts","../node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","../node_modules/typescript/lib/lib.es2017.date.d.ts","../node_modules/typescript/lib/lib.es2017.object.d.ts","../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../node_modules/typescript/lib/lib.es2017.string.d.ts","../node_modules/typescript/lib/lib.es2017.intl.d.ts","../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../node_modules/typescript/lib/lib.es2018.intl.d.ts","../node_modules/typescript/lib/lib.es2018.promise.d.ts","../node_modules/typescript/lib/lib.es2018.regexp.d.ts","../node_modules/typescript/lib/lib.es2019.array.d.ts","../node_modules/typescript/lib/lib.es2019.object.d.ts","../node_modules/typescript/lib/lib.es2019.string.d.ts","../node_modules/typescript/lib/lib.es2019.symbol.d.ts","../node_modules/typescript/lib/lib.es2019.intl.d.ts","../node_modules/typescript/lib/lib.es2020.bigint.d.ts","../node_modules/typescript/lib/lib.es2020.date.d.ts","../node_modules/typescript/lib/lib.es2020.promise.d.ts","../node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../node_modules/typescript/lib/lib.es2020.string.d.ts","../node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../node_modules/typescript/lib/lib.es2020.intl.d.ts","../node_modules/typescript/lib/lib.es2020.number.d.ts","../node_modules/typescript/lib/lib.es2021.promise.d.ts","../node_modules/typescript/lib/lib.es2021.string.d.ts","../node_modules/typescript/lib/lib.es2021.weakref.d.ts","../node_modules/typescript/lib/lib.es2021.intl.d.ts","../node_modules/typescript/lib/lib.es2022.array.d.ts","../node_modules/typescript/lib/lib.es2022.error.d.ts","../node_modules/typescript/lib/lib.es2022.intl.d.ts","../node_modules/typescript/lib/lib.es2022.object.d.ts","../node_modules/typescript/lib/lib.es2022.string.d.ts","../node_modules/typescript/lib/lib.es2022.regexp.d.ts","../node_modules/typescript/lib/lib.es2023.array.d.ts","../node_modules/typescript/lib/lib.es2023.collection.d.ts","../node_modules/typescript/lib/lib.es2023.intl.d.ts","../node_modules/typescript/lib/lib.decorators.d.ts","../node_modules/typescript/lib/lib.decorators.legacy.d.ts","../node_modules/typescript/lib/lib.es2023.full.d.ts","../node_modules/reflect-metadata/index.d.ts","../node_modules/@nestjs/common/decorators/core/bind.decorator.d.ts","../node_modules/@nestjs/common/interfaces/abstract.interface.d.ts","../node_modules/@nestjs/common/interfaces/controllers/controller-metadata.interface.d.ts","../node_modules/@nestjs/common/interfaces/controllers/controller.interface.d.ts","../node_modules/@nestjs/common/interfaces/features/arguments-host.interface.d.ts","../node_modules/@nestjs/common/interfaces/exceptions/exception-filter.interface.d.ts","../node_modules/rxjs/dist/types/internal/Subscription.d.ts","../node_modules/rxjs/dist/types/internal/Subscriber.d.ts","../node_modules/rxjs/dist/types/internal/Operator.d.ts","../node_modules/rxjs/dist/types/internal/Observable.d.ts","../node_modules/rxjs/dist/types/internal/types.d.ts","../node_modules/rxjs/dist/types/internal/operators/audit.d.ts","../node_modules/rxjs/dist/types/internal/operators/auditTime.d.ts","../node_modules/rxjs/dist/types/internal/operators/buffer.d.ts","../node_modules/rxjs/dist/types/internal/operators/bufferCount.d.ts","../node_modules/rxjs/dist/types/internal/operators/bufferTime.d.ts","../node_modules/rxjs/dist/types/internal/operators/bufferToggle.d.ts","../node_modules/rxjs/dist/tLine truncated
+139
View File
@@ -0,0 +1,139 @@
import { UserService } from './user.service';
export declare class UserController {
private readonly userService;
constructor(userService: UserService);
getWatchlist(userId: string): Promise<({
product: {
prices: ({
source: {
id: number;
name: string;
url: string;
logo: string | null;
lastScraped: Date | null;
createdAt: Date;
updatedAt: Date;
};
} & {
id: number;
createdAt: Date;
sourceId: number;
regularPrice: number;
discountedPrice: number | null;
discountPercentage: number | null;
unitPrice: string | null;
promotionType: string | null;
promotionStart: Date | null;
promotionEnd: Date | null;
vatIncluded: boolean;
lastUpdated: Date;
productId: number;
})[];
} & {
id: number;
name: string;
createdAt: Date;
updatedAt: Date;
description: string | null;
category: string;
availability: boolean;
sourceProductId: string | null;
sourceId: number;
};
} & {
id: number;
createdAt: Date;
productId: number;
userId: number;
})[]>;
addToWatchlist(userId: string, data: {
productId: number;
}): Promise<{
id: number;
createdAt: Date;
productId: number;
userId: number;
}>;
removeFromWatchlist(userId: string, productId: string): Promise<{
id: number;
createdAt: Date;
productId: number;
userId: number;
}>;
getNotifications(userId: string): Promise<({
product: {
prices: ({
source: {
id: number;
name: string;
url: string;
logo: string | null;
lastScraped: Date | null;
createdAt: Date;
updatedAt: Date;
};
} & {
id: number;
createdAt: Date;
sourceId: number;
regularPrice: number;
discountedPrice: number | null;
discountPercentage: number | null;
unitPrice: string | null;
promotionType: string | null;
promotionStart: Date | null;
promotionEnd: Date | null;
vatIncluded: boolean;
lastUpdated: Date;
productId: number;
})[];
} & {
id: number;
name: string;
createdAt: Date;
updatedAt: Date;
description: string | null;
category: string;
availability: boolean;
sourceProductId: string | null;
sourceId: number;
};
} & {
id: number;
createdAt: Date;
updatedAt: Date;
productId: number;
userId: number;
priceThreshold: number | null;
percentThreshold: number | null;
notifyOnAnyChange: boolean;
isActive: boolean;
})[]>;
configureNotification(userId: string, data: {
productId: number;
priceThreshold?: number;
percentThreshold?: number;
notifyOnAnyChange?: boolean;
}): Promise<{
id: number;
createdAt: Date;
updatedAt: Date;
productId: number;
userId: number;
priceThreshold: number | null;
percentThreshold: number | null;
notifyOnAnyChange: boolean;
isActive: boolean;
}>;
removeNotification(userId: string, productId: string): Promise<{
id: number;
createdAt: Date;
updatedAt: Date;
productId: number;
userId: number;
priceThreshold: number | null;
percentThreshold: number | null;
notifyOnAnyChange: boolean;
isActive: boolean;
}>;
}
+93
View File
@@ -0,0 +1,93 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.UserController = void 0;
const common_1 = require("@nestjs/common");
const user_service_1 = require("./user.service");
let UserController = class UserController {
userService;
constructor(userService) {
this.userService = userService;
}
async getWatchlist(userId) {
return this.userService.getWatchlist(Number(userId));
}
async addToWatchlist(userId, data) {
return this.userService.addToWatchlist(Number(userId), data.productId);
}
async removeFromWatchlist(userId, productId) {
return this.userService.removeFromWatchlist(Number(userId), Number(productId));
}
async getNotifications(userId) {
return this.userService.getNotifications(Number(userId));
}
async configureNotification(userId, data) {
return this.userService.configureNotification(Number(userId), data);
}
async removeNotification(userId, productId) {
return this.userService.removeNotification(Number(userId), Number(productId));
}
};
exports.UserController = UserController;
__decorate([
(0, common_1.Get)(':userId/watchlist'),
__param(0, (0, common_1.Param)('userId')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String]),
__metadata("design:returntype", Promise)
], UserController.prototype, "getWatchlist", null);
__decorate([
(0, common_1.Post)(':userId/watchlist'),
__param(0, (0, common_1.Param)('userId')),
__param(1, (0, common_1.Body)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, Object]),
__metadata("design:returntype", Promise)
], UserController.prototype, "addToWatchlist", null);
__decorate([
(0, common_1.Delete)(':userId/watchlist/:productId'),
__param(0, (0, common_1.Param)('userId')),
__param(1, (0, common_1.Param)('productId')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, String]),
__metadata("design:returntype", Promise)
], UserController.prototype, "removeFromWatchlist", null);
__decorate([
(0, common_1.Get)(':userId/notifications'),
__param(0, (0, common_1.Param)('userId')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String]),
__metadata("design:returntype", Promise)
], UserController.prototype, "getNotifications", null);
__decorate([
(0, common_1.Post)(':userId/notifications'),
__param(0, (0, common_1.Param)('userId')),
__param(1, (0, common_1.Body)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, Object]),
__metadata("design:returntype", Promise)
], UserController.prototype, "configureNotification", null);
__decorate([
(0, common_1.Delete)(':userId/notifications/:productId'),
__param(0, (0, common_1.Param)('userId')),
__param(1, (0, common_1.Param)('productId')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, String]),
__metadata("design:returntype", Promise)
], UserController.prototype, "removeNotification", null);
exports.UserController = UserController = __decorate([
(0, common_1.Controller)('users'),
__metadata("design:paramtypes", [user_service_1.UserService])
], UserController);
//# sourceMappingURL=user.controller.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"user.controller.js","sourceRoot":"","sources":["../../src/user/user.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAAqF;AACrF,iDAA6C;AAGtC,IAAM,cAAc,GAApB,MAAM,cAAc;IACI;IAA7B,YAA6B,WAAwB;QAAxB,gBAAW,GAAX,WAAW,CAAa;IAAG,CAAC;IAGnD,AAAN,KAAK,CAAC,YAAY,CAAkB,MAAc;QAChD,OAAO,IAAI,CAAC,WAAW,CAAC,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IACvD,CAAC;IAGK,AAAN,KAAK,CAAC,cAAc,CACD,MAAc,EACvB,IAA2B;QAEnC,OAAO,IAAI,CAAC,WAAW,CAAC,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IACzE,CAAC;IAGK,AAAN,KAAK,CAAC,mBAAmB,CACN,MAAc,EACX,SAAiB;QAErC,OAAO,IAAI,CAAC,WAAW,CAAC,mBAAmB,CACzC,MAAM,CAAC,MAAM,CAAC,EACd,MAAM,CAAC,SAAS,CAAC,CAClB,CAAC;IACJ,CAAC;IAGK,AAAN,KAAK,CAAC,gBAAgB,CAAkB,MAAc;QACpD,OAAO,IAAI,CAAC,WAAW,CAAC,gBAAgB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IAC3D,CAAC;IAGK,AAAN,KAAK,CAAC,qBAAqB,CACR,MAAc,EAE/B,IAKC;QAED,OAAO,IAAI,CAAC,WAAW,CAAC,qBAAqB,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC;IACtE,CAAC;IAGK,AAAN,KAAK,CAAC,kBAAkB,CACL,MAAc,EACX,SAAiB;QAErC,OAAO,IAAI,CAAC,WAAW,CAAC,kBAAkB,CACxC,MAAM,CAAC,MAAM,CAAC,EACd,MAAM,CAAC,SAAS,CAAC,CAClB,CAAC;IACJ,CAAC;CACF,CAAA;AAxDY,wCAAc;AAInB;IADL,IAAA,YAAG,EAAC,mBAAmB,CAAC;IACL,WAAA,IAAA,cAAK,EAAC,QAAQ,CAAC,CAAA;;;;kDAElC;AAGK;IADL,IAAA,aAAI,EAAC,mBAAmB,CAAC;IAEvB,WAAA,IAAA,cAAK,EAAC,QAAQ,CAAC,CAAA;IACf,WAAA,IAAA,aAAI,GAAE,CAAA;;;;oDAGR;AAGK;IADL,IAAA,eAAM,EAAC,8BAA8B,CAAC;IAEpC,WAAA,IAAA,cAAK,EAAC,QAAQ,CAAC,CAAA;IACf,WAAA,IAAA,cAAK,EAAC,WAAW,CAAC,CAAA;;;;yDAMpB;AAGK;IADL,IAAA,YAAG,EAAC,uBAAuB,CAAC;IACL,WAAA,IAAA,cAAK,EAAC,QAAQ,CAAC,CAAA;;;;sDAEtC;AAGK;IADL,IAAA,aAAI,EAAC,uBAAuB,CAAC;IAE3B,WAAA,IAAA,cAAK,EAAC,QAAQ,CAAC,CAAA;IACf,WAAA,IAAA,aAAI,GAAE,CAAA;;;;2DASR;AAGK;IADL,IAAA,eAAM,EAAC,kCAAkC,CAAC;IAExC,WAAA,IAAA,cAAK,EAAC,QAAQ,CAAC,CAAA;IACf,WAAA,IAAA,cAAK,EAAC,WAAW,CAAC,CAAA;;;;wDAMpB;yBAvDU,cAAc;IAD1B,IAAA,mBAAU,EAAC,OAAO,CAAC;qCAEwB,0BAAW;GAD1C,cAAc,CAwD1B"}
+2
View File
@@ -0,0 +1,2 @@
export declare class UserModule {
}
+25
View File
@@ -0,0 +1,25 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.UserModule = void 0;
const common_1 = require("@nestjs/common");
const user_controller_1 = require("./user.controller");
const user_service_1 = require("./user.service");
const prisma_module_1 = require("../prisma/prisma.module");
let UserModule = class UserModule {
};
exports.UserModule = UserModule;
exports.UserModule = UserModule = __decorate([
(0, common_1.Module)({
imports: [prisma_module_1.PrismaModule],
controllers: [user_controller_1.UserController],
providers: [user_service_1.UserService],
exports: [user_service_1.UserService],
})
], UserModule);
//# sourceMappingURL=user.module.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"user.module.js","sourceRoot":"","sources":["../../src/user/user.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,uDAAmD;AACnD,iDAA6C;AAC7C,2DAAuD;AAQhD,IAAM,UAAU,GAAhB,MAAM,UAAU;CAAG,CAAA;AAAb,gCAAU;qBAAV,UAAU;IANtB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE,CAAC,4BAAY,CAAC;QACvB,WAAW,EAAE,CAAC,gCAAc,CAAC;QAC7B,SAAS,EAAE,CAAC,0BAAW,CAAC;QACxB,OAAO,EAAE,CAAC,0BAAW,CAAC;KACvB,CAAC;GACW,UAAU,CAAG"}
+137
View File
@@ -0,0 +1,137 @@
import { PrismaService } from '../prisma/prisma.service';
export declare class UserService {
private prisma;
constructor(prisma: PrismaService);
getWatchlist(userId: number): Promise<({
product: {
prices: ({
source: {
id: number;
name: string;
url: string;
logo: string | null;
lastScraped: Date | null;
createdAt: Date;
updatedAt: Date;
};
} & {
id: number;
createdAt: Date;
sourceId: number;
regularPrice: number;
discountedPrice: number | null;
discountPercentage: number | null;
unitPrice: string | null;
promotionType: string | null;
promotionStart: Date | null;
promotionEnd: Date | null;
vatIncluded: boolean;
lastUpdated: Date;
productId: number;
})[];
} & {
id: number;
name: string;
createdAt: Date;
updatedAt: Date;
description: string | null;
category: string;
availability: boolean;
sourceProductId: string | null;
sourceId: number;
};
} & {
id: number;
createdAt: Date;
productId: number;
userId: number;
})[]>;
addToWatchlist(userId: number, productId: number): Promise<{
id: number;
createdAt: Date;
productId: number;
userId: number;
}>;
removeFromWatchlist(userId: number, productId: number): Promise<{
id: number;
createdAt: Date;
productId: number;
userId: number;
}>;
getNotifications(userId: number): Promise<({
product: {
prices: ({
source: {
id: number;
name: string;
url: string;
logo: string | null;
lastScraped: Date | null;
createdAt: Date;
updatedAt: Date;
};
} & {
id: number;
createdAt: Date;
sourceId: number;
regularPrice: number;
discountedPrice: number | null;
discountPercentage: number | null;
unitPrice: string | null;
promotionType: string | null;
promotionStart: Date | null;
promotionEnd: Date | null;
vatIncluded: boolean;
lastUpdated: Date;
productId: number;
})[];
} & {
id: number;
name: string;
createdAt: Date;
updatedAt: Date;
description: string | null;
category: string;
availability: boolean;
sourceProductId: string | null;
sourceId: number;
};
} & {
id: number;
createdAt: Date;
updatedAt: Date;
productId: number;
userId: number;
priceThreshold: number | null;
percentThreshold: number | null;
notifyOnAnyChange: boolean;
isActive: boolean;
})[]>;
configureNotification(userId: number, data: {
productId: number;
priceThreshold?: number;
percentThreshold?: number;
notifyOnAnyChange?: boolean;
}): Promise<{
id: number;
createdAt: Date;
updatedAt: Date;
productId: number;
userId: number;
priceThreshold: number | null;
percentThreshold: number | null;
notifyOnAnyChange: boolean;
isActive: boolean;
}>;
removeNotification(userId: number, productId: number): Promise<{
id: number;
createdAt: Date;
updatedAt: Date;
productId: number;
userId: number;
priceThreshold: number | null;
percentThreshold: number | null;
notifyOnAnyChange: boolean;
isActive: boolean;
}>;
}
+174
View File
@@ -0,0 +1,174 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.UserService = void 0;
const common_1 = require("@nestjs/common");
const prisma_service_1 = require("../prisma/prisma.service");
let UserService = class UserService {
prisma;
constructor(prisma) {
this.prisma = prisma;
}
async getWatchlist(userId) {
const user = await this.prisma.user.findUnique({
where: { id: userId },
});
if (!user) {
throw new common_1.NotFoundException(`User with ID ${userId} not found`);
}
return this.prisma.watchlistItem.findMany({
where: { userId },
include: {
product: {
include: {
prices: {
orderBy: { lastUpdated: 'desc' },
take: 1,
include: { source: true },
},
},
},
},
});
}
async addToWatchlist(userId, productId) {
const user = await this.prisma.user.findUnique({
where: { id: userId },
});
if (!user) {
throw new common_1.NotFoundException(`User with ID ${userId} not found`);
}
const product = await this.prisma.product.findUnique({
where: { id: productId },
});
if (!product) {
throw new common_1.NotFoundException(`Product with ID ${productId} not found`);
}
return this.prisma.watchlistItem.upsert({
where: {
userId_productId: {
userId,
productId,
},
},
update: {},
create: {
userId,
productId,
},
});
}
async removeFromWatchlist(userId, productId) {
const watchlistItem = await this.prisma.watchlistItem.findUnique({
where: {
userId_productId: {
userId,
productId,
},
},
});
if (!watchlistItem) {
throw new common_1.NotFoundException(`Watchlist item for user ${userId} and product ${productId} not found`);
}
return this.prisma.watchlistItem.delete({
where: {
userId_productId: {
userId,
productId,
},
},
});
}
async getNotifications(userId) {
const user = await this.prisma.user.findUnique({
where: { id: userId },
});
if (!user) {
throw new common_1.NotFoundException(`User with ID ${userId} not found`);
}
return this.prisma.notification.findMany({
where: { userId },
include: {
product: {
include: {
prices: {
orderBy: { lastUpdated: 'desc' },
take: 1,
include: { source: true },
},
},
},
},
});
}
async configureNotification(userId, data) {
const user = await this.prisma.user.findUnique({
where: { id: userId },
});
if (!user) {
throw new common_1.NotFoundException(`User with ID ${userId} not found`);
}
const product = await this.prisma.product.findUnique({
where: { id: data.productId },
});
if (!product) {
throw new common_1.NotFoundException(`Product with ID ${data.productId} not found`);
}
return this.prisma.notification.upsert({
where: {
userId_productId: {
userId,
productId: data.productId,
},
},
update: {
priceThreshold: data.priceThreshold,
percentThreshold: data.percentThreshold,
notifyOnAnyChange: data.notifyOnAnyChange ?? false,
isActive: true,
},
create: {
userId,
productId: data.productId,
priceThreshold: data.priceThreshold,
percentThreshold: data.percentThreshold,
notifyOnAnyChange: data.notifyOnAnyChange ?? false,
},
});
}
async removeNotification(userId, productId) {
const notification = await this.prisma.notification.findUnique({
where: {
userId_productId: {
userId,
productId,
},
},
});
if (!notification) {
throw new common_1.NotFoundException(`Notification for user ${userId} and product ${productId} not found`);
}
return this.prisma.notification.delete({
where: {
userId_productId: {
userId,
productId,
},
},
});
}
};
exports.UserService = UserService;
exports.UserService = UserService = __decorate([
(0, common_1.Injectable)(),
__metadata("design:paramtypes", [prisma_service_1.PrismaService])
], UserService);
//# sourceMappingURL=user.service.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"user.service.js","sourceRoot":"","sources":["../../src/user/user.service.ts"],"names":[],"mappings":"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"}