made register and login apis for host
This commit is contained in:
37
src/common/utils/helper/CodeGenerator.ts
Normal file
37
src/common/utils/helper/CodeGenerator.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import * as crypto from 'crypto';
|
||||
|
||||
const algorithm = 'aes-256-cbc';
|
||||
const secretKey = crypto.scryptSync('your-secret-password', 'salt', 32);
|
||||
const ivLength = 16;
|
||||
|
||||
// Encrypt function
|
||||
export function encryptUserId(id: string): string {
|
||||
const iv = crypto.randomBytes(ivLength);
|
||||
const cipher = crypto.createCipheriv(algorithm, secretKey, iv);
|
||||
let encrypted = cipher.update(id, 'utf8', 'hex');
|
||||
encrypted += cipher.final('hex');
|
||||
return `${iv.toString('hex')}:${encrypted}`;
|
||||
}
|
||||
|
||||
// Decrypt function
|
||||
export function decryptUserId(encryptedId: string): string | null {
|
||||
try {
|
||||
const parts = encryptedId.split(':');
|
||||
if (parts.length !== 2) {
|
||||
console.error('Invalid encryptedId format:', encryptedId);
|
||||
return null;
|
||||
}
|
||||
|
||||
const iv = Buffer.from(parts[0], 'hex');
|
||||
const encryptedText = Buffer.from(parts[1], 'hex');
|
||||
|
||||
const decipher = crypto.createDecipheriv(algorithm, secretKey, iv);
|
||||
let decrypted = decipher.update(encryptedText);
|
||||
decrypted = Buffer.concat([decrypted, decipher.final()]);
|
||||
|
||||
return decrypted.toString('utf8');
|
||||
} catch (error) {
|
||||
console.error('Decryption failed:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
18
src/common/utils/helper/OtpGenerator.ts
Normal file
18
src/common/utils/helper/OtpGenerator.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import config from '../../../config/config';
|
||||
|
||||
export class OtpGenerator {
|
||||
static generateOtp(): string {
|
||||
if (config.byPassOTP) {
|
||||
return '1234';
|
||||
}
|
||||
return Math.floor(1000 + Math.random() * 9000).toString();
|
||||
}
|
||||
}
|
||||
export class OtpGeneratorSixDigit {
|
||||
static generateOtp(): string {
|
||||
if (config.byPassOTP) {
|
||||
return '123456';
|
||||
}
|
||||
return Math.floor(100000 + Math.random() * 900000).toString();
|
||||
}
|
||||
}
|
||||
91
src/common/utils/helper/sendOtp.ts
Normal file
91
src/common/utils/helper/sendOtp.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import * as bcrypt from "bcryptjs";
|
||||
import { OtpGenerator, OtpGeneratorSixDigit } from "./OtpGenerator";
|
||||
import { encryptUserId } from "./CodeGenerator";
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
export interface OtpResult {
|
||||
otp: string; // Plain OTP (for sending)
|
||||
hashedOtp: string; // Hashed OTP (for DB storage)
|
||||
expiry: Date; // Expiry timestamp
|
||||
encryptedId: string; // Encrypted user ID
|
||||
emailMessage: string; // Message body for email
|
||||
emailSubject: string; // Subject line for email
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate OTP, store it in DB, and send email.
|
||||
* @param userId The user’s ID
|
||||
* @param email Recipient email
|
||||
* @param emailPurpose For which flow (e.g. "Register", "Login", "ForgotPassword")
|
||||
* @param otpLength OTP length (4 or 6)
|
||||
* @param expiryMinutes Expiry time in minutes
|
||||
*/
|
||||
export async function generateOtpHelper(
|
||||
userId: number,
|
||||
email: string,
|
||||
emailPurpose: "Register" | "Login" | "ForgotPassword",
|
||||
otpLength: 4 | 6 = 4,
|
||||
expiryMinutes: number = 5
|
||||
): Promise<OtpResult> {
|
||||
// Generate OTP
|
||||
const otp =
|
||||
otpLength === 6
|
||||
? OtpGeneratorSixDigit.generateOtp()
|
||||
: OtpGenerator.generateOtp();
|
||||
console.log("Generated OTP:", otp);
|
||||
// Hash OTP
|
||||
const hashedOtp = await bcrypt.hash(otp, 10);
|
||||
|
||||
// Expiry time
|
||||
const expiry = new Date(Date.now() + expiryMinutes * 60 * 1000);
|
||||
|
||||
// Encrypt user ID
|
||||
const encryptedId = encryptUserId(userId.toString());
|
||||
|
||||
// 🔹 First delete old OTPs for this user & purpose
|
||||
await prisma.userOtp.deleteMany({
|
||||
where: {
|
||||
userXid: userId,
|
||||
otpType: emailPurpose,
|
||||
isActive: true,
|
||||
},
|
||||
});
|
||||
|
||||
// Save OTP into user_otps table
|
||||
await prisma.userOtp.create({
|
||||
data: {
|
||||
userXid: userId,
|
||||
otpType: emailPurpose,
|
||||
otpCode: hashedOtp,
|
||||
expiresOn: expiry,
|
||||
isVerified: false,
|
||||
isActive: true,
|
||||
// sendOn will default to now()
|
||||
// createdAt will default to now()
|
||||
},
|
||||
});
|
||||
|
||||
// Build email content
|
||||
const emailSubject = `${emailPurpose} OTP Verification`;
|
||||
const emailMessage = `Your OTP for ${emailPurpose} is ${otp}. It will expire in ${expiryMinutes} minutes.`;
|
||||
|
||||
// Send email
|
||||
// await sendBulkEmailForOTP([
|
||||
// {
|
||||
// to: [{ email: email }],
|
||||
// subject: emailSubject,
|
||||
// htmlContent: emailMessage,
|
||||
// },
|
||||
// ]);
|
||||
|
||||
return {
|
||||
otp,
|
||||
hashedOtp,
|
||||
expiry,
|
||||
encryptedId,
|
||||
emailMessage,
|
||||
emailSubject,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// validations/hostBankDetails.validation.ts
|
||||
import { z } from "zod";
|
||||
|
||||
export const hostBankDetailsSchema = z.object({
|
||||
accountNumber: z
|
||||
.number()
|
||||
.int({ message: "Account number must be an integer" })
|
||||
.positive({ message: "Account number must be a positive number" }),
|
||||
|
||||
accountHolderName: z
|
||||
.string()
|
||||
.nonempty("Account holder name is required")
|
||||
.min(2, { message: "Account holder name must be at least 2 characters" }),
|
||||
|
||||
ifscCode: z
|
||||
.string()
|
||||
.nonempty("IFSC code is required")
|
||||
.regex(/^[A-Z]{4}0[A-Z0-9]{6}$/, { message: "Invalid IFSC code format" }),
|
||||
|
||||
bankXid: z
|
||||
.number()
|
||||
.int({ message: "Bank ID must be an integer" })
|
||||
.positive({ message: "Bank ID must be a positive number" }),
|
||||
|
||||
hostXid: z
|
||||
.number()
|
||||
.int({ message: "Host ID must be an integer" })
|
||||
.positive({ message: "Host ID must be a positive number" }),
|
||||
|
||||
bankBranchXid: z
|
||||
.number()
|
||||
.int({ message: "Bank branch ID must be an integer" })
|
||||
.positive({ message: "Bank branch ID must be a positive number" }),
|
||||
});
|
||||
|
||||
export type HostBankDetailsSchema = z.infer<typeof hostBankDetailsSchema>;
|
||||
@@ -0,0 +1,44 @@
|
||||
import { z } from "zod";
|
||||
|
||||
// Allowed document types (must match your DocumentType master table IDs)
|
||||
export const REQUIRED_DOC_TYPES = {
|
||||
PAN: 1,
|
||||
GST: 2,
|
||||
REGISTRATION: 3,
|
||||
AADHAAR: 4,
|
||||
};
|
||||
|
||||
export const hostCompanyDetailsSchema = z.object({
|
||||
companyName: z.string().min(1, "Company name is required"),
|
||||
address1: z.string().min(1, "Address1 is required"),
|
||||
address2: z.string().optional(),
|
||||
hostRefNumber: z.string().min(1, "Host reference number is required"),
|
||||
cityXid: z.number().min(1, "City is required"),
|
||||
stateXid: z.number().min(1, "State is required"),
|
||||
countryXid: z.number().min(1,"Country is required"),
|
||||
pinCode: z.string().min(4, "Pincode/Zipcode is required"),
|
||||
logoPath: z.string().optional(),
|
||||
isSubsidairy: z.boolean(),
|
||||
registrationNumber: z.string().min(1, "Registration number is required"),
|
||||
panNumber: z.string().min(1, "PAN number is required"),
|
||||
gstNumber: z.string().optional(),
|
||||
formationDate: z.string().refine((val) => !isNaN(Date.parse(val)), {
|
||||
message: "Formation date must be a valid date",
|
||||
}),
|
||||
companyType: z.string().min(1, "Company type is required"),
|
||||
websiteUrl: z.url().optional(),
|
||||
instagramUrl: z.url().optional(),
|
||||
facebookUrl: z.url().optional(),
|
||||
linkedinUrl: z.url().optional(),
|
||||
twitterUrl: z.url().optional(),
|
||||
currencyXid: z.number().min(1, "Currency is required"),
|
||||
});
|
||||
|
||||
// Validation for documents
|
||||
export const hostDocumentsSchema = z.array(
|
||||
z.object({
|
||||
documentTypeXid: z.number(),
|
||||
documentName: z.string(),
|
||||
filePath: z.string(),
|
||||
})
|
||||
);
|
||||
20
src/common/utils/validation/host/login.validation.ts
Normal file
20
src/common/utils/validation/host/login.validation.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
// validations/hostBankDetails.validation.ts
|
||||
import { z } from "zod";
|
||||
|
||||
export const loginForHostSchema = z.object({
|
||||
|
||||
|
||||
emailAddress : z
|
||||
.string()
|
||||
.nonempty("Email is required"),
|
||||
|
||||
userPassword : z
|
||||
.string()
|
||||
.nonempty("Password is required")
|
||||
.min(8, { message: "Password must be at least 8 characters" }),
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
export type loginForHostSchema = z.infer<typeof loginForHostSchema>;
|
||||
Reference in New Issue
Block a user