fitlien-service-881 Removed the moved functions and the sendEmailSMS, sendSMSMessage
This commit is contained in:
parent
a69ed7078a
commit
b0aaab0e81
@ -1,6 +1,3 @@
|
|||||||
TWILIO_ACCOUNT_SID=#{TWILIO_ACCOUNT_SID}#
|
|
||||||
TWILIO_AUTH_TOKEN=#{TWILIO_AUTH_TOKEN}#
|
|
||||||
TWILIO_PHONE_NUMBER=#{TWILIO_PHONE_NUMBER}#
|
|
||||||
SERVICES_RGN=#{SERVICES_RGN}#
|
SERVICES_RGN=#{SERVICES_RGN}#
|
||||||
CASHFREE_CLIENT_ID=#{CASHFREE_CLIENT_ID}#
|
CASHFREE_CLIENT_ID=#{CASHFREE_CLIENT_ID}#
|
||||||
CASHFREE_CLIENT_SECRET=#{CASHFREE_CLIENT_SECRET}#
|
CASHFREE_CLIENT_SECRET=#{CASHFREE_CLIENT_SECRET}#
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
export { sendEmailSES } from './sendEmailSES';
|
|
||||||
@ -1,209 +0,0 @@
|
|||||||
import { getLogger } from "../shared/config";
|
|
||||||
import { getCorsHandler } from "../shared/middleware";
|
|
||||||
import { onRequest } from "firebase-functions/v2/https";
|
|
||||||
import { Request } from "firebase-functions/v2/https";
|
|
||||||
import { Response } from "express";
|
|
||||||
import { SESClient } from "@aws-sdk/client-ses";
|
|
||||||
import { SendEmailCommand, SendRawEmailCommand } from "@aws-sdk/client-ses";
|
|
||||||
import { HttpsError } from "firebase-functions/v2/https";
|
|
||||||
import * as mime from 'mime-types';
|
|
||||||
import axios from 'axios';
|
|
||||||
|
|
||||||
const logger = getLogger();
|
|
||||||
const corsHandler = getCorsHandler();
|
|
||||||
|
|
||||||
interface EmailRequest {
|
|
||||||
to: string | string[];
|
|
||||||
subject: string;
|
|
||||||
html: string;
|
|
||||||
text?: string;
|
|
||||||
from: string;
|
|
||||||
replyTo?: string;
|
|
||||||
attachments?: Attachment[];
|
|
||||||
fileUrl?: string;
|
|
||||||
fileName?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Attachment {
|
|
||||||
filename: string;
|
|
||||||
content: string | Buffer;
|
|
||||||
contentType?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const stripHtml = (html: string): string => {
|
|
||||||
if (!html) return '';
|
|
||||||
return html.replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' ').trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function sendSimpleEmail(data: EmailRequest, recipients: string[]) {
|
|
||||||
const ses = new SESClient({
|
|
||||||
region: process.env.AWS_REGION,
|
|
||||||
credentials: {
|
|
||||||
accessKeyId: process.env.AWS_ACCESS_KEY_ID || '',
|
|
||||||
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY || ''
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const command = new SendEmailCommand({
|
|
||||||
Source: data.from,
|
|
||||||
Destination: { ToAddresses: recipients },
|
|
||||||
Message: {
|
|
||||||
Subject: { Data: data.subject },
|
|
||||||
Body: {
|
|
||||||
Html: { Data: data.html },
|
|
||||||
Text: { Data: data.text || stripHtml(data.html) }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ReplyToAddresses: data.replyTo ? [data.replyTo] : undefined,
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = await ses.send(command);
|
|
||||||
return { messageId: result.MessageId };
|
|
||||||
}
|
|
||||||
|
|
||||||
async function sendEmailWithAttachments(data: EmailRequest, recipients: string[]) {
|
|
||||||
const ses = new SESClient({
|
|
||||||
region: process.env.AWS_REGION,
|
|
||||||
credentials: {
|
|
||||||
accessKeyId: process.env.AWS_ACCESS_KEY_ID || '',
|
|
||||||
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY || ''
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const boundary = `boundary_${Math.random().toString(16).substr(2)}`;
|
|
||||||
let rawMessage = `From: ${data.from}\n`;
|
|
||||||
rawMessage += `To: ${recipients.join(', ')}\n`;
|
|
||||||
rawMessage += `Subject: ${data.subject}\n`;
|
|
||||||
rawMessage += `MIME-Version: 1.0\n`;
|
|
||||||
rawMessage += `Content-Type: multipart/mixed; boundary="${boundary}"\n\n`;
|
|
||||||
|
|
||||||
// Add email body (multipart/alternative)
|
|
||||||
rawMessage += `--${boundary}\n`;
|
|
||||||
rawMessage += `Content-Type: multipart/alternative; boundary="alt_${boundary}"\n\n`;
|
|
||||||
|
|
||||||
// Text part
|
|
||||||
if (data.text) {
|
|
||||||
rawMessage += `--alt_${boundary}\n`;
|
|
||||||
rawMessage += `Content-Type: text/plain; charset=UTF-8\n\n`;
|
|
||||||
rawMessage += `${data.text}\n\n`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTML part
|
|
||||||
rawMessage += `--alt_${boundary}\n`;
|
|
||||||
rawMessage += `Content-Type: text/html; charset=UTF-8\n\n`;
|
|
||||||
rawMessage += `${data.html}\n\n`;
|
|
||||||
|
|
||||||
// Close alternative part
|
|
||||||
rawMessage += `--alt_${boundary}--\n\n`;
|
|
||||||
|
|
||||||
// Add attachments
|
|
||||||
for (const attachment of data.attachments || []) {
|
|
||||||
const contentType = attachment.contentType ||
|
|
||||||
mime.lookup(attachment.filename) ||
|
|
||||||
'application/octet-stream';
|
|
||||||
|
|
||||||
rawMessage += `--${boundary}\n`;
|
|
||||||
rawMessage += `Content-Type: ${contentType}; name="${attachment.filename}"\n`;
|
|
||||||
rawMessage += `Content-Disposition: attachment; filename="${attachment.filename}"\n`;
|
|
||||||
rawMessage += `Content-Transfer-Encoding: base64\n\n`;
|
|
||||||
|
|
||||||
const contentBuffer = typeof attachment.content === 'string'
|
|
||||||
? Buffer.from(attachment.content, 'base64')
|
|
||||||
: attachment.content;
|
|
||||||
|
|
||||||
rawMessage += contentBuffer.toString('base64') + '\n\n';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close message
|
|
||||||
rawMessage += `--${boundary}--`;
|
|
||||||
|
|
||||||
const command = new SendRawEmailCommand({
|
|
||||||
RawMessage: { Data: Buffer.from(rawMessage) }
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = await ses.send(command);
|
|
||||||
return { messageId: result.MessageId };
|
|
||||||
}
|
|
||||||
|
|
||||||
async function downloadFileFromUrl(url: string): Promise<Buffer> {
|
|
||||||
try {
|
|
||||||
const response = await axios.get(url, { responseType: 'arraybuffer' });
|
|
||||||
return Buffer.from(response.data);
|
|
||||||
} catch (error) {
|
|
||||||
logger.error(`Error downloading file from URL: ${error}`);
|
|
||||||
throw new Error(`Failed to download file: ${error}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const sendEmailSES = onRequest({
|
|
||||||
region: '#{SERVICES_RGN}#'
|
|
||||||
}, (request: Request, response: Response) => {
|
|
||||||
return corsHandler(request, response, async () => {
|
|
||||||
try {
|
|
||||||
const toAddress = request.body.toAddress;
|
|
||||||
const subject = request.body.subject;
|
|
||||||
const message = request.body.message;
|
|
||||||
|
|
||||||
// Initialize data with basic fields
|
|
||||||
const data: EmailRequest = {
|
|
||||||
to: toAddress,
|
|
||||||
html: message,
|
|
||||||
subject: subject,
|
|
||||||
text: stripHtml(message),
|
|
||||||
from: process.env.SES_FROM_EMAIL || 'support@fitlien.com',
|
|
||||||
replyTo: process.env.SES_REPLY_TO_EMAIL || 'support@fitlien.com',
|
|
||||||
attachments: request.body.attachments as Attachment[] || []
|
|
||||||
};
|
|
||||||
|
|
||||||
// Handle file URL if provided
|
|
||||||
if (request.body.fileUrl && request.body.fileName) {
|
|
||||||
logger.info(`Downloading attachment from URL: ${request.body.fileUrl}`);
|
|
||||||
try {
|
|
||||||
const fileContent = await downloadFileFromUrl(request.body.fileUrl);
|
|
||||||
|
|
||||||
// If attachments array doesn't exist, create it
|
|
||||||
if (!data.attachments) {
|
|
||||||
data.attachments = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the downloaded file as an attachment
|
|
||||||
data.attachments.push({
|
|
||||||
filename: request.body.fileName,
|
|
||||||
content: fileContent,
|
|
||||||
contentType: mime.lookup(request.body.fileName) || 'application/octet-stream'
|
|
||||||
});
|
|
||||||
|
|
||||||
logger.info(`Successfully downloaded attachment: ${request.body.fileName}`);
|
|
||||||
} catch (downloadError) {
|
|
||||||
logger.error(`Failed to download attachment: ${downloadError}`);
|
|
||||||
throw new Error(`Failed to process attachment: ${downloadError}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!data.to || !data.subject || !data.html || !data.from) {
|
|
||||||
throw new HttpsError(
|
|
||||||
'invalid-argument',
|
|
||||||
'Missing required email fields'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info(`Sending Email '${data.subject}' to '${data.to}' from '${data.from}'`);
|
|
||||||
const recipients = Array.isArray(data.to) ? data.to : [data.to];
|
|
||||||
|
|
||||||
if (data.attachments && data.attachments.length > 0) {
|
|
||||||
const messageResult = await sendEmailWithAttachments(data, recipients);
|
|
||||||
response.status(200).json(messageResult);
|
|
||||||
} else {
|
|
||||||
const messageResult = await sendSimpleEmail(data, recipients);
|
|
||||||
response.status(200).json(messageResult);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
logger.error(`Error while sending E-mail. Error: ${e}`);
|
|
||||||
console.error(`Error while sending E-mail. Error: ${e}`);
|
|
||||||
response.status(500).json({
|
|
||||||
success: false,
|
|
||||||
error: 'Error while sending E-mail'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -10,16 +10,12 @@ setGlobalOptions({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export * from "./shared/config";
|
export * from "./shared/config";
|
||||||
export { sendEmailSES } from "./email";
|
|
||||||
export { sendSMSMessage } from "./sms";
|
|
||||||
export { accessFile } from "./storage";
|
export { accessFile } from "./storage";
|
||||||
export {
|
export {
|
||||||
processNotificationOnCreate,
|
processNotificationOnCreate,
|
||||||
checkExpiredMemberships,
|
checkExpiredMemberships,
|
||||||
} from "./notifications";
|
} from "./notifications";
|
||||||
export * from "./payments";
|
export * from "./payments";
|
||||||
export { getPlaceDetails, getPlacesAutocomplete } from "./places";
|
|
||||||
export { registerClient } from "./users";
|
|
||||||
export {
|
export {
|
||||||
esslGetUserDetails,
|
esslGetUserDetails,
|
||||||
esslUpdateUser,
|
esslUpdateUser,
|
||||||
|
|||||||
@ -1,67 +0,0 @@
|
|||||||
import { onRequest } from "firebase-functions/v2/https";
|
|
||||||
import { Request } from "firebase-functions/v2/https";
|
|
||||||
import * as express from "express";
|
|
||||||
import { getLogger } from "../shared/config";
|
|
||||||
import { getCorsHandler } from "../shared/middleware";
|
|
||||||
import axios from "axios";
|
|
||||||
|
|
||||||
const logger = getLogger();
|
|
||||||
const corsHandler = getCorsHandler();
|
|
||||||
|
|
||||||
export const getPlacesAutocomplete = onRequest({
|
|
||||||
region: '#{SERVICES_RGN}#'
|
|
||||||
}, async (request: Request, response: express.Response) => {
|
|
||||||
return corsHandler(request, response, async () => {
|
|
||||||
try {
|
|
||||||
const { input, location, radius, types, components, sessiontoken } = request.query;
|
|
||||||
|
|
||||||
if (!input) {
|
|
||||||
response.status(400).json({
|
|
||||||
error: 'Input parameter is required for autocomplete'
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const apiKey = process.env.GOOGLE_MAPS_API_KEY;
|
|
||||||
if (!apiKey) {
|
|
||||||
logger.error('Google Places API key is not configured');
|
|
||||||
response.status(500).json({ error: 'Server configuration error' });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const url = 'https://maps.googleapis.com/maps/api/place/autocomplete/json';
|
|
||||||
const params: any = {
|
|
||||||
key: apiKey,
|
|
||||||
input: input
|
|
||||||
};
|
|
||||||
|
|
||||||
if (location && radius) {
|
|
||||||
params.location = location;
|
|
||||||
params.radius = radius;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (types) {
|
|
||||||
params.types = types;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (components) {
|
|
||||||
params.components = components;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sessiontoken) {
|
|
||||||
params.sessiontoken = sessiontoken;
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await axios.get(url, { params });
|
|
||||||
|
|
||||||
logger.info('Google Places Autocomplete API request completed successfully');
|
|
||||||
response.json(result.data);
|
|
||||||
} catch (error) {
|
|
||||||
logger.error('Error fetching place autocomplete suggestions:', error);
|
|
||||||
response.status(500).json({
|
|
||||||
success: false,
|
|
||||||
error: error instanceof Error ? error.message : String(error)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1,51 +0,0 @@
|
|||||||
import { onRequest } from "firebase-functions/v2/https";
|
|
||||||
import { Request } from "firebase-functions/v2/https";
|
|
||||||
import * as express from "express";
|
|
||||||
import axios from "axios";
|
|
||||||
|
|
||||||
const { getCorsHandler } = require('../shared/middleware');
|
|
||||||
const corsHandler = getCorsHandler();
|
|
||||||
const { getLogger } = require('../shared/config');
|
|
||||||
const logger = getLogger();
|
|
||||||
|
|
||||||
|
|
||||||
export const getPlaceDetails = onRequest({
|
|
||||||
region: '#{SERVICES_RGN}#'
|
|
||||||
}, async (request: Request, response: express.Response) => {
|
|
||||||
return corsHandler(request, response, async () => {
|
|
||||||
try {
|
|
||||||
const { place_id, fields } = request.query;
|
|
||||||
|
|
||||||
if (!place_id) {
|
|
||||||
response.status(400).json({
|
|
||||||
error: 'place_id parameter is required'
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const apiKey = process.env.GOOGLE_MAPS_API_KEY;
|
|
||||||
if (!apiKey) {
|
|
||||||
logger.error('Google Places API key is not configured');
|
|
||||||
response.status(500).json({ error: 'Server configuration error' });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const url = 'https://maps.googleapis.com/maps/api/place/details/json';
|
|
||||||
const params: any = {
|
|
||||||
key: apiKey,
|
|
||||||
place_id: place_id,
|
|
||||||
fields: fields || 'geometry'
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = await axios.get(url, { params });
|
|
||||||
logger.info('Google Places Details API request completed successfully');
|
|
||||||
response.json(result.data);
|
|
||||||
} catch (error) {
|
|
||||||
logger.error('Error fetching place details:', error);
|
|
||||||
response.status(500).json({
|
|
||||||
success: false,
|
|
||||||
error: error instanceof Error ? error.message : String(error)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1,2 +0,0 @@
|
|||||||
export { getPlaceDetails } from './details';
|
|
||||||
export { getPlacesAutocomplete } from './autocomplete';
|
|
||||||
@ -1 +0,0 @@
|
|||||||
export { sendSMSMessage } from './sendSMS';
|
|
||||||
@ -1,77 +0,0 @@
|
|||||||
import { onRequest } from "firebase-functions/v2/https";
|
|
||||||
import { Request } from "firebase-functions/v2/https";
|
|
||||||
import { getCorsHandler } from "../shared/middleware";
|
|
||||||
import { getLogger } from "../shared/config";
|
|
||||||
import twilio from 'twilio';
|
|
||||||
|
|
||||||
const corsHandler = getCorsHandler();
|
|
||||||
const logger = getLogger();
|
|
||||||
|
|
||||||
// Initialize Twilio client
|
|
||||||
const twilioClient = twilio(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN);
|
|
||||||
|
|
||||||
interface SMSRequest {
|
|
||||||
to: string;
|
|
||||||
body: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const sendSMSMessage = onRequest({
|
|
||||||
region: '#{SERVICES_RGN}#'
|
|
||||||
}, async (request: Request, response) => {
|
|
||||||
return corsHandler(request, response, async () => {
|
|
||||||
try {
|
|
||||||
const { to, body } = request.body as SMSRequest;
|
|
||||||
|
|
||||||
// Input validation
|
|
||||||
if (!to || !body) {
|
|
||||||
logger.error('Missing required SMS parameters');
|
|
||||||
response.status(400).json({
|
|
||||||
success: false,
|
|
||||||
error: 'Both "to" and "body" parameters are required'
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate phone number format (basic check)
|
|
||||||
if (!/^\+?[1-9]\d{1,14}$/.test(to)) {
|
|
||||||
logger.error('Invalid phone number format', { to });
|
|
||||||
response.status(400).json({
|
|
||||||
success: false,
|
|
||||||
error: 'Invalid phone number format'
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send SMS
|
|
||||||
const message = await twilioClient.messages.create({
|
|
||||||
body: body,
|
|
||||||
from: process.env.TWILIO_PHONE_NUMBER,
|
|
||||||
to: to
|
|
||||||
});
|
|
||||||
|
|
||||||
logger.info('SMS sent successfully', {
|
|
||||||
messageId: message.sid,
|
|
||||||
to: to,
|
|
||||||
length: body.length
|
|
||||||
});
|
|
||||||
|
|
||||||
response.json({
|
|
||||||
success: true,
|
|
||||||
messageId: message.sid,
|
|
||||||
timestamp: message.dateCreated
|
|
||||||
});
|
|
||||||
|
|
||||||
} catch (error: any) {
|
|
||||||
logger.error('Error sending SMS:', error);
|
|
||||||
|
|
||||||
const statusCode = error.status === 401 ? 401 : 500;
|
|
||||||
|
|
||||||
response.status(statusCode).json({
|
|
||||||
success: false,
|
|
||||||
error: error.message,
|
|
||||||
code: error.code,
|
|
||||||
moreInfo: error.moreInfo
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1,117 +0,0 @@
|
|||||||
import { onRequest } from "firebase-functions/v2/https";
|
|
||||||
import { getCorsHandler } from "../shared/middleware";
|
|
||||||
import { getAdmin, getLogger } from "../shared/config";
|
|
||||||
import { Request } from "firebase-functions/v2/https";
|
|
||||||
import { Response } from "express";
|
|
||||||
|
|
||||||
|
|
||||||
const corsHandler = getCorsHandler();
|
|
||||||
const admin = getAdmin();
|
|
||||||
const logger = getLogger();
|
|
||||||
|
|
||||||
export const registerClient = onRequest({
|
|
||||||
region: '#{SERVICES_RGN}#'
|
|
||||||
}, async (req: Request, res: Response) => {
|
|
||||||
return corsHandler(req, res, async () => {
|
|
||||||
try {
|
|
||||||
if (req.method !== 'POST') {
|
|
||||||
return res.status(405).json({ error: 'Method not allowed. Please use POST.' });
|
|
||||||
}
|
|
||||||
const authHeader = req.headers.authorization;
|
|
||||||
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
|
||||||
return res.status(401).json({ error: 'Unauthorized. Missing or invalid authorization header.' });
|
|
||||||
}
|
|
||||||
const idToken = authHeader.split('Bearer ')[1];
|
|
||||||
try {
|
|
||||||
const decodedToken = await admin.auth().verifyIdToken(idToken);
|
|
||||||
const uid = decodedToken.uid;
|
|
||||||
const userDoc = await admin.firestore().collection('users').doc(uid).get();
|
|
||||||
|
|
||||||
if (!userDoc.exists) {
|
|
||||||
return res.status(403).json({ error: 'Forbidden. User not found.' });
|
|
||||||
}
|
|
||||||
|
|
||||||
const userData = userDoc.data();
|
|
||||||
if (!userData || !userData.roles || !userData.roles.includes('gym_owner')) {
|
|
||||||
return res.status(403).json({ error: 'Forbidden. Only gym owners can register clients.' });
|
|
||||||
}
|
|
||||||
const gymUser = req.body;
|
|
||||||
if (!gymUser.fields["phone-number"]) {
|
|
||||||
return res.status(400).json({ error: 'Phone number is required' });
|
|
||||||
}
|
|
||||||
|
|
||||||
let clientUid;
|
|
||||||
try {
|
|
||||||
const userRecord = await admin.auth().getUserByPhoneNumber(gymUser.fields["phone-number"])
|
|
||||||
.catch(() => null);
|
|
||||||
|
|
||||||
if (userRecord) {
|
|
||||||
clientUid = userRecord.uid;
|
|
||||||
} else {
|
|
||||||
const newUser = await admin.auth().createUser({
|
|
||||||
phoneNumber: gymUser.fields["phone-number"],
|
|
||||||
displayName: gymUser.fields["first-name"] || '',
|
|
||||||
});
|
|
||||||
clientUid = newUser.uid;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
logger.error('Error creating authentication user:', error);
|
|
||||||
return res.status(500).json({
|
|
||||||
error: 'Failed to create authentication user',
|
|
||||||
details: error
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
gymUser.uid = clientUid;
|
|
||||||
gymUser.registeredBy = uid;
|
|
||||||
|
|
||||||
if (gymUser.name) {
|
|
||||||
gymUser.normalizedName = gymUser.name.toLowerCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gymUser.dateOfBirth && !(typeof gymUser.dateOfBirth === 'string')) {
|
|
||||||
gymUser.dateOfBirth = new Date(gymUser.dateOfBirth).toISOString();
|
|
||||||
}
|
|
||||||
|
|
||||||
const clientData = {
|
|
||||||
...gymUser,
|
|
||||||
};
|
|
||||||
|
|
||||||
await admin.firestore().collection('client_profiles').doc(clientUid).set(clientData);
|
|
||||||
|
|
||||||
return res.status(201).json({
|
|
||||||
success: true,
|
|
||||||
message: 'Client registered successfully',
|
|
||||||
clientId: clientUid
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
logger.error('Error creating client profile:', error);
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (!gymUser.uid) {
|
|
||||||
await admin.auth().deleteUser(clientUid);
|
|
||||||
}
|
|
||||||
} catch (deleteError) {
|
|
||||||
logger.error('Error deleting auth user after failed profile creation:', deleteError);
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.status(500).json({
|
|
||||||
error: 'Failed to create client profile',
|
|
||||||
details: error
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (authError) {
|
|
||||||
logger.error('Authentication error:', authError);
|
|
||||||
return res.status(401).json({ error: 'Unauthorized. Invalid token.' });
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
logger.error('Unexpected error in client registration:', error);
|
|
||||||
return res.status(500).json({
|
|
||||||
error: 'Internal server error',
|
|
||||||
details: error
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1 +0,0 @@
|
|||||||
export { registerClient } from './clientRegistration';
|
|
||||||
Loading…
Reference in New Issue
Block a user