From 61f10310dc286da9a44bcbffc09c1907fa6c4c4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Espino=20Garc=C3=ADa?= Date: Tue, 10 Feb 2026 15:04:35 +0100 Subject: [PATCH] Database changes for autotranslations (#9495) --- app/database/migration/server/index.ts | 17 +++++++++++++++++ app/database/models/server/channel.ts | 4 ++++ app/database/models/server/my_channel.ts | 3 +++ .../transformers/channel.ts | 2 ++ app/database/schema/server/index.ts | 2 +- .../schema/server/table_schemas/channel.ts | 1 + .../schema/server/table_schemas/my_channel.ts | 1 + app/database/schema/server/test.ts | 6 +++++- docs/database/server/server-v5.sql | 6 ++++-- docs/database/server/server.md | 4 +++- test/test_helper.ts | 2 ++ types/api/channels.d.ts | 3 +++ types/api/config.d.ts | 2 ++ types/api/posts.d.ts | 10 ++++++++++ types/database/models/servers/channel.ts | 3 +++ types/database/models/servers/my_channel.ts | 3 +++ 16 files changed, 64 insertions(+), 5 deletions(-) diff --git a/app/database/migration/server/index.ts b/app/database/migration/server/index.ts index a1d225b2b..e4744307a 100644 --- a/app/database/migration/server/index.ts +++ b/app/database/migration/server/index.ts @@ -25,6 +25,23 @@ const { const {PLAYBOOK_RUN, PLAYBOOK_CHECKLIST, PLAYBOOK_CHECKLIST_ITEM, PLAYBOOK_RUN_ATTRIBUTE, PLAYBOOK_RUN_ATTRIBUTE_VALUE} = PLAYBOOK_TABLES; export default schemaMigrations({migrations: [ + { + toVersion: 18, + steps: [ + addColumns({ + table: MY_CHANNEL, + columns: [ + {name: 'autotranslation_disabled', type: 'boolean'}, + ], + }), + addColumns({ + table: CHANNEL, + columns: [ + {name: 'autotranslation', type: 'boolean'}, + ], + }), + ], + }, { toVersion: 17, steps: [ diff --git a/app/database/models/server/channel.ts b/app/database/models/server/channel.ts index b0d5427c7..33b22c495 100644 --- a/app/database/models/server/channel.ts +++ b/app/database/models/server/channel.ts @@ -155,6 +155,9 @@ export default class ChannelModel extends Model implements ChannelModelInterface /** categoryChannel : Query returning the membership data for the current user if it belongs to this channel */ @immutableRelation(CATEGORY_CHANNEL, 'channel_id') categoryChannel!: Relation; + /** autotranslation : Whether the channel has automatic translation enabled */ + @field('autotranslation') autotranslation!: boolean; + toApi = (): Channel => { return { id: this.id, @@ -176,6 +179,7 @@ export default class ChannelModel extends Model implements ChannelModelInterface shared: this.shared, banner_info: this.bannerInfo, policy_enforced: this.abacPolicyEnforced, + autotranslation: this.autotranslation, }; }; } diff --git a/app/database/models/server/my_channel.ts b/app/database/models/server/my_channel.ts index 61726ed23..92fe604b5 100644 --- a/app/database/models/server/my_channel.ts +++ b/app/database/models/server/my_channel.ts @@ -56,6 +56,9 @@ export default class MyChannelModel extends Model implements MyChannelModelInter /** last_playbook_runs_fetch_at : The timestamp of the last successful fetch of playbook runs for this channel, used as the "since" parameter for incremental updates */ @field('last_playbook_runs_fetch_at') lastPlaybookRunsFetchAt!: number; + /* autotranslation: Determines if the channel has automatic translation enabled for this user*/ + @field('autotranslation_disabled') autotranslationDisabled!: boolean; + /** channel : The relation pointing to the CHANNEL table */ @immutableRelation(CHANNEL, 'id') channel!: Relation; diff --git a/app/database/operator/server_data_operator/transformers/channel.ts b/app/database/operator/server_data_operator/transformers/channel.ts index aacda460a..b6a55c151 100644 --- a/app/database/operator/server_data_operator/transformers/channel.ts +++ b/app/database/operator/server_data_operator/transformers/channel.ts @@ -53,6 +53,7 @@ export const transformChannelRecord = ({action, database, value}: TransformerArg channel.type = raw.type; channel.bannerInfo = raw.banner_info; channel.abacPolicyEnforced = Boolean(raw.policy_enforced); + channel.autotranslation = Boolean(raw.autotranslation); }; return prepareBaseRecord({ @@ -156,6 +157,7 @@ export const transformMyChannelRecord = async ({action, database, value}: Transf myChannel.viewedAt = record?.viewedAt || 0; myChannel.lastFetchedAt = record?.lastFetchedAt || 0; myChannel.lastPlaybookRunsFetchAt = record?.lastPlaybookRunsFetchAt || 0; + myChannel.autotranslationDisabled = Boolean(raw.autotranslation_disabled); }; return prepareBaseRecord({ diff --git a/app/database/schema/server/index.ts b/app/database/schema/server/index.ts index c4f8d12eb..54469220a 100644 --- a/app/database/schema/server/index.ts +++ b/app/database/schema/server/index.ts @@ -45,7 +45,7 @@ import { } from './table_schemas'; export const serverSchema: AppSchema = appSchema({ - version: 17, + version: 18, tables: [ CategorySchema, CategoryChannelSchema, diff --git a/app/database/schema/server/table_schemas/channel.ts b/app/database/schema/server/table_schemas/channel.ts index 3ee49976a..726a98ebb 100644 --- a/app/database/schema/server/table_schemas/channel.ts +++ b/app/database/schema/server/table_schemas/channel.ts @@ -22,5 +22,6 @@ export default tableSchema({ {name: 'update_at', type: 'number'}, {name: 'banner_info', type: 'string', isOptional: true}, {name: 'abac_policy_enforced', type: 'boolean', isOptional: true}, + {name: 'autotranslation', type: 'boolean', isOptional: true}, ], }); diff --git a/app/database/schema/server/table_schemas/my_channel.ts b/app/database/schema/server/table_schemas/my_channel.ts index 957ffefba..5c9046fbb 100644 --- a/app/database/schema/server/table_schemas/my_channel.ts +++ b/app/database/schema/server/table_schemas/my_channel.ts @@ -20,6 +20,7 @@ export default tableSchema({ {name: 'viewed_at', type: 'number'}, {name: 'last_fetched_at', type: 'number', isIndexed: true}, {name: 'last_playbook_runs_fetch_at', type: 'number'}, + {name: 'autotranslation_disabled', type: 'boolean', isOptional: true}, ], }); diff --git a/app/database/schema/server/test.ts b/app/database/schema/server/test.ts index 37e2bf191..a95deeec8 100644 --- a/app/database/schema/server/test.ts +++ b/app/database/schema/server/test.ts @@ -52,7 +52,7 @@ const {PLAYBOOK_RUN, PLAYBOOK_CHECKLIST, PLAYBOOK_CHECKLIST_ITEM, PLAYBOOK_RUN_A describe('*** Test schema for SERVER database ***', () => { it('=> The SERVER SCHEMA should strictly match', () => { expect(serverSchema).toEqual({ - version: 17, + version: 18, unsafeSql: undefined, tables: { [CATEGORY]: { @@ -130,6 +130,7 @@ describe('*** Test schema for SERVER database ***', () => { update_at: {name: 'update_at', type: 'number'}, banner_info: {name: 'banner_info', type: 'string', isOptional: true}, abac_policy_enforced: {name: 'abac_policy_enforced', type: 'boolean', isOptional: true}, + autotranslation: {name: 'autotranslation', type: 'boolean', isOptional: true}, }, columnArray: [ {name: 'create_at', type: 'number'}, @@ -144,6 +145,7 @@ describe('*** Test schema for SERVER database ***', () => { {name: 'update_at', type: 'number'}, {name: 'banner_info', type: 'string', isOptional: true}, {name: 'abac_policy_enforced', type: 'boolean', isOptional: true}, + {name: 'autotranslation', type: 'boolean', isOptional: true}, ], }, [CHANNEL_BOOKMARK]: { @@ -269,6 +271,7 @@ describe('*** Test schema for SERVER database ***', () => { viewed_at: {name: 'viewed_at', type: 'number'}, last_fetched_at: {name: 'last_fetched_at', type: 'number', isIndexed: true}, last_playbook_runs_fetch_at: {name: 'last_playbook_runs_fetch_at', type: 'number'}, + autotranslation_disabled: {name: 'autotranslation_disabled', type: 'boolean', isOptional: true}, }, columnArray: [ {name: 'is_unread', type: 'boolean'}, @@ -281,6 +284,7 @@ describe('*** Test schema for SERVER database ***', () => { {name: 'viewed_at', type: 'number'}, {name: 'last_fetched_at', type: 'number', isIndexed: true}, {name: 'last_playbook_runs_fetch_at', type: 'number'}, + {name: 'autotranslation_disabled', type: 'boolean', isOptional: true}, ], }, [MY_CHANNEL_SETTINGS]: { diff --git a/docs/database/server/server-v5.sql b/docs/database/server/server-v5.sql index 87994901b..31de8fa2f 100644 --- a/docs/database/server/server-v5.sql +++ b/docs/database/server/server-v5.sql @@ -1,8 +1,8 @@ --- Exported from QuickDBD: https://www.quickdatabasediagrams.com/ +-- Exported from QuickDBD: https://www.quickdatabasediagrams.com/ -- Link to schema: https://app.quickdatabasediagrams.com/#/d/7cAwTM -- NOTE! If you have used non-SQL datatypes in your design, you will have to change these here. --- Server Database - Schema Version 2 +-- Server Database - Schema Version 18 -- Please bump the version by 1, any time the schema changes. -- Also, include the migration plan under app/database/migration/server, -- update all models, relationships and types. @@ -56,6 +56,7 @@ CREATE TABLE [Channel] ( [update_at] number NOT NULL , [banner_info] string, [abac_policy_enforced] boolean NOT NULL , + [autotranslation] boolean NULL , CONSTRAINT [PK_Channel] PRIMARY KEY CLUSTERED ( [id] ASC ) @@ -193,6 +194,7 @@ CREATE TABLE [MyChannel] ( [message_count] number NOT NULL , [roles] string NOT NULL , [viewed_at] number NOT NULL , + [autotranslation_disabled] boolean NULL , CONSTRAINT [PK_MyChannel] PRIMARY KEY CLUSTERED ( [id] ASC ) diff --git a/docs/database/server/server.md b/docs/database/server/server.md index 33a8a32be..ef089da01 100644 --- a/docs/database/server/server.md +++ b/docs/database/server/server.md @@ -1,4 +1,4 @@ -# Server Database - Schema Version 14 +# Server Database - Schema Version 18 # Please bump the version by 1, any time the schema changes. # Also, include the migration plan under app/database/migration/server, # update all models, relationships and types. @@ -41,6 +41,7 @@ type string update_at number banner_info string abac_policy_enforced boolean +autotranslation boolean ChannelInfo @@ -178,6 +179,7 @@ mentions_count number message_count number roles string viewed_at number +autotranslation_disabled boolean MyChannelSettings diff --git a/test/test_helper.ts b/test/test_helper.ts index d0fe838af..4b0f6b1e0 100644 --- a/test/test_helper.ts +++ b/test/test_helper.ts @@ -607,6 +607,7 @@ class TestHelperSingleton { shared: false, teamId: this.generateId(), type: 'O' as const, + autotranslation: false, members: this.fakeQuery([]), drafts: this.fakeQuery([]), bookmarks: this.fakeQuery([]), @@ -852,6 +853,7 @@ class TestHelperSingleton { roles: '', viewedAt: 0, lastPlaybookRunsFetchAt: 0, + autotranslationDisabled: false, channel: this.fakeRelation(), settings: this.fakeRelation(), resetPreparedState: jest.fn(), diff --git a/types/api/channels.d.ts b/types/api/channels.d.ts index 974793459..d8e711759 100644 --- a/types/api/channels.d.ts +++ b/types/api/channels.d.ts @@ -45,6 +45,7 @@ type Channel = { group_constrained: boolean|null; shared: boolean; banner_info?: ChannelBannerInfo; + autotranslation?: boolean; /** Whether the channel has Attribute-Based Access Control (ABAC) policy enforcement enabled, controlling access based on user attributes */ policy_enforced?: boolean; @@ -55,6 +56,7 @@ type ChannelPatch = { header?: string; purpose?: string; group_constrained?: boolean|null; + autotranslation?: boolean; }; type ChannelWithTeamData = Channel & { team_display_name: string; @@ -86,6 +88,7 @@ type ChannelMembership = { post_root_id?: string; is_unread?: boolean; manually_unread?: boolean; + autotranslation_disabled?: boolean; }; type ChannelUnread = { channel_id: string; diff --git a/types/api/config.d.ts b/types/api/config.d.ts index ea7a7e1bc..a2fddb958 100644 --- a/types/api/config.d.ts +++ b/types/api/config.d.ts @@ -13,6 +13,7 @@ interface ClientConfig { AndroidMinVersion: string; AppDownloadLink: string; AsymmetricSigningPublicKey: string; + AutoTranslationLanguages: string; AvailableLocales: string; BannerColor: string; BannerText: string; @@ -47,6 +48,7 @@ interface ClientConfig { EmailLoginButtonTextColor: string; EmailNotificationContentsType: string; EnableBanner: string; + EnableAutoTranslation: string; EnableBotAccountCreation: string; EnableBurnOnRead: string; EnableChannelViewedMessages: string; diff --git a/types/api/posts.d.ts b/types/api/posts.d.ts index b949c2841..7a25057cc 100644 --- a/types/api/posts.d.ts +++ b/types/api/posts.d.ts @@ -71,6 +71,15 @@ type PostImage = { frame_count?: number; }; +type PostTranslationState = 'ready' | 'skipped' | 'unavailable' | 'processing'; +type PostTranslation = { + object: { + message: string; + }; + state: PostTranslationState; + source_lang?: string; +}; + type PostMetadata = { acknowledgements?: PostAcknowledgement[]; embeds?: PostEmbed[]; @@ -82,6 +91,7 @@ type PostMetadata = { expire_at?: number; borConfig?: PostBoRConfig; recipients?: string[]; + translations?: Record; }; type Post = { diff --git a/types/database/models/servers/channel.ts b/types/database/models/servers/channel.ts index 8768952cc..a6042d148 100644 --- a/types/database/models/servers/channel.ts +++ b/types/database/models/servers/channel.ts @@ -60,6 +60,9 @@ declare class ChannelModel extends Model { /** Whether the channel has Attribute-Based Access Control (ABAC) policy enforcement enabled, controlling access based on user attributes */ abacPolicyEnforced?: boolean; + /** autotranslation : Whether the channel has automatic translation enabled */ + autotranslation: boolean; + /** members : Users belonging to this channel */ members: Query; diff --git a/types/database/models/servers/my_channel.ts b/types/database/models/servers/my_channel.ts index 6ceccd2fc..685329f68 100644 --- a/types/database/models/servers/my_channel.ts +++ b/types/database/models/servers/my_channel.ts @@ -42,6 +42,9 @@ declare class MyChannelModel extends Model { /** last_playbook_runs_fetch_at : The timestamp of the last successful fetch of playbook runs for this channel, used as the "since" parameter for incremental updates */ lastPlaybookRunsFetchAt: number; + /** autotranslation : Whether the channel has automatic translation enabled for this user */ + autotranslationDisabled: boolean; + /** channel : The relation pointing to the CHANNEL table */ channel: Relation;