Skip to content

Commit

Permalink
Merge pull request #169 from MurakawaTakuya/feat/168-logger-header-re…
Browse files Browse the repository at this point in the history
…quest

loggerにリクエストの詳細を記録
  • Loading branch information
MurakawaTakuya authored Jan 20, 2025
2 parents 339bfb7 + 105ddef commit 7f5f575
Show file tree
Hide file tree
Showing 9 changed files with 318 additions and 75 deletions.
102 changes: 67 additions & 35 deletions functions/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import cors from "cors";
import express from "express";
import express, { Request } from "express";
import { rateLimit } from "express-rate-limit";
import admin from "firebase-admin";
import { logger } from "firebase-functions";
Expand All @@ -21,7 +21,7 @@ import resultRouter from "./routers/resultRouter";
import userRouer from "./routers/userRouter";

const app = express();
app.set("trust proxy", true); // trueにしないと全ユーザーでlimitが共通になる
app.set("trust proxy", false);
app.use(helmet());
app.use(cors());
app.use(express.json());
Expand All @@ -41,13 +41,15 @@ const verifyAppCheckToken = async (
}

try {
const decodedToken = await admin
.appCheck()
.verifyToken(appCheckToken as string);
console.log("Verified App Check Token:", decodedToken);
await admin.appCheck().verifyToken(appCheckToken as string);
return next();
} catch (error) {
logger.error(`Invalid App Check token with ${appCheckToken}:`, error);
logger.error({
message: `Invalid App Check token with ${appCheckToken}:`,
error,
httpRequest: getHttpRequestData(req),
requestLog: getRequestData(req),
});
return res.status(401).send("Invalid App Check token.");
}
};
Expand All @@ -69,10 +71,10 @@ app.use(
rateLimit({
windowMs: 10 * 60 * 1000,
max: 200,
// keyGenerator: (req) => {
// const key = req.headers["x-forwarded-for"] || req.ip || "unknown";
// return Array.isArray(key) ? key[0] : key;
// },
keyGenerator: (req) => {
const key = req.headers["x-forwarded-for"] || req.ip || "unknown";
return Array.isArray(key) ? key[0] : key;
},
handler: (req, res) => {
return res
.status(429)
Expand All @@ -85,10 +87,10 @@ app.use(
rateLimit({
windowMs: 60 * 60 * 1000,
max: 500,
// keyGenerator: (req) => {
// const key = req.headers["x-forwarded-for"] || req.ip || "unknown";
// return Array.isArray(key) ? key[0] : key;
// },
keyGenerator: (req) => {
const key = req.headers["x-forwarded-for"] || req.ip || "unknown";
return Array.isArray(key) ? key[0] : key;
},
handler: (req, res) => {
return res
.status(429)
Expand Down Expand Up @@ -117,27 +119,28 @@ export {
} from "./tasks";

// テスト用API
app.use(
"/helloWorld",
rateLimit({
// 10分に最大10回に制限
windowMs: 10 * 60 * 1000,
max: 10,
keyGenerator: (req) => {
const key = req.headers["x-forwarded-for"] || req.ip || "unknown";
return Array.isArray(key) ? key[0] : key;
},
handler: (req, res) => {
return res
.status(429)
.json({ message: "Too many requests, please try again later." });
},
})
);
const helloWorldRateLimiter = rateLimit({
windowMs: 10 * 60 * 1000, // 10分
max: 10, // 最大10回
keyGenerator: (req) => {
const key = req.headers["x-forwarded-for"] || req.ip || "unknown";
return Array.isArray(key) ? key[0] : key;
},
handler: (req, res) => {
return res
.status(429)
.json({ message: "Too many requests, please try again later." });
},
});

export const helloWorld = onRequest({ region: region }, (req, res) => {
logger.info("Hello log!", { structuredData: true });
res.send("Hello World!");
export const helloWorld = onRequest({ region: region }, async (req, res) => {
helloWorldRateLimiter(req, res, async () => {
logger.info({
httpRequest: getHttpRequestData(req),
requestLog: getRequestData(req),
});
res.send("Hello World!");
});
});

const db = admin.firestore();
Expand Down Expand Up @@ -169,3 +172,32 @@ export const beforecreated = beforeUserCreated(
return;
}
);

// リクエストのheader, parameters, bodyを取得
export const getRequestData = (req: Request) => {
// requestLog
return {
headers: req.headers,
parameters: {
query: req.query,
params: req.params,
},
body: req.body,
};
};

// リクエストのhttp情報を取得
export const getHttpRequestData = (req: Request) => {
// httpRequest
const statusCode = req.statusCode || "202";
return {
requestMethod: req.method,
requestUrl: `${req.method} ${statusCode}: ${req.protocol}://${req.get(
"host"
)}${req.originalUrl}`,
status: statusCode,
userAgent: req.get("User-Agent"),
remoteIp: req.ip,
protocol: req.httpVersion ? `HTTP/${req.httpVersion}` : req.protocol,
};
};
89 changes: 79 additions & 10 deletions functions/src/routers/goalRouter.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import express, { Request, Response } from "express";
import admin from "firebase-admin";
import { logger } from "firebase-functions";
import { getHttpRequestData, getRequestData } from "..";
import { Goal, GoalWithId } from "../types";

const router = express.Router();
Expand All @@ -9,6 +10,10 @@ const db = admin.firestore();
// GET: 全ての目標を取得
router.get("/", async (req: Request, res: Response) => {
try {
logger.info({
httpRequest: getHttpRequestData(req),
requestLog: getRequestData(req),
});
const goalSnapshot = await db.collection("goal").get();

if (goalSnapshot.empty) {
Expand All @@ -29,23 +34,33 @@ router.get("/", async (req: Request, res: Response) => {

return res.json(goalData);
} catch (error) {
logger.error(error);
logger.error({
error,
httpRequest: getHttpRequestData(req),
requestLog: getRequestData(req),
});
return res.status(500).json({ message: "Error fetching goals" });
}
});

// GET: userIdから目標を取得
router.get("/:userId", async (req: Request, res: Response) => {
const userId = req.params.userId;

try {
const userId = req.params.userId;
logger.info({
httpRequest: getHttpRequestData(req),
requestLog: getRequestData(req),
});

const goalSnapshot = await db
.collection("goal")
.where("userId", "==", userId)
.get();

if (goalSnapshot.empty) {
return res.status(404).json({ message: "No goals found for this user" });
return res
.status(404)
.json({ message: "No goals found for this user", userId });
}

const goalData: GoalWithId[] = goalSnapshot.docs.map((doc) => {
Expand All @@ -62,7 +77,11 @@ router.get("/:userId", async (req: Request, res: Response) => {

return res.json(goalData);
} catch (error) {
logger.error(error);
logger.error({
error,
httpRequest: getHttpRequestData(req),
requestLog: getRequestData(req),
});
return res.status(500).json({ message: "Error fetching goals" });
}
});
Expand All @@ -75,8 +94,16 @@ router.post("/", async (req: Request, res: Response) => {

try {
({ userId, deadline, text } = req.body as Goal);
logger.info({
httpRequest: getHttpRequestData(req),
requestLog: getRequestData(req),
});
} catch (error) {
logger.error(error);
logger.error({
error,
httpRequest: getHttpRequestData(req),
requestLog: getRequestData(req),
});
return res.status(400).json({ message: "Invalid request body" });
}

Expand All @@ -99,18 +126,32 @@ router.post("/", async (req: Request, res: Response) => {
post: null,
});

logger.info({
message: "Goal created successfully",
goalId,
httpRequest: getHttpRequestData(req),
requestLog: getRequestData(req),
});
return res
.status(201)
.json({ message: "Goal created successfully", goalId });
} catch (error) {
logger.error(error);
logger.error({
error,
httpRequest: getHttpRequestData(req),
requestLog: getRequestData(req),
});
return res.status(500).json({ message: "Error creating goal" });
}
});

// PUT: 目標を更新
router.put("/:goalId", async (req: Request, res: Response) => {
const goalId = req.params.goalId;
logger.info({
httpRequest: getHttpRequestData(req),
requestLog: getRequestData(req),
});
const { userId, deadline, text }: Partial<Goal> = req.body;

if (!goalId) {
Expand Down Expand Up @@ -140,9 +181,19 @@ router.put("/:goalId", async (req: Request, res: Response) => {

try {
await db.collection("goal").doc(goalId).update(updateData);
logger.info({
message: "Goal updated successfully",
goalId,
httpRequest: getHttpRequestData(req),
requestLog: getRequestData(req),
});
return res.json({ message: "Goal updated successfully", goalId });
} catch (error) {
logger.error(error);
logger.error({
error,
httpRequest: getHttpRequestData(req),
requestLog: getRequestData(req),
});
return res.status(500).json({ message: "Error updating goal" });
}
});
Expand All @@ -151,6 +202,10 @@ router.put("/:goalId", async (req: Request, res: Response) => {
router.delete("/:goalId", async (req: Request, res: Response) => {
try {
const goalId = req.params.goalId;
logger.info({
httpRequest: getHttpRequestData(req),
requestLog: getRequestData(req),
});

if (!goalId) {
return res.status(400).json({ message: "goalId is required" });
Expand All @@ -172,15 +227,29 @@ router.delete("/:goalId", async (req: Request, res: Response) => {
await file.delete();
logger.info("Image deleted successfully:", storedId);
} catch (error) {
logger.error("Error deleting image:", error);
logger.error({
error: `Error deleting image: ${error}`,
httpRequest: getHttpRequestData(req),
requestLog: getRequestData(req),
});
return res.status(500).json({ message: "Error deleting image" });
}
}

await goalRef.delete();
logger.info({
message: "Goal deleted successfully",
goalId,
httpRequest: getHttpRequestData(req),
requestLog: getRequestData(req),
});
return res.json({ message: "Goal deleted successfully", goalId });
} catch (error) {
logger.error(error);
logger.error({
error,
httpRequest: getHttpRequestData(req),
requestLog: getRequestData(req),
});
return res.status(500).json({ message: "Error deleting goal" });
}
});
Expand Down
Loading

0 comments on commit 7f5f575

Please sign in to comment.