diff --git a/src/Components/DashBoard/DashBoard.tsx b/src/Components/DashBoard/DashBoard.tsx
index 9bb477d3..45293525 100644
--- a/src/Components/DashBoard/DashBoard.tsx
+++ b/src/Components/DashBoard/DashBoard.tsx
@@ -77,6 +77,18 @@ export default function DashBoard({
limit,
})
.then((data) => {
+ if (
+ !data ||
+ !data.successResults ||
+ !data.failedResults ||
+ !data.pendingResults
+ ) {
+ showSnackBar({
+ message: "データの取得に失敗しました。ログインし直してください。",
+ type: "warning",
+ });
+ return;
+ }
// 既に追加されている場合は追加しない
setSuccessResults((prev) => {
const newResults = data.successResults.filter(
@@ -213,6 +225,9 @@ export default function DashBoard({
limit: 1,
})
.then((data) => {
+ if (!data.successResults) {
+ return;
+ }
if (data.successResults.length > 0) {
setLastPostDate(data.successResults[0].post?.submittedAt);
}
diff --git a/src/Components/GoalModal/DeleteGoalModal.tsx b/src/Components/GoalModal/DeleteGoalModal.tsx
index c97635c1..4d592d4b 100644
--- a/src/Components/GoalModal/DeleteGoalModal.tsx
+++ b/src/Components/GoalModal/DeleteGoalModal.tsx
@@ -1,5 +1,6 @@
"use client";
-import { appCheckToken, functionsEndpoint } from "@/app/firebase";
+import { functionsEndpoint } from "@/app/firebase";
+import getAppCheckToken from "@/utils/getAppCheckToken";
import { useDeleteGoal } from "@/utils/ResultContext";
import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline";
import { DialogContent, DialogTitle, Modal, ModalDialog } from "@mui/joy";
@@ -14,6 +15,23 @@ export default function DeleteGoalModal({ goalId }: { goalId: string }) {
const [open, setOpen] = useState(false);
const handleDelete = async () => {
+ const appCheckToken = await getAppCheckToken().catch((error) => {
+ showSnackBar({
+ message: error.message,
+ type: "warning",
+ });
+ return "";
+ });
+
+ if (!appCheckToken) {
+ showSnackBar({
+ message:
+ "App Checkの初期化に失敗しました。debug tokenがサーバーに登録されていることを確認してください。",
+ type: "warning",
+ });
+ return;
+ }
+
try {
const response = await fetch(`${functionsEndpoint}/goal/${goalId}`, {
method: "DELETE",
diff --git a/src/Components/NameUpdate/NameUpdate.tsx b/src/Components/NameUpdate/NameUpdate.tsx
index 5365bffd..24d0b247 100644
--- a/src/Components/NameUpdate/NameUpdate.tsx
+++ b/src/Components/NameUpdate/NameUpdate.tsx
@@ -1,5 +1,6 @@
"use client";
-import { appCheckToken, auth, functionsEndpoint } from "@/app/firebase";
+import { auth, functionsEndpoint } from "@/app/firebase";
+import getAppCheckToken from "@/utils/getAppCheckToken";
import { useUser } from "@/utils/UserContext";
import {
DialogContent,
@@ -33,6 +34,23 @@ export default function NameUpdate() {
return;
}
+ const appCheckToken = await getAppCheckToken().catch((error) => {
+ showSnackBar({
+ message: error.message,
+ type: "warning",
+ });
+ return "";
+ });
+
+ if (!appCheckToken) {
+ showSnackBar({
+ message:
+ "App Checkの初期化に失敗しました。debug tokenがサーバーに登録されていることを確認してください。",
+ type: "warning",
+ });
+ return;
+ }
+
try {
const response = await fetch(
`${functionsEndpoint}/user/${user?.userId}`,
diff --git a/src/Components/PostModal/DeletePostModal.tsx b/src/Components/PostModal/DeletePostModal.tsx
index 65c99c34..e43f51c0 100644
--- a/src/Components/PostModal/DeletePostModal.tsx
+++ b/src/Components/PostModal/DeletePostModal.tsx
@@ -1,5 +1,6 @@
"use client";
-import { appCheckToken, functionsEndpoint } from "@/app/firebase";
+import { functionsEndpoint } from "@/app/firebase";
+import getAppCheckToken from "@/utils/getAppCheckToken";
import { useDeletePost } from "@/utils/ResultContext";
import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline";
import { DialogContent, DialogTitle, Modal, ModalDialog } from "@mui/joy";
@@ -20,6 +21,23 @@ export default function DeletePostModal({
const [open, setOpen] = useState(false);
const handleDelete = async () => {
+ const appCheckToken = await getAppCheckToken().catch((error) => {
+ showSnackBar({
+ message: error.message,
+ type: "warning",
+ });
+ return "";
+ });
+
+ if (!appCheckToken) {
+ showSnackBar({
+ message:
+ "App Checkの初期化に失敗しました。debug tokenがサーバーに登録されていることを確認してください。",
+ type: "warning",
+ });
+ return;
+ }
+
try {
const response = await fetch(`${functionsEndpoint}/post/${goalId}`, {
method: "DELETE",
diff --git a/src/Components/Progress/Progress.tsx b/src/Components/Progress/Progress.tsx
index daad667f..e12736ae 100644
--- a/src/Components/Progress/Progress.tsx
+++ b/src/Components/Progress/Progress.tsx
@@ -49,7 +49,7 @@ export default function Progress({
return (
);
@@ -72,7 +72,7 @@ export default function Progress({
return (
);
diff --git a/src/app/firebase.ts b/src/app/firebase.ts
index b5b0853c..4f6d803d 100644
--- a/src/app/firebase.ts
+++ b/src/app/firebase.ts
@@ -1,11 +1,5 @@
-import { showSnackBar } from "@/Components/SnackBar/SnackBar";
import { Analytics, getAnalytics } from "firebase/analytics";
import { initializeApp } from "firebase/app";
-import {
- getToken,
- initializeAppCheck,
- ReCaptchaV3Provider,
-} from "firebase/app-check";
import {
browserLocalPersistence,
connectAuthEmulator,
@@ -31,44 +25,11 @@ export const storage = getStorage(app);
export const auth = getAuth(app);
let messaging: Messaging | null = null;
let analytics: Analytics | null = null;
-let appCheckToken = "";
// クライアントサイドでのみ実行する初期化
if (typeof window !== "undefined") {
messaging = getMessaging(app);
analytics = getAnalytics(app);
-
- // 開発環境ならApp Checkを使用しない
- // ステージング環境でApp Checkのデバッグトークンを有効にする
- if (process.env.NODE_ENV !== "development") {
- if (process.env.NEXT_PUBLIC_IS_STAGING === "true") {
- (
- window as unknown as { FIREBASE_APPCHECK_DEBUG_TOKEN: boolean }
- ).FIREBASE_APPCHECK_DEBUG_TOKEN = true;
- console.log("App Check Debug Token Enabled");
- }
- // App Checkの設定
- const appCheck = initializeAppCheck(app, {
- provider: new ReCaptchaV3Provider(
- process.env.NEXT_PUBLIC_FIREBASE_RECAPTCHA_SITEKEY as string
- ),
- isTokenAutoRefreshEnabled: true,
- });
-
- getToken(appCheck)
- .then((token) => {
- console.log("App Check: Success");
- appCheckToken = token.token;
- })
- .catch((error) => {
- console.log(error.message);
- showSnackBar({
- message:
- "App Checkの初期化に失敗しました。debug tokenがサーバーに登録されていることを確認してください。",
- type: "warning",
- });
- });
- }
}
export const googleProvider = new GoogleAuthProvider();
@@ -99,5 +60,5 @@ if (process.env.NODE_ENV === "production") {
console.log("Functions: Emulator");
}
-export { analytics, appCheckToken, functionsEndpoint, messaging };
+export { analytics, functionsEndpoint, messaging };
console.log("firebaseConfig initialized");
diff --git a/src/utils/API/Goal/createGoal.ts b/src/utils/API/Goal/createGoal.ts
index d4440190..a28696c5 100644
--- a/src/utils/API/Goal/createGoal.ts
+++ b/src/utils/API/Goal/createGoal.ts
@@ -1,5 +1,6 @@
-import { appCheckToken, functionsEndpoint } from "@/app/firebase";
+import { functionsEndpoint } from "@/app/firebase";
import { Goal } from "@/types/types";
+import getAppCheckToken from "@/utils/getAppCheckToken";
/**
* Cloud FunctionsのAPIを呼び出して、目標をFirestoreに登録する
@@ -18,6 +19,8 @@ export const createGoal = async (postData: Goal) => {
throw new Error("too long comment");
}
+ const appCheckToken = await getAppCheckToken();
+
const response = await fetch(`${functionsEndpoint}/goal/`, {
method: "POST",
headers: {
@@ -61,6 +64,10 @@ export const handleCreateGoalError = (error: unknown) => {
if (error.message.includes("500")) {
snackBarMessage = "サーバーエラーが発生しました";
}
+ if (error.message.includes("App Checkの初期化に失敗しました。")) {
+ snackBarMessage =
+ "App Checkの初期化に失敗しました。debug tokenがサーバーに登録されていることを確認してください。";
+ }
} else {
console.error("An unknown error occurred");
snackBarMessage = "不明なエラーが発生しました";
diff --git a/src/utils/API/Goal/updateGoal.ts b/src/utils/API/Goal/updateGoal.ts
index d212dcec..33dc381a 100644
--- a/src/utils/API/Goal/updateGoal.ts
+++ b/src/utils/API/Goal/updateGoal.ts
@@ -1,4 +1,5 @@
-import { appCheckToken, functionsEndpoint } from "@/app/firebase";
+import { functionsEndpoint } from "@/app/firebase";
+import getAppCheckToken from "@/utils/getAppCheckToken";
/**
* Cloud FunctionsのAPIを呼び出して、目標を編集する
@@ -27,6 +28,8 @@ export const updateGoal = async (
throw new Error("too long comment");
}
+ const appCheckToken = await getAppCheckToken();
+
const response = await fetch(`${functionsEndpoint}/goal/${goalId}`, {
method: "PUT",
headers: {
@@ -70,6 +73,10 @@ export const handleUpdateGoalError = (error: unknown) => {
if (error.message.includes("500")) {
snackBarMessage = "サーバーエラーが発生しました";
}
+ if (error.message.includes("App Checkの初期化に失敗しました。")) {
+ snackBarMessage =
+ "App Checkの初期化に失敗しました。debug tokenがサーバーに登録されていることを確認してください。";
+ }
} else {
console.error("An unknown error occurred");
snackBarMessage = "不明なエラーが発生しました";
diff --git a/src/utils/API/Post/createPost.ts b/src/utils/API/Post/createPost.ts
index 77c57356..cd21d968 100644
--- a/src/utils/API/Post/createPost.ts
+++ b/src/utils/API/Post/createPost.ts
@@ -1,5 +1,6 @@
-import { appCheckToken, functionsEndpoint } from "@/app/firebase";
+import { functionsEndpoint } from "@/app/firebase";
import { PostWithGoalId } from "@/types/types";
+import getAppCheckToken from "@/utils/getAppCheckToken";
/**
* Cloud FunctionsのAPIを呼び出して、投稿をFirestoreに登録する
@@ -13,6 +14,8 @@ export const createPost = async (postData: PostWithGoalId) => {
throw new Error("too long comment");
}
+ const appCheckToken = await getAppCheckToken();
+
const response = await fetch(`${functionsEndpoint}/post/`, {
method: "POST",
headers: {
@@ -53,6 +56,10 @@ export const handleCreatePostError = (error: unknown) => {
if (error.message.includes("500")) {
snackBarMessage = "サーバーエラーが発生しました";
}
+ if (error.message.includes("App Checkの初期化に失敗しました。")) {
+ snackBarMessage =
+ "App Checkの初期化に失敗しました。debug tokenがサーバーに登録されていることを確認してください。";
+ }
} else {
console.error("An unknown error occurred");
snackBarMessage = "不明なエラーが発生しました";
diff --git a/src/utils/API/Reaction/updateReaction.ts b/src/utils/API/Reaction/updateReaction.ts
index 92e1b2f8..4b4a7f9b 100644
--- a/src/utils/API/Reaction/updateReaction.ts
+++ b/src/utils/API/Reaction/updateReaction.ts
@@ -1,5 +1,6 @@
-import { appCheckToken, functionsEndpoint } from "@/app/firebase";
+import { functionsEndpoint } from "@/app/firebase";
import { ReactionTypeMap } from "@/types/types";
+import getAppCheckToken from "@/utils/getAppCheckToken";
/**
* Cloud FunctionsのAPIを呼び出して、リアクションを更新する
@@ -15,6 +16,8 @@ export const updateReaction = async (
goalId: string,
reactionType: ReactionTypeMap | ""
) => {
+ const appCheckToken = await getAppCheckToken();
+
const response = await fetch(`${functionsEndpoint}/reaction/${goalId}`, {
method: "PUT",
headers: {
diff --git a/src/utils/API/Result/fetchResult.ts b/src/utils/API/Result/fetchResult.ts
index b04858b4..d5cf61da 100644
--- a/src/utils/API/Result/fetchResult.ts
+++ b/src/utils/API/Result/fetchResult.ts
@@ -1,4 +1,5 @@
-import { appCheckToken, functionsEndpoint } from "@/app/firebase";
+import { functionsEndpoint } from "@/app/firebase";
+import getAppCheckToken from "@/utils/getAppCheckToken";
/**
* Cloud FunctionsのAPIを呼び出して、結果の一覧を取得する
@@ -34,6 +35,8 @@ export const fetchResult = async ({
queryParams.append("limit", limit.toString());
}
+ const appCheckToken = await getAppCheckToken();
+
const response = await fetch(
`${functionsEndpoint}/result/${userId}?${queryParams.toString()}`,
{
@@ -75,6 +78,10 @@ export const handleFetchResultError = (error: unknown) => {
if (error.message.includes("500")) {
snackBarMessage = "サーバーエラーが発生しました";
}
+ if (error.message.includes("App Checkの初期化に失敗しました。")) {
+ snackBarMessage =
+ "App Checkの初期化に失敗しました。debug tokenがサーバーに登録されていることを確認してください。";
+ }
} else {
console.error("An unknown error occurred");
snackBarMessage = "不明なエラーが発生しました";
diff --git a/src/utils/API/User/fetchUser.ts b/src/utils/API/User/fetchUser.ts
index 534396dd..1d7cbdc5 100644
--- a/src/utils/API/User/fetchUser.ts
+++ b/src/utils/API/User/fetchUser.ts
@@ -1,5 +1,6 @@
-import { appCheckToken, functionsEndpoint } from "@/app/firebase";
+import { functionsEndpoint } from "@/app/firebase";
import { User } from "@/types/types";
+import getAppCheckToken from "@/utils/getAppCheckToken";
/**
* Cloud FunctionsのAPIを呼び出して、ユーザー情報をFirestoreから取得する
@@ -8,6 +9,8 @@ import { User } from "@/types/types";
* @return {*} {Promise}
*/
export const fetchUserById = async (userId: string): Promise => {
+ const appCheckToken = await getAppCheckToken();
+
const response = await fetch(`${functionsEndpoint}/user/id/${userId}`, {
method: "GET",
headers: {
@@ -45,6 +48,10 @@ export const handleFetchUserError = (error: unknown) => {
if (error.message.includes("429")) {
snackBarMessage = "リクエストが多すぎます。数分後に再度お試しください。";
}
+ if (error.message.includes("App Checkの初期化に失敗しました。")) {
+ snackBarMessage =
+ "App Checkの初期化に失敗しました。debug tokenがサーバーに登録されていることを確認してください。";
+ }
} else {
console.error("An unknown error occurred");
snackBarMessage = "不明なエラーが発生しました";
diff --git a/src/utils/Auth/signInWithGoogleAccount.ts b/src/utils/Auth/signInWithGoogleAccount.ts
index 222c5e2f..f2fb85d3 100644
--- a/src/utils/Auth/signInWithGoogleAccount.ts
+++ b/src/utils/Auth/signInWithGoogleAccount.ts
@@ -23,6 +23,11 @@ export const signInWithGoogleAccount = async () => {
"Googleアカウントでのログインに失敗しました。ページを更新して再度お試しください。";
if ((error as Error)?.message.includes("auth/popup-closed-by-user")) {
message = "ログインがキャンセルされました";
+ } else if (
+ (error as Error)?.message.includes("appCheck/fetch-status-error")
+ ) {
+ message =
+ "App Checkの初期化に失敗しました。debug tokenがサーバーに登録されていることを確認してください。";
}
showSnackBar({
message,
diff --git a/src/utils/Auth/signInWithMail.ts b/src/utils/Auth/signInWithMail.ts
index 90e56093..80662583 100644
--- a/src/utils/Auth/signInWithMail.ts
+++ b/src/utils/Auth/signInWithMail.ts
@@ -33,6 +33,9 @@ export const signInWithMail = async (email: string, password: string) => {
} else if (errorMessage.includes("auth/too-many-requests")) {
snackBarMessage =
"リクエストが多すぎます。しばらくしてからもう一度お試しください。";
+ } else if (errorMessage.includes("appCheck/fetch-status-error")) {
+ snackBarMessage =
+ "App Checkの初期化に失敗しました。debug tokenがサーバーに登録されていることを確認してください";
}
showSnackBar({
diff --git a/src/utils/Auth/signUpWithMail.tsx b/src/utils/Auth/signUpWithMail.tsx
index 87d97f9f..48a67c55 100644
--- a/src/utils/Auth/signUpWithMail.tsx
+++ b/src/utils/Auth/signUpWithMail.tsx
@@ -1,10 +1,11 @@
-import { appCheckToken, auth, functionsEndpoint } from "@/app/firebase";
+import { auth, functionsEndpoint } from "@/app/firebase";
import { showSnackBar } from "@/Components/SnackBar/SnackBar";
import {
createUserWithEmailAndPassword,
sendEmailVerification,
updateProfile,
} from "firebase/auth";
+import getAppCheckToken from "../getAppCheckToken";
import { updateUser } from "../UserContext";
/**
@@ -31,6 +32,23 @@ export const signUpWithMail = async (
console.error("Error updating user name:", profileUpdateError);
}
+ const appCheckToken = await getAppCheckToken().catch((error) => {
+ showSnackBar({
+ message: error.message,
+ type: "warning",
+ });
+ return "";
+ });
+
+ if (!appCheckToken) {
+ showSnackBar({
+ message:
+ "App Checkの初期化に失敗しました。debug tokenがサーバーに登録されていることを確認してください。",
+ type: "warning",
+ });
+ return;
+ }
+
// displayNameをFirestoreに登録
const response = await fetch(`${functionsEndpoint}/user/${user.uid}`, {
method: "PUT",
diff --git a/src/utils/CloudMessaging/notificationController.ts b/src/utils/CloudMessaging/notificationController.ts
index 56beb923..7664d4f4 100644
--- a/src/utils/CloudMessaging/notificationController.ts
+++ b/src/utils/CloudMessaging/notificationController.ts
@@ -1,6 +1,8 @@
"use client";
-import { appCheckToken, functionsEndpoint, messaging } from "@/app/firebase";
+import { functionsEndpoint, messaging } from "@/app/firebase";
+import { showSnackBar } from "@/Components/SnackBar/SnackBar";
import { getToken } from "firebase/messaging";
+import getAppCheckToken from "../getAppCheckToken";
// 通知を受信する
export async function requestPermission(userId: string): Promise {
@@ -9,6 +11,7 @@ export async function requestPermission(userId: string): Promise {
return;
}
+ // 通知の許可を取得
console.log("Requesting permission...");
const permission = await Notification.requestPermission();
if (permission !== "granted") {
@@ -16,6 +19,7 @@ export async function requestPermission(userId: string): Promise {
}
console.log("Notification permission granted.");
+ // Service Workerの登録
const registration = await navigator.serviceWorker
.register("/messaging-sw.js")
.then(() => navigator.serviceWorker.ready);
@@ -25,24 +29,43 @@ export async function requestPermission(userId: string): Promise {
throw new Error("Firebase messaging is not initialized");
}
- const currentToken = await getToken(messaging, {
+ // トークンの取得
+ const notificationToken = await getToken(messaging, {
vapidKey: process.env.NEXT_PUBLIC_FIREBASE_VAPID_KEY,
serviceWorkerRegistration: registration,
});
- if (!currentToken) {
+ if (!notificationToken) {
throw new Error("No registration token available");
}
- console.log("currentToken:", currentToken);
+ console.log("currentToken:", notificationToken);
+ const appCheckToken = await getAppCheckToken().catch((error) => {
+ showSnackBar({
+ message: error.message,
+ type: "warning",
+ });
+ return "";
+ });
+
+ if (!appCheckToken) {
+ showSnackBar({
+ message:
+ "App Checkの初期化に失敗しました。debug tokenがサーバーに登録されていることを確認してください。",
+ type: "warning",
+ });
+ return;
+ }
+
+ // トークンをFirestoreに登録
const response = await fetch(`${functionsEndpoint}/user/${userId}`, {
method: "PUT",
headers: {
"X-Firebase-AppCheck": appCheckToken,
"Content-Type": "application/json",
},
- body: JSON.stringify({ fcmToken: currentToken }),
+ body: JSON.stringify({ fcmToken: notificationToken }),
});
if (!response.ok) {
@@ -63,6 +86,7 @@ export async function revokePermission(userId: string): Promise {
const registration = await navigator.serviceWorker.ready;
const subscription = await registration.pushManager.getSubscription();
+ // 通知を解除
if (subscription) {
await subscription.unsubscribe();
const reg = await navigator.serviceWorker.getRegistration();
@@ -74,6 +98,24 @@ export async function revokePermission(userId: string): Promise {
console.log("No subscription found");
}
+ const appCheckToken = await getAppCheckToken().catch((error) => {
+ showSnackBar({
+ message: error.message,
+ type: "warning",
+ });
+ return "";
+ });
+
+ if (!appCheckToken) {
+ showSnackBar({
+ message:
+ "App Checkの初期化に失敗しました。debug tokenがサーバーに登録されていることを確認してください。",
+ type: "warning",
+ });
+ return;
+ }
+
+ // トークンをFirestoreから削除
const response = await fetch(`${functionsEndpoint}/user/${userId}`, {
method: "PUT",
headers: {
diff --git a/src/utils/getAppCheckToken.ts b/src/utils/getAppCheckToken.ts
new file mode 100644
index 00000000..981b48e8
--- /dev/null
+++ b/src/utils/getAppCheckToken.ts
@@ -0,0 +1,43 @@
+"use client";
+import { app } from "@/app/firebase";
+import {
+ getToken,
+ initializeAppCheck,
+ ReCaptchaV3Provider,
+} from "firebase/app-check";
+
+/**
+ * App Checkのトークンを生成する
+ *
+ * @export
+ * @return {*} {Promise}
+ */
+export default async function getAppCheckToken(): Promise {
+ try {
+ // 開発環境ならApp Checkを使用しない
+ if (process.env.NODE_ENV === "development") {
+ return "development";
+ }
+
+ // ステージング環境ならApp Checkのデバッグトークンを有効化
+ if (process.env.NEXT_PUBLIC_IS_STAGING === "true") {
+ (
+ window as unknown as { FIREBASE_APPCHECK_DEBUG_TOKEN: boolean }
+ ).FIREBASE_APPCHECK_DEBUG_TOKEN = true;
+ }
+
+ const appCheck = initializeAppCheck(app, {
+ provider: new ReCaptchaV3Provider(
+ process.env.NEXT_PUBLIC_FIREBASE_RECAPTCHA_SITEKEY as string
+ ),
+ isTokenAutoRefreshEnabled: true,
+ });
+
+ const token = await getToken(appCheck);
+ return token.token;
+ } catch {
+ throw new Error(
+ "App Checkの初期化に失敗しました。debug tokenがサーバーに登録されていることを確認してください。"
+ );
+ }
+}