summaryrefslogtreecommitdiff
path: root/packages/backend/src/server/api/endpoints
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2023-03-22 09:55:38 +0900
committerGitHub <noreply@github.com>2023-03-22 09:55:38 +0900
commit1e67e9c6616c6e87ae85ece71e5401006df2dd34 (patch)
treea0d6df03a3d0ac2edf1fda7ed4bfb789b5a29720 /packages/backend/src/server/api/endpoints
parentMerge pull request #10218 from misskey-dev/develop (diff)
parentfix drive-cleaner (diff)
downloadmisskey-1e67e9c6616c6e87ae85ece71e5401006df2dd34.tar.gz
misskey-1e67e9c6616c6e87ae85ece71e5401006df2dd34.tar.bz2
misskey-1e67e9c6616c6e87ae85ece71e5401006df2dd34.zip
Merge pull request #10342 from misskey-dev/develop
Release: 13.10.0
Diffstat (limited to 'packages/backend/src/server/api/endpoints')
-rw-r--r--packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/admin/emoji/add.ts1
-rw-r--r--packages/backend/src/server/api/endpoints/admin/emoji/copy.ts3
-rw-r--r--packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/admin/emoji/delete.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/admin/emoji/update.ts15
-rw-r--r--packages/backend/src/server/api/endpoints/admin/meta.ts13
-rw-r--r--packages/backend/src/server/api/endpoints/admin/queue/promote.ts52
-rw-r--r--packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/admin/roles/create.ts3
-rw-r--r--packages/backend/src/server/api/endpoints/admin/roles/update.ts3
-rw-r--r--packages/backend/src/server/api/endpoints/admin/update-meta.ts21
-rw-r--r--packages/backend/src/server/api/endpoints/antennas/create.ts9
-rw-r--r--packages/backend/src/server/api/endpoints/antennas/notes.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/ap/show.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/channels/update.ts8
-rw-r--r--packages/backend/src/server/api/endpoints/clips/add-note.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/clips/create.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/clips/favorite.ts76
-rw-r--r--packages/backend/src/server/api/endpoints/clips/list.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/clips/my-favorites.ts52
-rw-r--r--packages/backend/src/server/api/endpoints/clips/show.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/clips/unfavorite.ts65
-rw-r--r--packages/backend/src/server/api/endpoints/clips/update.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/drive/files.ts10
-rw-r--r--packages/backend/src/server/api/endpoints/emoji.ts56
-rw-r--r--packages/backend/src/server/api/endpoints/emojis.ts19
-rw-r--r--packages/backend/src/server/api/endpoints/federation/instances.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/i.ts16
-rw-r--r--packages/backend/src/server/api/endpoints/meta.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/notes/clips.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/notes/create.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/notes/featured.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/notes/global-timeline.ts1
-rw-r--r--packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts5
-rw-r--r--packages/backend/src/server/api/endpoints/notes/local-timeline.ts5
-rw-r--r--packages/backend/src/server/api/endpoints/notes/polls/vote.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/notes/search.ts13
-rw-r--r--packages/backend/src/server/api/endpoints/notes/timeline.ts5
-rw-r--r--packages/backend/src/server/api/endpoints/notifications/create.ts6
-rw-r--r--packages/backend/src/server/api/endpoints/renote-mute/create.ts99
-rw-r--r--packages/backend/src/server/api/endpoints/renote-mute/delete.ts87
-rw-r--r--packages/backend/src/server/api/endpoints/renote-mute/list.ts57
-rw-r--r--packages/backend/src/server/api/endpoints/users/clips.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/users/relation.ts8
-rw-r--r--packages/backend/src/server/api/endpoints/users/show.ts1
49 files changed, 691 insertions, 74 deletions
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts
index 0cc60e9191..4e4f845b0b 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts
@@ -53,7 +53,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
});
}
- await this.db.queryResultCache!.remove(['meta_emojis']);
+ await this.db.queryResultCache?.remove(['meta_emojis']);
this.globalEventService.publishBroadcastStream('emojiUpdated', {
emojis: await this.emojiEntityService.packDetailedMany(ps.ids),
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts
index 04c58050ff..2fb3e489e7 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts
@@ -56,6 +56,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
category: null,
aliases: [],
host: null,
+ license: null,
});
this.moderationLogService.insertModerationLog(me, 'addEmoji', {
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts
index 8885a40fd9..fea11a67d6 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts
@@ -87,9 +87,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
originalUrl: driveFile.url,
publicUrl: driveFile.webpublicUrl ?? driveFile.url,
type: driveFile.webpublicType ?? driveFile.type,
+ license: emoji.license,
}).then(x => this.emojisRepository.findOneByOrFail(x.identifiers[0]));
- await this.db.queryResultCache!.remove(['meta_emojis']);
+ await this.db.queryResultCache?.remove(['meta_emojis']);
this.globalEventService.publishBroadcastStream('emojiAdded', {
emoji: await this.emojiEntityService.packDetailed(copied.id),
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts
index f298baaedf..84aad020af 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts
@@ -47,7 +47,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
for (const emoji of emojis) {
await this.emojisRepository.delete(emoji.id);
- await this.db.queryResultCache!.remove(['meta_emojis']);
+ await this.db.queryResultCache?.remove(['meta_emojis']);
this.moderationLogService.insertModerationLog(me, 'deleteEmoji', {
emoji: emoji,
});
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts
index a5fbe3f4ea..90a5856a1b 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts
@@ -54,7 +54,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
await this.emojisRepository.delete(emoji.id);
- await this.db.queryResultCache!.remove(['meta_emojis']);
+ await this.db.queryResultCache?.remove(['meta_emojis']);
this.globalEventService.publishBroadcastStream('emojiDeleted', {
emojis: [await this.emojiEntityService.packDetailed(emoji)],
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts
index 66547024f7..3935183502 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts
@@ -53,7 +53,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
});
}
- await this.db.queryResultCache!.remove(['meta_emojis']);
+ await this.db.queryResultCache?.remove(['meta_emojis']);
this.globalEventService.publishBroadcastStream('emojiUpdated', {
emojis: await this.emojiEntityService.packDetailedMany(ps.ids),
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts
index c8992eeb04..6a875f9c83 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts
@@ -49,7 +49,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
aliases: ps.aliases,
});
- await this.db.queryResultCache!.remove(['meta_emojis']);
+ await this.db.queryResultCache?.remove(['meta_emojis']);
this.globalEventService.publishBroadcastStream('emojiUpdated', {
emojis: await this.emojiEntityService.packDetailedMany(ps.ids),
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts
index 8a538c1003..d3b999c0ed 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts
@@ -51,7 +51,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
category: ps.category,
});
- await this.db.queryResultCache!.remove(['meta_emojis']);
+ await this.db.queryResultCache?.remove(['meta_emojis']);
this.globalEventService.publishBroadcastStream('emojiUpdated', {
emojis: await this.emojiEntityService.packDetailedMany(ps.ids),
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts
index 809bf77d6b..1c649db93e 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts
@@ -19,6 +19,11 @@ export const meta = {
code: 'NO_SUCH_EMOJI',
id: '684dec9d-a8c2-4364-9aa8-456c49cb1dc8',
},
+ alreadyexistsemoji: {
+ message: 'Emoji already exists',
+ code: 'EMOJI_ALREADY_EXISTS',
+ id: '7180fe9d-1ee3-bff9-647d-fe9896d2ffb8',
+ },
},
} as const;
@@ -26,7 +31,7 @@ export const paramDef = {
type: 'object',
properties: {
id: { type: 'string', format: 'misskey:id' },
- name: { type: 'string' },
+ name: { type: 'string', pattern: '^[a-zA-Z0-9_]+$' },
category: {
type: 'string',
nullable: true,
@@ -35,6 +40,7 @@ export const paramDef = {
aliases: { type: 'array', items: {
type: 'string',
} },
+ license: { type: 'string', nullable: true },
},
required: ['id', 'name', 'aliases'],
} as const;
@@ -56,17 +62,18 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
) {
super(meta, paramDef, async (ps, me) => {
const emoji = await this.emojisRepository.findOneBy({ id: ps.id });
-
+ const emojiname = await this.emojisRepository.findOneBy({ name: ps.name });
if (emoji == null) throw new ApiError(meta.errors.noSuchEmoji);
-
+ if (emojiname != null && emojiname.id !== ps.id) throw new ApiError(meta.errors.alreadyexistsemoji);
await this.emojisRepository.update(emoji.id, {
updatedAt: new Date(),
name: ps.name,
category: ps.category,
aliases: ps.aliases,
+ license: ps.license,
});
- await this.db.queryResultCache!.remove(['meta_emojis']);
+ await this.db.queryResultCache?.remove(['meta_emojis']);
const updated = await this.emojiEntityService.packDetailed(emoji.id);
diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts
index 9eef1b29c5..ce7e0d569d 100644
--- a/packages/backend/src/server/api/endpoints/admin/meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/meta.ts
@@ -110,6 +110,14 @@ export const meta = {
optional: false, nullable: false,
},
},
+ sensitiveWords: {
+ type: 'array',
+ optional: true, nullable: false,
+ items: {
+ type: 'string',
+ optional: false, nullable: false,
+ },
+ },
hcaptchaSecretKey: {
type: 'string',
optional: true, nullable: true,
@@ -266,7 +274,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
uri: this.config.url,
description: instance.description,
langs: instance.langs,
- tosUrl: instance.ToSUrl,
+ tosUrl: instance.termsOfServiceUrl,
repositoryUrl: instance.repositoryUrl,
feedbackUrl: instance.feedbackUrl,
disableRegistration: instance.disableRegistration,
@@ -290,13 +298,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
enableEmail: instance.enableEmail,
enableServiceWorker: instance.enableServiceWorker,
translatorAvailable: instance.deeplAuthKey != null,
- pinnedPages: instance.pinnedPages,
- pinnedClipId: instance.pinnedClipId,
cacheRemoteFiles: instance.cacheRemoteFiles,
useStarForReactionFallback: instance.useStarForReactionFallback,
pinnedUsers: instance.pinnedUsers,
hiddenTags: instance.hiddenTags,
blockedHosts: instance.blockedHosts,
+ sensitiveWords: instance.sensitiveWords,
hcaptchaSecretKey: instance.hcaptchaSecretKey,
recaptchaSecretKey: instance.recaptchaSecretKey,
turnstileSecretKey: instance.turnstileSecretKey,
diff --git a/packages/backend/src/server/api/endpoints/admin/queue/promote.ts b/packages/backend/src/server/api/endpoints/admin/queue/promote.ts
new file mode 100644
index 0000000000..4e57e6613e
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/admin/queue/promote.ts
@@ -0,0 +1,52 @@
+import { Injectable } from '@nestjs/common';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import { ModerationLogService } from '@/core/ModerationLogService.js';
+import { QueueService } from '@/core/QueueService.js';
+
+export const meta = {
+ tags: ['admin'],
+
+ requireCredential: true,
+ requireModerator: true,
+} as const;
+
+export const paramDef = {
+ type: 'object',
+ properties: {
+ type: { type: 'string', enum: ['deliver', 'inbox'] },
+ },
+ required: ['type'],
+} as const;
+
+// eslint-disable-next-line import/no-default-export
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> {
+ constructor(
+ private moderationLogService: ModerationLogService,
+ private queueService: QueueService,
+ ) {
+ super(meta, paramDef, async (ps, me) => {
+ let delayedQueues;
+
+ switch (ps.type) {
+ case 'deliver':
+ delayedQueues = await this.queueService.deliverQueue.getDelayed();
+ for (let queueIndex = 0; queueIndex < delayedQueues.length; queueIndex++) {
+ const queue = delayedQueues[queueIndex];
+ await queue.promote();
+ }
+ break;
+
+ case 'inbox':
+ delayedQueues = await this.queueService.inboxQueue.getDelayed();
+ for (let queueIndex = 0; queueIndex < delayedQueues.length; queueIndex++) {
+ const queue = delayedQueues[queueIndex];
+ await queue.promote();
+ }
+ break;
+ }
+
+ this.moderationLogService.insertModerationLog(me, 'promoteQueue');
+ });
+ }
+}
diff --git a/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts b/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts
index d0d52089e6..aead894611 100644
--- a/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts
+++ b/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts
@@ -49,7 +49,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
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);
+ this.queueService.deliver(actor, this.apRendererService.addContext(this.apRendererService.renderFlag(actor, targetUser.uri!, report.comment)), targetUser.inbox, false);
}
await this.abuseUserReportsRepository.update(report.id, {
diff --git a/packages/backend/src/server/api/endpoints/admin/roles/create.ts b/packages/backend/src/server/api/endpoints/admin/roles/create.ts
index df60c6be94..1359894634 100644
--- a/packages/backend/src/server/api/endpoints/admin/roles/create.ts
+++ b/packages/backend/src/server/api/endpoints/admin/roles/create.ts
@@ -27,6 +27,7 @@ export const paramDef = {
isAdministrator: { type: 'boolean' },
asBadge: { type: 'boolean' },
canEditMembersByModerator: { type: 'boolean' },
+ displayOrder: { type: 'number' },
policies: {
type: 'object',
},
@@ -43,6 +44,7 @@ export const paramDef = {
'isAdministrator',
'asBadge',
'canEditMembersByModerator',
+ 'displayOrder',
'policies',
],
} as const;
@@ -76,6 +78,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
isModerator: ps.isModerator,
asBadge: ps.asBadge,
canEditMembersByModerator: ps.canEditMembersByModerator,
+ displayOrder: ps.displayOrder,
policies: ps.policies,
}).then(x => this.rolesRepository.findOneByOrFail(x.identifiers[0]));
diff --git a/packages/backend/src/server/api/endpoints/admin/roles/update.ts b/packages/backend/src/server/api/endpoints/admin/roles/update.ts
index b939ccdbf9..37b68c4c41 100644
--- a/packages/backend/src/server/api/endpoints/admin/roles/update.ts
+++ b/packages/backend/src/server/api/endpoints/admin/roles/update.ts
@@ -35,6 +35,7 @@ export const paramDef = {
isAdministrator: { type: 'boolean' },
asBadge: { type: 'boolean' },
canEditMembersByModerator: { type: 'boolean' },
+ displayOrder: { type: 'number' },
policies: {
type: 'object',
},
@@ -52,6 +53,7 @@ export const paramDef = {
'isAdministrator',
'asBadge',
'canEditMembersByModerator',
+ 'displayOrder',
'policies',
],
} as const;
@@ -85,6 +87,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
isAdministrator: ps.isAdministrator,
asBadge: ps.asBadge,
canEditMembersByModerator: ps.canEditMembersByModerator,
+ displayOrder: ps.displayOrder,
policies: ps.policies,
});
const updated = await this.rolesRepository.findOneByOrFail({ id: ps.roleId });
diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts
index a7531aae89..2f23aca243 100644
--- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts
@@ -27,6 +27,9 @@ export const paramDef = {
blockedHosts: { type: 'array', nullable: true, items: {
type: 'string',
} },
+ sensitiveWords: { type: 'array', nullable: true, items: {
+ type: 'string',
+ } },
themeColor: { type: 'string', nullable: true, pattern: '^#[0-9a-fA-F]{6}$' },
mascotImageUrl: { type: 'string', nullable: true },
bannerUrl: { type: 'string', nullable: true },
@@ -56,10 +59,6 @@ export const paramDef = {
proxyAccountId: { type: 'string', format: 'misskey:id', nullable: true },
maintainerName: { type: 'string', nullable: true },
maintainerEmail: { type: 'string', nullable: true },
- pinnedPages: { type: 'array', items: {
- type: 'string',
- } },
- pinnedClipId: { type: 'string', format: 'misskey:id', nullable: true },
langs: { type: 'array', items: {
type: 'string',
} },
@@ -131,6 +130,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
set.blockedHosts = ps.blockedHosts.filter(Boolean).map(x => x.toLowerCase());
}
+ if (Array.isArray(ps.sensitiveWords)) {
+ set.sensitiveWords = ps.sensitiveWords.filter(Boolean);
+ }
+
if (ps.themeColor !== undefined) {
set.themeColor = ps.themeColor;
}
@@ -247,14 +250,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
set.langs = ps.langs.filter(Boolean);
}
- if (Array.isArray(ps.pinnedPages)) {
- set.pinnedPages = ps.pinnedPages.filter(Boolean);
- }
-
- if (ps.pinnedClipId !== undefined) {
- set.pinnedClipId = ps.pinnedClipId;
- }
-
if (ps.summalyProxy !== undefined) {
set.summalyProxy = ps.summalyProxy;
}
@@ -304,7 +299,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
}
if (ps.tosUrl !== undefined) {
- set.ToSUrl = ps.tosUrl;
+ set.termsOfServiceUrl = ps.tosUrl;
}
if (ps.repositoryUrl !== undefined) {
diff --git a/packages/backend/src/server/api/endpoints/antennas/create.ts b/packages/backend/src/server/api/endpoints/antennas/create.ts
index bc5d249ae5..d147ddb7f1 100644
--- a/packages/backend/src/server/api/endpoints/antennas/create.ts
+++ b/packages/backend/src/server/api/endpoints/antennas/create.ts
@@ -79,6 +79,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
private globalEventService: GlobalEventService,
) {
super(meta, paramDef, async (ps, me) => {
+ if (ps.keywords.length === 0) {
+ throw new Error('invalid param');
+ }
+
const currentAntennasCount = await this.antennasRepository.countBy({
userId: me.id,
});
@@ -99,9 +103,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
}
}
+ const now = new Date();
+
const antenna = await this.antennasRepository.insert({
id: this.idService.genId(),
- createdAt: new Date(),
+ createdAt: now,
+ lastUsedAt: now,
userId: me.id,
name: ps.name,
src: ps.src,
diff --git a/packages/backend/src/server/api/endpoints/antennas/notes.ts b/packages/backend/src/server/api/endpoints/antennas/notes.ts
index fbb5acf617..039ba1115a 100644
--- a/packages/backend/src/server/api/endpoints/antennas/notes.ts
+++ b/packages/backend/src/server/api/endpoints/antennas/notes.ts
@@ -101,6 +101,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
this.noteReadService.read(me.id, notes);
}
+ this.antennasRepository.update(antenna.id, {
+ lastUsedAt: new Date(),
+ });
+
return await this.noteEntityService.packMany(notes, me);
});
}
diff --git a/packages/backend/src/server/api/endpoints/ap/show.ts b/packages/backend/src/server/api/endpoints/ap/show.ts
index 61e05531e6..a103d4196a 100644
--- a/packages/backend/src/server/api/endpoints/ap/show.ts
+++ b/packages/backend/src/server/api/endpoints/ap/show.ts
@@ -5,7 +5,7 @@ import type { UsersRepository, NotesRepository } from '@/models/index.js';
import type { Note } from '@/models/entities/Note.js';
import type { LocalUser, User } from '@/models/entities/User.js';
import { isActor, isPost, getApId } from '@/core/activitypub/type.js';
-import type { SchemaType } from '@/misc/schema.js';
+import type { SchemaType } from '@/misc/json-schema.js';
import { ApResolverService } from '@/core/activitypub/ApResolverService.js';
import { ApDbResolverService } from '@/core/activitypub/ApDbResolverService.js';
import { MetaService } from '@/core/MetaService.js';
diff --git a/packages/backend/src/server/api/endpoints/channels/update.ts b/packages/backend/src/server/api/endpoints/channels/update.ts
index d006e89bd2..a86cc2565a 100644
--- a/packages/backend/src/server/api/endpoints/channels/update.ts
+++ b/packages/backend/src/server/api/endpoints/channels/update.ts
@@ -4,6 +4,7 @@ import type { DriveFilesRepository, ChannelsRepository } from '@/models/index.js
import { ChannelEntityService } from '@/core/entities/ChannelEntityService.js';
import { DI } from '@/di-symbols.js';
import { ApiError } from '../../error.js';
+import { RoleService } from '@/core/RoleService.js';
export const meta = {
tags: ['channels'],
@@ -61,7 +62,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
private driveFilesRepository: DriveFilesRepository,
private channelEntityService: ChannelEntityService,
- ) {
+
+ private roleService: RoleService,
+ ) {
super(meta, paramDef, async (ps, me) => {
const channel = await this.channelsRepository.findOneBy({
id: ps.channelId,
@@ -71,7 +74,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
throw new ApiError(meta.errors.noSuchChannel);
}
- if (channel.userId !== me.id) {
+ const iAmModerator = await this.roleService.isModerator(me);
+ if (channel.userId !== me.id && !iAmModerator) {
throw new ApiError(meta.errors.accessDenied);
}
diff --git a/packages/backend/src/server/api/endpoints/clips/add-note.ts b/packages/backend/src/server/api/endpoints/clips/add-note.ts
index f3f9c3477f..b9d8dce47a 100644
--- a/packages/backend/src/server/api/endpoints/clips/add-note.ts
+++ b/packages/backend/src/server/api/endpoints/clips/add-note.ts
@@ -106,6 +106,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
noteId: note.id,
clipId: clip.id,
});
+
+ await this.clipsRepository.update(clip.id, {
+ lastClippedAt: new Date(),
+ });
});
}
}
diff --git a/packages/backend/src/server/api/endpoints/clips/create.ts b/packages/backend/src/server/api/endpoints/clips/create.ts
index c095de702c..a770dc986d 100644
--- a/packages/backend/src/server/api/endpoints/clips/create.ts
+++ b/packages/backend/src/server/api/endpoints/clips/create.ts
@@ -67,7 +67,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
description: ps.description,
}).then(x => this.clipsRepository.findOneByOrFail(x.identifiers[0]));
- return await this.clipEntityService.pack(clip);
+ return await this.clipEntityService.pack(clip, me);
});
}
}
diff --git a/packages/backend/src/server/api/endpoints/clips/favorite.ts b/packages/backend/src/server/api/endpoints/clips/favorite.ts
new file mode 100644
index 0000000000..6addf743a2
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/clips/favorite.ts
@@ -0,0 +1,76 @@
+import { Inject, Injectable } from '@nestjs/common';
+import type { ClipsRepository, ClipFavoritesRepository } from '@/models/index.js';
+import { IdService } from '@/core/IdService.js';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import { DI } from '@/di-symbols.js';
+import { ApiError } from '../../error.js';
+
+export const meta = {
+ tags: ['clip'],
+
+ requireCredential: true,
+
+ kind: 'write:clip-favorite',
+
+ errors: {
+ noSuchClip: {
+ message: 'No such clip.',
+ code: 'NO_SUCH_CLIP',
+ id: '4c2aaeae-80d8-4250-9606-26cb1fdb77a5',
+ },
+
+ alreadyFavorited: {
+ message: 'The clip has already been favorited.',
+ code: 'ALREADY_FAVORITED',
+ id: '92658936-c625-4273-8326-2d790129256e',
+ },
+ },
+} as const;
+
+export const paramDef = {
+ type: 'object',
+ properties: {
+ clipId: { type: 'string', format: 'misskey:id' },
+ },
+ required: ['clipId'],
+} as const;
+
+// eslint-disable-next-line import/no-default-export
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> {
+ constructor(
+ @Inject(DI.clipsRepository)
+ private clipsRepository: ClipsRepository,
+
+ @Inject(DI.clipFavoritesRepository)
+ private clipFavoritesRepository: ClipFavoritesRepository,
+
+ private idService: IdService,
+ ) {
+ super(meta, paramDef, async (ps, me) => {
+ const clip = await this.clipsRepository.findOneBy({ id: ps.clipId });
+ if (clip == null) {
+ throw new ApiError(meta.errors.noSuchClip);
+ }
+ if ((clip.userId !== me.id) && !clip.isPublic) {
+ throw new ApiError(meta.errors.noSuchClip);
+ }
+
+ const exist = await this.clipFavoritesRepository.findOneBy({
+ clipId: clip.id,
+ userId: me.id,
+ });
+
+ if (exist != null) {
+ throw new ApiError(meta.errors.alreadyFavorited);
+ }
+
+ await this.clipFavoritesRepository.insert({
+ id: this.idService.genId(),
+ createdAt: new Date(),
+ clipId: clip.id,
+ userId: me.id,
+ });
+ });
+ }
+}
diff --git a/packages/backend/src/server/api/endpoints/clips/list.ts b/packages/backend/src/server/api/endpoints/clips/list.ts
index 63ca069364..3b8deab709 100644
--- a/packages/backend/src/server/api/endpoints/clips/list.ts
+++ b/packages/backend/src/server/api/endpoints/clips/list.ts
@@ -42,7 +42,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
userId: me.id,
});
- return await Promise.all(clips.map(x => this.clipEntityService.pack(x)));
+ return await this.clipEntityService.packMany(clips, me);
});
}
}
diff --git a/packages/backend/src/server/api/endpoints/clips/my-favorites.ts b/packages/backend/src/server/api/endpoints/clips/my-favorites.ts
new file mode 100644
index 0000000000..fc727e93bd
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/clips/my-favorites.ts
@@ -0,0 +1,52 @@
+import { Inject, Injectable } from '@nestjs/common';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import type { ClipFavoritesRepository } from '@/models/index.js';
+import { DI } from '@/di-symbols.js';
+import { ClipEntityService } from '@/core/entities/ClipEntityService.js';
+
+export const meta = {
+ tags: ['account', 'clip'],
+
+ requireCredential: true,
+
+ kind: 'read:clip-favorite',
+
+ res: {
+ type: 'array',
+ optional: false, nullable: false,
+ items: {
+ type: 'object',
+ optional: false, nullable: false,
+ ref: 'Clip',
+ },
+ },
+} as const;
+
+export const paramDef = {
+ type: 'object',
+ properties: {
+ },
+ required: [],
+} as const;
+
+// eslint-disable-next-line import/no-default-export
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> {
+ constructor(
+ @Inject(DI.clipFavoritesRepository)
+ private clipFavoritesRepository: ClipFavoritesRepository,
+
+ private clipEntityService: ClipEntityService,
+ ) {
+ super(meta, paramDef, async (ps, me) => {
+ const query = this.clipFavoritesRepository.createQueryBuilder('favorite')
+ .andWhere('favorite.userId = :meId', { meId: me.id })
+ .leftJoinAndSelect('favorite.clip', 'clip');
+
+ const favorites = await query
+ .getMany();
+
+ return this.clipEntityService.packMany(favorites.map(x => x.clip!), me);
+ });
+ }
+}
diff --git a/packages/backend/src/server/api/endpoints/clips/show.ts b/packages/backend/src/server/api/endpoints/clips/show.ts
index e6d3f4f1f8..99d630a9b5 100644
--- a/packages/backend/src/server/api/endpoints/clips/show.ts
+++ b/packages/backend/src/server/api/endpoints/clips/show.ts
@@ -58,7 +58,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
throw new ApiError(meta.errors.noSuchClip);
}
- return await this.clipEntityService.pack(clip);
+ return await this.clipEntityService.pack(clip, me);
});
}
}
diff --git a/packages/backend/src/server/api/endpoints/clips/unfavorite.ts b/packages/backend/src/server/api/endpoints/clips/unfavorite.ts
new file mode 100644
index 0000000000..244843d50f
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/clips/unfavorite.ts
@@ -0,0 +1,65 @@
+import { Inject, Injectable } from '@nestjs/common';
+import type { ClipsRepository, ClipFavoritesRepository } from '@/models/index.js';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import { DI } from '@/di-symbols.js';
+import { ApiError } from '../../error.js';
+
+export const meta = {
+ tags: ['clip'],
+
+ requireCredential: true,
+
+ kind: 'write:clip-favorite',
+
+ errors: {
+ noSuchClip: {
+ message: 'No such clip.',
+ code: 'NO_SUCH_CLIP',
+ id: '2603966e-b865-426c-94a7-af4a01241dc1',
+ },
+
+ notFavorited: {
+ message: 'You have not favorited the clip.',
+ code: 'NOT_FAVORITED',
+ id: '90c3a9e8-b321-4dae-bf57-2bf79bbcc187',
+ },
+ },
+} as const;
+
+export const paramDef = {
+ type: 'object',
+ properties: {
+ clipId: { type: 'string', format: 'misskey:id' },
+ },
+ required: ['clipId'],
+} as const;
+
+// eslint-disable-next-line import/no-default-export
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> {
+ constructor(
+ @Inject(DI.clipsRepository)
+ private clipsRepository: ClipsRepository,
+
+ @Inject(DI.clipFavoritesRepository)
+ private clipFavoritesRepository: ClipFavoritesRepository,
+ ) {
+ super(meta, paramDef, async (ps, me) => {
+ const clip = await this.clipsRepository.findOneBy({ id: ps.clipId });
+ if (clip == null) {
+ throw new ApiError(meta.errors.noSuchClip);
+ }
+
+ const exist = await this.clipFavoritesRepository.findOneBy({
+ clipId: clip.id,
+ userId: me.id,
+ });
+
+ if (exist == null) {
+ throw new ApiError(meta.errors.notFavorited);
+ }
+
+ await this.clipFavoritesRepository.delete(exist.id);
+ });
+ }
+}
diff --git a/packages/backend/src/server/api/endpoints/clips/update.ts b/packages/backend/src/server/api/endpoints/clips/update.ts
index 597b67c442..a103c3f7d3 100644
--- a/packages/backend/src/server/api/endpoints/clips/update.ts
+++ b/packages/backend/src/server/api/endpoints/clips/update.ts
@@ -64,7 +64,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
isPublic: ps.isPublic,
});
- return await this.clipEntityService.pack(clip.id);
+ return await this.clipEntityService.pack(clip.id, me);
});
}
}
diff --git a/packages/backend/src/server/api/endpoints/drive/files.ts b/packages/backend/src/server/api/endpoints/drive/files.ts
index f6fad50fd9..4609307774 100644
--- a/packages/backend/src/server/api/endpoints/drive/files.ts
+++ b/packages/backend/src/server/api/endpoints/drive/files.ts
@@ -31,6 +31,7 @@ export const paramDef = {
untilId: { type: 'string', format: 'misskey:id' },
folderId: { type: 'string', format: 'misskey:id', nullable: true, default: null },
type: { type: 'string', nullable: true, pattern: /^[a-zA-Z\/\-*]+$/.toString().slice(1, -1) },
+ sort: { type: 'string', nullable: true, enum: ['+createdAt', '-createdAt', '+name', '-name', '+size', '-size'] },
},
required: [],
} as const;
@@ -63,6 +64,15 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
}
}
+ switch (ps.sort) {
+ case '+createdAt': query.orderBy('file.createdAt', 'DESC'); break;
+ case '-createdAt': query.orderBy('file.createdAt', 'ASC'); break;
+ case '+name': query.orderBy('file.name', 'DESC'); break;
+ case '-name': query.orderBy('file.name', 'ASC'); break;
+ case '+size': query.orderBy('file.size', 'DESC'); break;
+ case '-size': query.orderBy('file.size', 'ASC'); break;
+ }
+
const files = await query.take(ps.limit).getMany();
return await this.driveFileEntityService.packMany(files, { detail: false, self: true });
diff --git a/packages/backend/src/server/api/endpoints/emoji.ts b/packages/backend/src/server/api/endpoints/emoji.ts
new file mode 100644
index 0000000000..681d3e649e
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/emoji.ts
@@ -0,0 +1,56 @@
+import { IsNull } from 'typeorm';
+import { Inject, Injectable } from '@nestjs/common';
+import type { EmojisRepository } from '@/models/index.js';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js';
+import type { Config } from '@/config.js';
+import { DI } from '@/di-symbols.js';
+
+export const meta = {
+ tags: ['meta'],
+
+ requireCredential: false,
+ allowGet: true,
+ cacheSec: 3600,
+
+ res: {
+ type: 'object',
+ optional: false, nullable: false,
+ ref: 'EmojiDetailed',
+ },
+} as const;
+
+export const paramDef = {
+ type: 'object',
+ properties: {
+ name: {
+ type: 'string',
+ },
+ },
+ required: ['name'],
+} as const;
+
+// eslint-disable-next-line import/no-default-export
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> {
+ constructor(
+ @Inject(DI.config)
+ private config: Config,
+
+ @Inject(DI.emojisRepository)
+ private emojisRepository: EmojisRepository,
+
+ private emojiEntityService: EmojiEntityService,
+ ) {
+ super(meta, paramDef, async (ps, me) => {
+ const emoji = await this.emojisRepository.findOneOrFail({
+ where: {
+ name: ps.name,
+ host: IsNull(),
+ },
+ });
+
+ return this.emojiEntityService.packDetailed(emoji);
+ });
+ }
+}
diff --git a/packages/backend/src/server/api/endpoints/emojis.ts b/packages/backend/src/server/api/endpoints/emojis.ts
index 325b758358..0711fe4a57 100644
--- a/packages/backend/src/server/api/endpoints/emojis.ts
+++ b/packages/backend/src/server/api/endpoints/emojis.ts
@@ -23,24 +23,7 @@ export const meta = {
items: {
type: 'object',
optional: false, nullable: false,
- properties: {
- name: {
- type: 'string',
- optional: false, nullable: false,
- },
- aliases: {
- type: 'array',
- optional: false, nullable: false,
- items: {
- type: 'string',
- optional: false, nullable: false,
- },
- },
- category: {
- type: 'string',
- optional: false, nullable: true,
- },
- },
+ ref: 'EmojiSimple',
},
},
},
diff --git a/packages/backend/src/server/api/endpoints/federation/instances.ts b/packages/backend/src/server/api/endpoints/federation/instances.ts
index 60b24e9585..061c6eb5be 100644
--- a/packages/backend/src/server/api/endpoints/federation/instances.ts
+++ b/packages/backend/src/server/api/endpoints/federation/instances.ts
@@ -76,9 +76,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
if (typeof ps.blocked === 'boolean') {
const meta = await this.metaService.fetch(true);
if (ps.blocked) {
- query.andWhere('instance.host IN (:...blocks)', { blocks: meta.blockedHosts });
+ query.andWhere(meta.blockedHosts.length === 0 ? '1=0' : 'instance.host IN (:...blocks)', { blocks: meta.blockedHosts });
} else {
- query.andWhere('instance.host NOT IN (:...blocks)', { blocks: meta.blockedHosts });
+ query.andWhere(meta.blockedHosts.length === 0 ? '1=1' : 'instance.host NOT IN (:...blocks)', { blocks: meta.blockedHosts });
}
}
diff --git a/packages/backend/src/server/api/endpoints/i.ts b/packages/backend/src/server/api/endpoints/i.ts
index 6beef5ab85..a3e3e02a12 100644
--- a/packages/backend/src/server/api/endpoints/i.ts
+++ b/packages/backend/src/server/api/endpoints/i.ts
@@ -3,6 +3,7 @@ import type { UserProfilesRepository, UsersRepository } from '@/models/index.js'
import { Endpoint } from '@/server/api/endpoint-base.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { DI } from '@/di-symbols.js';
+import { ApiError } from '../error.js';
export const meta = {
tags: ['account'],
@@ -14,6 +15,15 @@ export const meta = {
optional: false, nullable: false,
ref: 'MeDetailed',
},
+
+ errors: {
+ userIsDeleted: {
+ message: 'User is deleted.',
+ code: 'USER_IS_DELETED',
+ id: 'e5b3b9f0-2b8f-4b9f-9c1f-8c5c1b2e1b1a',
+ kind: 'permission',
+ },
+ }
} as const;
export const paramDef = {
@@ -41,13 +51,17 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
const today = `${now.getFullYear()}/${now.getMonth() + 1}/${now.getDate()}`;
// 渡ってきている user はキャッシュされていて古い可能性があるので改めて取得
- const userProfile = await this.userProfilesRepository.findOneOrFail({
+ const userProfile = await this.userProfilesRepository.findOne({
where: {
userId: user.id,
},
relations: ['user'],
});
+ if (userProfile == null) {
+ throw new ApiError(meta.errors.userIsDeleted);
+ }
+
if (!userProfile.loggedInDates.includes(today)) {
this.userProfilesRepository.update({ userId: user.id }, {
loggedInDates: [...userProfile.loggedInDates, today],
diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts
index cdb314a873..37974ce2a3 100644
--- a/packages/backend/src/server/api/endpoints/meta.ts
+++ b/packages/backend/src/server/api/endpoints/meta.ts
@@ -276,7 +276,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
uri: this.config.url,
description: instance.description,
langs: instance.langs,
- tosUrl: instance.ToSUrl,
+ tosUrl: instance.termsOfServiceUrl,
repositoryUrl: instance.repositoryUrl,
feedbackUrl: instance.feedbackUrl,
disableRegistration: instance.disableRegistration,
@@ -315,8 +315,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
mediaProxy: this.config.mediaProxy,
...(ps.detail ? {
- pinnedPages: instance.pinnedPages,
- pinnedClipId: instance.pinnedClipId,
cacheRemoteFiles: instance.cacheRemoteFiles,
requireSetup: (await this.usersRepository.countBy({
host: IsNull(),
diff --git a/packages/backend/src/server/api/endpoints/notes/clips.ts b/packages/backend/src/server/api/endpoints/notes/clips.ts
index d5caec6e1d..0a5542f497 100644
--- a/packages/backend/src/server/api/endpoints/notes/clips.ts
+++ b/packages/backend/src/server/api/endpoints/notes/clips.ts
@@ -4,8 +4,8 @@ import type { ClipNotesRepository, ClipsRepository } from '@/models/index.js';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { ClipEntityService } from '@/core/entities/ClipEntityService.js';
import { DI } from '@/di-symbols.js';
-import { ApiError } from '../../error.js';
import { GetterService } from '@/server/api/GetterService.js';
+import { ApiError } from '../../error.js';
export const meta = {
tags: ['clips', 'notes'],
@@ -67,7 +67,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
isPublic: true,
});
- return await Promise.all(clips.map(x => this.clipEntityService.pack(x)));
+ return await this.clipEntityService.packMany(clips, me);
});
}
}
diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts
index 786ad103b0..69fafcb9c7 100644
--- a/packages/backend/src/server/api/endpoints/notes/create.ts
+++ b/packages/backend/src/server/api/endpoints/notes/create.ts
@@ -97,6 +97,7 @@ export const paramDef = {
} },
cw: { type: 'string', nullable: true, maxLength: 100 },
localOnly: { type: 'boolean', default: false },
+ reactionAcceptance: { type: 'string', nullable: true, enum: [null, 'likeOnly', 'likeOnlyForRemote'], default: null },
noExtractMentions: { type: 'boolean', default: false },
noExtractHashtags: { type: 'boolean', default: false },
noExtractEmojis: { type: 'boolean', default: false },
@@ -110,7 +111,7 @@ export const paramDef = {
type: 'string',
minLength: 1,
maxLength: MAX_NOTE_TEXT_LENGTH,
- nullable: false
+ nullable: false,
},
fileIds: {
type: 'array',
@@ -280,6 +281,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
renote,
cw: ps.cw,
localOnly: ps.localOnly,
+ reactionAcceptance: ps.reactionAcceptance,
visibility: ps.visibility,
visibleUsers,
channel,
diff --git a/packages/backend/src/server/api/endpoints/notes/featured.ts b/packages/backend/src/server/api/endpoints/notes/featured.ts
index cf939f6631..6bf17b222a 100644
--- a/packages/backend/src/server/api/endpoints/notes/featured.ts
+++ b/packages/backend/src/server/api/endpoints/notes/featured.ts
@@ -71,7 +71,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
let notes = await query
.orderBy('note.score', 'DESC')
- .take(50)
+ .take(100)
.getMany();
notes.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
diff --git a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts
index 5d0cdc3fca..9118d33936 100644
--- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts
@@ -89,6 +89,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
this.queryService.generateMutedUserQuery(query, me);
this.queryService.generateMutedNoteQuery(query, me);
this.queryService.generateBlockedUserQuery(query, me);
+ this.queryService.generateMutedUserRenotesQueryForNotes(query, me);
}
if (ps.withFiles) {
diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts
index 2819abb125..8a7ec65ab4 100644
--- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts
@@ -8,6 +8,7 @@ import { MetaService } from '@/core/MetaService.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { DI } from '@/di-symbols.js';
import { RoleService } from '@/core/RoleService.js';
+import { IdService } from '@/core/IdService.js';
import { ApiError } from '../../error.js';
export const meta = {
@@ -69,6 +70,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
private metaService: MetaService,
private roleService: RoleService,
private activeUsersChart: ActiveUsersChart,
+ private idService: IdService,
) {
super(meta, paramDef, async (ps, me) => {
const policies = await this.roleService.getUserPolicies(me.id);
@@ -83,7 +85,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'),
ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate)
- .andWhere('note.createdAt > :minDate', { minDate: new Date(Date.now() - (1000 * 60 * 60 * 24 * 30)) }) // 30日前まで
+ .andWhere('note.id > :minId', { minId: this.idService.genId(new Date(Date.now() - (1000 * 60 * 60 * 24 * 10))) }) // 10日前まで
.andWhere(new Brackets(qb => {
qb.where(`((note.userId IN (${ followingQuery.getQuery() })) OR (note.userId = :meId))`, { meId: me.id })
.orWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)');
@@ -107,6 +109,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
this.queryService.generateMutedUserQuery(query, me);
this.queryService.generateMutedNoteQuery(query, me);
this.queryService.generateBlockedUserQuery(query, me);
+ this.queryService.generateMutedUserRenotesQueryForNotes(query, me);
if (ps.includeMyRenotes === false) {
query.andWhere(new Brackets(qb => {
diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts
index 18ed6d4e21..8c1c07a9f4 100644
--- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts
@@ -8,6 +8,7 @@ import { MetaService } from '@/core/MetaService.js';
import ActiveUsersChart from '@/core/chart/charts/active-users.js';
import { DI } from '@/di-symbols.js';
import { RoleService } from '@/core/RoleService.js';
+import { IdService } from '@/core/IdService.js';
import { ApiError } from '../../error.js';
export const meta = {
@@ -65,6 +66,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
private metaService: MetaService,
private roleService: RoleService,
private activeUsersChart: ActiveUsersChart,
+ private idService: IdService,
) {
super(meta, paramDef, async (ps, me) => {
const policies = await this.roleService.getUserPolicies(me ? me.id : null);
@@ -75,7 +77,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
//#region Construct query
const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'),
ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate)
- .andWhere('note.createdAt > :minDate', { minDate: new Date(Date.now() - (1000 * 60 * 60 * 24 * 30)) }) // 30日前まで
+ .andWhere('note.id > :minId', { minId: this.idService.genId(new Date(Date.now() - (1000 * 60 * 60 * 24 * 10))) }) // 10日前まで
.andWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)')
.innerJoinAndSelect('note.user', 'user')
.leftJoinAndSelect('user.avatar', 'avatar')
@@ -95,6 +97,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
if (me) this.queryService.generateMutedUserQuery(query, me);
if (me) this.queryService.generateMutedNoteQuery(query, me);
if (me) this.queryService.generateBlockedUserQuery(query, me);
+ if (me) this.queryService.generateMutedUserRenotesQueryForNotes(query, me);
if (ps.withFiles) {
query.andWhere('note.fileIds != \'{}\'');
diff --git a/packages/backend/src/server/api/endpoints/notes/polls/vote.ts b/packages/backend/src/server/api/endpoints/notes/polls/vote.ts
index b9e06a7834..2a44dc537e 100644
--- a/packages/backend/src/server/api/endpoints/notes/polls/vote.ts
+++ b/packages/backend/src/server/api/endpoints/notes/polls/vote.ts
@@ -8,7 +8,6 @@ import { QueueService } from '@/core/QueueService.js';
import { PollService } from '@/core/PollService.js';
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
-import { CreateNotificationService } from '@/core/CreateNotificationService.js';
import { DI } from '@/di-symbols.js';
import { UserBlockingService } from '@/core/UserBlockingService.js';
import { ApiError } from '../../../error.js';
@@ -89,7 +88,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
private pollService: PollService,
private apRendererService: ApRendererService,
private globalEventService: GlobalEventService,
- private createNotificationService: CreateNotificationService,
private userBlockingService: UserBlockingService,
) {
super(meta, paramDef, async (ps, me) => {
@@ -161,7 +159,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
if (note.userHost != null) {
const pollOwner = await this.usersRepository.findOneByOrFail({ id: note.userId }) as RemoteUser;
- this.queueService.deliver(me, this.apRendererService.addContext(await this.apRendererService.renderVote(me, vote, note, poll, pollOwner)), pollOwner.inbox);
+ this.queueService.deliver(me, this.apRendererService.addContext(await this.apRendererService.renderVote(me, vote, note, poll, pollOwner)), pollOwner.inbox, false);
}
// リモートフォロワーにUpdate配信
diff --git a/packages/backend/src/server/api/endpoints/notes/search.ts b/packages/backend/src/server/api/endpoints/notes/search.ts
index ef47a3004d..5db5b6267f 100644
--- a/packages/backend/src/server/api/endpoints/notes/search.ts
+++ b/packages/backend/src/server/api/endpoints/notes/search.ts
@@ -6,6 +6,8 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import type { Config } from '@/config.js';
import { DI } from '@/di-symbols.js';
import { sqlLikeEscape } from '@/misc/sql-like-escape.js';
+import { RoleService } from '@/core/RoleService.js';
+import { ApiError } from '../../error.js';
export const meta = {
tags: ['notes'],
@@ -23,6 +25,11 @@ export const meta = {
},
errors: {
+ unavailable: {
+ message: 'Search of notes unavailable.',
+ code: 'UNAVAILABLE',
+ id: '0b44998d-77aa-4427-80d0-d2c9b8523011',
+ },
},
} as const;
@@ -59,8 +66,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
private noteEntityService: NoteEntityService,
private queryService: QueryService,
+ private roleService: RoleService,
) {
super(meta, paramDef, async (ps, me) => {
+ const policies = await this.roleService.getUserPolicies(me ? me.id : null);
+ if (!policies.canSearchNotes) {
+ throw new ApiError(meta.errors.unavailable);
+ }
+
const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId);
if (ps.userId) {
diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts
index e6de087c4a..d9e72d2603 100644
--- a/packages/backend/src/server/api/endpoints/notes/timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts
@@ -6,6 +6,7 @@ import { QueryService } from '@/core/QueryService.js';
import ActiveUsersChart from '@/core/chart/charts/active-users.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { DI } from '@/di-symbols.js';
+import { IdService } from '@/core/IdService.js';
export const meta = {
tags: ['notes'],
@@ -56,6 +57,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
private noteEntityService: NoteEntityService,
private queryService: QueryService,
private activeUsersChart: ActiveUsersChart,
+ private idService: IdService,
) {
super(meta, paramDef, async (ps, me) => {
const followees = await this.followingsRepository.createQueryBuilder('following')
@@ -66,7 +68,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
//#region Construct query
const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'),
ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate)
- .andWhere('note.createdAt > :minDate', { minDate: new Date(Date.now() - (1000 * 60 * 60 * 24 * 30)) }) // 30日前まで
+ .andWhere('note.id > :minId', { minId: this.idService.genId(new Date(Date.now() - (1000 * 60 * 60 * 24 * 10))) }) // 10日前まで
.innerJoinAndSelect('note.user', 'user')
.leftJoinAndSelect('user.avatar', 'avatar')
.leftJoinAndSelect('user.banner', 'banner')
@@ -93,6 +95,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
this.queryService.generateMutedUserQuery(query, me);
this.queryService.generateMutedNoteQuery(query, me);
this.queryService.generateBlockedUserQuery(query, me);
+ this.queryService.generateMutedUserRenotesQueryForNotes(query, me);
if (ps.includeMyRenotes === false) {
query.andWhere(new Brackets(qb => {
diff --git a/packages/backend/src/server/api/endpoints/notifications/create.ts b/packages/backend/src/server/api/endpoints/notifications/create.ts
index 2e63eee263..4102a924ad 100644
--- a/packages/backend/src/server/api/endpoints/notifications/create.ts
+++ b/packages/backend/src/server/api/endpoints/notifications/create.ts
@@ -1,6 +1,6 @@
import { Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
-import { CreateNotificationService } from '@/core/CreateNotificationService.js';
+import { NotificationService } from '@/core/NotificationService.js';
export const meta = {
tags: ['notifications'],
@@ -27,10 +27,10 @@ export const paramDef = {
@Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> {
constructor(
- private createNotificationService: CreateNotificationService,
+ private notificationService: NotificationService,
) {
super(meta, paramDef, async (ps, user, token) => {
- this.createNotificationService.createNotification(user.id, 'app', {
+ this.notificationService.createNotification(user.id, 'app', {
appAccessTokenId: token ? token.id : null,
customBody: ps.body,
customHeader: ps.header,
diff --git a/packages/backend/src/server/api/endpoints/renote-mute/create.ts b/packages/backend/src/server/api/endpoints/renote-mute/create.ts
new file mode 100644
index 0000000000..051a005b67
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/renote-mute/create.ts
@@ -0,0 +1,99 @@
+import { Inject, Injectable } from '@nestjs/common';
+import ms from 'ms';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import { IdService } from '@/core/IdService.js';
+import type { RenoteMutingsRepository } from '@/models/index.js';
+import type { RenoteMuting } from '@/models/entities/RenoteMuting.js';
+import { GlobalEventService } from '@/core/GlobalEventService.js';
+import { DI } from '@/di-symbols.js';
+import { GetterService } from '@/server/api/GetterService.js';
+import { ApiError } from '../../error.js';
+
+export const meta = {
+ tags: ['account'],
+
+ requireCredential: true,
+
+ kind: 'write:mutes',
+
+ limit: {
+ duration: ms('1hour'),
+ max: 20,
+ },
+
+ errors: {
+ noSuchUser: {
+ message: 'No such user.',
+ code: 'NO_SUCH_USER',
+ id: '5e0a5dff-1e94-4202-87ae-4d9c89eb2271',
+ },
+
+ muteeIsYourself: {
+ message: 'Mutee is yourself.',
+ code: 'MUTEE_IS_YOURSELF',
+ id: '37285718-52f7-4aef-b7de-c38b8e8a8420',
+ },
+
+ alreadyMuting: {
+ message: 'You are already muting that user.',
+ code: 'ALREADY_MUTING',
+ id: 'ccfecbe4-1f1c-4fc2-8a3d-c3ffee61cb7b',
+ },
+ },
+} as const;
+
+export const paramDef = {
+ type: 'object',
+ properties: {
+ userId: { type: 'string', format: 'misskey:id' },
+ },
+ required: ['userId'],
+} as const;
+
+// eslint-disable-next-line import/no-default-export
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> {
+ constructor(
+ @Inject(DI.renoteMutingsRepository)
+ private renoteMutingsRepository: RenoteMutingsRepository,
+
+ private globalEventService: GlobalEventService,
+ private getterService: GetterService,
+ private idService: IdService,
+ ) {
+ super(meta, paramDef, async (ps, me) => {
+ const muter = me;
+
+ // 自分自身
+ if (me.id === ps.userId) {
+ throw new ApiError(meta.errors.muteeIsYourself);
+ }
+
+ // Get mutee
+ const mutee = await getterService.getUser(ps.userId).catch(err => {
+ if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser);
+ throw err;
+ });
+
+ // Check if already muting
+ const exist = await this.renoteMutingsRepository.findOneBy({
+ muterId: muter.id,
+ muteeId: mutee.id,
+ });
+
+ if (exist != null) {
+ throw new ApiError(meta.errors.alreadyMuting);
+ }
+
+ // Create mute
+ await this.renoteMutingsRepository.insert({
+ id: this.idService.genId(),
+ createdAt: new Date(),
+ muterId: muter.id,
+ muteeId: mutee.id,
+ } as RenoteMuting);
+
+ // publishUserEvent(user.id, 'mute', mutee);
+ });
+ }
+}
diff --git a/packages/backend/src/server/api/endpoints/renote-mute/delete.ts b/packages/backend/src/server/api/endpoints/renote-mute/delete.ts
new file mode 100644
index 0000000000..51a895fb7e
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/renote-mute/delete.ts
@@ -0,0 +1,87 @@
+import { Inject, Injectable } from '@nestjs/common';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import type { RenoteMutingsRepository } from '@/models/index.js';
+import { GlobalEventService } from '@/core/GlobalEventService.js';
+import { DI } from '@/di-symbols.js';
+import { GetterService } from '@/server/api/GetterService.js';
+import { ApiError } from '../../error.js';
+
+export const meta = {
+ tags: ['account'],
+
+ requireCredential: true,
+
+ kind: 'write:mutes',
+
+ errors: {
+ noSuchUser: {
+ message: 'No such user.',
+ code: 'NO_SUCH_USER',
+ id: '9b6728cf-638c-4aa1-bedb-e07d8101474d',
+ },
+
+ muteeIsYourself: {
+ message: 'Mutee is yourself.',
+ code: 'MUTEE_IS_YOURSELF',
+ id: '619b1314-0850-4597-a242-e245f3da42af',
+ },
+
+ notMuting: {
+ message: 'You are not muting that user.',
+ code: 'NOT_MUTING',
+ id: '2e4ef874-8bf0-4b4b-b069-4598f6d05817',
+ },
+ },
+} as const;
+
+export const paramDef = {
+ type: 'object',
+ properties: {
+ userId: { type: 'string', format: 'misskey:id' },
+ },
+ required: ['userId'],
+} as const;
+
+// eslint-disable-next-line import/no-default-export
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> {
+ constructor(
+ @Inject(DI.renoteMutingsRepository)
+ private renoteMutingsRepository: RenoteMutingsRepository,
+
+ private globalEventService: GlobalEventService,
+ private getterService: GetterService,
+ ) {
+ super(meta, paramDef, async (ps, me) => {
+ const muter = me;
+
+ // Check if the mutee is yourself
+ if (me.id === ps.userId) {
+ throw new ApiError(meta.errors.muteeIsYourself);
+ }
+
+ // Get mutee
+ const mutee = await this.getterService.getUser(ps.userId).catch(err => {
+ if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser);
+ throw err;
+ });
+
+ // Check not muting
+ const exist = await this.renoteMutingsRepository.findOneBy({
+ muterId: muter.id,
+ muteeId: mutee.id,
+ });
+
+ if (exist == null) {
+ throw new ApiError(meta.errors.notMuting);
+ }
+
+ // Delete mute
+ await this.renoteMutingsRepository.delete({
+ id: exist.id,
+ });
+
+ // publishUserEvent(user.id, 'unmute', mutee);
+ });
+ }
+}
diff --git a/packages/backend/src/server/api/endpoints/renote-mute/list.ts b/packages/backend/src/server/api/endpoints/renote-mute/list.ts
new file mode 100644
index 0000000000..b2d7addb64
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/renote-mute/list.ts
@@ -0,0 +1,57 @@
+import { Inject, Injectable } from '@nestjs/common';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import type { RenoteMutingsRepository } from '@/models/index.js';
+import { QueryService } from '@/core/QueryService.js';
+import { RenoteMutingEntityService } from '@/core/entities/RenoteMutingEntityService.js';
+import { DI } from '@/di-symbols.js';
+
+export const meta = {
+ tags: ['account'],
+
+ requireCredential: true,
+
+ kind: 'read:mutes',
+
+ res: {
+ type: 'array',
+ optional: false, nullable: false,
+ items: {
+ type: 'object',
+ optional: false, nullable: false,
+ ref: 'RenoteMuting',
+ },
+ },
+} as const;
+
+export const paramDef = {
+ type: 'object',
+ properties: {
+ limit: { type: 'integer', minimum: 1, maximum: 100, default: 30 },
+ sinceId: { type: 'string', format: 'misskey:id' },
+ untilId: { type: 'string', format: 'misskey:id' },
+ },
+ required: [],
+} as const;
+
+// eslint-disable-next-line import/no-default-export
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> {
+ constructor(
+ @Inject(DI.renoteMutingsRepository)
+ private renoteMutingsRepository: RenoteMutingsRepository,
+
+ private renoteMutingEntityService: RenoteMutingEntityService,
+ private queryService: QueryService,
+ ) {
+ super(meta, paramDef, async (ps, me) => {
+ const query = this.queryService.makePaginationQuery(this.renoteMutingsRepository.createQueryBuilder('muting'), ps.sinceId, ps.untilId)
+ .andWhere('muting.muterId = :meId', { meId: me.id });
+
+ const mutings = await query
+ .take(ps.limit)
+ .getMany();
+
+ return await this.renoteMutingEntityService.packMany(mutings, me);
+ });
+ }
+}
diff --git a/packages/backend/src/server/api/endpoints/users/clips.ts b/packages/backend/src/server/api/endpoints/users/clips.ts
index e3fd0920c9..c5aa93baaf 100644
--- a/packages/backend/src/server/api/endpoints/users/clips.ts
+++ b/packages/backend/src/server/api/endpoints/users/clips.ts
@@ -51,7 +51,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
.take(ps.limit)
.getMany();
- return await this.clipEntityService.packMany(clips);
+ return await this.clipEntityService.packMany(clips, me);
});
}
}
diff --git a/packages/backend/src/server/api/endpoints/users/relation.ts b/packages/backend/src/server/api/endpoints/users/relation.ts
index ac9104bf92..3267c18846 100644
--- a/packages/backend/src/server/api/endpoints/users/relation.ts
+++ b/packages/backend/src/server/api/endpoints/users/relation.ts
@@ -50,6 +50,10 @@ export const meta = {
type: 'boolean',
optional: false, nullable: false,
},
+ isRenoteMuted: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
},
},
{
@@ -91,6 +95,10 @@ export const meta = {
type: 'boolean',
optional: false, nullable: false,
},
+ isRenoteMuted: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
},
},
},
diff --git a/packages/backend/src/server/api/endpoints/users/show.ts b/packages/backend/src/server/api/endpoints/users/show.ts
index 29f24b045a..ba432c273b 100644
--- a/packages/backend/src/server/api/endpoints/users/show.ts
+++ b/packages/backend/src/server/api/endpoints/users/show.ts
@@ -48,6 +48,7 @@ export const meta = {
message: 'No such user.',
code: 'NO_SUCH_USER',
id: '4362f8dc-731f-4ad8-a694-be5a88922a24',
+ httpStatusCode: 404,
},
},
} as const;