Merge pull request 'feat(nostr): added nostrEvent validation' () from payload-validation into staging

Reviewed-on: 
This commit is contained in:
Otto 2025-04-02 08:10:40 +00:00
commit 063f945cf0
8 changed files with 101 additions and 34 deletions

@ -1,10 +1,12 @@
import { ObjectId } from 'mongodb'
export class NostrEvent {
constructor(
public nostrId: string, // nostr unique identifier
public pubkey: string, // public key of the event creator
public created_at: number, // timestamp
public kind: number, // event type, e.g., review, article, comment
public tags: [string][], // array of keywords or hashtags
public content: string // text content of the event
public tags: string[][], // array of keywords or hashtags
public content: string, // text content of the event
public id?: ObjectId
) {}
}

@ -2,6 +2,13 @@
import express, { Request, Response } from 'express'
import { collections } from '../services/database.service'
import { NostrEvent } from '../models'
import {
nostrEventValidation,
handleReqError,
handleReqSuccess
} from '../utils'
import { Event } from 'nostr-tools'
import Joi from 'joi'
// Global Config
export const nostrRouter = express.Router()
@ -26,23 +33,39 @@ nostrRouter.get('/', async (_req: Request, res: Response) => {
// POST
nostrRouter.post('/', async (req: Request, res: Response) => {
try {
const nostrEvent = req.body as NostrEvent
const {
error,
value: event
}: { error: Joi.ValidationError | undefined; value: Event } =
nostrEventValidation(req.body)
if (error) {
throw error.details[0].message
}
const { id, pubkey, created_at, kind, tags, content } = event
const events = await collections.nostrEvents
?.find({ nostrId: id })
.toArray()
if (events?.length) {
throw new Error('nostr event with provided "id" exists')
}
const nostrEvent: NostrEvent = {
nostrId: id,
pubkey,
created_at,
kind,
tags,
content
}
const result = await collections.nostrEvents?.insertOne(nostrEvent)
if (result) {
res
.status(201)
.send(
`Successfully created a new nostrEvent with id ${result.insertedId}`
)
} else {
res.status(500).send('Failed to create a new nostrEvent.')
}
handleReqSuccess(res, result, 'nostrEvent')
} catch (error: unknown) {
console.error(error)
if (error instanceof Error) {
res.status(400).send(error.message)
}
handleReqError(res, error)
}
})

@ -2,7 +2,7 @@
import express, { Request, Response } from 'express'
import { collections } from '../services/database.service'
import { User } from '../models'
import { userValidation } from '../utils'
import { userValidation, handleReqError, handleReqSuccess } from '../utils'
import Joi from 'joi'
// Global Config
@ -59,20 +59,8 @@ usersRouter.post('/', async (req: Request, res: Response) => {
const result = await collections.users?.insertOne(newUser)
if (result) {
res
.status(201)
.send(`Successfully created a new user with id ${result.insertedId}`)
} else {
res.status(500).send('Failed to create a new user.')
}
handleReqSuccess(res, result, 'user')
} catch (error) {
console.error(error)
if (error instanceof Error) {
res.status(400).send(error.message)
} else if (typeof error === 'string') {
res.status(400).send(error)
}
handleReqError(res, error)
}
})

@ -1,2 +1,3 @@
export * from './validation'
export * from './nostr'
export * from './route'

28
src/utils/route.ts Normal file

@ -0,0 +1,28 @@
import { Response } from 'express'
import { InsertOneResult } from 'mongodb'
export const handleReqError = (res: Response, error: unknown) => {
console.error(error)
if (error instanceof Error) {
res.status(400).send(error.message)
} else if (typeof error === 'string') {
res.status(400).send(error)
}
}
export const handleReqSuccess = (
res: Response,
result: InsertOneResult<Document> | undefined,
itemName: string
) => {
if (result) {
res
.status(201)
.send(
`Successfully created a new ${itemName} with id ${result.insertedId}`
)
} else {
res.status(500).send(`Failed to create a new ${itemName}.`)
}
}

@ -1 +1,2 @@
export * from './user'
export * from './nostr'

@ -0,0 +1,24 @@
import Joi from 'joi'
import { npubToHex, validateHex } from '../nostr'
export const nostrEventValidation = (data: unknown): Joi.ValidationResult =>
Joi.object({
id: Joi.string().required(),
kind: Joi.number().required(),
content: Joi.string().required(),
tags: Joi.array().items(Joi.array().items(Joi.string())),
created_at: Joi.number().required(),
pubkey: Joi.string()
.custom((value, helper) => {
const hex = npubToHex(value as string)
if (!hex || !validateHex(hex)) {
return helper.message({
custom: Joi.expression('"pubkey" contains an invalid value')
})
}
return value
})
.required()
}).validate(data)

@ -16,11 +16,11 @@ const npubValidation = (value: unknown, helper: Joi.CustomHelpers<unknown>) => {
export const userValidation = (data: unknown): Joi.ValidationResult =>
Joi.object({
name: Joi.string().not('').required(),
name: Joi.string().required(),
npub: Joi.alternatives()
.try(
Joi.array().items(Joi.string().custom(npubValidation)),
Joi.string().not('').custom(npubValidation)
Joi.string().custom(npubValidation)
)
.required(),
role: Joi.string()