From 302d756ae43cefa95989770889caaee6a71c11f8 Mon Sep 17 00:00:00 2001 From: AndyOoh Date: Sun, 31 Mar 2024 15:47:38 +0700 Subject: [PATCH 1/7] new sahred types, add dep in app and fcf. new emul firestore data --- .../emulator-data/auth_export/accounts.json | 2 +- .../all_namespaces_all_kinds.export_metadata | Bin 52 -> 52 bytes .../all_namespaces/all_kinds/output-0 | Bin 7715 -> 10965 bytes .../firestore_export.overall_export_metadata | Bin 95 -> 95 bytes ...2 => 0cb38c76-a4cb-461b-aedb-92be7094f815} | 0 ...1 => 4f647ce7-707c-40b0-b8ca-d4915501a4fa} | Bin ...3 => ec4461ce-3ca2-4a68-accf-920e6a095f28} | Bin ...4 => fc476dc0-811c-42a8-9e92-4c5492287c68} | Bin ...0cb38c76-a4cb-461b-aedb-92be7094f815.json} | 0 ...4f647ce7-707c-40b0-b8ca-d4915501a4fa.json} | 0 ...ec4461ce-3ca2-4a68-accf-920e6a095f28.json} | 0 ...fc476dc0-811c-42a8-9e92-4c5492287c68.json} | 0 apps/firebase-cloud-functions/package.json | 1 + apps/firebase-cloud-functions/src/callable.ts | 112 ++++++++++++------ 14 files changed, 78 insertions(+), 37 deletions(-) rename apps/firebase-cloud-functions/emulator-data/storage_export/blobs/{b4e6106c-c5b3-43df-9a61-782a8459ade2 => 0cb38c76-a4cb-461b-aedb-92be7094f815} (100%) rename apps/firebase-cloud-functions/emulator-data/storage_export/blobs/{02405e7a-679b-4aca-876e-4c024b7395c1 => 4f647ce7-707c-40b0-b8ca-d4915501a4fa} (100%) rename apps/firebase-cloud-functions/emulator-data/storage_export/blobs/{85c00590-1dbb-4e1f-bf3e-a6978bd2c6a3 => ec4461ce-3ca2-4a68-accf-920e6a095f28} (100%) rename apps/firebase-cloud-functions/emulator-data/storage_export/blobs/{7931c88a-b8c1-4af8-a353-3aeb2bc9adc4 => fc476dc0-811c-42a8-9e92-4c5492287c68} (100%) rename apps/firebase-cloud-functions/emulator-data/storage_export/metadata/{b4e6106c-c5b3-43df-9a61-782a8459ade2.json => 0cb38c76-a4cb-461b-aedb-92be7094f815.json} (100%) rename apps/firebase-cloud-functions/emulator-data/storage_export/metadata/{02405e7a-679b-4aca-876e-4c024b7395c1.json => 4f647ce7-707c-40b0-b8ca-d4915501a4fa.json} (100%) rename apps/firebase-cloud-functions/emulator-data/storage_export/metadata/{85c00590-1dbb-4e1f-bf3e-a6978bd2c6a3.json => ec4461ce-3ca2-4a68-accf-920e6a095f28.json} (100%) rename apps/firebase-cloud-functions/emulator-data/storage_export/metadata/{7931c88a-b8c1-4af8-a353-3aeb2bc9adc4.json => fc476dc0-811c-42a8-9e92-4c5492287c68.json} (100%) diff --git a/apps/firebase-cloud-functions/emulator-data/auth_export/accounts.json b/apps/firebase-cloud-functions/emulator-data/auth_export/accounts.json index 2313078..8178b70 100644 --- a/apps/firebase-cloud-functions/emulator-data/auth_export/accounts.json +++ b/apps/firebase-cloud-functions/emulator-data/auth_export/accounts.json @@ -1 +1 @@ -{"kind":"identitytoolkit#DownloadAccountResponse","users":[{"localId":"DI4Dx3YiiDonUcaz0UiiFjdCUkAU","createdAt":"1704033339723","lastLoginAt":"1707153741167","displayName":"Orange Chicken","photoUrl":"http://localhost:9199/v0/b/event-dee-staging.appspot.com/o/users%2FDI4Dx3YiiDonUcaz0UiiFjdCUkAU%2Fimages%2Fprofile?alt=media&token=aaa55962-ac74-4b1d-8045-7c688f925cc4","customAttributes":"{\"basic_info_done\":true,\"type\":\"freelancer\"}","providerUserInfo":[{"providerId":"google.com","rawId":"6233296176568572704053954672800598602635","federatedId":"6233296176568572704053954672800598602635","displayName":"Orange Chicken","email":"orange.chicken.38@example.com","screenName":"chicken_orange"}],"validSince":"1707153003","email":"orange.chicken.38@example.com","emailVerified":true,"disabled":false,"lastRefreshAt":"2024-02-05T17:26:05.063Z"},{"localId":"XobXVTMfKX21NwEP7V5LEuuX4q8u","createdAt":"1704023132744","lastLoginAt":"1704023132744","photoUrl":"http://localhost:9199/v0/b/event-dee-staging.appspot.com/o/users%2FXobXVTMfKX21NwEP7V5LEuuX4q8u%2Fimages%2Fprofile?alt=media&token=be3f3bef-d833-4dd0-b275-11a58ec238a3","passwordHash":"fakeHash:salt=fakeSaltUu0sqgKGXdW18M52lrCJ:password=oooooo","salt":"fakeSaltUu0sqgKGXdW18M52lrCJ","passwordUpdatedAt":1707153003743,"customAttributes":"{\"basic_info_done\":true,\"type\":\"freelancer\"}","providerUserInfo":[{"providerId":"password","email":"oo@oo.oo","federatedId":"oo@oo.oo","rawId":"oo@oo.oo","photoUrl":"http://localhost:9199/v0/b/event-dee-staging.appspot.com/o/users%2FXobXVTMfKX21NwEP7V5LEuuX4q8u%2Fimages%2Fprofile?alt=media&token=be3f3bef-d833-4dd0-b275-11a58ec238a3"}],"validSince":"1707153003","email":"oo@oo.oo","emailVerified":false,"disabled":false},{"localId":"qIsokoD9bxhWt2tcFdcNpAg0DPZb","createdAt":"1704107208217","lastLoginAt":"1707153085233","photoUrl":"https://storage.cloud.google.com/event-dee-staging.appspot.com/misc/profile-photo-placeholder.jpg","passwordHash":"fakeHash:salt=fakeSalth89eFQSxrdhCytX68Nxe:password=bbbbbb","salt":"fakeSalth89eFQSxrdhCytX68Nxe","passwordUpdatedAt":1707153003743,"customAttributes":"{\"basic_info_done\":true,\"type\":\"business\"}","providerUserInfo":[{"providerId":"password","email":"bb@bb.bb","federatedId":"bb@bb.bb","rawId":"bb@bb.bb","photoUrl":"https://storage.cloud.google.com/event-dee-staging.appspot.com/misc/profile-photo-placeholder.jpg"}],"validSince":"1707153003","email":"bb@bb.bb","emailVerified":false,"disabled":false,"lastRefreshAt":"2024-02-05T17:16:54.279Z"}]} \ No newline at end of file +{"kind":"identitytoolkit#DownloadAccountResponse","users":[{"localId":"DI4Dx3YiiDonUcaz0UiiFjdCUkAU","createdAt":"1704033339723","lastLoginAt":"1707153741167","displayName":"Orange Chicken","photoUrl":"http://localhost:9199/v0/b/event-dee-staging.appspot.com/o/users%2FDI4Dx3YiiDonUcaz0UiiFjdCUkAU%2Fimages%2Fprofile?alt=media&token=aaa55962-ac74-4b1d-8045-7c688f925cc4","customAttributes":"{\"basic_info_done\":true,\"type\":\"freelancer\"}","providerUserInfo":[{"providerId":"google.com","rawId":"6233296176568572704053954672800598602635","federatedId":"6233296176568572704053954672800598602635","displayName":"Orange Chicken","email":"orange.chicken.38@example.com","screenName":"chicken_orange"}],"validSince":"1711872929","email":"orange.chicken.38@example.com","emailVerified":true,"disabled":false},{"localId":"XobXVTMfKX21NwEP7V5LEuuX4q8u","createdAt":"1704023132744","lastLoginAt":"1704023132744","photoUrl":"http://localhost:9199/v0/b/event-dee-staging.appspot.com/o/users%2FXobXVTMfKX21NwEP7V5LEuuX4q8u%2Fimages%2Fprofile?alt=media&token=be3f3bef-d833-4dd0-b275-11a58ec238a3","passwordHash":"fakeHash:salt=fakeSaltUu0sqgKGXdW18M52lrCJ:password=oooooo","salt":"fakeSaltUu0sqgKGXdW18M52lrCJ","passwordUpdatedAt":1711872929949,"customAttributes":"{\"basic_info_done\":true,\"type\":\"freelancer\"}","providerUserInfo":[{"providerId":"password","email":"oo@oo.oo","federatedId":"oo@oo.oo","rawId":"oo@oo.oo","photoUrl":"http://localhost:9199/v0/b/event-dee-staging.appspot.com/o/users%2FXobXVTMfKX21NwEP7V5LEuuX4q8u%2Fimages%2Fprofile?alt=media&token=be3f3bef-d833-4dd0-b275-11a58ec238a3"}],"validSince":"1711872929","email":"oo@oo.oo","emailVerified":false,"disabled":false},{"localId":"qIsokoD9bxhWt2tcFdcNpAg0DPZb","createdAt":"1704107208217","lastLoginAt":"1711872965495","photoUrl":"https://storage.cloud.google.com/event-dee-staging.appspot.com/misc/profile-photo-placeholder.jpg","passwordHash":"fakeHash:salt=fakeSalth89eFQSxrdhCytX68Nxe:password=bbbbbb","salt":"fakeSalth89eFQSxrdhCytX68Nxe","passwordUpdatedAt":1711872929949,"customAttributes":"{\"basic_info_done\":true,\"type\":\"business\"}","providerUserInfo":[{"providerId":"password","email":"bb@bb.bb","federatedId":"bb@bb.bb","rawId":"bb@bb.bb","photoUrl":"https://storage.cloud.google.com/event-dee-staging.appspot.com/misc/profile-photo-placeholder.jpg"}],"validSince":"1711872929","email":"bb@bb.bb","emailVerified":false,"disabled":false,"lastRefreshAt":"2024-03-31T08:35:24.874Z"}]} \ No newline at end of file diff --git a/apps/firebase-cloud-functions/emulator-data/firestore_export/all_namespaces/all_kinds/all_namespaces_all_kinds.export_metadata b/apps/firebase-cloud-functions/emulator-data/firestore_export/all_namespaces/all_kinds/all_namespaces_all_kinds.export_metadata index eb19f3f7bfa77f40f125e92c90f4b8e58f9859af..a94ac3e44d775455b3b9aad66e999ae60497c8a1 100644 GIT binary patch delta 36 pcmXppnIJ0v@BZskee+tGB_Edl$Pik001qk4w?V} delta 36 pcmXppnII}Z@4>zccc!#3OWc}x1k4cP;bIWt$S*A^C@s-7001ce4vqi- diff --git a/apps/firebase-cloud-functions/emulator-data/firestore_export/all_namespaces/all_kinds/output-0 b/apps/firebase-cloud-functions/emulator-data/firestore_export/all_namespaces/all_kinds/output-0 index ec5d8673faf5549e90d224553e5c7e48788ed8ee..dcdfb0e10915b2b622f7c82e766d9816175b8075 100644 GIT binary patch delta 2122 zcmeH}U2NM_6vu5RcI-6iTGvUI+Yj8XC232deix*%j;bq6Xd7c1s8CUyepcuwv@QQFzwhQjd5K5}YSE z^^hr3Z36%6>ibvc7G84A(({dt0yWh1_y|R3WAJ=%CtPqxT+A#@t^uxZ1%CdKgS>bz zAhrR%5+8&&*gXLWapPD^VS@zY5jTt{l^kCBjvb=mm=J=!QUF2~5h!_D;Zx5tyv{el zA#YuE72;EBK}qCrifm-6C@6WKhR^vgV6}Re9W;a0@rFq0LUp+&^6)!q`kgq|0 zO*hGoq++VNR*>no2T*_3ouv%Q@*y{2wyDc%K#XeN2a(j%)isjunlaRUJ=2-={hj#9 zME@*J5kNcGD7P44GE*K0(0dgh`+Qr#z^LyX=!V&$%9Djgk~UL+W(Hp;rYbYMVi=VmSni5P3!a(6k& zJ}XRW#SPH&Gr zj4D#NkTlf1?M0%l7j-{E63Z$PByWACr)4vP4RcFrFRgxa{u5H#a?@yS z;rhljk}E{c5nE`I%9w`7zSAq+?H5?ma1D=c@8+hmo$4!>@AQXP*Ip#k&uT{=B}wMw zeo*|)1D6HjFE=rxLO5O&(6X>O8~!)yf6%D*0FaQ|oXOj5$>bfZ?wdu~oEaxGDM(G;%B3>dUcqAW zc6M1YQ7PWk+{DbBu+*Z=w9M2L1qLl90mjLz6e1>TbEHnbC?q_&N-2mn=8$4DG&563&Yk?8TZ>iUU-zSDK!>VNt`wG?oWwI@a-6*X0#;>XCqX#8nL`Q?c`{wy2{S3?0@A_vU? delta 23 ecma!#=h>0#;>XCqXuQ=S@6ALWe-<`{#Wny{fCl>j diff --git a/apps/firebase-cloud-functions/emulator-data/storage_export/blobs/b4e6106c-c5b3-43df-9a61-782a8459ade2 b/apps/firebase-cloud-functions/emulator-data/storage_export/blobs/0cb38c76-a4cb-461b-aedb-92be7094f815 similarity index 100% rename from apps/firebase-cloud-functions/emulator-data/storage_export/blobs/b4e6106c-c5b3-43df-9a61-782a8459ade2 rename to apps/firebase-cloud-functions/emulator-data/storage_export/blobs/0cb38c76-a4cb-461b-aedb-92be7094f815 diff --git a/apps/firebase-cloud-functions/emulator-data/storage_export/blobs/02405e7a-679b-4aca-876e-4c024b7395c1 b/apps/firebase-cloud-functions/emulator-data/storage_export/blobs/4f647ce7-707c-40b0-b8ca-d4915501a4fa similarity index 100% rename from apps/firebase-cloud-functions/emulator-data/storage_export/blobs/02405e7a-679b-4aca-876e-4c024b7395c1 rename to apps/firebase-cloud-functions/emulator-data/storage_export/blobs/4f647ce7-707c-40b0-b8ca-d4915501a4fa diff --git a/apps/firebase-cloud-functions/emulator-data/storage_export/blobs/85c00590-1dbb-4e1f-bf3e-a6978bd2c6a3 b/apps/firebase-cloud-functions/emulator-data/storage_export/blobs/ec4461ce-3ca2-4a68-accf-920e6a095f28 similarity index 100% rename from apps/firebase-cloud-functions/emulator-data/storage_export/blobs/85c00590-1dbb-4e1f-bf3e-a6978bd2c6a3 rename to apps/firebase-cloud-functions/emulator-data/storage_export/blobs/ec4461ce-3ca2-4a68-accf-920e6a095f28 diff --git a/apps/firebase-cloud-functions/emulator-data/storage_export/blobs/7931c88a-b8c1-4af8-a353-3aeb2bc9adc4 b/apps/firebase-cloud-functions/emulator-data/storage_export/blobs/fc476dc0-811c-42a8-9e92-4c5492287c68 similarity index 100% rename from apps/firebase-cloud-functions/emulator-data/storage_export/blobs/7931c88a-b8c1-4af8-a353-3aeb2bc9adc4 rename to apps/firebase-cloud-functions/emulator-data/storage_export/blobs/fc476dc0-811c-42a8-9e92-4c5492287c68 diff --git a/apps/firebase-cloud-functions/emulator-data/storage_export/metadata/b4e6106c-c5b3-43df-9a61-782a8459ade2.json b/apps/firebase-cloud-functions/emulator-data/storage_export/metadata/0cb38c76-a4cb-461b-aedb-92be7094f815.json similarity index 100% rename from apps/firebase-cloud-functions/emulator-data/storage_export/metadata/b4e6106c-c5b3-43df-9a61-782a8459ade2.json rename to apps/firebase-cloud-functions/emulator-data/storage_export/metadata/0cb38c76-a4cb-461b-aedb-92be7094f815.json diff --git a/apps/firebase-cloud-functions/emulator-data/storage_export/metadata/02405e7a-679b-4aca-876e-4c024b7395c1.json b/apps/firebase-cloud-functions/emulator-data/storage_export/metadata/4f647ce7-707c-40b0-b8ca-d4915501a4fa.json similarity index 100% rename from apps/firebase-cloud-functions/emulator-data/storage_export/metadata/02405e7a-679b-4aca-876e-4c024b7395c1.json rename to apps/firebase-cloud-functions/emulator-data/storage_export/metadata/4f647ce7-707c-40b0-b8ca-d4915501a4fa.json diff --git a/apps/firebase-cloud-functions/emulator-data/storage_export/metadata/85c00590-1dbb-4e1f-bf3e-a6978bd2c6a3.json b/apps/firebase-cloud-functions/emulator-data/storage_export/metadata/ec4461ce-3ca2-4a68-accf-920e6a095f28.json similarity index 100% rename from apps/firebase-cloud-functions/emulator-data/storage_export/metadata/85c00590-1dbb-4e1f-bf3e-a6978bd2c6a3.json rename to apps/firebase-cloud-functions/emulator-data/storage_export/metadata/ec4461ce-3ca2-4a68-accf-920e6a095f28.json diff --git a/apps/firebase-cloud-functions/emulator-data/storage_export/metadata/7931c88a-b8c1-4af8-a353-3aeb2bc9adc4.json b/apps/firebase-cloud-functions/emulator-data/storage_export/metadata/fc476dc0-811c-42a8-9e92-4c5492287c68.json similarity index 100% rename from apps/firebase-cloud-functions/emulator-data/storage_export/metadata/7931c88a-b8c1-4af8-a353-3aeb2bc9adc4.json rename to apps/firebase-cloud-functions/emulator-data/storage_export/metadata/fc476dc0-811c-42a8-9e92-4c5492287c68.json diff --git a/apps/firebase-cloud-functions/package.json b/apps/firebase-cloud-functions/package.json index a22628b..08a4eb5 100644 --- a/apps/firebase-cloud-functions/package.json +++ b/apps/firebase-cloud-functions/package.json @@ -27,6 +27,7 @@ "firebase-functions": "^4.2.0" }, "devDependencies": { + "event-dee-types": "*", "firebase-functions-test": "^3.0.0", "typescript": "^4.9.0" }, diff --git a/apps/firebase-cloud-functions/src/callable.ts b/apps/firebase-cloud-functions/src/callable.ts index 06b5c2d..832d8f7 100644 --- a/apps/firebase-cloud-functions/src/callable.ts +++ b/apps/firebase-cloud-functions/src/callable.ts @@ -3,15 +3,19 @@ import { WhereFilterOp } from '@google-cloud/firestore'; import { db } from '.'; import { DocumentData } from 'firebase-admin/firestore'; -type DocData = { id: string; [key: string]: any }; +// type DocData = { id: string; [key: string]: any }; -type FetchDocsWithQueryParams = { - collectionName: string; - field?: string; - operator?: WhereFilterOp; - value?: any; - limit?: number; -}; +// type FetchDocsWithQueryParams = { +// collectionName: string; +// field?: string; +// operator?: WhereFilterOp; +// value?: any; +// limit?: number; +// orderBy?: { +// field: string; +// direction: 'asc' | 'desc'; +// }; +// }; /** * Callable @@ -21,45 +25,39 @@ type FetchDocsWithQueryParams = { * @param {WhereFilterOp} [operator] - Query operator (e.g., '==', '>', '<'). * @param {any} [value] - Value to compare in the query. * @param {number} [limit] - Number of documents to limit the result to. + * @param {object} [orderBy] - Field to order by and direction. * @returns {Promise} - Array of documents matching the query or the entire collection. */ -export const fetchDocsWithQuery = https.onCall( +export const fetchDocsWithQuery: FetchDocsWithQueryFunction = https.onCall( async ({ collectionName, field, operator, value, limit = 10, - }: FetchDocsWithQueryParams): Promise => { + orderBy, + // }: FetchDocsWithQueryParams): Promise => { + } => { try { const collectionRef = db.collection(collectionName); + let query: FirebaseFirestore.Query = collectionRef; if (field && operator && value) { - /* Fetch based on query */ - const querySnapshot = await collectionRef - .where(field, operator, value) - .limit(limit || Infinity) - .get(); - - const documents: DocData[] = []; - querySnapshot.forEach(doc => { - documents.push({ id: doc.id, ...doc.data() }); - }); - - console.log('Fetched documents:', documents); - return documents; - } else { - /* Fetch entire collection */ - const querySnapshot = await collectionRef.limit(limit).get(); - - const documents: DocData[] = []; - querySnapshot.forEach(doc => { - documents.push({ id: doc.id, ...doc.data() }); - }); - - console.log('Fetched entire collection:', documents); - return documents; + query = query.where(field, operator, value); + } + + if (orderBy) { + query = query.orderBy(orderBy.field, orderBy.direction); } + + const querySnapshot = await query.limit(limit || Infinity).get(); + const documents: DocData[] = []; + querySnapshot.forEach(doc => { + documents.push({ id: doc.id, ...doc.data() }); + }); + + console.log('Fetched documents:', documents); + return documents; } catch (error) { console.error('Error fetching documents, fetchDocsWithQuery:', error); throw new https.HttpsError('internal', 'Error fetching documents', error); @@ -67,6 +65,50 @@ export const fetchDocsWithQuery = https.onCall( } ); +// export const fetchDocsWithQuery = https.onCall( +// async ({ +// collectionName, +// field, +// operator, +// value, +// limit = 10, +// }: FetchDocsWithQueryParams): Promise => { +// try { +// const collectionRef = db.collection(collectionName); + +// if (field && operator && value) { +// /* Fetch based on query */ +// const querySnapshot = await collectionRef +// .where(field, operator, value) +// .limit(limit || Infinity) +// .get(); + +// const documents: DocData[] = []; +// querySnapshot.forEach(doc => { +// documents.push({ id: doc.id, ...doc.data() }); +// }); + +// console.log('Fetched documents:', documents); +// return documents; +// } else { +// /* Fetch entire collection */ +// const querySnapshot = await collectionRef.limit(limit).get(); + +// const documents: DocData[] = []; +// querySnapshot.forEach(doc => { +// documents.push({ id: doc.id, ...doc.data() }); +// }); + +// console.log('Fetched entire collection:', documents); +// return documents; +// } +// } catch (error) { +// console.error('Error fetching documents, fetchDocsWithQuery:', error); +// throw new https.HttpsError('internal', 'Error fetching documents', error); +// } +// } +// ); + type fetchDocByIdParams = { collectionName: string; id: string; @@ -91,5 +133,3 @@ export const fetchDocById = https.onCall( } } ); - - From 7633bddca742215da7ba62ba0ea067c0ee7580e7 Mon Sep 17 00:00:00 2001 From: AndyOoh Date: Sun, 31 Mar 2024 17:45:51 +0700 Subject: [PATCH 2/7] make getCloudFunction type safe --- apps/app/package.json | 3 +- .../(protected)/events/create/form/index.tsx | 3 +- apps/app/src/app/(protected)/events/page.tsx | 13 +++++++-- .../SignupBusinessRight/steps/Step1.tsx | 3 +- apps/app/src/firebase/clientApp.ts | 28 ++++++++++++++++--- apps/firebase-cloud-functions/src/callable.ts | 15 ++++++---- .../event-dee-types/fb-callable-functions.ts | 17 +++++++++++ packages/event-dee-types/fb-docs/index.ts | 0 packages/event-dee-types/index.ts | 1 + 9 files changed, 69 insertions(+), 14 deletions(-) create mode 100644 packages/event-dee-types/fb-callable-functions.ts create mode 100644 packages/event-dee-types/fb-docs/index.ts diff --git a/apps/app/package.json b/apps/app/package.json index bd30eeb..483dbf9 100644 --- a/apps/app/package.json +++ b/apps/app/package.json @@ -27,8 +27,9 @@ "yup": "1.3.2" }, "devDependencies": { - "@hookform/devtools": "^4.3.1", "eslint-config-custom": "*", + "event-dee-types": "*", + "@hookform/devtools": "^4.3.1", "tsconfig": "*" } } diff --git a/apps/app/src/app/(protected)/events/create/form/index.tsx b/apps/app/src/app/(protected)/events/create/form/index.tsx index 1f9efe5..b5ae5f4 100644 --- a/apps/app/src/app/(protected)/events/create/form/index.tsx +++ b/apps/app/src/app/(protected)/events/create/form/index.tsx @@ -1,7 +1,7 @@ 'use client'; import React, { useContext, useEffect } from 'react'; -import { addDoc, arrayUnion, collection, doc, increment, updateDoc } from 'firebase/firestore'; +import { addDoc, arrayUnion, collection, doc, increment, serverTimestamp, updateDoc } from 'firebase/firestore'; import { DevTool } from '@hookform/devtools'; import { useForm } from 'react-hook-form'; import { yupResolver } from '@hookform/resolvers/yup'; @@ -51,6 +51,7 @@ export const CreateEventForm = () => { ...data, event_id: eventsMetadata.currentId + 1, creatorUid: currentUser.uid, + createdAt: serverTimestamp() }); // Step 2: Get the reference to the newly created event diff --git a/apps/app/src/app/(protected)/events/page.tsx b/apps/app/src/app/(protected)/events/page.tsx index c48cd32..cbee666 100644 --- a/apps/app/src/app/(protected)/events/page.tsx +++ b/apps/app/src/app/(protected)/events/page.tsx @@ -4,12 +4,21 @@ import { getCloudFunction } from '__firebase/clientApp'; import { Events as EventComp } from './components/Events'; import { CreateEventButton } from './components/create-event-button'; import { cache } from 'react'; +import { HttpsCallable } from 'firebase/functions'; +import { DocData, FetchDocsWithQueryParams } from 'event-dee-types'; +// type FetchDocsWithQueryFunction = (params: FetchDocsWithQueryParams) => Promise; export const revalidate = 3600; // revalidate the data at most every hour const getEvents = cache(async () => { - const fetchDocsWithQuery = getCloudFunction('fetchDocsWithQuery'); - const { data } = await fetchDocsWithQuery({ collectionName: 'events' }); + const fetchDocsWithQuery = getCloudFunction('fetchDocsWithQuery'); + const data = await fetchDocsWithQuery({ + collectionName: 'events', + orderBy: { + field: 'createdAt', + direction: 'desc', + }, + }); return data; }); diff --git a/apps/app/src/app/(public)/(auth)/signup/business/SignupBusinessRight/steps/Step1.tsx b/apps/app/src/app/(public)/(auth)/signup/business/SignupBusinessRight/steps/Step1.tsx index 7f4801f..9d073cf 100644 --- a/apps/app/src/app/(public)/(auth)/signup/business/SignupBusinessRight/steps/Step1.tsx +++ b/apps/app/src/app/(public)/(auth)/signup/business/SignupBusinessRight/steps/Step1.tsx @@ -44,7 +44,8 @@ export const Step1 = () => { const onSubmit = async (data: any) => { const checkEmailExists = getCloudFunction('checkEmailExists'); - const emailExists = (await checkEmailExists(data.email)).data; + // const emailExists = (await checkEmailExists(data.email)).data; + const emailExists = (await checkEmailExists(data.email)); if (emailExists) { setError('email', { message: 'Email already exists' }); return; diff --git a/apps/app/src/firebase/clientApp.ts b/apps/app/src/firebase/clientApp.ts index 090b08d..9b230ee 100644 --- a/apps/app/src/firebase/clientApp.ts +++ b/apps/app/src/firebase/clientApp.ts @@ -10,7 +10,14 @@ import { } from 'firebase/auth'; import { connectFirestoreEmulator, getFirestore } from 'firebase/firestore'; import { connectStorageEmulator, getStorage } from 'firebase/storage'; -import { connectFunctionsEmulator, getFunctions, httpsCallable } from 'firebase/functions'; +import { + connectFunctionsEmulator, + getFunctions, + httpsCallable, + HttpsCallable, + HttpsCallableResult, +} from 'firebase/functions'; +import { DocData } from 'event-dee-types'; /* * Do NOT use window object or other browser specific objects here @@ -36,11 +43,24 @@ const auth = getAuth(app); // popupRedirectResolver: browserPopupRedirectResolver, // }); const functions = getFunctions(); -const getCloudFunction = (functionName: string) => { - const returnedFunction = httpsCallable(functions, functionName); - return returnedFunction; + +const getCloudFunction = ( + functionName: string +): ((params: Params) => Promise) => { + const callable: HttpsCallable = httpsCallable(functions, functionName); + + return async (params: Params) => { + const result = await callable(params); + return result.data; + }; }; +/* OLD */ +// const getCloudFunction = (functionName: string) => { +// const returnedFunction = httpsCallable(functions, functionName); +// return returnedFunction; +// }; + if (process.env.NEXT_PUBLIC_EMULATORS_ON === 'true' && process.env.NODE_ENV === 'development') { connectAuthEmulator(auth, 'http://localhost:9099'); connectFirestoreEmulator(db, 'localhost', 8080); diff --git a/apps/firebase-cloud-functions/src/callable.ts b/apps/firebase-cloud-functions/src/callable.ts index 832d8f7..0262b52 100644 --- a/apps/firebase-cloud-functions/src/callable.ts +++ b/apps/firebase-cloud-functions/src/callable.ts @@ -1,7 +1,12 @@ import { https } from 'firebase-functions'; -import { WhereFilterOp } from '@google-cloud/firestore'; +// import { WhereFilterOp } from '@google-cloud/firestore'; import { db } from '.'; import { DocumentData } from 'firebase-admin/firestore'; +import { + DocData, + // FetchDocsWithQueryFunction, + FetchDocsWithQueryParams, +} from 'event-dee-types'; // type DocData = { id: string; [key: string]: any }; @@ -28,7 +33,8 @@ import { DocumentData } from 'firebase-admin/firestore'; * @param {object} [orderBy] - Field to order by and direction. * @returns {Promise} - Array of documents matching the query or the entire collection. */ -export const fetchDocsWithQuery: FetchDocsWithQueryFunction = https.onCall( + +export const fetchDocsWithQuery = https.onCall( async ({ collectionName, field, @@ -36,8 +42,7 @@ export const fetchDocsWithQuery: FetchDocsWithQueryFunction = https.onCall( value, limit = 10, orderBy, - // }: FetchDocsWithQueryParams): Promise => { - } => { + }: FetchDocsWithQueryParams): Promise => { try { const collectionRef = db.collection(collectionName); let query: FirebaseFirestore.Query = collectionRef; @@ -47,7 +52,7 @@ export const fetchDocsWithQuery: FetchDocsWithQueryFunction = https.onCall( } if (orderBy) { - query = query.orderBy(orderBy.field, orderBy.direction); + query = query.orderBy(orderBy.field, orderBy.direction || 'asc'); } const querySnapshot = await query.limit(limit || Infinity).get(); diff --git a/packages/event-dee-types/fb-callable-functions.ts b/packages/event-dee-types/fb-callable-functions.ts new file mode 100644 index 0000000..335063d --- /dev/null +++ b/packages/event-dee-types/fb-callable-functions.ts @@ -0,0 +1,17 @@ +import { WhereFilterOp } from '@google-cloud/firestore'; + +export type DocData = { id: string; [key: string]: any }; + +export type FetchDocsWithQueryParams = { + collectionName: string; + field?: string; + operator?: WhereFilterOp; + value?: any; + limit?: number; + orderBy?: { + field: string; + direction?: 'asc' | 'desc'; + }; +}; + +export type FetchDocsWithQueryFunction = (params: FetchDocsWithQueryParams) => Promise; diff --git a/packages/event-dee-types/fb-docs/index.ts b/packages/event-dee-types/fb-docs/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/event-dee-types/index.ts b/packages/event-dee-types/index.ts index 27dea0d..1a8db2b 100644 --- a/packages/event-dee-types/index.ts +++ b/packages/event-dee-types/index.ts @@ -1 +1,2 @@ export * from './ui-types'; +export * from './fb-callable-functions'; From 216d1c4639a8bbf8cf082b481bc850f53624b029 Mon Sep 17 00:00:00 2001 From: AndyOoh Date: Sun, 31 Mar 2024 17:50:09 +0700 Subject: [PATCH 3/7] clean up --- apps/app/src/app/(protected)/events/page.tsx | 4 +--- apps/app/src/firebase/clientApp.ts | 22 +++++++++---------- apps/firebase-cloud-functions/src/callable.ts | 21 +----------------- .../event-dee-types/fb-callable-functions.ts | 2 -- 4 files changed, 12 insertions(+), 37 deletions(-) diff --git a/apps/app/src/app/(protected)/events/page.tsx b/apps/app/src/app/(protected)/events/page.tsx index cbee666..efd1208 100644 --- a/apps/app/src/app/(protected)/events/page.tsx +++ b/apps/app/src/app/(protected)/events/page.tsx @@ -4,10 +4,8 @@ import { getCloudFunction } from '__firebase/clientApp'; import { Events as EventComp } from './components/Events'; import { CreateEventButton } from './components/create-event-button'; import { cache } from 'react'; -import { HttpsCallable } from 'firebase/functions'; -import { DocData, FetchDocsWithQueryParams } from 'event-dee-types'; +import { FetchDocsWithQueryParams } from 'event-dee-types'; -// type FetchDocsWithQueryFunction = (params: FetchDocsWithQueryParams) => Promise; export const revalidate = 3600; // revalidate the data at most every hour const getEvents = cache(async () => { diff --git a/apps/app/src/firebase/clientApp.ts b/apps/app/src/firebase/clientApp.ts index 9b230ee..9664500 100644 --- a/apps/app/src/firebase/clientApp.ts +++ b/apps/app/src/firebase/clientApp.ts @@ -1,12 +1,12 @@ import { initializeApp, getApp, getApps, FirebaseOptions } from 'firebase/app'; import { - browserLocalPersistence, - browserPopupRedirectResolver, - browserSessionPersistence, + // browserLocalPersistence, + // browserPopupRedirectResolver, + // browserSessionPersistence, + // indexedDBLocalPersistence, + // initializeAuth, connectAuthEmulator, getAuth, - indexedDBLocalPersistence, - initializeAuth, } from 'firebase/auth'; import { connectFirestoreEmulator, getFirestore } from 'firebase/firestore'; import { connectStorageEmulator, getStorage } from 'firebase/storage'; @@ -15,7 +15,6 @@ import { getFunctions, httpsCallable, HttpsCallable, - HttpsCallableResult, } from 'firebase/functions'; import { DocData } from 'event-dee-types'; @@ -44,6 +43,11 @@ const auth = getAuth(app); // }); const functions = getFunctions(); +/* OLD */ +// const getCloudFunction = (functionName: string) => { +// const returnedFunction = httpsCallable(functions, functionName); +// return returnedFunction; +// }; const getCloudFunction = ( functionName: string ): ((params: Params) => Promise) => { @@ -55,12 +59,6 @@ const getCloudFunction = ( }; }; -/* OLD */ -// const getCloudFunction = (functionName: string) => { -// const returnedFunction = httpsCallable(functions, functionName); -// return returnedFunction; -// }; - if (process.env.NEXT_PUBLIC_EMULATORS_ON === 'true' && process.env.NODE_ENV === 'development') { connectAuthEmulator(auth, 'http://localhost:9099'); connectFirestoreEmulator(db, 'localhost', 8080); diff --git a/apps/firebase-cloud-functions/src/callable.ts b/apps/firebase-cloud-functions/src/callable.ts index 0262b52..9b069b7 100644 --- a/apps/firebase-cloud-functions/src/callable.ts +++ b/apps/firebase-cloud-functions/src/callable.ts @@ -1,26 +1,7 @@ import { https } from 'firebase-functions'; -// import { WhereFilterOp } from '@google-cloud/firestore'; import { db } from '.'; import { DocumentData } from 'firebase-admin/firestore'; -import { - DocData, - // FetchDocsWithQueryFunction, - FetchDocsWithQueryParams, -} from 'event-dee-types'; - -// type DocData = { id: string; [key: string]: any }; - -// type FetchDocsWithQueryParams = { -// collectionName: string; -// field?: string; -// operator?: WhereFilterOp; -// value?: any; -// limit?: number; -// orderBy?: { -// field: string; -// direction: 'asc' | 'desc'; -// }; -// }; +import { DocData, FetchDocsWithQueryParams } from 'event-dee-types'; /** * Callable diff --git a/packages/event-dee-types/fb-callable-functions.ts b/packages/event-dee-types/fb-callable-functions.ts index 335063d..fd5a8f6 100644 --- a/packages/event-dee-types/fb-callable-functions.ts +++ b/packages/event-dee-types/fb-callable-functions.ts @@ -13,5 +13,3 @@ export type FetchDocsWithQueryParams = { direction?: 'asc' | 'desc'; }; }; - -export type FetchDocsWithQueryFunction = (params: FetchDocsWithQueryParams) => Promise; From 98ad24af2f6fb3d4a62679e020fd064a88862fb0 Mon Sep 17 00:00:00 2001 From: AndyOoh Date: Sun, 31 Mar 2024 18:18:53 +0700 Subject: [PATCH 4/7] more clean up, restructure --- apps/app/src/firebase/clientApp.ts | 6 +- apps/firebase-cloud-functions/src/callable.ts | 66 ++++--------------- .../event-dee-types/fb-callable-functions.ts | 7 +- 3 files changed, 21 insertions(+), 58 deletions(-) diff --git a/apps/app/src/firebase/clientApp.ts b/apps/app/src/firebase/clientApp.ts index 9664500..8069393 100644 --- a/apps/app/src/firebase/clientApp.ts +++ b/apps/app/src/firebase/clientApp.ts @@ -8,7 +8,7 @@ import { connectAuthEmulator, getAuth, } from 'firebase/auth'; -import { connectFirestoreEmulator, getFirestore } from 'firebase/firestore'; +import { DocumentData, connectFirestoreEmulator, getFirestore } from 'firebase/firestore'; import { connectStorageEmulator, getStorage } from 'firebase/storage'; import { connectFunctionsEmulator, @@ -16,7 +16,7 @@ import { httpsCallable, HttpsCallable, } from 'firebase/functions'; -import { DocData } from 'event-dee-types'; +// import { DocData } from 'event-dee-types'; /* * Do NOT use window object or other browser specific objects here @@ -48,7 +48,7 @@ const functions = getFunctions(); // const returnedFunction = httpsCallable(functions, functionName); // return returnedFunction; // }; -const getCloudFunction = ( +const getCloudFunction = ( functionName: string ): ((params: Params) => Promise) => { const callable: HttpsCallable = httpsCallable(functions, functionName); diff --git a/apps/firebase-cloud-functions/src/callable.ts b/apps/firebase-cloud-functions/src/callable.ts index 9b069b7..c2e1f76 100644 --- a/apps/firebase-cloud-functions/src/callable.ts +++ b/apps/firebase-cloud-functions/src/callable.ts @@ -1,7 +1,11 @@ import { https } from 'firebase-functions'; import { db } from '.'; -import { DocumentData } from 'firebase-admin/firestore'; -import { DocData, FetchDocsWithQueryParams } from 'event-dee-types'; +import { DocumentData, Query } from 'firebase-admin/firestore'; +import { + // DocData, + FetchDocsWithQueryParams, + fetchDocByIdParams, +} from 'event-dee-types'; /** * Callable @@ -23,10 +27,11 @@ export const fetchDocsWithQuery = https.onCall( value, limit = 10, orderBy, - }: FetchDocsWithQueryParams): Promise => { + }: // }: FetchDocsWithQueryParams): Promise => { + FetchDocsWithQueryParams): Promise => { try { const collectionRef = db.collection(collectionName); - let query: FirebaseFirestore.Query = collectionRef; + let query: Query = collectionRef; if (field && operator && value) { query = query.where(field, operator, value); @@ -37,7 +42,7 @@ export const fetchDocsWithQuery = https.onCall( } const querySnapshot = await query.limit(limit || Infinity).get(); - const documents: DocData[] = []; + const documents: DocumentData[] = []; querySnapshot.forEach(doc => { documents.push({ id: doc.id, ...doc.data() }); }); @@ -51,58 +56,11 @@ export const fetchDocsWithQuery = https.onCall( } ); -// export const fetchDocsWithQuery = https.onCall( -// async ({ -// collectionName, -// field, -// operator, -// value, -// limit = 10, -// }: FetchDocsWithQueryParams): Promise => { -// try { -// const collectionRef = db.collection(collectionName); - -// if (field && operator && value) { -// /* Fetch based on query */ -// const querySnapshot = await collectionRef -// .where(field, operator, value) -// .limit(limit || Infinity) -// .get(); - -// const documents: DocData[] = []; -// querySnapshot.forEach(doc => { -// documents.push({ id: doc.id, ...doc.data() }); -// }); - -// console.log('Fetched documents:', documents); -// return documents; -// } else { -// /* Fetch entire collection */ -// const querySnapshot = await collectionRef.limit(limit).get(); - -// const documents: DocData[] = []; -// querySnapshot.forEach(doc => { -// documents.push({ id: doc.id, ...doc.data() }); -// }); - -// console.log('Fetched entire collection:', documents); -// return documents; -// } -// } catch (error) { -// console.error('Error fetching documents, fetchDocsWithQuery:', error); -// throw new https.HttpsError('internal', 'Error fetching documents', error); -// } -// } -// ); - -type fetchDocByIdParams = { - collectionName: string; - id: string; -}; - /* * Callable * Fetch a document by its ID. + * @param {string} collectionName - Name of the Firestore collection. + * @param {string} id - ID of the document to fetch. */ export const fetchDocById = https.onCall( async ({ collectionName, id }: fetchDocByIdParams): Promise => { diff --git a/packages/event-dee-types/fb-callable-functions.ts b/packages/event-dee-types/fb-callable-functions.ts index fd5a8f6..4ae5704 100644 --- a/packages/event-dee-types/fb-callable-functions.ts +++ b/packages/event-dee-types/fb-callable-functions.ts @@ -1,6 +1,6 @@ import { WhereFilterOp } from '@google-cloud/firestore'; -export type DocData = { id: string; [key: string]: any }; +// export type DocData = { id: string; [key: string]: any }; export type FetchDocsWithQueryParams = { collectionName: string; @@ -13,3 +13,8 @@ export type FetchDocsWithQueryParams = { direction?: 'asc' | 'desc'; }; }; + +export type fetchDocByIdParams = { + collectionName: string; + id: string; +}; From da1066249cee45cb0016f195198fffe33fe2a323 Mon Sep 17 00:00:00 2001 From: AndyOoh Date: Sun, 31 Mar 2024 18:47:50 +0700 Subject: [PATCH 5/7] secure type safety on all callables --- .../(protected)/events/create/form/index.tsx | 18 +++++++-- .../components/personal-info/validation.ts | 4 +- .../SignupBusinessRight/steps/Step1.tsx | 5 +-- .../SignupBusinessRight/steps/Step2.tsx | 13 +++++-- .../signup-member-right/steps/Step1.tsx | 4 +- .../signup-member-right/steps/Step3.tsx | 2 +- apps/app/src/firebase/clientApp.ts | 3 +- .../src/auth/callable.ts | 39 ++++++++++--------- apps/firebase-cloud-functions/src/callable.ts | 7 ++-- .../event-dee-types/fb-callable-functions.ts | 10 +++-- packages/event-dee-types/fb-docs/index.ts | 4 ++ packages/event-dee-types/index.ts | 1 + 12 files changed, 67 insertions(+), 43 deletions(-) diff --git a/apps/app/src/app/(protected)/events/create/form/index.tsx b/apps/app/src/app/(protected)/events/create/form/index.tsx index b5ae5f4..1a36794 100644 --- a/apps/app/src/app/(protected)/events/create/form/index.tsx +++ b/apps/app/src/app/(protected)/events/create/form/index.tsx @@ -1,7 +1,16 @@ 'use client'; import React, { useContext, useEffect } from 'react'; -import { addDoc, arrayUnion, collection, doc, increment, serverTimestamp, updateDoc } from 'firebase/firestore'; +import { + DocumentData, + addDoc, + arrayUnion, + collection, + doc, + increment, + serverTimestamp, + updateDoc, +} from 'firebase/firestore'; import { DevTool } from '@hookform/devtools'; import { useForm } from 'react-hook-form'; import { yupResolver } from '@hookform/resolvers/yup'; @@ -14,6 +23,7 @@ import { EventInfo } from './event-info'; import { IcreateEventSchema, createEventSchema } from './validation'; import { EventLocation } from './event-location'; import { EventRoles } from './event-roles'; +import { FetchDocByIdParams } from 'event-dee-types'; export const CreateEventForm = () => { const { currentUser } = useContext(CurrUserContext); @@ -41,8 +51,8 @@ export const CreateEventForm = () => { * Fetch metaData/events doc to get currentId for events. * Perhaps this can be done using const eventsDocRef = doc(db, 'metaData', 'events') ?? */ - const fetchDocById = getCloudFunction('fetchDocById'); - const { data: eventsMetadata }: any = await fetchDocById({ + const fetchDocById = getCloudFunction('fetchDocById'); + const eventsMetadata = await fetchDocById({ collectionName: 'metaData', id: 'events', }); @@ -51,7 +61,7 @@ export const CreateEventForm = () => { ...data, event_id: eventsMetadata.currentId + 1, creatorUid: currentUser.uid, - createdAt: serverTimestamp() + createdAt: serverTimestamp(), }); // Step 2: Get the reference to the newly created event diff --git a/apps/app/src/app/(protected)/profile/components/form/components/personal-info/validation.ts b/apps/app/src/app/(protected)/profile/components/form/components/personal-info/validation.ts index 74b32e9..3755af3 100644 --- a/apps/app/src/app/(protected)/profile/components/form/components/personal-info/validation.ts +++ b/apps/app/src/app/(protected)/profile/components/form/components/personal-info/validation.ts @@ -42,8 +42,8 @@ export const personalInfoSchema = ({ initialEmail }) => // const exists = await checkEmailExists(email); // return !exists; // return !(await checkEmailExists(value)); - const checkEmailExists = getCloudFunction('checkEmailExists'); // Our custom function - const emailExists = (await checkEmailExists(value)).data; + const checkEmailExists = getCloudFunction('checkEmailExists'); + const emailExists = await checkEmailExists(value); return !emailExists; }), invite_link: yup diff --git a/apps/app/src/app/(public)/(auth)/signup/business/SignupBusinessRight/steps/Step1.tsx b/apps/app/src/app/(public)/(auth)/signup/business/SignupBusinessRight/steps/Step1.tsx index 9d073cf..f3506cf 100644 --- a/apps/app/src/app/(public)/(auth)/signup/business/SignupBusinessRight/steps/Step1.tsx +++ b/apps/app/src/app/(public)/(auth)/signup/business/SignupBusinessRight/steps/Step1.tsx @@ -43,9 +43,8 @@ export const Step1 = () => { const provider = watch('provider'); const onSubmit = async (data: any) => { - const checkEmailExists = getCloudFunction('checkEmailExists'); - // const emailExists = (await checkEmailExists(data.email)).data; - const emailExists = (await checkEmailExists(data.email)); + const checkEmailExists = getCloudFunction('checkEmailExists'); + const emailExists = await checkEmailExists(data.email); if (emailExists) { setError('email', { message: 'Email already exists' }); return; diff --git a/apps/app/src/app/(public)/(auth)/signup/business/SignupBusinessRight/steps/Step2.tsx b/apps/app/src/app/(public)/(auth)/signup/business/SignupBusinessRight/steps/Step2.tsx index 5e79822..a60536f 100644 --- a/apps/app/src/app/(public)/(auth)/signup/business/SignupBusinessRight/steps/Step2.tsx +++ b/apps/app/src/app/(public)/(auth)/signup/business/SignupBusinessRight/steps/Step2.tsx @@ -15,6 +15,7 @@ import { DEFAULT_PROFILE_PHOTO_URL } from '__utils/global-consts'; import { useRouter } from 'next/navigation'; import { useState } from 'react'; import { LoaderSpinner } from '__components/ui/LoaderSpinner'; +import { CustomClaims, SetCustomClaimsParams } from 'event-dee-types'; export const Step2 = () => { const [, setWFormData] = useRecoilState(wizardForm); @@ -38,7 +39,7 @@ export const Step2 = () => { setLoading(true); const { first_name, last_name, company_name, company_type } = data; - const customClaims = { + const customClaims: CustomClaims = { basic_info_done: true, type: 'business', }; @@ -60,10 +61,14 @@ export const Step2 = () => { const userDocRef = doc(db, 'users', authUser?.uid); await updateDoc(userDocRef, userDocUpdates); - const setCustomClaims = getCloudFunction('setCustomClaims'); // Our custom function + const setCustomClaims = getCloudFunction>( + 'setCustomClaims' + ); await setCustomClaims({ - uid: authUser?.uid, - payload: customClaims, + data: { + uid: authUser?.uid, + payload: customClaims, + }, }); if (!authUser?.photoURL) { diff --git a/apps/app/src/app/(public)/(auth)/signup/freelancer/signup-member-right/steps/Step1.tsx b/apps/app/src/app/(public)/(auth)/signup/freelancer/signup-member-right/steps/Step1.tsx index 0a6ffd9..1a42fff 100644 --- a/apps/app/src/app/(public)/(auth)/signup/freelancer/signup-member-right/steps/Step1.tsx +++ b/apps/app/src/app/(public)/(auth)/signup/freelancer/signup-member-right/steps/Step1.tsx @@ -43,8 +43,8 @@ export const Step1 = () => { const provider = watch('provider'); const onSubmit = async (data: any) => { - const checkEmailExists = getCloudFunction('checkEmailExists'); - const emailExists = (await checkEmailExists(data.email)).data; + const checkEmailExists = getCloudFunction('checkEmailExists'); + const emailExists = await checkEmailExists(data.email); if (emailExists) { setError('email', { message: 'Email already exists' }); return; diff --git a/apps/app/src/app/(public)/(auth)/signup/freelancer/signup-member-right/steps/Step3.tsx b/apps/app/src/app/(public)/(auth)/signup/freelancer/signup-member-right/steps/Step3.tsx index 02d56e5..e7d454d 100644 --- a/apps/app/src/app/(public)/(auth)/signup/freelancer/signup-member-right/steps/Step3.tsx +++ b/apps/app/src/app/(public)/(auth)/signup/freelancer/signup-member-right/steps/Step3.tsx @@ -69,7 +69,7 @@ export const Step3 = () => { await updateDoc(userDocRef, userDocUpdates); - const setCustomClaims = getCloudFunction('setCustomClaims'); // Our custom function + const setCustomClaims = getCloudFunction('setCustomClaims'); await setCustomClaims({ uid: authUser?.uid, payload: customClaims, diff --git a/apps/app/src/firebase/clientApp.ts b/apps/app/src/firebase/clientApp.ts index 8069393..5b3bba4 100644 --- a/apps/app/src/firebase/clientApp.ts +++ b/apps/app/src/firebase/clientApp.ts @@ -16,7 +16,6 @@ import { httpsCallable, HttpsCallable, } from 'firebase/functions'; -// import { DocData } from 'event-dee-types'; /* * Do NOT use window object or other browser specific objects here @@ -48,7 +47,7 @@ const functions = getFunctions(); // const returnedFunction = httpsCallable(functions, functionName); // return returnedFunction; // }; -const getCloudFunction = ( +const getCloudFunction = ( functionName: string ): ((params: Params) => Promise) => { const callable: HttpsCallable = httpsCallable(functions, functionName); diff --git a/apps/firebase-cloud-functions/src/auth/callable.ts b/apps/firebase-cloud-functions/src/auth/callable.ts index 0fd7020..963eae2 100644 --- a/apps/firebase-cloud-functions/src/auth/callable.ts +++ b/apps/firebase-cloud-functions/src/auth/callable.ts @@ -1,17 +1,18 @@ import * as functions from 'firebase-functions'; import * as admin from 'firebase-admin'; import { getAuth } from 'firebase-admin/auth'; +import { SetCustomClaimsParams } from 'event-dee-types'; /* * Callable * Checks if email exists in auth */ -export const checkEmailExists = functions.https.onCall(async email => { +export const checkEmailExists = functions.https.onCall(async (email: string): Promise => { try { console.log('checkEmailExists called!'); const userRecord = await admin.auth().getUserByEmail(email); console.log('🚀 file: index.ts:40 userRecord:', userRecord); - return { emailExists: true }; + return true; } catch (error) { console.log('🚀 file: index.ts:44 error:', error); return false; @@ -23,21 +24,23 @@ export const checkEmailExists = functions.https.onCall(async email => { * Callable * Sets custom claims on auth user */ -export const setCustomClaims = functions.https.onCall(async (data, context) => { - try { - const { uid, payload } = data; - await getAuth().setCustomUserClaims(uid, payload); - // /* Lookup the user associated with the specified uid. */ - // const userNew = await getAuth() - // .getUser(uid) - // .then(userRecord => { - // // The claims can be accessed on the user record. - // console.log(userRecord?.customClaims); - // }); - // console.log('🚀 file: crud-user.ts:85 userNew:', userNew); +export const setCustomClaims = functions.https.onCall( + async ({ data, context }: SetCustomClaimsParams): Promise<{ message: string }> => { + try { + const { uid, payload } = data; + await getAuth().setCustomUserClaims(uid, payload); + // /* Lookup the user associated with the specified uid. */ + // const userNew = await getAuth() + // .getUser(uid) + // .then(userRecord => { + // // The claims can be accessed on the user record. + // console.log(userRecord?.customClaims); + // }); + // console.log('🚀 file: crud-user.ts:85 userNew:', userNew); - return { message: `Custom claims set on uid: ${data.uid}` }; - } catch (error) { - throw error; + return { message: `Custom claims set on uid: ${data.uid}` }; + } catch (error) { + throw error; + } } -}); +); diff --git a/apps/firebase-cloud-functions/src/callable.ts b/apps/firebase-cloud-functions/src/callable.ts index c2e1f76..6acc385 100644 --- a/apps/firebase-cloud-functions/src/callable.ts +++ b/apps/firebase-cloud-functions/src/callable.ts @@ -4,7 +4,7 @@ import { DocumentData, Query } from 'firebase-admin/firestore'; import { // DocData, FetchDocsWithQueryParams, - fetchDocByIdParams, + FetchDocByIdParams, } from 'event-dee-types'; /** @@ -27,8 +27,7 @@ export const fetchDocsWithQuery = https.onCall( value, limit = 10, orderBy, - }: // }: FetchDocsWithQueryParams): Promise => { - FetchDocsWithQueryParams): Promise => { + }: FetchDocsWithQueryParams): Promise => { try { const collectionRef = db.collection(collectionName); let query: Query = collectionRef; @@ -63,7 +62,7 @@ export const fetchDocsWithQuery = https.onCall( * @param {string} id - ID of the document to fetch. */ export const fetchDocById = https.onCall( - async ({ collectionName, id }: fetchDocByIdParams): Promise => { + async ({ collectionName, id }: FetchDocByIdParams): Promise => { try { const collectionRef = db.collection(collectionName); diff --git a/packages/event-dee-types/fb-callable-functions.ts b/packages/event-dee-types/fb-callable-functions.ts index 4ae5704..752be2a 100644 --- a/packages/event-dee-types/fb-callable-functions.ts +++ b/packages/event-dee-types/fb-callable-functions.ts @@ -1,6 +1,5 @@ import { WhereFilterOp } from '@google-cloud/firestore'; - -// export type DocData = { id: string; [key: string]: any }; +import { CustomClaims } from '.'; export type FetchDocsWithQueryParams = { collectionName: string; @@ -14,7 +13,12 @@ export type FetchDocsWithQueryParams = { }; }; -export type fetchDocByIdParams = { +export type FetchDocByIdParams = { collectionName: string; id: string; }; + +export type SetCustomClaimsParams = { + data: { uid: string; payload: CustomClaims }; + context?: any; // not in use, if needed find type in fb. +}; diff --git a/packages/event-dee-types/fb-docs/index.ts b/packages/event-dee-types/fb-docs/index.ts index e69de29..f0cdc82 100644 --- a/packages/event-dee-types/fb-docs/index.ts +++ b/packages/event-dee-types/fb-docs/index.ts @@ -0,0 +1,4 @@ +export type CustomClaims = { + basic_info_done: boolean; + type: string; +}; diff --git a/packages/event-dee-types/index.ts b/packages/event-dee-types/index.ts index 1a8db2b..c146632 100644 --- a/packages/event-dee-types/index.ts +++ b/packages/event-dee-types/index.ts @@ -1,2 +1,3 @@ export * from './ui-types'; export * from './fb-callable-functions'; +export * from './fb-docs'; From d7a34ebbc76f2b9da9a915e24b004737dfb731fd Mon Sep 17 00:00:00 2001 From: AndyOoh Date: Sun, 31 Mar 2024 18:51:44 +0700 Subject: [PATCH 6/7] fix function types --- .../freelancer/signup-member-right/steps/Step3.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/apps/app/src/app/(public)/(auth)/signup/freelancer/signup-member-right/steps/Step3.tsx b/apps/app/src/app/(public)/(auth)/signup/freelancer/signup-member-right/steps/Step3.tsx index e7d454d..8ac8733 100644 --- a/apps/app/src/app/(public)/(auth)/signup/freelancer/signup-member-right/steps/Step3.tsx +++ b/apps/app/src/app/(public)/(auth)/signup/freelancer/signup-member-right/steps/Step3.tsx @@ -14,6 +14,7 @@ import { ImageUpload } from '__components/ImageUpload'; import { LoaderSpinner } from '__components/ui/LoaderSpinner'; import { DEFAULT_PROFILE_PHOTO_URL } from '__utils/global-consts'; import { ActionButton } from 'ui'; +import { SetCustomClaimsParams } from 'event-dee-types'; export const Step3 = () => { const [authUser] = useAuthState(auth); @@ -69,10 +70,14 @@ export const Step3 = () => { await updateDoc(userDocRef, userDocUpdates); - const setCustomClaims = getCloudFunction('setCustomClaims'); + const setCustomClaims = getCloudFunction( + 'setCustomClaims' + ); await setCustomClaims({ - uid: authUser?.uid, - payload: customClaims, + data: { + uid: authUser?.uid, + payload: customClaims, + }, }); setWFormData(prev => ({ From 4e8a4b0adf0aff2f99d5c8669c2cf9b69e0f9714 Mon Sep 17 00:00:00 2001 From: AndyOoh Date: Sun, 31 Mar 2024 19:11:32 +0700 Subject: [PATCH 7/7] fix business email signup --- .../(auth)/signup/business/SignupBusinessRight/steps/Step1.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/app/src/app/(public)/(auth)/signup/business/SignupBusinessRight/steps/Step1.tsx b/apps/app/src/app/(public)/(auth)/signup/business/SignupBusinessRight/steps/Step1.tsx index f3506cf..3ff4d55 100644 --- a/apps/app/src/app/(public)/(auth)/signup/business/SignupBusinessRight/steps/Step1.tsx +++ b/apps/app/src/app/(public)/(auth)/signup/business/SignupBusinessRight/steps/Step1.tsx @@ -50,6 +50,8 @@ export const Step1 = () => { return; } + await createUserWithEmailAndPassword(data.email, data.new_password); + setWFormData(prev => ({ ...prev, ...data,