refactor (backend): convert jsonb to array
This commit is contained in:
parent
3d28acb2c9
commit
a4779f233b
21 changed files with 394 additions and 143 deletions
|
@ -1,6 +1,9 @@
|
|||
BEGIN;
|
||||
|
||||
DELETE FROM "migrations" WHERE name IN (
|
||||
'UserprofileJsonbToArray1714270605574',
|
||||
'DropUnusedUserprofileColumns1714259023878',
|
||||
'AntennaJsonbToArray1714192520471',
|
||||
'AddUserProfileLanguage1714888400293',
|
||||
'DropUnusedIndexes1714643926317',
|
||||
'AlterAkaType1714099399879',
|
||||
|
@ -27,6 +30,45 @@ DELETE FROM "migrations" WHERE name IN (
|
|||
'RemoveNativeUtilsMigration1705877093218'
|
||||
);
|
||||
|
||||
-- userprofile-jsonb-to-array
|
||||
ALTER TABLE "user_profile" RENAME COLUMN "mutedInstances" TO "mutedInstances_old";
|
||||
ALTER TABLE "user_profile" ADD COLUMN "mutedInstances" jsonb NOT NULL DEFAULT '[]';
|
||||
UPDATE "user_profile" SET "mutedInstances" = to_jsonb("mutedInstances_old");
|
||||
ALTER TABLE "user_profile" DROP COLUMN "mutedInstances_old";
|
||||
ALTER TABLE "user_profile" RENAME COLUMN "mutedWords" TO "mutedWords_old";
|
||||
ALTER TABLE "user_profile" ADD COLUMN "mutedWords" jsonb NOT NULL DEFAULT '[]';
|
||||
CREATE TEMP TABLE "BCrsGgLCUeMMLARy" ("userId" character varying(32), "kws" jsonb NOT NULL DEFAULT '[]');
|
||||
INSERT INTO "BCrsGgLCUeMMLARy" ("userId", "kws") SELECT "userId", jsonb_agg("X"."w") FROM (SELECT "userId", to_jsonb(string_to_array(unnest("mutedWords_old"), ' ')) AS "w" FROM "user_profile") AS "X" GROUP BY "userId";
|
||||
UPDATE "user_profile" SET "mutedWords" = "kws" FROM "BCrsGgLCUeMMLARy" WHERE "user_profile"."userId" = "BCrsGgLCUeMMLARy"."userId";
|
||||
ALTER TABLE "user_profile" DROP COLUMN "mutedWords_old";
|
||||
|
||||
-- drop-unused-userprofile-columns
|
||||
ALTER TABLE "user_profile" ADD "room" jsonb NOT NULL DEFAULT '{}';
|
||||
COMMENT ON COLUMN "user_profile"."room" IS 'The room data of the User.';
|
||||
ALTER TABLE "user_profile" ADD "clientData" jsonb NOT NULL DEFAULT '{}';
|
||||
COMMENT ON COLUMN "user_profile"."clientData" IS 'The client-specific data of the User.';
|
||||
|
||||
-- antenna-jsonb-to-array
|
||||
UPDATE "antenna" SET "instances" = '{""}' WHERE "instances" = '{}';
|
||||
ALTER TABLE "antenna" RENAME COLUMN "instances" TO "instances_old";
|
||||
ALTER TABLE "antenna" ADD COLUMN "instances" jsonb NOT NULL DEFAULT '[]';
|
||||
UPDATE "antenna" SET "instances" = to_jsonb("instances_old");
|
||||
ALTER TABLE "antenna" DROP COLUMN "instances_old";
|
||||
UPDATE "antenna" SET "keywords" = '{""}' WHERE "keywords" = '{}';
|
||||
ALTER TABLE "antenna" RENAME COLUMN "keywords" TO "keywords_old";
|
||||
ALTER TABLE "antenna" ADD COLUMN "keywords" jsonb NOT NULL DEFAULT '[]';
|
||||
CREATE TEMP TABLE "QvPNcMitBFkqqBgm" ("id" character varying(32), "kws" jsonb NOT NULL DEFAULT '[]');
|
||||
INSERT INTO "QvPNcMitBFkqqBgm" ("id", "kws") SELECT "id", jsonb_agg("X"."w") FROM (SELECT "id", to_jsonb(string_to_array(unnest("keywords_old"), ' ')) AS "w" FROM "antenna") AS "X" GROUP BY "id";
|
||||
UPDATE "antenna" SET "keywords" = "kws" FROM "QvPNcMitBFkqqBgm" WHERE "antenna"."id" = "QvPNcMitBFkqqBgm"."id";
|
||||
ALTER TABLE "antenna" DROP COLUMN "keywords_old";
|
||||
UPDATE "antenna" SET "excludeKeywords" = '{""}' WHERE "excludeKeywords" = '{}';
|
||||
ALTER TABLE "antenna" RENAME COLUMN "excludeKeywords" TO "excludeKeywords_old";
|
||||
ALTER TABLE "antenna" ADD COLUMN "excludeKeywords" jsonb NOT NULL DEFAULT '[]';
|
||||
CREATE TEMP TABLE "MZvVSjHzYcGXmGmz" ("id" character varying(32), "kws" jsonb NOT NULL DEFAULT '[]');
|
||||
INSERT INTO "MZvVSjHzYcGXmGmz" ("id", "kws") SELECT "id", jsonb_agg("X"."w") FROM (SELECT "id", to_jsonb(string_to_array(unnest("excludeKeywords_old"), ' ')) AS "w" FROM "antenna") AS "X" GROUP BY "id";
|
||||
UPDATE "antenna" SET "excludeKeywords" = "kws" FROM "MZvVSjHzYcGXmGmz" WHERE "antenna"."id" = "MZvVSjHzYcGXmGmz"."id";
|
||||
ALTER TABLE "antenna" DROP COLUMN "excludeKeywords_old";
|
||||
|
||||
-- drop-unused-indexes
|
||||
CREATE INDEX "IDX_01f4581f114e0ebd2bbb876f0b" ON "note_reaction" ("createdAt");
|
||||
CREATE INDEX "IDX_0610ebcfcfb4a18441a9bcdab2" ON "poll" ("userId");
|
||||
|
|
14
packages/backend-rs/index.d.ts
vendored
14
packages/backend-rs/index.d.ts
vendored
|
@ -244,7 +244,7 @@ export interface NoteLikeForCheckWordMute {
|
|||
renoteId: string | null
|
||||
replyId: string | null
|
||||
}
|
||||
export function checkWordMute(note: NoteLikeForCheckWordMute, mutedWordLists: Array<Array<string>>, mutedPatterns: Array<string>): Promise<boolean>
|
||||
export function checkWordMute(note: NoteLikeForCheckWordMute, mutedWords: Array<string>, mutedPatterns: Array<string>): Promise<boolean>
|
||||
export function getFullApAccount(username: string, host?: string | undefined | null): string
|
||||
export function isSelfHost(host?: string | undefined | null): boolean
|
||||
export function isSameOrigin(uri: string): boolean
|
||||
|
@ -381,7 +381,6 @@ export interface Antenna {
|
|||
name: string
|
||||
src: AntennaSrcEnum
|
||||
userListId: string | null
|
||||
keywords: Json
|
||||
withFile: boolean
|
||||
expression: string | null
|
||||
notify: boolean
|
||||
|
@ -389,8 +388,9 @@ export interface Antenna {
|
|||
withReplies: boolean
|
||||
userGroupJoiningId: string | null
|
||||
users: Array<string>
|
||||
excludeKeywords: Json
|
||||
instances: Json
|
||||
instances: Array<string>
|
||||
keywords: Array<string>
|
||||
excludeKeywords: Array<string>
|
||||
}
|
||||
export interface App {
|
||||
id: string
|
||||
|
@ -1128,7 +1128,6 @@ export interface UserProfile {
|
|||
twoFactorSecret: string | null
|
||||
twoFactorEnabled: boolean
|
||||
password: string | null
|
||||
clientData: Json
|
||||
autoAcceptFollowed: boolean
|
||||
alwaysMarkNsfw: boolean
|
||||
carefulBot: boolean
|
||||
|
@ -1136,21 +1135,20 @@ export interface UserProfile {
|
|||
securityKeysAvailable: boolean
|
||||
usePasswordLessLogin: boolean
|
||||
pinnedPageId: string | null
|
||||
room: Json
|
||||
injectFeaturedNote: boolean
|
||||
enableWordMute: boolean
|
||||
mutedWords: Json
|
||||
mutingNotificationTypes: Array<UserProfileMutingnotificationtypesEnum>
|
||||
noCrawle: boolean
|
||||
receiveAnnouncementEmail: boolean
|
||||
emailNotificationTypes: Json
|
||||
mutedInstances: Json
|
||||
publicReactions: boolean
|
||||
ffVisibility: UserProfileFfvisibilityEnum
|
||||
moderationNote: string
|
||||
preventAiLearning: boolean
|
||||
isIndexable: boolean
|
||||
mutedPatterns: Array<string>
|
||||
mutedInstances: Array<string>
|
||||
mutedWords: Array<string>
|
||||
lang: string | null
|
||||
}
|
||||
export interface UserPublickey {
|
||||
|
|
|
@ -87,14 +87,13 @@ fn convert_regex(js_regex: &str) -> String {
|
|||
|
||||
fn check_word_mute_impl(
|
||||
texts: &[String],
|
||||
muted_word_lists: &[Vec<String>],
|
||||
muted_words: &[String],
|
||||
muted_patterns: &[String],
|
||||
) -> bool {
|
||||
muted_word_lists.iter().any(|muted_word_list| {
|
||||
muted_words.iter().any(|item| {
|
||||
texts.iter().any(|text| {
|
||||
let text_lower = text.to_lowercase();
|
||||
muted_word_list
|
||||
.iter()
|
||||
item.split_whitespace()
|
||||
.all(|muted_word| text_lower.contains(&muted_word.to_lowercase()))
|
||||
})
|
||||
}) || muted_patterns.iter().any(|muted_pattern| {
|
||||
|
@ -107,16 +106,16 @@ fn check_word_mute_impl(
|
|||
#[crate::export]
|
||||
pub async fn check_word_mute(
|
||||
note: NoteLike,
|
||||
muted_word_lists: Vec<Vec<String>>,
|
||||
muted_patterns: Vec<String>,
|
||||
muted_words: &[String],
|
||||
muted_patterns: &[String],
|
||||
) -> Result<bool, DbErr> {
|
||||
if muted_word_lists.is_empty() && muted_patterns.is_empty() {
|
||||
if muted_words.is_empty() && muted_patterns.is_empty() {
|
||||
Ok(false)
|
||||
} else {
|
||||
Ok(check_word_mute_impl(
|
||||
&all_texts(note).await?,
|
||||
&muted_word_lists,
|
||||
&muted_patterns,
|
||||
muted_words,
|
||||
muted_patterns,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,8 +21,6 @@ pub struct Model {
|
|||
pub src: AntennaSrcEnum,
|
||||
#[sea_orm(column_name = "userListId")]
|
||||
pub user_list_id: Option<String>,
|
||||
#[sea_orm(column_type = "JsonBinary")]
|
||||
pub keywords: Json,
|
||||
#[sea_orm(column_name = "withFile")]
|
||||
pub with_file: bool,
|
||||
pub expression: Option<String>,
|
||||
|
@ -34,10 +32,10 @@ pub struct Model {
|
|||
#[sea_orm(column_name = "userGroupJoiningId")]
|
||||
pub user_group_joining_id: Option<String>,
|
||||
pub users: Vec<String>,
|
||||
#[sea_orm(column_name = "excludeKeywords", column_type = "JsonBinary")]
|
||||
pub exclude_keywords: Json,
|
||||
#[sea_orm(column_type = "JsonBinary")]
|
||||
pub instances: Json,
|
||||
pub instances: Vec<String>,
|
||||
pub keywords: Vec<String>,
|
||||
#[sea_orm(column_name = "excludeKeywords")]
|
||||
pub exclude_keywords: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
|
|
|
@ -32,8 +32,6 @@ pub struct Model {
|
|||
#[sea_orm(column_name = "twoFactorEnabled")]
|
||||
pub two_factor_enabled: bool,
|
||||
pub password: Option<String>,
|
||||
#[sea_orm(column_name = "clientData", column_type = "JsonBinary")]
|
||||
pub client_data: Json,
|
||||
#[sea_orm(column_name = "autoAcceptFollowed")]
|
||||
pub auto_accept_followed: bool,
|
||||
#[sea_orm(column_name = "alwaysMarkNsfw")]
|
||||
|
@ -48,14 +46,10 @@ pub struct Model {
|
|||
pub use_password_less_login: bool,
|
||||
#[sea_orm(column_name = "pinnedPageId", unique)]
|
||||
pub pinned_page_id: Option<String>,
|
||||
#[sea_orm(column_type = "JsonBinary")]
|
||||
pub room: Json,
|
||||
#[sea_orm(column_name = "injectFeaturedNote")]
|
||||
pub inject_featured_note: bool,
|
||||
#[sea_orm(column_name = "enableWordMute")]
|
||||
pub enable_word_mute: bool,
|
||||
#[sea_orm(column_name = "mutedWords", column_type = "JsonBinary")]
|
||||
pub muted_words: Json,
|
||||
#[sea_orm(column_name = "mutingNotificationTypes")]
|
||||
pub muting_notification_types: Vec<UserProfileMutingnotificationtypesEnum>,
|
||||
#[sea_orm(column_name = "noCrawle")]
|
||||
|
@ -64,8 +58,6 @@ pub struct Model {
|
|||
pub receive_announcement_email: bool,
|
||||
#[sea_orm(column_name = "emailNotificationTypes", column_type = "JsonBinary")]
|
||||
pub email_notification_types: Json,
|
||||
#[sea_orm(column_name = "mutedInstances", column_type = "JsonBinary")]
|
||||
pub muted_instances: Json,
|
||||
#[sea_orm(column_name = "publicReactions")]
|
||||
pub public_reactions: bool,
|
||||
#[sea_orm(column_name = "ffVisibility")]
|
||||
|
@ -78,6 +70,10 @@ pub struct Model {
|
|||
pub is_indexable: bool,
|
||||
#[sea_orm(column_name = "mutedPatterns")]
|
||||
pub muted_patterns: Vec<String>,
|
||||
#[sea_orm(column_name = "mutedInstances")]
|
||||
pub muted_instances: Vec<String>,
|
||||
#[sea_orm(column_name = "mutedWords")]
|
||||
pub muted_words: Vec<String>,
|
||||
pub lang: Option<String>,
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
import type { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class AntennaJsonbToArray1714192520471 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "antenna" RENAME COLUMN "instances" TO "instances_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "antenna" ADD COLUMN "instances" character varying(512)[] NOT NULL DEFAULT '{}'`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`UPDATE "antenna" SET "instances" = ARRAY(SELECT jsonb_array_elements_text("instances_old"))::character varying(512)[]`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`UPDATE "antenna" SET "instances" = '{}' WHERE "instances" = '{""}'`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "antenna" DROP COLUMN "instances_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "antenna" RENAME COLUMN "keywords" TO "keywords_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "antenna" ADD COLUMN "keywords" text[] NOT NULL DEFAULT '{}'`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TEMP TABLE "HMyeXPcdtQYGsSrf" ("id" character varying(32), "kws" text[])`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`INSERT INTO "HMyeXPcdtQYGsSrf" ("id", "kws") SELECT "id", array_agg("X"."w") FROM (SELECT "id", array_to_string(ARRAY(SELECT jsonb_array_elements_text("kw")), ' ') AS "w" FROM (SELECT "id", jsonb_array_elements("keywords_old") AS "kw" FROM "antenna") AS "a") AS "X" GROUP BY "id"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`UPDATE "antenna" SET "keywords" = "kws" FROM "HMyeXPcdtQYGsSrf" WHERE "antenna"."id" = "HMyeXPcdtQYGsSrf"."id"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`UPDATE "antenna" SET "keywords" = '{}' WHERE "keywords" = '{""}'`,
|
||||
);
|
||||
await queryRunner.query(`ALTER TABLE "antenna" DROP COLUMN "keywords_old"`);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "antenna" RENAME COLUMN "excludeKeywords" TO "excludeKeywords_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "antenna" ADD COLUMN "excludeKeywords" text[] NOT NULL DEFAULT '{}'`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TEMP TABLE "kpdsACdZTRYqLkfK" ("id" character varying(32), "kws" text[])`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`INSERT INTO "kpdsACdZTRYqLkfK" ("id", "kws") SELECT "id", array_agg("X"."w") FROM (SELECT "id", array_to_string(ARRAY(SELECT jsonb_array_elements_text("kw")), ' ') AS "w" FROM (SELECT "id", jsonb_array_elements("excludeKeywords_old") AS "kw" FROM "antenna") AS "a") AS "X" GROUP BY "id"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`UPDATE "antenna" SET "excludeKeywords" = "kws" FROM "kpdsACdZTRYqLkfK" WHERE "antenna"."id" = "kpdsACdZTRYqLkfK"."id"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`UPDATE "antenna" SET "excludeKeywords" = '{}' WHERE "excludeKeywords" = '{""}'`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "antenna" DROP COLUMN "excludeKeywords_old"`,
|
||||
);
|
||||
}
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`UPDATE "antenna" SET "instances" = '{""}' WHERE "instances" = '{}'`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "antenna" RENAME COLUMN "instances" TO "instances_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "antenna" ADD COLUMN "instances" jsonb NOT NULL DEFAULT '[]'`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`UPDATE "antenna" SET "instances" = to_jsonb("instances_old")`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "antenna" DROP COLUMN "instances_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`UPDATE "antenna" SET "keywords" = '{""}' WHERE "keywords" = '{}'`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "antenna" RENAME COLUMN "keywords" TO "keywords_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "antenna" ADD COLUMN "keywords" jsonb NOT NULL DEFAULT '[]'`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TEMP TABLE "QvPNcMitBFkqqBgm" ("id" character varying(32), "kws" jsonb NOT NULL DEFAULT '[]')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`INSERT INTO "QvPNcMitBFkqqBgm" ("id", "kws") SELECT "id", jsonb_agg("X"."w") FROM (SELECT "id", to_jsonb(string_to_array(unnest("keywords_old"), ' ')) AS "w" FROM "antenna") AS "X" GROUP BY "id"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`UPDATE "antenna" SET "keywords" = "kws" FROM "QvPNcMitBFkqqBgm" WHERE "antenna"."id" = "QvPNcMitBFkqqBgm"."id"`,
|
||||
);
|
||||
await queryRunner.query(`ALTER TABLE "antenna" DROP COLUMN "keywords_old"`);
|
||||
await queryRunner.query(
|
||||
`UPDATE "antenna" SET "excludeKeywords" = '{""}' WHERE "excludeKeywords" = '{}'`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "antenna" RENAME COLUMN "excludeKeywords" TO "excludeKeywords_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "antenna" ADD COLUMN "excludeKeywords" jsonb NOT NULL DEFAULT '[]'`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TEMP TABLE "MZvVSjHzYcGXmGmz" ("id" character varying(32), "kws" jsonb NOT NULL DEFAULT '[]')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`INSERT INTO "MZvVSjHzYcGXmGmz" ("id", "kws") SELECT "id", jsonb_agg("X"."w") FROM (SELECT "id", to_jsonb(string_to_array(unnest("excludeKeywords_old"), ' ')) AS "w" FROM "antenna") AS "X" GROUP BY "id"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`UPDATE "antenna" SET "excludeKeywords" = "kws" FROM "MZvVSjHzYcGXmGmz" WHERE "antenna"."id" = "MZvVSjHzYcGXmGmz"."id"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "antenna" DROP COLUMN "excludeKeywords_old"`,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
import type { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class DropUnusedUserprofileColumns1714259023878
|
||||
implements MigrationInterface
|
||||
{
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_profile" DROP COLUMN "clientData"`,
|
||||
);
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "room"`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_profile" ADD "room" jsonb NOT NULL DEFAULT '{}'`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`COMMENT ON COLUMN "user_profile"."room" IS 'The room data of the User.'`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_profile" ADD "clientData" jsonb NOT NULL DEFAULT '{}'`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`COMMENT ON COLUMN "user_profile"."clientData" IS 'The client-specific data of the User.'`,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
import type { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class UserprofileJsonbToArray1714270605574
|
||||
implements MigrationInterface
|
||||
{
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_profile" RENAME COLUMN "mutedInstances" TO "mutedInstances_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_profile" ADD COLUMN "mutedInstances" character varying(512)[] NOT NULL DEFAULT '{}'`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`UPDATE "user_profile" SET "mutedInstances" = ARRAY(SELECT jsonb_array_elements_text("mutedInstances_old"))::character varying(512)[]`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_profile" DROP COLUMN "mutedInstances_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_profile" RENAME COLUMN "mutedWords" TO "mutedWords_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_profile" ADD COLUMN "mutedWords" text[] NOT NULL DEFAULT '{}'`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TEMP TABLE "MmVqAUUgpshTCQcw" ("userId" character varying(32), "kws" text[])`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`INSERT INTO "MmVqAUUgpshTCQcw" ("userId", "kws") SELECT "userId", array_agg("X"."w") FROM (SELECT "userId", array_to_string(ARRAY(SELECT jsonb_array_elements_text("kw")), ' ') AS "w" FROM (SELECT "userId", jsonb_array_elements("mutedWords_old") AS "kw" FROM "user_profile") AS "a") AS "X" GROUP BY "userId"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`UPDATE "user_profile" SET "mutedWords" = "kws" FROM "MmVqAUUgpshTCQcw" WHERE "user_profile"."userId" = "MmVqAUUgpshTCQcw"."userId"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_profile" DROP COLUMN "mutedWords_old"`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_profile" RENAME COLUMN "mutedInstances" TO "mutedInstances_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_profile" ADD COLUMN "mutedInstances" jsonb NOT NULL DEFAULT '[]'`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`UPDATE "user_profile" SET "mutedInstances" = to_jsonb("mutedInstances_old")`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_profile" DROP COLUMN "mutedInstances_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_profile" RENAME COLUMN "mutedWords" TO "mutedWords_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_profile" ADD COLUMN "mutedWords" jsonb NOT NULL DEFAULT '[]'`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TEMP TABLE "BCrsGgLCUeMMLARy" ("userId" character varying(32), "kws" jsonb NOT NULL DEFAULT '[]')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`INSERT INTO "BCrsGgLCUeMMLARy" ("userId", "kws") SELECT "userId", jsonb_agg("X"."w") FROM (SELECT "userId", to_jsonb(string_to_array(unnest("mutedWords_old"), ' ')) AS "w" FROM "user_profile") AS "X" GROUP BY "userId"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`UPDATE "user_profile" SET "mutedWords" = "kws" FROM "BCrsGgLCUeMMLARy" WHERE "user_profile"."userId" = "BCrsGgLCUeMMLARy"."userId"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_profile" DROP COLUMN "mutedWords_old"`,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -46,44 +46,38 @@ export async function checkHitAntenna(
|
|||
if (!instances.includes(noteUser.host?.toLowerCase() ?? "")) return false;
|
||||
}
|
||||
|
||||
const keywords = antenna.keywords
|
||||
// Clean up
|
||||
.map((xs) => xs.filter((x) => x !== ""))
|
||||
.filter((xs) => xs.length > 0);
|
||||
|
||||
let text = `${note.text ?? ""} ${note.cw ?? ""}`;
|
||||
if (note.files != null)
|
||||
text += ` ${note.files.map((f) => f.comment ?? "").join(" ")}`;
|
||||
text = text.trim();
|
||||
|
||||
if (keywords.length > 0) {
|
||||
if (antenna.keywords.length > 0) {
|
||||
if (note.text == null) return false;
|
||||
|
||||
const matched = keywords.some((and) =>
|
||||
and.every((keyword) =>
|
||||
antenna.caseSensitive
|
||||
? text.includes(keyword)
|
||||
: text.toLowerCase().includes(keyword.toLowerCase()),
|
||||
),
|
||||
const matched = antenna.keywords.some((item) =>
|
||||
item
|
||||
.split(" ")
|
||||
.every((keyword) =>
|
||||
antenna.caseSensitive
|
||||
? text.includes(keyword)
|
||||
: text.toLowerCase().includes(keyword.toLowerCase()),
|
||||
),
|
||||
);
|
||||
|
||||
if (!matched) return false;
|
||||
}
|
||||
|
||||
const excludeKeywords = antenna.excludeKeywords
|
||||
// Clean up
|
||||
.map((xs) => xs.filter((x) => x !== ""))
|
||||
.filter((xs) => xs.length > 0);
|
||||
|
||||
if (excludeKeywords.length > 0) {
|
||||
if (antenna.excludeKeywords.length > 0) {
|
||||
if (note.text == null) return false;
|
||||
|
||||
const matched = excludeKeywords.some((and) =>
|
||||
and.every((keyword) =>
|
||||
antenna.caseSensitive
|
||||
? note.text?.includes(keyword)
|
||||
: note.text?.toLowerCase().includes(keyword.toLowerCase()),
|
||||
),
|
||||
const matched = antenna.excludeKeywords.some((item) =>
|
||||
item
|
||||
.split(" ")
|
||||
.every((keyword) =>
|
||||
antenna.caseSensitive
|
||||
? note.text?.includes(keyword)
|
||||
: note.text?.toLowerCase().includes(keyword.toLowerCase()),
|
||||
),
|
||||
);
|
||||
|
||||
if (matched) return false;
|
||||
|
|
|
@ -59,20 +59,30 @@ export class Antenna {
|
|||
})
|
||||
public users: string[];
|
||||
|
||||
@Column("jsonb", {
|
||||
default: [],
|
||||
@Column("varchar", {
|
||||
length: 512,
|
||||
array: true,
|
||||
default: "{}",
|
||||
})
|
||||
public instances: string[];
|
||||
|
||||
@Column("jsonb", {
|
||||
default: [],
|
||||
// whitespace: AND condition
|
||||
// array items: OR condition
|
||||
// e.g., ["alpha beta", "gamma"]
|
||||
// does match "alpha beta", "beta alpha alpha", "gamma alpha", "gamma epsilon"
|
||||
// does not match "alpha", "beta gamma", "alpha alpha", "eplison"
|
||||
@Column("text", {
|
||||
array: true,
|
||||
default: "{}",
|
||||
})
|
||||
public keywords: string[][];
|
||||
public keywords: string[];
|
||||
|
||||
@Column("jsonb", {
|
||||
default: [],
|
||||
// same match rule as `keywords`, except that this field is for excluded words
|
||||
@Column("text", {
|
||||
array: true,
|
||||
default: "{}",
|
||||
})
|
||||
public excludeKeywords: string[][];
|
||||
public excludeKeywords: string[];
|
||||
|
||||
@Column("boolean", {
|
||||
default: false,
|
||||
|
|
|
@ -138,20 +138,6 @@ export class UserProfile {
|
|||
})
|
||||
public moderationNote: string | null;
|
||||
|
||||
// TODO: そのうち消す
|
||||
@Column("jsonb", {
|
||||
default: {},
|
||||
comment: "The client-specific data of the User.",
|
||||
})
|
||||
public clientData: Record<string, any>;
|
||||
|
||||
// TODO: そのうち消す
|
||||
@Column("jsonb", {
|
||||
default: {},
|
||||
comment: "The room data of the User.",
|
||||
})
|
||||
public room: Record<string, any>;
|
||||
|
||||
@Column("boolean", {
|
||||
default: false,
|
||||
})
|
||||
|
@ -200,12 +186,6 @@ export class UserProfile {
|
|||
})
|
||||
public pinnedPageId: Page["id"] | null;
|
||||
|
||||
@OneToOne((type) => Page, {
|
||||
onDelete: "SET NULL",
|
||||
})
|
||||
@JoinColumn()
|
||||
public pinnedPage: Page | null;
|
||||
|
||||
@Index()
|
||||
@Column("boolean", {
|
||||
default: false,
|
||||
|
@ -213,19 +193,28 @@ export class UserProfile {
|
|||
})
|
||||
public enableWordMute: boolean;
|
||||
|
||||
@Column("jsonb", {
|
||||
default: [],
|
||||
// whitespace: AND condition
|
||||
// array items: OR condition
|
||||
// e.g., ["alpha beta", "gamma"]
|
||||
// does match "alpha beta", "beta alpha alpha", "gamma alpha", "gamma epsilon"
|
||||
// does not match "alpha", "beta gamma", "alpha alpha", "eplison"
|
||||
@Column("text", {
|
||||
array: true,
|
||||
default: "{}",
|
||||
})
|
||||
public mutedWords: string[][];
|
||||
public mutedWords: string[];
|
||||
|
||||
// array of regular expressions
|
||||
@Column("text", {
|
||||
array: true,
|
||||
nullable: false,
|
||||
})
|
||||
public mutedPatterns: string[];
|
||||
|
||||
@Column("jsonb", {
|
||||
default: [],
|
||||
@Column("varchar", {
|
||||
length: 512,
|
||||
array: true,
|
||||
default: "{}",
|
||||
comment: "List of instances muted by the user.",
|
||||
})
|
||||
public mutedInstances: string[];
|
||||
|
@ -253,6 +242,13 @@ export class UserProfile {
|
|||
})
|
||||
@JoinColumn()
|
||||
public user: Relation<User>;
|
||||
|
||||
@OneToOne(() => Page, {
|
||||
onDelete: "SET NULL",
|
||||
nullable: true,
|
||||
})
|
||||
@JoinColumn()
|
||||
public pinnedPage: Relation<Page | null>;
|
||||
//#endregion
|
||||
|
||||
constructor(data: Partial<UserProfile>) {
|
||||
|
|
|
@ -16,8 +16,8 @@ export const AntennaRepository = db.getRepository(Antenna).extend({
|
|||
id: antenna.id,
|
||||
createdAt: antenna.createdAt.toISOString(),
|
||||
name: antenna.name,
|
||||
keywords: antenna.keywords,
|
||||
excludeKeywords: antenna.excludeKeywords,
|
||||
keywords: antenna.keywords.map((row) => row.split(" ")),
|
||||
excludeKeywords: antenna.excludeKeywords.map((row) => row.split(" ")),
|
||||
src: antenna.src,
|
||||
userListId: antenna.userListId,
|
||||
userGroupId: userGroupJoining ? userGroupJoining.userGroupId : null,
|
||||
|
|
|
@ -573,7 +573,7 @@ export const UserRepository = db.getRepository(User).extend({
|
|||
hasUnreadNotification: this.getHasUnreadNotification(user.id),
|
||||
hasPendingReceivedFollowRequest:
|
||||
this.getHasPendingReceivedFollowRequest(user.id),
|
||||
mutedWords: profile?.mutedWords,
|
||||
mutedWords: profile?.mutedWords.map((row) => row.split(" ")),
|
||||
mutedPatterns: profile?.mutedPatterns,
|
||||
mutedInstances: profile?.mutedInstances,
|
||||
mutingNotificationTypes: profile?.mutingNotificationTypes,
|
||||
|
|
|
@ -42,25 +42,13 @@ export function generateMutedUserQuery(
|
|||
)
|
||||
// mute instances
|
||||
.andWhere(
|
||||
new Brackets((qb) => {
|
||||
qb.andWhere("note.userHost IS NULL").orWhere(
|
||||
`NOT ((${mutingInstanceQuery.getQuery()})::jsonb ? note.userHost)`,
|
||||
);
|
||||
}),
|
||||
)
|
||||
.andWhere(
|
||||
new Brackets((qb) => {
|
||||
qb.where("note.replyUserHost IS NULL").orWhere(
|
||||
`NOT ((${mutingInstanceQuery.getQuery()})::jsonb ? note.replyUserHost)`,
|
||||
);
|
||||
}),
|
||||
)
|
||||
.andWhere(
|
||||
new Brackets((qb) => {
|
||||
qb.where("note.renoteUserHost IS NULL").orWhere(
|
||||
`NOT ((${mutingInstanceQuery.getQuery()})::jsonb ? note.renoteUserHost)`,
|
||||
);
|
||||
}),
|
||||
`NOT
|
||||
ARRAY[
|
||||
note."userHost",
|
||||
note."replyUserHost",
|
||||
note."renoteUserHost"
|
||||
]::character varying[]
|
||||
&& (${mutingInstanceQuery.getQuery()})`,
|
||||
);
|
||||
|
||||
q.setParameters(mutingQuery.getParameters());
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Brackets, SelectQueryBuilder } from "typeorm";
|
||||
import { User } from "@/models/entities/user.js";
|
||||
import { Brackets, type SelectQueryBuilder } from "typeorm";
|
||||
import type { User } from "@/models/entities/user.js";
|
||||
import { RenoteMutings } from "@/models/index.js";
|
||||
|
||||
export function generateMutedUserRenotesQueryForNotes(
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Brackets, SelectQueryBuilder } from "typeorm";
|
||||
import { User } from "@/models/entities/user.js";
|
||||
import { Brackets, type SelectQueryBuilder } from "typeorm";
|
||||
import type { User } from "@/models/entities/user.js";
|
||||
import { ReplyMutings } from "@/models/index.js";
|
||||
|
||||
export function generateMutedUserRepliesQueryForNotes(
|
||||
|
|
|
@ -59,7 +59,7 @@ export default define(meta, paramDef, async (ps, me) => {
|
|||
carefulBot: profile.carefulBot,
|
||||
injectFeaturedNote: profile.injectFeaturedNote,
|
||||
receiveAnnouncementEmail: profile.receiveAnnouncementEmail,
|
||||
mutedWords: profile.mutedWords,
|
||||
mutedWords: profile.mutedWords.map((row) => row.split(" ")),
|
||||
mutedPatterns: profile.mutedPatterns,
|
||||
mutedInstances: profile.mutedInstances,
|
||||
mutingNotificationTypes: profile.mutingNotificationTypes,
|
||||
|
|
|
@ -104,8 +104,22 @@ export const paramDef = {
|
|||
} as const;
|
||||
|
||||
export default define(meta, paramDef, async (ps, user) => {
|
||||
const flatten = (arr: string[][]) =>
|
||||
JSON.stringify(arr) === "[[]]"
|
||||
? ([] as string[])
|
||||
: arr.map((row) => row.join(" "));
|
||||
|
||||
const keywords = flatten(
|
||||
ps.keywords.map((row) => row.filter((word) => word.trim().length > 0)),
|
||||
);
|
||||
const excludedWords = flatten(
|
||||
ps.excludeKeywords.map((row) =>
|
||||
row.filter((word) => word.trim().length > 0),
|
||||
),
|
||||
);
|
||||
|
||||
if (user.movedToUri != null) throw new ApiError(meta.errors.noSuchUserGroup);
|
||||
if (ps.keywords.length === 0) throw new ApiError(meta.errors.noKeywords);
|
||||
if (keywords.length === 0) throw new ApiError(meta.errors.noKeywords);
|
||||
let userList;
|
||||
let userGroupJoining;
|
||||
|
||||
|
@ -146,10 +160,10 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||
src: ps.src,
|
||||
userListId: userList ? userList.id : null,
|
||||
userGroupJoiningId: userGroupJoining ? userGroupJoining.id : null,
|
||||
keywords: ps.keywords,
|
||||
excludeKeywords: ps.excludeKeywords,
|
||||
keywords: keywords,
|
||||
excludeKeywords: excludedWords,
|
||||
users: ps.users,
|
||||
instances: ps.instances,
|
||||
instances: ps.instances.filter((instance) => instance.trim().length > 0),
|
||||
caseSensitive: ps.caseSensitive,
|
||||
withReplies: ps.withReplies,
|
||||
withFile: ps.withFile,
|
||||
|
|
|
@ -100,6 +100,20 @@ export const paramDef = {
|
|||
} as const;
|
||||
|
||||
export default define(meta, paramDef, async (ps, user) => {
|
||||
const flatten = (arr: string[][]) =>
|
||||
JSON.stringify(arr) === "[[]]"
|
||||
? ([] as string[])
|
||||
: arr.map((row) => row.join(" "));
|
||||
|
||||
const keywords = flatten(
|
||||
ps.keywords.map((row) => row.filter((word) => word.trim().length > 0)),
|
||||
);
|
||||
const excludedWords = flatten(
|
||||
ps.excludeKeywords.map((row) =>
|
||||
row.filter((word) => word.trim().length > 0),
|
||||
),
|
||||
);
|
||||
|
||||
// Fetch the antenna
|
||||
const antenna = await Antennas.findOneBy({
|
||||
id: ps.antennaId,
|
||||
|
@ -138,10 +152,10 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||
src: ps.src,
|
||||
userListId: userList ? userList.id : null,
|
||||
userGroupJoiningId: userGroupJoining ? userGroupJoining.id : null,
|
||||
keywords: ps.keywords,
|
||||
excludeKeywords: ps.excludeKeywords,
|
||||
keywords: keywords,
|
||||
excludeKeywords: excludedWords,
|
||||
users: ps.users,
|
||||
instances: ps.instances,
|
||||
instances: ps.instances.filter((instance) => instance.trim().length > 0),
|
||||
caseSensitive: ps.caseSensitive,
|
||||
withReplies: ps.withReplies,
|
||||
withFile: ps.withFile,
|
||||
|
|
|
@ -125,7 +125,8 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||
query.andWhere(
|
||||
new Brackets((qb) => {
|
||||
qb.andWhere("notifier.host IS NULL").orWhere(
|
||||
`NOT (( ${mutingInstanceQuery.getQuery()} )::jsonb ? notifier.host)`,
|
||||
`NOT EXISTS (SELECT 1 FROM "user_profile" WHERE "userId" = :muterId AND notifier.host = ANY("mutedInstances"))`,
|
||||
{ muterId: user.id },
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
|
|
@ -178,26 +178,11 @@ export default define(meta, paramDef, async (ps, _user, token) => {
|
|||
}
|
||||
}
|
||||
if (ps.mutedWords !== undefined) {
|
||||
// for backward compatibility
|
||||
for (const item of ps.mutedWords) {
|
||||
if (Array.isArray(item)) continue;
|
||||
|
||||
const regexp = item.match(/^\/(.+)\/(.*)$/);
|
||||
if (!regexp) throw new ApiError(meta.errors.invalidRegexp);
|
||||
|
||||
try {
|
||||
new RegExp(regexp[1], regexp[2]);
|
||||
} catch (err) {
|
||||
throw new ApiError(meta.errors.invalidRegexp);
|
||||
}
|
||||
|
||||
profileUpdates.mutedPatterns = profileUpdates.mutedPatterns ?? [];
|
||||
profileUpdates.mutedPatterns.push(item);
|
||||
}
|
||||
|
||||
profileUpdates.mutedWords = ps.mutedWords.filter((item) =>
|
||||
Array.isArray(item),
|
||||
);
|
||||
const flatten = (arr: string[][]) =>
|
||||
JSON.stringify(arr) === "[[]]"
|
||||
? ([] as string[])
|
||||
: arr.map((row) => row.join(" "));
|
||||
profileUpdates.mutedWords = flatten(ps.mutedWords);
|
||||
}
|
||||
if (
|
||||
profileUpdates.mutedWords !== undefined ||
|
||||
|
|
Loading…
Reference in a new issue