Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Dashboardデザイン実装 #77

Merged
merged 13 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .stylelintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
"extends": ["stylelint-config-standard"],
"plugins": ["stylelint-order"],
"rules": {
"order/properties-alphabetical-order": true
"order/properties-alphabetical-order": true,
"selector-class-pattern": null,
"block-no-empty": null
},
"ignoreFiles": "node_modules/**/*"
}
109 changes: 77 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,15 +110,8 @@ API is provided by Firebase Cloud Functions. Database is provided by Firestore.
```
UserId is hash string generated by Firebase and saved as its document id.

When a user with the same name already exists, the response will be
```json
{
"message":"A user with the same user name 'testUser' already exists"
}
```

### Get User Data
#### Get User Data by User Id
### Get User
#### Get User by User Id
- URL: /user/id/:userId
- Method: GET
- Response
Expand All @@ -130,11 +123,11 @@ API is provided by Firebase Cloud Functions. Database is provided by Firestore.
}
```

#### Get User Data by User Name
#### Get User by User Name
- URL: /user/name/:userName
- Method: GET
- Response
the same as Get User Data by User Id
the same as Get User by User Id

## Goals
### Create Goal
Expand Down Expand Up @@ -169,12 +162,15 @@ the same as Get User Data by User Id
- Response
```json
[
{
"id":"RXlHJiv3GtpzSDHhfljS",
"userId":"IK0Zc2hoUYaYjXoqzmCl",
"deadline":"2024-12-31T23:59:59.000Z",
"text":"数学の勉強する"
}
{
"id": "AnaGg7GVwsXzJqSJdqGg",
"userId": "IK0Zc2hoUYaYjXoqzmCl",
"deadline": {
"_seconds": 1732321560,
"_nanoseconds": 0
},
"text": "数学の勉強する"
}
]
```

Expand All @@ -186,15 +182,21 @@ the same as Get User Data by User Id
- Headers
- Content-Type: multipart/form-data
- Body (form-data)
- userId: string (投稿者のユーザーID)
- storeId: string (画像のストレージパス、/post/{storeId}/image)
- text: string (投稿内容のテキスト)
- goalId: string (関連するゴールのID)
- userId: string
- storedId: string (画像のストレージパス、/post/{storedId}/image)
- text: string
- goalId: string
- submittedAt: Date
- Example
- userId: "IK0Zc2hoUYaYjXoqzmCl"
- storeId: "generatedStoreHash"
- text: "今日は勉強をがんばった"
- goalId: "RXlHJiv3GtpzSDHhfljS"
```json
{
"userId": "IK0Zc2hoUYaYjXoqzmCl",
"storedId": "DF48XfTFc42l6C58lLDq",
"text": "今日は勉強をがんばった",
"goalId": "RXlHJiv3GtpzSDHhfljS",
"submittedAt": "2024-12-31T23:59:59.000Z"
}
```

