@@ -65,6 +65,8 @@ import {OpenAPIV3} from 'openapi-types';
6565import { version as pkgVersion } from './../package.json'
6666import { z , ZodFirstPartyTypeKind , ZodTypeAny } from "zod" ;
6767import { getUser } from "api/get-user" ;
68+ import { IS_LOCAL } from "common/envs/constants" ;
69+ import { localSendTestEmail } from "api/test" ;
6870
6971// const corsOptions: CorsOptions = {
7072// origin: ['*'], // Only allow requests from this domain
@@ -119,113 +121,6 @@ const apiErrorHandler: ErrorRequestHandler = (error, _req, res, _next) => {
119121export const app = express ( )
120122app . use ( requestMonitoring )
121123
122- // Triggers Missing parameter name at index 3: *; visit https://git.new/pathToRegexpError for info
123- // May not be necessary
124- // app.options('*', allowCorsUnrestricted)
125-
126- const handlers : { [ k in APIPath ] : APIHandler < k > } = {
127- health : health ,
128- 'get-supabase-token' : getSupabaseToken ,
129- 'get-notifications' : getNotifications ,
130- 'mark-all-notifs-read' : markAllNotifsRead ,
131- // 'user/:username': getUser,
132- // 'user/:username/lite': getDisplayUser,
133- 'user/by-id/:id' : getUser ,
134- // 'user/by-id/:id/lite': getDisplayUser,
135- 'user/by-id/:id/block' : blockUser ,
136- 'user/by-id/:id/unblock' : unblockUser ,
137- 'search-users' : searchUsers ,
138- 'ban-user' : banUser ,
139- report : report ,
140- 'create-user' : createUser ,
141- 'create-profile' : createProfile ,
142- me : getMe ,
143- 'me/private' : getCurrentPrivateUser ,
144- 'me/update' : updateMe ,
145- 'update-notif-settings' : updateNotifSettings ,
146- 'me/delete' : deleteMe ,
147- 'update-profile' : updateProfile ,
148- 'like-profile' : likeProfile ,
149- 'ship-profiles' : shipProfiles ,
150- 'get-likes-and-ships' : getLikesAndShips ,
151- 'has-free-like' : hasFreeLike ,
152- 'star-profile' : starProfile ,
153- 'get-profiles' : getProfiles ,
154- 'get-profile-answers' : getProfileAnswers ,
155- 'get-compatibility-questions' : getCompatibilityQuestions ,
156- 'remove-pinned-photo' : removePinnedPhoto ,
157- 'create-comment' : createComment ,
158- 'hide-comment' : hideComment ,
159- 'create-compatibility-question' : createCompatibilityQuestion ,
160- 'set-compatibility-answer' : setCompatibilityAnswer ,
161- 'create-vote' : createVote ,
162- 'vote' : vote ,
163- 'contact' : contact ,
164- 'compatible-profiles' : getCompatibleProfilesHandler ,
165- 'search-location' : searchLocation ,
166- 'search-near-city' : searchNearCity ,
167- 'create-private-user-message' : createPrivateUserMessage ,
168- 'create-private-user-message-channel' : createPrivateUserMessageChannel ,
169- 'update-private-user-message-channel' : updatePrivateUserMessageChannel ,
170- 'leave-private-user-message-channel' : leavePrivateUserMessageChannel ,
171- 'get-channel-memberships' : getChannelMemberships ,
172- 'get-channel-messages' : getChannelMessagesEndpoint ,
173- 'get-channel-seen-time' : getLastSeenChannelTime ,
174- 'set-channel-seen-time' : setChannelLastSeenTime ,
175- 'get-messages-count' : getMessagesCount ,
176- 'set-last-online-time' : setLastOnlineTime ,
177- 'save-subscription' : saveSubscription ,
178- 'create-bookmarked-search' : createBookmarkedSearch ,
179- 'delete-bookmarked-search' : deleteBookmarkedSearch ,
180- }
181-
182- Object . entries ( handlers ) . forEach ( ( [ path , handler ] ) => {
183- const api = API [ path as APIPath ]
184- const cache = cacheController ( ( api as any ) . cache )
185- const url = pathWithPrefix ( '/' + path as APIPath )
186-
187- const apiRoute = [
188- url ,
189- express . json ( ) ,
190- allowCorsUnrestricted ,
191- cache ,
192- typedEndpoint ( path as any , handler as any ) ,
193- apiErrorHandler ,
194- ] as const
195-
196- if ( api . method === 'POST' ) {
197- app . post ( ...apiRoute )
198- } else if ( api . method === 'GET' ) {
199- app . get ( ...apiRoute )
200- // } else if (api.method === 'PUT') {
201- // app.put(...apiRoute)
202- } else {
203- throw new Error ( 'Unsupported API method' )
204- }
205- } )
206-
207- // Internal Endpoints
208- app . post ( pathWithPrefix ( "/internal/send-search-notifications" ) ,
209- async ( req , res ) => {
210- const apiKey = req . header ( "x-api-key" ) ;
211- if ( apiKey !== process . env . COMPASS_API_KEY ) {
212- return res . status ( 401 ) . json ( { error : "Unauthorized" } ) ;
213- }
214-
215- try {
216- const result = await sendSearchNotifications ( )
217- return res . status ( 200 ) . json ( result )
218- } catch ( err ) {
219- console . error ( "Failed to send notifications:" , err ) ;
220- await sendDiscordMessage (
221- "Failed to send [daily notifications](https://console.cloud.google.com/cloudscheduler?project=compass-130ba) for bookmarked searches..." ,
222- "health"
223- )
224- return res . status ( 500 ) . json ( { error : "Internal server error" } ) ;
225- }
226- }
227- ) ;
228-
229124
230125const schemaCache = new WeakMap < ZodTypeAny , any > ( ) ;
231126
@@ -402,6 +297,113 @@ const swaggerDocument: OpenAPIV3.Document = {
402297 }
403298} as OpenAPIV3 . Document ;
404299
300+ // Triggers Missing parameter name at index 3: *; visit https://git.new/pathToRegexpError for info
301+ // May not be necessary
302+ // app.options('*', allowCorsUnrestricted)
303+
304+ const handlers : { [ k in APIPath ] : APIHandler < k > } = {
305+ health : health ,
306+ 'get-supabase-token' : getSupabaseToken ,
307+ 'get-notifications' : getNotifications ,
308+ 'mark-all-notifs-read' : markAllNotifsRead ,
309+ // 'user/:username': getUser,
310+ // 'user/:username/lite': getDisplayUser,
311+ 'user/by-id/:id' : getUser ,
312+ // 'user/by-id/:id/lite': getDisplayUser,
313+ 'user/by-id/:id/block' : blockUser ,
314+ 'user/by-id/:id/unblock' : unblockUser ,
315+ 'search-users' : searchUsers ,
316+ 'ban-user' : banUser ,
317+ report : report ,
318+ 'create-user' : createUser ,
319+ 'create-profile' : createProfile ,
320+ me : getMe ,
321+ 'me/private' : getCurrentPrivateUser ,
322+ 'me/update' : updateMe ,
323+ 'update-notif-settings' : updateNotifSettings ,
324+ 'me/delete' : deleteMe ,
325+ 'update-profile' : updateProfile ,
326+ 'like-profile' : likeProfile ,
327+ 'ship-profiles' : shipProfiles ,
328+ 'get-likes-and-ships' : getLikesAndShips ,
329+ 'has-free-like' : hasFreeLike ,
330+ 'star-profile' : starProfile ,
331+ 'get-profiles' : getProfiles ,
332+ 'get-profile-answers' : getProfileAnswers ,
333+ 'get-compatibility-questions' : getCompatibilityQuestions ,
334+ 'remove-pinned-photo' : removePinnedPhoto ,
335+ 'create-comment' : createComment ,
336+ 'hide-comment' : hideComment ,
337+ 'create-compatibility-question' : createCompatibilityQuestion ,
338+ 'set-compatibility-answer' : setCompatibilityAnswer ,
339+ 'create-vote' : createVote ,
340+ 'vote' : vote ,
341+ 'contact' : contact ,
342+ 'compatible-profiles' : getCompatibleProfilesHandler ,
343+ 'search-location' : searchLocation ,
344+ 'search-near-city' : searchNearCity ,
345+ 'create-private-user-message' : createPrivateUserMessage ,
346+ 'create-private-user-message-channel' : createPrivateUserMessageChannel ,
347+ 'update-private-user-message-channel' : updatePrivateUserMessageChannel ,
348+ 'leave-private-user-message-channel' : leavePrivateUserMessageChannel ,
349+ 'get-channel-memberships' : getChannelMemberships ,
350+ 'get-channel-messages' : getChannelMessagesEndpoint ,
351+ 'get-channel-seen-time' : getLastSeenChannelTime ,
352+ 'set-channel-seen-time' : setChannelLastSeenTime ,
353+ 'get-messages-count' : getMessagesCount ,
354+ 'set-last-online-time' : setLastOnlineTime ,
355+ 'save-subscription' : saveSubscription ,
356+ 'create-bookmarked-search' : createBookmarkedSearch ,
357+ 'delete-bookmarked-search' : deleteBookmarkedSearch ,
358+ }
359+
360+ Object . entries ( handlers ) . forEach ( ( [ path , handler ] ) => {
361+ const api = API [ path as APIPath ]
362+ const cache = cacheController ( ( api as any ) . cache )
363+ const url = pathWithPrefix ( '/' + path as APIPath )
364+
365+ const apiRoute = [
366+ url ,
367+ express . json ( ) ,
368+ allowCorsUnrestricted ,
369+ cache ,
370+ typedEndpoint ( path as any , handler as any ) ,
371+ apiErrorHandler ,
372+ ] as const
373+
374+ if ( api . method === 'POST' ) {
375+ app . post ( ...apiRoute )
376+ } else if ( api . method === 'GET' ) {
377+ app . get ( ...apiRoute )
378+ // } else if (api.method === 'PUT') {
379+ // app.put(...apiRoute)
380+ } else {
381+ throw new Error ( 'Unsupported API method' )
382+ }
383+ } )
384+
385+ // Internal Endpoints
386+ app . post ( pathWithPrefix ( "/internal/send-search-notifications" ) ,
387+ async ( req , res ) => {
388+ const apiKey = req . header ( "x-api-key" ) ;
389+ if ( apiKey !== process . env . COMPASS_API_KEY ) {
390+ return res . status ( 401 ) . json ( { error : "Unauthorized" } ) ;
391+ }
392+
393+ try {
394+ const result = await sendSearchNotifications ( )
395+ return res . status ( 200 ) . json ( result )
396+ } catch ( err ) {
397+ console . error ( "Failed to send notifications:" , err ) ;
398+ await sendDiscordMessage (
399+ "Failed to send [daily notifications](https://console.cloud.google.com/cloudscheduler?project=compass-130ba) for bookmarked searches..." ,
400+ "health"
401+ )
402+ return res . status ( 500 ) . json ( { error : "Internal server error" } ) ;
403+ }
404+ }
405+ ) ;
406+
405407swaggerDocument . paths [ "/internal/send-search-notifications" ] = {
406408 post : {
407409 summary : "Trigger daily search notifications" ,
@@ -460,6 +462,33 @@ swaggerDocument.paths["/internal/send-search-notifications"] = {
460462 } ,
461463} as any
462464
465+ // Local Endpoints
466+ app . post ( pathWithPrefix ( "/local/send-test-email" ) ,
467+ async ( req , res ) => {
468+ if ( ! IS_LOCAL ) {
469+ return res . status ( 401 ) . json ( { error : "Unauthorized" } ) ;
470+ }
471+
472+ try {
473+ const result = await localSendTestEmail ( )
474+ return res . status ( 200 ) . json ( result )
475+ } catch ( err ) {
476+ return res . status ( 500 ) . json ( { error : err } ) ;
477+ }
478+ }
479+ ) ;
480+
481+ swaggerDocument . paths [ "/local/send-test-email" ] = {
482+ post : {
483+ summary : "Send a test email" ,
484+ description : "Local endpoint to send a test email." ,
485+ tags : [ "Local" ] ,
486+ requestBody : {
487+ required : false ,
488+ } ,
489+ } ,
490+ } as any
491+
463492
464493const rootPath = pathWithPrefix ( "/" )
465494app . get (
0 commit comments