Merge remote-tracking branch 'misskey/master' into feature/misskey-2024.07

This commit is contained in:
dakkar 2024-08-02 12:25:58 +01:00
commit cfa9b852df
585 changed files with 23423 additions and 9623 deletions

View file

@ -0,0 +1,122 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { Inject, Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { ApiError } from '@/server/api/error.js';
import {
AbuseReportNotificationRecipientEntityService,
} from '@/core/entities/AbuseReportNotificationRecipientEntityService.js';
import { AbuseReportNotificationService } from '@/core/AbuseReportNotificationService.js';
import { DI } from '@/di-symbols.js';
import type { UserProfilesRepository } from '@/models/_.js';
export const meta = {
tags: ['admin', 'abuse-report', 'notification-recipient'],
requireCredential: true,
requireModerator: true,
secure: true,
kind: 'write:admin:abuse-report:notification-recipient',
res: {
type: 'object',
ref: 'AbuseReportNotificationRecipient',
},
errors: {
correlationCheckEmail: {
message: 'If "method" is email, "userId" must be set.',
code: 'CORRELATION_CHECK_EMAIL',
id: '348bb8ae-575a-6fe9-4327-5811999def8f',
httpStatusCode: 400,
},
correlationCheckWebhook: {
message: 'If "method" is webhook, "systemWebhookId" must be set.',
code: 'CORRELATION_CHECK_WEBHOOK',
id: 'b0c15051-de2d-29ef-260c-9585cddd701a',
httpStatusCode: 400,
},
emailAddressNotSet: {
message: 'Email address is not set.',
code: 'EMAIL_ADDRESS_NOT_SET',
id: '7cc1d85e-2f58-fc31-b644-3de8d0d3421f',
httpStatusCode: 400,
},
},
} as const;
export const paramDef = {
type: 'object',
properties: {
isActive: {
type: 'boolean',
},
name: {
type: 'string',
minLength: 1,
maxLength: 255,
},
method: {
type: 'string',
enum: ['email', 'webhook'],
},
userId: {
type: 'string',
format: 'misskey:id',
},
systemWebhookId: {
type: 'string',
format: 'misskey:id',
},
},
required: [
'isActive',
'name',
'method',
],
} as const;
@Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
constructor(
@Inject(DI.userProfilesRepository)
private userProfilesRepository: UserProfilesRepository,
private abuseReportNotificationService: AbuseReportNotificationService,
private abuseReportNotificationRecipientEntityService: AbuseReportNotificationRecipientEntityService,
) {
super(meta, paramDef, async (ps, me) => {
if (ps.method === 'email') {
const userProfile = await this.userProfilesRepository.findOneBy({ userId: ps.userId });
if (!ps.userId || !userProfile) {
throw new ApiError(meta.errors.correlationCheckEmail);
}
if (!userProfile.email || !userProfile.emailVerified) {
throw new ApiError(meta.errors.emailAddressNotSet);
}
}
if (ps.method === 'webhook' && !ps.systemWebhookId) {
throw new ApiError(meta.errors.correlationCheckWebhook);
}
const userId = ps.method === 'email' ? ps.userId : null;
const systemWebhookId = ps.method === 'webhook' ? ps.systemWebhookId : null;
const result = await this.abuseReportNotificationService.createRecipient(
{
isActive: ps.isActive,
name: ps.name,
method: ps.method,
userId: userId ?? null,
systemWebhookId: systemWebhookId ?? null,
},
me,
);
return this.abuseReportNotificationRecipientEntityService.pack(result);
});
}
}

View file

@ -0,0 +1,44 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { AbuseReportNotificationService } from '@/core/AbuseReportNotificationService.js';
export const meta = {
tags: ['admin', 'abuse-report', 'notification-recipient'],
requireCredential: true,
requireModerator: true,
secure: true,
kind: 'write:admin:abuse-report:notification-recipient',
} as const;
export const paramDef = {
type: 'object',
properties: {
id: {
type: 'string',
format: 'misskey:id',
},
},
required: [
'id',
],
} as const;
@Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
constructor(
private abuseReportNotificationService: AbuseReportNotificationService,
) {
super(meta, paramDef, async (ps, me) => {
await this.abuseReportNotificationService.deleteRecipient(
ps.id,
me,
);
});
}
}

View file

