Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Firebase initialization and update TypeScript ESLint configuration #470

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/cloud-functions/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"cjs"
],
"bundle": false,
"main": "apps/cloud-functions/src/main.ts",
"main": "apps/cloud-functions/src/index.ts",
"tsConfig": "apps/cloud-functions/tsconfig.app.json",
"assets": [
"apps/cloud-functions/src/assets"
Expand Down
1 change: 0 additions & 1 deletion apps/cloud-functions/src/main.ts

This file was deleted.

25 changes: 13 additions & 12 deletions apps/cloud-functions/src/triggers/document-publish.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import {TanamDocument} from "@tanam/domain-backend";
import * as admin from "firebase-admin";
import {getFirestore} from "firebase-admin/firestore";
import {getFunctions} from "firebase-admin/functions";
import {getStorage} from "firebase-admin/storage";
import {logger} from "firebase-functions/v2";
import {onDocumentWritten} from "firebase-functions/v2/firestore";
import {onTaskDispatched} from "firebase-functions/v2/tasks";

const db = admin.firestore();
const storage = getStorage().bucket();

// Document publish change handler
// This function is handling updates when a document is published or unpublished.
// It will ignore updates that does not change the publish status of the document.
Expand Down Expand Up @@ -73,8 +70,8 @@ export const taskPublishDocument = onTaskDispatched(
},
async (req) => {
const documentId = req.data.documentId;
const documentRef = db.collection("tanam-documents").doc(documentId);
const publicDocumentRef = db.collection("tanam-public").doc(documentId);
const documentRef = getFirestore().collection("tanam-documents").doc(documentId);
const publicDocumentRef = getFirestore().collection("tanam-public").doc(documentId);
const snap = await documentRef.get();

if (!snap.exists) {
Expand All @@ -96,10 +93,12 @@ export const taskPublishDocument = onTaskDispatched(
promises.push(publicDocumentRef.set(document.data));

// Copy associated files to public directory
const [files] = await storage.getFiles({prefix: `tanam-documents/${documentId}/`});
const [files] = await getStorage()
.bucket()
.getFiles({prefix: `tanam-documents/${documentId}/`});
for (const file of files) {
const publishedFileName = file.name.replace("tanam-documents/", "tanam-public/");
promises.push(storage.file(file.name).copy(storage.file(publishedFileName)));
promises.push(getStorage().bucket().file(file.name).copy(getStorage().bucket().file(publishedFileName)));
}

await Promise.all(promises);
Expand All @@ -122,8 +121,8 @@ export const taskUnpublishDocument = onTaskDispatched(
},
async (req) => {
const documentId = req.data.documentId;
const publicDocumentRef = db.collection("tanam-public").doc(documentId);
const documentRef = db.collection("tanam-documents").doc(documentId);
const publicDocumentRef = getFirestore().collection("tanam-public").doc(documentId);
const documentRef = getFirestore().collection("tanam-documents").doc(documentId);
const snap = await documentRef.get();

const document = TanamDocument.fromFirestore(snap);
Expand All @@ -138,9 +137,11 @@ export const taskUnpublishDocument = onTaskDispatched(
const promises = [publicDocumentRef.delete()];

// Delete associated files from public directory
const [files] = await storage.getFiles({prefix: `tanam-public/${documentId}/`});
const [files] = await getStorage()
.bucket()
.getFiles({prefix: `tanam-public/${documentId}/`});
for (const file of files) {
promises.push(storage.file(file.name).delete().then());
promises.push(getStorage().bucket().file(file.name).delete().then());
}

await Promise.all(promises);
Expand Down
32 changes: 15 additions & 17 deletions apps/cloud-functions/src/triggers/users.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,30 @@
import {TanamRole, TanamUser} from "@tanam/domain-backend";
import axios from "axios";
import * as admin from "firebase-admin";
import {getAuth} from "firebase-admin/auth";
import {getFirestore} from "firebase-admin/firestore";
import {getStorage} from "firebase-admin/storage";
import {logger} from "firebase-functions/v2";
import {onDocumentCreated, onDocumentDeleted, onDocumentUpdated} from "firebase-functions/v2/firestore";
import {onObjectFinalized} from "firebase-functions/v2/storage";
import sharp from "sharp";

const auth = admin.auth();
const db = admin.firestore();
const storage = admin.storage();

// Function to validate and assign role on document creation
// This function will scaffold and create a new user document with a role field
// and assert that all the document fields are populated.
export const tanamNewUserInit = onDocumentCreated("tanam-users/{docId}", async (event) => {
const uid = event.params.docId;
const docRef = db.collection("tanam-users").doc(uid);
const docRef = getFirestore().collection("tanam-users").doc(uid);

logger.info(`Validating User ID: ${uid}`);
try {
await auth.getUser(uid);
await getAuth().getUser(uid);
} catch (error) {
console.log("Document ID does not match any Firebase Auth UID, deleting document");
return docRef.delete();
}

const firebaseUser = await auth.getUser(uid);
const existingDocs = await db.collection("tanam-users").get();
const firebaseUser = await getAuth().getUser(uid);
const existingDocs = await getFirestore().collection("tanam-users").get();
const tanamUser = new TanamUser(
uid,
undefined,
Expand All @@ -36,15 +34,15 @@ export const tanamNewUserInit = onDocumentCreated("tanam-users/{docId}", async (
);
logger.info("Creating User", tanamUser.toJson());

const customClaimsBefore = (await auth.getUser(uid)).customClaims || {};
const customClaimsBefore = (await getAuth().getUser(uid)).customClaims || {};
const customClaimsAfter = {...customClaimsBefore, tanamRole: tanamUser.role};

logger.info(`Setting custom claims for ${uid}`, {
customClaimsBefore,
customClaimsAfter,
});

return Promise.all([auth.setCustomUserClaims(uid, customClaimsAfter), docRef.set(tanamUser.toJson())]);
return Promise.all([getAuth().setCustomUserClaims(uid, customClaimsAfter), docRef.set(tanamUser.toJson())]);
});

// Function to enforce role management on document update
Expand All @@ -69,30 +67,30 @@ export const onTanamUserRoleChange = onDocumentUpdated("tanam-users/{docId}", as
}

logger.info(`Role change detected for ${uid}.`, {before: beforeData.role, after: afterData.role});
return auth.setCustomUserClaims(uid, {tanamRole: afterData.role});
return getAuth().setCustomUserClaims(uid, {tanamRole: afterData.role});
});

// Function to remove role on document deletion
export const onTanamUserDeleted = onDocumentDeleted("tanam-users/{docId}", async (event) => {
const uid = event.params.docId;

console.log(`Document deleted: ${uid}, removing custom claims`);
const customClaims = (await auth.getUser(uid)).customClaims || {};
const customClaims = (await getAuth().getUser(uid)).customClaims || {};
customClaims.tanamRole = undefined;

logger.info(`Tanam user deleted, removing custom claims for ${uid}`, {
customClaims,
});

await auth.setCustomUserClaims(uid, customClaims);
await getAuth().setCustomUserClaims(uid, customClaims);
});

// Function to download and store user profile image
// This function will download the user's profile image from Firebase Auth
// and store it in Cloud Storage when a new user is created.
export const tanamNewUserGetImage = onDocumentCreated("tanam-users/{docId}", async (event) => {
const uid = event.params.docId;
const firebaseUser = await auth.getUser(uid);
const firebaseUser = await getAuth().getUser(uid);

const imageUrl = firebaseUser.photoURL;
if (!imageUrl) {
Expand All @@ -107,7 +105,7 @@ export const tanamNewUserGetImage = onDocumentCreated("tanam-users/{docId}", asy

// Define the file path in Cloud Storage
const filePath = `tanam-users/${uid}/new-profile-image`;
const file = storage.bucket().file(filePath);
const file = getStorage().bucket().file(filePath);

// Upload the image to Cloud Storage
await file.save(buffer, {
Expand All @@ -129,7 +127,7 @@ export const tanamNewUserGetImage = onDocumentCreated("tanam-users/{docId}", asy
export const processUserProfileImage = onObjectFinalized(async (event) => {
const filePath = event.data.name;
const contentType = event.data.contentType;
const bucket = storage.bucket(event.bucket);
const bucket = getStorage().bucket(event.bucket);
const promises = [];
if (!filePath || !contentType) {
logger.error("File path or content type is missing", {filePath, contentType});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export default function DocumentDetailsPage() {
if (document) {
setTitle(document.data.title as string);
setDescription(document.data.blurb as string);
setTags(document.data.tags as string[]);
setTags((document.data.tags ?? []) as string[]);
}

return () => {
Expand All @@ -55,7 +55,7 @@ export default function DocumentDetailsPage() {
function pruneState() {
setTitle("");
setDescription("");
setTags((document?.data.tags as string[]) ?? []);
setTags((document?.data.tags ?? []) as string[]);
}

async function fetchDocumentUpdate(title: string, blurb: string, tags: string[]) {
Expand Down
Loading
Loading