1
0
Fork 0

feat(backend): No more people, sets instead

This commit is contained in:
joostdecock 2023-02-26 16:04:12 +01:00
parent 3c31901c2a
commit e691253d51
21 changed files with 308 additions and 316 deletions

View file

@ -40,8 +40,8 @@ BACKEND_WEBSITE_SCHEME=https
# For users # For users
#BACKEND_AVATAR_USER=https://freesewing.org/avatar.svg #BACKEND_AVATAR_USER=https://freesewing.org/avatar.svg
# For people # For measurement sets
#BACKEND_AVATAR_PERSON=https://freesewing.org/avatar.svg #BACKEND_AVATAR_SET=https://freesewing.org/avatar.svg
# For patterns # For patterns
#BACKEND_AVATAR_PATTERN=https://freesewing.org/avatar.svg #BACKEND_AVATAR_PATTERN=https://freesewing.org/avatar.svg

View file

@ -3,7 +3,7 @@ import { schemas } from './lib.mjs'
import { paths as apikeyPaths } from './apikeys.mjs' import { paths as apikeyPaths } from './apikeys.mjs'
//import { paths as confirmationPaths, schemas as confirmationSchemas } from './confirmations.mjs' //import { paths as confirmationPaths, schemas as confirmationSchemas } from './confirmations.mjs'
import { paths as patternPaths } from './patterns.mjs' import { paths as patternPaths } from './patterns.mjs'
import { paths as personPaths } from './people.mjs' import { paths as setPaths } from './sets.mjs'
import { paths as userPaths } from './users.mjs' import { paths as userPaths } from './users.mjs'
const description = ` const description = `
@ -49,7 +49,7 @@ export const openapi = {
paths: { paths: {
...apikeyPaths, ...apikeyPaths,
...patternPaths, ...patternPaths,
...personPaths, ...setPaths,
...userPaths, ...userPaths,
}, },
} }

View file