@ -0,0 +1,55 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
import {
AbuseReportNotificationRecipientEntityService,
} from '@/core/entities/AbuseReportNotificationRecipientEntityService.js';
import { AbuseReportNotificationService } from '@/core/AbuseReportNotificationService.js';
export const meta = {
tags: ['admin', 'abuse-report', 'notification-recipient'],
requireCredential: true,
requireModerator: true,
secure: true,
kind: 'read:admin:abuse-report:notification-recipient',
res: {
type: 'array',
items: {
type: 'object',
ref: 'AbuseReportNotificationRecipient',
},
},
} as const;
export const paramDef = {
type: 'object',
properties: {
method: {
type: 'array',
items: {
type: 'string',
enum: ['email', 'webhook'],
},
},
},
required: [],
} as const;
@Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
constructor(
private abuseReportNotificationService: AbuseReportNotificationService,
private abuseReportNotificationRecipientEntityService: AbuseReportNotificationRecipientEntityService,
) {
super(meta, paramDef, async (ps) => {
const recipients = await this.abuseReportNotificationService.fetchRecipients({ method: ps.method });
return this.abuseReportNotificationRecipientEntityService.packMany(recipients);
});
}
}

View file

@ -0,0 +1,64 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
import {
AbuseReportNotificationRecipientEntityService,
} from '@/core/entities/AbuseReportNotificationRecipientEntityService.js';
import { AbuseReportNotificationService } from '@/core/AbuseReportNotificationService.js';
import { ApiError } from '@/server/api/error.js';
export const meta = {
tags: ['admin', 'abuse-report', 'notification-recipient'],
requireCredential: true,
requireModerator: true,
secure: true,
kind: 'read:admin:abuse-report:notification-recipient',
res: {
type: 'object',
ref: 'AbuseReportNotificationRecipient',
},
errors: {
noSuchRecipient: {
message: 'No such recipient.',
code: 'NO_SUCH_RECIPIENT',
id: '013de6a8-f757-04cb-4d73-cc2a7e3368e4',
kind: 'server',
httpStatusCode: 404,
},
},
} as const;
export const paramDef = {
type: 'object',
properties: {
id: {
type: 'string',
format: 'misskey:id',
},
},
required: ['id'],
} as const;
@Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
constructor(
private abuseReportNotificationService: AbuseReportNotificationService,
private abuseReportNotificationRecipientEntityService: AbuseReportNotificationRecipientEntityService,
) {
super(meta, paramDef, async (ps) => {
const recipients = await this.abuseReportNotificationService.fetchRecipients({ ids: [ps.id] });
if (recipients.length === 0) {
throw new ApiError(meta.errors.noSuchRecipient);
}
return this.abuseReportNotificationRecipientEntityService.pack(recipients[0]);
});
}
}

View file

