feat: Add internal runtime configuration

This commit is contained in:
Maksim Eltyshev
2026-01-22 18:02:42 +01:00
parent b852481850
commit 1264fd5715
79 changed files with 561 additions and 122 deletions
@@ -0,0 +1,47 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
exports.up = async (knex) => {
await knex.schema.createTable('internal_config', (table) => {
/* Columns */
table.bigInteger('id').primary().defaultTo(knex.raw('next_id()'));
table.text('storage_limit');
table.integer('active_users_limit');
table.boolean('is_initialized').notNullable();
table.timestamp('created_at', true);
table.timestamp('updated_at', true);
});
const { is_initialized: isInitialized } = await knex('config').select('is_initialized').first();
await knex('internal_config').insert({
isInitialized,
id: 1,
createdAt: new Date().toISOString(),
});
return knex.schema.alterTable('config', (table) => {
table.dropColumn('is_initialized');
});
};
exports.down = async (knex) => {
const { is_initialized: isInitialized } = await knex('internal_config')
.select('is_initialized')
.first();
await knex.schema.alterTable('config', (table) => {
table.boolean('is_initialized').notNullable().default(isInitialized);
});
await knex.schema.alterTable('config', (table) => {
table.boolean('is_initialized').notNullable().alter();
});
return knex.schema.dropTable('internal_config');
};
+41 -13
View File
@@ -5,7 +5,7 @@
const bcrypt = require('bcrypt');
const buildData = () => {
const buildUserData = () => {
const data = {
role: 'admin',
isSsoUser: false,
@@ -25,18 +25,35 @@ const buildData = () => {
return data;
};
exports.seed = async (knex) => {
const email = process.env.DEFAULT_ADMIN_EMAIL && process.env.DEFAULT_ADMIN_EMAIL.toLowerCase();
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 (email) {
const data = buildData();
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) {
const userData = buildUserData();
let userId;
try {
[{ id: userId }] = await knex('user_account').insert(
{
...data,
email,
...userData,
email: defaultAdminEmail,
subscribeToOwnCards: false,
subscribeToCardWhenCommenting: true,
turnOffRecentCardHighlighting: false,
@@ -53,19 +70,30 @@ exports.seed = async (knex) => {
}
if (!userId) {
await knex('user_account').update(data).where('email', email);
await knex('user_account').update(data).where('email', defaultAdminEmail);
}
}
const activeUserLimit = parseInt(process.env.ACTIVE_USER_LIMIT, 10);
const internalConfigData = buildInternalConfigData();
if (!Number.isNaN(activeUserLimit)) {
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 (email) {
if (defaultAdminEmail) {
orderByQuery = 'CASE WHEN email = ? THEN 0 WHEN role = ? THEN 1 ELSE 2 END';
orderByQueryValues = [email, 'admin'];
orderByQueryValues = [defaultAdminEmail, 'admin'];
} else {
orderByQuery = 'CASE WHEN role = ? THEN 0 ELSE 1 END';
orderByQueryValues = 'admin';
@@ -76,7 +104,7 @@ exports.seed = async (knex) => {
.where('is_deactivated', false)
.orderByRaw(orderByQuery, orderByQueryValues)
.orderBy('id')
.offset(activeUserLimit);
.offset(activeUsersLimit);
if (users.length > 0) {
const userIds = users.map(({ id }) => id);