first commit
This commit is contained in:
274
functions/src/auth/registerUser.ts
Normal file
274
functions/src/auth/registerUser.ts
Normal file
@@ -0,0 +1,274 @@
|
||||
//NEEDS WORKS ON < PAST CODE >
|
||||
|
||||
import { onRequest } from 'firebase-functions/v2/https';
|
||||
import { logger } from 'firebase-functions/v2';
|
||||
import { corsMiddlewareHandler, db, FieldValue } from '../config';
|
||||
import { toErrorWithMessage } from '../utils';
|
||||
|
||||
import * as admin from 'firebase-admin';
|
||||
|
||||
// --- Function 1: registerNewUser (Admin/Role-Protected Registration) ---
|
||||
export const registerNewUserV2 = onRequest(async (request, response) => {
|
||||
return corsMiddlewareHandler(request, response, async () => {
|
||||
logger.info('registerNewUser: Function invoked via CORS Middleware.');
|
||||
|
||||
const token = request.get('Authorization')?.split('Bearer ')[1];
|
||||
if (token == null || token === '') {
|
||||
logger.warn('registerNewUser: No auth token provided.');
|
||||
response.status(401).send({
|
||||
error: 'unauthenticated',
|
||||
message: 'You must be logged in to register a user.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let decodedToken;
|
||||
try {
|
||||
decodedToken = await admin.auth().verifyIdToken(token);
|
||||
logger.info(
|
||||
`registerNewUser: Token verified for admin UID: ${decodedToken.uid}`,
|
||||
);
|
||||
} catch (error: unknown) {
|
||||
const errorMsg = toErrorWithMessage(error).message;
|
||||
logger.error(`registerNewUser: Invalid token: ${errorMsg}`);
|
||||
response.status(401).send({
|
||||
error: 'unauthenticated',
|
||||
message: 'Invalid token.',
|
||||
details: errorMsg,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch the user's role
|
||||
let userRole = 0;
|
||||
try {
|
||||
const userDoc = await db.collection('users').doc(decodedToken.uid).get();
|
||||
if (userDoc.exists) {
|
||||
userRole = userDoc.data()?.role || 0;
|
||||
logger.info(`registerNewUser: Requesting admin role is ${userRole}.`);
|
||||
} else {
|
||||
logger.warn(
|
||||
`registerNewUser: Admin user document not found for UID: ${decodedToken.uid}`,
|
||||
);
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
const errorMsg = toErrorWithMessage(error).message;
|
||||
logger.error(
|
||||
`registerNewUser: Failed to fetch admin role for UID ${decodedToken.uid}: ${errorMsg}`,
|
||||
);
|
||||
response.status(500).send({
|
||||
error: 'internal',
|
||||
message: "Failed to fetch user's role.",
|
||||
details: errorMsg,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Role Check
|
||||
const requiredAdminRole = 5;
|
||||
if (userRole < requiredAdminRole) {
|
||||
logger.warn(
|
||||
`registerNewUser: Permission denied for UID ${decodedToken.uid} with role ${userRole}.`,
|
||||
);
|
||||
response.status(403).send({
|
||||
error: 'permission-denied',
|
||||
message: 'Insufficient role level to register a new user.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
logger.info(
|
||||
`registerNewUser: Permission granted for UID ${decodedToken.uid}. Proceeding.`,
|
||||
);
|
||||
|
||||
// Proceed with user registration
|
||||
try {
|
||||
const { email, password, name, phone, membership, ...rest } =
|
||||
request.body;
|
||||
|
||||
if (!email || !password || !name) {
|
||||
logger.error(
|
||||
'registerNewUser: Missing required fields (email, password, name).',
|
||||
);
|
||||
response.status(400).send({
|
||||
error: 'missing-fields',
|
||||
message: 'Missing required fields: email, password, name.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`registerNewUser: Attempting to create user with email: ${email}`,
|
||||
);
|
||||
// Corrected: Pass user data object to createUser
|
||||
const userCred = await admin.auth().createUser({
|
||||
email: email,
|
||||
password: password,
|
||||
displayName: name,
|
||||
});
|
||||
logger.info(
|
||||
`registerNewUser: User created successfully with UID: ${userCred.uid}`,
|
||||
);
|
||||
|
||||
await db
|
||||
.collection('users')
|
||||
.doc(userCred.uid)
|
||||
.set({
|
||||
docId: userCred.uid,
|
||||
name: name,
|
||||
phone: phone || null,
|
||||
email: email,
|
||||
membership: membership || null,
|
||||
role: 1, // Default role
|
||||
isActive: true,
|
||||
created: FieldValue.serverTimestamp(),
|
||||
...rest,
|
||||
});
|
||||
logger.info(
|
||||
`registerNewUser: User data stored in Firestore for UID: ${userCred.uid}`,
|
||||
);
|
||||
|
||||
response.send({ success: true, uid: userCred.uid });
|
||||
} catch (error: unknown) {
|
||||
const errorMsg = toErrorWithMessage(error).message;
|
||||
logger.error(
|
||||
`registerNewUser: Failed during user creation/write: ${errorMsg}`,
|
||||
{ error },
|
||||
);
|
||||
if (
|
||||
error instanceof Error &&
|
||||
'code' in error &&
|
||||
error.code === 'auth/email-already-exists'
|
||||
) {
|
||||
response.status(409).send({
|
||||
error: 'email-already-exists',
|
||||
message: 'The email address is already in use by another account.',
|
||||
details: errorMsg,
|
||||
});
|
||||
} else {
|
||||
response.status(500).send({
|
||||
error: 'internal',
|
||||
message: 'Failed to register user.',
|
||||
details: errorMsg,
|
||||
});
|
||||
}
|
||||
}
|
||||
}); // End of corsMiddlewareHandler callback
|
||||
}); // End of registerNewUser onRequest
|
||||
|
||||
// --- Function 2: visitorRegister (Public Registration) ---
|
||||
export const visitorRegisterV2 = onRequest(async (request, response) => {
|
||||
return corsMiddlewareHandler(request, response, async () => {
|
||||
// Note: Add 'as any' assertion here too if needed for TS build error workaround
|
||||
// return corsMiddlewareHandler(request as any, response as any, async () => {
|
||||
|
||||
logger.info('visitorRegister: Function invoked via CORS Middleware.'); // Log updated
|
||||
|
||||
if (request.method !== 'POST') {
|
||||
logger.warn(`visitorRegister: Method not allowed: ${request.method}`);
|
||||
response.status(405).send({ message: 'Method Not Allowed.' });
|
||||
return;
|
||||
}
|
||||
|
||||
const { email, password, name, membership, uid, ...rest } = request.body;
|
||||
|
||||
if (!email || !password || !name) {
|
||||
logger.warn(
|
||||
'visitorRegister: Missing essential fields (email, password, name).',
|
||||
);
|
||||
response.status(400).send({
|
||||
error: 'missing-fields',
|
||||
message: 'Missing essential registration fields.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
logger.info(`visitorRegister: Attempting to create user: ${email}`);
|
||||
const userCred = await admin.auth().createUser({
|
||||
email: email,
|
||||
password: password,
|
||||
displayName: name,
|
||||
});
|
||||
logger.info(`visitorRegister: User created with UID: ${userCred.uid}`);
|
||||
|
||||
if (uid && typeof uid === 'string') {
|
||||
logger.info(
|
||||
`visitorRegister: Attempting to link phone number from temp UID: ${uid}`,
|
||||
);
|
||||
try {
|
||||
const phoneUser = await admin.auth().getUser(uid);
|
||||
logger.info(
|
||||
`visitorRegister: Fetched phone user details for temp UID ${uid}. Phone: ${phoneUser.phoneNumber}`,
|
||||
);
|
||||
if (phoneUser.phoneNumber) {
|
||||
await admin.auth().updateUser(userCred.uid, {
|
||||
phoneNumber: phoneUser.phoneNumber,
|
||||
});
|
||||
logger.info(
|
||||
`visitorRegister: Phone number ${phoneUser.phoneNumber} linked to new user UID: ${userCred.uid}`,
|
||||
);
|
||||
} else {
|
||||
logger.warn(
|
||||
`visitorRegister: Temp user UID ${uid} exists but has no phone number.`,
|
||||
);
|
||||
}
|
||||
await admin.auth().deleteUser(uid);
|
||||
logger.info(
|
||||
`visitorRegister: Deleted temporary phone auth user UID: ${uid}`,
|
||||
);
|
||||
} catch (linkError: unknown) {
|
||||
const linkErrorMsg = toErrorWithMessage(linkError).message;
|
||||
logger.error(
|
||||
`visitorRegister: Error during phone linking/deletion for temp UID ${uid} (non-fatal): ${linkErrorMsg}`,
|
||||
{ linkError },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`visitorRegister: Storing user data in Firestore for UID: ${userCred.uid}`,
|
||||
);
|
||||
await db
|
||||
.collection('users')
|
||||
.doc(userCred.uid)
|
||||
.set({
|
||||
docId: userCred.uid,
|
||||
name: name,
|
||||
email: email,
|
||||
membership: membership || null,
|
||||
role: 1,
|
||||
isActive: true,
|
||||
created: FieldValue.serverTimestamp(),
|
||||
...rest,
|
||||
});
|
||||
logger.info(
|
||||
`visitorRegister: Stored user data in Firestore for UID: ${userCred.uid}`,
|
||||
);
|
||||
|
||||
response.send({ success: true, uid: userCred.uid });
|
||||
} catch (error: unknown) {
|
||||
const errorMsg = toErrorWithMessage(error).message;
|
||||
logger.error(
|
||||
`visitorRegister: Failed during registration for ${email}: ${errorMsg}`,
|
||||
{ error },
|
||||
);
|
||||
if (
|
||||
error instanceof Error &&
|
||||
'code' in error &&
|
||||
error.code === 'auth/email-already-exists'
|
||||
) {
|
||||
response.status(409).send({
|
||||
error: 'email-already-exists',
|
||||
message: 'The email address is already in use by another account.',
|
||||
details: errorMsg,
|
||||
});
|
||||
} else {
|
||||
response.status(500).send({
|
||||
error: 'registration-failed',
|
||||
message: 'Failed to register the user.',
|
||||
details: errorMsg,
|
||||
});
|
||||
}
|
||||
}
|
||||
}); // End of corsMiddlewareHandler callback
|
||||
}); // End of visitorRegister onRequest
|
||||
Reference in New Issue
Block a user