1
0
Fork 0

Merge pull request #5191 from freesewing/joost

chore(backend): Remove (c)set from patterns, img from users
This commit is contained in:
Joost De Cock 2023-10-17 18:23:11 +02:00 committed by GitHub
commit e0838da197
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 55 additions and 622 deletions

View file

@ -64,7 +64,6 @@ model User {
ehash String @unique
email String
ihash String
img String?
initial String
imperial Boolean @default(false)
jwtCalls Int @default(0)
@ -94,17 +93,13 @@ model Pattern {
img String?
name String @default("")
notes String
set Set? @relation(fields: [setId], references: [id])
setId Int?
cset CuratedSet? @relation(fields: [csetId], references: [id])
csetId Int?
public Boolean @default(false)
settings String
user User @relation(fields: [userId], references: [id])
userId Int
updatedAt DateTime @updatedAt
@@index([userId, setId])
@@index([userId, design])
}
model Set {
@ -117,7 +112,6 @@ model Set {
user User @relation(fields: [userId], references: [id])
userId Int
measies String @default("{}")
patterns Pattern[]
public Boolean @default(false)
updatedAt DateTime @updatedAt
@ -127,7 +121,7 @@ model Set {
model CuratedSet {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
img String?
info String @default("")
nameDe String @default("")
nameEn String @default("")
nameEs String @default("")
@ -142,7 +136,6 @@ model CuratedSet {
notesUk String @default("")
tags String @default("{}")
measies String @default("[]")
patterns Pattern[]
updatedAt DateTime @updatedAt
published Boolean @default(false)
}
@ -152,6 +145,7 @@ model OptionPack {
createdAt DateTime @default(now())
design String @default("")
img String?
info String @default("")
nameDe String @default("")
nameEn String @default("")
nameEs String @default("")

View file

@ -58,9 +58,9 @@ CuratedSetModel.prototype.guardedCreate = async function ({ body, user }) {
else data.measies = '{}'
/*
* Set an initial img as we need the record ID to store an image on cloudflare
* Add the info
*/
data.img = this.config.avatars.set
if (body.info) data.info = body.info
/*
* Create the database record
@ -68,9 +68,9 @@ CuratedSetModel.prototype.guardedCreate = async function ({ body, user }) {
await this.createRecord(data)
/*
* Now that we have a record and ID, we can update the image after uploading it to cloudflare
* Now that we have a record and ID, we can upload the image to cloudflare and set its id
*/
const img = await storeImage(
await storeImage(
{
id: `cset-${this.record.id}`,
metadata: { user: user.uid },
@ -79,14 +79,6 @@ CuratedSetModel.prototype.guardedCreate = async function ({ body, user }) {
this.isTest(body)
)
/*
* If an image was uploaded, update the record with the image ID
*/
if (img) await this.update({ img })
/*
* If not, just read the record from the datbasa
*/ else await this.read({ id: this.record.id })
/*
* Record created, return data in the proper format
*/
@ -147,6 +139,7 @@ CuratedSetModel.prototype.allCuratedSets = async function () {
*/
asPojo.measies = JSON.parse(asPojo.measies)
asPojo.tags = JSON.parse(asPojo.tags)
delete asPojo.info
list.push(asPojo)
}
@ -229,6 +222,11 @@ CuratedSetModel.prototype.guardedUpdate = async function ({ params, body, user }
*/
if ([true, false].includes(body.published)) data.published = body.published
/*
* Handle the info field
*/
if (typeof body.info === 'string') data.info = body.info
/*
* Unlike a regular set, curated set have notes and name in each language
*/
@ -255,7 +253,7 @@ CuratedSetModel.prototype.guardedUpdate = async function ({ params, body, user }
* Handle the image, if there is one
*/
if (typeof body.img === 'string') {
const img = await storeImage(
await storeImage(
{
id: `cset-${this.record.id}`,
metadata: { user: this.user.uid },
@ -263,7 +261,6 @@ CuratedSetModel.prototype.guardedUpdate = async function ({ params, body, user }
},
this.isTest(body)
)
data.img = img
}
/*
@ -365,10 +362,7 @@ CuratedSetModel.prototype.suggest = async function ({ body, user }) {
/*
* If an image was uploaded, update the record with the image ID
*/
if (img) {
data.img = img
await this.Confirmation.update({ data })
}
if (img) await this.Confirmation.update({ data })
/*
* Return id
@ -438,8 +432,6 @@ CuratedSetModel.prototype.fromSuggestion = async function ({ params, user }) {
* Now create the curated set
*/
await this.createRecord({
// This image will need to be replaced
img: this.Confirmation.clear.data.img,
nameDe: name,
nameEn: name,
nameEs: name,
@ -476,13 +468,14 @@ CuratedSetModel.prototype.fromSuggestion = async function ({ params, user }) {
* @returns {curatedSet} object - The Cureated Set as a plain object
*/
CuratedSetModel.prototype.asCuratedSet = function () {
return {
...this.record,
measies:
typeof this.record.measies === 'string'
? JSON.parse(this.record.measies)
: this.record.measies,
const data = { ...this.record }
for (const field of this.jsonFields) {
data[field] =
typeof this.record[field] === 'string' ? JSON.parse(this.record[field]) : this.record[field]
}
delete data.info
return data
}
/*
@ -499,6 +492,7 @@ CuratedSetModel.prototype.asData = function () {
}
data.measurements = data.measies
delete data.measies
delete data.info
return data
}

View file

@ -35,6 +35,7 @@ OptionPackModel.prototype.guardedCreate = async function ({ body, user }) {
* Is design set?
*/
if (!body.design || typeof body.design !== 'string') return this.setResponse(400, 'designMissing')
/*
* Is nameEn set?
*/
@ -57,6 +58,11 @@ OptionPackModel.prototype.guardedCreate = async function ({ body, user }) {
}
if (body.tags && Array.isArray(body.tags)) data.tags = JSON.stringify(body.tags)
/*
* Add the info
*/
if (body.info) data.info = body.info
/*
* Add the options if there are any
*/
@ -187,6 +193,11 @@ OptionPackModel.prototype.guardedUpdate = async function ({ params, body, user }
}
}
/*
* Handle the info field
*/
if (typeof body.info === 'string') data.info = body.info
/*
* Handle the options
*/
@ -305,7 +316,10 @@ OptionPackModel.prototype.suggest = async function ({ body, user }) {
* @returns {optionPack} object - The Option Pack as a plain object
*/
OptionPackModel.prototype.asOptionPack = function () {
return this.record
const data = { ...this.record }
delete data.info
return data
}
/*

View file

@ -10,7 +10,6 @@ export function PatternModel(tools) {
name: 'pattern',
encryptedFields: ['data', 'name', 'notes', 'settings'],
jsonFields: ['data'],
models: ['set'],
})
}
@ -33,10 +32,6 @@ PatternModel.prototype.userPatterns = async function (uid) {
try {
patterns = await this.prisma.pattern.findMany({
where: { userId: uid },
include: {
set: true,
cset: true,
},
})
} catch (err) {
log.warn(`Failed to search patterns for user ${uid}: ${err}`)
@ -95,11 +90,9 @@ PatternModel.prototype.guardedCreate = async function ({ body, user }) {
* Create initial record
*/
await this.createRecord({
csetId: body.cset ? body.cset : null,
data: typeof body.data === 'object' ? body.data : {},
design: body.design,
img: this.config.avatars.pattern,
setId: body.set ? body.set : null,
settings: {
...body.settings,
measurements:
@ -129,7 +122,7 @@ PatternModel.prototype.guardedCreate = async function ({ body, user }) {
* If not, just update the record from the database
*/
await this.update({ img })
} else await this.read({ id: this.record.id }, { set: true, cset: true })
} else await this.read({ id: this.record.id })
/*
* Now return 201 and the record data
@ -147,7 +140,7 @@ PatternModel.prototype.publicRead = async function ({ params }) {
/*
* Attempt to read the database record
*/
await this.read({ id: parseInt(params.id) }, { set: true, cset: true })
await this.read({ id: parseInt(params.id) })
/*
* Ensure it is public and if it is not public, return 404
@ -184,7 +177,7 @@ PatternModel.prototype.guardedRead = async function ({ params, user }) {
/*
* Attempt to read record from database
*/
await this.read({ id: parseInt(params.id) }, { set: true, cset: true })
await this.read({ id: parseInt(params.id) })
/*
* Return 404 if it cannot be found
@ -272,7 +265,7 @@ PatternModel.prototype.guardedUpdate = async function ({ params, body, user }) {
/*
* Attempt to read record from the database
*/
await this.read({ id: parseInt(params.id) }, { set: true, cset: true })
await this.read({ id: parseInt(params.id) })
/*
* Only admins can update other people's patterns
@ -322,7 +315,7 @@ PatternModel.prototype.guardedUpdate = async function ({ params, body, user }) {
/*
* Now update the record
*/
await this.update(data, { set: true, cset: true })
await this.update(data)
/*
* Return 200 and the data
@ -391,8 +384,6 @@ PatternModel.prototype.revealPattern = function (pattern) {
//console.log(err)
}
}
if (pattern.set) delete pattern.set.measies
if (pattern.cset) delete pattern.cset.measies
return { ...pattern, ...clear }
}
@ -410,219 +401,3 @@ PatternModel.prototype.asPublicPattern = function () {
return data
}
/*
*
* Everything below this comment is v2 => v3 migration code
* And can be removed after the migration
*/
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,
'maat 28, met borsten': 1,
'maat 30, met borsten': 2,
'maat 32, met borsten': 3,
'maat 34, met borsten': 4,
'maat 36, met borsten': 5,
'maat 38, met borsten': 6,
'maat 40, met borsten': 7,
'maat 42, met borsten': 8,
'maat 44, met borsten': 9,
'maat 46, met borsten': 10,
'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,
'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,
'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,
'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,
'size-28-without-breasts': 11,
'size-30-without-breasts': 12,
'size-32-without-breasts': 13,
'size-34-without-breasts': 14,
'size-36-without-breasts': 15,
'size-38-without-breasts': 16,
'size-40-without-breasts': 17,
'size-42-without-breasts': 18,
'size-44-without-breasts': 19,
'size-46-without-breasts': 20,
'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,
'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,
'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) {
for (const pattern of Object.values(v2user.patterns)) {
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 {
this.createRecord(data)
} catch (err) {
log.warn(err, 'Could not create pattern')
console.log(data)
}
}
}
}

View file

@ -1,5 +1,5 @@
import { log } from '../utils/log.mjs'
import { replaceImage, storeImage, importImage } from '../utils/cloudflare-images.mjs'
import { replaceImage, storeImage } from '../utils/cloudflare-images.mjs'
import { decorateModel } from '../utils/model-decorator.mjs'
/*
@ -386,79 +386,3 @@ SetModel.prototype.asPublicSet = function () {
return data
}
/*
*
* Everything below this comment is part of the v2 => v3 migration code
* and can be removed once that migration is complete
*/
const migratePerson = (v2) => ({
createdAt: new Date(v2.created ? v2.created : v2.createdAt),
imperial: v2.units === 'imperial',
name: v2.name || '--', // Encrypted, so always set _some_ value
notes: v2.notes || '--', // Encrypted, so always set _some_ value
measies: v2.measurements || {}, // Encrypted, so always set _some_ value
updatedAt: new Date(v2.updatedAt),
})
/*
* This is a special import route
*/
SetModel.prototype.migrate = async function (v2user, userId) {
const lut = {} // lookup tabel for v2 handle to v3 id
for (const [handle, person] of Object.entries(v2user.people)) {
const data = { ...migratePerson(person), userId }
data.img = 'default-avatar'
try {
await this.createRecord(data)
lut[handle] = this.record.id
} catch (err) {
log.warn(err, 'Could not create set')
console.log(person)
}
}
return lut
}
/*
* This is a special route not available for API users
*/
SetModel.prototype.import = async function (v2user, userId) {
const lut = {} // lookup tabel for v2 handle to v3 id
for (const [handle, person] of Object.entries(v2user.people)) {
const data = { ...migratePerson(person), userId }
await this.createRecord(data)
// Now that we have an ID, we can handle the image
if (person.picture && person.picture.slice(-4) !== '.svg') {
const imgId = `set-${this.record.id}`
const imgUrl =
'https://static.freesewing.org/users/' +
encodeURIComponent(v2user.handle.slice(0, 1)) +
'/' +
encodeURIComponent(v2user.handle) +
'/people/' +
encodeURIComponent(person.handle) +
'/' +
encodeURIComponent(person.picture)
data.img = await importImage({
id: imgId,
metadata: {
user: userId,
v2PersonHandle: handle,
},
url: imgUrl,
})
data.img = imgId
} else data.img = 'default-avatar'
try {
await this.createRecord(data)
lut[handle] = this.record.id
} catch (err) {
log.warn(err, 'Could not create set')
console.log(person)
}
}
return lut
}

View file

@ -1,5 +1,4 @@
import { hash } from '../utils/crypto.mjs'
import { log } from '../utils/log.mjs'
import { clean, i18nUrl } from '../utils/index.mjs'
import { decorateModel } from '../utils/model-decorator.mjs'
@ -192,42 +191,3 @@ SubscriberModel.prototype.verifySubscription = async function (body) {
return this
}
/*
*
* Anything below this comment is migration code for the v2 => v3 migration
* and can be safely removed after the migration is done
*/
/*
* This is a special route not available for API users
*/
SubscriberModel.prototype.import = async function (list) {
let created = 0
for (const sub of list) {
const email = clean(sub)
const ehash = hash(email)
await this.read({ ehash })
if (!this.record) {
const data = await this.cloak({
ehash,
email,
language: 'en',
active: true,
})
try {
this.record = await this.prisma.subscriber.create({ data })
created++
} catch (err) {
log.warn(err, 'Could not create subscriber record')
return this.setResponse(500, 'createSubscriberFailed')
}
}
}
return this.setResponse(200, 'success', {
total: list.length,
imported: created,
})
}

View file

@ -1,7 +1,7 @@
import jwt from 'jsonwebtoken'
import { log } from '../utils/log.mjs'
import { hash, hashPassword, randomString, verifyPassword } from '../utils/crypto.mjs'
import { replaceImage, importImage, removeImage } from '../utils/cloudflare-images.mjs'
import { replaceImage, removeImage } from '../utils/cloudflare-images.mjs'
import { clean, asJson, i18nUrl, writeExportedData } from '../utils/index.mjs'
import { decorateModel } from '../utils/model-decorator.mjs'
import { userCard } from '../templates/svg/user-card.mjs'
@ -171,18 +171,16 @@ UserModel.prototype.oauthSignIn = async function ({ body }) {
*/
if (oauthData.img) {
try {
const img = await replaceImage({
await replaceImage({
id: `user-${ihash}`,
metadata: { ihash },
url: oauthData.img,
})
if (img) data.img = this.encrypt(img)
else data.img = this.encrypt(this.config.avatars.user)
} catch (err) {
log.info(err, `Unable to update image post-oauth signup for user ${email}`)
return this.setResponse(500, 'createAccountFailed')
}
} else data.img = this.config.avatars.user
}
/*
* Now attempt to create the record in the database
@ -763,7 +761,6 @@ UserModel.prototype.guardedCreate = async function ({ body }) {
*/
data: this.encrypt({}),
bio: this.encrypt(''),
img: this.config.avatars.user,
}
/*
* During tests, users can set their own permission level so you can test admin stuff
@ -1295,7 +1292,7 @@ UserModel.prototype.guardedUpdate = async function ({ body, user }) {
* Image (img)
*/
if (typeof body.img === 'string')
data.img = await replaceImage({
await replaceImage({
id: `uid-${this.record.ihash}`,
data: body.img,
})
@ -1593,7 +1590,6 @@ UserModel.prototype.asProfile = function () {
return {
id: this.record.id,
bio: this.clear.bio,
img: this.record.img,
ihash: this.record.ihash,
patron: this.record.patron,
role: this.record.role,
@ -1620,7 +1616,6 @@ UserModel.prototype.asAccount = function () {
email: this.clear.email,
data: this.clear.data,
ihash: this.record.ihash,
img: this.record.img,
imperial: this.record.imperial,
initial: this.clear.initial,
jwtCalls: this.record.jwtCalls,
@ -1901,150 +1896,3 @@ UserModel.prototype.papersPlease = async function (id, type, payload) {
*/
return [true, false]
}
/*
* Everything below this comment is migration code.
* This can all be removed after v3 is in production and all users have been migrated.
*/
const migrateUser = (v2) => {
const email = clean(v2.email)
const initial = v2.initial ? clean(v2.initial) : email
const data = {
bio: v2.bio || '--',
consent: 0,
createdAt: v2.time?.created ? new Date(v2.time.created) : new Date(),
email,
ehash: hash(email),
data: {},
ihash: hash(initial),
img: 'default-avatar',
initial,
imperial: v2.settings.units === 'imperial',
language: v2.settings.language,
lastSeen: new Date(),
lusername: v2.username.toLowerCase(),
mfaEnabled: false,
newsletter: v2.newsletter === true ? true : false,
patron: v2.patron,
role: v2._id === '5d62aa44ce141a3b816a3dd9' ? 'admin' : 'user',
status: v2.status === 'active' ? 1 : 0,
username: v2.username,
}
if (data.consent.profile) data.consent++
if (data.consent.measurements) data.consent++
if (data.consent.openData) data.consent++
return data
}
/*
* This is a special migration route
*/
UserModel.prototype.migrate = async function ({ password, v2 }) {
//let lut = false
const data = migrateUser(v2.account)
if (v2.account.consent.profile && (v2.account.consent.model || v2.account.consent.measurements)) {
data.consent++
if (v2.account.consent.openData) v2.account.consent++
}
data.password = password
await this.read({ ehash: data.ehash })
if (!this.record) {
/*
* Skip images for now
*/
data.img = 'default-avatar'
let available = await this.isLusernameAvailable(data.lusername)
while (!available) {
data.username += '+'
data.lusername += '+'
available = await this.isLusernameAvailable(data.lusername)
}
try {
await this.createRecord(data)
} catch (err) {
log.warn(err, 'Could not create user record')
return this.setResponse(500, 'createUserFailed')
}
// That's the user, now load their people as sets
const user = {
...v2.account,
people: v2.people,
patterns: v2.patterns,
}
if (user.people) await this.Set.migrate(user, this.record.id)
//if (user.people) lut = await this.Set.migrate(user, this.record.id)
//if (user.patterns) await this.Pattern.import(user, lut, this.record.id)
} else {
return this.setResponse(400, 'userExists')
}
/*
* Decrypt data so we can return it
*/
await this.reveal()
/*
* Looks like the migration was a success. Return a passwordless sign in
*/
return this.signInOk()
}
/*
* This is a special route not available for API users
*/
UserModel.prototype.import = async function (user) {
if (user.status === 'active') {
const data = migrateUser(user)
if (user.consent.profile && (user.consent.model || user.consent.measurements)) {
data.consent++
if (user.consent.openData) data.consent++
}
await this.read({ ehash: data.ehash })
if (!this.record) {
/*
* Skip images for now
*/
if (data.img) {
/*
* Figure out what image to grab from the FreeSewing v2 backend server
*/
const imgId = `user-${data.ihash}`
const imgUrl =
'https://static.freesewing.org/users/' +
encodeURIComponent(user.handle.slice(0, 1)) +
'/' +
encodeURIComponent(user.handle) +
'/' +
encodeURIComponent(data.img)
data.img = await importImage({
id: imgId,
metadata: {
user: `v2-${user.handle}`,
ihash: data.ihash,
},
url: imgUrl,
})
data.img = imgId
} else data.img = 'default-avatar'
let available = await this.isLusernameAvailable(data.lusername)
while (!available) {
data.username += '+'
data.lusername += '+'
available = await this.isLusernameAvailable(data.lusername)
}
try {
await this.createRecord(data)
} catch (err) {
log.warn(err, 'Could not create user record')
return this.setResponse(500, 'createUserFailed')
}
// That's the user, now load their people as sets
let lut = false
if (user.people) lut = await this.Set.import(user, this.record.id)
if (user.patterns) await this.Pattern.import(user, lut, this.record.id)
}
}
return this.setResponse200()
}

View file

@ -1,21 +0,0 @@
import { ImportsController } from '../controllers/imports.mjs'
const Import = new ImportsController()
export function importsRoutes(tools) {
const { app } = tools
/*
* All these routes use hard-coded credentials because they should never be used
* outside the v2-v3 migration which is handled by joost
*/
// Import newsletter subscriptions
app.post('/import/subscribers', (req, res) => Import.subscribers(req, res, tools))
// Import users
app.post('/import/user', (req, res) => Import.user(req, res, tools))
// Migrate user
app.post('/migrate', (req, res) => Import.migrate(req, res, tools))
}

View file

@ -9,7 +9,6 @@ import { optionPacksRoutes } from './option-packs.mjs'
import { subscribersRoutes } from './subscribers.mjs'
import { flowsRoutes } from './flows.mjs'
import { adminRoutes } from './admin.mjs'
import { importsRoutes } from './imports.mjs'
export const routes = {
apikeysRoutes,
@ -23,5 +22,4 @@ export const routes = {
subscribersRoutes,
flowsRoutes,
adminRoutes,
importsRoutes,
}

View file

@ -92,30 +92,6 @@ export async function removeImage(id) {
return true
}
/*
* Method that imports and image from URL and does not bother waiting for the answer
*/
export async function importImage(props, isTest = false) {
if (isTest) return props.id || false
// Bypass slow ass upload when testing import
if (!config.import) return `default-avatar`
// Only upload user images for now
if (props.id.slice(0, 5) !== 'user-') return `default-avatar`
const form = getFormData(props)
/*
* The image may already exist, so swallow the error
*/
try {
await axios.post(config.api, form, { headers })
} catch {
// Do nothing
}
return props.id
}
/*
* Helper method to construct the form data for cloudflare
*/

View file

@ -217,13 +217,13 @@ export const accountTests = async (chai, config, expect, store) => {
expect(err === null).to.equal(true)
expect(res.status).to.equal(200)
expect(res.body.result).to.equal(`success`)
expect(typeof res.body.account.img).to.equal('string')
done()
})
}).timeout(5000)
}
let confirmation
// eslint-disable-next-line no-undef
step(
`${store.icon('user', auth)} Should update the account email address (${auth})`,
(done) => {
@ -247,13 +247,13 @@ export const accountTests = async (chai, config, expect, store) => {
expect(err === null).to.equal(true)
expect(res.status).to.equal(200)
expect(res.body.result).to.equal(`success`)
expect(typeof res.body.account.img).to.equal('string')
confirmation = res.body.confirmation
done()
})
}
)
// eslint-disable-next-line no-undef
step(`${store.icon('user', auth)} Should confirm the email change (${auth})`, (done) => {
chai
.request(config.api)
@ -275,12 +275,12 @@ export const accountTests = async (chai, config, expect, store) => {
expect(err === null).to.equal(true)
expect(res.status).to.equal(200)
expect(res.body.result).to.equal(`success`)
expect(typeof res.body.account.img).to.equal('string')
confirmation = res.body.confirmation
done()
})
})
// eslint-disable-next-line no-undef
step(`${store.icon('user', auth)} Restore email address (${auth})`, (done) => {
chai
.request(config.api)
@ -302,12 +302,12 @@ export const accountTests = async (chai, config, expect, store) => {
expect(err === null).to.equal(true)
expect(res.status).to.equal(200)
expect(res.body.result).to.equal(`success`)
expect(typeof res.body.account.img).to.equal('string')
confirmation = res.body.confirmation
done()
})
})
// eslint-disable-next-line no-undef
step(
`${store.icon('user', auth)} Should confirm the (restore) email change (${auth})`,
(done) => {
@ -331,7 +331,6 @@ export const accountTests = async (chai, config, expect, store) => {
expect(err === null).to.equal(true)
expect(res.status).to.equal(200)
expect(res.body.result).to.equal(`success`)
expect(typeof res.body.account.img).to.equal('string')
confirmation = res.body.confirmation
done()
})

View file

@ -42,7 +42,6 @@ export const curatedSetTests = async (chai, config, expect, store) => {
chest: 930,
neck: 360,
},
img: cat,
},
}
store.curatedSet = {
@ -77,7 +76,7 @@ export const curatedSetTests = async (chai, config, expect, store) => {
expect(res.status).to.equal(201)
expect(res.body.result).to.equal(`created`)
for (const [key, val] of Object.entries(data[auth])) {
if (!['measies', 'img', 'test'].includes(key)) {
if (!['measies', 'test'].includes(key)) {
expect(JSON.stringify(res.body.curatedSet[key])).to.equal(JSON.stringify(val))
}
}
@ -226,8 +225,8 @@ export const curatedSetTests = async (chai, config, expect, store) => {
set: 1,
notes: 'These are the notes',
name: 'me',
img: 'https://images.pexels.com/photos/1404819/pexels-photo-1404819.jpeg?cs=srgb&dl=pexels-cong-h-1404819.jpg&fm=jpg&w=640&h=427&_gl=1*nyz31t*_ga*MTM0OTk5OTY4NS4xNjYxMjUyMjc0*_ga_8JE65Q40S6*MTY5Mzg0MzAwNi4yNC4xLjE2OTM4NDMwMjIuMC4wLjA.',
height: '166cm',
img: cat,
})
.end((err, res) => {
expect(err === null).to.equal(true)

View file

@ -1,4 +1,3 @@
import { cat } from './cat.mjs'
import { capitalize } from '../src/utils/index.mjs'
export const optionPackTests = async (chai, config, expect, store) => {
@ -46,7 +45,6 @@ export const optionPackTests = async (chai, config, expect, store) => {
necklineBend: 0.7,
necklineDrop: 0.4,
},
img: cat,
},
}
store.apack = {
@ -81,7 +79,7 @@ export const optionPackTests = async (chai, config, expect, store) => {
expect(res.status).to.equal(201)
expect(res.body.result).to.equal(`created`)
for (const [key, val] of Object.entries(data[auth])) {
if (!['options', 'img', 'test'].includes(key)) {
if (!['options', 'test'].includes(key)) {
expect(JSON.stringify(res.body.optionPack[key])).to.equal(JSON.stringify(val))
}
}

View file

@ -27,7 +27,6 @@ export const patternTests = async (chai, config, expect, store) => {
name: 'Just a test',
notes: 'These are my notes',
public: true,
set: store.account.sets.her.id,
data: {
some: 'value',
},
@ -39,7 +38,6 @@ export const patternTests = async (chai, config, expect, store) => {
expect(res.body.result).to.equal(`created`)
expect(typeof res.body.pattern?.id).to.equal('number')
expect(res.body.pattern.userId).to.equal(store.account.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.public).to.equal(true)
store.account.patterns[auth] = res.body.pattern
@ -121,29 +119,6 @@ export const patternTests = async (chai, config, expect, store) => {
})
})
it(`${store.icon('pattern', auth)} Should not update the set field (${auth})`, (done) => {
chai
.request(config.api)
.patch(`/patterns/${store.account.patterns[auth].id}/${auth}`)
.set(
'Authorization',
auth === 'jwt'
? 'Bearer ' + store.account.token
: 'Basic ' +
new Buffer(`${store.account.apikey.key}:${store.account.apikey.secret}`).toString(
'base64'
)
)
.send({ set: 1 })
.end((err, res) => {
expect(err === null).to.equal(true)
expect(res.status).to.equal(200)
expect(res.body.result).to.equal(`success`)
expect(res.body.pattern.setId).to.equal(store.account.sets.her.id)
done()
})
})
for (const field of ['data', 'settings']) {
it(`${store.icon('pattern', auth)} Should update the ${field} field (${auth})`, (done) => {
const data = {}