wip(backend): Account retrieval and doubles
This commit is contained in:
parent
553ab91d26
commit
dc82faee73
6 changed files with 124 additions and 102 deletions
51
sites/backend/scripts/find-duplicate-usernames.mjs
Normal file
51
sites/backend/scripts/find-duplicate-usernames.mjs
Normal 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)}`)
|
||||
}
|
||||
}
|
|
@ -32,7 +32,6 @@ console.log(' 🕺 ', Object.keys(data.people).length, 'people')
|
|||
console.log(' 👕 ', Object.keys(data.patterns).length, 'patterns')
|
||||
console.log(' 📰 ', data.subscribers.length, 'subscribers')
|
||||
console.log()
|
||||
data.lusernames = {}
|
||||
data.userhandles = {}
|
||||
await migrateUsers(data.users)
|
||||
console.log()
|
||||
|
@ -127,34 +126,39 @@ async function migrateUsers(users) {
|
|||
async function createUser(user) {
|
||||
const ehash = hash(clean(user.email))
|
||||
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 {
|
||||
record = await prisma.user.create({
|
||||
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(),
|
||||
},
|
||||
})
|
||||
record = await prisma.user.create({ data: _data })
|
||||
} catch (err) {
|
||||
console.log(user, err, data.lusernames[user.username.toLowerCase()])
|
||||
process.exit()
|
||||
_data.username += ' 2'
|
||||
_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.lusernames[record.lusername] = user
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -116,6 +116,7 @@ function migrateUser(entry) {
|
|||
initial: entry.initial,
|
||||
newsletter: entry.newsletter,
|
||||
img: entry.img,
|
||||
lastLogin,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -209,14 +209,14 @@ UserController.prototype.confirm = async (req, res, tools) => {
|
|||
// Update user consent and status
|
||||
let updateUser
|
||||
try {
|
||||
updateUser = prisma.user.update({
|
||||
updateUser = await prisma.user.update({
|
||||
where: {
|
||||
id: data.id,
|
||||
id: account.id,
|
||||
},
|
||||
data: {
|
||||
status: 1,
|
||||
consent: req.body.consent,
|
||||
lastLogin: 'CURRENT_TIMESTAMP',
|
||||
lastLogin: new Date(),
|
||||
},
|
||||
})
|
||||
} catch (err) {
|
||||
|
@ -302,6 +302,37 @@ UserController.prototype.login = async function (req, res, tools) {
|
|||
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
|
||||
|
@ -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) => {
|
||||
User.findOne({ username: req.params.username }, (err, user) => {
|
||||
if (err) return res.sendStatus(404)
|
||||
|
|
|
@ -14,6 +14,11 @@ export function userRoutes(tools) {
|
|||
// Login
|
||||
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
|
||||
//app.post(
|
||||
// '/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
|
||||
app.put(
|
||||
|
|
|
@ -240,15 +240,11 @@ describe(`${user} Signup flow and authentication`, () => {
|
|||
})
|
||||
})
|
||||
|
||||
/*
|
||||
step(`${user} Should login with email address and password`, (done) => {
|
||||
step(`${user} Should load account with JWT`, (done) => {
|
||||
chai
|
||||
.request(config.api)
|
||||
.post('/login')
|
||||
.send({
|
||||
username: store.username,
|
||||
password: data.email,
|
||||
})
|
||||
.get('/account')
|
||||
.set('Authorization', 'Bearer ' + store.token)
|
||||
.end((err, res) => {
|
||||
expect(res.status).to.equal(200)
|
||||
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.username).to.equal(store.username)
|
||||
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`)
|
||||
store.token = res.body.token
|
||||
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', () => {
|
||||
it('should update the account avatar', (done) => {
|
||||
chai
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue