275 lines
8.9 KiB
TypeScript
275 lines
8.9 KiB
TypeScript
//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
|