Add public homepage subscribe endpoint for newsletter phone capture.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-10-07 20:43:14 +03:30
co-authored by Cursor
parent ff291448f3
commit 88f6a430d9
3 changed files with 66 additions and 2 deletions
+21
View File
@@ -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;
}
+7 -1
View File
@@ -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);
}
}
+38 -1
View File
@@ -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 };
}
}