@ -0,0 +1,128 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { Inject, Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { ApiError } from '@/server/api/error.js';
import {
AbuseReportNotificationRecipientEntityService,
} from '@/core/entities/AbuseReportNotificationRecipientEntityService.js';
import { AbuseReportNotificationService } from '@/core/AbuseReportNotificationService.js';
import { DI } from '@/di-symbols.js';
import type { UserProfilesRepository } from '@/models/_.js';
export const meta = {
tags: ['admin', 'abuse-report', 'notification-recipient'],
requireCredential: true,
requireModerator: true,
secure: true,
kind: 'write:admin:abuse-report:notification-recipient',
res: {
type: 'object',
ref: 'AbuseReportNotificationRecipient',
},
errors: {
correlationCheckEmail: {
message: 'If "method" is email, "userId" must be set.',
code: 'CORRELATION_CHECK_EMAIL',
id: '348bb8ae-575a-6fe9-4327-5811999def8f',
httpStatusCode: 400,
},
correlationCheckWebhook: {
message: 'If "method" is webhook, "systemWebhookId" must be set.',
code: 'CORRELATION_CHECK_WEBHOOK',
id: 'b0c15051-de2d-29ef-260c-9585cddd701a',
httpStatusCode: 400,
},
emailAddressNotSet: {
message: 'Email address is not set.',
code: 'EMAIL_ADDRESS_NOT_SET',
id: '7cc1d85e-2f58-fc31-b644-3de8d0d3421f',
httpStatusCode: 400,
},
},
} as const;
export const paramDef = {
type: 'object',
properties: {
id: {
type: 'string',
format: 'misskey:id',
},
isActive: {
type: 'boolean',
},
name: {
type: 'string',
minLength: 1,
maxLength: 255,
},
method: {
type: 'string',
enum: ['email', 'webhook'],
},
userId: {
type: 'string',
format: 'misskey:id',
},
systemWebhookId: {
type: 'string',
format: 'misskey:id',
},
},
required: [
'id',
'isActive',
'name',
'method',
],
} as const;
@Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
constructor(
@Inject(DI.userProfilesRepository)
private userProfilesRepository: UserProfilesRepository,
private abuseReportNotificationService: AbuseReportNotificationService,
private abuseReportNotificationRecipientEntityService: AbuseReportNotificationRecipientEntityService,
) {
super(meta, paramDef, async (ps, me) => {
if (ps.method === 'email') {
const userProfile = await this.userProfilesRepository.findOneBy({ userId: ps.userId });
if (!ps.userId || !userProfile) {
throw new ApiError(meta.errors.correlationCheckEmail);
}
if (!userProfile.email || !userProfile.emailVerified) {
throw new ApiError(meta.errors.emailAddressNotSet);
}
}
if (ps.method === 'webhook' && !ps.systemWebhookId) {
throw new ApiError(meta.errors.correlationCheckWebhook);
}
const userId = ps.method === 'email' ? ps.userId : null;
const systemWebhookId = ps.method === 'webhook' ? ps.systemWebhookId : null;
const result = await this.abuseReportNotificationService.updateRecipient(
{
id: ps.id,
isActive: ps.isActive,
name: ps.name,
method: ps.method,
userId: userId ?? null,
systemWebhookId: systemWebhookId ?? null,
},
me,
);
return this.abuseReportNotificationRecipientEntityService.pack(result);
});
}
}

View file

