mirror of
https://github.com/CompassConnections/Compass.git
synced 2026-04-04 14:53:33 -04:00
Rename getLovers and add base send-search-notifications.ts
This commit is contained in:
@@ -1,56 +1,58 @@
|
||||
import { API, type APIPath } from 'common/api/schema'
|
||||
import { APIError, pathWithPrefix } from 'common/api/utils'
|
||||
import {API, type APIPath} from 'common/api/schema'
|
||||
import {APIError, pathWithPrefix} from 'common/api/utils'
|
||||
import cors from 'cors'
|
||||
import * as crypto from 'crypto'
|
||||
import express from 'express'
|
||||
import { type ErrorRequestHandler, type RequestHandler } from 'express'
|
||||
import { hrtime } from 'node:process'
|
||||
import { withMonitoringContext } from 'shared/monitoring/context'
|
||||
import { log } from 'shared/monitoring/log'
|
||||
import { metrics } from 'shared/monitoring/metrics'
|
||||
import { banUser } from './ban-user'
|
||||
import { blockUser, unblockUser } from './block-user'
|
||||
import { getCompatibleLoversHandler } from './compatible-lovers'
|
||||
import { createComment } from './create-comment'
|
||||
import { createCompatibilityQuestion } from './create-compatibility-question'
|
||||
import { createLover } from './create-lover'
|
||||
import { createUser } from './create-user'
|
||||
import { getCompatibilityQuestions } from './get-compatibililty-questions'
|
||||
import { getLikesAndShips } from './get-likes-and-ships'
|
||||
import { getLoverAnswers } from './get-lover-answers'
|
||||
import { getLovers } from './get-lovers'
|
||||
import { getSupabaseToken } from './get-supabase-token'
|
||||
import { getDisplayUser, getUser } from './get-user'
|
||||
import { getMe } from './get-me'
|
||||
import { hasFreeLike } from './has-free-like'
|
||||
import { health } from './health'
|
||||
import { typedEndpoint, type APIHandler } from './helpers/endpoint'
|
||||
import { hideComment } from './hide-comment'
|
||||
import { likeLover } from './like-lover'
|
||||
import { markAllNotifsRead } from './mark-all-notifications-read'
|
||||
import { removePinnedPhoto } from './remove-pinned-photo'
|
||||
import { report } from './report'
|
||||
import { searchLocation } from './search-location'
|
||||
import { searchNearCity } from './search-near-city'
|
||||
import { shipLovers } from './ship-lovers'
|
||||
import { starLover } from './star-lover'
|
||||
import { updateLover } from './update-lover'
|
||||
import { updateMe } from './update-me'
|
||||
import { deleteMe } from './delete-me'
|
||||
import { getCurrentPrivateUser } from './get-current-private-user'
|
||||
import { createPrivateUserMessage } from './create-private-user-message'
|
||||
import express, {type ErrorRequestHandler, type RequestHandler} from 'express'
|
||||
import {hrtime} from 'node:process'
|
||||
import {withMonitoringContext} from 'shared/monitoring/context'
|
||||
import {log} from 'shared/monitoring/log'
|
||||
import {metrics} from 'shared/monitoring/metrics'
|
||||
import {banUser} from './ban-user'
|
||||
import {blockUser, unblockUser} from './block-user'
|
||||
import {getCompatibleLoversHandler} from './compatible-lovers'
|
||||
import {createComment} from './create-comment'
|
||||
import {createCompatibilityQuestion} from './create-compatibility-question'
|
||||
import {createLover} from './create-lover'
|
||||
import {createUser} from './create-user'
|
||||
import {getCompatibilityQuestions} from './get-compatibililty-questions'
|
||||
import {getLikesAndShips} from './get-likes-and-ships'
|
||||
import {getLoverAnswers} from './get-lover-answers'
|
||||
import {getProfiles} from './get-profiles'
|
||||
import {getSupabaseToken} from './get-supabase-token'
|
||||
import {getDisplayUser, getUser} from './get-user'
|
||||
import {getMe} from './get-me'
|
||||
import {hasFreeLike} from './has-free-like'
|
||||
import {health} from './health'
|
||||
import {sendSearchNotifications} from './send-search-notifications'
|
||||
import {type APIHandler, typedEndpoint} from './helpers/endpoint'
|
||||
import {hideComment} from './hide-comment'
|
||||
import {likeLover} from './like-lover'
|
||||
import {markAllNotifsRead} from './mark-all-notifications-read'
|
||||
import {removePinnedPhoto} from './remove-pinned-photo'
|
||||
import {report} from './report'
|
||||
import {searchLocation} from './search-location'
|
||||
import {searchNearCity} from './search-near-city'
|
||||
import {shipLovers} from './ship-lovers'
|
||||
import {starLover} from './star-lover'
|
||||
import {updateLover} from './update-lover'
|
||||
import {updateMe} from './update-me'
|
||||
import {deleteMe} from './delete-me'
|
||||
import {getCurrentPrivateUser} from './get-current-private-user'
|
||||
import {createPrivateUserMessage} from './create-private-user-message'
|
||||
import {
|
||||
getChannelMemberships,
|
||||
getChannelMessages,
|
||||
getLastSeenChannelTime,
|
||||
setChannelLastSeenTime,
|
||||
} from 'api/get-private-messages'
|
||||
import { searchUsers } from './search-users'
|
||||
import { createPrivateUserMessageChannel } from './create-private-user-message-channel'
|
||||
import { leavePrivateUserMessageChannel } from './leave-private-user-message-channel'
|
||||
import { updatePrivateUserMessageChannel } from './update-private-user-message-channel'
|
||||
import { getNotifications } from './get-notifications'
|
||||
import { updateNotifSettings } from './update-notif-setting'
|
||||
import {searchUsers} from './search-users'
|
||||
import {createPrivateUserMessageChannel} from './create-private-user-message-channel'
|
||||
import {leavePrivateUserMessageChannel} from './leave-private-user-message-channel'
|
||||
import {updatePrivateUserMessageChannel} from './update-private-user-message-channel'
|
||||
import {getNotifications} from './get-notifications'
|
||||
import {updateNotifSettings} from './update-notif-setting'
|
||||
import swaggerUi from "swagger-ui-express"
|
||||
import * as fs from "fs"
|
||||
|
||||
const allowCorsUnrestricted: RequestHandler = cors({})
|
||||
|
||||
@@ -66,15 +68,15 @@ const requestMonitoring: RequestHandler = (req, _res, next) => {
|
||||
const traceId = traceContext
|
||||
? traceContext.split('/')[0]
|
||||
: crypto.randomUUID()
|
||||
const context = { endpoint: req.path, traceId }
|
||||
const context = {endpoint: req.path, traceId}
|
||||
withMonitoringContext(context, () => {
|
||||
const startTs = hrtime.bigint()
|
||||
log(`${req.method} ${req.url}`)
|
||||
metrics.inc('http/request_count', { endpoint: req.path })
|
||||
metrics.inc('http/request_count', {endpoint: req.path})
|
||||
next()
|
||||
const endTs = hrtime.bigint()
|
||||
const latencyMs = Number(endTs - startTs) / 1e6
|
||||
metrics.push('http/request_latency', latencyMs, { endpoint: req.path })
|
||||
metrics.push('http/request_latency', latencyMs, {endpoint: req.path})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -82,7 +84,7 @@ const apiErrorHandler: ErrorRequestHandler = (error, _req, res, _next) => {
|
||||
if (error instanceof APIError) {
|
||||
log.info(error)
|
||||
if (!res.headersSent) {
|
||||
const output: { [k: string]: unknown } = { message: error.message }
|
||||
const output: { [k: string]: unknown } = {message: error.message}
|
||||
if (error.details != null) {
|
||||
output.details = error.details
|
||||
}
|
||||
@@ -91,7 +93,7 @@ const apiErrorHandler: ErrorRequestHandler = (error, _req, res, _next) => {
|
||||
} else {
|
||||
log.error(error)
|
||||
if (!res.headersSent) {
|
||||
res.status(500).json({ message: error.stack, error })
|
||||
res.status(500).json({message: error.stack, error})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -99,6 +101,24 @@ const apiErrorHandler: ErrorRequestHandler = (error, _req, res, _next) => {
|
||||
export const app = express()
|
||||
app.use(requestMonitoring)
|
||||
|
||||
const swaggerDocument = JSON.parse(fs.readFileSync("./openapi.json", "utf-8"))
|
||||
swaggerDocument.info = {
|
||||
...swaggerDocument.info,
|
||||
description: "Compass is a free, open-source platform to help people form deep, meaningful, and lasting connections — whether platonic, romantic, or collaborative. It’s made possible by contributions from the community, including code, ideas, feedback, and donations. Unlike typical apps, Compass prioritizes values, interests, and personality over swipes and ads, giving you full control over who you discover and how you connect.",
|
||||
version: "1.0.0",
|
||||
contact: {
|
||||
name: "Compass",
|
||||
email: "compass.meet.info@gmail.com",
|
||||
url: "https://compassmeet.com"
|
||||
}
|
||||
};
|
||||
|
||||
app.use("/docs", swaggerUi.serve, swaggerUi.setup(swaggerDocument))
|
||||
|
||||
app.listen(process.env.PORT ?? 8088, () => {
|
||||
console.log(`API UI available at /docs`)
|
||||
})
|
||||
|
||||
app.options('*', allowCorsUnrestricted)
|
||||
|
||||
const handlers: { [k in APIPath]: APIHandler<k> } = {
|
||||
@@ -128,7 +148,7 @@ const handlers: { [k in APIPath]: APIHandler<k> } = {
|
||||
'get-likes-and-ships': getLikesAndShips,
|
||||
'has-free-like': hasFreeLike,
|
||||
'star-lover': starLover,
|
||||
'get-lovers': getLovers,
|
||||
'get-profiles': getProfiles,
|
||||
'get-lover-answers': getLoverAnswers,
|
||||
'get-compatibility-questions': getCompatibilityQuestions,
|
||||
'remove-pinned-photo': removePinnedPhoto,
|
||||
@@ -146,6 +166,7 @@ const handlers: { [k in APIPath]: APIHandler<k> } = {
|
||||
'get-channel-messages': getChannelMessages,
|
||||
'get-channel-seen-time': getLastSeenChannelTime,
|
||||
'set-channel-seen-time': setChannelLastSeenTime,
|
||||
'send-search-notifications': sendSearchNotifications,
|
||||
}
|
||||
|
||||
Object.entries(handlers).forEach(([path, handler]) => {
|
||||
|
||||
@@ -6,7 +6,7 @@ import {getCompatibleLovers} from 'api/compatible-lovers'
|
||||
import {intersection} from 'lodash'
|
||||
import {MAX_INT, MIN_INT} from "common/constants";
|
||||
|
||||
export const getLovers: APIHandler<'get-lovers'> = async (props, _auth) => {
|
||||
export const getProfiles: APIHandler<'get-profiles'> = async (props, _auth) => {
|
||||
const pg = createSupabaseDirectClient()
|
||||
const {
|
||||
limit: limitParam,
|
||||
33
backend/api/src/send-search-notifications.ts
Normal file
33
backend/api/src/send-search-notifications.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import {APIHandler} from './helpers/endpoint'
|
||||
import {createSupabaseDirectClient} from "shared/supabase/init";
|
||||
import {convertRow} from "shared/love/supabase";
|
||||
import {from, join, renderSql, select, where} from "shared/supabase/sql-builder";
|
||||
|
||||
export function convertSearchRow(row: any): any {
|
||||
return row
|
||||
}
|
||||
|
||||
export const sendSearchNotifications: APIHandler<'send-search-notifications'> = async (_, auth) => {
|
||||
const pg = createSupabaseDirectClient()
|
||||
|
||||
const search_query = renderSql(
|
||||
select('bookmarked_searches.*'),
|
||||
from('bookmarked_searches'),
|
||||
)
|
||||
const searches = pg.map(search_query, [], convertSearchRow)
|
||||
|
||||
const query = renderSql(
|
||||
select('lovers.*, name, username, users.data as user'),
|
||||
from('lovers'),
|
||||
join('users on users.id = lovers.user_id'),
|
||||
where('looking_for_matches = true'),
|
||||
where(
|
||||
`(data->>'isBannedFromPosting' != 'true' or data->>'isBannedFromPosting' is null)`
|
||||
),
|
||||
where(`data->>'userDeleted' != 'true' or data->>'userDeleted' is null`),
|
||||
)
|
||||
|
||||
const profiles = await pg.map(query, [], convertRow)
|
||||
|
||||
return {status: 'success', lovers: profiles}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ export const getLover = async (userId: string) => {
|
||||
)
|
||||
}
|
||||
|
||||
export const getLovers = async (userIds: string[]) => {
|
||||
export const getProfiles = async (userIds: string[]) => {
|
||||
const pg = createSupabaseDirectClient()
|
||||
return await pg.map(
|
||||
`
|
||||
|
||||
@@ -51,6 +51,15 @@ export const API = (_apiTypeCheck = {
|
||||
props: z.object({}),
|
||||
returns: {} as { jwt: string },
|
||||
},
|
||||
'send-search-notifications': {
|
||||
method: 'POST',
|
||||
authed: false,
|
||||
props: z.object({}),
|
||||
returns: {} as {
|
||||
status: 'success' | 'fail'
|
||||
lovers: Lover[]
|
||||
},
|
||||
},
|
||||
'mark-all-notifs-read': {
|
||||
method: 'POST',
|
||||
authed: true,
|
||||
@@ -304,7 +313,7 @@ export const API = (_apiTypeCheck = {
|
||||
status: 'success'
|
||||
},
|
||||
},
|
||||
'get-lovers': {
|
||||
'get-profiles': {
|
||||
method: 'GET',
|
||||
authed: false,
|
||||
props: z
|
||||
|
||||
@@ -132,7 +132,7 @@ export function getScoredAnswerCompatibility(
|
||||
)
|
||||
}
|
||||
|
||||
export const getLoversCompatibilityFactor = (
|
||||
export const getProfilesCompatibilityFactor = (
|
||||
lover1: LoverRow,
|
||||
lover2: LoverRow
|
||||
) => {
|
||||
|
||||
5
scripts/curl.sh
Executable file
5
scripts/curl.sh
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
curl -X POST http://localhost:8088/v0/send-search-notifications
|
||||
|
||||
|
||||
@@ -15,8 +15,6 @@ import {useGetter} from 'web/hooks/use-getter'
|
||||
import {usePersistentInMemoryState} from 'web/hooks/use-persistent-in-memory-state'
|
||||
import {useUser} from 'web/hooks/use-user'
|
||||
import {api} from 'web/lib/api'
|
||||
import {debounce, omit} from 'lodash'
|
||||
import {PREF_AGE_MAX, PREF_AGE_MIN,} from 'web/components/filters/location-filter'
|
||||
import {useBookmarkedSearches} from "web/hooks/use-bookmarked-searches";
|
||||
|
||||
export function ProfilesHome() {
|
||||
@@ -55,7 +53,7 @@ export function ProfilesHome() {
|
||||
if (!user) return;
|
||||
setIsReloading(true);
|
||||
const current = ++id.current;
|
||||
api('get-lovers', removeNullOrUndefinedProps({
|
||||
api('get-profiles', removeNullOrUndefinedProps({
|
||||
limit: 20,
|
||||
compatibleWithUserId: user?.id,
|
||||
...filters
|
||||
@@ -77,7 +75,7 @@ export function ProfilesHome() {
|
||||
try {
|
||||
setIsLoadingMore(true);
|
||||
const lastLover = lovers[lovers.length - 1];
|
||||
const result = await api('get-lovers', removeNullOrUndefinedProps({
|
||||
const result = await api('get-profiles', removeNullOrUndefinedProps({
|
||||
limit: 20,
|
||||
compatibleWithUserId: user?.id,
|
||||
after: lastLover?.id.toString(),
|
||||
|
||||
@@ -4,7 +4,7 @@ import { usePersistentInMemoryState } from 'web/hooks/use-persistent-in-memory-s
|
||||
import { api } from 'web/lib/api'
|
||||
import { APIResponse } from 'common/api/schema'
|
||||
import { useLoverByUserId } from './use-lover'
|
||||
import { getLoversCompatibilityFactor } from 'common/love/compatibility-score'
|
||||
import { getProfilesCompatibilityFactor } from 'common/love/compatibility-score'
|
||||
|
||||
export const useCompatibleLovers = (
|
||||
userId: string | null | undefined,
|
||||
@@ -32,7 +32,7 @@ export const useCompatibleLovers = (
|
||||
|
||||
if (data && lover && options?.sortWithModifiers) {
|
||||
data.compatibleLovers = sortBy(data.compatibleLovers, (l) => {
|
||||
const modifier = !lover ? 1 : getLoversCompatibilityFactor(lover, l)
|
||||
const modifier = !lover ? 1 : getProfilesCompatibilityFactor(lover, l)
|
||||
return -1 * modifier * data.loverCompatibilityScores[l.user.id].score
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user