fix: Keep the seed from refusing to start an instance that already ran
The validation added with the admin user checks reached further than intended. The seed runs on every container start, not only on the first, and it threw on three conditions that are normal on a second start. The worst was the check for an existing user: after the first start the default admin exists by definition, so every restart after that ended in a container that would not come up. The insert is wrapped in a try and falls back to an update precisely because it is meant to run again, and that path was never reached. The other two were narrower but the same shape. `validator.isEmail` rejects `admin@localhost`, which is a common value in self-hosted setups, so an instance that had been running for a year would stop coming up after the upgrade. None of it throws now. Bad input is reported on stderr and skipped, the way an invalid username already was, and a username that belongs to someone else is left alone rather than taken. `db:create-admin-user` keeps the strict validation, which is where a person is there to read it and type again.
This commit is contained in:
+19
-17
@@ -62,34 +62,36 @@ 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.`,
|
||||
// Nothing in here may throw. The seed runs on every start, not only on the
|
||||
// first one, and a throw here leaves the container in a restart loop with an
|
||||
// instance that used to come up fine. Bad input is reported and skipped; the
|
||||
// interactive `db:create-admin-user` is where invalid input is refused.
|
||||
const isDefaultAdminEmailUsable =
|
||||
defaultAdminEmail && validator.isEmail(defaultAdminEmail) && defaultAdminEmail.length <= 256;
|
||||
|
||||
if (defaultAdminEmail && !isDefaultAdminEmailUsable) {
|
||||
console.warn(
|
||||
`Warning: DEFAULT_ADMIN_EMAIL "${process.env.DEFAULT_ADMIN_EMAIL}" is not a usable e-mail address; skipping the default admin user.`,
|
||||
);
|
||||
}
|
||||
|
||||
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.`);
|
||||
}
|
||||
|
||||
if (isDefaultAdminEmailUsable) {
|
||||
const userData = buildUserData();
|
||||
|
||||
if (userData.username) {
|
||||
const existingUsernameUser = await knex('user_account')
|
||||
.where('username', userData.username)
|
||||
.whereNot('email', defaultAdminEmail)
|
||||
.first();
|
||||
|
||||
// Taken by somebody else, so it cannot be applied. The account itself is
|
||||
// still created or refreshed, it simply keeps the username it has.
|
||||
if (existingUsernameUser) {
|
||||
throw new Error(`User with DEFAULT_ADMIN_USERNAME "${userData.username}" already exists.`);
|
||||
console.warn(
|
||||
`Warning: DEFAULT_ADMIN_USERNAME "${userData.username}" belongs to another user; leaving the username unchanged.`,
|
||||
);
|
||||
|
||||
delete userData.username;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user