@ -50,7 +50,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private moderationLogService: ModerationLogService,
) {
super(meta, paramDef, async (ps, me) => {
const ad = await this.adsRepository.insert({
const ad = await this.adsRepository.insertOne({
id: this.idService.gen(),
expiresAt: new Date(ps.expiresAt),
startsAt: new Date(ps.startsAt),
@ -61,7 +61,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
ratio: ps.ratio,
place: ps.place,
memo: ps.memo,
}).then(r => this.adsRepository.findOneByOrFail({ id: r.identifiers[0].id }));
});
this.moderationLogService.log(me, 'createAd', {
adId: ad.id,

View file

@ -40,7 +40,7 @@ export const paramDef = {
startsAt: { type: 'integer' },
dayOfWeek: { type: 'integer' },
},
required: ['id', 'memo', 'url', 'imageUrl', 'place', 'priority', 'ratio', 'expiresAt', 'startsAt', 'dayOfWeek'],
required: ['id'],
} as const;
@Injectable()
@ -63,8 +63,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
ratio: ps.ratio,
memo: ps.memo,
imageUrl: ps.imageUrl,
expiresAt: new Date(ps.expiresAt),
startsAt: new Date(ps.startsAt),
expiresAt: ps.expiresAt ? new Date(ps.expiresAt) : undefined,
startsAt: ps.startsAt ? new Date(ps.startsAt) : undefined,
dayOfWeek: ps.dayOfWeek,
});

View file

@ -69,6 +69,7 @@ export const paramDef = {
sinceId: { type: 'string', format: 'misskey:id' },
untilId: { type: 'string', format: 'misskey:id' },
userId: { type: 'string', format: 'misskey:id', nullable: true },
status: { type: 'string', enum: ['all', 'active', 'archived'], default: 'active' },
},
required: [],
} as const;
@ -87,7 +88,13 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
) {
super(meta, paramDef, async (ps, me) => {
const query = this.queryService.makePaginationQuery(this.announcementsRepository.createQueryBuilder('announcement'), ps.sinceId, ps.untilId);
query.andWhere('announcement.isActive = true');
if (ps.status === 'archived') {
query.andWhere('announcement.isActive = false');
} else if (ps.status === 'active') {
query.andWhere('announcement.isActive = true');
}
if (ps.userId) {
query.andWhere('announcement.userId = :userId', { userId: ps.userId });
} else {

View file

@ -61,7 +61,7 @@ export const meta = {
name: {
type: 'string',
optional: false, nullable: false,
example: 'lenna.jpg',
example: '192.jpg',
},
type: {
type: 'string',

View file

@ -66,11 +66,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
const ticketsPromises = [];
for (let i = 0; i < ps.count; i++) {
ticketsPromises.push(this.registrationTicketsRepository.insert({
ticketsPromises.push(this.registrationTicketsRepository.insertOne({
id: this.idService.gen(),
expiresAt: ps.expiresAt ? new Date(ps.expiresAt) : null,
code: generateInviteCode(),
}).then(x => this.registrationTicketsRepository.findOneByOrFail(x.identifiers[0])));
}));
}
const tickets = await Promise.all(ticketsPromises);

View file

@ -132,6 +132,16 @@ export const meta = {
nullable: false,
},
},
mediaSilencedHosts: {
type: 'array',
optional: false,
nullable: false,
items: {
type: 'string',
optional: false,
nullable: false,
},
},
pinnedUsers: {
type: 'array',
optional: false, nullable: false,
@ -586,6 +596,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
hiddenTags: instance.hiddenTags,
blockedHosts: instance.blockedHosts,
silencedHosts: instance.silencedHosts,
mediaSilencedHosts: instance.mediaSilencedHosts,
sensitiveWords: instance.sensitiveWords,
prohibitedWords: instance.prohibitedWords,
preservedUsernames: instance.preservedUsernames,

View file

@ -5,7 +5,7 @@
import { Inject, Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, WebhookDeliverQueue } from '@/core/QueueModule.js';
import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, UserWebhookDeliverQueue, SystemWebhookDeliverQueue } from '@/core/QueueModule.js';
export const meta = {
tags: ['admin'],
@ -53,7 +53,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
@Inject('queue:inbox') public inboxQueue: InboxQueue,
@Inject('queue:db') public dbQueue: DbQueue,
@Inject('queue:objectStorage') public objectStorageQueue: ObjectStorageQueue,
@Inject('queue:webhookDeliver') public webhookDeliverQueue: WebhookDeliverQueue,
@Inject('queue:userWebhookDeliver') public userWebhookDeliverQueue: UserWebhookDeliverQueue,
@Inject('queue:systemWebhookDeliver') public systemWebhookDeliverQueue: SystemWebhookDeliverQueue,
) {
super(meta, paramDef, async (ps, me) => {
const deliverJobCounts = await this.deliverQueue.getJobCounts();

View file

@ -5,12 +5,10 @@
import { Inject, Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
import type { UsersRepository, AbuseUserReportsRepository } from '@/models/_.js';
import { InstanceActorService } from '@/core/InstanceActorService.js';
import { QueueService } from '@/core/QueueService.js';
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
import type { AbuseUserReportsRepository } from '@/models/_.js';
import { DI } from '@/di-symbols.js';
import { ModerationLogService } from '@/core/ModerationLogService.js';
import { ApiError } from '@/server/api/error.js';
import { AbuseReportService } from '@/core/AbuseReportService.js';
export const meta = {
tags: ['admin'],
@ -18,6 +16,16 @@ export const meta = {
requireCredential: true,
requireModerator: true,
kind: 'write:admin:resolve-abuse-user-report',
errors: {
noSuchAbuseReport: {
message: 'No such abuse report.',
code: 'NO_SUCH_ABUSE_REPORT',
id: 'ac3794dd-2ce4-d878-e546-73c60c06b398',
kind: 'server',
httpStatusCode: 404,
},
},
} as const;
export const paramDef = {
@ -29,47 +37,20 @@ export const paramDef = {
required: ['reportId'],
} as const;
// TODO: ロジックをサービスに切り出す
@Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
constructor(
@Inject(DI.usersRepository)
private usersRepository: UsersRepository,
@Inject(DI.abuseUserReportsRepository)
private abuseUserReportsRepository: AbuseUserReportsRepository,
private queueService: QueueService,
private instanceActorService: InstanceActorService,
private apRendererService: ApRendererService,
private moderationLogService: ModerationLogService,
private abuseReportService: AbuseReportService,
) {
super(meta, paramDef, async (ps, me) => {
const report = await this.abuseUserReportsRepository.findOneBy({ id: ps.reportId });
if (report == null) {
throw new Error('report not found');
if (!report) {
throw new ApiError(meta.errors.noSuchAbuseReport);
}
if (ps.forward && report.targetUserHost != null) {
const actor = await this.instanceActorService.getInstanceActor();
const targetUser = await this.usersRepository.findOneByOrFail({ id: report.targetUserId });
this.queueService.deliver(actor, this.apRendererService.addContext(this.apRendererService.renderFlag(actor, targetUser.uri!, report.comment)), targetUser.inbox, false);
}
await this.abuseUserReportsRepository.update(report.id, {
resolved: true,
assigneeId: me.id,
forwarded: ps.forward && report.targetUserHost != null,
});
this.moderationLogService.log(me, 'resolveAbuseReport', {
reportId: report.id,
report: report,
forwarded: ps.forward && report.targetUserHost != null,
});
await this.abuseReportService.resolve([{ reportId: report.id, forward: ps.forward }], me);
});
}
}

View file

@ -6,7 +6,6 @@
import { Inject, Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
import type { RolesRepository } from '@/models/_.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
import { DI } from '@/di-symbols.js';
import { ApiError } from '@/server/api/error.js';
import { RoleService } from '@/core/RoleService.js';
@ -50,19 +49,6 @@ export const paramDef = {
},
required: [
'roleId',
'name',
'description',
'color',
'iconUrl',
'target',
'condFormula',
'isPublic',
'isModerator',
'isAdministrator',
'asBadge',
'canEditMembersByModerator',
'displayOrder',
'policies',
],
} as const;

View file

@ -0,0 +1,85 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { SystemWebhookEntityService } from '@/core/entities/SystemWebhookEntityService.js';
import { systemWebhookEventTypes } from '@/models/SystemWebhook.js';
import { SystemWebhookService } from '@/core/SystemWebhookService.js';
export const meta = {
tags: ['admin', 'system-webhook'],
requireCredential: true,
requireModerator: true,
secure: true,
kind: 'write:admin:system-webhook',
res: {
type: 'object',
ref: 'SystemWebhook',
},
} as const;
export const paramDef = {
type: 'object',
properties: {
isActive: {
type: 'boolean',
},
name: {
type: 'string',
minLength: 1,
maxLength: 255,
},
on: {
type: 'array',
items: {
type: 'string',
enum: systemWebhookEventTypes,
},
},
url: {
type: 'string',
minLength: 1,
maxLength: 1024,
},
secret: {
type: 'string',
minLength: 1,
maxLength: 1024,
},
},
required: [
'isActive',
'name',
'on',
'url',
'secret',
],
} as const;
@Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
constructor(
private systemWebhookService: SystemWebhookService,
private systemWebhookEntityService: SystemWebhookEntityService,
) {
super(meta, paramDef, async (ps, me) => {
const result = await this.systemWebhookService.createSystemWebhook(
{
isActive: ps.isActive,
name: ps.name,
on: ps.on,
url: ps.url,
secret: ps.secret,
},
me,
);
return this.systemWebhookEntityService.pack(result);
});
}
}

View file

@ -0,0 +1,44 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { SystemWebhookService } from '@/core/SystemWebhookService.js';
export const meta = {
tags: ['admin', 'system-webhook'],
requireCredential: true,
requireModerator: true,
secure: true,
kind: 'write:admin:system-webhook',
} as const;
export const paramDef = {
type: 'object',
properties: {
id: {
type: 'string',
format: 'misskey:id',
},
},
required: [
'id',
],
} as const;
@Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
constructor(
private systemWebhookService: SystemWebhookService,
) {
super(meta, paramDef, async (ps, me) => {
await this.systemWebhookService.deleteSystemWebhook(
ps.id,
me,
);
});
}
}

View file

@ -0,0 +1,60 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { SystemWebhookEntityService } from '@/core/entities/SystemWebhookEntityService.js';
import { systemWebhookEventTypes } from '@/models/SystemWebhook.js';
import { SystemWebhookService } from '@/core/SystemWebhookService.js';
export const meta = {
tags: ['admin', 'system-webhook'],
requireCredential: true,
requireModerator: true,
secure: true,
kind: 'write:admin:system-webhook',
res: {
type: 'array',
items: {
type: 'object',
ref: 'SystemWebhook',
},
},
} as const;
export const paramDef = {
type: 'object',
properties: {
isActive: {
type: 'boolean',
},
on: {
type: 'array',
items: {
type: 'string',
enum: systemWebhookEventTypes,
},
},
},
required: [],
} as const;
@Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
constructor(
private systemWebhookService: SystemWebhookService,
private systemWebhookEntityService: SystemWebhookEntityService,
) {
super(meta, paramDef, async (ps) => {
const webhooks = await this.systemWebhookService.fetchSystemWebhooks({
isActive: ps.isActive,
on: ps.on,
});
return this.systemWebhookEntityService.packMany(webhooks);
});
}
}

View file

@ -0,0 +1,62 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { SystemWebhookEntityService } from '@/core/entities/SystemWebhookEntityService.js';
import { ApiError } from '@/server/api/error.js';
import { SystemWebhookService } from '@/core/SystemWebhookService.js';
export const meta = {
tags: ['admin', 'system-webhook'],
requireCredential: true,
requireModerator: true,
secure: true,
kind: 'write:admin:system-webhook',
res: {
type: 'object',
ref: 'SystemWebhook',
},
errors: {
noSuchSystemWebhook: {
message: 'No such SystemWebhook.',
code: 'NO_SUCH_SYSTEM_WEBHOOK',
id: '38dd1ffe-04b4-6ff5-d8ba-4e6a6ae22c9d',
kind: 'server',
httpStatusCode: 404,
},
},
} as const;
export const paramDef = {
type: 'object',
properties: {
id: {
type: 'string',
format: 'misskey:id',
},
},
required: ['id'],
} as const;
@Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
constructor(
private systemWebhookService: SystemWebhookService,
private systemWebhookEntityService: SystemWebhookEntityService,
) {
super(meta, paramDef, async (ps) => {
const webhooks = await this.systemWebhookService.fetchSystemWebhooks({ ids: [ps.id] });
if (webhooks.length === 0) {
throw new ApiError(meta.errors.noSuchSystemWebhook);
}
return this.systemWebhookEntityService.pack(webhooks[0]);
});
}
}

View file

@ -0,0 +1,91 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { SystemWebhookEntityService } from '@/core/entities/SystemWebhookEntityService.js';
import { systemWebhookEventTypes } from '@/models/SystemWebhook.js';
import { SystemWebhookService } from '@/core/SystemWebhookService.js';
export const meta = {
tags: ['admin', 'system-webhook'],
requireCredential: true,
requireModerator: true,
secure: true,
kind: 'write:admin:system-webhook',
res: {
type: 'object',
ref: 'SystemWebhook',
},
} as const;
export const paramDef = {
type: 'object',
properties: {
id: {
type: 'string',
format: 'misskey:id',
},
isActive: {
type: 'boolean',
},
name: {
type: 'string',
minLength: 1,
maxLength: 255,
},
on: {
type: 'array',
items: {
type: 'string',
enum: systemWebhookEventTypes,
},
},
url: {
type: 'string',
minLength: 1,
maxLength: 1024,
},
secret: {
type: 'string',
minLength: 1,
maxLength: 1024,
},
},
required: [
'id',
'isActive',
'name',
'on',
'url',
'secret',
],
} as const;
@Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
constructor(
private systemWebhookService: SystemWebhookService,
private systemWebhookEntityService: SystemWebhookEntityService,
) {
super(meta, paramDef, async (ps, me) => {
const result = await this.systemWebhookService.updateSystemWebhook(
{
id: ps.id,
isActive: ps.isActive,
name: ps.name,
on: ps.on,
url: ps.url,
secret: ps.secret,
},
me,
);
return this.systemWebhookEntityService.pack(result);
});
}
}

View file

@ -158,6 +158,13 @@ export const paramDef = {
type: 'string',
},
},
mediaSilencedHosts: {
type: 'array',
nullable: true,
items: {
type: 'string',
},
},
summalyProxy: {
type: 'string', nullable: true,
description: '[Deprecated] Use "urlPreviewSummaryProxyUrl" instead.',
@ -211,6 +218,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
return h !== '' && h !== lv && !set.blockedHosts?.includes(h);
});
}
if (Array.isArray(ps.mediaSilencedHosts)) {
let lastValue = '';
set.mediaSilencedHosts = ps.mediaSilencedHosts.sort().filter((h) => {
const lv = lastValue;
lastValue = h;
return h !== '' && h !== lv && !set.blockedHosts?.includes(h);
});
}
if (ps.themeColor !== undefined) {
set.themeColor = ps.themeColor;
}