Initial commit
This commit is contained in:
@@ -0,0 +1,470 @@
|
||||
const db = require('./db')
|
||||
const bcrypt = require('bcrypt')
|
||||
const uid = require('uid-safe')
|
||||
const { deserialize } = require('./helpers/user')
|
||||
|
||||
const User = {}
|
||||
|
||||
// Create new user in DB.
|
||||
User.create = async user => {
|
||||
const SALT_ROUNDS = 12
|
||||
const bcryptHash = await bcrypt.hash(user.password, SALT_ROUNDS)
|
||||
|
||||
const values = [
|
||||
user.username || null,
|
||||
user.firstName || null,
|
||||
user.lastName || null,
|
||||
user.email || null,
|
||||
bcryptHash || null
|
||||
]
|
||||
|
||||
const text = `INSERT INTO "user" (
|
||||
username,
|
||||
first_name,
|
||||
last_name,
|
||||
email,
|
||||
bcrypt_hash
|
||||
)
|
||||
VALUES (${db.genParamStr(values)})
|
||||
RETURNING id`
|
||||
|
||||
const { rows } = await db.query(text, values)
|
||||
|
||||
const userId = rows[0].id
|
||||
|
||||
return userId
|
||||
}
|
||||
|
||||
// Save an activation token for a single user in DB.
|
||||
User.saveActivationToken = async (userId, activationToken) => {
|
||||
await db.query(
|
||||
'INSERT INTO user_token_activation (token, user_id) VALUES ($1, $2)',
|
||||
[activationToken, userId]
|
||||
)
|
||||
}
|
||||
|
||||
// Fetch user from DB by (valid) activation token.
|
||||
User.fetchByActivationToken = async activationToken => {
|
||||
const TOKEN_VALID_PERIOD = '1 week'
|
||||
const text = `
|
||||
SELECT u.id
|
||||
FROM user_token_activation t
|
||||
INNER JOIN "user" u ON u.id = t.user_id
|
||||
WHERE
|
||||
t.token = $1
|
||||
AND AGE(NOW(), t.time_created) < INTERVAL '${TOKEN_VALID_PERIOD}'
|
||||
`
|
||||
const values = [activationToken]
|
||||
|
||||
const { rows } = await db.query(text, values)
|
||||
const user = rows[0]
|
||||
|
||||
// TODO Perhaps suggest to the user to request another one and make a shortcut.
|
||||
if (!user) throw Error('Povezava je neveljavna ali pa je že potekla')
|
||||
|
||||
return user
|
||||
}
|
||||
|
||||
// Activate user account.
|
||||
User.activateAccount = async user => {
|
||||
await db.query(`UPDATE "user" SET status = 'active' WHERE id = $1`, [user.id])
|
||||
}
|
||||
|
||||
// Generate a user remember me token.
|
||||
User.generateRememberMeToken = async () => {
|
||||
const token = await uid(32)
|
||||
return token
|
||||
}
|
||||
|
||||
// Save a remember me token for a single user in DB.
|
||||
User.saveRememberMeToken = async (user, rememberMeToken) => {
|
||||
await db.query(
|
||||
'INSERT INTO user_token_remember_me (token, user_id) VALUES ($1, $2)',
|
||||
[rememberMeToken, user.id]
|
||||
)
|
||||
}
|
||||
|
||||
// Remove a specific remember me token from DB.
|
||||
User.clearRememberMeToken = async rememberMeToken => {
|
||||
await db.query('DELETE FROM user_token_remember_me WHERE token = $1', [
|
||||
rememberMeToken
|
||||
])
|
||||
}
|
||||
|
||||
// Fetch user data that should be available on every request from DB by id.
|
||||
User.fetchDeserializedDataById = async userId => {
|
||||
const text = `
|
||||
SELECT
|
||||
u.id,
|
||||
u.username,
|
||||
u.first_name,
|
||||
u.last_name,
|
||||
u.email,
|
||||
u.hits_per_page,
|
||||
ARRAY(
|
||||
SELECT jsonb_build_object(
|
||||
'roleName', r.role_name,
|
||||
'dictionaryId', r.dictionary_id,
|
||||
'administration', r.administration,
|
||||
'terminologyReview', r.terminology_review,
|
||||
'languageReview', r.language_review,
|
||||
'editing', r.editing)
|
||||
FROM user_role r
|
||||
WHERE r.user_id = u.id
|
||||
) user_roles,
|
||||
ARRAY(
|
||||
SELECT jsonb_build_object(
|
||||
'id', cr.entry_id,
|
||||
'isModerator', cr.is_moderator)
|
||||
FROM consultancy_entry_consultant cr
|
||||
WHERE cr.user_id = u.id
|
||||
) assigned_consultancy_entries
|
||||
FROM "user" u
|
||||
WHERE
|
||||
u.id = $1`
|
||||
const values = [userId]
|
||||
|
||||
const {
|
||||
rows: [user]
|
||||
} = await db.query(text, values)
|
||||
|
||||
const deserializedUser = deserialize.userById(user)
|
||||
return deserializedUser
|
||||
}
|
||||
|
||||
// Fetch all registered users on the portal
|
||||
User.fetchAll = async (resultsPerPage, page) => {
|
||||
const {
|
||||
rows: [{ result }]
|
||||
} = await db.query(
|
||||
`
|
||||
SELECT jsonb_build_object(
|
||||
'pages_total', (
|
||||
SELECT CEIL(COUNT(*) / $1::float)
|
||||
FROM "user"
|
||||
),
|
||||
'results', ARRAY(
|
||||
SELECT jsonb_build_object(
|
||||
'id', id,
|
||||
'userName', username,
|
||||
'email', email,
|
||||
'status', status
|
||||
)
|
||||
FROM "user"
|
||||
ORDER BY username
|
||||
LIMIT $1
|
||||
OFFSET $2
|
||||
)
|
||||
) result
|
||||
`,
|
||||
[resultsPerPage, resultsPerPage * (page - 1)]
|
||||
)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Fetch all users with at least one portal role along with all their portal roles.
|
||||
User.fetchAllWithPortalRoles = async () => {
|
||||
const text = `
|
||||
SELECT u.id,
|
||||
u.username,
|
||||
u.email,
|
||||
jsonb_build_object(
|
||||
'isPortalAdmin', 'portal admin' = ANY (r.roles_array),
|
||||
'isDictionariesAdmin', 'dictionaries admin' = ANY (r.roles_array),
|
||||
'isConsultancyAdmin', 'consultancy admin' = ANY (r.roles_array)
|
||||
) roles
|
||||
FROM (
|
||||
SELECT user_id, array_agg(role_name) roles_array
|
||||
FROM user_role
|
||||
WHERE role_name IN ('portal admin', 'dictionaries admin', 'consultancy admin')
|
||||
GROUP BY user_id
|
||||
) r
|
||||
LEFT JOIN "user" u ON u.id = r.user_id
|
||||
ORDER BY u.id
|
||||
`
|
||||
const { rows: users } = await db.query(text)
|
||||
return users
|
||||
}
|
||||
|
||||
// Find searched user with specific username or email
|
||||
User.findByUsernameOrEmail = async userNameEmail => {
|
||||
const text = `
|
||||
SELECT
|
||||
id,
|
||||
username,
|
||||
email
|
||||
FROM "user"
|
||||
WHERE username=$1
|
||||
OR email=$1`
|
||||
const value = [userNameEmail]
|
||||
|
||||
const { rows: user } = await db.query(text, value)
|
||||
return user
|
||||
}
|
||||
|
||||
// Delete relevant portal roles and set new ones.
|
||||
User.updatePortalRoles = async rolesPerUser => {
|
||||
const rolesPerUserArr = Object.entries(rolesPerUser)
|
||||
const dbClient = await db.getClient()
|
||||
try {
|
||||
await dbClient.query('BEGIN')
|
||||
|
||||
const removeAllPortalRolesQuery = `
|
||||
DELETE FROM user_role
|
||||
WHERE role_name IN ('portal admin', 'dictionaries admin', 'consultancy admin')`
|
||||
await dbClient.query(removeAllPortalRolesQuery)
|
||||
|
||||
const updateRoles = rolesPerUserArr.map(async ([userId, roles]) => {
|
||||
userId = +userId.replaceAll("'", '')
|
||||
const rolesArr = []
|
||||
if (roles.isPortalAdmin) rolesArr.push('portal admin')
|
||||
if (roles.isDictionariesAdmin) rolesArr.push('dictionaries admin')
|
||||
if (roles.isConsultancyAdmin) {
|
||||
rolesArr.push('consultancy admin')
|
||||
rolesArr.push('consultant')
|
||||
}
|
||||
|
||||
const updateRolesPerUser = rolesArr.map(async roleName => {
|
||||
const text = `
|
||||
INSERT INTO user_role (user_id, role_name)
|
||||
VALUES ($1, $2)`
|
||||
const values = [userId, roleName]
|
||||
|
||||
await dbClient.query(text, values)
|
||||
})
|
||||
|
||||
await Promise.all(updateRolesPerUser)
|
||||
})
|
||||
|
||||
await Promise.all(updateRoles)
|
||||
|
||||
const countAdminsQuery = `
|
||||
SELECT COUNT(*) portal_admins_count
|
||||
FROM user_role
|
||||
WHERE role_name = 'portal admin'`
|
||||
const { rows } = await dbClient.query(countAdminsQuery)
|
||||
const portalAdminsCount = +rows[0].portal_admins_count
|
||||
if (!portalAdminsCount) throw Error('Can not delete all portal admins')
|
||||
|
||||
await dbClient.query('COMMIT')
|
||||
} catch (error) {
|
||||
await dbClient.query('ROLLBACK')
|
||||
throw error
|
||||
} finally {
|
||||
dbClient.release()
|
||||
}
|
||||
}
|
||||
|
||||
User.fetchUser = async userId => {
|
||||
const text = `
|
||||
SELECT id, username, first_name, last_name, email
|
||||
FROM "user"
|
||||
WHERE id=$1`
|
||||
const value = [userId]
|
||||
|
||||
const { rows } = await db.query(text, value)
|
||||
const fetchedUser = rows[0]
|
||||
|
||||
const deserializedUserData = deserialize.user(fetchedUser)
|
||||
return deserializedUserData
|
||||
}
|
||||
|
||||
User.updateUser = async (userId, payload) => {
|
||||
const text = `
|
||||
UPDATE "user"
|
||||
SET
|
||||
username = $2,
|
||||
first_name = $3,
|
||||
last_name = $4
|
||||
WHERE id = $1`
|
||||
|
||||
const values = [userId, payload.username, payload.firstName, payload.lastName]
|
||||
|
||||
await db.query(text, values)
|
||||
}
|
||||
|
||||
User.fetchUserRoles = async userId => {
|
||||
const text = `
|
||||
SELECT
|
||||
jsonb_build_object(
|
||||
'isPortalAdmin', 'portal admin' = ANY (r.roles_array),
|
||||
'isDictionariesAdmin', 'dictionaries admin' = ANY (r.roles_array),
|
||||
'isConsultancyAdmin', 'consultancy admin' = ANY (r.roles_array),
|
||||
'isConsultant', 'consultant' = ANY (r.roles_array),
|
||||
'isEditor', 'editor' = ANY (r.roles_array)
|
||||
) roles
|
||||
FROM (
|
||||
SELECT user_id, array_agg(role_name) roles_array
|
||||
FROM user_role
|
||||
WHERE role_name IN ('portal admin', 'dictionaries admin', 'consultancy admin', 'consultant', 'editor')
|
||||
GROUP BY user_id
|
||||
) r
|
||||
LEFT JOIN "user" u ON u.id = r.user_id
|
||||
WHERE u.id=$1`
|
||||
const value = [userId]
|
||||
|
||||
const { rows } = await db.query(text, value)
|
||||
const fetchedUser = rows[0]
|
||||
|
||||
return fetchedUser
|
||||
}
|
||||
|
||||
User.fetchAllWithDictionaryRights = async dictionaryId => {
|
||||
const text = `
|
||||
SELECT u.username,u.email, u.id, r.administration, r.editing, r.terminology_review, r.language_review
|
||||
FROM user_role r
|
||||
INNER JOIN "user" u ON u.id = r.user_id
|
||||
WHERE dictionary_id = $1
|
||||
ORDER BY u.username`
|
||||
|
||||
const value = [dictionaryId]
|
||||
const { rows: fetchedUserRights } = await db.query(text, value)
|
||||
|
||||
const deserializedRights = fetchedUserRights.map(user =>
|
||||
deserialize.userRights(user)
|
||||
)
|
||||
return deserializedRights
|
||||
}
|
||||
|
||||
User.updateUserRights = async (dictionaryId, rightsPerUser) => {
|
||||
const rightsPerUserArr = Object.entries(rightsPerUser)
|
||||
const dbClient = await db.getClient()
|
||||
try {
|
||||
await dbClient.query('BEGIN')
|
||||
|
||||
const value = [dictionaryId]
|
||||
const text = `
|
||||
DELETE FROM user_role
|
||||
WHERE dictionary_id = $1`
|
||||
await dbClient.query(text, value)
|
||||
|
||||
const roleName = 'editor'
|
||||
const updateRights = rightsPerUserArr.map(async ([userId, roles]) => {
|
||||
userId = +userId.replaceAll("'", '')
|
||||
const values = [
|
||||
userId,
|
||||
roleName,
|
||||
dictionaryId,
|
||||
!!roles.isAdministration,
|
||||
!!roles.isEditing,
|
||||
!!roles.isTerminologyReview,
|
||||
!!roles.isLanguageReview
|
||||
]
|
||||
|
||||
const text = `
|
||||
INSERT INTO user_role (user_id, role_name, dictionary_id, administration, editing, terminology_review, language_review)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)`
|
||||
await dbClient.query(text, values)
|
||||
})
|
||||
|
||||
await Promise.all(updateRights)
|
||||
|
||||
const textCount = `
|
||||
SELECT COUNT(*) dictionary_admins_count
|
||||
FROM user_role
|
||||
WHERE administration = true
|
||||
AND dictionary_id = $1`
|
||||
const { rows } = await dbClient.query(textCount, value)
|
||||
const portalAdminsCount = +rows[0].dictionary_admins_count
|
||||
if (!portalAdminsCount) throw Error('Can not delete all dictionary admins')
|
||||
|
||||
await dbClient.query('COMMIT')
|
||||
} catch (error) {
|
||||
await dbClient.query('ROLLBACK')
|
||||
throw error
|
||||
} finally {
|
||||
dbClient.release()
|
||||
}
|
||||
}
|
||||
|
||||
User.deleteUserDictionary = async dictionaryId => {
|
||||
const text = 'DELETE FROM user_role WHERE dictionary_id = $1'
|
||||
const value = [dictionaryId]
|
||||
|
||||
await db.query(text, value)
|
||||
}
|
||||
|
||||
// Fetch user with role consultant or consultancy admin
|
||||
User.fetchConsultants = async () => {
|
||||
// fetch domains string for the user
|
||||
const text = `
|
||||
SELECT DISTINCT u.id, u.username, u.first_name, u.last_name, ur.domains
|
||||
FROM user_role ur
|
||||
INNER JOIN "user" u ON u.id = ur.user_id
|
||||
ORDER BY u.id ASC
|
||||
`
|
||||
|
||||
const { rows } = await db.query(text)
|
||||
const users = rows
|
||||
|
||||
return users
|
||||
}
|
||||
|
||||
// Insert consultancy role domains
|
||||
User.updateConsultancyDomains = async (id, value) => {
|
||||
await db.query('UPDATE user_role SET domains=$1 WHERE user_id = $2', [
|
||||
value,
|
||||
id
|
||||
])
|
||||
}
|
||||
|
||||
// Insert new consultant role with domain of
|
||||
User.insertNewConsultantWithDomain = async (userId, domains) => {
|
||||
const { rows } = await db.query(
|
||||
"SELECT user_id FROM user_role WHERE user_id = $1 and role_name = 'consultant'",
|
||||
[userId]
|
||||
)
|
||||
|
||||
if (rows.length) return
|
||||
|
||||
await db.query(
|
||||
"INSERT INTO user_role (user_id, domains, role_name) VALUES ($1, $2, 'consultant')",
|
||||
[userId, domains]
|
||||
)
|
||||
}
|
||||
|
||||
// Insert new consultant role with domain of
|
||||
User.insertNewConsultantWithDomainByUsername = async (username, domains) => {
|
||||
const { rows } = await db.query(
|
||||
'SELECT id FROM "user" WHERE username = $1 or email = $1',
|
||||
[username]
|
||||
)
|
||||
|
||||
await User.insertNewConsultantWithDomain(rows[0].id, domains)
|
||||
}
|
||||
|
||||
// Remove consultant role
|
||||
User.removeConsultant = async userId => {
|
||||
await db.query(
|
||||
`DELETE FROM user_role
|
||||
WHERE user_id=$1 and role_name='consultant'`,
|
||||
[userId]
|
||||
)
|
||||
}
|
||||
|
||||
User.fetchAllowedHitsPerPage = async () => {
|
||||
return (
|
||||
await db.query(`SELECT unnest(enum_range(NULL::user_hits_per_page))`)
|
||||
).rows.map(e => e.unnest)
|
||||
}
|
||||
|
||||
User.updateFirstNameAndLastName = async (username, firstName, lastName) => {
|
||||
return await db.query(
|
||||
`UPDATE "user"
|
||||
SET first_name=$2, last_name=$3
|
||||
WHERE username=$1;`,
|
||||
[username, firstName, lastName]
|
||||
)
|
||||
}
|
||||
|
||||
User.updateHitsPerPage = async (username, hitsPerPageAmount) => {
|
||||
return await db.query(
|
||||
`UPDATE "user"
|
||||
SET hits_per_page=$2::user_hits_per_page
|
||||
WHERE username=$1;`,
|
||||
[username, hitsPerPageAmount]
|
||||
)
|
||||
}
|
||||
|
||||
module.exports = User
|
||||
Reference in New Issue
Block a user