@@ -12,6 +12,54 @@ import { isWorkspacePaused } from '../model/billing/workspace.js';
12
12
13
13
export const applicationRouter = Router ( ) ;
14
14
15
+ // Shared validation schema for event payload
16
+ const eventPayloadSchema = yup . object ( ) . shape ( {
17
+ data : yup . object ( ) ,
18
+ language : yup . string ( ) . max ( 35 ) ,
19
+ os : yup . string ( ) . max ( 20 ) ,
20
+ application : yup . string ( ) . required ( ) ,
21
+ name : yup . string ( ) . max ( 50 ) ,
22
+ screen : yup . string ( ) . max ( 500 ) ,
23
+ params : yup . object ( ) ,
24
+ } ) ;
25
+
26
+ // Shared function to process a single event
27
+ async function processEvent ( type : string , payload : any , session : any ) {
28
+ const {
29
+ name : eventName ,
30
+ data : eventData ,
31
+ screen : screenName ,
32
+ params : screenParams ,
33
+ } = payload ;
34
+
35
+ if ( type === COLLECTION_TYPE . event ) {
36
+ await saveApplicationEvent ( {
37
+ eventName,
38
+ eventData,
39
+ screenName,
40
+ screenParams,
41
+ ...session ,
42
+ sessionId : session . id ,
43
+ } ) ;
44
+ return { status : 'success' , type : 'event' } ;
45
+ }
46
+
47
+ if ( type === COLLECTION_TYPE . identify ) {
48
+ if ( ! eventData ) {
49
+ throw new Error ( 'Data required for identify event' ) ;
50
+ }
51
+
52
+ await saveApplicationSessionData ( {
53
+ ...session ,
54
+ sessionData : eventData ,
55
+ sessionId : session . id ,
56
+ } ) ;
57
+ return { status : 'success' , type : 'identify' } ;
58
+ }
59
+
60
+ throw new Error ( `Unsupported event type: ${ type } ` ) ;
61
+ }
62
+
15
63
applicationRouter . post (
16
64
'/send' ,
17
65
validate (
@@ -20,20 +68,7 @@ applicationRouter.post(
20
68
. withMessage ( 'payload should be existed' )
21
69
. isObject ( )
22
70
. custom ( async ( input ) => {
23
- return yup
24
- . object ( )
25
- . shape ( {
26
- data : yup . object ( ) ,
27
- language : yup . string ( ) . max ( 35 ) ,
28
- os : yup . string ( ) . max ( 20 ) ,
29
- url : yup . string ( ) . max ( 500 ) ,
30
- application : yup . string ( ) . required ( ) ,
31
- name : yup . string ( ) . max ( 50 ) ,
32
- screen : yup . string ( ) . max ( 500 ) ,
33
- params : yup . object ( ) ,
34
- } )
35
- . required ( )
36
- . validate ( input ) ;
71
+ return eventPayloadSchema . required ( ) . validate ( input ) ;
37
72
} ) ,
38
73
body ( 'type' )
39
74
. exists ( )
@@ -43,46 +78,86 @@ applicationRouter.post(
43
78
) ,
44
79
async ( req , res ) => {
45
80
const { type, payload } = req . body ;
46
- const {
47
- url,
48
- name : eventName ,
49
- data : eventData ,
50
- screen : screenName ,
51
- params : screenParams ,
52
- } = payload ;
53
81
54
- const session = await findSession ( req ) ;
82
+ const session = await findSession ( req , req . body ) ;
55
83
56
84
if ( await isWorkspacePaused ( session . workspaceId ) ) {
57
85
res . status ( 403 ) . send ( 'Workspace is paused.' ) ;
58
86
return ;
59
87
}
60
88
61
- if ( type === COLLECTION_TYPE . event ) {
62
- await saveApplicationEvent ( {
63
- eventName,
64
- eventData,
65
- screenName,
66
- screenParams,
67
- ...session ,
68
- sessionId : session . id ,
89
+ try {
90
+ await processEvent ( type , payload , session ) ;
91
+ const token = createToken ( session ) ;
92
+ res . send ( token ) ;
93
+ } catch ( error ) {
94
+ res . status ( 400 ) . json ( {
95
+ error : error instanceof Error ? error . message : 'Unknown error' ,
69
96
} ) ;
70
97
}
98
+ }
99
+ ) ;
71
100
72
- if ( type === COLLECTION_TYPE . identify ) {
73
- if ( ! eventData ) {
74
- throw new Error ( 'Data required' ) ;
75
- }
101
+ applicationRouter . post (
102
+ '/batch' ,
103
+ validate (
104
+ body ( 'events' )
105
+ . exists ( )
106
+ . withMessage ( 'events should be existed' )
107
+ . isArray ( { min : 1 , max : 100 } )
108
+ . withMessage ( 'events should be an array with 1-100 items' )
109
+ . custom ( async ( events ) => {
110
+ // Validate each event in the array
111
+ const eventSchema = yup . object ( ) . shape ( {
112
+ type : yup
113
+ . string ( )
114
+ . required ( )
115
+ . matches ( / e v e n t | i d e n t i f y / i) ,
116
+ payload : eventPayloadSchema . required ( ) ,
117
+ } ) ;
76
118
77
- await saveApplicationSessionData ( {
78
- ...session ,
79
- sessionData : eventData ,
80
- sessionId : session . id ,
81
- } ) ;
119
+ for ( const event of events ) {
120
+ await eventSchema . validate ( event ) ;
121
+ }
122
+ return true ;
123
+ } )
124
+ ) ,
125
+ async ( req , res ) => {
126
+ const { events } = req . body ;
127
+
128
+ const session = await findSession ( req , events [ 0 ] ) ;
129
+
130
+ if ( await isWorkspacePaused ( session . workspaceId ) ) {
131
+ res . status ( 403 ) . send ( 'Workspace is paused.' ) ;
132
+ return ;
133
+ }
134
+
135
+ const results = [ ] ;
136
+ const errors = [ ] ;
137
+
138
+ // Process each event in the batch
139
+ for ( let i = 0 ; i < events . length ; i ++ ) {
140
+ const event = events [ i ] ;
141
+ try {
142
+ const result = await processEvent ( event . type , event . payload , session ) ;
143
+ results . push ( { index : i , ...result } ) ;
144
+ } catch ( error ) {
145
+ errors . push ( {
146
+ index : i ,
147
+ status : 'error' ,
148
+ message : error instanceof Error ? error . message : 'Unknown error' ,
149
+ } ) ;
150
+ }
82
151
}
83
152
84
153
const token = createToken ( session ) ;
85
154
86
- res . send ( token ) ;
155
+ res . json ( {
156
+ token,
157
+ processed : results . length ,
158
+ errors : errors . length ,
159
+ results,
160
+ errorDetails : errors . length > 0 ? errors : undefined ,
161
+ } ) ;
87
162
}
88
163
) ;
0 commit comments