Skip to content

Commit

Permalink
feat(functions): update and migrate outdated firebase functions package
Browse files Browse the repository at this point in the history
  • Loading branch information
hlolli committed Dec 10, 2024
1 parent 1005f47 commit de544f9
Show file tree
Hide file tree
Showing 10 changed files with 904 additions and 651 deletions.
14 changes: 7 additions & 7 deletions functions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,17 @@
"dependencies": {
"@google-cloud/logging": "^11.2.0",
"@types/ramda": "^0.30.2",
"firebase": "^10.13.1",
"firebase-admin": "^12.4.0",
"firebase-functions": "^5.1.1",
"firebase-tools": "^13.16.0",
"firebase": "^11.0.2",
"firebase-admin": "^13.0.1",
"firebase-functions": "^6.1.2",
"firebase-tools": "^13.28.0",
"isbot": "^5.1.17",
"ramda": "^0.30.1",
"typescript": "^5.6.2"
"typescript": "^5.7.2"
},
"devDependencies": {
"eslint": "^9.10.0",
"eslint-plugin-promise": "^7.1.0",
"eslint": "^9.16.0",
"eslint-plugin-promise": "^7.2.1",
"firebase-functions-test": "^3.3.0"
},
"private": true
Expand Down
25 changes: 12 additions & 13 deletions functions/src/add_project_file_on_storage_upload.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
import admin from "firebase-admin";
import { storage } from "firebase-functions";
import { onObjectFinalized } from "firebase-functions/v2/storage";
import { makeLogger } from "./logger.js";

admin.initializeApp(); // ensure this is done somewhere in your setup
const log = makeLogger("addProjectFileOnStorageUpload");
const newTimestamp = admin.firestore.FieldValue.serverTimestamp();

const newTimestamp = admin.firestore.FieldValue.serverTimestamp;
export const addProjectFileOnStorageUploadCallback = onObjectFinalized(
async (event) => {
const obj = event.data;

// Add a project file entry in Firestore when a binary file is uploaded
export const addProjectFileOnStorageUploadCallback = storage
.object()
.onFinalize(async (obj) => {
const metadata = obj.metadata;

if (metadata) {
const { userUid, projectUid, docUid, filename } = metadata;
if (obj.metadata) {
const { userUid, projectUid, docUid, filename } = obj.metadata;

if (userUid && projectUid && docUid && filename) {
const collection = `/projects/${userUid}/${projectUid}/files`;
Expand All @@ -32,11 +30,12 @@ export const addProjectFileOnStorageUploadCallback = storage
type: "bin",
userUid,
value: "",
created: newTimestamp(),
lastModified: newTimestamp()
created: newTimestamp,
lastModified: newTimestamp
});
}
}

return true;
});
}
);
30 changes: 14 additions & 16 deletions functions/src/delete_user.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import * as admin from "firebase-admin";
import * as functions from "firebase-functions";
import functions from "firebase-functions/v1";
import { makeLogger } from "./logger.js";

const log = makeLogger("deleteUser");

admin.initializeApp();

