2022-11-16 15:07:24 +01:00
|
|
|
import { log } from '../utils/log.mjs'
|
2023-08-08 06:51:56 +02:00
|
|
|
import { storeImage } from '../utils/cloudflare-images.mjs'
|
2023-08-13 09:39:05 +02:00
|
|
|
import { decorateModel } from '../utils/model-decorator.mjs'
|
2022-11-16 15:07:24 +01:00
|
|
|
|
2023-08-13 09:39:05 +02:00
|
|
|
/*
|
|
|
|
* This model handles all flows (typically that involves sending out emails)
|
|
|
|
*/
|
2022-11-16 15:07:24 +01:00
|
|
|
export function PatternModel(tools) {
|
2023-08-13 09:39:05 +02:00
|
|
|
return decorateModel(this, tools, {
|
|
|
|
name: 'pattern',
|
|
|
|
encryptedFields: ['data', 'img', 'name', 'notes', 'settings'],
|
|
|
|
models: ['set'],
|
|
|
|
})
|
2022-11-16 15:07:24 +01:00
|
|
|
}
|
|
|
|
|
2023-05-26 17:23:56 +02:00
|
|
|
/*
|
2023-08-13 09:39:05 +02:00
|
|
|
* Returns a list of patterns for the user making the API call
|
|
|
|
*
|
|
|
|
* @param {uid} string - uid of the user, as provided by the auth middleware
|
|
|
|
* @returns {patterns} array - The list of patterns
|
2023-05-26 17:23:56 +02:00
|
|
|
*/
|
|
|
|
PatternModel.prototype.userPatterns = async function (uid) {
|
2023-08-13 09:39:05 +02:00
|
|
|
/*
|
|
|
|
* No uid no deal
|
|
|
|
*/
|
2023-05-26 17:23:56 +02:00
|
|
|
if (!uid) return false
|
2023-08-13 09:39:05 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Run query returning all patterns from the database
|
|
|
|
*/
|
2023-05-26 17:23:56 +02:00
|
|
|
let patterns
|
|
|
|
try {
|
2023-05-27 17:34:09 +02:00
|
|
|
patterns = await this.prisma.pattern.findMany({
|
|
|
|
where: { userId: uid },
|
|
|
|
include: {
|
|
|
|
set: true,
|
|
|
|
cset: true,
|
|
|
|
},
|
|
|
|
})
|
2023-05-26 17:23:56 +02:00
|
|
|
} catch (err) {
|
|
|
|
log.warn(`Failed to search patterns for user ${uid}: ${err}`)
|
|
|
|
}
|
|
|
|
|
2023-08-13 09:39:05 +02:00
|
|
|
/*
|
|
|
|
* Decrypt data for all patterns found
|
|
|
|
*/
|
|
|
|
const list = patterns.map((pattern) => this.revealPattern(pattern))
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return the list of patterns
|
|
|
|
*/
|
2023-05-26 17:23:56 +02:00
|
|
|
return list
|
|
|
|
}
|
|
|
|
|
2023-08-13 09:39:05 +02:00
|
|
|
/*
|
|
|
|
* Creates a new pattern - Takes user input so we validate data and access
|
|
|
|
*
|
|
|
|
* @param {body} object - The request body
|
|
|
|
* @param {user} object - The user data as provided by the auth middleware
|
|
|
|
* @returns {PatternModel} object - The PatternModel
|
|
|
|
*/
|
2022-11-16 15:07:24 +01:00
|
|
|
PatternModel.prototype.guardedCreate = async function ({ body, user }) {
|
2023-08-13 09:39:05 +02:00
|
|
|
/*
|
|
|
|
* Enforce RBAC
|
|
|
|
*/
|
2023-05-06 12:52:26 +02:00
|
|
|
if (!this.rbac.user(user)) return this.setResponse(403, 'insufficientAccessLevel')
|
2023-08-13 09:39:05 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Do we have a POST body?
|
|
|
|
*/
|
2022-12-29 13:40:25 -08:00
|
|
|
if (Object.keys(body).length < 2) return this.setResponse(400, 'postBodyMissing')
|
2023-08-13 09:39:05 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Is settings set?
|
|
|
|
*/
|
2022-11-16 15:07:24 +01:00
|
|
|
if (typeof body.settings !== 'object') return this.setResponse(400, 'settingsNotAnObject')
|
2023-08-13 09:39:05 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Is data set?
|
|
|
|
*/
|
2022-11-16 15:07:24 +01:00
|
|
|
if (body.data && typeof body.data !== 'object') return this.setResponse(400, 'dataNotAnObject')
|
2023-08-13 09:39:05 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Is design set?
|
|
|
|
*/
|
2022-11-16 15:07:24 +01:00
|
|
|
if (!body.design && !body.data?.design) return this.setResponse(400, 'designMissing')
|
2023-08-13 09:39:05 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Is design a string?
|
|
|
|
*/
|
2022-11-16 15:07:24 +01:00
|
|
|
if (typeof body.design !== 'string') return this.setResponse(400, 'designNotStringy')
|
|
|
|
|
2023-08-13 09:39:05 +02:00
|
|
|
/*
|
|
|
|
* Create initial record
|
|
|
|
*/
|
|
|
|
await this.createRecord({
|
|
|
|
csetId: body.cset ? body.cset : null,
|
|
|
|
data: typeof body.data === 'object' ? body.data : {},
|
2022-11-16 15:07:24 +01:00
|
|
|
design: body.design,
|
2023-08-13 09:39:05 +02:00
|
|
|
img: this.config.avatars.pattern,
|
|
|
|
setId: body.set ? body.set : null,
|
|
|
|
settings: {
|
|
|
|
...body.settings,
|
|
|
|
measurements: body.settings.measurements === 'object' ? body.settings.measurements : {},
|
|
|
|
},
|
|
|
|
userId: user.uid,
|
|
|
|
name: typeof body.name === 'string' && body.name.length > 0 ? body.name : '--',
|
|
|
|
notes: typeof body.notes === 'string' && body.notes.length > 0 ? body.notes : '--',
|
|
|
|
public: body.public === true ? true : false,
|
|
|
|
})
|
2022-11-16 15:07:24 +01:00
|
|
|
|
2023-08-13 09:39:05 +02:00
|
|
|
/*
|
|
|
|
* Now that we have a record ID, we can update the image
|
|
|
|
*/
|
|
|
|
const img = await storeImage(
|
|
|
|
{
|
|
|
|
id: `pattern-${this.record.id}`,
|
|
|
|
metadata: { user: user.uid },
|
|
|
|
b64: body.img,
|
|
|
|
},
|
|
|
|
this.isTest(body)
|
|
|
|
)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If an image was created, update the record with its ID
|
|
|
|
* If not, just update the record from the database
|
|
|
|
*/
|
|
|
|
if (img) await this.update(this.cloak({ img: img.url }))
|
|
|
|
else await this.read({ id: this.record.id }, { set: true, cset: true })
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now return 201 and the record data
|
|
|
|
*/
|
|
|
|
return this.setResponse201({ pattern: this.asPattern() })
|
2022-11-16 15:07:24 +01:00
|
|
|
}
|
|
|
|
|
2023-05-30 09:50:46 +02:00
|
|
|
/*
|
|
|
|
* Loads a pattern from the database but only if it's public
|
|
|
|
*
|
2023-08-13 09:39:05 +02:00
|
|
|
* @param {params} object - The request (URL) parameters
|
|
|
|
* @returns {PatternModel} object - The PatternModel
|
2023-05-30 09:50:46 +02:00
|
|
|
*/
|
|
|
|
PatternModel.prototype.publicRead = async function ({ params }) {
|
2023-08-13 09:39:05 +02:00
|
|
|
/*
|
|
|
|
* Attempt to read the database record
|
|
|
|
*/
|
|
|
|
await this.read({ id: parseInt(params.id) }, { set: true, cset: true })
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ensure it is public and if it is not public, return 404
|
|
|
|
* rather than reveal that a non-public pattern exists
|
|
|
|
*/
|
|
|
|
if (this.record.public !== true) return this.setResponse(404)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return pattern
|
|
|
|
*/
|
|
|
|
return this.setResponse200(this.asPublicPattern())
|
2023-05-30 09:50:46 +02:00
|
|
|
}
|
|
|
|
|
2022-11-16 15:07:24 +01:00
|
|
|
/*
|
|
|
|
* Loads a pattern from the database based on the where clause you pass it
|
|
|
|
* In addition prepares it for returning the pattern data
|
|
|
|
*
|
2023-08-13 09:39:05 +02:00
|
|
|
* @param {params} object - The request (URL) parameters
|
|
|
|
* @param {user} object - The user data as provided by the auth middleware
|
|
|
|
* @returns {PatternModel} object - The PatternModel
|
2022-11-16 15:07:24 +01:00
|
|
|
*/
|
|
|
|
PatternModel.prototype.guardedRead = async function ({ params, user }) {
|
2023-08-13 09:39:05 +02:00
|
|
|
/*
|
|
|
|
* Enforce RBAC
|
|
|
|
*/
|
2023-05-06 12:52:26 +02:00
|
|
|
if (!this.rbac.readSome(user)) return this.setResponse(403, 'insufficientAccessLevel')
|
2023-08-13 09:39:05 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Check JWT for status
|
|
|
|
*/
|
2022-11-16 15:07:24 +01:00
|
|
|
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
|
2023-08-13 09:39:05 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Is the id set?
|
|
|
|
*/
|
2023-05-30 16:47:22 +02:00
|
|
|
if (typeof params.id !== 'undefined' && !Number(params.id))
|
|
|
|
return this.setResponse(403, 'idNotNumeric')
|
2022-11-16 15:07:24 +01:00
|
|
|
|
2023-08-13 09:39:05 +02:00
|
|
|
/*
|
|
|
|
* Attempt to read record from database
|
|
|
|
*/
|
|
|
|
await this.read({ id: parseInt(params.id) }, { set: true, cset: true })
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return 404 if it cannot be found
|
|
|
|
*/
|
2023-05-30 16:47:22 +02:00
|
|
|
if (!this.record) return this.setResponse(404, 'notFound')
|
|
|
|
|
2023-08-13 09:39:05 +02:00
|
|
|
/*
|
|
|
|
* You need at least the bughunter role to read another user's pattern
|
|
|
|
*/
|
2023-05-06 12:52:26 +02:00
|
|
|
if (this.record.userId !== user.uid && !this.rbac.bughunter(user)) {
|
2022-11-16 15:07:24 +01:00
|
|
|
return this.setResponse(403, 'insufficientAccessLevel')
|
|
|
|
}
|
|
|
|
|
2023-08-13 09:39:05 +02:00
|
|
|
/*
|
|
|
|
* Return the loaded pattern
|
|
|
|
*/
|
|
|
|
return this.setResponse200({ pattern: this.asPattern() })
|
2022-11-16 15:07:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Clones a pattern
|
|
|
|
* In addition prepares it for returning the pattern data
|
|
|
|
*
|
2023-08-13 09:39:05 +02:00
|
|
|
* @param {params} object - The request (URL) parameters
|
|
|
|
* @param {user} object - The user data as provided by the auth middleware
|
|
|
|
* @returns {PatternModel} object - The PatternModel
|
2022-11-16 15:07:24 +01:00
|
|
|
*/
|
|
|
|
PatternModel.prototype.guardedClone = async function ({ params, user }) {
|
2023-08-13 09:39:05 +02:00
|
|
|
/*
|
|
|
|
* Enforce RBAC
|
|
|
|
*/
|
2023-05-06 12:52:26 +02:00
|
|
|
if (!this.rbac.writeSome(user)) return this.setResponse(403, 'insufficientAccessLevel')
|
2023-08-13 09:39:05 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Check JWT
|
|
|
|
*/
|
2022-11-16 15:07:24 +01:00
|
|
|
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
|
|
|
|
|
2023-08-13 09:39:05 +02:00
|
|
|
/*
|
|
|
|
* Attempt to read record from database
|
|
|
|
*/
|
2022-11-16 15:07:24 +01:00
|
|
|
await this.read({ id: parseInt(params.id) })
|
2023-08-13 09:39:05 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* You need the support role to clone another user's pattern that is not public
|
|
|
|
*/
|
2023-05-31 18:34:37 +02:00
|
|
|
if (this.record.userId !== user.uid && !this.record.public && !this.rbac.support(user)) {
|
2022-11-16 15:07:24 +01:00
|
|
|
return this.setResponse(403, 'insufficientAccessLevel')
|
|
|
|
}
|
|
|
|
|
2023-08-13 09:39:05 +02:00
|
|
|
/*
|
|
|
|
* Now clone the pattern
|
|
|
|
*/
|
2022-11-16 15:07:24 +01:00
|
|
|
const data = this.asPattern()
|
|
|
|
delete data.id
|
|
|
|
data.name += ` (cloned from #${this.record.id})`
|
|
|
|
data.notes += ` (Note: This pattern was cloned from pattern #${this.record.id})`
|
|
|
|
|
2023-08-13 09:39:05 +02:00
|
|
|
/*
|
|
|
|
* Write it to the database
|
|
|
|
*/
|
|
|
|
await this.createRecord(data)
|
2022-11-16 15:07:24 +01:00
|
|
|
|
2023-08-13 09:39:05 +02:00
|
|
|
/*
|
|
|
|
* Update record with unencrypted data
|
|
|
|
*/
|
|
|
|
this.reveal()
|
2022-11-16 15:07:24 +01:00
|
|
|
|
2023-08-13 09:39:05 +02:00
|
|
|
/*
|
|
|
|
* And return the cloned pattern
|
|
|
|
*/
|
|
|
|
return this.setResponse200({ pattern: this.asPattern() })
|
2022-11-16 15:07:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Updates the pattern data - Used when we pass through user-provided data
|
|
|
|
* so we can't be certain it's safe
|
2023-08-13 09:39:05 +02:00
|
|
|
*
|
|
|
|
* @param {params} object - The request (URL) parameters
|
|
|
|
* @param {body} object - The request body
|
|
|
|
* @param {user} object - The user data as provided by the auth middleware
|
|
|
|
* @returns {PatternModel} object - The PatternModel
|
2022-11-16 15:07:24 +01:00
|
|
|
*/
|
|
|
|
PatternModel.prototype.guardedUpdate = async function ({ params, body, user }) {
|
2023-08-13 09:39:05 +02:00
|
|
|
/*
|
|
|
|
* Enforce RBAC
|
|
|
|
*/
|
2023-05-06 12:52:26 +02:00
|
|
|
if (!this.rbac.writeSome(user)) return this.setResponse(403, 'insufficientAccessLevel')
|
2023-08-13 09:39:05 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Check JWT
|
|
|
|
*/
|
2022-11-16 15:07:24 +01:00
|
|
|
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
|
2023-08-13 09:39:05 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Attempt to read record from the database
|
|
|
|
*/
|
|
|
|
await this.read({ id: parseInt(params.id) }, { set: true, cset: true })
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Only admins can update other people's patterns
|
|
|
|
*/
|
2023-05-06 12:52:26 +02:00
|
|
|
if (this.record.userId !== user.uid && !this.rbac.admin(user)) {
|
2022-11-16 15:07:24 +01:00
|
|
|
return this.setResponse(403, 'insufficientAccessLevel')
|
|
|
|
}
|
2023-08-13 09:39:05 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Prepare data for updating the record
|
|
|
|
*/
|
2022-11-16 15:07:24 +01:00
|
|
|
const data = {}
|
2023-08-13 09:39:05 +02:00
|
|
|
/*
|
|
|
|
* name
|
|
|
|
*/
|
2022-11-16 15:07:24 +01:00
|
|
|
if (typeof body.name === 'string') data.name = body.name
|
2023-08-13 09:39:05 +02:00
|
|
|
/*
|
|
|
|
* notes
|
|
|
|
*/
|
2022-11-16 15:07:24 +01:00
|
|
|
if (typeof body.notes === 'string') data.notes = body.notes
|
2023-08-13 09:39:05 +02:00
|
|
|
/*
|
|
|
|
* public
|
|
|
|
*/
|
2022-11-16 15:07:24 +01:00
|
|
|
if (body.public === true || body.public === false) data.public = body.public
|
2023-08-13 09:39:05 +02:00
|
|
|
/*
|
|
|
|
* data
|
|
|
|
*/
|
2022-11-16 15:07:24 +01:00
|
|
|
if (typeof body.data === 'object') data.data = body.data
|
2023-08-13 09:39:05 +02:00
|
|
|
/*
|
|
|
|
* settings
|
|
|
|
*/
|
2022-11-16 15:07:24 +01:00
|
|
|
if (typeof body.settings === 'object') data.settings = body.settings
|
2023-08-13 09:39:05 +02:00
|
|
|
/*
|
|
|
|
* img
|
|
|
|
*/
|
2022-11-16 15:07:24 +01:00
|
|
|
if (typeof body.img === 'string') {
|
2023-08-13 09:39:05 +02:00
|
|
|
data.img = await storeImage(
|
|
|
|
{
|
|
|
|
id: `pattern-${this.record.id}`,
|
|
|
|
metadata: { user: this.user.uid },
|
|
|
|
b64: body.img,
|
|
|
|
},
|
|
|
|
this.isTest(body)
|
|
|
|
)
|
2022-11-16 15:07:24 +01:00
|
|
|
}
|
|
|
|
|
2023-08-13 09:39:05 +02:00
|
|
|
/*
|
|
|
|
* Now update the record
|
|
|
|
*/
|
|
|
|
await this.update(data, { set: true, cset: true })
|
2022-11-16 15:07:24 +01:00
|
|
|
|
2023-08-13 09:39:05 +02:00
|
|
|
/*
|
|
|
|
* Return 200 and the data
|
|
|
|
*/
|
|
|
|
return this.setResponse200({ pattern: this.asPattern() })
|
2022-11-16 15:07:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Removes the pattern - Checks permissions
|
2023-08-13 09:39:05 +02:00
|
|
|
*
|
|
|
|
* @param {params} object - The request (URL) parameters
|
|
|
|
* @param {user} object - The user data as provided by the auth middleware
|
|
|
|
* @returns {PatternModel} object - The PatternModel
|
2022-11-16 15:07:24 +01:00
|
|
|
*/
|
2022-12-29 15:51:21 -08:00
|
|
|
PatternModel.prototype.guardedDelete = async function ({ params, user }) {
|
2023-08-13 09:39:05 +02:00
|
|
|
/*
|
|
|
|
* Enforce RBAC
|
|
|
|
*/
|
2023-05-26 17:23:56 +02:00
|
|
|
if (!this.rbac.writeSome(user)) return this.setResponse(403, 'insufficientAccessLevel')
|
2023-08-13 09:39:05 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Check JWT
|
|
|
|
*/
|
2022-11-16 15:07:24 +01:00
|
|
|
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
|
|
|
|
|
2023-08-13 09:39:05 +02:00
|
|
|
/*
|
|
|
|
* Attempt to read record from database
|
|
|
|
*/
|
2022-11-16 15:07:24 +01:00
|
|
|
await this.read({ id: parseInt(params.id) })
|
2023-08-13 09:39:05 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Only admins can delete other user's patterns
|
|
|
|
*/
|
2023-05-06 12:52:26 +02:00
|
|
|
if (this.record.userId !== user.uid && !this.rbac.admin(user)) {
|
2022-11-16 15:07:24 +01:00
|
|
|
return this.setResponse(403, 'insufficientAccessLevel')
|
|
|
|
}
|
|
|
|
|
2023-08-13 09:39:05 +02:00
|
|
|
/*
|
|
|
|
* Remove the record
|
|
|
|
*/
|
|
|
|
await this.delete()
|
2022-11-16 15:07:24 +01:00
|
|
|
|
2023-08-13 09:39:05 +02:00
|
|
|
/*
|
|
|
|
* Return 204
|
|
|
|
*/
|
2022-11-16 15:07:24 +01:00
|
|
|
return this.setResponse(204, false)
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns record data
|
|
|
|
*/
|
|
|
|
PatternModel.prototype.asPattern = function () {
|
|
|
|
return {
|
|
|
|
...this.record,
|
|
|
|
...this.clear,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-26 17:23:56 +02:00
|
|
|
/*
|
|
|
|
* Helper method to decrypt data from a non-instantiated pattern
|
2023-08-13 09:39:05 +02:00
|
|
|
*
|
|
|
|
* @param {pattern} object - The pattern data
|
|
|
|
* @returns {pattern} object - The unencrypted pattern data
|
2023-05-26 17:23:56 +02:00
|
|
|
*/
|
|
|
|
PatternModel.prototype.revealPattern = function (pattern) {
|
|
|
|
const clear = {}
|
|
|
|
for (const field of this.encryptedFields) {
|
|
|
|
try {
|
|
|
|
clear[field] = this.decrypt(pattern[field])
|
|
|
|
} catch (err) {
|
|
|
|
//console.log(err)
|
|
|
|
}
|
|
|
|
}
|
2023-05-30 09:50:46 +02:00
|
|
|
if (pattern.set) delete pattern.set.measies
|
|
|
|
if (pattern.cset) delete pattern.cset.measies
|
2023-05-26 17:23:56 +02:00
|
|
|
|
|
|
|
return { ...pattern, ...clear }
|
|
|
|
}
|
|
|
|
|
2023-05-30 09:50:46 +02:00
|
|
|
/*
|
|
|
|
* Returns record data fit for public publishing
|
|
|
|
*/
|
|
|
|
PatternModel.prototype.asPublicPattern = function () {
|
|
|
|
const data = {
|
|
|
|
author: 'FreeSewing.org',
|
|
|
|
type: 'pattern',
|
|
|
|
...this.asPattern(),
|
|
|
|
}
|
|
|
|
delete data.userId
|
|
|
|
delete data.public
|
|
|
|
|
|
|
|
return data
|
|
|
|
}
|
2023-08-09 20:40:38 +02:00
|
|
|
|
2023-08-13 09:39:05 +02:00
|
|
|
/*
|
|
|
|
*
|
|
|
|
* Everything below this comment is v2 => v3 migration code
|
|
|
|
* And can be removed after the migration
|
|
|
|
*/
|
|
|
|
|
2023-08-09 20:40:38 +02:00
|
|
|
const migratePattern = (v2, userId) => ({
|
|
|
|
createdAt: new Date(v2.created ? v2.created : v2.createdAt),
|
|
|
|
data: { version: v2.data.version, notes: ['Migrated from version 2'] },
|
|
|
|
design: v2.design || v2.data.design,
|
|
|
|
name: v2.name || '--',
|
|
|
|
notes: v2.notes ? v2.notes + '\n\nMigrated from v2' : 'Migrated from v2',
|
|
|
|
settings: v2.data.settings,
|
|
|
|
userId,
|
|
|
|
})
|
|
|
|
|
|
|
|
const v2lut = {
|
|
|
|
'size 28, with breasts': 1,
|
|
|
|
'size 30, with breasts': 2,
|
|
|
|
'size 32, with breasts': 3,
|
|
|
|
'size 34, with breasts': 4,
|
|
|
|
'size 36, with breasts': 5,
|
|
|
|
'size 38, with breasts': 6,
|
|
|
|
'size 40, with breasts': 7,
|
|
|
|
'size 42, with breasts': 8,
|
|
|
|
'size 44, with breasts': 9,
|
|
|
|
'size 46, with breasts': 10,
|
|
|
|
'size-28-b': 1,
|
|
|
|
'size-30-b': 2,
|
|
|
|
'size-32-b': 3,
|
|
|
|
'size-34-b': 4,
|
|
|
|
'size-36-b': 5,
|
|
|
|
'size-38-b': 6,
|
|
|
|
'size-40-b': 7,
|
|
|
|
'size-42-b': 8,
|
|
|
|
'size-44-b': 9,
|
|
|
|
'size-46-b': 10,
|
|
|
|
'size-28-with-breasts': 1,
|
|
|
|
'size-30-with-breasts': 2,
|
|
|
|
'size-32-with-breasts': 3,
|
|
|
|
'size-34-with-breasts': 4,
|
|
|
|
'size-36-with-breasts': 5,
|
|
|
|
'size-38-with-breasts': 6,
|
|
|
|
'size-40-with-breasts': 7,
|
|
|
|
'size-42-with-breasts': 8,
|
|
|
|
'size-44-with-breasts': 9,
|
|
|
|
'size-46-with-breasts': 10,
|
|
|
|
'größe 28, mit brüsten': 1,
|
|
|
|
'größe 30, mit brüsten': 2,
|
|
|
|
'größe 32, mit brüsten': 3,
|
|
|
|
'größe 34, mit brüsten': 4,
|
|
|
|
'größe 36, mit brüsten': 5,
|
|
|
|
'größe 38, mit brüsten': 6,
|
|
|
|
'größe 40, mit brüsten': 7,
|
|
|
|
'größe 42, mit brüsten': 8,
|
|
|
|
'größe 44, mit brüsten': 9,
|
|
|
|
'größe 46, mit brüsten': 10,
|
2023-08-13 16:15:06 +02:00
|
|
|
'grösse 28, mit brüsten': 1,
|
|
|
|
'grösse 30, mit brüsten': 2,
|
|
|
|
'grösse 32, mit brüsten': 3,
|
|
|
|
'grösse 34, mit brüsten': 4,
|
|
|
|
'grösse 36, mit brüsten': 5,
|
|
|
|
'grösse 38, mit brüsten': 6,
|
|
|
|
'grösse 40, mit brüsten': 7,
|
|
|
|
'grösse 42, mit brüsten': 8,
|
|
|
|
'grösse 44, mit brüsten': 9,
|
|
|
|
'grösse 46, mit brüsten': 10,
|
2023-08-09 20:40:38 +02:00
|
|
|
'taille 28, avec des seins': 1,
|
|
|
|
'taille 30, avec des seins': 2,
|
|
|
|
'taille 32, avec des seins': 3,
|
|
|
|
'taille 34, avec des seins': 4,
|
|
|
|
'taille 36, avec des seins': 5,
|
|
|
|
'taille 38, avec des seins': 6,
|
|
|
|
'taille 40, avec des seins': 7,
|
|
|
|
'taille 42, avec des seins': 8,
|
|
|
|
'taille 44, avec des seins': 9,
|
|
|
|
'taille 46, avec des seins': 10,
|
2023-08-13 18:33:02 +02:00
|
|
|
'size 28, avec des seins': 1,
|
|
|
|
'size 30, avec des seins': 2,
|
|
|
|
'size 32, avec des seins': 3,
|
|
|
|
'size 34, avec des seins': 4,
|
|
|
|
'size 36, avec des seins': 5,
|
|
|
|
'size 38, avec des seins': 6,
|
|
|
|
'size 40, avec des seins': 7,
|
|
|
|
'size 42, avec des seins': 8,
|
|
|
|
'size 44, avec des seins': 9,
|
|
|
|
'size 46, avec des seins': 10,
|
2023-08-09 20:40:38 +02:00
|
|
|
'tamaño 28, con pechos': 1,
|
|
|
|
'tamaño 30, con pechos': 2,
|
|
|
|
'tamaño 32, con pechos': 3,
|
|
|
|
'tamaño 34, con pechos': 4,
|
|
|
|
'tamaño 36, con pechos': 5,
|
|
|
|
'tamaño 38, con pechos': 6,
|
|
|
|
'tamaño 40, con pechos': 7,
|
|
|
|
'tamaño 42, con pechos': 8,
|
|
|
|
'tamaño 44, con pechos': 9,
|
|
|
|
'tamaño 46, con pechos': 10,
|
|
|
|
|
|
|
|
'size 32, without breasts': 11,
|
|
|
|
'size 34, without breasts': 12,
|
|
|
|
'size 36, without breasts': 13,
|
|
|
|
'size 38, without breasts': 14,
|
|
|
|
'size 40, without breasts': 15,
|
|
|
|
'size 42, without breasts': 16,
|
|
|
|
'size 44, without breasts': 17,
|
|
|
|
'size 46, without breasts': 18,
|
|
|
|
'size 48, without breasts': 19,
|
|
|
|
'size 50, without breasts': 20,
|
|
|
|
'taille 32, sans seins': 11,
|
|
|
|
'taille 34, sans seins': 12,
|
|
|
|
'taille 36, sans seins': 13,
|
|
|
|
'taille 38, sans seins': 14,
|
|
|
|
'taille 40, sans seins': 15,
|
|
|
|
'taille 42, sans seins': 16,
|
|
|
|
'taille 44, sans seins': 17,
|
|
|
|
'taille 46, sans seins': 18,
|
|
|
|
'taille 48, sans seins': 19,
|
|
|
|
'taille 50, sans seins': 20,
|
2023-08-13 18:33:02 +02:00
|
|
|
'size 32, sans seins': 11,
|
|
|
|
'size 34, sans seins': 12,
|
|
|
|
'size 36, sans seins': 13,
|
|
|
|
'size 38, sans seins': 14,
|
|
|
|
'size 40, sans seins': 15,
|
|
|
|
'size 42, sans seins': 16,
|
|
|
|
'size 44, sans seins': 17,
|
|
|
|
'size 46, sans seins': 18,
|
|
|
|
'size 48, sans seins': 19,
|
|
|
|
'size 50, sans seins': 20,
|
2023-08-09 20:40:38 +02:00
|
|
|
'size-32-a': 11,
|
|
|
|
'size-34-a': 12,
|
|
|
|
'size-36-a': 13,
|
|
|
|
'size-38-a': 14,
|
|
|
|
'size-40-a': 15,
|
|
|
|
'size-42-a': 16,
|
|
|
|
'size-44-a': 17,
|
|
|
|
'size-46-a': 18,
|
|
|
|
'size-48-a': 19,
|
|
|
|
'size-50-a': 20,
|
|
|
|
'maat 32, zonder borsten': 11,
|
|
|
|
'maat 34, zonder borsten': 12,
|
|
|
|
'maat 36, zonder borsten': 13,
|
|
|
|
'maat 38, zonder borsten': 14,
|
|
|
|
'maat 40, zonder borsten': 15,
|
|
|
|
'maat 42, zonder borsten': 16,
|
|
|
|
'maat 44, zonder borsten': 17,
|
|
|
|
'maat 46, zonder borsten': 18,
|
|
|
|
'maat 48, zonder borsten': 19,
|
|
|
|
'maat 50, zonder borsten': 20,
|
|
|
|
'größe 32, ohne brüste': 11,
|
|
|
|
'größe 34, ohne brüste': 12,
|
|
|
|
'größe 36, ohne brüste': 13,
|
|
|
|
'größe 38, ohne brüste': 14,
|
|
|
|
'größe 40, ohne brüste': 15,
|
|
|
|
'größe 42, ohne brüste': 16,
|
|
|
|
'größe 44, ohne brüste': 17,
|
|
|
|
'größe 46, ohne brüste': 18,
|
|
|
|
'größe 48, ohne brüste': 19,
|
|
|
|
'größe 50, ohne brüste': 20,
|
2023-08-13 16:15:06 +02:00
|
|
|
'grösse 32, ohne brüste': 11,
|
|
|
|
'grösse 34, ohne brüste': 12,
|
|
|
|
'grösse 36, ohne brüste': 13,
|
|
|
|
'grösse 38, ohne brüste': 14,
|
|
|
|
'grösse 40, ohne brüste': 15,
|
|
|
|
'grösse 42, ohne brüste': 16,
|
|
|
|
'grösse 44, ohne brüste': 17,
|
|
|
|
'grösse 46, ohne brüste': 18,
|
|
|
|
'grösse 48, ohne brüste': 19,
|
|
|
|
'grösse 50, ohne brüste': 20,
|
2023-08-09 20:40:38 +02:00
|
|
|
'tamaño 32, sin pechos': 11,
|
|
|
|
'tamaño 34, sin pechos': 12,
|
|
|
|
'tamaño 36, sin pechos': 13,
|
|
|
|
'tamaño 38, sin pechos': 14,
|
|
|
|
'tamaño 40, sin pechos': 15,
|
|
|
|
'tamaño 42, sin pechos': 16,
|
|
|
|
'tamaño 44, sin pechos': 17,
|
|
|
|
'tamaño 46, sin pechos': 18,
|
|
|
|
'tamaño 48, sin pechos': 19,
|
|
|
|
'tamaño 50, sin pechos': 20,
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* This is a special route not available for API users
|
|
|
|
*/
|
|
|
|
PatternModel.prototype.import = async function (v2user, lut, userId) {
|
2023-08-13 09:54:56 +02:00
|
|
|
for (const pattern of Object.values(v2user.patterns)) {
|
2023-08-09 20:40:38 +02:00
|
|
|
let skip = false
|
|
|
|
const data = { ...migratePattern(pattern, userId), userId }
|
|
|
|
if (lut[pattern.person]) data.setId = lut[pattern.person]
|
|
|
|
else if (v2lut[pattern.person]) data.csetId = v2lut[pattern.person]
|
|
|
|
else if (pattern.person.length !== 5 && !['any', 'original'].includes(pattern.person)) {
|
|
|
|
console.log(`Cannot find ${pattern.person}`, pattern, { lut, v2lut })
|
|
|
|
process.exit()
|
|
|
|
}
|
|
|
|
if (!data.design || ['theo', 'ursula', 'unice'].includes(data.design)) skip = true
|
|
|
|
if (!skip) {
|
|
|
|
// V2 does not support images for patterns
|
|
|
|
data.img = 'default-avatar'
|
|
|
|
try {
|
2023-08-13 16:15:06 +02:00
|
|
|
this.createRecord(data)
|
2023-08-09 20:40:38 +02:00
|
|
|
} catch (err) {
|
|
|
|
log.warn(err, 'Could not create pattern')
|
|
|
|
console.log(data)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|