diff --git a/sites/backend/example.env b/sites/backend/example.env index b0dbbf1bbc1..5e9255f5a69 100644 --- a/sites/backend/example.env +++ b/sites/backend/example.env @@ -33,6 +33,20 @@ BACKEND_WEBSITE_DOMAIN=freesewing.org BACKEND_WEBSITE_SCHEME=https +##################################################################### +# Default avatars # +##################################################################### + +# For users +#BACKEND_AVATAR_USER=https://freesewing.org/avatar.svg + +# For people +#BACKEND_AVATAR_PERSON=https://freesewing.org/avatar.svg + +# For patterns +#BACKEND_AVATAR_PATTERN=https://freesewing.org/avatar.svg + + ##################################################################### # Encryption # ##################################################################### diff --git a/sites/backend/prisma/schema.prisma b/sites/backend/prisma/schema.prisma index adc0800d60d..bd9b9e5178f 100644 --- a/sites/backend/prisma/schema.prisma +++ b/sites/backend/prisma/schema.prisma @@ -48,7 +48,7 @@ model User { email String github String @default("") ihash String - img String @default("https://freesewing.org/avatar.svg") + img String? initial String imperial Boolean @default(false) language String @default("en") @@ -70,6 +70,7 @@ model Pattern { id Int @id @default(autoincrement()) createdAt DateTime @default(now()) data String? + img String? person Person? @relation(fields: [personId], references: [id]) personId Int? user User @relation(fields: [userId], references: [id]) @@ -82,7 +83,8 @@ model Pattern { model Person { id Int @id @default(autoincrement()) createdAt DateTime @default(now()) - img String @default("https://freesewing.org/avatar.svg") + img String? + imperial Boolean @default(false) name String @default("") notes String @default("") user User @relation(fields: [userId], references: [id]) diff --git a/sites/backend/prisma/schema.sqlite b/sites/backend/prisma/schema.sqlite index 27145196bb6..3b70180f5d1 100644 Binary files a/sites/backend/prisma/schema.sqlite and b/sites/backend/prisma/schema.sqlite differ diff --git a/sites/backend/scripts/newdb.mjs b/sites/backend/scripts/newdb.mjs index 2e453d8e90d..60d7eb595b9 100644 --- a/sites/backend/scripts/newdb.mjs +++ b/sites/backend/scripts/newdb.mjs @@ -8,27 +8,25 @@ dotenv.config() const newDb = () => { // Say hi console.log(banner + '\n') - const db = process.env.API_DB_URL.slice(6) + const db = process.env.BACKEND_DB_URL.slice(6) console.log(db) const schema = path.resolve('./prisma/schema.sqlite') try { if (fs.existsSync(db)) { console.log(` ⛔ Database detected - Not proceeding`) console.log(` If you want to create a new database, remove this file: ${chalk.cyan(db)}`) - } - else { + } else { console.log(` 🚨 Going to create a database at ${chalk.cyan(db)}`) - fs.copyFile(schema, db, err => { + fs.copyFile(schema, db, (err) => { if (err) console.log(` ⚠️ ${chalk.red(err)}: Unable to create database file`, err) else { console.log(` ${chalk.green('Database created')}`) } }) } - } catch(err) { + } catch (err) { console.log(` ERROR: Unable to detect database file at ${db}`, err) } } newDb() - diff --git a/sites/backend/scripts/rmdb.mjs b/sites/backend/scripts/rmdb.mjs index e799e20709c..3c7695419fd 100644 --- a/sites/backend/scripts/rmdb.mjs +++ b/sites/backend/scripts/rmdb.mjs @@ -6,7 +6,6 @@ import { banner } from '../../../scripts/banner.mjs' import dotenv from 'dotenv' dotenv.config() - const rmdb = async () => { // Say hi console.log(banner + '\n') @@ -16,18 +15,20 @@ const rmdb = async () => { ⚠️ There is ${chalk.bold('no way back')} from this - proceed with caution `) - const answer = await prompts([{ - type: 'confirm', - name: 'confirms', - message: 'Are you sure you want to completely remove your FreeSewing database?', - initial: false - }]) + const answer = await prompts([ + { + type: 'confirm', + name: 'confirms', + message: 'Are you sure you want to completely remove your FreeSewing database?', + initial: false, + }, + ]) if (answer.confirms) { console.log() // Nuke it from orbit - const db = process.env.API_DB_URL.slice(6) - fs.access(db, fs.constants.W_OK, err => { + const db = process.env.BACKEND_DB_URL.slice(6) + fs.access(db, fs.constants.W_OK, (err) => { if (err) console.log(` ⛔ Cannot remove ${chalk.green(db)} 🤔`) else { fs.unlinkSync(db) @@ -43,5 +44,3 @@ const rmdb = async () => { } rmdb() - - diff --git a/sites/backend/src/config.mjs b/sites/backend/src/config.mjs index fdd0078d293..27f27dd8fdc 100644 --- a/sites/backend/src/config.mjs +++ b/sites/backend/src/config.mjs @@ -39,6 +39,11 @@ const config = { levels: [0, 1, 2, 3, 4, 5, 6, 7, 8], expiryMaxSeconds: 365 * 24 * 3600, }, + avatars: { + user: process.env.BACKEND_AVATAR_USER || 'https://freesewing.org/avatar.svg', + person: process.env.BACKEND_AVATAR_PERSON || 'https://freesewing.org/avatar.svg', + pattern: process.env.BACKEND_AVATAR_PATTERN || 'https://freesewing.org/avatar.svg', + }, db: { url: process.env.BACKEND_DB_URL, }, diff --git a/sites/backend/src/models/person.mjs b/sites/backend/src/models/person.mjs index 59644217cb1..802f932197d 100644 --- a/sites/backend/src/models/person.mjs +++ b/sites/backend/src/models/person.mjs @@ -22,7 +22,10 @@ PersonModel.prototype.create = async function ({ body, user }) { if (body.notes || typeof body.notes === 'string') data.notes = body.notes if (body.public === true) data.public = true if (body.measies) data.measies = this.sanitizeMeasurements(body.measies) + data.imperial = body.imperial === true ? true : false data.userId = user.uid + // Set this one initially as we need the ID to create a custom img via Sanity + data.img = this.encrypt(this.config.avatars.person) // Create record try { @@ -32,7 +35,7 @@ PersonModel.prototype.create = async function ({ body, user }) { return this.setResponse(500, 'createPersonFailed') } - // Update img? (now that we have the ID) + // Update img? (now that we have the ID) const img = this.config.use.sanity && typeof body.img === 'string' && @@ -70,12 +73,9 @@ PersonModel.prototype.reveal = async function () { this.clear = {} if (this.record) { for (const field of this.encryptedFields) { - // Default avatar is not encrypted - if (field === 'img' && this.record.img.slice(0, 4) === 'http') - this.clear.img = this.record.img - else this.clear[field] = this.decrypt(this.record[field]) + this.clear[field] = this.decrypt(this.record[field]) } - } else console.log('no record') + } return this } diff --git a/sites/backend/src/models/user.mjs b/sites/backend/src/models/user.mjs index 08e2dc0ddc9..cab3c2c2970 100644 --- a/sites/backend/src/models/user.mjs +++ b/sites/backend/src/models/user.mjs @@ -12,6 +12,7 @@ export function UserModel(tools) { this.encrypt = tools.encrypt this.mailer = tools.email this.Confirmation = new ConfirmationModel(tools) + this.encryptedFields = ['bio', 'github', 'email', 'initial', 'img'] this.clear = {} // For holding decrypted data return this @@ -40,10 +41,9 @@ UserModel.prototype.read = async function (where) { UserModel.prototype.reveal = async function (where) { this.clear = {} if (this.record) { - this.clear.bio = this.decrypt(this.record.bio) - this.clear.github = this.decrypt(this.record.github) - this.clear.email = this.decrypt(this.record.email) - this.clear.initial = this.decrypt(this.record.initial) + for (const field of this.encryptedFields) { + this.clear[field] = this.decrypt(this.record[field]) + } } return this @@ -53,7 +53,7 @@ UserModel.prototype.reveal = async function (where) { * Helper method to encrypt at-rest data */ UserModel.prototype.cloak = function (data) { - for (const field of ['bio', 'github', 'email']) { + for (const field of this.encryptedFields) { if (typeof data[field] !== 'undefined') data[field] = this.encrypt(data[field]) } if (typeof data.password === 'string') data.password = asJson(hashPassword(data.password)) @@ -164,6 +164,8 @@ UserModel.prototype.create = async function ({ body }) { password: asJson(hashPassword(randomString())), // We'll change this later github: this.encrypt(''), bio: this.encrypt(''), + // Set this one initially as we need the ID to create a custom img via Sanity + img: this.encrypt(this.config.avatars.user), } this.record = await this.prisma.user.create({ data }) } catch (err) { diff --git a/sites/backend/src/utils/crypto.mjs b/sites/backend/src/utils/crypto.mjs index d3bbebec76b..07faae467ef 100644 --- a/sites/backend/src/utils/crypto.mjs +++ b/sites/backend/src/utils/crypto.mjs @@ -67,8 +67,10 @@ export const encryption = (stringKey, salt = 'FreeSewing') => { * Always return a string so we can store this in SQLite no problemo */ return asJson({ + // iv = Initialization Vector iv: iv.toString('hex'), - encrypted: Buffer.concat([cipher.update(data), cipher.final()]).toString('hex'), + // ct = CipherText + ct: Buffer.concat([cipher.update(data), cipher.final()]).toString('hex'), }) }, decrypt: (data) => { @@ -80,7 +82,7 @@ export const encryption = (stringKey, salt = 'FreeSewing') => { } catch (err) { throw ('Could not parse encrypted data in decrypt() call', err) } - if (!data.iv || typeof data.encrypted === 'undefined') { + if (!data.iv || typeof data.ct === 'undefined') { throw 'Encrypted data passed to decrypt() was malformed' } @@ -94,10 +96,9 @@ export const encryption = (stringKey, salt = 'FreeSewing') => { * so we return the same type as what was passed to encrypt() */ return JSON.parse( - Buffer.concat([ - decipher.update(Buffer.from(data.encrypted, 'hex')), - decipher.final(), - ]).toString('utf-8') + Buffer.concat([decipher.update(Buffer.from(data.ct, 'hex')), decipher.final()]).toString( + 'utf-8' + ) ) }, } diff --git a/sites/backend/tests/person.mjs b/sites/backend/tests/person.mjs index c124f6529dc..aa6e091657c 100644 --- a/sites/backend/tests/person.mjs +++ b/sites/backend/tests/person.mjs @@ -22,6 +22,7 @@ export const personTests = async (chai, config, expect, store) => { }, public: true, unittest: true, + imperial: true, }, key: { name: 'Sorcha', @@ -33,6 +34,7 @@ export const personTests = async (chai, config, expect, store) => { public: false, img: cat, unittest: true, + imperial: false, }, }