- Response
```json
Expand All @@ -211,12 +213,55 @@ the same as Get User Data by User Id
- Response
```json
[
{
"id":"generatedPostId",
"userId":"IK0Zc2hoUYaYjXoqzmCl",
"storeId": "hoge hoge hash",
"text":"今日は勉強をがんばった",
"goalId":"RXlHJiv3GtpzSDHhfljS",
}
{
"id": "9fgWJA6wMN54EkxIC2WD",
"userId": "IK0Zc2hoUYaYjXoqzmCl",
"storedId": "t8eo1wEE7AT12eZo3dKA",
"text": "今日は勉強をがんばった",
"goalId": "RXlHJiv3GtpzSDHhfljS",
"submittedAt": "2024-12-31T23:59:59.000Z"
}
]
```

## Result
### Get Result
- URL: /result/:userId
- Empty userId will return all results.
- Parameters
- limit?: number - The maximum number of results to return.(Default is 50)
- offset?: number - The number of results to skip before starting to collect the result set.
- Method: GET
- Response
```json
{
"successResults": [
{
"userId": "Vlx6GCtq90ag3lxgh0pcCKGp5ba0",
"goalId": "DESiyiEIHFDpuCjo08Si",
"postId": "5KffB5x2SykU6lY9sHGB",
"goalText": "数学の勉強する",
"postText": "数学の勉強したよ^^",
"storedId": "ointtq9NT5TPgEnKS4tb",
"deadline": "2025-01-31T23:59:59.000Z",
"submittedAt": "2024-12-30T23:59:59.000Z"
}
],
"failedResults": [
{
"goalId": "cl2wtpf5RufkCL2N8s98",
"userId": "Vlx6GCtq90ag3lxgh0pcCKGp5ba0",
"deadline": "2024-10-30T23:59:59.000Z",
"text": "ちょいと前のやつ"
}
],
"pendingResults": [
{
"goalId": "2CA5Q7JygkXSHUmJZ8B7",
"userId": "Vlx6GCtq90ag3lxgh0pcCKGp5ba0",
"deadline": "2024-12-09T23:59:59.000Z",
"text": "まだまだ先だよ"
}
]
}
```
12 changes: 7 additions & 5 deletions functions/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import cors from "cors";
import express from "express";
import rateLimit from "express-rate-limit";
import { rateLimit } from "express-rate-limit";
import admin from "firebase-admin";
import * as logger from "firebase-functions/logger";
import { onRequest } from "firebase-functions/v2/https";
Expand All @@ -12,6 +12,11 @@ admin.initializeApp({
storageBucket: "todo-real-c28fa.appspot.com",
});

import goalRouter from "./routers/goalRouter";
import postRouter from "./routers/postRouter";
import resultRouter from "./routers/resultRouter";
import userRouer from "./routers/userRouter";

const app = express();
app.use(cors({ origin: true }));
app.use(helmet());
Expand All @@ -33,14 +38,11 @@ app.use(
})
);

import goalRouter from "./routers/goalRouter";
import postRouter from "./routers/postRouter";
import userRouer from "./routers/userRouter";
app.use("/user", userRouer);
app.use("/goal", goalRouter);
app.use("/post", postRouter);
app.use("/result", resultRouter);

// Cloud Functionsにデプロイする関数
const region = "asia-northeast1";

export const helloWorld = onRequest({ region: region }, (req, res) => {
Expand Down
48 changes: 25 additions & 23 deletions functions/src/routers/goalRouter.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
import express, { Request, Response } from "express";
import admin from "firebase-admin";
import { Goal } from "./types";

const router = express.Router();
const db = admin.firestore();

interface Goal {
userId: string;
deadline: Date;
text: string;
}

// GET: 全ての目標を取得
router.get("/", async (req: Request, res: Response) => {
try {
Expand All @@ -18,14 +13,18 @@ router.get("/", async (req: Request, res: Response) => {
return res.status(404).json({ message: "No goals found" });
}

const goals = goalSnapshot.docs.map((doc) => ({
id: doc.id,
...doc.data(),
}));
const goals = goalSnapshot.docs.map((doc) => {
const data = doc.data();
return {
id: doc.id,
...data,
deadline: new Date(data.deadline._seconds * 1000),
};
});

return res.json(goals);
} catch (error) {
return res.status(500).json({ message: "Error fetching goals", error });
return res.status(500).json({ message: "Error fetching goals" });
}
});

Expand All @@ -47,20 +46,24 @@ router.get("/:userId", async (req: Request, res: Response) => {
return res.status(404).json({ message: "No goals found for this user" });
}

const goals = goalSnapshot.docs.map((doc) => ({
id: doc.id,
...doc.data(),
}));
const goals = goalSnapshot.docs.map((doc) => {
const data = doc.data();
return {
id: doc.id,
...data,
deadline: new Date(data.deadline._seconds * 1000),
};
});

return res.json(goals);
} catch (error) {
return res.status(500).json({ message: "Error fetching goals", error });
return res.status(500).json({ message: "Error fetching goals" });
}
});

// POST: 新しい目標を作成
router.post("/", async (req: Request, res: Response) => {
const goalId = db.collection("goal").doc().id; // FirebaseのドキュメントIDを生成
const goalId = db.collection("goal").doc().id;

let userId: Goal["userId"];
let deadline: Goal["deadline"];
Expand All @@ -69,7 +72,7 @@ router.post("/", async (req: Request, res: Response) => {
try {
({ userId, deadline, text } = req.body as Goal);
} catch (error) {
return res.status(400).json({ message: "Invalid request body", error });
return res.status(400).json({ message: "Invalid request body" });
}

if (!userId || !deadline || !text) {
Expand All @@ -79,7 +82,6 @@ router.post("/", async (req: Request, res: Response) => {
}

try {
// goalId をドキュメント名として使用してデータを保存
await db
.collection("goal")
.doc(goalId)
Expand All @@ -93,7 +95,7 @@ router.post("/", async (req: Request, res: Response) => {
.status(201)
.json({ message: "Goal created successfully", goalId });
} catch (error) {
return res.status(500).json({ message: "Error creating goal", error });
return res.status(500).json({ message: "Error creating goal" });
}
});

Expand All @@ -110,7 +112,7 @@ router.put("/:goalId", async (req: Request, res: Response) => {

const updateData: Partial<Omit<Goal, "deadline">> & {
deadline?: admin.firestore.Timestamp;
} = {}; // 型エラーが出たため書き方変更
} = {};
if (userId) updateData.userId = userId;
if (deadline)
updateData.deadline = admin.firestore.Timestamp.fromDate(
Expand All @@ -122,7 +124,7 @@ router.put("/:goalId", async (req: Request, res: Response) => {
await db.collection("goal").doc(goalId).update(updateData);
return res.json({ message: "Goal updated successfully", goalId });
} catch (error) {
return res.status(500).json({ message: "Error updating goal", error });
return res.status(500).json({ message: "Error updating goal" });
}
});

Expand All @@ -138,7 +140,7 @@ router.delete("/:goalId", async (req: Request, res: Response) => {
await db.collection("goal").doc(goalId).delete();
return res.json({ message: "Goal deleted successfully", goalId });
} catch (error) {
return res.status(500).json({ message: "Error deleting goal", error });
return res.status(500).json({ message: "Error deleting goal" });
}
});

Expand Down
Loading
Loading