Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 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 | 1x 1x | import { readFileSync } from "node:fs";
import { type App, cert, getApps, initializeApp } from "firebase-admin/app";
import { getAuth } from "firebase-admin/auth";
export type AuthUserMeta = {
uid: string;
email: string | null;
emailVerified: boolean;
authProvider: string | null;
registeredAt: string | null;
lastSignInAt: string | null;
};
let cached: App | null | undefined;
/** Ленивая инициализация Admin SDK из service-account JSON. null — если не настроено/ошибка. */
function getApp(path: string | undefined): App | null {
if (cached !== undefined) return cached;
if (!path) {
cached = null;
return cached;
}
try {
const json = JSON.parse(readFileSync(path, "utf8")) as Record<string, unknown>;
cached = getApps().length > 0 ? getApps()[0]! : initializeApp({ credential: cert(json) });
} catch (err) {
console.error("firebase-admin init failed:", err);
cached = null;
}
return cached;
}
function toIso(value: string | undefined): string | null {
if (!value) return null;
const d = new Date(value);
return Number.isNaN(d.getTime()) ? null : d.toISOString();
}
/**
* Полный список пользователей Firebase Auth (постранично), либо null, если
* Admin SDK не настроен — тогда вызывающий падает на список из БД.
*/
export async function listAuthUsers(path: string | undefined): Promise<AuthUserMeta[] | null> {
const app = getApp(path);
if (!app) return null;
const auth = getAuth(app);
const out: AuthUserMeta[] = [];
let pageToken: string | undefined;
do {
const res = await auth.listUsers(1000, pageToken);
for (const u of res.users) {
out.push({
uid: u.uid,
email: u.email ?? null,
emailVerified: u.emailVerified,
authProvider: u.providerData[0]?.providerId ?? null,
registeredAt: toIso(u.metadata.creationTime),
lastSignInAt: toIso(u.metadata.lastSignInTime),
});
}
pageToken = res.pageToken;
} while (pageToken);
return out;
}
/** Помечает email подтверждённым. Возвращает false, если Admin SDK не настроен. */
export async function setEmailVerified(path: string | undefined, uid: string): Promise<boolean> {
const app = getApp(path);
if (!app) return false;
await getAuth(app).updateUser(uid, { emailVerified: true });
return true;
}
/** Удаляет пользователя из Firebase Auth. No-op, если Admin SDK не настроен или юзер уже отсутствует. */
export async function deleteAuthUser(path: string | undefined, uid: string): Promise<void> {
const app = getApp(path);
if (!app) return;
try {
await getAuth(app).deleteUser(uid);
} catch (err) {
if ((err as { code?: string })?.code === "auth/user-not-found") return;
throw err;
}
}
|