1
0
Fork 0

wip(backend): Account retrieval and doubles

This commit is contained in:
joostdecock 2022-11-02 13:54:20 +01:00
parent 553ab91d26
commit dc82faee73
6 changed files with 124 additions and 102 deletions

View file

@ -0,0 +1,51 @@
import path from 'path'
import fs from 'fs'
import chalk from 'chalk'
/*
* Note: This is not intended to work for you
*
* This script imports a raw database dump of the current (v2)
* FreeSewing backend and checks for duplicate usernames now that
* we treat them as case-insensitive.
*
* This is not the kind of thing you should try to run yourself
* because for one thing you do not have a raw database dump
*/
// Dumped data folder
const dir = '/home/joost/'
let i = 0
// Load filtered data for migration
const file = 'freesewing-filtered.json'
const data = JSON.parse(fs.readFileSync(path.resolve(dir, file), { encoding: 'utf-8' }))
console.log()
console.log('Checking:')
console.log(' 🧑 ', Object.keys(data.users).length, 'users')
console.log()
data.lusernames = {}
await checkUsers(data.users)
console.log()
async function checkUsers(users) {
const total = Object.keys(users).length
let i = 0
for (const user of Object.values(users)) {
i++
await checkUser(user)
}
}
async function checkUser(user) {
const lusername = user.username.toLowerCase()
if (typeof data.lusernames[lusername] === 'undefined') {
data.lusernames[lusername] = user
} else {
i++
const first = data.lusernames[lusername]
console.log(chalk.yellow(`${i}: ${lusername}`))
console.log(` - First by: ${chalk.green(first.handle)} / ${chalk.green(first.email)}`)
console.log(` - Later by: ${chalk.cyan(user.handle)} / ${chalk.cyan(user.email)}`)
}
}

View file

