Add public homepage subscribe endpoint for newsletter phone capture.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
ff291448f3
commit
88f6a430d9
@@ -0,0 +1,21 @@
|
||||
import { Transform } from 'class-transformer';
|
||||
import { IsString, Matches } from 'class-validator';
|
||||
|
||||
const PERSIAN_DIGITS = '۰۱۲۳۴۵۶۷۸۹';
|
||||
const ARABIC_DIGITS = '٠١٢٣٤٥٦٧٨٩';
|
||||
|
||||
export function normalizeCellNumber(value: unknown) {
|
||||
if (typeof value !== 'string') return value;
|
||||
return value
|
||||
.trim()
|
||||
.replace(/[۰-۹]/g, (digit) => String(PERSIAN_DIGITS.indexOf(digit)))
|
||||
.replace(/[٠-٩]/g, (digit) => String(ARABIC_DIGITS.indexOf(digit)))
|
||||
.replace(/[^\d]/g, '');
|
||||
}
|
||||
|
||||
export class SubscribeDto {
|
||||
@Transform(({ value }) => normalizeCellNumber(value))
|
||||
@IsString()
|
||||
@Matches(/^09\d{9}$/, { message: 'شماره موبایل معتبر نیست' })
|
||||
cellNumber!: string;
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Controller, Get, Param, ParseIntPipe, Query } from '@nestjs/common';
|
||||
import { Body, Controller, Get, Param, ParseIntPipe, Post, Query } from '@nestjs/common';
|
||||
import { SubscribeDto } from './dto/subscribe.dto';
|
||||
import { PublicService } from './public.service';
|
||||
|
||||
@Controller('public')
|
||||
@@ -78,4 +79,9 @@ export class PublicController {
|
||||
listWebsiteSlots(@Query('storeSlug') storeSlug?: string) {
|
||||
return this.publicService.listWebsiteSlots(storeSlug);
|
||||
}
|
||||
|
||||
@Post('subscribe')
|
||||
subscribe(@Body() dto: SubscribeDto) {
|
||||
return this.publicService.subscribe(dto.cellNumber);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { Prisma, StoreStatus } from '@prisma/client';
|
||||
import { Prisma, StoreStatus, UserRole } from '@prisma/client';
|
||||
import * as bcrypt from 'bcrypt';
|
||||
import { randomBytes } from 'crypto';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
|
||||
export type PublicCategoryTreeNode = {
|
||||
@@ -267,4 +269,39 @@ export class PublicService {
|
||||
|
||||
return { storeSlug: store.slug, items };
|
||||
}
|
||||
|
||||
async subscribe(cellNumber: string) {
|
||||
const existing = await this.prisma.user.findUnique({
|
||||
where: { cellNumber },
|
||||
select: { id: true },
|
||||
});
|
||||
if (existing) {
|
||||
return { ok: true as const, created: false };
|
||||
}
|
||||
|
||||
const passwordHash = await bcrypt.hash(randomBytes(32).toString('hex'), 10);
|
||||
|
||||
try {
|
||||
await this.prisma.user.create({
|
||||
data: {
|
||||
firstName: 'مشترک',
|
||||
lastName: 'خبرنامه',
|
||||
cellNumber,
|
||||
passwordHash,
|
||||
role: UserRole.CUSTOMER,
|
||||
about: 'ثبتنام از پنجره اشتراک صفحه اصلی',
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
if (
|
||||
error instanceof Prisma.PrismaClientKnownRequestError &&
|
||||
error.code === 'P2002'
|
||||
) {
|
||||
return { ok: true as const, created: false };
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
return { ok: true as const, created: true };
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user