const deleteUserDocument = async (
user: admin.auth.UserRecord
): Promise<void> => {
Expand Down Expand Up @@ -33,18 +35,16 @@ const deleteProfileDocument = async (
const deleteUsernameDocument = async (
user: admin.auth.UserRecord
): Promise<void> => {
log(
`deleteUsernameDocument: Deleting the username of: ${user.displayName}`
);
log(`deleteUsernameDocument: Deleting username of: ${user.displayName}`);
try {
const querySnapshot = await admin
.firestore()
.collection("usernames")
.where("userUid", "==", user.uid)
.get();
querySnapshot.forEach(async (doc) => {
for (const doc of querySnapshot.docs) {
await admin.firestore().doc(doc.ref.path).delete();
});
}
} catch (error) {
log(
"error: " + JSON.stringify(error, Object.getOwnPropertyNames(error))
Expand All @@ -57,7 +57,6 @@ const deleteUserProjects = async (
): Promise<void> => {
log(`deleteProjects: Deleting projects created by: ${user.displayName}`);
const batch = admin.firestore().batch();

try {
const allProjectsRef = await admin
.firestore()
Expand All @@ -70,14 +69,12 @@ const deleteUserProjects = async (
const projectRef = admin.firestore().doc(doc.ref.path);
const projectSubcolls = await projectRef.listCollections();

await Promise.all(
projectSubcolls.map(async (subcoll) => {
const subcollDocs = await subcoll.get();
subcollDocs.forEach((subcollDoc) => {
batch.delete(subcollDoc.ref);
});
})
);
for (const subcoll of projectSubcolls) {
const subcollDocs = await subcoll.get();
subcollDocs.forEach((subcollDoc) => {
batch.delete(subcollDoc.ref);
});
}

const projectLastModifiedRef = admin
.firestore()
Expand Down Expand Up @@ -119,12 +116,13 @@ export const deleteUserCallback = functions.auth
.user()
.onDelete(async (user) => {
log(
`deleteUserCallback: Removing user: ${user.displayName}, with uid ${user.uid}`
`deleteUserCallback: Removing user: ${user.displayName}, uid: ${user.uid}`
);
await deleteUserProjects(user);
await deleteProfileDocument(user);
await deleteUserDocument(user);
await deleteUsernameDocument(user);
await deleteProjectsCount(user);

return true;
});
34 changes: 22 additions & 12 deletions functions/src/followers_counter.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,40 @@
import * as admin from "firebase-admin";
import * as functions from "firebase-functions";
import { onDocumentWritten } from "firebase-functions/v2/firestore";
import { makeLogger } from "./logger.js";

admin.initializeApp();
const log = makeLogger("followersCounter");

export const followersCounter = functions.firestore
.document("followers/{userUid}")
.onWrite(async (change, context) => {
const userUid = context.params.userUid;
export const followersCounter = onDocumentWritten(
"followers/{userUid}",
async (event) => {
const before = event.data.before;
const after = event.data.after;
const userUid = event.params.userUid;

const followersCountRef = admin
.firestore()
.collection("followersCount")
.doc(userUid);

if (!change.before.exists) {
// Document created
if (!before.exists && after.exists) {
// New followers doc created = 1 new follower
await followersCountRef.set({ followersCount: 1 });
} else if (change.before.exists && change.after.exists) {
// Follower count changed
const dataAfter = change.after.data();
}
// Document updated
else if (before.exists && after.exists) {
const dataAfter = after.data();
await followersCountRef.set({
followersCount: Object.keys(dataAfter || {}).length
});
} else if (!change.after.exists) {
// No followers left, do nothing
}
// Document deleted
else if (!after.exists) {
// No followers left, do nothing (or you could remove the doc)
// await followersCountRef.delete();
}

return;
});
}
);
29 changes: 18 additions & 11 deletions functions/src/following_counter.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,37 @@
import * as admin from "firebase-admin";
import * as functions from "firebase-functions";
import { onDocumentWritten } from "firebase-functions/v2/firestore";
import { makeLogger } from "./logger.js";

admin.initializeApp();
const log = makeLogger("followersCounter");

export const followingCounter = functions.firestore
.document("following/{userUid}")
.onWrite(async (change, context) => {
const userUid = context.params.userUid;
export const followingCounter = onDocumentWritten(
"following/{userUid}",
async (event) => {
const before = event.data.before;
const after = event.data.after;
const userUid = event.params.userUid;

const followingCountRef = admin
.firestore()
.collection("followingCount")
.doc(userUid);

if (!change.before.exists) {
if (!before.exists && after.exists) {
// First time following = 1 following
await followingCountRef.set({ followingCount: 1 });
} else if (change.before.exists && change.after.exists) {
} else if (before.exists && after.exists) {
// Following count changed
const dataAfter = change.after.data();
const dataAfter = after.data();
await followingCountRef.set({
followingCount: Object.keys(dataAfter || {}).length
});
} else if (!change.after.exists) {
// Not following anyone anymore, do nothing
} else if (!after.exists) {
// No longer following anyone
// You can leave it as is, or optionally remove the doc
// await followingCountRef.delete();
}

return;
});
}
);
31 changes: 16 additions & 15 deletions functions/src/host.ts
Original file line number Diff line number Diff line change
@@ -1,47 +1,47 @@
import * as admin from "firebase-admin";
import * as functions from "firebase-functions";
import { onRequest } from "firebase-functions/v2/https";
import { isbot } from "isbot";
import fs from "node:fs";
import path from "node:path";
import * as R from "ramda";

function printTree(dirPath, indent = "") {
// Read all files and directories inside the current directory
admin.initializeApp();

function printTree(dirPath: string, indent = "") {
const files = fs.readdirSync(dirPath);

// Loop through each file/directory
files.forEach((file, index) => {
const isLast = index === files.length - 1;
for (let i = 0; i < files.length; i++) {
const file = files[i];
const isLast = i === files.length - 1;
const filePath = path.join(dirPath, file);
const stats = fs.statSync(filePath);

// Print the file or directory name
const prefix = isLast ? "└── " : "├── ";
console.log(indent + prefix + file);

// If it's a directory, recursively print its contents
if (stats.isDirectory() && file !== "node_modules") {
const newIndent = indent + (isLast ? " " : "│ ");
printTree(filePath, newIndent);
}
});
}
}

export const host = functions.https.onRequest(async (req, res) => {
export const host = onRequest(async (req, res) => {
try {
console.log("Current Working Directory:", process.cwd());
printTree("./");

let indexHTML = fs.readFileSync("./dist/index.html").toString();
const path = req.path ? req.path.split("/") : req.path;
const reqPath = req.path ? req.path.split("/") : req.path;
const ogPlaceholder = '<meta name="functions-insert-dynamic-og"/>';

if (
isbot(req.headers["user-agent"] || "") &&
path &&
path.length > 1 &&
path[1] === "editor"
reqPath &&
reqPath.length > 1 &&
reqPath[1] === "editor"
) {
const projectUid = path[2];
const projectUid = reqPath[2];

const projectSnapshot = await admin
.firestore()
Expand Down Expand Up @@ -79,6 +79,7 @@ export const host = functions.https.onRequest(async (req, res) => {
projectUid,
projectData
);

indexHTML = indexHTML.replace(
ogPlaceholder,
getProjectOg(projectWithUid, profile)
Expand Down
21 changes: 10 additions & 11 deletions functions/src/new_user.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { WriteResult } from "@google-cloud/firestore";
import { Timestamp } from "firebase-admin/firestore";
import * as admin from "firebase-admin";
import * as functions from "firebase-functions";
import functions from "firebase-functions/v1";
import { makeLogger } from "./logger.js";

admin.initializeApp();
const log = makeLogger("newUser");

// For every new user, create a queryable Firestore profile document
const createProfileDocument = async (
async function createProfileDocument(
user: admin.auth.UserRecord
): Promise<WriteResult> => {
): Promise<WriteResult> {
log(
`createProfileDocument: Adding: ${user.displayName}, with uid ${user.uid} to profiles`
`createProfileDocument: Adding: ${user.displayName}, uid: ${user.uid} to profiles`
);

const profileDoc = {
Expand Down Expand Up @@ -39,18 +39,17 @@ const createProfileDocument = async (
.collection("profiles")
.doc(user.uid)
.set(profileDoc);
};
}

export const newUserCallback = functions.auth.user().onCreate(async (user) => {
log(
`newUserCallback: Creating new user: ${user.displayName}, with uid ${user.uid}`
console.log(
`newUserCallback: Creating new user: ${user.displayName}, uid: ${user.uid}`
);

try {
await createProfileDocument(user);
} catch (error) {
log(
"error: " + JSON.stringify(error, Object.getOwnPropertyNames(error))
);
console.error("Error creating profile document:", error);
}

return true;
Expand Down
Loading

0 comments on commit de544f9

Please sign in to comment.