wip(backend): Initial work on people
This commit is contained in:
parent
75841ff0a2
commit
d1f9528e70
11 changed files with 532 additions and 21 deletions
|
@ -82,11 +82,13 @@ model Pattern {
|
||||||
model Person {
|
model Person {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
data String?
|
name String @default("")
|
||||||
|
notes String @default("")
|
||||||
user User @relation(fields: [userId], references: [id])
|
user User @relation(fields: [userId], references: [id])
|
||||||
userId Int
|
userId Int
|
||||||
measies String @default("{}")
|
measies String @default("{}")
|
||||||
Pattern Pattern[]
|
Pattern Pattern[]
|
||||||
|
public Boolean @default(false)
|
||||||
|
|
||||||
@@index([userId])
|
@@index([userId])
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import chalk from 'chalk'
|
||||||
// Load environment variables
|
// Load environment variables
|
||||||
import dotenv from 'dotenv'
|
import dotenv from 'dotenv'
|
||||||
import { asJson } from './utils/index.mjs'
|
import { asJson } from './utils/index.mjs'
|
||||||
|
import { measurements } from '../../../config/measurements.mjs'
|
||||||
dotenv.config()
|
dotenv.config()
|
||||||
|
|
||||||
// Allow these 2 to be imported
|
// Allow these 2 to be imported
|
||||||
|
@ -55,6 +56,7 @@ const config = {
|
||||||
base: 'user',
|
base: 'user',
|
||||||
},
|
},
|
||||||
languages: ['en', 'de', 'es', 'fr', 'nl'],
|
languages: ['en', 'de', 'es', 'fr', 'nl'],
|
||||||
|
measies: measurements,
|
||||||
aws: {
|
aws: {
|
||||||
ses: {
|
ses: {
|
||||||
region: process.env.BACKEND_AWS_SES_REGION || 'us-east-1',
|
region: process.env.BACKEND_AWS_SES_REGION || 'us-east-1',
|
||||||
|
@ -197,7 +199,7 @@ if (envToBool(process.env.BACKEND_ENABLE_TESTS)) {
|
||||||
* which is not a given since there's a number of environment
|
* which is not a given since there's a number of environment
|
||||||
* variables that need to be set for this backend to function.
|
* variables that need to be set for this backend to function.
|
||||||
*/
|
*/
|
||||||
export function verifyConfig() {
|
export function verifyConfig(silent = false) {
|
||||||
const emptyString = (input) => {
|
const emptyString = (input) => {
|
||||||
if (typeof input === 'string' && input.length > 0) return false
|
if (typeof input === 'string' && input.length > 0) return false
|
||||||
return true
|
return true
|
||||||
|
@ -219,8 +221,8 @@ export function verifyConfig() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!silent) {
|
||||||
for (const o of ok) console.log(o)
|
for (const o of ok) console.log(o)
|
||||||
|
|
||||||
for (const e of errors) {
|
for (const e of errors) {
|
||||||
console.log(
|
console.log(
|
||||||
chalk.redBright('Error:'),
|
chalk.redBright('Error:'),
|
||||||
|
@ -233,6 +235,7 @@ export function verifyConfig() {
|
||||||
'\n'
|
'\n'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (errors.length > 0) {
|
if (errors.length > 0) {
|
||||||
console.log(chalk.redBright('Invalid configuration. Stopping here...'))
|
console.log(chalk.redBright('Invalid configuration. Stopping here...'))
|
||||||
|
|
48
sites/backend/src/controllers/person.mjs
Normal file
48
sites/backend/src/controllers/person.mjs
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
import { PersonModel } from '../models/person.mjs'
|
||||||
|
|
||||||
|
export function PersonController() {}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a person for the authenticated user
|
||||||
|
*
|
||||||
|
* See: https://freesewing.dev/reference/backend/api
|
||||||
|
*/
|
||||||
|
PersonController.prototype.create = async (req, res, tools) => {
|
||||||
|
const Person = new PersonModel(tools)
|
||||||
|
await Person.create(req)
|
||||||
|
|
||||||
|
return Person.sendResponse(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read a person
|
||||||
|
*
|
||||||
|
* See: https://freesewing.dev/reference/backend/api
|
||||||
|
*/
|
||||||
|
PersonController.prototype.read = async (req, res, tools) => {
|
||||||
|
//const Person = new PersonModel(tools)
|
||||||
|
//await Person.read({ id: req.params.id })
|
||||||
|
//return Person.sendResponse(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update a person
|
||||||
|
*
|
||||||
|
* See: https://freesewing.dev/reference/backend/api
|
||||||
|
*/
|
||||||
|
PersonController.prototype.update = async (req, res, tools) => {
|
||||||
|
//const Person = new PersonModel(tools)
|
||||||
|
//await Person.update(req)
|
||||||
|
//return Person.sendResponse(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove a person
|
||||||
|
*
|
||||||
|
* See: https://freesewing.dev/reference/backend/api
|
||||||
|
*/
|
||||||
|
PersonController.prototype.delete = async (req, res, tools) => {
|
||||||
|
//const Person = new PersonModel(tools)
|
||||||
|
//await Person.remove(req)
|
||||||
|
//return Person.sendResponse(res)
|
||||||
|
}
|
|
@ -4,6 +4,15 @@ import http from 'passport-http'
|
||||||
import jwt from 'passport-jwt'
|
import jwt from 'passport-jwt'
|
||||||
import { ApikeyModel } from './models/apikey.mjs'
|
import { ApikeyModel } from './models/apikey.mjs'
|
||||||
|
|
||||||
|
const levelFromRole = (role) => {
|
||||||
|
if (role === 'user') return 4
|
||||||
|
if (role === 'bughunter') return 5
|
||||||
|
if (role === 'support') return 6
|
||||||
|
if (role === 'admin') return 8
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
function loadExpressMiddleware(app) {
|
function loadExpressMiddleware(app) {
|
||||||
// FIXME: Is this still needed in FreeSewing v3?
|
// FIXME: Is this still needed in FreeSewing v3?
|
||||||
//app.use(bodyParser.urlencoded({ extended: true }))
|
//app.use(bodyParser.urlencoded({ extended: true }))
|
||||||
|
@ -27,7 +36,11 @@ function loadPassportMiddleware(passport, tools) {
|
||||||
...tools.config.jwt,
|
...tools.config.jwt,
|
||||||
},
|
},
|
||||||
(jwt_payload, done) => {
|
(jwt_payload, done) => {
|
||||||
return done(null, { ...jwt_payload, uid: jwt_payload._id })
|
return done(null, {
|
||||||
|
...jwt_payload,
|
||||||
|
uid: jwt_payload._id,
|
||||||
|
level: levelFromRole(jwt_payload.role),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
336
sites/backend/src/models/person.mjs
Normal file
336
sites/backend/src/models/person.mjs
Normal file
|
@ -0,0 +1,336 @@
|
||||||
|
import { log } from '../utils/log.mjs'
|
||||||
|
import { setPersonAvatar } from '../utils/sanity.mjs'
|
||||||
|
|
||||||
|
export function PersonModel(tools) {
|
||||||
|
this.config = tools.config
|
||||||
|
this.prisma = tools.prisma
|
||||||
|
this.decrypt = tools.decrypt
|
||||||
|
this.encrypt = tools.encrypt
|
||||||
|
this.clear = {} // For holding decrypted data
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
PersonModel.prototype.create = async function ({ body, user }) {
|
||||||
|
if (user.level < 3) return this.setResponse(403, 'insufficientAccessLevel')
|
||||||
|
if (Object.keys(body) < 1) return this.setResponse(400, 'postBodyMissing')
|
||||||
|
if (!body.name || typeof body.name !== 'string') return this.setResponse(400, 'nameMissing')
|
||||||
|
|
||||||
|
// Prepare data
|
||||||
|
const data = { name: body.name }
|
||||||
|
if (body.notes || typeof body.notes === 'string') data.notes = body.notes
|
||||||
|
if (body.public === true) data.public = true
|
||||||
|
if (body.measies) data.measies = this.encrypt(this.sanitizeMeasurements(body.measies))
|
||||||
|
data.userId = user.uid
|
||||||
|
|
||||||
|
// Create record
|
||||||
|
try {
|
||||||
|
this.record = await this.prisma.person.create({ data })
|
||||||
|
} catch (err) {
|
||||||
|
log.warn(err, 'Could not create person')
|
||||||
|
return this.setResponse(500, 'createPersonFailed')
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.setResponse(201, 'created', {
|
||||||
|
person: {
|
||||||
|
...this.record,
|
||||||
|
measies: this.decrypt(this.record.measies),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Loads a person from the database based on the where clause you pass it
|
||||||
|
*
|
||||||
|
* Stores result in this.record
|
||||||
|
*/
|
||||||
|
PersonModel.prototype.read = async function (where) {
|
||||||
|
try {
|
||||||
|
this.record = await this.prisma.person.findUnique({ where })
|
||||||
|
} catch (err) {
|
||||||
|
log.warn({ err, where }, 'Could not read person')
|
||||||
|
}
|
||||||
|
|
||||||
|
this.reveal()
|
||||||
|
|
||||||
|
return this.setExists()
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Helper method to decrypt at-rest data
|
||||||
|
*/
|
||||||
|
//PersonModel.prototype.reveal = async function (where) {
|
||||||
|
// this.clear = {}
|
||||||
|
// if (this.record) {
|
||||||
|
// this.clear.bio = this.decrypt(this.record.bio)
|
||||||
|
// this.clear.github = this.decrypt(this.record.github)
|
||||||
|
// this.clear.email = this.decrypt(this.record.email)
|
||||||
|
// this.clear.initial = this.decrypt(this.record.initial)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return this
|
||||||
|
//}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Helper method to encrypt at-rest data
|
||||||
|
*/
|
||||||
|
//UserModel.prototype.cloak = function (data) {
|
||||||
|
// for (const field of ['bio', 'github', 'email']) {
|
||||||
|
// if (typeof data[field] !== 'undefined') data[field] = this.encrypt(data[field])
|
||||||
|
// }
|
||||||
|
// if (typeof data.password === 'string') data.password = asJson(hashPassword(data.password))
|
||||||
|
//
|
||||||
|
// return data
|
||||||
|
//}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Loads a user from the database based on the where clause you pass it
|
||||||
|
* In addition prepares it for returning the account data
|
||||||
|
*
|
||||||
|
* Stores result in this.record
|
||||||
|
*/
|
||||||
|
//UserModel.prototype.readAsAccount = async function (where) {
|
||||||
|
// await this.read(where)
|
||||||
|
//
|
||||||
|
// return this.setResponse(200, false, {
|
||||||
|
// result: 'success',
|
||||||
|
// account: this.asAccount(),
|
||||||
|
// })
|
||||||
|
//}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Finds a user based on one of the accepted unique fields which are:
|
||||||
|
* - lusername (lowercase username)
|
||||||
|
* - ehash
|
||||||
|
* - id
|
||||||
|
*
|
||||||
|
* Stores result in this.record
|
||||||
|
*/
|
||||||
|
//UserModel.prototype.find = async function (body) {
|
||||||
|
// try {
|
||||||
|
// this.record = await this.prisma.user.findFirst({
|
||||||
|
// where: {
|
||||||
|
// OR: [
|
||||||
|
// { lusername: { equals: clean(body.username) } },
|
||||||
|
// { ehash: { equals: hash(clean(body.username)) } },
|
||||||
|
// { id: { equals: parseInt(body.username) || -1 } },
|
||||||
|
// ],
|
||||||
|
// },
|
||||||
|
// })
|
||||||
|
// } catch (err) {
|
||||||
|
// log.warn({ err, body }, `Error while trying to find user: ${body.username}`)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// this.reveal()
|
||||||
|
//
|
||||||
|
// return this.setExists()
|
||||||
|
//}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Checks this.record and sets a boolean to indicate whether
|
||||||
|
* the user exists or not
|
||||||
|
*
|
||||||
|
* Stores result in this.exists
|
||||||
|
*/
|
||||||
|
PersonModel.prototype.setExists = function () {
|
||||||
|
this.exists = this.record ? true : false
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Updates the user data - Used when we create the data ourselves
|
||||||
|
* so we know it's safe
|
||||||
|
*/
|
||||||
|
//UserModel.prototype.safeUpdate = async function (data) {
|
||||||
|
// try {
|
||||||
|
// this.record = await this.prisma.user.update({
|
||||||
|
// where: { id: this.record.id },
|
||||||
|
// data,
|
||||||
|
// })
|
||||||
|
// } catch (err) {
|
||||||
|
// log.warn(err, 'Could not update user record')
|
||||||
|
// process.exit()
|
||||||
|
// return this.setResponse(500, 'updateUserFailed')
|
||||||
|
// }
|
||||||
|
// await this.reveal()
|
||||||
|
//
|
||||||
|
// return this.setResponse(200)
|
||||||
|
//}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Updates the user data - Used when we pass through user-provided data
|
||||||
|
* so we can't be certain it's safe
|
||||||
|
*/
|
||||||
|
//UserModel.prototype.unsafeUpdate = async function (body) {
|
||||||
|
// const data = {}
|
||||||
|
// const notes = []
|
||||||
|
// // Bio
|
||||||
|
// if (typeof body.bio === 'string') data.bio = body.bio
|
||||||
|
// // Consent
|
||||||
|
// if ([0, 1, 2, 3].includes(body.consent)) data.consent = body.consent
|
||||||
|
// // Github
|
||||||
|
// if (typeof body.github === 'string') data.github = body.github.split('@').pop()
|
||||||
|
// // Imperial
|
||||||
|
// if ([true, false].includes(body.imperial)) data.imperial = body.imperial
|
||||||
|
// // Language
|
||||||
|
// if (this.config.languages.includes(body.language)) data.language = body.language
|
||||||
|
// // Newsletter
|
||||||
|
// if ([true, false].includes(body.newsletter)) data.newsletter = body.newsletter
|
||||||
|
// // Password
|
||||||
|
// if (typeof body.password === 'string') data.password = body.password // Will be cloaked below
|
||||||
|
// // Username
|
||||||
|
// if (typeof body.username === 'string') {
|
||||||
|
// const available = await this.isLusernameAvailable(body.username)
|
||||||
|
// if (available) {
|
||||||
|
// data.username = body.username.trim()
|
||||||
|
// data.lusername = clean(body.username)
|
||||||
|
// } else {
|
||||||
|
// log.info(`Rejected user name change from ${data.username} to ${body.username.trim()}`)
|
||||||
|
// notes.push('usernameChangeRejected')
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// // Image (img)
|
||||||
|
// if (typeof body.img === 'string') {
|
||||||
|
// const img = await setPersonAvatar(this.record.id, body.img)
|
||||||
|
// data.img = img.url
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Now update the record
|
||||||
|
// await this.safeUpdate(this.cloak(data))
|
||||||
|
//
|
||||||
|
// const isUnitTest = this.isUnitTest(body)
|
||||||
|
// if (typeof body.email === 'string' && this.clear.email !== clean(body.email)) {
|
||||||
|
// // Email change (requires confirmation)
|
||||||
|
// this.confirmation = await this.Confirmation.create({
|
||||||
|
// type: 'emailchange',
|
||||||
|
// data: {
|
||||||
|
// language: this.record.language,
|
||||||
|
// email: {
|
||||||
|
// current: this.clear.email,
|
||||||
|
// new: body.email,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// userId: this.record.id,
|
||||||
|
// })
|
||||||
|
// if (!isUnitTest || this.config.tests.sendEmail) {
|
||||||
|
// // Send confirmation email
|
||||||
|
// await this.mailer.send({
|
||||||
|
// template: 'emailchange',
|
||||||
|
// language: this.record.language,
|
||||||
|
// to: body.email,
|
||||||
|
// cc: this.clear.email,
|
||||||
|
// replacements: {
|
||||||
|
// actionUrl: i18nUrl(this.language, `/confirm/emailchange/${this.Confirmation.record.id}`),
|
||||||
|
// whyUrl: i18nUrl(this.language, `/docs/faq/email/why-emailchange`),
|
||||||
|
// supportUrl: i18nUrl(this.language, `/patrons/join`),
|
||||||
|
// },
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// } else if (typeof body.confirmation === 'string' && body.confirm === 'emailchange') {
|
||||||
|
// // Handle email change confirmation
|
||||||
|
// await this.Confirmation.read({ id: body.confirmation })
|
||||||
|
//
|
||||||
|
// if (!this.Confirmation.exists) {
|
||||||
|
// log.warn(err, `Could not find confirmation id ${params.id}`)
|
||||||
|
// return this.setResponse(404, 'failedToFindConfirmationId')
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (this.Confirmation.record.type !== 'emailchange') {
|
||||||
|
// log.warn(err, `Confirmation mismatch; ${params.id} is not an emailchange id`)
|
||||||
|
// return this.setResponse(404, 'confirmationIdTypeMismatch')
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// const data = this.Confirmation.clear.data
|
||||||
|
// if (data.email.current === this.clear.email && typeof data.email.new === 'string') {
|
||||||
|
// await this.safeUpdate({
|
||||||
|
// email: this.encrypt(data.email.new),
|
||||||
|
// ehash: hash(clean(data.email.new)),
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// const returnData = {
|
||||||
|
// result: 'success',
|
||||||
|
// account: this.asAccount(),
|
||||||
|
// }
|
||||||
|
// if (isUnitTest) returnData.confirmation = this.Confirmation.record.id
|
||||||
|
//
|
||||||
|
// return this.setResponse(200, false, returnData)
|
||||||
|
//}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns record data
|
||||||
|
*/
|
||||||
|
PersonModel.prototype.asPerson = function () {
|
||||||
|
return this.reveal()
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Helper method to set the response code, result, and body
|
||||||
|
*
|
||||||
|
* Will be used by this.sendResponse()
|
||||||
|
*/
|
||||||
|
PersonModel.prototype.setResponse = function (status = 200, error = false, data = {}) {
|
||||||
|
this.response = {
|
||||||
|
status,
|
||||||
|
body: {
|
||||||
|
result: 'success',
|
||||||
|
...data,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if (status > 201) {
|
||||||
|
this.response.body.error = error
|
||||||
|
this.response.body.result = 'error'
|
||||||
|
this.error = true
|
||||||
|
} else this.error = false
|
||||||
|
|
||||||
|
return this.setExists()
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Helper method to send response
|
||||||
|
*/
|
||||||
|
PersonModel.prototype.sendResponse = async function (res) {
|
||||||
|
return res.status(this.response.status).send(this.response.body)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update method to determine whether this request is
|
||||||
|
* part of a unit test
|
||||||
|
*/
|
||||||
|
//UserModel.prototype.isUnitTest = function (body) {
|
||||||
|
// if (!body.unittest) return false
|
||||||
|
// if (!this.clear.email.split('@').pop() === this.config.tests.domain) return false
|
||||||
|
// if (body.email && !body.email.split('@').pop() === this.config.tests.domain) return false
|
||||||
|
//
|
||||||
|
// return true
|
||||||
|
//}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Helper method to check an account is ok
|
||||||
|
*/
|
||||||
|
//UserModel.prototype.isOk = function () {
|
||||||
|
// if (
|
||||||
|
// this.exists &&
|
||||||
|
// this.record &&
|
||||||
|
// this.record.status > 0 &&
|
||||||
|
// this.record.consent > 0 &&
|
||||||
|
// this.record.role &&
|
||||||
|
// this.record.role !== 'blocked'
|
||||||
|
// )
|
||||||
|
// return true
|
||||||
|
//
|
||||||
|
// return false
|
||||||
|
//}
|
||||||
|
|
||||||
|
/* Helper method to parse user-supplied measurements */
|
||||||
|
PersonModel.prototype.sanitizeMeasurements = function (input) {
|
||||||
|
const measies = {}
|
||||||
|
if (typeof input !== 'object') return measies
|
||||||
|
for (const [m, val] of Object.entries(input)) {
|
||||||
|
if (this.config.measies.includes(m) && typeof val === 'number' && val > 0) measies[m] = val
|
||||||
|
}
|
||||||
|
|
||||||
|
return measies
|
||||||
|
}
|
|
@ -367,7 +367,6 @@ UserModel.prototype.unsafeUpdate = async function (body) {
|
||||||
},
|
},
|
||||||
userId: this.record.id,
|
userId: this.record.id,
|
||||||
})
|
})
|
||||||
console.log(this.config.tests)
|
|
||||||
if (!isUnitTest || this.config.tests.sendEmail) {
|
if (!isUnitTest || this.config.tests.sendEmail) {
|
||||||
// Send confirmation email
|
// Send confirmation email
|
||||||
await this.mailer.send({
|
await this.mailer.send({
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import { apikeyRoutes } from './apikey.mjs'
|
import { apikeyRoutes } from './apikey.mjs'
|
||||||
import { userRoutes } from './user.mjs'
|
import { userRoutes } from './user.mjs'
|
||||||
|
import { personRoutes } from './person.mjs'
|
||||||
|
|
||||||
export const routes = {
|
export const routes = {
|
||||||
apikeyRoutes,
|
apikeyRoutes,
|
||||||
userRoutes,
|
userRoutes,
|
||||||
|
personRoutes,
|
||||||
}
|
}
|
||||||
|
|
41
sites/backend/src/routes/person.mjs
Normal file
41
sites/backend/src/routes/person.mjs
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import { PersonController } from '../controllers/person.mjs'
|
||||||
|
|
||||||
|
const Person = new PersonController()
|
||||||
|
const jwt = ['jwt', { session: false }]
|
||||||
|
const bsc = ['basic', { session: false }]
|
||||||
|
|
||||||
|
export function personRoutes(tools) {
|
||||||
|
const { app, passport } = tools
|
||||||
|
|
||||||
|
// Create person
|
||||||
|
app.post('/people/jwt', passport.authenticate(...jwt), (req, res) =>
|
||||||
|
Person.create(req, res, tools)
|
||||||
|
)
|
||||||
|
app.post('/people/key', passport.authenticate(...bsc), (req, res) =>
|
||||||
|
Person.create(req, res, tools)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Read person
|
||||||
|
app.get('/people/:handle/jwt', passport.authenticate(...jwt), (req, res) =>
|
||||||
|
Person.read(req, res, tools)
|
||||||
|
)
|
||||||
|
app.get('/people/:handle/jwt', passport.authenticate(...bsc), (req, res) =>
|
||||||
|
Person.read(req, res, tools)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Update person
|
||||||
|
app.put('/people/:handle/jwt', passport.authenticate(...jwt), (req, res) =>
|
||||||
|
Person.update(req, res, tools)
|
||||||
|
)
|
||||||
|
app.put('/people/:handle/key', passport.authenticate(...bsc), (req, res) =>
|
||||||
|
Person.update(req, res, tools)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Delete person
|
||||||
|
app.delete('/people/:handle/jwt', passport.authenticate(...jwt), (req, res) =>
|
||||||
|
Person.delete(req, res, tools)
|
||||||
|
)
|
||||||
|
app.delete('/people/:handle/key', passport.authenticate(...bsc), (req, res) =>
|
||||||
|
Person.delete(req, res, tools)
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,12 +1,14 @@
|
||||||
import { userTests } from './user.mjs'
|
import { userTests } from './user.mjs'
|
||||||
import { accountTests } from './account.mjs'
|
import { accountTests } from './account.mjs'
|
||||||
import { apikeyTests } from './apikey.mjs'
|
import { apikeyTests } from './apikey.mjs'
|
||||||
|
import { personTests } from './person.mjs'
|
||||||
import { setup } from './shared.mjs'
|
import { setup } from './shared.mjs'
|
||||||
|
|
||||||
const runTests = async (...params) => {
|
const runTests = async (...params) => {
|
||||||
await userTests(...params)
|
//await userTests(...params)
|
||||||
await apikeyTests(...params)
|
//await apikeyTests(...params)
|
||||||
await accountTests(...params)
|
//await accountTests(...params)
|
||||||
|
await personTests(...params)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load initial data required for tests
|
// Load initial data required for tests
|
||||||
|
|
64
sites/backend/tests/person.mjs
Normal file
64
sites/backend/tests/person.mjs
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
name String @default("")
|
||||||
|
notes String @default("")
|
||||||
|
user User @relation(fields: [userId], references: [id])
|
||||||
|
userId Int
|
||||||
|
measies String @default("{}")
|
||||||
|
Pattern Pattern[]
|
||||||
|
public Boolean @default(false)
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const personTests = async (chai, config, expect, store) => {
|
||||||
|
const data = {
|
||||||
|
jwt: {
|
||||||
|
name: 'Joost',
|
||||||
|
notes: 'These are them notes',
|
||||||
|
measies: {
|
||||||
|
chest: 1000,
|
||||||
|
neck: 420,
|
||||||
|
},
|
||||||
|
public: true,
|
||||||
|
},
|
||||||
|
key: {
|
||||||
|
name: 'Sorcha',
|
||||||
|
notes: 'These are also notes',
|
||||||
|
measies: {
|
||||||
|
chest: 930,
|
||||||
|
neck: 360,
|
||||||
|
},
|
||||||
|
public: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auth of ['jwt', 'key']) {
|
||||||
|
describe(`${store.icon('person', auth)} Person tests (${auth})`, () => {
|
||||||
|
it(`${store.icon('person', auth)} Should create a new person (${auth})`, (done) => {
|
||||||
|
chai
|
||||||
|
.request(config.api)
|
||||||
|
.post(`/people/${auth}`)
|
||||||
|
.set(
|
||||||
|
'Authorization',
|
||||||
|
auth === 'jwt'
|
||||||
|
? 'Bearer ' + store.account.token
|
||||||
|
: 'Basic ' +
|
||||||
|
new Buffer(`${store.account.apikey.key}:${store.account.apikey.secret}`).toString(
|
||||||
|
'base64'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.send(data[auth])
|
||||||
|
.end((err, res) => {
|
||||||
|
console.log(res.body)
|
||||||
|
expect(err === null).to.equal(true)
|
||||||
|
expect(res.status).to.equal(201)
|
||||||
|
expect(res.body.result).to.equal(`success`)
|
||||||
|
for (const [key, val] of Object.entries(data[auth])) {
|
||||||
|
expect(res.body.person[key]).to.equal(val)
|
||||||
|
}
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,7 +7,7 @@ import { randomString } from '../src/utils/crypto.mjs'
|
||||||
|
|
||||||
dotenv.config()
|
dotenv.config()
|
||||||
|
|
||||||
const config = verifyConfig()
|
const config = verifyConfig(true)
|
||||||
const expect = chai.expect
|
const expect = chai.expect
|
||||||
chai.use(http)
|
chai.use(http)
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ export const setup = async () => {
|
||||||
user: '🧑 ',
|
user: '🧑 ',
|
||||||
jwt: '🎫 ',
|
jwt: '🎫 ',
|
||||||
key: '🎟️ ',
|
key: '🎟️ ',
|
||||||
|
person: '🧕 ',
|
||||||
},
|
},
|
||||||
randomString,
|
randomString,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue