first commit
This commit is contained in:
24
services/contacts/app/js/ContactManager.js
Normal file
24
services/contacts/app/js/ContactManager.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import { db, ObjectId } from './mongodb.js'
|
||||
|
||||
export async function touchContact(userId, contactId) {
|
||||
await db.contacts.updateOne(
|
||||
{ user_id: new ObjectId(userId.toString()) },
|
||||
{
|
||||
$inc: {
|
||||
[`contacts.${contactId}.n`]: 1,
|
||||
},
|
||||
$set: {
|
||||
[`contacts.${contactId}.ts`]: new Date(),
|
||||
},
|
||||
},
|
||||
{ upsert: true }
|
||||
)
|
||||
}
|
||||
|
||||
export async function getContacts(userId) {
|
||||
const user = await db.contacts.findOne({
|
||||
user_id: new ObjectId(userId.toString()),
|
||||
})
|
||||
|
||||
return user?.contacts
|
||||
}
|
6
services/contacts/app/js/Errors.js
Normal file
6
services/contacts/app/js/Errors.js
Normal file
@@ -0,0 +1,6 @@
|
||||
export class NotFoundError extends Error {
|
||||
constructor(message) {
|
||||
super(message)
|
||||
this.name = 'NotFoundError'
|
||||
}
|
||||
}
|
48
services/contacts/app/js/HttpController.js
Normal file
48
services/contacts/app/js/HttpController.js
Normal file
@@ -0,0 +1,48 @@
|
||||
import logger from '@overleaf/logger'
|
||||
import * as ContactManager from './ContactManager.js'
|
||||
import { buildContactIds } from './contacts.js'
|
||||
|
||||
const CONTACT_LIMIT = 50
|
||||
|
||||
export function addContact(req, res, next) {
|
||||
const { user_id: userId } = req.params
|
||||
const { contact_id: contactId } = req.body
|
||||
|
||||
if (contactId == null || contactId === '') {
|
||||
res.status(400).send('contact_id should be a non-blank string')
|
||||
return
|
||||
}
|
||||
|
||||
logger.debug({ userId, contactId }, 'adding contact')
|
||||
|
||||
Promise.all([
|
||||
ContactManager.touchContact(userId, contactId),
|
||||
ContactManager.touchContact(contactId, userId),
|
||||
])
|
||||
.then(() => {
|
||||
res.sendStatus(204)
|
||||
})
|
||||
.catch(error => {
|
||||
next(error)
|
||||
})
|
||||
}
|
||||
|
||||
export function getContacts(req, res, next) {
|
||||
const { user_id: userId } = req.params
|
||||
const { limit } = req.query
|
||||
|
||||
const contactLimit =
|
||||
limit == null ? CONTACT_LIMIT : Math.min(parseInt(limit, 10), CONTACT_LIMIT)
|
||||
|
||||
logger.debug({ userId }, 'getting contacts')
|
||||
|
||||
ContactManager.getContacts(userId)
|
||||
.then(contacts => {
|
||||
res.json({
|
||||
contact_ids: buildContactIds(contacts, contactLimit),
|
||||
})
|
||||
})
|
||||
.catch(error => {
|
||||
next(error)
|
||||
})
|
||||
}
|
13
services/contacts/app/js/contacts.js
Normal file
13
services/contacts/app/js/contacts.js
Normal file
@@ -0,0 +1,13 @@
|
||||
export function buildContactIds(contacts, limit) {
|
||||
return Object.entries(contacts || {})
|
||||
.map(([id, { n, ts }]) => ({ id, n, ts }))
|
||||
.sort(sortContacts)
|
||||
.slice(0, limit)
|
||||
.map(contact => contact.id)
|
||||
}
|
||||
|
||||
// sort by decreasing count, decreasing timestamp.
|
||||
// i.e. highest count, most recent first.
|
||||
function sortContacts(a, b) {
|
||||
return a.n === b.n ? b.ts - a.ts : b.n - a.n
|
||||
}
|
17
services/contacts/app/js/mongodb.js
Normal file
17
services/contacts/app/js/mongodb.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import Metrics from '@overleaf/metrics'
|
||||
import Settings from '@overleaf/settings'
|
||||
import { MongoClient } from 'mongodb'
|
||||
|
||||
export { ObjectId } from 'mongodb'
|
||||
|
||||
export const mongoClient = new MongoClient(
|
||||
Settings.mongo.url,
|
||||
Settings.mongo.options
|
||||
)
|
||||
const mongoDb = mongoClient.db()
|
||||
|
||||
export const db = {
|
||||
contacts: mongoDb.collection('contacts'),
|
||||
}
|
||||
|
||||
Metrics.mongodb.monitor(mongoClient)
|
32
services/contacts/app/js/server.js
Normal file
32
services/contacts/app/js/server.js
Normal file
@@ -0,0 +1,32 @@
|
||||
import * as Metrics from '@overleaf/metrics'
|
||||
import logger from '@overleaf/logger'
|
||||
import express from 'express'
|
||||
import bodyParser from 'body-parser'
|
||||
import * as HttpController from './HttpController.js'
|
||||
import * as Errors from './Errors.js'
|
||||
|
||||
logger.initialize('contacts')
|
||||
Metrics.event_loop?.monitor(logger)
|
||||
Metrics.open_sockets.monitor()
|
||||
|
||||
export const app = express()
|
||||
app.use(Metrics.http.monitor(logger))
|
||||
Metrics.injectMetricsRoute(app)
|
||||
|
||||
app.get('/user/:user_id/contacts', HttpController.getContacts)
|
||||
app.post(
|
||||
'/user/:user_id/contacts',
|
||||
bodyParser.json({ limit: '2mb' }),
|
||||
HttpController.addContact
|
||||
)
|
||||
|
||||
app.get('/status', (req, res) => res.send('contacts is alive'))
|
||||
|
||||
app.use(function (error, req, res, next) {
|
||||
logger.error({ err: error }, 'request errored')
|
||||
if (error instanceof Errors.NotFoundError) {
|
||||
return res.sendStatus(404)
|
||||
} else {
|
||||
return res.status(500).send('Oops, something went wrong')
|
||||
}
|
||||
})
|
Reference in New Issue
Block a user