merge: Persisted instance blocks (!1068)
View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/1068 Approved-by: dakkar <dakkar@thenautilus.net> Approved-by: Marie <github@yuugi.dev>
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: hazelnoot and other Sharkey contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
export class IndexIDXInstanceHostKey1748104955717 {
|
||||
name = 'IndexIDXInstanceHostKey1748104955717'
|
||||
|
||||
async up(queryRunner) {
|
||||
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_instance_host_key" ON "instance" (((lower(reverse("host")) || '.')::text) text_pattern_ops)`);
|
||||
}
|
||||
|
||||
async down(queryRunner) {
|
||||
await queryRunner.query(`DROP INDEX "IDX_instance_host_key"`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: hazelnoot and other Sharkey contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {import('typeorm').MigrationInterface} MigrationInterface
|
||||
* @typedef {{ blockedHosts: string[], silencedHosts: string[], mediaSilencedHosts: string[], federationHosts: string[], bubbleInstances: string[] }} Meta
|
||||
*/
|
||||
|
||||
/**
|
||||
* @class
|
||||
* @implements {MigrationInterface}
|
||||
*/
|
||||
export class AddInstanceBlockColumns1748105111513 {
|
||||
name = 'AddInstanceBlockColumns1748105111513'
|
||||
|
||||
async up(queryRunner) {
|
||||
// Schema migration
|
||||
await queryRunner.query(`ALTER TABLE "instance" ADD "isBlocked" boolean NOT NULL DEFAULT false`);
|
||||
await queryRunner.query(`COMMENT ON COLUMN "instance"."isBlocked" IS 'True if this instance is blocked from federation.'`);
|
||||
await queryRunner.query(`ALTER TABLE "instance" ADD "isAllowListed" boolean NOT NULL DEFAULT false`);
|
||||
await queryRunner.query(`COMMENT ON COLUMN "instance"."isAllowListed" IS 'True if this instance is allow-listed.'`);
|
||||
await queryRunner.query(`ALTER TABLE "instance" ADD "isBubbled" boolean NOT NULL DEFAULT false`);
|
||||
await queryRunner.query(`COMMENT ON COLUMN "instance"."isBubbled" IS 'True if this instance is part of the local bubble.'`);
|
||||
await queryRunner.query(`ALTER TABLE "instance" ADD "isSilenced" boolean NOT NULL DEFAULT false`);
|
||||
await queryRunner.query(`COMMENT ON COLUMN "instance"."isSilenced" IS 'True if this instance is silenced.'`);
|
||||
await queryRunner.query(`ALTER TABLE "instance" ADD "isMediaSilenced" boolean NOT NULL DEFAULT false`);
|
||||
await queryRunner.query(`COMMENT ON COLUMN "instance"."isMediaSilenced" IS 'True if this instance is media-silenced.'`);
|
||||
|
||||
// Data migration
|
||||
/** @type {Meta[]} */
|
||||
const metas = await queryRunner.query(`SELECT "blockedHosts", "silencedHosts", "mediaSilencedHosts", "federationHosts", "bubbleInstances" FROM "meta"`);
|
||||
if (metas.length > 0) {
|
||||
/** @type {Meta} */
|
||||
const meta = metas[0];
|
||||
|
||||
// Blocked hosts
|
||||
if (meta.blockedHosts.length > 0) {
|
||||
const patterns = buildPatterns(meta.blockedHosts);
|
||||
await queryRunner.query(`UPDATE "instance" SET "isBlocked" = true WHERE ((lower(reverse("host")) || '.')::text) LIKE ANY ($1)`, [ patterns ]);
|
||||
}
|
||||
|
||||
// Silenced hosts
|
||||
if (meta.silencedHosts.length > 0) {
|
||||
const patterns = buildPatterns(meta.silencedHosts);
|
||||
await queryRunner.query(`UPDATE "instance" SET "isSilenced" = true WHERE ((lower(reverse("host")) || '.')::text) LIKE ANY ($1)`, [ patterns ]);
|
||||
}
|
||||
|
||||
// Media silenced hosts
|
||||
if (meta.mediaSilencedHosts.length > 0) {
|
||||
const patterns = buildPatterns(meta.mediaSilencedHosts);
|
||||
await queryRunner.query(`UPDATE "instance" SET "isMediaSilenced" = true WHERE ((lower(reverse("host")) || '.')::text) LIKE ANY ($1)`, [ patterns ]);
|
||||
}
|
||||
|
||||
// Allow-listed hosts
|
||||
if (meta.federationHosts.length > 0) {
|
||||
const patterns = buildPatterns(meta.federationHosts);
|
||||
await queryRunner.query(`UPDATE "instance" SET "isAllowListed" = true WHERE ((lower(reverse("host")) || '.')::text) LIKE ANY ($1)`, [ patterns ]);
|
||||
}
|
||||
|
||||
// Bubbled hosts
|
||||
if (meta.bubbleInstances.length > 0) {
|
||||
const patterns = buildPatterns(meta.bubbleInstances);
|
||||
await queryRunner.query(`UPDATE "instance" SET "isBubbled" = true WHERE ((lower(reverse("host")) || '.')::text) LIKE ANY ($1)`, [ patterns ]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async down(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "isMediaSilenced"`);
|
||||
await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "isSilenced"`);
|
||||
await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "isBubbled"`);
|
||||
await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "isAllowListed"`);
|
||||
await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "isBlocked"`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string[]} input
|
||||
* @returns {string[]}
|
||||
*/
|
||||
function buildPatterns(input) {
|
||||
return input.map(i => i.toLowerCase().split('').reverse().join('') + '.%');
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: hazelnoot and other Sharkey contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {import('typeorm').MigrationInterface} MigrationInterface
|
||||
*/
|
||||
|
||||
/**
|
||||
* @class
|
||||
* @implements {MigrationInterface}
|
||||
*/
|
||||
export class AddInstanceForeignKeys1748128176881 {
|
||||
name = 'AddInstanceForeignKeys1748128176881'
|
||||
|
||||
async up(queryRunner) {
|
||||
// Fix-up: Some older instances have users without a matching instance entry
|
||||
await queryRunner.query(`
|
||||
INSERT INTO "instance" ("id", "host", "firstRetrievedAt")
|
||||
SELECT
|
||||
MIN("id"),
|
||||
"host",
|
||||
COALESCE(MIN("lastFetchedAt"), CURRENT_TIMESTAMP)
|
||||
FROM "user"
|
||||
WHERE
|
||||
"host" IS NOT NULL AND
|
||||
NOT EXISTS (select 1 from "instance" where "instance"."host" = "user"."host")
|
||||
GROUP BY "host"
|
||||
`);
|
||||
|
||||
await queryRunner.query(`ALTER TABLE "user" ADD CONSTRAINT "FK_user_host" FOREIGN KEY ("host") REFERENCES "instance"("host") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||
await queryRunner.query(`ALTER TABLE "note" ADD CONSTRAINT "FK_note_userHost" FOREIGN KEY ("userHost") REFERENCES "instance"("host") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||
await queryRunner.query(`ALTER TABLE "note" ADD CONSTRAINT "FK_note_replyUserHost" FOREIGN KEY ("replyUserHost") REFERENCES "instance"("host") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||
await queryRunner.query(`ALTER TABLE "note" ADD CONSTRAINT "FK_note_renoteUserHost" FOREIGN KEY ("renoteUserHost") REFERENCES "instance"("host") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||
}
|
||||
|
||||
async down(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "note" DROP CONSTRAINT "FK_note_renoteUserHost"`);
|
||||
await queryRunner.query(`ALTER TABLE "note" DROP CONSTRAINT "FK_note_replyUserHost"`);
|
||||
await queryRunner.query(`ALTER TABLE "note" DROP CONSTRAINT "FK_note_userHost"`);
|
||||
await queryRunner.query(`ALTER TABLE "user" DROP CONSTRAINT "FK_user_host"`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: hazelnoot and other Sharkey contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {import('typeorm').MigrationInterface} MigrationInterface
|
||||
*/
|
||||
|
||||
/**
|
||||
* @class
|
||||
* @implements {MigrationInterface}
|
||||
*/
|
||||
export class AddInstanceForeignKeysToFollowing1748137683887 {
|
||||
name = 'AddInstanceForeignKeysToFollowing1748137683887'
|
||||
|
||||
async up(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "following" ADD CONSTRAINT "FK_following_followerHost" FOREIGN KEY ("followerHost") REFERENCES "instance"("host") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||
await queryRunner.query(`ALTER TABLE "following" ADD CONSTRAINT "FK_following_followeeHost" FOREIGN KEY ("followeeHost") REFERENCES "instance"("host") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||
}
|
||||
|
||||
async down(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "following" DROP CONSTRAINT "FK_following_followeeHost"`);
|
||||
await queryRunner.query(`ALTER TABLE "following" DROP CONSTRAINT "FK_following_followerHost"`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: hazelnoot and other Sharkey contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {import('typeorm').MigrationInterface} MigrationInterface
|
||||
*/
|
||||
|
||||
/**
|
||||
* @class
|
||||
* @implements {MigrationInterface}
|
||||
*/
|
||||
export class AnalyzeInstanceUserNoteFollowing1748191631151 {
|
||||
name = 'AnalyzeInstanceUserNoteFollowing1748191631151'
|
||||
|
||||
async up(queryRunner) {
|
||||
// Refresh statistics for tables impacted by new indexes.
|
||||
// This helps the query planner to efficiently use them without waiting for the next full vacuum.
|
||||
await queryRunner.query(`ANALYZE "instance", "user", "following", "note"`);
|
||||
}
|
||||
|
||||
async down(queryRunner) {
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user