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

기록을 서버 사이드 API가 아닌 클라이언트 사이드로 옮기는 작업 #161

Merged
merged 4 commits into from
Feb 13, 2025
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
88 changes: 88 additions & 0 deletions firestore_schema.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
Firestore Database Schema

1. users Collection
- Document ID: {userId}
- Fields:
- uid (string): Unique identifier for the user
- realName (string | null): User's real name
- nickname (string | null): User's chosen nickname
- email (string | null): User's email address
- profilePhotoURL (string | null): Profile picture URL
- bio (string | null): Short bio about the user
- boardPermissions (map): { [boardId]: 'read' | 'write' }

Subcollections:
- notifications
- Document ID: {notificationId}
- Fields:
- id (string)
- type (enum): Notification type (COMMENT_ON_POST, REPLY_ON_COMMENT, REPLY_ON_POST)
- boardId (string)
- postId (string)
- commentId (string | optional)
- replyId (string | optional)
- fromUserId (string)
- fromUserProfileImage (string | optional)
- message (string)
- timestamp (Timestamp)
- read (boolean)

- writingHistories
- Document ID: {writingHistoryId}
- Fields:
- day (string): YYYY-MM-DD
- createdAt (Timestamp)
- board.id (string)
- post.id (string)
- post.contentLength (number)

- firebaseMessagingTokens
- Document ID: {firebaseMessagingTokenId}
- Fields: (Not explicitly defined)

2. boards Collection
- Document ID: {boardId}
- Fields:
- id (string)
- title (string)
- description (string)
- createdAt (Date)
- firstDay (Timestamp | optional)
- cohort (number | optional)

Subcollections:
- posts
- Document ID: {postId}
- Fields:
- id (string)
- boardId (string)
- title (string)
- content (string)
- thumbnailImageURL (string | null)
- authorId (string)
- authorName (string)
- createdAt (Date | optional)
- updatedAt (Date | optional)
- countOfComments (number)
- countOfReplies (number)
- weekDaysFromFirstDay (number | optional)

- comments
- Document ID: {commentId}
- Fields:
- id (string)
- content (string)
- userId (string)
- userName (string)
- userProfileImage (string)
- createdAt (Timestamp)

- replies
- Document ID: {replyId}
- Fields:
- id (string)
- content (string)
- userId (string)
- userName (string)
- userProfileImage (string)
- createdAt (Timestamp)
54 changes: 54 additions & 0 deletions functions/src/postings/createPosting.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { onDocumentCreated } from "firebase-functions/v2/firestore";
import admin from "../admin";
import { Post } from "../types/Post";
import { Posting } from "../types/Posting";
import { Timestamp } from "firebase-admin/firestore";

export const createPosting = onDocumentCreated(
'boards/{boardId}/posts/{postId}',
async (event) => {

const postData = event.data?.data() as Post;
if (!postData) {
console.error('No post data found.');
return null;
}

// Destructure necessary fields
const boardId = postData.boardId;
const postId = postData.id;
const postTitle = postData.title;
const content = postData.content || '';
const createdAt = postData.createdAt;
const authorId = postData.authorId;

if (!authorId) {
console.error('Post is missing an authorId.');
return null;
}

// Compute the content length for our posting record.
const contentLength = content.length;

// 3. Build the posting data model.
const postingData: Posting = {
board: { id: boardId },
post: { id: postId, title: postTitle, contentLength: contentLength },
createdAt: createdAt || Timestamp.now(),
};

// 4. Write the posting document into the user's activities subcollection.
try {
await admin.firestore()
.collection('users')
.doc(authorId)
.collection('postings')
.add(postingData);

console.log(`Created posting activity for user ${authorId} for post ${postId}`);
} catch (error) {
console.error('Error writing posting activity:', error);
}

return null;
});
78 changes: 78 additions & 0 deletions functions/src/postings/updatePosting.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { onRequest } from "firebase-functions/v2/https";
import admin from "../admin";
import { Posting } from "../types/Posting";

/**
* updatePosting is a one-time migration function.
* It takes a boardId as a parameter, fetches every post in that board,
* converts each into a "posting" record, and writes it to the corresponding
* user's activities (or postings) subcollection.
*/
export const updatePosting = onRequest(async (req, res) => {
// Accept boardId as a query parameter (or in the request body)
const boardId = req.query.boardId || req.body.boardId;
if (!boardId) {
res.status(400).send("Missing boardId parameter.");
return;
}

console.log(`Migration started for boardId: ${boardId}`);

try {
// Reference to the posts subcollection for the given board
const postsRef = admin.firestore().collection('boards').doc(boardId).collection('posts');
const postsSnapshot = await postsRef.get();
console.log(`Found ${postsSnapshot.size} post(s) for board ${boardId}.`);

let processedCount = 0;
let errorCount = 0;

// Loop through each post document
for (const postDoc of postsSnapshot.docs) {
const postData = postDoc.data();
const postId = postData.id;
const postTitle = postData.title;
const authorId = postData.authorId;

if (!authorId) {
console.error(`Post ${postId} is missing an authorId. Skipping.`);
errorCount++;
continue;
}

// Compute the content length. Default to empty string if missing.
const content = postData.content || "";
const contentLength = content.length;

// Use the post's createdAt timestamp directly.
const createdAt = postData.createdAt;
// Build the posting data model
const postingData: Posting = {
board: { id: boardId },
post: { id: postId, title: postTitle, contentLength: contentLength },
createdAt: createdAt
};

try {
// Write the posting record into the user's subcollection.
await admin.firestore()
.collection('users')
.doc(authorId)
.collection('postings')
.add(postingData);

console.log(`Successfully migrated post ${postTitle} for author ${authorId}.`);
processedCount++;
} catch (writeError) {
console.error(`Error migrating post ${postTitle} for author ${authorId}:`, writeError);
errorCount++;
}
}

console.log(`Migration completed for board ${boardId}: ${processedCount} post(s) processed, ${errorCount} error(s).`);
res.status(200).send(`Migration completed for board ${boardId}: ${processedCount} post(s) processed, ${errorCount} error(s).`);
} catch (error) {
console.error(`Error during migration for board ${boardId}:`, error);
res.status(500).send(`Error during migration for board ${boardId}: ${error}`);
}
});
7 changes: 7 additions & 0 deletions functions/src/types/Posting.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Timestamp } from "firebase-admin/firestore";

export interface Posting {
board: { id: string };
post: { id: string; title: string; contentLength: number };
createdAt: Timestamp
}