1
0
Fork 0

wip(backend): More work on person/user

This commit is contained in:
joostdecock 2022-11-12 20:36:47 +01:00
parent 2e938ac29f
commit 39774f76fa
10 changed files with 59 additions and 36 deletions

View file

@ -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 #
#####################################################################

View file

@ -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])

Binary file not shown.

View file

@ -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()

View file

@ -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()

View file

@ -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,
},

View file

@ -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
}

View file

@ -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) {

View file

@ -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'
)
)
},
}

View file

@ -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,
},
}