11import { onCall , HttpsError } from "firebase-functions/v2/https" ;
2- import { onTaskDispatched } from "firebase-functions/v2/tasks" ;
3- import { getFunctions } from "firebase-admin/functions" ;
2+ import { FieldValue } from "firebase-admin/firestore" ;
43import * as admin from "firebase-admin" ;
54import * as logger from "firebase-functions/logger" ;
65import { toError } from "../common/error" ;
76import { FirestorePath } from "../common/firestorePath" ;
87
98const LOCATION = "asia-northeast3" ;
10- const DELETE_DELAY_SECONDS = 5 ;
11-
12- type NotificationDeletionPayload = {
13- userId : string ;
14- notificationId : string ;
15- } ;
169
1710export const requestPushNotificationDeletion = onCall ( {
1811 cors : true ,
@@ -42,30 +35,23 @@ export const requestPushNotificationDeletion = onCall({
4235
4336 try {
4437 await notificationRef . set ( {
45- // deletingAt: 삭제 요청은 되었지만, 5초 유예 후 최종 삭제되기 전 상태를 의미한다.
46- deletingAt : admin . firestore . FieldValue . serverTimestamp ( ) ,
47- isDeleted : false
38+ deletingAt : FieldValue . delete ( ) ,
39+ isDeleted : true
4840 } , { merge : true } ) ;
49-
50- const queue = getFunctions ( ) . taskQueue (
51- `locations/${ LOCATION } /functions/completePushNotificationDeletion`
52- ) ;
53- await queue . enqueue (
54- { userId, notificationId } ,
55- { scheduleDelaySeconds : DELETE_DELAY_SECONDS }
56- ) ;
5741 } catch ( error ) {
58- const currentNotificationSnapshot = await notificationRef . get ( ) ;
59- if ( currentNotificationSnapshot . exists && currentNotificationSnapshot . data ( ) ?. isDeleted !== true ) {
60- await notificationRef . update ( {
61- deletingAt : admin . firestore . FieldValue . delete ( )
62- } ) ;
42+ try {
43+ const currentNotificationSnapshot = await notificationRef . get ( ) ;
44+ if ( currentNotificationSnapshot . exists && currentNotificationSnapshot . data ( ) ?. isDeleted === true ) {
45+ await notificationRef . update ( {
46+ deletingAt : FieldValue . delete ( ) ,
47+ isDeleted : false
48+ } ) ;
49+ }
50+ } catch ( cleanupError ) {
51+ logger . error ( "푸시 알림 삭제 요청 cleanup 실패" , toError ( cleanupError ) , { userId, notificationId } ) ;
6352 }
6453
65- logger . error ( "푸시 알림 삭제 요청 실패" , toError ( error ) , {
66- userId,
67- notificationId
68- } ) ;
54+ logger . error ( "푸시 알림 삭제 요청 실패" , toError ( error ) , { userId, notificationId } ) ;
6955 throw new HttpsError ( "internal" , "푸시 알림 삭제 요청에 실패했습니다." ) ;
7056 }
7157
@@ -95,9 +81,9 @@ export const undoPushNotificationDeletion = onCall({
9581
9682 try {
9783 const notificationSnapshot = await notificationRef . get ( ) ;
98- if ( notificationSnapshot . exists && notificationSnapshot . data ( ) ?. isDeleted ! == true ) {
84+ if ( notificationSnapshot . exists && notificationSnapshot . data ( ) ?. isDeleted = == true ) {
9985 await notificationRef . update ( {
100- deletingAt : admin . firestore . FieldValue . delete ( ) ,
86+ deletingAt : FieldValue . delete ( ) ,
10187 isDeleted : false
10288 } ) ;
10389 }
@@ -112,61 +98,3 @@ export const undoPushNotificationDeletion = onCall({
11298 return { success : true } ;
11399 }
114100) ;
115-
116- export const completePushNotificationDeletion = onTaskDispatched ( {
117- maxInstances : 5 ,
118- region : LOCATION ,
119- retryConfig : { maxAttempts : 3 , minBackoffSeconds : 5 } ,
120- rateLimits : { maxDispatchesPerSecond : 5 } ,
121- } ,
122- async ( request ) => {
123- const payload = parseDeletionPayload ( request . data ) ;
124- if ( ! payload ) {
125- logger . warn ( "유효하지 않은 푸시 알림 삭제 payload" , request . data ) ;
126- return ;
127- }
128-
129- const { userId, notificationId } = payload ;
130-
131- const notificationRef = admin . firestore ( ) . doc ( FirestorePath . notification ( userId , notificationId ) ) ;
132-
133- try {
134- const notificationSnapshot = await notificationRef . get ( ) ;
135- const deletingAt = notificationSnapshot . data ( ) ?. deletingAt ;
136- const isDeleted = notificationSnapshot . data ( ) ?. isDeleted === true ;
137-
138- if ( ! notificationSnapshot . exists || ! deletingAt || isDeleted ) {
139- return ;
140- }
141-
142- await notificationRef . set ( {
143- deletingAt : admin . firestore . FieldValue . delete ( ) ,
144- isDeleted : true
145- } , { merge : true } ) ;
146- } catch ( error ) {
147- logger . error ( "푸시 알림 최종 삭제 실패" , toError ( error ) , {
148- userId,
149- notificationId
150- } ) ;
151- throw error ;
152- }
153- }
154- ) ;
155-
156- function parseDeletionPayload ( data : unknown ) : NotificationDeletionPayload | null {
157- const userId = typeof ( data as NotificationDeletionPayload | undefined ) ?. userId === "string" ?
158- ( data as NotificationDeletionPayload ) . userId . trim ( ) :
159- "" ;
160- const notificationId = typeof ( data as NotificationDeletionPayload | undefined ) ?. notificationId === "string" ?
161- ( data as NotificationDeletionPayload ) . notificationId . trim ( ) :
162- "" ;
163-
164- if ( ! userId || ! notificationId ) {
165- return null ;
166- }
167-
168- return {
169- userId,
170- notificationId
171- } ;
172- }
0 commit comments