wip(backend): More work on person/user
This commit is contained in:
parent
2e938ac29f
commit
39774f76fa
10 changed files with 59 additions and 36 deletions
|
@ -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 #
|
||||
#####################################################################
|
||||
|
|
|
@ -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.
|
@ -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()
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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'
|
||||
)
|
||||
)
|
||||
},
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue