/*! * Copyright (c) 2024 PLANKA Software GmbH * Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md */ /* eslint-disable no-console */ const bcrypt = require('bcrypt'); const validator = require('validator'); const USERNAME_REGEX = /^[a-zA-Z0-9]+((_|\.)?[a-zA-Z0-9])*$/; const buildUserData = () => { const data = { role: 'admin', isDeactivated: false, }; if (process.env.DEFAULT_ADMIN_PASSWORD) { data.password = bcrypt.hashSync(process.env.DEFAULT_ADMIN_PASSWORD, 10); } if (process.env.DEFAULT_ADMIN_NAME) { if (process.env.DEFAULT_ADMIN_NAME.length > 128) { console.warn('Warning: DEFAULT_ADMIN_NAME exceeds 128 characters; truncating.'); data.name = process.env.DEFAULT_ADMIN_NAME.slice(0, 128); } else { data.name = process.env.DEFAULT_ADMIN_NAME; } } if (process.env.DEFAULT_ADMIN_USERNAME) { const username = process.env.DEFAULT_ADMIN_USERNAME.toLowerCase(); if (username.length < 3 || username.length > 32 || !USERNAME_REGEX.test(username)) { console.warn( `Warning: DEFAULT_ADMIN_USERNAME "${process.env.DEFAULT_ADMIN_USERNAME}" is invalid; skipping.`, ); } else { data.username = username; } } return data; }; const buildInternalConfigData = () => { const data = {}; if (process.env.STORAGE_LIMIT) { data.storageLimit = process.env.STORAGE_LIMIT; } if (process.env.ACTIVE_USERS_LIMIT) { const activeUsersLimit = parseInt(process.env.ACTIVE_USERS_LIMIT, 10); if (Number.isInteger(activeUsersLimit)) { data.activeUsersLimit = activeUsersLimit; } } return data; }; exports.seed = async (knex) => { const defaultAdminEmail = process.env.DEFAULT_ADMIN_EMAIL && process.env.DEFAULT_ADMIN_EMAIL.toLowerCase(); if (defaultAdminEmail) { if (!validator.isEmail(defaultAdminEmail)) { throw new Error( `DEFAULT_ADMIN_EMAIL "${process.env.DEFAULT_ADMIN_EMAIL}" is not a valid e-mail address.`, ); } if (defaultAdminEmail.length > 256) { throw new Error( `DEFAULT_ADMIN_EMAIL "${process.env.DEFAULT_ADMIN_EMAIL}" exceeds 256 characters.`, ); } const existingEmailUser = await knex('user_account').where('email', defaultAdminEmail).first(); if (existingEmailUser) { throw new Error(`User with DEFAULT_ADMIN_EMAIL "${defaultAdminEmail}" already exists.`); } const userData = buildUserData(); if (userData.username) { const existingUsernameUser = await knex('user_account') .where('username', userData.username) .first(); if (existingUsernameUser) { throw new Error(`User with DEFAULT_ADMIN_USERNAME "${userData.username}" already exists.`); } } let userId; try { [{ id: userId }] = await knex('user_account').insert( { ...userData, email: defaultAdminEmail, subscribeToOwnCards: false, subscribeToCardWhenCommenting: true, turnOffRecentCardHighlighting: false, enableFavoritesByDefault: true, defaultEditorMode: 'wysiwyg', defaultHomeView: 'groupedProjects', defaultProjectsOrder: 'byDefault', createdAt: new Date().toISOString(), }, 'id', ); } catch (error) { /* empty */ } if (!userId) { await knex('user_account').update(userData).where('email', defaultAdminEmail); } } const internalConfigData = buildInternalConfigData(); let activeUsersLimit; if (Object.keys(internalConfigData).length > 0) { [{ active_users_limit: activeUsersLimit }] = await knex('internal_config') .update(internalConfigData) .returning('active_users_limit'); } else { ({ active_users_limit: activeUsersLimit } = await knex('internal_config') .select('active_users_limit') .first()); } if (Number.isInteger(activeUsersLimit)) { let orderByQuery; let orderByQueryValues; if (defaultAdminEmail) { orderByQuery = 'CASE WHEN email = ? THEN 0 WHEN role = ? THEN 1 ELSE 2 END'; orderByQueryValues = [defaultAdminEmail, 'admin']; } else { orderByQuery = 'CASE WHEN role = ? THEN 0 ELSE 1 END'; orderByQueryValues = 'admin'; } const users = await knex('user_account') .select('id') .where('is_deactivated', false) .orderByRaw(orderByQuery, orderByQueryValues) .orderBy('id') .offset(activeUsersLimit); if (users.length > 0) { const userIds = users.map(({ id }) => id); await knex('user_account') .update({ isDeactivated: true, }) .whereIn('id', userIds); } } };