Spaces:
Running
Running
File size: 5,862 Bytes
90f7646 0ef1453 78849f8 90f7646 55c2535 78849f8 55c2535 90f7646 55c2535 90f7646 55c2535 90f7646 55c2535 90f7646 55c2535 34e0dbf 90f7646 34e0dbf 90f7646 34e0dbf 90f7646 34e0dbf 90f7646 34e0dbf 6ec003e 90f7646 8fbd913 55c2535 e815114 c520e90 e815114 c520e90 e815114 c520e90 e815114 55c2535 91301da 55c2535 66ccfc2 55c2535 c520e90 55c2535 c520e90 55c2535 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 | import { auth, db } from "./firebase.js";
import {
signInWithEmailAndPassword,
createUserWithEmailAndPassword,
signOut,
sendPasswordResetEmail,
getRedirectResult
} from "https://www.gstatic.com/firebasejs/10.7.1/firebase-auth.js";
import {
doc,
getDoc,
setDoc,
updateDoc,
collection,
getDocs,
deleteDoc,
serverTimestamp,
query
} from "https://www.gstatic.com/firebasejs/10.7.1/firebase-firestore.js";
const INSTRUCTORS_COLLECTION = "instructors";
const SUPER_ADMIN_EMAIL = "t92206@gmail.com";
/**
* Handle Redirect Result (for OAuth flows like Google Sign In)
*/
export async function handleRedirectResult() {
try {
const result = await getRedirectResult(auth);
return result ? result.user : null;
} catch (error) {
console.error("Redirect Result Error:", error);
throw error;
}
}
/**
* Sign in with Email/Password
*/
export async function loginWithEmail(email, password) {
try {
const result = await signInWithEmailAndPassword(auth, email, password);
return result.user;
} catch (error) {
console.error("Login Error:", error);
throw error;
}
}
/**
* Register with Email/Password
* (Used for new instructors to create their auth account matching their whitelisted email)
*/
export async function registerWithEmail(email, password) {
try {
const result = await createUserWithEmailAndPassword(auth, email, password);
return result.user;
} catch (error) {
console.error("Register Error:", error);
throw error;
}
}
/**
* Send Password Reset Email
*/
export async function resetPassword(email) {
try {
await sendPasswordResetEmail(auth, email);
} catch (error) {
console.error("Reset Password Error:", error);
throw error;
}
}
/**
* Sign out
*/
export async function signOutUser() {
try {
await signOut(auth);
} catch (error) {
console.error("Sign Out Error:", error);
throw error;
}
}
/**
* Check if user is an instructor and get permissions
* Bootstraps the Super Admin if not exists
* @param {object} user - Firebase User object
* @returns {Promise<object|null>} Instructor data or null if not authorized
*/
export async function checkInstructorPermission(user) {
if (!user || !user.email) return null;
const email = user.email;
const instructorRef = doc(db, INSTRUCTORS_COLLECTION, email);
let snap;
try {
console.log(`[Permission Check] Checking whitelist for email: '${email}'`);
snap = await getDoc(instructorRef);
console.log(`[Permission Check] Result for '${email}': exists=${snap.exists()}`);
} catch (error) {
console.warn(`[Permission Check] Failed for '${email}'. Error:`, error);
console.warn("Instructor Permission Check Failed (likely not whitelisted):", error.code);
// If permission denied, it means they are not allowed to read the doc => likely not in whitelist
return null;
}
// Bootstrap Super Admin
if (email === SUPER_ADMIN_EMAIL) {
const adminData = {
name: user.displayName || "Super Admin",
email: email,
role: 'admin',
permissions: ['create_room', 'add_question', 'manage_instructors'],
lastLogin: serverTimestamp()
};
try {
if (!snap.exists()) {
await setDoc(instructorRef, {
...adminData,
createdAt: serverTimestamp()
});
} else {
// Ensure admin always has full permissions
await updateDoc(instructorRef, {
role: 'admin',
permissions: ['create_room', 'add_question', 'manage_instructors'],
lastLogin: serverTimestamp()
});
}
} catch (e) {
console.warn("Admin bootstrap failed (likely permission issues), but allowing login as admin.", e);
// We continue because we return adminData anyway, effectively granting admin rights in UI.
}
return adminData;
}
if (snap.exists()) {
const data = snap.data();
try {
await updateDoc(instructorRef, { lastLogin: serverTimestamp() });
} catch (e) {
console.warn("Failed to update lastLogin (likely permission), proceeding anyway.", e);
}
return data;
}
return null; // Not an instructor
}
/**
* Get all instructors (Admin Only)
*/
export async function getInstructors() {
const q = query(collection(db, INSTRUCTORS_COLLECTION));
const snapshot = await getDocs(q);
return snapshot.docs.map(doc => doc.data());
}
/**
* Add new instructor (Admin Only)
*/
export async function addInstructor(email, name, permissions) {
const safeEmail = email.trim(); // Ensure no leading/trailing spaces
const instructorRef = doc(db, INSTRUCTORS_COLLECTION, safeEmail);
await setDoc(instructorRef, {
email: safeEmail,
name,
role: 'instructor',
permissions,
createdAt: serverTimestamp()
});
}
/**
* Update instructor (Admin Only)
*/
export async function updateInstructor(email, data) {
const instructorRef = doc(db, INSTRUCTORS_COLLECTION, email);
await updateDoc(instructorRef, data);
}
/**
* Remove instructor (Admin Only)
*/
export async function removeInstructor(email) {
if (email === SUPER_ADMIN_EMAIL) throw new Error("Cannot remove Super Admin");
await deleteDoc(doc(db, INSTRUCTORS_COLLECTION, email));
}
|