mirror of
https://iceshrimp.dev/limepotato/jormungandr-bite.git
synced 2024-11-26 03:47:40 -07:00
instance silence
This commit is contained in:
parent
622c2fa499
commit
ce60660128
12 changed files with 188 additions and 10 deletions
63
packages/backend/migration/1682844825247-InstanceSilence.js
Normal file
63
packages/backend/migration/1682844825247-InstanceSilence.js
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
export class InstanceSilence1682844825247 {
|
||||||
|
name = 'InstanceSilence1682844825247'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "abuse_user_report" DROP CONSTRAINT "fk_7f4e851a35d81b64dda28eee0"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_renote_muting_createdAt"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_renote_muting_muteeId"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_renote_muting_muterId"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "useStarForReactionFallback"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "enableGuestTimeline"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" ADD "silencedHosts" character varying(256) array NOT NULL DEFAULT '{}'`);
|
||||||
|
await queryRunner.query(`COMMENT ON COLUMN "notification"."isRead" IS 'Whether the notification was read.'`);
|
||||||
|
await queryRunner.query(`COMMENT ON COLUMN "meta"."defaultReaction" IS NULL`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "secureMode" SET NOT NULL`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "privateMode" SET NOT NULL`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "allowedHosts" SET NOT NULL`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "pinnedPages" SET DEFAULT '{/featured,/channels,/explore,/pages,/about-calckey}'`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "repositoryUrl" SET DEFAULT 'https://codeberg.org/calckey/calckey'`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "feedbackUrl" SET DEFAULT 'https://codeberg.org/calckey/calckey/issues/new'`);
|
||||||
|
await queryRunner.query(`COMMENT ON COLUMN "renote_muting"."createdAt" IS 'The created date of the Muting.'`);
|
||||||
|
await queryRunner.query(`COMMENT ON COLUMN "renote_muting"."muteeId" IS 'The mutee user ID.'`);
|
||||||
|
await queryRunner.query(`COMMENT ON COLUMN "renote_muting"."muterId" IS 'The muter user ID.'`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "page" ALTER COLUMN "isPublic" DROP DEFAULT`);
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_d1259a2c2b7bb413ff449e8711" ON "renote_muting" ("createdAt") `);
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_7eac97594bcac5ffcf2068089b" ON "renote_muting" ("muteeId") `);
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_7aa72a5fe76019bfe8e5e0e8b7" ON "renote_muting" ("muterId") `);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_0d801c609cec4e9eb4b6b4490c" ON "renote_muting" ("muterId", "muteeId") `);
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_a9021cc2e1feb5f72d3db6e9f5" ON "abuse_user_report" ("targetUserId") `);
|
||||||
|
await queryRunner.query(`ALTER TABLE "renote_muting" ADD CONSTRAINT "FK_7eac97594bcac5ffcf2068089b6" FOREIGN KEY ("muteeId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "renote_muting" ADD CONSTRAINT "FK_7aa72a5fe76019bfe8e5e0e8b7d" FOREIGN KEY ("muterId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "abuse_user_report" ADD CONSTRAINT "FK_a9021cc2e1feb5f72d3db6e9f5f" FOREIGN KEY ("targetUserId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "abuse_user_report" DROP CONSTRAINT "FK_a9021cc2e1feb5f72d3db6e9f5f"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "renote_muting" DROP CONSTRAINT "FK_7aa72a5fe76019bfe8e5e0e8b7d"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "renote_muting" DROP CONSTRAINT "FK_7eac97594bcac5ffcf2068089b6"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_a9021cc2e1feb5f72d3db6e9f5"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_0d801c609cec4e9eb4b6b4490c"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_7aa72a5fe76019bfe8e5e0e8b7"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_7eac97594bcac5ffcf2068089b"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_d1259a2c2b7bb413ff449e8711"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "page" ALTER COLUMN "isPublic" SET DEFAULT true`);
|
||||||
|
await queryRunner.query(`COMMENT ON COLUMN "renote_muting"."muterId" IS NULL`);
|
||||||
|
await queryRunner.query(`COMMENT ON COLUMN "renote_muting"."muteeId" IS NULL`);
|
||||||
|
await queryRunner.query(`COMMENT ON COLUMN "renote_muting"."createdAt" IS NULL`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "feedbackUrl" SET DEFAULT 'https://github.com/misskey-dev/misskey/issues/new'`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "repositoryUrl" SET DEFAULT 'https://github.com/misskey-dev/misskey'`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "pinnedPages" SET DEFAULT '{/featured,/channels,/explore,/pages,/about-misskey}'`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "allowedHosts" DROP NOT NULL`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "privateMode" DROP NOT NULL`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "secureMode" DROP NOT NULL`);
|
||||||
|
await queryRunner.query(`COMMENT ON COLUMN "meta"."defaultReaction" IS 'The fallback reaction for emoji reacts'`);
|
||||||
|
await queryRunner.query(`COMMENT ON COLUMN "notification"."isRead" IS 'Whether the Notification is read.'`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "silencedHosts"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" ADD "enableGuestTimeline" boolean NOT NULL DEFAULT false`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" ADD "useStarForReactionFallback" boolean NOT NULL DEFAULT false`);
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_renote_muting_muterId" ON "muting" ("muterId") `);
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_renote_muting_muteeId" ON "muting" ("muteeId") `);
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_renote_muting_createdAt" ON "muting" ("createdAt") `);
|
||||||
|
await queryRunner.query(`ALTER TABLE "abuse_user_report" ADD CONSTRAINT "fk_7f4e851a35d81b64dda28eee0" FOREIGN KEY ("targetUserId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,3 +18,20 @@ export async function shouldBlockInstance(
|
||||||
(blockedHost) => host === blockedHost || host.endsWith(`.${blockedHost}`),
|
(blockedHost) => host === blockedHost || host.endsWith(`.${blockedHost}`),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether a specific host (punycoded) should be limited.
|
||||||
|
*
|
||||||
|
* @param host punycoded instance host
|
||||||
|
* @param meta a resolved Meta table
|
||||||
|
* @returns whether the given host should be limited
|
||||||
|
*/
|
||||||
|
export async function shouldSilenceInstance(
|
||||||
|
host: Instance["host"],
|
||||||
|
meta?: Meta,
|
||||||
|
): Promise<boolean> {
|
||||||
|
const { silencedHosts } = meta ?? (await fetchMeta());
|
||||||
|
return silencedHosts.some(
|
||||||
|
(limitedHost) => host === limitedHost || host.endsWith(`.${limitedHost}`),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -97,6 +97,11 @@ export class Meta {
|
||||||
})
|
})
|
||||||
public blockedHosts: string[];
|
public blockedHosts: string[];
|
||||||
|
|
||||||
|
@Column('varchar', {
|
||||||
|
length: 256, array: true, default: '{}',
|
||||||
|
})
|
||||||
|
public silencedHosts: string[];
|
||||||
|
|
||||||
@Column('boolean', {
|
@Column('boolean', {
|
||||||
default: false,
|
default: false,
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
import { db } from "@/db/postgre.js";
|
import { db } from "@/db/postgre.js";
|
||||||
import { Instance } from "@/models/entities/instance.js";
|
import { Instance } from "@/models/entities/instance.js";
|
||||||
import type { Packed } from "@/misc/schema.js";
|
import type { Packed } from "@/misc/schema.js";
|
||||||
import { fetchMeta } from "@/misc/fetch-meta.js";
|
import { shouldBlockInstance, shouldSilenceInstance } from "@/misc/should-block-instance.js";
|
||||||
import { shouldBlockInstance } from "@/misc/should-block-instance.js";
|
|
||||||
|
|
||||||
export const InstanceRepository = db.getRepository(Instance).extend({
|
export const InstanceRepository = db.getRepository(Instance).extend({
|
||||||
async pack(instance: Instance): Promise<Packed<"FederationInstance">> {
|
async pack(instance: Instance): Promise<Packed<"FederationInstance">> {
|
||||||
const meta = await fetchMeta();
|
|
||||||
return {
|
return {
|
||||||
id: instance.id,
|
id: instance.id,
|
||||||
caughtAt: instance.caughtAt.toISOString(),
|
caughtAt: instance.caughtAt.toISOString(),
|
||||||
|
@ -22,6 +20,7 @@ export const InstanceRepository = db.getRepository(Instance).extend({
|
||||||
isNotResponding: instance.isNotResponding,
|
isNotResponding: instance.isNotResponding,
|
||||||
isSuspended: instance.isSuspended,
|
isSuspended: instance.isSuspended,
|
||||||
isBlocked: await shouldBlockInstance(instance.host),
|
isBlocked: await shouldBlockInstance(instance.host),
|
||||||
|
isSilenced: await shouldSilenceInstance(instance.host),
|
||||||
softwareName: instance.softwareName,
|
softwareName: instance.softwareName,
|
||||||
softwareVersion: instance.softwareVersion,
|
softwareVersion: instance.softwareVersion,
|
||||||
openRegistrations: instance.openRegistrations,
|
openRegistrations: instance.openRegistrations,
|
||||||
|
|
|
@ -68,6 +68,11 @@ export const packedFederationInstanceSchema = {
|
||||||
optional: false,
|
optional: false,
|
||||||
nullable: false,
|
nullable: false,
|
||||||
},
|
},
|
||||||
|
isSilenced: {
|
||||||
|
type: "boolean",
|
||||||
|
optional: false,
|
||||||
|
nullable: false,
|
||||||
|
},
|
||||||
softwareName: {
|
softwareName: {
|
||||||
type: "string",
|
type: "string",
|
||||||
optional: false,
|
optional: false,
|
||||||
|
|
|
@ -259,6 +259,16 @@ export const meta = {
|
||||||
nullable: false,
|
nullable: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
silencedHosts: {
|
||||||
|
type: "array",
|
||||||
|
optional: true,
|
||||||
|
nullable: false,
|
||||||
|
items: {
|
||||||
|
type: "string",
|
||||||
|
optional: false,
|
||||||
|
nullable: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
allowedHosts: {
|
allowedHosts: {
|
||||||
type: "array",
|
type: "array",
|
||||||
optional: true,
|
optional: true,
|
||||||
|
@ -524,6 +534,7 @@ export default define(meta, paramDef, async (ps, me) => {
|
||||||
customSplashIcons: instance.customSplashIcons,
|
customSplashIcons: instance.customSplashIcons,
|
||||||
hiddenTags: instance.hiddenTags,
|
hiddenTags: instance.hiddenTags,
|
||||||
blockedHosts: instance.blockedHosts,
|
blockedHosts: instance.blockedHosts,
|
||||||
|
silencedHosts: instance.silencedHosts,
|
||||||
allowedHosts: instance.allowedHosts,
|
allowedHosts: instance.allowedHosts,
|
||||||
privateMode: instance.privateMode,
|
privateMode: instance.privateMode,
|
||||||
secureMode: instance.secureMode,
|
secureMode: instance.secureMode,
|
||||||
|
|
|
@ -61,6 +61,13 @@ export const paramDef = {
|
||||||
type: "string",
|
type: "string",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
silencedHosts: {
|
||||||
|
type: "array",
|
||||||
|
nullable: true,
|
||||||
|
items: {
|
||||||
|
type: "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
allowedHosts: {
|
allowedHosts: {
|
||||||
type: "array",
|
type: "array",
|
||||||
nullable: true,
|
nullable: true,
|
||||||
|
@ -219,6 +226,15 @@ export default define(meta, paramDef, async (ps, me) => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(ps.silencedHosts)) {
|
||||||
|
let lastValue = "";
|
||||||
|
set.silencedHosts = ps.silencedHosts.sort().filter((h) => {
|
||||||
|
const lv = lastValue;
|
||||||
|
lastValue = h;
|
||||||
|
return h !== "" && h !== lv;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (ps.themeColor !== undefined) {
|
if (ps.themeColor !== undefined) {
|
||||||
set.themeColor = ps.themeColor;
|
set.themeColor = ps.themeColor;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@ export const paramDef = {
|
||||||
notResponding: { type: "boolean", nullable: true },
|
notResponding: { type: "boolean", nullable: true },
|
||||||
suspended: { type: "boolean", nullable: true },
|
suspended: { type: "boolean", nullable: true },
|
||||||
federating: { type: "boolean", nullable: true },
|
federating: { type: "boolean", nullable: true },
|
||||||
|
silenced: { type: "boolean", nullable: true },
|
||||||
subscribing: { type: "boolean", nullable: true },
|
subscribing: { type: "boolean", nullable: true },
|
||||||
publishing: { type: "boolean", nullable: true },
|
publishing: { type: "boolean", nullable: true },
|
||||||
limit: { type: "integer", minimum: 1, maximum: 100, default: 30 },
|
limit: { type: "integer", minimum: 1, maximum: 100, default: 30 },
|
||||||
|
@ -115,6 +116,22 @@ export default define(meta, paramDef, async (ps, me) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (typeof ps.silenced === "boolean") {
|
||||||
|
const meta = await fetchMeta(true);
|
||||||
|
if (ps.silenced) {
|
||||||
|
if (meta.silencedHosts.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
query.andWhere("instance.host IN (:...silences)", {
|
||||||
|
silences: meta.silencedHosts,
|
||||||
|
});
|
||||||
|
} else if (meta.silencedHosts.length > 0) {
|
||||||
|
query.andWhere("instance.host NOT IN (:...silences)", {
|
||||||
|
silences: meta.silencedHosts,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof ps.notResponding === "boolean") {
|
if (typeof ps.notResponding === "boolean") {
|
||||||
if (ps.notResponding) {
|
if (ps.notResponding) {
|
||||||
query.andWhere("instance.isNotResponding = TRUE");
|
query.andWhere("instance.isNotResponding = TRUE");
|
||||||
|
|
|
@ -27,6 +27,7 @@ import { isDuplicateKeyValueError } from "@/misc/is-duplicate-key-value-error.js
|
||||||
import type { Packed } from "@/misc/schema.js";
|
import type { Packed } from "@/misc/schema.js";
|
||||||
import { getActiveWebhooks } from "@/misc/webhook-cache.js";
|
import { getActiveWebhooks } from "@/misc/webhook-cache.js";
|
||||||
import { webhookDeliver } from "@/queue/index.js";
|
import { webhookDeliver } from "@/queue/index.js";
|
||||||
|
import { shouldSilenceInstance } from "@/misc/should-block-instance.js";
|
||||||
|
|
||||||
const logger = new Logger("following/create");
|
const logger = new Logger("following/create");
|
||||||
|
|
||||||
|
@ -227,12 +228,14 @@ export default async function (
|
||||||
|
|
||||||
// フォロー対象が鍵アカウントである or
|
// フォロー対象が鍵アカウントである or
|
||||||
// フォロワーがBotであり、フォロー対象がBotからのフォローに慎重である or
|
// フォロワーがBotであり、フォロー対象がBotからのフォローに慎重である or
|
||||||
// フォロワーがローカルユーザーであり、フォロー対象がリモートユーザーである
|
// フォロワーがローカルユーザーであり、フォロー対象がリモートユーザーである or
|
||||||
|
// The follower is remote, the followee is local, and the follower is in a silenced instance.
|
||||||
// 上記のいずれかに当てはまる場合はすぐフォローせずにフォローリクエストを発行しておく
|
// 上記のいずれかに当てはまる場合はすぐフォローせずにフォローリクエストを発行しておく
|
||||||
if (
|
if (
|
||||||
followee.isLocked ||
|
followee.isLocked ||
|
||||||
(followeeProfile.carefulBot && follower.isBot) ||
|
(followeeProfile.carefulBot && follower.isBot) ||
|
||||||
(Users.isLocalUser(follower) && Users.isRemoteUser(followee))
|
(Users.isLocalUser(follower) && Users.isRemoteUser(followee)) ||
|
||||||
|
(Users.isRemoteUser(follower) && Users.isLocalUser(followee) && await shouldSilenceInstance(follower.host))
|
||||||
) {
|
) {
|
||||||
let autoAccept = false;
|
let autoAccept = false;
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ import {
|
||||||
} from "@/models/index.js";
|
} from "@/models/index.js";
|
||||||
import type { DriveFile } from "@/models/entities/drive-file.js";
|
import type { DriveFile } from "@/models/entities/drive-file.js";
|
||||||
import type { App } from "@/models/entities/app.js";
|
import type { App } from "@/models/entities/app.js";
|
||||||
import { Not, In } from "typeorm";
|
import { Not, In, IsNull } from "typeorm";
|
||||||
import type { User, ILocalUser, IRemoteUser } from "@/models/entities/user.js";
|
import type { User, ILocalUser, IRemoteUser } from "@/models/entities/user.js";
|
||||||
import { genId } from "@/misc/gen-id.js";
|
import { genId } from "@/misc/gen-id.js";
|
||||||
import {
|
import {
|
||||||
|
@ -66,6 +66,7 @@ import { Cache } from "@/misc/cache.js";
|
||||||
import type { UserProfile } from "@/models/entities/user-profile.js";
|
import type { UserProfile } from "@/models/entities/user-profile.js";
|
||||||
import { db } from "@/db/postgre.js";
|
import { db } from "@/db/postgre.js";
|
||||||
import { getActiveWebhooks } from "@/misc/webhook-cache.js";
|
import { getActiveWebhooks } from "@/misc/webhook-cache.js";
|
||||||
|
import { shouldSilenceInstance } from "@/misc/should-block-instance.js";
|
||||||
|
|
||||||
const mutedWordsCache = new Cache<
|
const mutedWordsCache = new Cache<
|
||||||
{ userId: UserProfile["userId"]; mutedWords: UserProfile["mutedWords"] }[]
|
{ userId: UserProfile["userId"]; mutedWords: UserProfile["mutedWords"] }[]
|
||||||
|
@ -166,7 +167,8 @@ export default async (
|
||||||
data: Option,
|
data: Option,
|
||||||
silent = false,
|
silent = false,
|
||||||
) =>
|
) =>
|
||||||
new Promise<Note>(async (res, rej) => {
|
// rome-ignore lint/suspicious/noAsyncPromiseExecutor: FIXME
|
||||||
|
new Promise<Note>(async (res, rej) => {
|
||||||
// If you reply outside the channel, match the scope of the target.
|
// If you reply outside the channel, match the scope of the target.
|
||||||
// TODO (I think it's a process that could be done on the client side, but it's server side for now.)
|
// TODO (I think it's a process that could be done on the client side, but it's server side for now.)
|
||||||
if (
|
if (
|
||||||
|
@ -203,6 +205,13 @@ export default async (
|
||||||
data.visibility = "home";
|
data.visibility = "home";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const inSilencedInstance = Users.isRemoteUser(user) && await shouldSilenceInstance(user.host);
|
||||||
|
|
||||||
|
// If the
|
||||||
|
if (data.visibility === "public" && inSilencedInstance) {
|
||||||
|
data.visibility = "home";
|
||||||
|
}
|
||||||
|
|
||||||
// Reject if the target of the renote is a public range other than "Home or Entire".
|
// Reject if the target of the renote is a public range other than "Home or Entire".
|
||||||
if (
|
if (
|
||||||
data.renote &&
|
data.renote &&
|
||||||
|
@ -307,6 +316,14 @@ export default async (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove from mention the local users who aren't following the remote user in the silenced instance.
|
||||||
|
if (inSilencedInstance) {
|
||||||
|
const relations = await Followings.findBy([
|
||||||
|
{ followeeId: user.id, followerHost: IsNull() }, // a local user following the silenced user
|
||||||
|
]).then(rels => rels.map(rel => rel.followerId));
|
||||||
|
mentionedUsers = mentionedUsers.filter(mentioned => relations.includes(mentioned.id));
|
||||||
|
}
|
||||||
|
|
||||||
const note = await insertNote(user, data, tags, emojis, mentionedUsers);
|
const note = await insertNote(user, data, tags, emojis, mentionedUsers);
|
||||||
|
|
||||||
res(note);
|
res(note);
|
||||||
|
|
|
@ -55,6 +55,7 @@ export type Endpoints = {
|
||||||
"admin/get-table-stats": { req: TODO; res: TODO };
|
"admin/get-table-stats": { req: TODO; res: TODO };
|
||||||
"admin/invite": { req: TODO; res: TODO };
|
"admin/invite": { req: TODO; res: TODO };
|
||||||
"admin/logs": { req: TODO; res: TODO };
|
"admin/logs": { req: TODO; res: TODO };
|
||||||
|
"admin/meta": { req: TODO; res: TODO };
|
||||||
"admin/reset-password": { req: TODO; res: TODO };
|
"admin/reset-password": { req: TODO; res: TODO };
|
||||||
"admin/resolve-abuse-user-report": { req: TODO; res: TODO };
|
"admin/resolve-abuse-user-report": { req: TODO; res: TODO };
|
||||||
"admin/resync-chart": { req: TODO; res: TODO };
|
"admin/resync-chart": { req: TODO; res: TODO };
|
||||||
|
|
|
@ -2,18 +2,25 @@
|
||||||
<MkStickyContainer>
|
<MkStickyContainer>
|
||||||
<template #header
|
<template #header
|
||||||
><MkPageHeader
|
><MkPageHeader
|
||||||
|
v-model:tab="tab"
|
||||||
:actions="headerActions"
|
:actions="headerActions"
|
||||||
:tabs="headerTabs"
|
:tabs="headerTabs"
|
||||||
:display-back-button="true"
|
:display-back-button="true"
|
||||||
/></template>
|
/></template>
|
||||||
<MkSpacer :content-max="700" :margin-min="16" :margin-max="32">
|
<MkSpacer :content-max="700" :margin-min="16" :margin-max="32">
|
||||||
<FormSuspense :p="init">
|
<FormSuspense :p="init">
|
||||||
<FormTextarea v-model="blockedHosts" class="_formBlock">
|
<FormTextarea v-if="tab === 'block'" v-model="blockedHosts" class="_formBlock">
|
||||||
<span>{{ i18n.ts.blockedInstances }}</span>
|
<span>{{ i18n.ts.blockedInstances }}</span>
|
||||||
<template #caption>{{
|
<template #caption>{{
|
||||||
i18n.ts.blockedInstancesDescription
|
i18n.ts.blockedInstancesDescription
|
||||||
}}</template>
|
}}</template>
|
||||||
</FormTextarea>
|
</FormTextarea>
|
||||||
|
<FormTextarea v-else-if="tab === 'silence'" v-model="silencedHosts" class="_formBlock">
|
||||||
|
<span>{{ i18n.ts.silencedInstances }}</span>
|
||||||
|
<template #caption>{{
|
||||||
|
i18n.ts.silencedInstancesDescription
|
||||||
|
}}</template>
|
||||||
|
</FormTextarea>
|
||||||
|
|
||||||
<FormButton primary class="_formBlock" @click="save"
|
<FormButton primary class="_formBlock" @click="save"
|
||||||
><i class="ph-floppy-disk-back ph-bold ph-lg"></i>
|
><i class="ph-floppy-disk-back ph-bold ph-lg"></i>
|
||||||
|
@ -35,15 +42,21 @@ import { i18n } from "@/i18n";
|
||||||
import { definePageMetadata } from "@/scripts/page-metadata";
|
import { definePageMetadata } from "@/scripts/page-metadata";
|
||||||
|
|
||||||
let blockedHosts: string = $ref("");
|
let blockedHosts: string = $ref("");
|
||||||
|
let silencedHosts: string = $ref("");
|
||||||
|
let tab = $ref("block");
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
const meta = await os.api("admin/meta");
|
const meta = await os.api("admin/meta");
|
||||||
|
if (meta) {
|
||||||
blockedHosts = meta.blockedHosts.join("\n");
|
blockedHosts = meta.blockedHosts.join("\n");
|
||||||
|
silencedHosts = meta.silencedHosts.join("\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function save() {
|
function save() {
|
||||||
os.apiWithDialog("admin/update-meta", {
|
os.apiWithDialog("admin/update-meta", {
|
||||||
blockedHosts: blockedHosts.split("\n").map((h) => h.trim()) || [],
|
blockedHosts: blockedHosts.split("\n").map((h) => h.trim()) || [],
|
||||||
|
silencedHosts: silencedHosts.split("\n").map((h) => h.trim()) || [],
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
fetchInstance();
|
fetchInstance();
|
||||||
});
|
});
|
||||||
|
@ -51,7 +64,18 @@ function save() {
|
||||||
|
|
||||||
const headerActions = $computed(() => []);
|
const headerActions = $computed(() => []);
|
||||||
|
|
||||||
const headerTabs = $computed(() => []);
|
const headerTabs = $computed(() => [
|
||||||
|
{
|
||||||
|
key: "block",
|
||||||
|
title: i18n.ts.block,
|
||||||
|
icon: "ph-prohibit ph-bold ph-lg",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "silence",
|
||||||
|
title: i18n.ts.silence,
|
||||||
|
icon: "ph-eye-slash ph-bold ph-lg",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
definePageMetadata({
|
definePageMetadata({
|
||||||
title: i18n.ts.instanceBlocking,
|
title: i18n.ts.instanceBlocking,
|
||||||
|
|
Loading…
Reference in a new issue