import Planka upstream
This commit is contained in:
@@ -0,0 +1,661 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
module.exports.up = async (knex) => {
|
||||
await knex.raw(`
|
||||
CREATE EXTENSION IF NOT EXISTS pg_trgm;
|
||||
|
||||
CREATE SEQUENCE next_id_seq;
|
||||
CREATE FUNCTION next_id(OUT id BIGINT) AS $$
|
||||
DECLARE
|
||||
shard INT := 1;
|
||||
epoch BIGINT := 1567191600000;
|
||||
sequence BIGINT;
|
||||
milliseconds BIGINT;
|
||||
BEGIN
|
||||
SELECT nextval('next_id_seq') % 1024 INTO sequence;
|
||||
SELECT FLOOR(EXTRACT(EPOCH FROM clock_timestamp()) * 1000) INTO milliseconds;
|
||||
id := (milliseconds - epoch) << 23;
|
||||
id := id | (shard << 10);
|
||||
id := id | (sequence);
|
||||
END;
|
||||
$$ LANGUAGE PLPGSQL;
|
||||
`);
|
||||
|
||||
await knex.schema.createTable('file_reference', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.bigInteger('id').primary().defaultTo(knex.raw('next_id()'));
|
||||
|
||||
table.integer('total');
|
||||
|
||||
table.timestamp('created_at', true);
|
||||
table.timestamp('updated_at', true);
|
||||
|
||||
/* Indexes */
|
||||
|
||||
table.index('total');
|
||||
});
|
||||
|
||||
await knex.schema
|
||||
.createTable('user_account', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.bigInteger('id').primary().defaultTo(knex.raw('next_id()'));
|
||||
|
||||
table.text('email').notNullable();
|
||||
table.text('password');
|
||||
table.text('role').notNullable();
|
||||
table.text('name').notNullable();
|
||||
table.text('username');
|
||||
table.jsonb('avatar');
|
||||
table.text('phone');
|
||||
table.text('organization');
|
||||
table.text('language');
|
||||
table.boolean('subscribe_to_own_cards').notNullable();
|
||||
table.boolean('subscribe_to_card_when_commenting').notNullable();
|
||||
table.boolean('turn_off_recent_card_highlighting').notNullable();
|
||||
table.boolean('enable_favorites_by_default').notNullable();
|
||||
table.text('default_editor_mode').notNullable();
|
||||
table.text('default_home_view').notNullable();
|
||||
table.text('default_projects_order').notNullable();
|
||||
table.boolean('is_sso_user').notNullable();
|
||||
table.boolean('is_deactivated').notNullable();
|
||||
|
||||
table.timestamp('created_at', true);
|
||||
table.timestamp('updated_at', true);
|
||||
table.timestamp('password_changed_at', true);
|
||||
|
||||
/* Indexes */
|
||||
|
||||
table.unique('email');
|
||||
table.index('role');
|
||||
table.index('username');
|
||||
table.index('is_deactivated');
|
||||
})
|
||||
.raw(
|
||||
'ALTER TABLE user_account ADD CONSTRAINT user_account_username_unique EXCLUDE (username WITH =) WHERE (username IS NOT NULL);',
|
||||
);
|
||||
|
||||
await knex.schema.createTable('identity_provider_user', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.bigInteger('id').primary().defaultTo(knex.raw('next_id()'));
|
||||
|
||||
table.bigInteger('user_id').notNullable();
|
||||
|
||||
table.text('issuer').notNullable();
|
||||
table.text('sub').notNullable();
|
||||
|
||||
table.timestamp('created_at', true);
|
||||
table.timestamp('updated_at', true);
|
||||
|
||||
/* Indexes */
|
||||
|
||||
table.unique(['issuer', 'sub']);
|
||||
table.index('user_id');
|
||||
});
|
||||
|
||||
await knex.schema.createTable('session', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.bigInteger('id').primary().defaultTo(knex.raw('next_id()'));
|
||||
|
||||
table.bigInteger('user_id').notNullable();
|
||||
|
||||
table.text('access_token').notNullable();
|
||||
table.text('http_only_token');
|
||||
table.text('remote_address').notNullable();
|
||||
table.text('user_agent');
|
||||
|
||||
table.timestamp('created_at', true);
|
||||
table.timestamp('updated_at', true);
|
||||
table.timestamp('deleted_at', true);
|
||||
|
||||
/* Indexes */
|
||||
|
||||
table.index('user_id');
|
||||
table.unique('access_token');
|
||||
table.index('remote_address');
|
||||
});
|
||||
|
||||
await knex.schema.createTable('project', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.bigInteger('id').primary().defaultTo(knex.raw('next_id()'));
|
||||
|
||||
table.bigInteger('owner_project_manager_id');
|
||||
table.bigInteger('background_image_id');
|
||||
|
||||
table.text('name').notNullable();
|
||||
table.text('description');
|
||||
table.text('background_type');
|
||||
table.text('background_gradient');
|
||||
table.boolean('is_hidden').notNullable();
|
||||
|
||||
table.timestamp('created_at', true);
|
||||
table.timestamp('updated_at', true);
|
||||
|
||||
/* Indexes */
|
||||
|
||||
table.index('owner_project_manager_id');
|
||||
});
|
||||
|
||||
await knex.schema.createTable('project_favorite', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.bigInteger('id').primary().defaultTo(knex.raw('next_id()'));
|
||||
|
||||
table.bigInteger('project_id').notNullable();
|
||||
table.bigInteger('user_id').notNullable();
|
||||
|
||||
table.timestamp('created_at', true);
|
||||
table.timestamp('updated_at', true);
|
||||
|
||||
/* Indexes */
|
||||
|
||||
table.unique(['project_id', 'user_id']);
|
||||
table.index('user_id');
|
||||
});
|
||||
|
||||
await knex.schema.createTable('project_manager', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.bigInteger('id').primary().defaultTo(knex.raw('next_id()'));
|
||||
|
||||
table.bigInteger('project_id').notNullable();
|
||||
table.bigInteger('user_id').notNullable();
|
||||
|
||||
table.timestamp('created_at', true);
|
||||
table.timestamp('updated_at', true);
|
||||
|
||||
/* Indexes */
|
||||
|
||||
table.unique(['project_id', 'user_id']);
|
||||
table.index('user_id');
|
||||
});
|
||||
|
||||
await knex.schema.createTable('background_image', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.bigInteger('id').primary().defaultTo(knex.raw('next_id()'));
|
||||
|
||||
table.bigInteger('project_id').notNullable();
|
||||
|
||||
table.text('dirname').notNullable();
|
||||
table.text('extension').notNullable();
|
||||
table.bigInteger('size_in_bytes').notNullable();
|
||||
|
||||
table.timestamp('created_at', true);
|
||||
table.timestamp('updated_at', true);
|
||||
|
||||
/* Indexes */
|
||||
|
||||
table.index('project_id');
|
||||
});
|
||||
|
||||
await knex.schema.createTable('base_custom_field_group', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.bigInteger('id').primary().defaultTo(knex.raw('next_id()'));
|
||||
|
||||
table.bigInteger('project_id').notNullable();
|
||||
|
||||
table.text('name').notNullable();
|
||||
|
||||
table.timestamp('created_at', true);
|
||||
table.timestamp('updated_at', true);
|
||||
|
||||
/* Indexes */
|
||||
|
||||
table.index('project_id');
|
||||
});
|
||||
|
||||
await knex.schema.createTable('board', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.bigInteger('id').primary().defaultTo(knex.raw('next_id()'));
|
||||
|
||||
table.bigInteger('project_id').notNullable();
|
||||
|
||||
table.specificType('position', 'double precision').notNullable();
|
||||
table.text('name').notNullable();
|
||||
table.text('default_view').notNullable();
|
||||
table.text('default_card_type').notNullable();
|
||||
table.boolean('limit_card_types_to_default_one').notNullable();
|
||||
table.boolean('always_display_card_creator').notNullable();
|
||||
|
||||
table.timestamp('created_at', true);
|
||||
table.timestamp('updated_at', true);
|
||||
|
||||
/* Indexes */
|
||||
|
||||
table.index('project_id');
|
||||
table.index('position');
|
||||
});
|
||||
|
||||
await knex.schema.createTable('board_subscription', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.bigInteger('id').primary().defaultTo(knex.raw('next_id()'));
|
||||
|
||||
table.bigInteger('board_id').notNullable();
|
||||
table.bigInteger('user_id').notNullable();
|
||||
|
||||
table.timestamp('created_at', true);
|
||||
table.timestamp('updated_at', true);
|
||||
|
||||
/* Indexes */
|
||||
|
||||
table.unique(['board_id', 'user_id']);
|
||||
table.index('user_id');
|
||||
});
|
||||
|
||||
await knex.schema.createTable('board_membership', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.bigInteger('id').primary().defaultTo(knex.raw('next_id()'));
|
||||
|
||||
table.bigInteger('project_id').notNullable();
|
||||
table.bigInteger('board_id').notNullable();
|
||||
table.bigInteger('user_id').notNullable();
|
||||
|
||||
table.text('role').notNullable();
|
||||
table.boolean('can_comment');
|
||||
|
||||
table.timestamp('created_at', true);
|
||||
table.timestamp('updated_at', true);
|
||||
|
||||
/* Indexes */
|
||||
|
||||
table.index('project_id');
|
||||
table.unique(['board_id', 'user_id']);
|
||||
table.index('user_id');
|
||||
});
|
||||
|
||||
await knex.schema.createTable('label', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.bigInteger('id').primary().defaultTo(knex.raw('next_id()'));
|
||||
|
||||
table.bigInteger('board_id').notNullable();
|
||||
|
||||
table.specificType('position', 'double precision').notNullable();
|
||||
table.text('name');
|
||||
table.text('color').notNullable();
|
||||
|
||||
table.timestamp('created_at', true);
|
||||
table.timestamp('updated_at', true);
|
||||
|
||||
/* Indexes */
|
||||
|
||||
table.index('board_id');
|
||||
table.index('position');
|
||||
});
|
||||
|
||||
await knex.schema.createTable('list', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.bigInteger('id').primary().defaultTo(knex.raw('next_id()'));
|
||||
|
||||
table.bigInteger('board_id').notNullable();
|
||||
|
||||
table.text('type').notNullable();
|
||||
table.specificType('position', 'double precision');
|
||||
table.text('name');
|
||||
table.text('color');
|
||||
|
||||
table.timestamp('created_at', true);
|
||||
table.timestamp('updated_at', true);
|
||||
|
||||
/* Indexes */
|
||||
|
||||
table.index('board_id');
|
||||
table.index('type');
|
||||
table.index('position');
|
||||
});
|
||||
|
||||
await knex.schema.createTable('card', async (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.bigInteger('id').primary().defaultTo(knex.raw('next_id()'));
|
||||
|
||||
table.bigInteger('board_id').notNullable();
|
||||
table.bigInteger('list_id').notNullable();
|
||||
table.bigInteger('creator_user_id');
|
||||
table.bigInteger('prev_list_id');
|
||||
table.bigInteger('cover_attachment_id');
|
||||
|
||||
table.text('type').notNullable();
|
||||
table.specificType('position', 'double precision');
|
||||
table.text('name').notNullable();
|
||||
table.text('description');
|
||||
table.timestamp('due_date', true);
|
||||
table.jsonb('stopwatch');
|
||||
|
||||
table.timestamp('created_at', true);
|
||||
table.timestamp('updated_at', true);
|
||||
table.timestamp('list_changed_at', true);
|
||||
|
||||
/* Indexes */
|
||||
|
||||
table.index('board_id');
|
||||
table.index('list_id');
|
||||
table.index('creator_user_id');
|
||||
table.index('position');
|
||||
table.index('list_changed_at');
|
||||
}).raw(`
|
||||
CREATE INDEX card_name_index ON card USING GIN (name gin_trgm_ops);
|
||||
CREATE INDEX card_description_index ON card USING GIN (description gin_trgm_ops);
|
||||
`);
|
||||
|
||||
await knex.schema.createTable('card_subscription', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.bigInteger('id').primary().defaultTo(knex.raw('next_id()'));
|
||||
|
||||
table.bigInteger('card_id').notNullable();
|
||||
table.bigInteger('user_id').notNullable();
|
||||
|
||||
table.boolean('is_permanent').notNullable();
|
||||
|
||||
table.timestamp('created_at', true);
|
||||
table.timestamp('updated_at', true);
|
||||
|
||||
/* Indexes */
|
||||
|
||||
table.unique(['card_id', 'user_id']);
|
||||
table.index('user_id');
|
||||
});
|
||||
|
||||
await knex.schema.createTable('card_membership', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.bigInteger('id').primary().defaultTo(knex.raw('next_id()'));
|
||||
|
||||
table.bigInteger('card_id').notNullable();
|
||||
table.bigInteger('user_id').notNullable();
|
||||
|
||||
table.timestamp('created_at', true);
|
||||
table.timestamp('updated_at', true);
|
||||
|
||||
/* Indexes */
|
||||
|
||||
table.unique(['card_id', 'user_id']);
|
||||
table.index('user_id');
|
||||
});
|
||||
|
||||
await knex.schema.createTable('card_label', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.bigInteger('id').primary().defaultTo(knex.raw('next_id()'));
|
||||
|
||||
table.bigInteger('card_id').notNullable();
|
||||
table.bigInteger('label_id').notNullable();
|
||||
|
||||
table.timestamp('created_at', true);
|
||||
table.timestamp('updated_at', true);
|
||||
|
||||
/* Indexes */
|
||||
|
||||
table.unique(['card_id', 'label_id']);
|
||||
table.index('label_id');
|
||||
});
|
||||
|
||||
await knex.schema.createTable('task_list', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.bigInteger('id').primary().defaultTo(knex.raw('next_id()'));
|
||||
|
||||
table.bigInteger('card_id').notNullable();
|
||||
|
||||
table.specificType('position', 'double precision').notNullable();
|
||||
table.text('name').notNullable();
|
||||
table.boolean('show_on_front_of_card').notNullable();
|
||||
|
||||
table.timestamp('created_at', true);
|
||||
table.timestamp('updated_at', true);
|
||||
|
||||
/* Indexes */
|
||||
|
||||
table.index('card_id');
|
||||
table.index('position');
|
||||
});
|
||||
|
||||
await knex.schema.createTable('task', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.bigInteger('id').primary().defaultTo(knex.raw('next_id()'));
|
||||
|
||||
table.bigInteger('task_list_id').notNullable();
|
||||
table.bigInteger('assignee_user_id');
|
||||
|
||||
table.specificType('position', 'double precision').notNullable();
|
||||
table.text('name').notNullable();
|
||||
table.boolean('is_completed').notNullable();
|
||||
|
||||
table.timestamp('created_at', true);
|
||||
table.timestamp('updated_at', true);
|
||||
|
||||
/* Indexes */
|
||||
|
||||
table.index('task_list_id');
|
||||
table.index('assignee_user_id');
|
||||
table.index('position');
|
||||
});
|
||||
|
||||
await knex.schema.createTable('attachment', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.bigInteger('id').primary().defaultTo(knex.raw('next_id()'));
|
||||
|
||||
table.bigInteger('card_id').notNullable();
|
||||
table.bigInteger('creator_user_id');
|
||||
|
||||
table.text('type').notNullable();
|
||||
table.jsonb('data').notNullable();
|
||||
table.text('name').notNullable();
|
||||
|
||||
table.timestamp('created_at', true);
|
||||
table.timestamp('updated_at', true);
|
||||
|
||||
/* Indexes */
|
||||
|
||||
table.index('card_id');
|
||||
table.index('creator_user_id');
|
||||
});
|
||||
|
||||
await knex.schema.createTable('custom_field_group', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.bigInteger('id').primary().defaultTo(knex.raw('next_id()'));
|
||||
|
||||
table.bigInteger('board_id');
|
||||
table.bigInteger('card_id');
|
||||
table.bigInteger('base_custom_field_group_id');
|
||||
|
||||
table.specificType('position', 'double precision').notNullable();
|
||||
table.text('name');
|
||||
|
||||
table.timestamp('created_at', true);
|
||||
table.timestamp('updated_at', true);
|
||||
|
||||
/* Indexes */
|
||||
|
||||
table.index('board_id');
|
||||
table.index('card_id');
|
||||
table.index('base_custom_field_group_id');
|
||||
table.index('position');
|
||||
});
|
||||
|
||||
await knex.schema.createTable('custom_field', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.bigInteger('id').primary().defaultTo(knex.raw('next_id()'));
|
||||
|
||||
table.bigInteger('base_custom_field_group_id');
|
||||
table.bigInteger('custom_field_group_id');
|
||||
|
||||
table.specificType('position', 'double precision').notNullable();
|
||||
table.text('name').notNullable();
|
||||
table.boolean('show_on_front_of_card').notNullable();
|
||||
|
||||
table.timestamp('created_at', true);
|
||||
table.timestamp('updated_at', true);
|
||||
|
||||
/* Indexes */
|
||||
|
||||
table.index('base_custom_field_group_id');
|
||||
table.index('custom_field_group_id');
|
||||
table.index('position');
|
||||
});
|
||||
|
||||
await knex.schema.createTable('custom_field_value', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.bigInteger('id').primary().defaultTo(knex.raw('next_id()'));
|
||||
|
||||
table.bigInteger('card_id').notNullable();
|
||||
table.bigInteger('custom_field_group_id').notNullable();
|
||||
table.bigInteger('custom_field_id').notNullable();
|
||||
|
||||
table.text('content').notNullable();
|
||||
|
||||
table.timestamp('created_at', true);
|
||||
table.timestamp('updated_at', true);
|
||||
|
||||
/* Indexes */
|
||||
|
||||
table.unique(['card_id', 'custom_field_group_id', 'custom_field_id']);
|
||||
table.index('custom_field_group_id');
|
||||
table.index('custom_field_id');
|
||||
});
|
||||
|
||||
await knex.schema.createTable('comment', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.bigInteger('id').primary().defaultTo(knex.raw('next_id()'));
|
||||
|
||||
table.bigInteger('card_id').notNullable();
|
||||
table.bigInteger('user_id');
|
||||
|
||||
table.text('text').notNullable();
|
||||
|
||||
table.timestamp('created_at', true);
|
||||
table.timestamp('updated_at', true);
|
||||
|
||||
/* Indexes */
|
||||
|
||||
table.index('card_id');
|
||||
table.index('user_id');
|
||||
});
|
||||
|
||||
await knex.schema.createTable('action', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.bigInteger('id').primary().defaultTo(knex.raw('next_id()'));
|
||||
|
||||
table.bigInteger('card_id').notNullable();
|
||||
table.bigInteger('user_id');
|
||||
|
||||
table.text('type').notNullable();
|
||||
table.jsonb('data').notNullable();
|
||||
|
||||
table.timestamp('created_at', true);
|
||||
table.timestamp('updated_at', true);
|
||||
|
||||
/* Indexes */
|
||||
|
||||
table.index('card_id');
|
||||
table.index('user_id');
|
||||
});
|
||||
|
||||
await knex.schema.createTable('notification', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.bigInteger('id').primary().defaultTo(knex.raw('next_id()'));
|
||||
|
||||
table.bigInteger('user_id').notNullable();
|
||||
table.bigInteger('creator_user_id');
|
||||
table.bigInteger('board_id').notNullable();
|
||||
table.bigInteger('card_id').notNullable();
|
||||
table.bigInteger('comment_id');
|
||||
table.bigInteger('action_id');
|
||||
|
||||
table.text('type').notNullable();
|
||||
table.jsonb('data').notNullable();
|
||||
table.boolean('is_read').notNullable();
|
||||
|
||||
table.timestamp('created_at', true);
|
||||
table.timestamp('updated_at', true);
|
||||
|
||||
/* Indexes */
|
||||
|
||||
table.index('user_id');
|
||||
table.index('creator_user_id');
|
||||
table.index('card_id');
|
||||
table.index('comment_id');
|
||||
table.index('action_id');
|
||||
table.index('is_read');
|
||||
});
|
||||
|
||||
return knex.schema.createTable('notification_service', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.bigInteger('id').primary().defaultTo(knex.raw('next_id()'));
|
||||
|
||||
table.bigInteger('user_id');
|
||||
table.bigInteger('board_id');
|
||||
|
||||
table.text('url').notNullable();
|
||||
table.text('format').notNullable();
|
||||
|
||||
table.timestamp('created_at', true);
|
||||
table.timestamp('updated_at', true);
|
||||
|
||||
/* Indexes */
|
||||
|
||||
table.index('user_id');
|
||||
table.index('board_id');
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.down = async (knex) => {
|
||||
await knex.schema.dropTable('file_reference');
|
||||
await knex.schema.dropTable('user_account');
|
||||
await knex.schema.dropTable('identity_provider_user');
|
||||
await knex.schema.dropTable('session');
|
||||
await knex.schema.dropTable('project');
|
||||
await knex.schema.dropTable('project_favorite');
|
||||
await knex.schema.dropTable('project_manager');
|
||||
await knex.schema.dropTable('background_image');
|
||||
await knex.schema.dropTable('base_custom_field_group');
|
||||
await knex.schema.dropTable('board');
|
||||
await knex.schema.dropTable('board_subscription');
|
||||
await knex.schema.dropTable('board_membership');
|
||||
await knex.schema.dropTable('label');
|
||||
await knex.schema.dropTable('list');
|
||||
await knex.schema.dropTable('card');
|
||||
await knex.schema.dropTable('card_subscription');
|
||||
await knex.schema.dropTable('card_membership');
|
||||
await knex.schema.dropTable('card_label');
|
||||
await knex.schema.dropTable('task_list');
|
||||
await knex.schema.dropTable('task');
|
||||
await knex.schema.dropTable('attachment');
|
||||
await knex.schema.dropTable('custom_field_group');
|
||||
await knex.schema.dropTable('custom_field');
|
||||
await knex.schema.dropTable('custom_field_value');
|
||||
await knex.schema.dropTable('comment');
|
||||
await knex.schema.dropTable('action');
|
||||
await knex.schema.dropTable('notification');
|
||||
await knex.schema.dropTable('notification_service');
|
||||
|
||||
return knex.raw(`
|
||||
DROP EXTENSION pg_trgm;
|
||||
|
||||
DROP SEQUENCE next_id_seq;
|
||||
DROP FUNCTION next_id(OUT id BIGINT);
|
||||
`);
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
/*!
|
||||
* 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.alterTable('action', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.bigInteger('board_id');
|
||||
|
||||
/* Indexes */
|
||||
|
||||
table.index('board_id');
|
||||
});
|
||||
|
||||
return knex.raw(`
|
||||
UPDATE action
|
||||
SET
|
||||
board_id = card.board_id,
|
||||
data = data || jsonb_build_object('card', jsonb_build_object('name', card.name))
|
||||
FROM card
|
||||
WHERE action.card_id = card.id;
|
||||
`);
|
||||
};
|
||||
|
||||
exports.down = (knex) =>
|
||||
knex.schema.table('action', (table) => {
|
||||
table.dropColumn('board_id');
|
||||
});
|
||||
@@ -0,0 +1,32 @@
|
||||
/*!
|
||||
* 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.alterTable('card', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.integer('comments_total').notNullable().defaultTo(0);
|
||||
});
|
||||
|
||||
await knex.raw(`
|
||||
UPDATE card
|
||||
SET comments_total = comments_total_by_card_id.comments_total
|
||||
FROM (
|
||||
SELECT card_id, COUNT(*) as comments_total
|
||||
FROM comment
|
||||
GROUP BY card_id
|
||||
) AS comments_total_by_card_id
|
||||
WHERE card.id = comments_total_by_card_id.card_id;
|
||||
`);
|
||||
|
||||
return knex.schema.alterTable('card', (table) => {
|
||||
table.integer('comments_total').notNullable().alter();
|
||||
});
|
||||
};
|
||||
|
||||
exports.down = (knex) =>
|
||||
knex.schema.table('card', (table) => {
|
||||
table.dropColumn('comments_total');
|
||||
});
|
||||
@@ -0,0 +1,26 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
exports.up = (knex) =>
|
||||
knex.raw(`
|
||||
UPDATE user_account
|
||||
SET language =
|
||||
CASE
|
||||
WHEN language = 'sr-Cyrl-CS' THEN 'sr-Cyrl-RS'
|
||||
WHEN language = 'sr-Latn-CS' THEN 'sr-Latn-RS'
|
||||
END
|
||||
WHERE language IN ('sr-Cyrl-CS', 'sr-Latn-CS');
|
||||
`);
|
||||
|
||||
exports.down = (knex) =>
|
||||
knex.raw(`
|
||||
UPDATE user_account
|
||||
SET language =
|
||||
CASE
|
||||
WHEN language = 'sr-Cyrl-RS' THEN 'sr-Cyrl-CS'
|
||||
WHEN language = 'sr-Latn-RS' THEN 'sr-Latn-CS'
|
||||
END
|
||||
WHERE language IN ('sr-Cyrl-RS', 'sr-Latn-RS');
|
||||
`);
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
exports.up = (knex) =>
|
||||
knex.schema.createTable('webhook', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.bigInteger('id').primary().defaultTo(knex.raw('next_id()'));
|
||||
|
||||
table.bigInteger('board_id');
|
||||
|
||||
table.text('name').notNullable();
|
||||
table.text('url').notNullable();
|
||||
table.text('access_token');
|
||||
table.specificType('events', 'text[]');
|
||||
table.specificType('excluded_events', 'text[]');
|
||||
|
||||
table.timestamp('created_at', true);
|
||||
table.timestamp('updated_at', true);
|
||||
|
||||
/* Indexes */
|
||||
|
||||
table.index('board_id');
|
||||
});
|
||||
|
||||
exports.down = (knex) => knex.schema.dropTable('webhook');
|
||||
@@ -0,0 +1,28 @@
|
||||
/*!
|
||||
* 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.alterTable('card', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.boolean('is_closed').notNullable().default(false);
|
||||
});
|
||||
|
||||
await knex.raw(`
|
||||
UPDATE card
|
||||
SET is_closed = TRUE
|
||||
FROM list
|
||||
WHERE card.list_id = list.id AND list.type = 'closed';
|
||||
`);
|
||||
|
||||
return knex.schema.alterTable('card', (table) => {
|
||||
table.boolean('is_closed').notNullable().alter();
|
||||
});
|
||||
};
|
||||
|
||||
exports.down = (knex) =>
|
||||
knex.schema.table('card', (table) => {
|
||||
table.dropColumn('is_closed');
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
/*!
|
||||
* 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) =>
|
||||
knex.schema.alterTable('task', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.bigInteger('linked_card_id');
|
||||
|
||||
/* Indexes */
|
||||
|
||||
table.index('linked_card_id');
|
||||
});
|
||||
|
||||
exports.down = (knex) =>
|
||||
knex.schema.table('task', (table) => {
|
||||
table.dropColumn('linked_card_id');
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
/*!
|
||||
* 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.alterTable('task_list', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.boolean('hide_completed_tasks').notNullable().defaultTo(false);
|
||||
});
|
||||
|
||||
return knex.schema.alterTable('task_list', (table) => {
|
||||
table.boolean('hide_completed_tasks').notNullable().alter();
|
||||
});
|
||||
};
|
||||
|
||||
exports.down = (knex) =>
|
||||
knex.schema.table('task_list', (table) => {
|
||||
table.dropColumn('hide_completed_tasks');
|
||||
});
|
||||
@@ -0,0 +1,70 @@
|
||||
/*!
|
||||
* 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('config', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.bigInteger('id').primary().defaultTo(knex.raw('next_id()'));
|
||||
|
||||
table.boolean('is_initialized').notNullable();
|
||||
|
||||
table.timestamp('created_at', true);
|
||||
table.timestamp('updated_at', true);
|
||||
});
|
||||
|
||||
await knex.schema.alterTable('session', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.text('pending_token');
|
||||
|
||||
/* Modifications */
|
||||
|
||||
table.setNullable('access_token');
|
||||
|
||||
/* Indexes */
|
||||
|
||||
table.unique('pending_token');
|
||||
});
|
||||
|
||||
await knex.schema.alterTable('user_account', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.text('terms_signature');
|
||||
|
||||
table.timestamp('terms_accepted_at', true);
|
||||
});
|
||||
|
||||
const isInitialized = !!(await knex('user_account').first());
|
||||
|
||||
await knex('config').insert({
|
||||
isInitialized,
|
||||
id: 1,
|
||||
createdAt: new Date().toISOString(),
|
||||
});
|
||||
|
||||
await knex('session')
|
||||
.update({
|
||||
deletedAt: new Date().toISOString(),
|
||||
})
|
||||
.whereNull('deletedAt');
|
||||
};
|
||||
|
||||
exports.down = async (knex) => {
|
||||
await knex.schema.dropTable('config');
|
||||
|
||||
await knex('session').del().whereNull('access_token');
|
||||
|
||||
await knex.schema.alterTable('session', (table) => {
|
||||
table.dropColumn('pending_token');
|
||||
|
||||
table.dropNullable('access_token');
|
||||
});
|
||||
|
||||
return knex.schema.table('user_account', (table) => {
|
||||
table.dropColumn('terms_signature');
|
||||
table.dropColumn('terms_accepted_at');
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,161 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
const mime = require('mime-types');
|
||||
|
||||
exports.up = async (knex) => {
|
||||
await knex.schema.createTable('storage_usage', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.bigInteger('id').primary().defaultTo(knex.raw('next_id()'));
|
||||
|
||||
table.bigInteger('total').notNullable();
|
||||
table.bigInteger('user_avatars').notNullable();
|
||||
table.bigInteger('background_images').notNullable();
|
||||
table.bigInteger('attachments').notNullable();
|
||||
|
||||
table.timestamp('created_at', true);
|
||||
table.timestamp('updated_at', true);
|
||||
});
|
||||
|
||||
await knex.schema.alterTable('file_reference', (table) => {
|
||||
table.dropPrimary();
|
||||
table.dropIndex('total');
|
||||
});
|
||||
|
||||
await knex.schema.renameTable('file_reference', 'uploaded_file');
|
||||
|
||||
await knex.schema.alterTable('uploaded_file', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.text('type').notNullable().defaultTo('attachment');
|
||||
table.text('mime_type');
|
||||
table.bigInteger('size').notNullable().defaultTo(0);
|
||||
|
||||
/* Modifications */
|
||||
|
||||
table.text('id').primary().defaultTo(knex.raw('next_id()')).alter();
|
||||
table.renameColumn('total', 'references_total');
|
||||
|
||||
/* Indexes */
|
||||
|
||||
table.index('type');
|
||||
table.index('references_total');
|
||||
});
|
||||
|
||||
await knex.schema.alterTable('uploaded_file', (table) => {
|
||||
table.text('type').notNullable().alter();
|
||||
table.bigInteger('size').notNullable().alter();
|
||||
});
|
||||
|
||||
await knex.raw(`
|
||||
UPDATE user_account
|
||||
SET avatar = avatar - 'dirname' - 'sizeInBytes' || jsonb_build_object('uploadedFileId', avatar->'dirname', 'size', avatar->'sizeInBytes')
|
||||
WHERE avatar IS NOT NULL;
|
||||
`);
|
||||
|
||||
await knex.schema.alterTable('background_image', (table) => {
|
||||
table.renameColumn('dirname', 'uploaded_file_id');
|
||||
table.renameColumn('size_in_bytes', 'size');
|
||||
});
|
||||
|
||||
await knex.raw(`
|
||||
UPDATE attachment
|
||||
SET data = data - 'fileReferenceId' - 'sizeInBytes' || jsonb_build_object('uploadedFileId', data->'fileReferenceId', 'size', data->'sizeInBytes')
|
||||
WHERE type = 'file';
|
||||
`);
|
||||
|
||||
await knex.raw(`
|
||||
UPDATE uploaded_file
|
||||
SET
|
||||
type = 'attachment',
|
||||
mime_type = attachment.data->>'mimeType',
|
||||
size = (attachment.data->>'size')::bigint
|
||||
FROM attachment
|
||||
WHERE (attachment.data->>'uploadedFileId')::text = uploaded_file.id AND attachment.type = 'file';
|
||||
`);
|
||||
|
||||
const users = await knex('user_account').whereNotNull('avatar');
|
||||
const createdAt = new Date().toISOString();
|
||||
|
||||
await knex.batchInsert(
|
||||
'uploaded_file',
|
||||
users.map(({ avatar }) => ({
|
||||
createdAt,
|
||||
id: avatar.uploadedFileId,
|
||||
type: 'userAvatar',
|
||||
referencesTotal: 1,
|
||||
mimeType: mime.lookup(avatar.extension) || null,
|
||||
size: avatar.size,
|
||||
})),
|
||||
);
|
||||
|
||||
const backgroundImages = await knex('background_image');
|
||||
|
||||
await knex.batchInsert(
|
||||
'uploaded_file',
|
||||
backgroundImages.map((backgroundImage) => ({
|
||||
id: backgroundImage.uploaded_file_id,
|
||||
type: 'backgroundImage',
|
||||
referencesTotal: 1,
|
||||
mimeType: mime.lookup(backgroundImage.extension) || null,
|
||||
size: backgroundImage.size,
|
||||
createdAt: backgroundImage.created_at,
|
||||
})),
|
||||
);
|
||||
|
||||
return knex.raw(`
|
||||
INSERT INTO storage_usage (id, total, user_avatars, background_images, attachments, created_at)
|
||||
SELECT
|
||||
1 AS id,
|
||||
COALESCE(SUM(size), 0) AS total,
|
||||
COALESCE(SUM(CASE WHEN type = 'userAvatar' THEN size ELSE 0 END), 0) AS user_avatars,
|
||||
COALESCE(SUM(CASE WHEN type = 'backgroundImage' THEN size ELSE 0 END), 0) AS background_images,
|
||||
COALESCE(SUM(CASE WHEN type = 'attachment' THEN size ELSE 0 END), 0) AS attachments,
|
||||
timezone('UTC', now()) AS created_at
|
||||
FROM uploaded_file;
|
||||
`);
|
||||
};
|
||||
|
||||
exports.down = async (knex) => {
|
||||
await knex.schema.dropTable('storage_usage');
|
||||
|
||||
await knex('uploaded_file').delete().whereNot('type', 'attachment');
|
||||
|
||||
await knex.schema.alterTable('uploaded_file', (table) => {
|
||||
table.dropPrimary();
|
||||
table.dropIndex('references_total');
|
||||
});
|
||||
|
||||
await knex.schema.renameTable('uploaded_file', 'file_reference');
|
||||
|
||||
await knex.schema.alterTable('file_reference', (table) => {
|
||||
table.dropColumn('type');
|
||||
table.dropColumn('mime_type');
|
||||
table.dropColumn('size');
|
||||
|
||||
table.bigInteger('id').primary().defaultTo(knex.raw('next_id()')).alter();
|
||||
table.renameColumn('references_total', 'total');
|
||||
|
||||
table.index('total');
|
||||
});
|
||||
|
||||
await knex.raw(`
|
||||
UPDATE user_account
|
||||
SET avatar = avatar - 'uploadedFileId' - 'size' || jsonb_build_object('dirname', avatar->'uploadedFileId', 'sizeInBytes', avatar->'size')
|
||||
WHERE avatar IS NOT NULL;
|
||||
`);
|
||||
|
||||
await knex.schema.alterTable('background_image', (table) => {
|
||||
table.renameColumn('uploaded_file_id', 'dirname');
|
||||
table.renameColumn('size', 'size_in_bytes');
|
||||
});
|
||||
|
||||
return knex.raw(`
|
||||
UPDATE attachment
|
||||
SET data = data - 'uploadedFileId' - 'size' || jsonb_build_object('fileReferenceId', data->'uploadedFileId', 'sizeInBytes', data->'size')
|
||||
WHERE type = 'file';
|
||||
`);
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
/*!
|
||||
* 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.alterTable('card', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.boolean('is_due_completed');
|
||||
});
|
||||
|
||||
return knex('card')
|
||||
.update({
|
||||
isDueCompleted: false,
|
||||
})
|
||||
.whereNotNull('due_date');
|
||||
};
|
||||
|
||||
exports.down = (knex) =>
|
||||
knex.schema.table('card', (table) => {
|
||||
table.dropColumn('is_due_completed');
|
||||
});
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/*!
|
||||
* 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.alterTable('board', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.boolean('expand_task_lists_by_default').notNullable().defaultTo(false);
|
||||
});
|
||||
|
||||
return knex.schema.alterTable('board', (table) => {
|
||||
table.boolean('expand_task_lists_by_default').notNullable().alter();
|
||||
});
|
||||
};
|
||||
|
||||
exports.down = (knex) =>
|
||||
knex.schema.table('board', (table) => {
|
||||
table.dropColumn('expand_task_lists_by_default');
|
||||
});
|
||||
@@ -0,0 +1,36 @@
|
||||
/*!
|
||||
* 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.alterTable('config', (table) => {
|
||||
/* Columns */
|
||||
|
||||
table.text('smtp_host');
|
||||
table.integer('smtp_port');
|
||||
table.text('smtp_name');
|
||||
table.boolean('smtp_secure').notNullable().defaultTo(false);
|
||||
table.boolean('smtp_tls_reject_unauthorized').notNullable().defaultTo(true);
|
||||
table.text('smtp_user');
|
||||
table.text('smtp_password');
|
||||
table.text('smtp_from');
|
||||
});
|
||||
|
||||
return knex.schema.alterTable('config', (table) => {
|
||||
table.boolean('smtp_secure').notNullable().alter();
|
||||
table.boolean('smtp_tls_reject_unauthorized').notNullable().alter();
|
||||
});
|
||||
};
|
||||
|
||||
exports.down = (knex) =>
|
||||
knex.schema.alterTable('config', (table) => {
|
||||
table.dropColumn('smtp_host');
|
||||
table.dropColumn('smtp_port');
|
||||
table.dropColumn('smtp_name');
|
||||
table.dropColumn('smtp_secure');
|
||||
table.dropColumn('smtp_tls_reject_unauthorized');
|
||||
table.dropColumn('smtp_user');
|
||||
table.dropColumn('smtp_password');
|
||||
table.dropColumn('smtp_from');
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
exports.up = (knex) =>
|
||||
knex.schema.alterTable('user_account', (table) => {
|
||||
table.text('api_key_prefix');
|
||||
table.text('api_key_hash');
|
||||
|
||||
table.timestamp('api_key_created_at', true);
|
||||
|
||||
/* Indexes */
|
||||
|
||||
table.unique('api_key_hash');
|
||||
});
|
||||
|
||||
exports.down = (knex) =>
|
||||
knex.schema.alterTable('user_account', (table) => {
|
||||
table.dropColumn('api_key_prefix');
|
||||
table.dropColumn('api_key_hash');
|
||||
table.dropColumn('api_key_created_at');
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
module.exports.up = (knex) =>
|
||||
knex.schema.raw(`
|
||||
ALTER INDEX card_name_index RENAME TO card_name_gin_index;
|
||||
ALTER INDEX card_description_index RENAME TO card_description_gin_index;
|
||||
`);
|
||||
|
||||
module.exports.down = (knex) =>
|
||||
knex.schema.raw(`
|
||||
ALTER INDEX card_name_gin_index RENAME TO card_name_index;
|
||||
ALTER INDEX card_description_gin_index RENAME TO card_description_index;
|
||||
`);
|
||||
@@ -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');
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
/*!
|
||||
* Copyright (c) 2024 PLANKA Software GmbH
|
||||
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
|
||||
*/
|
||||
|
||||
module.exports.up = async (knex) => {
|
||||
await knex.schema.alterTable('board', (table) => {
|
||||
table.boolean('display_card_ages').notNullable().defaultTo(false);
|
||||
});
|
||||
|
||||
return knex.schema.alterTable('board', (table) => {
|
||||
table.boolean('display_card_ages').notNullable().alter();
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.down = (knex) =>
|
||||
knex.schema.alterTable('board', (table) => {
|
||||
table.dropColumn('display_card_ages');
|
||||
});
|
||||
Reference in New Issue
Block a user