Skip to content

Commit 3411f50

Browse files
committed
Add /local/send-test-email endpoint
1 parent 319c14b commit 3411f50

File tree

2 files changed

+143
-107
lines changed

2 files changed

+143
-107
lines changed

backend/api/src/app.ts

Lines changed: 136 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ import {OpenAPIV3} from 'openapi-types';
6565
import {version as pkgVersion} from './../package.json'
6666
import {z, ZodFirstPartyTypeKind, ZodTypeAny} from "zod";
6767
import {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) => {
119121
export const app = express()
120122
app.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

230125
const 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+
405407
swaggerDocument.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

464493
const rootPath = pathWithPrefix("/")
465494
app.get(

backend/api/src/test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import {sendTestEmail} from "email/functions/helpers";
2+
3+
export const localSendTestEmail = async () => {
4+
sendTestEmail('hello@compassmeet.com')
5+
.then(() => console.debug('Email sent successfully!'))
6+
.catch((error) => console.error('Failed to send email:', error))
7+
}

0 commit comments

Comments
 (0)