@ -32,7 +32,6 @@ console.log(' 🕺 ', Object.keys(data.people).length, 'people')
console.log(' 👕 ', Object.keys(data.patterns).length, 'patterns') console.log(' 👕 ', Object.keys(data.patterns).length, 'patterns')
console.log(' 📰 ', data.subscribers.length, 'subscribers') console.log(' 📰 ', data.subscribers.length, 'subscribers')
console.log() console.log()
data.lusernames = {}
data.userhandles = {} data.userhandles = {}
await migrateUsers(data.users) await migrateUsers(data.users)
console.log() console.log()
@ -127,34 +126,39 @@ async function migrateUsers(users) {
async function createUser(user) { async function createUser(user) {
const ehash = hash(clean(user.email)) const ehash = hash(clean(user.email))
let record let record
const _data = {
consent: user.consent,
createdAt: user.createdAt,
data: JSON.stringify(user.data),
ehash,
email: encrypt(clean(user.email)),
ihash: ehash,
initial: encrypt(clean(user.email)),
newsletter: user.newsletter,
password: JSON.stringify({
type: 'v2',
data: user.password,
}),
patron: user.patron,
role: user.role,
status: user.status,
username: user.username,
lusername: user.username.toLowerCase(),
lastLogin: new Date(user.lastLogin),
}
try { try {
record = await prisma.user.create({ record = await prisma.user.create({ data: _data })
data: {
consent: user.consent,
createdAt: user.createdAt,
data: JSON.stringify(user.data),
ehash,
email: encrypt(clean(user.email)),
ihash: ehash,
initial: encrypt(clean(user.email)),
newsletter: user.newsletter,
password: JSON.stringify({
type: 'v2',
data: user.password,
}),
patron: user.patron,
role: user.role,
status: user.status,
username: user.username,
lusername: user.username.toLowerCase(),
},
})
} catch (err) { } catch (err) {
console.log(user, err, data.lusernames[user.username.toLowerCase()]) _data.username += ' 2'
process.exit() _data.lusername += ' 2'
try {
record = await prisma.user.create({ data: _data })
} catch (err) {
console.log(err)
process.exit()
}
} }
data.userhandles[user.handle] = record.id data.userhandles[user.handle] = record.id
data.lusernames[record.lusername] = user
} }
/* /*

View file

@ -116,6 +116,7 @@ function migrateUser(entry) {
initial: entry.initial, initial: entry.initial,
newsletter: entry.newsletter, newsletter: entry.newsletter,
img: entry.img, img: entry.img,
lastLogin,
} }
} }

View file

@ -209,14 +209,14 @@ UserController.prototype.confirm = async (req, res, tools) => {
// Update user consent and status // Update user consent and status
let updateUser let updateUser
try { try {
updateUser = prisma.user.update({ updateUser = await prisma.user.update({
where: { where: {
id: data.id, id: account.id,
}, },
data: { data: {
status: 1, status: 1,
consent: req.body.consent, consent: req.body.consent,
lastLogin: 'CURRENT_TIMESTAMP', lastLogin: new Date(),
}, },
}) })
} catch (err) { } catch (err) {
@ -302,6 +302,37 @@ UserController.prototype.login = async function (req, res, tools) {
account: asAccount({ ...account }, decrypt), account: asAccount({ ...account }, decrypt),
}) })
} }
UserController.prototype.readAccount = async (req, res, tools) => {
if (!req.user?._id) return res.status(400).send({ error: 'bearerMissing', result })
// Destructure what we need from tools
const { prisma, decrypt } = tools
// Retrieve user account
let account
try {
account = await prisma.user.findUnique({
where: {
id: req.user._id,
},
})
} catch (err) {
log.warn(err, `Could not lookup user id ${req.user._id} from token data`)
return res.status(404).send({ error: 'failedToRetrieveUserIdFromTokenData', result })
}
if (!account) {
log.warn(err, `Could not find user id ${req.user._id} from token data`)
return res.status(404).send({ error: 'failedToLoadUserFromTokenData', result })
}
// Return account data
return res.status(200).send({
result: 'success',
account: asAccount({ ...account }, decrypt),
})
}
/* /*
// For people who have forgotten their password, or password-less logins // For people who have forgotten their password, or password-less logins
@ -365,28 +396,6 @@ UserController.prototype.create = (req, res) => {
}) })
} }
UserController.prototype.readAccount = (req, res) => {
if (!req.user._id) return res.sendStatus(400)
User.findById(req.user._id, (err, user) => {
if (user !== null) {
log.info('ping', { user, req })
const people = {}
Person.find({ user: user.handle }, (err, personList) => {
if (err) return res.sendStatus(400)
for (let person of personList) people[person.handle] = person.info()
const patterns = {}
Pattern.find({ user: user.handle }, (err, patternList) => {
if (err) return res.sendStatus(400)
for (let pattern of patternList) patterns[pattern.handle] = pattern.export()
return res.send({ account: user.account(), people, patterns })
})
})
} else {
return res.sendStatus(400)
}
})
}
UserController.prototype.readProfile = (req, res) => { UserController.prototype.readProfile = (req, res) => {
User.findOne({ username: req.params.username }, (err, user) => { User.findOne({ username: req.params.username }, (err, user) => {
if (err) return res.sendStatus(404) if (err) return res.sendStatus(404)

View file

@ -14,6 +14,11 @@ export function userRoutes(tools) {
// Login // Login
app.post('/login', (req, res) => User.login(req, res, tools)) app.post('/login', (req, res) => User.login(req, res, tools))
// Read account (own data)
app.get('/account', passport.authenticate(...jwt), (req, res) =>
User.readAccount(req, res, tools)
)
// Create account // Create account
//app.post( //app.post(
// '/account', // '/account',
@ -21,12 +26,6 @@ export function userRoutes(tools) {
//) //)
/* /*
// Read account (own data)
app.get(
'/account',
passport.authenticate(...jwt),
(req, res) => User.readAccount(req, res, params)
)
// Update account // Update account
app.put( app.put(

View file

@ -240,15 +240,11 @@ describe(`${user} Signup flow and authentication`, () => {
}) })
}) })
/* step(`${user} Should load account with JWT`, (done) => {
step(`${user} Should login with email address and password`, (done) => {
chai chai
.request(config.api) .request(config.api)
.post('/login') .get('/account')
.send({ .set('Authorization', 'Bearer ' + store.token)
username: store.username,
password: data.email,
})
.end((err, res) => { .end((err, res) => {
expect(res.status).to.equal(200) expect(res.status).to.equal(200)
expect(res.type).to.equal('application/json') expect(res.type).to.equal('application/json')
@ -257,52 +253,14 @@ describe(`${user} Signup flow and authentication`, () => {
expect(res.body.account.email).to.equal(data.email) expect(res.body.account.email).to.equal(data.email)
expect(res.body.account.username).to.equal(store.username) expect(res.body.account.username).to.equal(store.username)
expect(res.body.account.lusername).to.equal(store.username.toLowerCase()) expect(res.body.account.lusername).to.equal(store.username.toLowerCase())
expect(typeof res.body.token).to.equal(`string`)
expect(typeof res.body.account.id).to.equal(`number`) expect(typeof res.body.account.id).to.equal(`number`)
store.token = res.body.token
done() done()
}) })
}) })
/*
describe('Login/Logout and session handling', () => {
it('should login with the email address', (done) => {
chai
.request(config.backend)
.post('/login')
.send({
username: config.user.email,
password: config.user.password,
})
.end((err, res) => {
res.should.have.status(200)
let data = JSON.parse(res.text)
data.account.username.should.equal(config.user.username)
data.token.should.be.a('string')
done()
})
})
it('should load account with JSON Web Token', (done) => {
chai
.request(config.backend)
.get('/account')
.set('Authorization', 'Bearer ' + config.user.token)
.end((err, res) => {
if (err) console.log(err)
let data = JSON.parse(res.text)
res.should.have.status(200)
data.account.username.should.equal(config.user.username)
// Enable this once cleanup is implemented
//Object.keys(data.recipes).length.should.equal(0)
//Object.keys(data.people).length.should.equal(0)
done()
})
})
})
describe('Account management', () => { describe('Account management', () => {
it('should update the account avatar', (done) => { it('should update the account avatar', (done) => {
chai chai