@ -28,8 +28,8 @@ export const errors = {
mfaTokenMissing: 'The `token` field is missing from the request body', mfaTokenMissing: 'The `token` field is missing from the request body',
nameMissing: 'The `name` field was missing from the request body', nameMissing: 'The `name` field was missing from the request body',
passwordMissing: 'The `password` field is missing from the request body', passwordMissing: 'The `password` field is missing from the request body',
personMissing: 'The request lacks a `person` value in the POST body', setMissing: 'The request lacks a `set` value in the POST body',
personNotNumeric: 'The `person` field in the POST body is not of type `integer`', setNotNumeric: 'The `set` field in the POST body is not of type `integer`',
postBodyMissing: 'The request lacks a POST body', postBodyMissing: 'The request lacks a POST body',
settingsNotAnObject: settingsNotAnObject:
'The `settings` field in the POST body does not contain a value of type `object`', 'The `settings` field in the POST body does not contain a value of type `object`',
@ -221,8 +221,8 @@ export const response = {
type: 'string', type: 'string',
example: 'These are my notes. I can keep them alongside the pattern. Handy!', example: 'These are my notes. I can keep them alongside the pattern. Handy!',
}, },
personId: { setId: {
description: `The unique ID of the Person this pattern was drafted for`, description: `The unique ID of the Measurements Set this pattern was drafted for`,
type: 'integer', type: 'integer',
example: 33, example: 33,
}, },
@ -248,39 +248,39 @@ export const response = {
}, },
}, },
}, },
person: { set: {
description: 'Object holding the data of the person', description: 'Object holding the data of the measurements set',
type: 'object', type: 'object',
properties: { properties: {
id: { id: {
description: `The Person's unique ID`, description: `The Measurements Set's unique ID`,
type: 'integer', type: 'integer',
example: 666, example: 666,
}, },
createdAt: { createdAt: {
description: 'Timestamp of when the Person was created, in ISO 8601 format.', description: 'Timestamp of when the Measurement Set was created, in ISO 8601 format.',
type: 'string', type: 'string',
example: '2022-12-18T18:14:30.460Z', example: '2022-12-18T18:14:30.460Z',
}, },
img: { img: {
description: `An image that was stored with this person`, description: `An image that was stored with this measurements set`,
type: 'string', type: 'string',
example: 'https://freesewing.org/avatar.svg', example: 'https://freesewing.org/avatar.svg',
}, },
imperial: { imperial: {
description: `Whether or not to use imperial units for this person`, description: `Whether or not to use imperial units for this measurements set`,
type: 'boolean', type: 'boolean',
example: false, example: false,
}, },
name: { name: {
description: `The name of the Person exists solely to help you differentiate between your people.`, description: `The name of the Measurements Set exists solely to help you differentiate between your people.`,
type: 'string', type: 'string',
example: 'My bestie Ronda', example: 'My bestie Ronda',
}, },
notes: { notes: {
description: `Any notes to be stored with the person`, description: `Any notes to be stored with the measurements set`,
type: 'string', type: 'string',
example: 'These are my notes. I can keep them alongside the person. Handy!', example: 'These are my notes. I can keep them alongside the measurements set. Handy!',
}, },
public: { public: {
description: `Whether or not this pattern can be viewed/used by others`, description: `Whether or not this pattern can be viewed/used by others`,
@ -288,7 +288,7 @@ export const response = {
example: false, example: false,
}, },
measies: { measies: {
description: `The measurements for this person`, description: `The measurements of this set`,
type: 'object', type: 'object',
example: { neck: 420 }, example: { neck: 420 },
}, },
@ -314,7 +314,7 @@ export const response = {
ehash String @unique ehash String @unique
ihash String ihash String
patterns Pattern[] patterns Pattern[]
people Person[] people Set[]
*/ */
id: { id: {
description: `The User's unique ID`, description: `The User's unique ID`,
@ -336,8 +336,8 @@ Also: Introvert 🙊
description: `This field is about data protection. It indicates the level of consent the user has given to process their data. description: `This field is about data protection. It indicates the level of consent the user has given to process their data.
- \`0\`: No consent given - \`0\`: No consent given
- \`1\`: Consent given to process account data - \`1\`: Consent given to process account data
- \`2\`: Consent given to process account data and person data - \`2\`: Consent given to process account data and measurement data
- \`3\`: Consent given to process account data and person data, and use anonymized data for research - \`3\`: Consent given to process account data and measurement data, and use anonymized data for research
`, `,
type: 'integer', type: 'integer',
enum: [0, 1, 2, 3], enum: [0, 1, 2, 3],
@ -371,7 +371,7 @@ Also: Introvert 🙊
example: 'joostdecock', example: 'joostdecock',
}, },
img: { img: {
description: `An image that was stored with this person`, description: `An image that was stored with this measurements set`,
type: 'string', type: 'string',
example: 'https://freesewing.org/avatar.svg', example: 'https://freesewing.org/avatar.svg',
}, },
@ -486,7 +486,7 @@ export const token = {
export const schemas = { export const schemas = {
apikey: response.body.apikey, apikey: response.body.apikey,
pattern: response.body.pattern, pattern: response.body.pattern,
person: response.body.person, set: response.body.set,
userAccount: response.body.userAccount, userAccount: response.body.userAccount,
userProfile: response.body.userProfile, userProfile: response.body.userProfile,
} }

View file

@ -10,7 +10,7 @@ import {
} from './lib.mjs' } from './lib.mjs'
const common = { const common = {
tags: ['People'], tags: ['Measurements Sets'],
security: [jwt, key], security: [jwt, key],
} }
@ -20,7 +20,7 @@ const local = {
in: 'path', in: 'path',
name: 'id', name: 'id',
required: true, required: true,
description: "The Person's unique ID", description: "The Set's unique ID",
schema: { schema: {
example: 666, example: 666,
type: 'integer', type: 'integer',
@ -32,12 +32,12 @@ const local = {
// Paths // Paths
export const paths = {} export const paths = {}
// Create Person // Create set
paths['/people/{auth}'] = { paths['/sets/{auth}'] = {
post: { post: {
...common, ...common,
summary: 'Create a new Person', summary: 'Create a new Measurements Set',
description: 'Creates a new Person and returns it.', description: 'Creates a new Measurements Set and returns it.',
parameters: [parameters.auth], parameters: [parameters.auth],
requestBody: { requestBody: {
required: true, required: true,
@ -47,11 +47,11 @@ paths['/people/{auth}'] = {
type: 'object', type: 'object',
properties: { properties: {
img: uploadImg, img: uploadImg,
imperial: response.body.person.properties.imperial, imperial: response.body.set.properties.imperial,
name: response.body.person.properties.name, name: response.body.set.properties.name,
notes: response.body.person.properties.notes, notes: response.body.set.properties.notes,
public: response.body.person.properties.public, public: response.body.set.properties.public,
measies: response.body.person.properties.measies, measies: response.body.set.properties.measies,
}, },
}, },
}, },
@ -62,7 +62,7 @@ paths['/people/{auth}'] = {
...response.status['201'], ...response.status['201'],
...jsonResponse({ ...jsonResponse({
result: fields.result, result: fields.result,
person: response.body.person, set: response.body.set,
}), }),
}, },
400: { 400: {
@ -82,25 +82,25 @@ paths['/people/{auth}'] = {
}, },
} }
// Get/Remove Person // Get/Remove Set
paths['/people/{id}/{auth}'] = { paths['/sets/{id}/{auth}'] = {
// Get a Person // Get a Set
get: { get: {
...common, ...common,
summary: 'Retrieve a Person', summary: 'Retrieve a Measurements Set',
description: 'Retrieves information about Person `id`.', description: 'Retrieves information about Measurements Set `id`.',
parameters: [parameters.auth, local.params.id], parameters: [parameters.auth, local.params.id],
responses: { responses: {
200: { 200: {
description: description:
'**Success - Person returned**\n\n' + '**Success - Measurements Set returned**\n\n' +
'Status code `200` indicates that the resource was returned successfully.', 'Status code `200` indicates that the resource was returned successfully.',
...jsonResponse({ ...jsonResponse({
result: { result: {
...fields.result, ...fields.result,
example: 'success', example: 'success',
}, },
person: response.body.person, set: response.body.set,
}), }),
}, },
401: response.status['401'], 401: response.status['401'],
@ -114,11 +114,11 @@ paths['/people/{id}/{auth}'] = {
500: response.status['500'], 500: response.status['500'],
}, },
}, },
// Update a Person // Update a Set
patch: { patch: {
...common, ...common,
summary: 'Update a Person', summary: 'Update a Measurements Set',
description: 'Updates information about Person `id`.', description: 'Updates information about Measurements Set `id`.',
parameters: [parameters.auth, local.params.id], parameters: [parameters.auth, local.params.id],
requestBody: { requestBody: {
required: true, required: true,
@ -128,11 +128,11 @@ paths['/people/{id}/{auth}'] = {
type: 'object', type: 'object',
properties: { properties: {
img: uploadImg, img: uploadImg,
imperial: response.body.person.properties.imperial, imperial: response.body.set.properties.imperial,
name: response.body.person.properties.name, name: response.body.set.properties.name,
notes: response.body.person.properties.notes, notes: response.body.set.properties.notes,
public: response.body.person.properties.public, public: response.body.set.properties.public,
measies: response.body.person.properties.measies, measies: response.body.set.properties.measies,
}, },
}, },
}, },
@ -141,14 +141,14 @@ paths['/people/{id}/{auth}'] = {
responses: { responses: {
200: { 200: {
description: description:
'**Success - Person returned**\n\n' + '**Success - Measurements Set returned**\n\n' +
'Status code `200` indicates that the resource was returned successfully.', 'Status code `200` indicates that the resource was returned successfully.',
...jsonResponse({ ...jsonResponse({
result: { result: {
...fields.result, ...fields.result,
example: 'success', example: 'success',
}, },
person: response.body.person, set: response.body.set,
}), }),
}, },
401: response.status['401'], 401: response.status['401'],
@ -162,11 +162,11 @@ paths['/people/{id}/{auth}'] = {
500: response.status['500'], 500: response.status['500'],
}, },
}, },
// Remove a Person // Remove a Set
delete: { delete: {
...common, ...common,
summary: 'Remove a Person', summary: 'Remove a Set',
description: 'Removes the Person `id`.', description: 'Removes the Measurements Set `id`.',
parameters: [parameters.auth, local.params.id], parameters: [parameters.auth, local.params.id],
responses: { responses: {
204: response.status['204'], 204: response.status['204'],
@ -183,19 +183,19 @@ paths['/people/{id}/{auth}'] = {
}, },
} }
// Clone Person // Clone a Set
paths['/people/{id}/clone/{auth}'] = { paths['/sets/{id}/clone/{auth}'] = {
post: { post: {
...common, ...common,
summary: 'Clone a Person', summary: 'Clone a Measurements Set',
description: 'Creates a new Person by cloning an existing one.', description: 'Creates a new Measurments Set by cloning an existing one.',
parameters: [parameters.auth], parameters: [parameters.auth],
responses: { responses: {
201: { 201: {
...response.status['201'], ...response.status['201'],
...jsonResponse({ ...jsonResponse({
result: fields.result, result: fields.result,
person: response.body.person, set: response.body.set,
}), }),
}, },
401: response.status['401'], 401: response.status['401'],

View file

@ -62,7 +62,7 @@ model User {
password String password String
patron Int @default(0) patron Int @default(0)
patterns Pattern[] patterns Pattern[]
people Person[] sets Set[]
role String @default("user") role String @default("user")
status Int @default(0) status Int @default(0)
updatedAt DateTime? @updatedAt updatedAt DateTime? @updatedAt
@ -78,18 +78,18 @@ model Pattern {
img String? img String?
name String @default("") name String @default("")
notes String notes String
person Person? @relation(fields: [personId], references: [id]) set Set? @relation(fields: [setId], references: [id])
personId Int? setId Int?
public Boolean @default(false) public Boolean @default(false)
settings String settings String
user User @relation(fields: [userId], references: [id]) user User @relation(fields: [userId], references: [id])
userId Int userId Int
updatedAt DateTime @updatedAt updatedAt DateTime @updatedAt
@@index([userId, personId]) @@index([userId, setId])
} }
model Person { model Set {
id Int @id @default(autoincrement()) id Int @id @default(autoincrement())
createdAt DateTime @default(now()) createdAt DateTime @default(now())
img String? img String?

View file

@ -50,7 +50,7 @@ const baseConfig = {
}, },
avatars: { avatars: {
user: process.env.BACKEND_AVATAR_USER || 'https://freesewing.org/avatar.svg', user: process.env.BACKEND_AVATAR_USER || 'https://freesewing.org/avatar.svg',
person: process.env.BACKEND_AVATAR_PERSON || 'https://freesewing.org/avatar.svg', set: process.env.BACKEND_AVATAR_SET || 'https://freesewing.org/avatar.svg',
pattern: process.env.BACKEND_AVATAR_PATTERN || 'https://freesewing.org/avatar.svg', pattern: process.env.BACKEND_AVATAR_PATTERN || 'https://freesewing.org/avatar.svg',
}, },
db: { db: {

View file

@ -1,58 +0,0 @@
import { PersonModel } from '../models/person.mjs'
export function PeopleController() {}
/*
* Create a person for the authenticated user
* See: https://freesewing.dev/reference/backend/api
*/
PeopleController.prototype.create = async (req, res, tools) => {
const Person = new PersonModel(tools)
await Person.guardedCreate(req)
return Person.sendResponse(res)
}
/*
* Read a person
* See: https://freesewing.dev/reference/backend/api
*/
PeopleController.prototype.read = async (req, res, tools) => {
const Person = new PersonModel(tools)
await Person.guardedRead(req)
return Person.sendResponse(res)
}
/*
* Update a person
* See: https://freesewing.dev/reference/backend/api
*/
PeopleController.prototype.update = async (req, res, tools) => {
const Person = new PersonModel(tools)
await Person.guardedUpdate(req)
return Person.sendResponse(res)
}
/*
* Remove a person
* See: https://freesewing.dev/reference/backend/api
*/
PeopleController.prototype.delete = async (req, res, tools) => {
const Person = new PersonModel(tools)
await Person.guardedDelete(req)
return Person.sendResponse(res)
}
/*
* Clone a person
* See: https://freesewing.dev/reference/backend/api
*/
PeopleController.prototype.clone = async (req, res, tools) => {
const Person = new PersonModel(tools)
await Person.guardedClone(req)
return Person.sendResponse(res)
}

View file

@ -0,0 +1,58 @@
import { SetModel } from '../models/set.mjs'
export function SetsController() {}
/*
* Create a measurements set for the authenticated user
* See: https://freesewing.dev/reference/backend/api
*/
SetsController.prototype.create = async (req, res, tools) => {
const Set = new SetModel(tools)
await Set.guardedCreate(req)
return Set.sendResponse(res)
}
/*
* Read a measurements set
* See: https://freesewing.dev/reference/backend/api
*/
SetsController.prototype.read = async (req, res, tools) => {
const Set = new SetModel(tools)
await Set.guardedRead(req)
return Set.sendResponse(res)
}
/*
* Update a measurements set
* See: https://freesewing.dev/reference/backend/api
*/
SetsController.prototype.update = async (req, res, tools) => {
const Set = new SetModel(tools)
await Set.guardedUpdate(req)
return Set.sendResponse(res)
}
/*
* Remove a measurements set
* See: https://freesewing.dev/reference/backend/api
*/
SetsController.prototype.delete = async (req, res, tools) => {
const Set = new SetModel(tools)
await Set.guardedDelete(req)
return Set.sendResponse(res)
}
/*
* Clone a measurements set
* See: https://freesewing.dev/reference/backend/api
*/
SetsController.prototype.clone = async (req, res, tools) => {
const Set = new SetModel(tools)
await Set.guardedClone(req)
return Set.sendResponse(res)
}

View file

@ -15,8 +15,8 @@ export function PatternModel(tools) {
PatternModel.prototype.guardedCreate = async function ({ body, user }) { PatternModel.prototype.guardedCreate = async function ({ body, user }) {
if (user.level < 3) return this.setResponse(403, 'insufficientAccessLevel') if (user.level < 3) return this.setResponse(403, 'insufficientAccessLevel')
if (Object.keys(body).length < 2) return this.setResponse(400, 'postBodyMissing') if (Object.keys(body).length < 2) return this.setResponse(400, 'postBodyMissing')
if (!body.person) return this.setResponse(400, 'personMissing') if (!body.set) return this.setResponse(400, 'setMissing')
if (typeof body.person !== 'number') return this.setResponse(400, 'personNotNumeric') if (typeof body.set !== 'number') return this.setResponse(400, 'setNotNumeric')
if (typeof body.settings !== 'object') return this.setResponse(400, 'settingsNotAnObject') if (typeof body.settings !== 'object') return this.setResponse(400, 'settingsNotAnObject')
if (body.data && typeof body.data !== 'object') return this.setResponse(400, 'dataNotAnObject') if (body.data && typeof body.data !== 'object') return this.setResponse(400, 'dataNotAnObject')
if (!body.design && !body.data?.design) return this.setResponse(400, 'designMissing') if (!body.design && !body.data?.design) return this.setResponse(400, 'designMissing')
@ -25,7 +25,7 @@ PatternModel.prototype.guardedCreate = async function ({ body, user }) {
// Prepare data // Prepare data
const data = { const data = {
design: body.design, design: body.design,
personId: body.person, setId: body.set,
settings: body.settings, settings: body.settings,
} }
// Data (will be encrypted, so always set _some_ value) // Data (will be encrypted, so always set _some_ value)

View file

@ -1,7 +1,7 @@
import { log } from '../utils/log.mjs' import { log } from '../utils/log.mjs'
import { setPersonAvatar } from '../utils/sanity.mjs' import { setSetAvatar } from '../utils/sanity.mjs'
export function PersonModel(tools) { export function SetModel(tools) {
this.config = tools.config this.config = tools.config
this.prisma = tools.prisma this.prisma = tools.prisma
this.decrypt = tools.decrypt this.decrypt = tools.decrypt
@ -12,7 +12,7 @@ export function PersonModel(tools) {
return this return this
} }
PersonModel.prototype.guardedCreate = async function ({ body, user }) { SetModel.prototype.guardedCreate = async function ({ body, user }) {
if (user.level < 3) return this.setResponse(403, 'insufficientAccessLevel') if (user.level < 3) return this.setResponse(403, 'insufficientAccessLevel')
if (Object.keys(body) < 1) return this.setResponse(400, 'postBodyMissing') if (Object.keys(body) < 1) return this.setResponse(400, 'postBodyMissing')
if (!body.name || typeof body.name !== 'string') return this.setResponse(400, 'nameMissing') if (!body.name || typeof body.name !== 'string') return this.setResponse(400, 'nameMissing')
@ -30,7 +30,7 @@ PersonModel.prototype.guardedCreate = async function ({ body, user }) {
data.imperial = body.imperial === true ? true : false data.imperial = body.imperial === true ? true : false
data.userId = user.uid data.userId = user.uid
// Set this one initially as we need the ID to create a custom img via Sanity // Set this one initially as we need the ID to create a custom img via Sanity
data.img = this.config.avatars.person data.img = this.config.avatars.set
// Create record // Create record
await this.unguardedCreate(data) await this.unguardedCreate(data)
@ -40,36 +40,36 @@ PersonModel.prototype.guardedCreate = async function ({ body, user }) {
this.config.use.sanity && this.config.use.sanity &&
typeof body.img === 'string' && typeof body.img === 'string' &&
(!body.unittest || (body.unittest && this.config.use.tests?.sanity)) (!body.unittest || (body.unittest && this.config.use.tests?.sanity))
? await setPersonAvatar(this.record.id, body.img) ? await setSetAvatar(this.record.id, body.img)
: false : false
if (img) await this.unguardedUpdate(this.cloak({ img: img.url })) if (img) await this.unguardedUpdate(this.cloak({ img: img.url }))
else await this.read({ id: this.record.id }) else await this.read({ id: this.record.id })
return this.setResponse(201, 'created', { person: this.asPerson() }) return this.setResponse(201, 'created', { set: this.asSet() })
} }
PersonModel.prototype.unguardedCreate = async function (data) { SetModel.prototype.unguardedCreate = async function (data) {
try { try {
this.record = await this.prisma.person.create({ data: this.cloak(data) }) this.record = await this.prisma.set.create({ data: this.cloak(data) })
} catch (err) { } catch (err) {
log.warn(err, 'Could not create person') log.warn(err, 'Could not create set')
return this.setResponse(500, 'createPersonFailed') return this.setResponse(500, 'createSetFailed')
} }
return this return this
} }
/* /*
* Loads a person from the database based on the where clause you pass it * Loads a measurements set from the database based on the where clause you pass it
* *
* Stores result in this.record * Stores result in this.record
*/ */
PersonModel.prototype.read = async function (where) { SetModel.prototype.read = async function (where) {
try { try {
this.record = await this.prisma.person.findUnique({ where }) this.record = await this.prisma.set.findUnique({ where })
} catch (err) { } catch (err) {
log.warn({ err, where }, 'Could not read person') log.warn({ err, where }, 'Could not read measurements set')
} }
this.reveal() this.reveal()
@ -78,12 +78,12 @@ PersonModel.prototype.read = async function (where) {
} }
/* /*
* Loads a person from the database based on the where clause you pass it * Loads a measurements set from the database based on the where clause you pass it
* In addition prepares it for returning the person data * In addition prepares it for returning the set data
* *
* Stores result in this.record * Stores result in this.record
*/ */
PersonModel.prototype.guardedRead = async function ({ params, user }) { SetModel.prototype.guardedRead = async function ({ params, user }) {
if (user.level < 1) return this.setResponse(403, 'insufficientAccessLevel') if (user.level < 1) return this.setResponse(403, 'insufficientAccessLevel')
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking') if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
@ -94,17 +94,17 @@ PersonModel.prototype.guardedRead = async function ({ params, user }) {
return this.setResponse(200, false, { return this.setResponse(200, false, {
result: 'success', result: 'success',
person: this.asPerson(), set: this.asSet(),
}) })
} }
/* /*
* Clones a person * Clones a measurements set
* In addition prepares it for returning the person data * In addition prepares it for returning the set data
* *
* Stores result in this.record * Stores result in this.record
*/ */
PersonModel.prototype.guardedClone = async function ({ params, user }) { SetModel.prototype.guardedClone = async function ({ params, user }) {
if (user.level < 3) return this.setResponse(403, 'insufficientAccessLevel') if (user.level < 3) return this.setResponse(403, 'insufficientAccessLevel')
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking') if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
@ -113,11 +113,11 @@ PersonModel.prototype.guardedClone = async function ({ params, user }) {
return this.setResponse(403, 'insufficientAccessLevel') return this.setResponse(403, 'insufficientAccessLevel')
} }
// Clone person // Clone set
const data = this.asPerson() const data = this.asSet()
delete data.id delete data.id
data.name += ` (cloned from #${this.record.id})` data.name += ` (cloned from #${this.record.id})`
data.notes += ` (Note: This person was cloned from person #${this.record.id})` data.notes += ` (Note: This measurements set was cloned from set #${this.record.id})`
await this.unguardedCreate(data) await this.unguardedCreate(data)
// Update unencrypted data // Update unencrypted data
@ -125,14 +125,14 @@ PersonModel.prototype.guardedClone = async function ({ params, user }) {
return this.setResponse(200, false, { return this.setResponse(200, false, {
result: 'success', result: 'success',
person: this.asPerson(), set: this.asSet(),
}) })
} }
/* /*
* Helper method to decrypt at-rest data * Helper method to decrypt at-rest data
*/ */
PersonModel.prototype.reveal = async function () { SetModel.prototype.reveal = async function () {
this.clear = {} this.clear = {}
if (this.record) { if (this.record) {
for (const field of this.encryptedFields) { for (const field of this.encryptedFields) {
@ -146,7 +146,7 @@ PersonModel.prototype.reveal = async function () {
/* /*
* Helper method to encrypt at-rest data * Helper method to encrypt at-rest data
*/ */
PersonModel.prototype.cloak = function (data) { SetModel.prototype.cloak = function (data) {
for (const field of this.encryptedFields) { for (const field of this.encryptedFields) {
if (typeof data[field] !== 'undefined') { if (typeof data[field] !== 'undefined') {
data[field] = this.encrypt(data[field]) data[field] = this.encrypt(data[field])
@ -162,26 +162,26 @@ PersonModel.prototype.cloak = function (data) {
* *
* Stores result in this.exists * Stores result in this.exists
*/ */
PersonModel.prototype.setExists = function () { SetModel.prototype.setExists = function () {
this.exists = this.record ? true : false this.exists = this.record ? true : false
return this return this
} }
/* /*
* Updates the person data - Used when we create the data ourselves * Updates the set data - Used when we create the data ourselves
* so we know it's safe * so we know it's safe
*/ */
PersonModel.prototype.unguardedUpdate = async function (data) { SetModel.prototype.unguardedUpdate = async function (data) {
try { try {
this.record = await this.prisma.person.update({ this.record = await this.prisma.set.update({
where: { id: this.record.id }, where: { id: this.record.id },
data, data,
}) })
} catch (err) { } catch (err) {
log.warn(err, 'Could not update person record') log.warn(err, 'Could not update set record')
process.exit() process.exit()
return this.setResponse(500, 'updatePersonFailed') return this.setResponse(500, 'updateSetFailed')
} }
await this.reveal() await this.reveal()
@ -189,10 +189,10 @@ PersonModel.prototype.unguardedUpdate = async function (data) {
} }
/* /*
* Updates the person data - Used when we pass through user-provided data * Updates the set data - Used when we pass through user-provided data
* so we can't be certain it's safe * so we can't be certain it's safe
*/ */
PersonModel.prototype.guardedUpdate = async function ({ params, body, user }) { SetModel.prototype.guardedUpdate = async function ({ params, body, user }) {
if (user.level < 3) return this.setResponse(403, 'insufficientAccessLevel') if (user.level < 3) return this.setResponse(403, 'insufficientAccessLevel')
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking') if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
await this.read({ id: parseInt(params.id) }) await this.read({ id: parseInt(params.id) })
@ -224,21 +224,21 @@ PersonModel.prototype.guardedUpdate = async function ({ params, body, user }) {
// Image (img) // Image (img)
if (typeof body.img === 'string') { if (typeof body.img === 'string') {
const img = await setPersonAvatar(params.id, body.img) const img = await setSetAvatar(params.id, body.img)
data.img = img.url data.img = img.url
} }
// Now update the record // Now update the record
await this.unguardedUpdate(this.cloak(data)) await this.unguardedUpdate(this.cloak(data))
return this.setResponse(200, false, { person: this.asPerson() }) return this.setResponse(200, false, { set: this.asSet() })
} }
/* /*
* Removes the person - No questions asked * Removes the set - No questions asked
*/ */
PersonModel.prototype.unguardedDelete = async function () { SetModel.prototype.unguardedDelete = async function () {
await this.prisma.person.delete({ here: { id: this.record.id } }) await this.prisma.set.delete({ here: { id: this.record.id } })
this.record = null this.record = null
this.clear = null this.clear = null
@ -246,9 +246,9 @@ PersonModel.prototype.unguardedDelete = async function () {
} }
/* /*
* Removes the person - Checks permissions * Removes the set - Checks permissions
*/ */
PersonModel.prototype.guardedDelete = async function ({ params, body, user }) { SetModel.prototype.guardedDelete = async function ({ params, body, user }) {
if (user.level < 3) return this.setResponse(403, 'insufficientAccessLevel') if (user.level < 3) return this.setResponse(403, 'insufficientAccessLevel')
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking') if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
@ -265,7 +265,7 @@ PersonModel.prototype.guardedDelete = async function ({ params, body, user }) {
/* /*
* Returns record data * Returns record data
*/ */
PersonModel.prototype.asPerson = function () { SetModel.prototype.asSet = function () {
return { return {
...this.record, ...this.record,
...this.clear, ...this.clear,
@ -277,7 +277,7 @@ PersonModel.prototype.asPerson = function () {
* *
* Will be used by this.sendResponse() * Will be used by this.sendResponse()
*/ */
PersonModel.prototype.setResponse = function (status = 200, error = false, data = {}) { SetModel.prototype.setResponse = function (status = 200, error = false, data = {}) {
this.response = { this.response = {
status, status,
body: { body: {
@ -297,7 +297,7 @@ PersonModel.prototype.setResponse = function (status = 200, error = false, data
/* /*
* Helper method to send response * Helper method to send response
*/ */
PersonModel.prototype.sendResponse = async function (res) { SetModel.prototype.sendResponse = async function (res) {
return res.status(this.response.status).send(this.response.body) return res.status(this.response.status).send(this.response.body)
} }
@ -331,7 +331,7 @@ PersonModel.prototype.sendResponse = async function (res) {
//} //}
/* Helper method to parse user-supplied measurements */ /* Helper method to parse user-supplied measurements */
PersonModel.prototype.sanitizeMeasurements = function (input) { SetModel.prototype.sanitizeMeasurements = function (input) {
const measies = {} const measies = {}
if (typeof input !== 'object') return measies if (typeof input !== 'object') return measies
for (const [m, val] of Object.entries(input)) { for (const [m, val] of Object.entries(input)) {

View file

@ -151,7 +151,7 @@ UserModel.prototype.guardedCreate = async function ({ body }) {
await this.read({ ehash }) await this.read({ ehash })
if (this.exists) { if (this.exists) {
/* /*
* User already exists. However, if we return an error, then people can * User already exists. However, if we return an error, then baddies can
* spam the signup endpoint to figure out who has a FreeSewing account * spam the signup endpoint to figure out who has a FreeSewing account
* which would be a privacy leak. So instead, pretend there is no user * which would be a privacy leak. So instead, pretend there is no user
* with that account, and that signup is proceeding as normal. * with that account, and that signup is proceeding as normal.
@ -253,7 +253,6 @@ UserModel.prototype.guardedCreate = async function ({ body }) {
}, },
userId: this.record.id, userId: this.record.id,
}) })
console.log(check)
// Send signup email // Send signup email
if (!this.isUnitTest(body) || this.config.tests.sendEmail) if (!this.isUnitTest(body) || this.config.tests.sendEmail)
@ -497,7 +496,8 @@ UserModel.prototype.guardedUpdate = async function ({ body, user }) {
result: 'success', result: 'success',
account: this.asAccount(), account: this.asAccount(),
} }
if (isUnitTest) returnData.confirmation = this.Confirmation.record.id if (isUnitTest && this.Confirmation.record?.id)
returnData.confirmation = this.Confirmation.record.id
return this.setResponse(200, false, returnData) return this.setResponse(200, false, returnData)
} }
@ -519,7 +519,6 @@ UserModel.prototype.guardedMfaUpdate = async function ({ body, user, ip }) {
// Check password // Check password
const [valid] = verifyPassword(body.password, this.record.password) const [valid] = verifyPassword(body.password, this.record.password)
if (!valid) { if (!valid) {
console.log('password check failed')
log.warn(`Wrong password for existing user while disabling MFA: ${user.uid} from ${ip}`) log.warn(`Wrong password for existing user while disabling MFA: ${user.uid} from ${ip}`)
return this.setResponse(401, 'authenticationFailed') return this.setResponse(401, 'authenticationFailed')
} }
@ -537,7 +536,6 @@ UserModel.prototype.guardedMfaUpdate = async function ({ body, user, ip }) {
account: this.asAccount(), account: this.asAccount(),
}) })
} else { } else {
console.log('token check failed')
return this.setResponse(401, 'authenticationFailed') return this.setResponse(401, 'authenticationFailed')
} }
} }
@ -713,7 +711,7 @@ UserModel.prototype.isLusernameAvailable = async function (lusername) {
user = await this.prisma.user.findUnique({ where: { lusername } }) user = await this.prisma.user.findUnique({ where: { lusername } })
} catch (err) { } catch (err) {
log.warn({ err, lusername }, 'Could not search for free username') log.warn({ err, lusername }, 'Could not search for free username')
return true return false
} }
if (user) return false if (user) return false

View file

@ -19,7 +19,7 @@ export function apikeysRoutes(tools) {
app.get('/apikeys/jwt', passport.authenticate(...jwt), (req, res) => app.get('/apikeys/jwt', passport.authenticate(...jwt), (req, res) =>
Apikeys.list(req, res, tools) Apikeys.list(req, res, tools)
) )
app.get('/apikeys/:id/key', passport.authenticate(...bsc), (req, res) => app.get('/apikeys/key', passport.authenticate(...bsc), (req, res) =>
Apikeys.list(req, res, tools) Apikeys.list(req, res, tools)
) )

View file

@ -1,13 +1,13 @@
import { apikeysRoutes } from './apikeys.mjs' import { apikeysRoutes } from './apikeys.mjs'
import { usersRoutes } from './users.mjs' import { usersRoutes } from './users.mjs'
import { peopleRoutes } from './people.mjs' import { setsRoutes } from './sets.mjs'
import { patternsRoutes } from './patterns.mjs' import { patternsRoutes } from './patterns.mjs'
import { confirmationsRoutes } from './confirmations.mjs' import { confirmationsRoutes } from './confirmations.mjs'
export const routes = { export const routes = {
apikeysRoutes, apikeysRoutes,
usersRoutes, usersRoutes,
peopleRoutes, setsRoutes,
patternsRoutes, patternsRoutes,
confirmationsRoutes, confirmationsRoutes,
} }

View file

@ -1,49 +0,0 @@
import { PeopleController } from '../controllers/people.mjs'
const People = new PeopleController()
const jwt = ['jwt', { session: false }]
const bsc = ['basic', { session: false }]
export function peopleRoutes(tools) {
const { app, passport } = tools
// Create person
app.post('/people/jwt', passport.authenticate(...jwt), (req, res) =>
People.create(req, res, tools)
)
app.post('/people/key', passport.authenticate(...bsc), (req, res) =>
People.create(req, res, tools)
)
// Clone person
app.post('/people/:id/clone/jwt', passport.authenticate(...jwt), (req, res) =>
People.clone(req, res, tools)
)
app.post('/people/:id/clone/key', passport.authenticate(...bsc), (req, res) =>
People.clone(req, res, tools)
)
// Read person
app.get('/people/:id/jwt', passport.authenticate(...jwt), (req, res) =>
People.read(req, res, tools)
)
app.get('/people/:id/key', passport.authenticate(...bsc), (req, res) =>
People.read(req, res, tools)
)
// Update person
app.patch('/people/:id/jwt', passport.authenticate(...jwt), (req, res) =>
People.update(req, res, tools)
)
app.patch('/people/:id/key', passport.authenticate(...bsc), (req, res) =>
People.update(req, res, tools)
)
// Delete person
app.delete('/people/:id/jwt', passport.authenticate(...jwt), (req, res) =>
People.delete(req, res, tools)
)
app.delete('/people/:id/key', passport.authenticate(...bsc), (req, res) =>
People.delete(req, res, tools)
)
}

View file

@ -0,0 +1,41 @@
import { SetsController } from '../controllers/sets.mjs'
const Sets = new SetsController()
const jwt = ['jwt', { session: false }]
const bsc = ['basic', { session: false }]
export function setsRoutes(tools) {
const { app, passport } = tools
// Create a measurments set
app.post('/sets/jwt', passport.authenticate(...jwt), (req, res) => Sets.create(req, res, tools))
app.post('/sets/key', passport.authenticate(...bsc), (req, res) => Sets.create(req, res, tools))
// Clone a measurements set
app.post('/sets/:id/clone/jwt', passport.authenticate(...jwt), (req, res) =>
Sets.clone(req, res, tools)
)
app.post('/sets/:id/clone/key', passport.authenticate(...bsc), (req, res) =>
Sets.clone(req, res, tools)
)
// Read a measurments set
app.get('/sets/:id/jwt', passport.authenticate(...jwt), (req, res) => Sets.read(req, res, tools))
app.get('/sets/:id/key', passport.authenticate(...bsc), (req, res) => Sets.read(req, res, tools))
// Update a measurements set
app.patch('/sets/:id/jwt', passport.authenticate(...jwt), (req, res) =>
Sets.update(req, res, tools)
)
app.patch('/sets/:id/key', passport.authenticate(...bsc), (req, res) =>
Sets.update(req, res, tools)
)
// Delete a measurements set
app.delete('/sets/:id/jwt', passport.authenticate(...jwt), (req, res) =>
Sets.delete(req, res, tools)
)
app.delete('/sets/:id/key', passport.authenticate(...bsc), (req, res) =>
Sets.delete(req, res, tools)
)
}

View file

@ -15,7 +15,7 @@ function imageUrl(ref) {
* Retrieval of avatar images from the Sanity API * Retrieval of avatar images from the Sanity API
*/ */
export const getUserAvatar = async (id) => getAvatar('user', id) export const getUserAvatar = async (id) => getAvatar('user', id)
export const getPersonAvatar = async (id) => getAvatar('person', id) export const getSetAvatar = async (id) => getAvatar('set', id)
async function getAvatar(type, id) { async function getAvatar(type, id) {
const url = const url =
`${config.api}/data/query/${config.dataset}?query=` + `${config.api}/data/query/${config.dataset}?query=` +
@ -32,7 +32,7 @@ async function getAvatar(type, id) {
* Uploads an image to sanity * Uploads an image to sanity
*/ */
export const setUserAvatar = async (id, data) => setAvatar('user', id, data) export const setUserAvatar = async (id, data) => setAvatar('user', id, data)
export const setPersonAvatar = async (id, data) => setAvatar('person', id, data) export const setSetAvatar = async (id, data) => setAvatar('set', id, data)
export const setPatternAvatar = async (id, data) => setAvatar('pattern', id, data) export const setPatternAvatar = async (id, data) => setAvatar('pattern', id, data)
export async function setAvatar(type, id, data) { export async function setAvatar(type, id, data) {
// Step 1: Upload the image as asset // Step 1: Upload the image as asset

View file

@ -2,7 +2,7 @@ import { userTests } from './user.mjs'
import { mfaTests } from './mfa.mjs' import { mfaTests } from './mfa.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 { setTests } from './set.mjs'
import { patternTests } from './pattern.mjs' import { patternTests } from './pattern.mjs'
import { setup } from './shared.mjs' import { setup } from './shared.mjs'
@ -11,7 +11,7 @@ const runTests = async (...params) => {
await mfaTests(...params) await mfaTests(...params)
await apikeyTests(...params) await apikeyTests(...params)
await accountTests(...params) await accountTests(...params)
await personTests(...params) await setTests(...params)
await patternTests(...params) await patternTests(...params)
} }

View file

@ -25,7 +25,7 @@ export const patternTests = async (chai, config, expect, store) => {
name: 'Just a test', name: 'Just a test',
notes: 'These are my notes', notes: 'These are my notes',
public: true, public: true,
person: store.account.people.her.id, set: store.account.sets.her.id,
data: { data: {
some: 'value', some: 'value',
}, },
@ -37,7 +37,7 @@ export const patternTests = async (chai, config, expect, store) => {
expect(res.body.result).to.equal(`success`) expect(res.body.result).to.equal(`success`)
expect(typeof res.body.pattern?.id).to.equal('number') expect(typeof res.body.pattern?.id).to.equal('number')
expect(res.body.pattern.userId).to.equal(store.account.id) expect(res.body.pattern.userId).to.equal(store.account.id)
expect(res.body.pattern.personId).to.equal(store.account.people.her.id) expect(res.body.pattern.setId).to.equal(store.account.sets.her.id)
expect(res.body.pattern.design).to.equal('aaron') expect(res.body.pattern.design).to.equal('aaron')
expect(res.body.pattern.public).to.equal(true) expect(res.body.pattern.public).to.equal(true)
store.account.patterns[auth] = res.body.pattern store.account.patterns[auth] = res.body.pattern
@ -73,7 +73,7 @@ export const patternTests = async (chai, config, expect, store) => {
}) })
} }
it(`${store.icon('person', auth)} Should update the public field (${auth})`, (done) => { it(`${store.icon('set', auth)} Should update the public field (${auth})`, (done) => {
chai chai
.request(config.api) .request(config.api)
.patch(`/patterns/${store.account.patterns[auth].id}/${auth}`) .patch(`/patterns/${store.account.patterns[auth].id}/${auth}`)
@ -96,7 +96,7 @@ export const patternTests = async (chai, config, expect, store) => {
}) })
}) })
it(`${store.icon('person', auth)} Should not update the design field (${auth})`, (done) => { it(`${store.icon('set', auth)} Should not update the design field (${auth})`, (done) => {
chai chai
.request(config.api) .request(config.api)
.patch(`/patterns/${store.account.patterns[auth].id}/${auth}`) .patch(`/patterns/${store.account.patterns[auth].id}/${auth}`)
@ -119,7 +119,7 @@ export const patternTests = async (chai, config, expect, store) => {
}) })
}) })
it(`${store.icon('person', auth)} Should not update the person field (${auth})`, (done) => { it(`${store.icon('set', auth)} Should not update the set field (${auth})`, (done) => {
chai chai
.request(config.api) .request(config.api)
.patch(`/patterns/${store.account.patterns[auth].id}/${auth}`) .patch(`/patterns/${store.account.patterns[auth].id}/${auth}`)
@ -132,18 +132,18 @@ export const patternTests = async (chai, config, expect, store) => {
'base64' 'base64'
) )
) )
.send({ person: 1 }) .send({ set: 1 })
.end((err, res) => { .end((err, res) => {
expect(err === null).to.equal(true) expect(err === null).to.equal(true)
expect(res.status).to.equal(200) expect(res.status).to.equal(200)
expect(res.body.result).to.equal(`success`) expect(res.body.result).to.equal(`success`)
expect(res.body.pattern.personId).to.equal(store.account.people.her.id) expect(res.body.pattern.setId).to.equal(store.account.sets.her.id)
done() done()
}) })
}) })
for (const field of ['data', 'settings']) { for (const field of ['data', 'settings']) {
it(`${store.icon('person', auth)} Should update the ${field} field (${auth})`, (done) => { it(`${store.icon('set', auth)} Should update the ${field} field (${auth})`, (done) => {
const data = {} const data = {}
data[field] = { test: { value: 'hello' } } data[field] = { test: { value: 'hello' } }
chai chai
@ -192,7 +192,7 @@ export const patternTests = async (chai, config, expect, store) => {
}) })
it(`${store.icon( it(`${store.icon(
'person', 'set',
auth auth
)} Should not allow reading another user's pattern (${auth})`, (done) => { )} Should not allow reading another user's pattern (${auth})`, (done) => {
chai chai
@ -217,7 +217,7 @@ export const patternTests = async (chai, config, expect, store) => {
}) })
it(`${store.icon( it(`${store.icon(
'person', 'set',
auth auth
)} Should not allow updating another user's pattern (${auth})`, (done) => { )} Should not allow updating another user's pattern (${auth})`, (done) => {
chai chai
@ -245,7 +245,7 @@ export const patternTests = async (chai, config, expect, store) => {
}) })
it(`${store.icon( it(`${store.icon(
'person', 'set',
auth auth
)} Should not allow removing another user's pattern (${auth})`, (done) => { )} Should not allow removing another user's pattern (${auth})`, (done) => {
chai chai
@ -270,10 +270,10 @@ export const patternTests = async (chai, config, expect, store) => {
}) })
/* /*
it(`${store.icon('person', auth)} Should clone a person (${auth})`, (done) => { it(`${store.icon('set', auth)} Should clone a set (${auth})`, (done) => {
chai chai
.request(config.api) .request(config.api)
.post(`/people/${store.person[auth].id}/clone/${auth}`) .post(`/sets/${store.set[auth].id}/clone/${auth}`)
.set( .set(
'Authorization', 'Authorization',
auth === 'jwt' auth === 'jwt'
@ -288,18 +288,18 @@ export const patternTests = async (chai, config, expect, store) => {
expect(res.status).to.equal(200) expect(res.status).to.equal(200)
expect(res.body.result).to.equal(`success`) expect(res.body.result).to.equal(`success`)
expect(typeof res.body.error).to.equal(`undefined`) expect(typeof res.body.error).to.equal(`undefined`)
expect(typeof res.body.person.id).to.equal(`number`) expect(typeof res.body.set.id).to.equal(`number`)
done() done()
}) })
}) })
it(`${store.icon( it(`${store.icon(
'person', 'set',
auth auth
)} Should (not) clone a public person across accounts (${auth})`, (done) => { )} Should (not) clone a public set across accounts (${auth})`, (done) => {
chai chai
.request(config.api) .request(config.api)
.post(`/people/${store.person[auth].id}/clone/${auth}`) .post(`/sets/${store.set[auth].id}/clone/${auth}`)
.set( .set(
'Authorization', 'Authorization',
auth === 'jwt' auth === 'jwt'
@ -310,12 +310,12 @@ export const patternTests = async (chai, config, expect, store) => {
).toString('base64') ).toString('base64')
) )
.end((err, res) => { .end((err, res) => {
if (store.person[auth].public) { if (store.set[auth].public) {
expect(err === null).to.equal(true) expect(err === null).to.equal(true)
expect(res.status).to.equal(200) expect(res.status).to.equal(200)
expect(res.body.result).to.equal(`success`) expect(res.body.result).to.equal(`success`)
expect(typeof res.body.error).to.equal(`undefined`) expect(typeof res.body.error).to.equal(`undefined`)
expect(typeof res.body.person.id).to.equal(`number`) expect(typeof res.body.set.id).to.equal(`number`)
} else { } else {
expect(err === null).to.equal(true) expect(err === null).to.equal(true)
expect(res.status).to.equal(403) expect(res.status).to.equal(403)
@ -327,8 +327,8 @@ export const patternTests = async (chai, config, expect, store) => {
}) })
// TODO: // TODO:
// - Clone person // - Clone set
// - Clone person accross accounts of they are public // - Clone set accross accounts of they are public
*/ */
}) })
} }

View file

@ -1,6 +1,6 @@
import { cat } from './cat.mjs' import { cat } from './cat.mjs'
export const personTests = async (chai, config, expect, store) => { export const setTests = async (chai, config, expect, store) => {
const data = { const data = {
jwt: { jwt: {
name: 'Joost', name: 'Joost',
@ -26,21 +26,21 @@ export const personTests = async (chai, config, expect, store) => {
imperial: false, imperial: false,
}, },
} }
store.person = { store.set = {
jwt: {}, jwt: {},
key: {}, key: {},
} }
store.altperson = { store.altset = {
jwt: {}, jwt: {},
key: {}, key: {},
} }
for (const auth of ['jwt', 'key']) { for (const auth of ['jwt', 'key']) {
describe(`${store.icon('person', auth)} Person tests (${auth})`, () => { describe(`${store.icon('set', auth)} Set tests (${auth})`, () => {
step(`${store.icon('person', auth)} Should create a new person (${auth})`, (done) => { step(`${store.icon('set', auth)} Should create a new set (${auth})`, (done) => {
chai chai
.request(config.api) .request(config.api)
.post(`/people/${auth}`) .post(`/sets/${auth}`)
.set( .set(
'Authorization', 'Authorization',
auth === 'jwt' auth === 'jwt'
@ -57,21 +57,21 @@ export const personTests = async (chai, config, expect, store) => {
expect(res.body.result).to.equal(`success`) expect(res.body.result).to.equal(`success`)
for (const [key, val] of Object.entries(data[auth])) { for (const [key, val] of Object.entries(data[auth])) {
if (!['measies', 'img', 'unittest'].includes(key)) if (!['measies', 'img', 'unittest'].includes(key))
expect(res.body.person[key]).to.equal(val) expect(res.body.set[key]).to.equal(val)
} }
store.person[auth] = res.body.person store.set[auth] = res.body.set
done() done()
}) })
}).timeout(5000) }).timeout(5000)
for (const field of ['name', 'notes']) { for (const field of ['name', 'notes']) {
it(`${store.icon('person', auth)} Should update the ${field} field (${auth})`, (done) => { it(`${store.icon('set', auth)} Should update the ${field} field (${auth})`, (done) => {
const data = {} const data = {}
const val = store.person[auth][field] + '_updated' const val = store.set[auth][field] + '_updated'
data[field] = val data[field] = val
chai chai
.request(config.api) .request(config.api)
.patch(`/people/${store.person[auth].id}/${auth}`) .patch(`/sets/${store.set[auth].id}/${auth}`)
.set( .set(
'Authorization', 'Authorization',
auth === 'jwt' auth === 'jwt'
@ -86,20 +86,20 @@ export const personTests = async (chai, config, expect, store) => {
expect(err === null).to.equal(true) expect(err === null).to.equal(true)
expect(res.status).to.equal(200) expect(res.status).to.equal(200)
expect(res.body.result).to.equal(`success`) expect(res.body.result).to.equal(`success`)
expect(res.body.person[field]).to.equal(val) expect(res.body.set[field]).to.equal(val)
done() done()
}) })
}) })
} }
for (const field of ['imperial', 'public']) { for (const field of ['imperial', 'public']) {
it(`${store.icon('person', auth)} Should update the ${field} field (${auth})`, (done) => { it(`${store.icon('set', auth)} Should update the ${field} field (${auth})`, (done) => {
const data = {} const data = {}
const val = !store.person[auth][field] const val = !store.set[auth][field]
data[field] = val data[field] = val
chai chai
.request(config.api) .request(config.api)
.patch(`/people/${store.person[auth].id}/${auth}`) .patch(`/sets/${store.set[auth].id}/${auth}`)
.set( .set(
'Authorization', 'Authorization',
auth === 'jwt' auth === 'jwt'
@ -114,8 +114,8 @@ export const personTests = async (chai, config, expect, store) => {
expect(err === null).to.equal(true) expect(err === null).to.equal(true)
expect(res.status).to.equal(200) expect(res.status).to.equal(200)
expect(res.body.result).to.equal(`success`) expect(res.body.result).to.equal(`success`)
expect(res.body.person[field]).to.equal(val) expect(res.body.set[field]).to.equal(val)
store.person[auth][field] = val store.set[auth][field] = val
done() done()
}) })
}) })
@ -123,7 +123,7 @@ export const personTests = async (chai, config, expect, store) => {
for (const field of ['chest', 'neck', 'ankle']) { for (const field of ['chest', 'neck', 'ankle']) {
it(`${store.icon( it(`${store.icon(
'person', 'set',
auth auth
)} Should update the ${field} measurement (${auth})`, (done) => { )} Should update the ${field} measurement (${auth})`, (done) => {
const data = { measies: {} } const data = { measies: {} }
@ -131,7 +131,7 @@ export const personTests = async (chai, config, expect, store) => {
data.measies[field] = val data.measies[field] = val
chai chai
.request(config.api) .request(config.api)
.patch(`/people/${store.person[auth].id}/${auth}`) .patch(`/sets/${store.set[auth].id}/${auth}`)
.set( .set(
'Authorization', 'Authorization',
auth === 'jwt' auth === 'jwt'
@ -146,19 +146,19 @@ export const personTests = async (chai, config, expect, store) => {
expect(err === null).to.equal(true) expect(err === null).to.equal(true)
expect(res.status).to.equal(200) expect(res.status).to.equal(200)
expect(res.body.result).to.equal(`success`) expect(res.body.result).to.equal(`success`)
expect(res.body.person.measies[field]).to.equal(val) expect(res.body.set.measies[field]).to.equal(val)
done() done()
}) })
}) })
} }
it(`${store.icon( it(`${store.icon(
'person', 'set',
auth auth
)} Should not set an non-existing measurement (${auth})`, (done) => { )} Should not set an non-existing measurement (${auth})`, (done) => {
chai chai
.request(config.api) .request(config.api)
.patch(`/people/${store.person[auth].id}/${auth}`) .patch(`/sets/${store.set[auth].id}/${auth}`)
.set( .set(
'Authorization', 'Authorization',
auth === 'jwt' auth === 'jwt'
@ -178,16 +178,16 @@ export const personTests = async (chai, config, expect, store) => {
expect(err === null).to.equal(true) expect(err === null).to.equal(true)
expect(res.status).to.equal(200) expect(res.status).to.equal(200)
expect(res.body.result).to.equal(`success`) expect(res.body.result).to.equal(`success`)
expect(res.body.person.measies.ankle).to.equal(320) expect(res.body.set.measies.ankle).to.equal(320)
expect(typeof res.body.person.measies.potatoe).to.equal('undefined') expect(typeof res.body.set.measies.potatoe).to.equal('undefined')
done() done()
}) })
}) })
it(`${store.icon('person', auth)} Should clear a measurement (${auth})`, (done) => { it(`${store.icon('set', auth)} Should clear a measurement (${auth})`, (done) => {
chai chai
.request(config.api) .request(config.api)
.patch(`/people/${store.person[auth].id}/${auth}`) .patch(`/sets/${store.set[auth].id}/${auth}`)
.set( .set(
'Authorization', 'Authorization',
auth === 'jwt' auth === 'jwt'
@ -206,15 +206,15 @@ export const personTests = async (chai, config, expect, store) => {
expect(err === null).to.equal(true) expect(err === null).to.equal(true)
expect(res.status).to.equal(200) expect(res.status).to.equal(200)
expect(res.body.result).to.equal(`success`) expect(res.body.result).to.equal(`success`)
expect(typeof res.body.person.measies.chest).to.equal('undefined') expect(typeof res.body.set.measies.chest).to.equal('undefined')
done() done()
}) })
}) })
it(`${store.icon('person', auth)} Should read a person (${auth})`, (done) => { it(`${store.icon('set', auth)} Should read a set (${auth})`, (done) => {
chai chai
.request(config.api) .request(config.api)
.get(`/people/${store.person[auth].id}/${auth}`) .get(`/sets/${store.set[auth].id}/${auth}`)
.set( .set(
'Authorization', 'Authorization',
auth === 'jwt' auth === 'jwt'
@ -228,18 +228,18 @@ export const personTests = async (chai, config, expect, store) => {
expect(err === null).to.equal(true) expect(err === null).to.equal(true)
expect(res.status).to.equal(200) expect(res.status).to.equal(200)
expect(res.body.result).to.equal(`success`) expect(res.body.result).to.equal(`success`)
expect(typeof res.body.person.measies).to.equal('object') expect(typeof res.body.set.measies).to.equal('object')
done() done()
}) })
}) })
it(`${store.icon( it(`${store.icon(
'person', 'set',
auth auth
)} Should not allow reading another user's person (${auth})`, (done) => { )} Should not allow reading another user's set (${auth})`, (done) => {
chai chai
.request(config.api) .request(config.api)
.get(`/people/${store.person[auth].id}/${auth}`) .get(`/sets/${store.set[auth].id}/${auth}`)
.set( .set(
'Authorization', 'Authorization',
auth === 'jwt' auth === 'jwt'
@ -259,12 +259,12 @@ export const personTests = async (chai, config, expect, store) => {
}) })
it(`${store.icon( it(`${store.icon(
'person', 'set',
auth auth
)} Should not allow updating another user's person (${auth})`, (done) => { )} Should not allow updating another user's set (${auth})`, (done) => {
chai chai
.request(config.api) .request(config.api)
.patch(`/people/${store.person[auth].id}/${auth}`) .patch(`/sets/${store.set[auth].id}/${auth}`)
.set( .set(
'Authorization', 'Authorization',
auth === 'jwt' auth === 'jwt'
@ -287,12 +287,12 @@ export const personTests = async (chai, config, expect, store) => {
}) })
it(`${store.icon( it(`${store.icon(
'person', 'set',
auth auth
)} Should not allow removing another user's person (${auth})`, (done) => { )} Should not allow removing another user's set (${auth})`, (done) => {
chai chai
.request(config.api) .request(config.api)
.delete(`/people/${store.person[auth].id}/${auth}`) .delete(`/sets/${store.set[auth].id}/${auth}`)
.set( .set(
'Authorization', 'Authorization',
auth === 'jwt' auth === 'jwt'
@ -311,10 +311,10 @@ export const personTests = async (chai, config, expect, store) => {
}) })
}) })
it(`${store.icon('person', auth)} Should clone a person (${auth})`, (done) => { it(`${store.icon('set', auth)} Should clone a set (${auth})`, (done) => {
chai chai
.request(config.api) .request(config.api)
.post(`/people/${store.person[auth].id}/clone/${auth}`) .post(`/sets/${store.set[auth].id}/clone/${auth}`)
.set( .set(
'Authorization', 'Authorization',
auth === 'jwt' auth === 'jwt'
@ -329,18 +329,18 @@ export const personTests = async (chai, config, expect, store) => {
expect(res.status).to.equal(200) expect(res.status).to.equal(200)
expect(res.body.result).to.equal(`success`) expect(res.body.result).to.equal(`success`)
expect(typeof res.body.error).to.equal(`undefined`) expect(typeof res.body.error).to.equal(`undefined`)
expect(typeof res.body.person.id).to.equal(`number`) expect(typeof res.body.set.id).to.equal(`number`)
done() done()
}) })
}) })
it(`${store.icon( it(`${store.icon(
'person', 'set',
auth auth
)} Should (not) clone a public person across accounts (${auth})`, (done) => { )} Should (not) clone a public set across accounts (${auth})`, (done) => {
chai chai
.request(config.api) .request(config.api)
.post(`/people/${store.person[auth].id}/clone/${auth}`) .post(`/sets/${store.set[auth].id}/clone/${auth}`)
.set( .set(
'Authorization', 'Authorization',
auth === 'jwt' auth === 'jwt'
@ -351,12 +351,12 @@ export const personTests = async (chai, config, expect, store) => {
).toString('base64') ).toString('base64')
) )
.end((err, res) => { .end((err, res) => {
if (store.person[auth].public) { if (store.set[auth].public) {
expect(err === null).to.equal(true) expect(err === null).to.equal(true)
expect(res.status).to.equal(200) expect(res.status).to.equal(200)
expect(res.body.result).to.equal(`success`) expect(res.body.result).to.equal(`success`)
expect(typeof res.body.error).to.equal(`undefined`) expect(typeof res.body.error).to.equal(`undefined`)
expect(typeof res.body.person.id).to.equal(`number`) expect(typeof res.body.set.id).to.equal(`number`)
} else { } else {
expect(err === null).to.equal(true) expect(err === null).to.equal(true)
expect(res.status).to.equal(403) expect(res.status).to.equal(403)
@ -368,8 +368,8 @@ export const personTests = async (chai, config, expect, store) => {
}) })
// TODO: // TODO:
// - Clone person // - Clone set
// - Clone person accross accounts of they are public // - Clone set accross accounts of they are public
}) })
} }
} }

View file

@ -14,7 +14,7 @@ dotenv.config()
const config = verifyConfig(true) const config = verifyConfig(true)
const expect = chai.expect const expect = chai.expect
chai.use(http) chai.use(http)
const people = { her, him } const sets = { her, him }
export const setup = async () => { export const setup = async () => {
// Initial store contents // Initial store contents
@ -26,20 +26,20 @@ export const setup = async () => {
email: `test_${randomString()}@${config.tests.domain}`, email: `test_${randomString()}@${config.tests.domain}`,
language: 'en', language: 'en',
password: randomString(), password: randomString(),
people: {}, sets: {},
}, },
altaccount: { altaccount: {
email: `test_${randomString()}@${config.tests.domain}`, email: `test_${randomString()}@${config.tests.domain}`,
language: 'en', language: 'en',
password: randomString(), password: randomString(),
people: {}, sets: {},
}, },
icons: { icons: {
user: '🧑 ', user: '🧑 ',
mfa: '🔒 ', mfa: '🔒 ',
jwt: '🎫 ', jwt: '🎫 ',
key: '🎟️ ', key: '🎟️ ',
person: '🧕 ', set: '🧕 ',
pattern: '👕 ', pattern: '👕 ',
}, },
randomString, randomString,
@ -95,15 +95,15 @@ export const setup = async () => {
} }
store[acc].apikey = result.data.apikey store[acc].apikey = result.data.apikey
// Create people key // Create sets key
for (const name in people) { for (const name in sets) {
try { try {
result = await axios.post( result = await axios.post(
`${store.config.api}/people/jwt`, `${store.config.api}/sets/jwt`,
{ {
name: `This is ${name} name`, name: `This is ${name} name`,
notes: `These are ${name} notes`, notes: `These are ${name} notes`,
measies: people[name], measies: sets[name],
}, },
{ {
headers: { headers: {
@ -115,7 +115,7 @@ export const setup = async () => {
console.log('Failed at API key creation request', err) console.log('Failed at API key creation request', err)
process.exit() process.exit()
} }
store[acc].people[name] = result.data.person store[acc].sets[name] = result.data.set
} }
} }

View file

@ -265,7 +265,8 @@ export const userTests = async (chai, config, expect, store) => {
it(`${store.icon('user')} Should find a username is available`, (done) => { it(`${store.icon('user')} Should find a username is available`, (done) => {
chai chai
.request(config.api) .request(config.api)
.post(`/available/username`) .post(`/available/username/jwt`)
.set('Authorization', 'Bearer ' + store.token)
.send({ .send({
username: 'haochi', username: 'haochi',
}) })
@ -278,7 +279,8 @@ export const userTests = async (chai, config, expect, store) => {
it(`${store.icon('user')} Should find a username is not available`, (done) => { it(`${store.icon('user')} Should find a username is not available`, (done) => {
chai chai
.request(config.api) .request(config.api)
.post(`/available/username`) .post(`/available/username/jwt`)
.set('Authorization', 'Bearer ' + store.token)
.send({ .send({
username: store.account.username, username: store.account.username,
}) })