;
+type PropertiesToUnion = p['properties'] extends NonNullable ? keyof p['properties'] : never;
+
type TypeStringef = 'null' | 'boolean' | 'integer' | 'number' | 'string' | 'array' | 'object' | 'any';
type StringDefToType =
T extends 'null' ? null :
@@ -119,6 +124,7 @@ export interface Schema extends OfSchema {
readonly example?: any;
readonly format?: string;
readonly ref?: keyof typeof refs;
+ readonly selfRef?: boolean;
readonly enum?: ReadonlyArray;
readonly default?: (this['type'] extends TypeStringef ? StringDefToType : any) | null;
readonly maxLength?: number;
diff --git a/packages/backend/src/models/Announcement.ts b/packages/backend/src/models/Announcement.ts
index 8f8be88fed..c2d9e9878c 100644
--- a/packages/backend/src/models/Announcement.ts
+++ b/packages/backend/src/models/Announcement.ts
@@ -38,7 +38,7 @@ export class MiAnnouncement {
length: 256, nullable: false,
default: 'info',
})
- public icon: string;
+ public icon: 'info' | 'warning' | 'error' | 'success';
// normal ... お知らせページ掲載
// banner ... お知らせページ掲載 + バナー表示
@@ -47,7 +47,7 @@ export class MiAnnouncement {
length: 256, nullable: false,
default: 'normal',
})
- public display: string;
+ public display: 'normal' | 'banner' | 'dialog';
@Column('boolean', {
default: false,
diff --git a/packages/backend/src/models/Meta.ts b/packages/backend/src/models/Meta.ts
index 3265e85dd7..bcde2db0b7 100644
--- a/packages/backend/src/models/Meta.ts
+++ b/packages/backend/src/models/Meta.ts
@@ -76,6 +76,11 @@ export class MiMeta {
})
public sensitiveWords: string[];
+ @Column('varchar', {
+ length: 1024, array: true, default: '{}',
+ })
+ public prohibitedWords: string[];
+
@Column('varchar', {
length: 1024, array: true, default: '{}',
})
diff --git a/packages/backend/src/models/json-schema/announcement.ts b/packages/backend/src/models/json-schema/announcement.ts
index 78a98872b2..57fd7d605d 100644
--- a/packages/backend/src/models/json-schema/announcement.ts
+++ b/packages/backend/src/models/json-schema/announcement.ts
@@ -37,10 +37,12 @@ export const packedAnnouncementSchema = {
icon: {
type: 'string',
optional: false, nullable: false,
+ enum: ['info', 'warning', 'error', 'success'],
},
display: {
type: 'string',
optional: false, nullable: false,
+ enum: ['dialog', 'normal', 'banner'],
},
needConfirmationToRead: {
type: 'boolean',
diff --git a/packages/backend/src/models/json-schema/blocking.ts b/packages/backend/src/models/json-schema/blocking.ts
index 0b58f1f8d7..1b3227d455 100644
--- a/packages/backend/src/models/json-schema/blocking.ts
+++ b/packages/backend/src/models/json-schema/blocking.ts
@@ -25,7 +25,7 @@ export const packedBlockingSchema = {
blockee: {
type: 'object',
optional: false, nullable: false,
- ref: 'UserDetailed',
+ ref: 'UserDetailedNotMe',
},
},
} as const;
diff --git a/packages/backend/src/models/json-schema/emoji.ts b/packages/backend/src/models/json-schema/emoji.ts
index 2c1e8f8afd..995287709f 100644
--- a/packages/backend/src/models/json-schema/emoji.ts
+++ b/packages/backend/src/models/json-schema/emoji.ts
@@ -27,6 +27,10 @@ export const packedEmojiSimpleSchema = {
type: 'string',
optional: false, nullable: false,
},
+ localOnly: {
+ type: 'boolean',
+ optional: true, nullable: false,
+ },
isSensitive: {
type: 'boolean',
optional: true, nullable: false,
diff --git a/packages/backend/src/models/json-schema/following.ts b/packages/backend/src/models/json-schema/following.ts
index e92cff20a1..dd3234ee5a 100644
--- a/packages/backend/src/models/json-schema/following.ts
+++ b/packages/backend/src/models/json-schema/following.ts
@@ -30,12 +30,12 @@ export const packedFollowingSchema = {
followee: {
type: 'object',
optional: true, nullable: false,
- ref: 'UserDetailed',
+ ref: 'UserDetailedNotMe',
},
follower: {
type: 'object',
optional: true, nullable: false,
- ref: 'UserDetailed',
+ ref: 'UserDetailedNotMe',
},
},
} as const;
diff --git a/packages/backend/src/models/json-schema/muting.ts b/packages/backend/src/models/json-schema/muting.ts
index dde9dc0288..c3d0bb603c 100644
--- a/packages/backend/src/models/json-schema/muting.ts
+++ b/packages/backend/src/models/json-schema/muting.ts
@@ -30,7 +30,7 @@ export const packedMutingSchema = {
mutee: {
type: 'object',
optional: false, nullable: false,
- ref: 'UserDetailed',
+ ref: 'UserDetailedNotMe',
},
},
} as const;
diff --git a/packages/backend/src/models/json-schema/note.ts b/packages/backend/src/models/json-schema/note.ts
index 2b7722129b..929f697e8a 100644
--- a/packages/backend/src/models/json-schema/note.ts
+++ b/packages/backend/src/models/json-schema/note.ts
@@ -69,6 +69,7 @@ export const packedNoteSchema = {
visibility: {
type: 'string',
optional: false, nullable: false,
+ enum: ['public', 'home', 'followers', 'specified'],
},
mentions: {
type: 'array',
@@ -117,6 +118,48 @@ export const packedNoteSchema = {
poll: {
type: 'object',
optional: true, nullable: true,
+ properties: {
+ expiresAt: {
+ type: 'string',
+ optional: true, nullable: true,
+ format: 'date-time',
+ },
+ multiple: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ choices: {
+ type: 'array',
+ optional: false, nullable: false,
+ items: {
+ type: 'object',
+ optional: false, nullable: false,
+ properties: {
+ isVoted: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ text: {
+ type: 'string',
+ optional: false, nullable: false,
+ },
+ votes: {
+ type: 'number',
+ optional: false, nullable: false,
+ },
+ },
+ },
+ },
+ },
+ },
+ emojis: {
+ type: 'object',
+ optional: true, nullable: false,
+ additionalProperties: {
+ anyOf: [{
+ type: 'string',
+ }],
+ },
},
channelId: {
type: 'string',
@@ -162,9 +205,23 @@ export const packedNoteSchema = {
type: 'string',
optional: false, nullable: true,
},
+ reactionEmojis: {
+ type: 'object',
+ optional: false, nullable: false,
+ additionalProperties: {
+ anyOf: [{
+ type: 'string',
+ }],
+ },
+ },
reactions: {
type: 'object',
optional: false, nullable: false,
+ additionalProperties: {
+ anyOf: [{
+ type: 'number',
+ }],
+ },
},
renoteCount: {
type: 'number',
@@ -196,7 +253,7 @@ export const packedNoteSchema = {
},
myReaction: {
- type: 'object',
+ type: 'string',
optional: true, nullable: true,
},
},
diff --git a/packages/backend/src/models/json-schema/notification.ts b/packages/backend/src/models/json-schema/notification.ts
index c6d6e84317..6286950de5 100644
--- a/packages/backend/src/models/json-schema/notification.ts
+++ b/packages/backend/src/models/json-schema/notification.ts
@@ -5,7 +5,7 @@
import { notificationTypes } from '@/types.js';
-export const packedNotificationSchema = {
+const baseSchema = {
type: 'object',
properties: {
id: {
@@ -23,68 +23,368 @@ export const packedNotificationSchema = {
optional: false, nullable: false,
enum: [...notificationTypes, 'reaction:grouped', 'renote:grouped'],
},
- user: {
- type: 'object',
- ref: 'UserLite',
- optional: true, nullable: true,
- },
- userId: {
- type: 'string',
- optional: true, nullable: true,
- format: 'id',
- },
- note: {
- type: 'object',
- ref: 'Note',
- optional: true, nullable: true,
- },
- reaction: {
- type: 'string',
- optional: true, nullable: true,
- },
- achievement: {
- type: 'string',
- optional: true, nullable: false,
- },
- body: {
- type: 'string',
- optional: true, nullable: true,
- },
- header: {
- type: 'string',
- optional: true, nullable: true,
- },
- icon: {
- type: 'string',
- optional: true, nullable: true,
- },
- reactions: {
- type: 'array',
- optional: true, nullable: true,
- items: {
- type: 'object',
- properties: {
- user: {
- type: 'object',
- ref: 'UserLite',
- optional: false, nullable: false,
- },
- reaction: {
- type: 'string',
- optional: false, nullable: false,
- },
- },
- required: ['user', 'reaction'],
+ },
+} as const;
+
+export const packedNotificationSchema = {
+ type: 'object',
+ oneOf: [{
+ type: 'object',
+ properties: {
+ ...baseSchema.properties,
+ type: {
+ type: 'string',
+ optional: false, nullable: false,
+ enum: ['note'],
},
- },
- users: {
- type: 'array',
- optional: true, nullable: true,
- items: {
+ user: {
type: 'object',
ref: 'UserLite',
optional: false, nullable: false,
},
+ userId: {
+ type: 'string',
+ optional: false, nullable: false,
+ format: 'id',
+ },
+ note: {
+ type: 'object',
+ ref: 'Note',
+ optional: false, nullable: false,
+ },
},
- },
+ }, {
+ type: 'object',
+ properties: {
+ ...baseSchema.properties,
+ type: {
+ type: 'string',
+ optional: false, nullable: false,
+ enum: ['mention'],
+ },
+ user: {
+ type: 'object',
+ ref: 'UserLite',
+ optional: false, nullable: false,
+ },
+ userId: {
+ type: 'string',
+ optional: false, nullable: false,
+ format: 'id',
+ },
+ note: {
+ type: 'object',
+ ref: 'Note',
+ optional: false, nullable: false,
+ },
+ },
+ }, {
+ type: 'object',
+ properties: {
+ ...baseSchema.properties,
+ type: {
+ type: 'string',
+ optional: false, nullable: false,
+ enum: ['reply'],
+ },
+ user: {
+ type: 'object',
+ ref: 'UserLite',
+ optional: false, nullable: false,
+ },
+ userId: {
+ type: 'string',
+ optional: false, nullable: false,
+ format: 'id',
+ },
+ note: {
+ type: 'object',
+ ref: 'Note',
+ optional: false, nullable: false,
+ },
+ },
+ }, {
+ type: 'object',
+ properties: {
+ ...baseSchema.properties,
+ type: {
+ type: 'string',
+ optional: false, nullable: false,
+ enum: ['renote'],
+ },
+ user: {
+ type: 'object',
+ ref: 'UserLite',
+ optional: false, nullable: false,
+ },
+ userId: {
+ type: 'string',
+ optional: false, nullable: false,
+ format: 'id',
+ },
+ note: {
+ type: 'object',
+ ref: 'Note',
+ optional: false, nullable: false,
+ },
+ },
+ }, {
+ type: 'object',
+ properties: {
+ ...baseSchema.properties,
+ type: {
+ type: 'string',
+ optional: false, nullable: false,
+ enum: ['quote'],
+ },
+ user: {
+ type: 'object',
+ ref: 'UserLite',
+ optional: false, nullable: false,
+ },
+ userId: {
+ type: 'string',
+ optional: false, nullable: false,
+ format: 'id',
+ },
+ note: {
+ type: 'object',
+ ref: 'Note',
+ optional: false, nullable: false,
+ },
+ },
+ }, {
+ type: 'object',
+ properties: {
+ ...baseSchema.properties,
+ type: {
+ type: 'string',
+ optional: false, nullable: false,
+ enum: ['reaction'],
+ },
+ user: {
+ type: 'object',
+ ref: 'UserLite',
+ optional: false, nullable: false,
+ },
+ userId: {
+ type: 'string',
+ optional: false, nullable: false,
+ format: 'id',
+ },
+ note: {
+ type: 'object',
+ ref: 'Note',
+ optional: false, nullable: false,
+ },
+ reaction: {
+ type: 'string',
+ optional: false, nullable: false,
+ },
+ },
+ }, {
+ type: 'object',
+ properties: {
+ ...baseSchema.properties,
+ type: {
+ type: 'string',
+ optional: false, nullable: false,
+ enum: ['pollEnded'],
+ },
+ user: {
+ type: 'object',
+ ref: 'UserLite',
+ optional: false, nullable: false,
+ },
+ userId: {
+ type: 'string',
+ optional: false, nullable: false,
+ format: 'id',
+ },
+ note: {
+ type: 'object',
+ ref: 'Note',
+ optional: false, nullable: false,
+ },
+ },
+ }, {
+ type: 'object',
+ properties: {
+ ...baseSchema.properties,
+ type: {
+ type: 'string',
+ optional: false, nullable: false,
+ enum: ['follow'],
+ },
+ user: {
+ type: 'object',
+ ref: 'UserLite',
+ optional: false, nullable: false,
+ },
+ userId: {
+ type: 'string',
+ optional: false, nullable: false,
+ format: 'id',
+ },
+ },
+ }, {
+ type: 'object',
+ properties: {
+ ...baseSchema.properties,
+ type: {
+ type: 'string',
+ optional: false, nullable: false,
+ enum: ['receiveFollowRequest'],
+ },
+ user: {
+ type: 'object',
+ ref: 'UserLite',
+ optional: false, nullable: false,
+ },
+ userId: {
+ type: 'string',
+ optional: false, nullable: false,
+ format: 'id',
+ },
+ },
+ }, {
+ type: 'object',
+ properties: {
+ ...baseSchema.properties,
+ type: {
+ type: 'string',
+ optional: false, nullable: false,
+ enum: ['followRequestAccepted'],
+ },
+ user: {
+ type: 'object',
+ ref: 'UserLite',
+ optional: false, nullable: false,
+ },
+ userId: {
+ type: 'string',
+ optional: false, nullable: false,
+ format: 'id',
+ },
+ },
+ }, {
+ type: 'object',
+ properties: {
+ ...baseSchema.properties,
+ type: {
+ type: 'string',
+ optional: false, nullable: false,
+ enum: ['roleAssigned'],
+ },
+ role: {
+ type: 'object',
+ ref: 'Role',
+ optional: false, nullable: false,
+ },
+ },
+ }, {
+ type: 'object',
+ properties: {
+ ...baseSchema.properties,
+ type: {
+ type: 'string',
+ optional: false, nullable: false,
+ enum: ['achievementEarned'],
+ },
+ achievement: {
+ type: 'string',
+ optional: false, nullable: false,
+ },
+ },
+ }, {
+ type: 'object',
+ properties: {
+ ...baseSchema.properties,
+ type: {
+ type: 'string',
+ optional: false, nullable: false,
+ enum: ['app'],
+ },
+ body: {
+ type: 'string',
+ optional: false, nullable: false,
+ },
+ header: {
+ type: 'string',
+ optional: false, nullable: false,
+ },
+ icon: {
+ type: 'string',
+ optional: false, nullable: false,
+ },
+ },
+ }, {
+ type: 'object',
+ properties: {
+ ...baseSchema.properties,
+ type: {
+ type: 'string',
+ optional: false, nullable: false,
+ enum: ['reaction:grouped'],
+ },
+ note: {
+ type: 'object',
+ ref: 'Note',
+ optional: false, nullable: false,
+ },
+ reactions: {
+ type: 'array',
+ optional: false, nullable: false,
+ items: {
+ type: 'object',
+ properties: {
+ user: {
+ type: 'object',
+ ref: 'UserLite',
+ optional: false, nullable: false,
+ },
+ reaction: {
+ type: 'string',
+ optional: false, nullable: false,
+ },
+ },
+ required: ['user', 'reaction'],
+ },
+ },
+ },
+ }, {
+ type: 'object',
+ properties: {
+ ...baseSchema.properties,
+ type: {
+ type: 'string',
+ optional: false, nullable: false,
+ enum: ['renote:grouped'],
+ },
+ note: {
+ type: 'object',
+ ref: 'Note',
+ optional: false, nullable: false,
+ },
+ users: {
+ type: 'array',
+ optional: false, nullable: false,
+ items: {
+ type: 'object',
+ ref: 'UserLite',
+ optional: false, nullable: false,
+ },
+ },
+ },
+ }, {
+ type: 'object',
+ properties: {
+ ...baseSchema.properties,
+ type: {
+ type: 'string',
+ optional: false, nullable: false,
+ enum: ['test'],
+ },
+ },
+ }],
} as const;
diff --git a/packages/backend/src/models/json-schema/page.ts b/packages/backend/src/models/json-schema/page.ts
index 9baacd6884..e4d844645e 100644
--- a/packages/backend/src/models/json-schema/page.ts
+++ b/packages/backend/src/models/json-schema/page.ts
@@ -3,6 +3,108 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
+const blockBaseSchema = {
+ type: 'object',
+ properties: {
+ id: {
+ type: 'string',
+ optional: false, nullable: false,
+ },
+ type: {
+ type: 'string',
+ optional: false, nullable: false,
+ },
+ },
+} as const;
+
+const textBlockSchema = {
+ type: 'object',
+ properties: {
+ ...blockBaseSchema.properties,
+ type: {
+ type: 'string',
+ optional: false, nullable: false,
+ enum: ['text'],
+ },
+ text: {
+ type: 'string',
+ optional: false, nullable: false,
+ },
+ },
+} as const;
+
+const sectionBlockSchema = {
+ type: 'object',
+ properties: {
+ ...blockBaseSchema.properties,
+ type: {
+ type: 'string',
+ optional: false, nullable: false,
+ enum: ['section'],
+ },
+ title: {
+ type: 'string',
+ optional: false, nullable: false,
+ },
+ children: {
+ type: 'array',
+ optional: false, nullable: false,
+ items: {
+ type: 'object',
+ optional: false, nullable: false,
+ ref: 'PageBlock',
+ selfRef: true,
+ },
+ },
+ },
+} as const;
+
+const imageBlockSchema = {
+ type: 'object',
+ properties: {
+ ...blockBaseSchema.properties,
+ type: {
+ type: 'string',
+ optional: false, nullable: false,
+ enum: ['image'],
+ },
+ fileId: {
+ type: 'string',
+ optional: false, nullable: true,
+ },
+ },
+} as const;
+
+const noteBlockSchema = {
+ type: 'object',
+ properties: {
+ ...blockBaseSchema.properties,
+ type: {
+ type: 'string',
+ optional: false, nullable: false,
+ enum: ['note'],
+ },
+ detailed: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ note: {
+ type: 'string',
+ optional: false, nullable: true,
+ },
+ },
+} as const;
+
+export const packedPageBlockSchema = {
+ type: 'object',
+ oneOf: [
+ textBlockSchema,
+ sectionBlockSchema,
+ imageBlockSchema,
+ noteBlockSchema,
+ ],
+} as const;
+
export const packedPageSchema = {
type: 'object',
properties: {
@@ -38,6 +140,7 @@ export const packedPageSchema = {
items: {
type: 'object',
optional: false, nullable: false,
+ ref: 'PageBlock',
},
},
variables: {
diff --git a/packages/backend/src/models/json-schema/renote-muting.ts b/packages/backend/src/models/json-schema/renote-muting.ts
index feed1ceb09..769b33f515 100644
--- a/packages/backend/src/models/json-schema/renote-muting.ts
+++ b/packages/backend/src/models/json-schema/renote-muting.ts
@@ -25,7 +25,7 @@ export const packedRenoteMutingSchema = {
mutee: {
type: 'object',
optional: false, nullable: false,
- ref: 'UserDetailed',
+ ref: 'UserDetailedNotMe',
},
},
} as const;
diff --git a/packages/backend/src/models/json-schema/role.ts b/packages/backend/src/models/json-schema/role.ts
index b0c6804bb8..55348d4f3d 100644
--- a/packages/backend/src/models/json-schema/role.ts
+++ b/packages/backend/src/models/json-schema/role.ts
@@ -1,26 +1,103 @@
-const rolePolicyValue = {
+export const packedRolePoliciesSchema = {
type: 'object',
+ optional: false, nullable: false,
properties: {
- value: {
- oneOf: [
- {
- type: 'integer',
- optional: false, nullable: false,
- },
- {
- type: 'boolean',
- optional: false, nullable: false,
- },
- ],
+ gtlAvailable: {
+ type: 'boolean',
+ optional: false, nullable: false,
},
- priority: {
+ ltlAvailable: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ canPublicNote: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ canInvite: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ inviteLimit: {
type: 'integer',
optional: false, nullable: false,
},
- useDefault: {
+ inviteLimitCycle: {
+ type: 'integer',
+ optional: false, nullable: false,
+ },
+ inviteExpirationTime: {
+ type: 'integer',
+ optional: false, nullable: false,
+ },
+ canManageCustomEmojis: {
type: 'boolean',
optional: false, nullable: false,
},
+ canManageAvatarDecorations: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ canSearchNotes: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ canUseTranslator: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ canHideAds: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ driveCapacityMb: {
+ type: 'integer',
+ optional: false, nullable: false,
+ },
+ alwaysMarkNsfw: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ },
+ pinLimit: {
+ type: 'integer',
+ optional: false, nullable: false,
+ },
+ antennaLimit: {
+ type: 'integer',
+ optional: false, nullable: false,
+ },
+ wordMuteLimit: {
+ type: 'integer',
+ optional: false, nullable: false,
+ },
+ webhookLimit: {
+ type: 'integer',
+ optional: false, nullable: false,
+ },
+ clipLimit: {
+ type: 'integer',
+ optional: false, nullable: false,
+ },
+ noteEachClipsLimit: {
+ type: 'integer',
+ optional: false, nullable: false,
+ },
+ userListLimit: {
+ type: 'integer',
+ optional: false, nullable: false,
+ },
+ userEachUserListsLimit: {
+ type: 'integer',
+ optional: false, nullable: false,
+ },
+ rateLimitFactor: {
+ type: 'integer',
+ optional: false, nullable: false,
+ },
+ avatarDecorationLimit: {
+ type: 'integer',
+ optional: false, nullable: false,
+ },
},
} as const;
@@ -121,31 +198,28 @@ export const packedRoleSchema = {
policies: {
type: 'object',
optional: false, nullable: false,
- properties: {
- pinLimit: rolePolicyValue,
- canInvite: rolePolicyValue,
- clipLimit: rolePolicyValue,
- canHideAds: rolePolicyValue,
- inviteLimit: rolePolicyValue,
- antennaLimit: rolePolicyValue,
- gtlAvailable: rolePolicyValue,
- ltlAvailable: rolePolicyValue,
- webhookLimit: rolePolicyValue,
- canPublicNote: rolePolicyValue,
- userListLimit: rolePolicyValue,
- wordMuteLimit: rolePolicyValue,
- alwaysMarkNsfw: rolePolicyValue,
- canSearchNotes: rolePolicyValue,
- driveCapacityMb: rolePolicyValue,
- rateLimitFactor: rolePolicyValue,
- inviteLimitCycle: rolePolicyValue,
- noteEachClipsLimit: rolePolicyValue,
- inviteExpirationTime: rolePolicyValue,
- canManageCustomEmojis: rolePolicyValue,
- userEachUserListsLimit: rolePolicyValue,
- canManageAvatarDecorations: rolePolicyValue,
- canUseTranslator: rolePolicyValue,
- avatarDecorationLimit: rolePolicyValue,
+ additionalProperties: {
+ anyOf: [{
+ type: 'object',
+ properties: {
+ value: {
+ oneOf: [
+ {
+ type: 'integer',
+ },
+ {
+ type: 'boolean',
+ },
+ ],
+ },
+ priority: {
+ type: 'integer',
+ },
+ useDefault: {
+ type: 'boolean',
+ },
+ },
+ }],
},
},
usersCount: {
diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts
index 6a0d43b1ac..ca51755317 100644
--- a/packages/backend/src/models/json-schema/user.ts
+++ b/packages/backend/src/models/json-schema/user.ts
@@ -590,104 +590,7 @@ export const packedMeDetailedOnlySchema = {
policies: {
type: 'object',
nullable: false, optional: false,
- properties: {
- gtlAvailable: {
- type: 'boolean',
- nullable: false, optional: false,
- },
- ltlAvailable: {
- type: 'boolean',
- nullable: false, optional: false,
- },
- canPublicNote: {
- type: 'boolean',
- nullable: false, optional: false,
- },
- canInvite: {
- type: 'boolean',
- nullable: false, optional: false,
- },
- inviteLimit: {
- type: 'number',
- nullable: false, optional: false,
- },
- inviteLimitCycle: {
- type: 'number',
- nullable: false, optional: false,
- },
- inviteExpirationTime: {
- type: 'number',
- nullable: false, optional: false,
- },
- canManageCustomEmojis: {
- type: 'boolean',
- nullable: false, optional: false,
- },
- canManageAvatarDecorations: {
- type: 'boolean',
- nullable: false, optional: false,
- },
- canSearchNotes: {
- type: 'boolean',
- nullable: false, optional: false,
- },
- canUseTranslator: {
- type: 'boolean',
- nullable: false, optional: false,
- },
- canHideAds: {
- type: 'boolean',
- nullable: false, optional: false,
- },
- driveCapacityMb: {
- type: 'number',
- nullable: false, optional: false,
- },
- alwaysMarkNsfw: {
- type: 'boolean',
- nullable: false, optional: false,
- },
- pinLimit: {
- type: 'number',
- nullable: false, optional: false,
- },
- antennaLimit: {
- type: 'number',
- nullable: false, optional: false,
- },
- wordMuteLimit: {
- type: 'number',
- nullable: false, optional: false,
- },
- webhookLimit: {
- type: 'number',
- nullable: false, optional: false,
- },
- clipLimit: {
- type: 'number',
- nullable: false, optional: false,
- },
- noteEachClipsLimit: {
- type: 'number',
- nullable: false, optional: false,
- },
- userListLimit: {
- type: 'number',
- nullable: false, optional: false,
- },
- userEachUserListsLimit: {
- type: 'number',
- nullable: false, optional: false,
- },
- rateLimitFactor: {
- type: 'number',
- nullable: false, optional: false,
- },
- avatarDecorationLimit: {
- type: 'number',
- nullable: false, optional: false,
- },
- },
+ ref: 'RolePolicies',
},
//#region secrets
email: {
@@ -782,13 +685,5 @@ export const packedUserSchema = {
type: 'object',
ref: 'UserDetailed',
},
- {
- type: 'object',
- ref: 'UserDetailedNotMe',
- },
- {
- type: 'object',
- ref: 'MeDetailed',
- },
],
} as const;
diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts
index 632a7692cd..0b65c1ea74 100644
--- a/packages/backend/src/server/ServerService.ts
+++ b/packages/backend/src/server/ServerService.ts
@@ -204,7 +204,7 @@ export class ServerService implements OnApplicationShutdown {
});
this.globalEventService.publishMainStream(profile.userId, 'meUpdated', await this.userEntityService.pack(profile.userId, { id: profile.userId }, {
- detail: true,
+ schema: 'MeDetailed',
includeSecrets: true,
}));
diff --git a/packages/backend/src/server/api/ApiServerService.ts b/packages/backend/src/server/api/ApiServerService.ts
index 1758c03aca..386e593636 100644
--- a/packages/backend/src/server/api/ApiServerService.ts
+++ b/packages/backend/src/server/api/ApiServerService.ts
@@ -157,7 +157,7 @@ export class ApiServerService {
return {
ok: true,
token: token.token,
- user: await this.userEntityService.pack(token.userId, null, { detail: true }),
+ user: await this.userEntityService.pack(token.userId, null, { schema: 'UserDetailedNotMe' }),
};
} else {
return {
diff --git a/packages/backend/src/server/api/SignupApiService.ts b/packages/backend/src/server/api/SignupApiService.ts
index 6b4d9d9f70..f025999c7a 100644
--- a/packages/backend/src/server/api/SignupApiService.ts
+++ b/packages/backend/src/server/api/SignupApiService.ts
@@ -163,12 +163,12 @@ export class SignupApiService {
}
if (instance.emailRequiredForSignup) {
- if (await this.usersRepository.exist({ where: { usernameLower: username.toLowerCase(), host: IsNull() } })) {
+ if (await this.usersRepository.exists({ where: { usernameLower: username.toLowerCase(), host: IsNull() } })) {
throw new FastifyReplyError(400, 'DUPLICATED_USERNAME');
}
// Check deleted username duplication
- if (await this.usedUsernamesRepository.exist({ where: { username: username.toLowerCase() } })) {
+ if (await this.usedUsernamesRepository.exists({ where: { username: username.toLowerCase() } })) {
throw new FastifyReplyError(400, 'USED_USERNAME');
}
@@ -213,7 +213,7 @@ export class SignupApiService {
});
const res = await this.userEntityService.pack(account, account, {
- detail: true,
+ schema: 'MeDetailed',
includeSecrets: true,
});
diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts
index 44bb4e1321..ff3c67f2f6 100644
--- a/packages/backend/src/server/api/endpoints.ts
+++ b/packages/backend/src/server/api/endpoints.ts
@@ -4,8 +4,7 @@
*/
import { permissions } from 'misskey-js';
-import type { Schema } from '@/misc/json-schema.js';
-import { RolePolicies } from '@/core/RoleService.js';
+import type { KeyOf, Schema } from '@/misc/json-schema.js';
import * as ep___admin_meta from './endpoints/admin/meta.js';
import * as ep___admin_abuseUserReports from './endpoints/admin/abuse-user-reports.js';
@@ -778,7 +777,7 @@ interface IEndpointMetaBase {
*/
readonly requireAdmin?: boolean;
- readonly requireRolePolicy?: keyof RolePolicies;
+ readonly requireRolePolicy?: KeyOf<'RolePolicies'>;
/**
* 引っ越し済みのユーザーによるリクエストを禁止するか
diff --git a/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts b/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts
index 3484d6707a..4ffefa05b7 100644
--- a/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts
+++ b/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts
@@ -62,17 +62,17 @@ export const meta = {
reporter: {
type: 'object',
nullable: false, optional: false,
- ref: 'User',
+ ref: 'UserDetailedNotMe',
},
targetUser: {
type: 'object',
nullable: false, optional: false,
- ref: 'User',
+ ref: 'UserDetailedNotMe',
},
assignee: {
type: 'object',
nullable: true, optional: true,
- ref: 'User',
+ ref: 'UserDetailedNotMe',
},
},
},
diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/create.ts b/packages/backend/src/server/api/endpoints/admin/accounts/create.ts
index f54d567fff..14fd69a1a2 100644
--- a/packages/backend/src/server/api/endpoints/admin/accounts/create.ts
+++ b/packages/backend/src/server/api/endpoints/admin/accounts/create.ts
@@ -9,8 +9,10 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
import type { UsersRepository } from '@/models/_.js';
import { SignupService } from '@/core/SignupService.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
+import { InstanceActorService } from '@/core/InstanceActorService.js';
import { localUsernameSchema, passwordSchema } from '@/models/User.js';
import { DI } from '@/di-symbols.js';
+import { Packed } from '@/misc/json-schema.js';
export const meta = {
tags: ['admin'],
@@ -18,7 +20,7 @@ export const meta = {
res: {
type: 'object',
optional: false, nullable: false,
- ref: 'User',
+ ref: 'MeDetailed',
properties: {
token: {
type: 'string',
@@ -45,13 +47,12 @@ export default class extends Endpoint { // eslint-
private userEntityService: UserEntityService,
private signupService: SignupService,
+ private instanceActorService: InstanceActorService,
) {
super(meta, paramDef, async (ps, _me, token) => {
const me = _me ? await this.usersRepository.findOneByOrFail({ id: _me.id }) : null;
- const noUsers = (await this.usersRepository.countBy({
- host: IsNull(),
- })) === 0;
- if ((!noUsers && !me?.isRoot) || token !== null) throw new Error('access denied');
+ const realUsers = await this.instanceActorService.realLocalUsersPresent();
+ if ((realUsers && !me?.isRoot) || token !== null) throw new Error('access denied');
const { account, secret } = await this.signupService.signup({
username: ps.username,
@@ -60,11 +61,11 @@ export default class extends Endpoint { // eslint-
});
const res = await this.userEntityService.pack(account, account, {
- detail: true,
+ schema: 'MeDetailed',
includeSecrets: true,
- });
+ }) as Packed<'MeDetailed'> & { token: string };
- (res as any).token = secret;
+ res.token = secret;
return res;
});
diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/find-by-email.ts b/packages/backend/src/server/api/endpoints/admin/accounts/find-by-email.ts
index 93673453d6..80b198eb80 100644
--- a/packages/backend/src/server/api/endpoints/admin/accounts/find-by-email.ts
+++ b/packages/backend/src/server/api/endpoints/admin/accounts/find-by-email.ts
@@ -27,7 +27,7 @@ export const meta = {
res: {
type: 'object',
optional: false, nullable: false,
- ref: 'User',
+ ref: 'UserDetailedNotMe',
},
} as const;
@@ -58,7 +58,7 @@ export default class extends Endpoint { // eslint-
}
const res = await this.userEntityService.pack(profile.user!, null, {
- detail: true,
+ schema: 'UserDetailedNotMe',
});
return res;
diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts
index 0627c5055c..2af9e7cd9a 100644
--- a/packages/backend/src/server/api/endpoints/admin/meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/meta.ts
@@ -156,6 +156,13 @@ export const meta = {
type: 'string',
},
},
+ prohibitedWords: {
+ type: 'array',
+ optional: false, nullable: false,
+ items: {
+ type: 'string',
+ },
+ },
bannedEmailDomains: {
type: 'array',
optional: true, nullable: false,
@@ -515,6 +522,7 @@ export default class extends Endpoint { // eslint-
blockedHosts: instance.blockedHosts,
silencedHosts: instance.silencedHosts,
sensitiveWords: instance.sensitiveWords,
+ prohibitedWords: instance.prohibitedWords,
preservedUsernames: instance.preservedUsernames,
hcaptchaSecretKey: instance.hcaptchaSecretKey,
mcaptchaSecretKey: instance.mcaptchaSecretKey,
diff --git a/packages/backend/src/server/api/endpoints/admin/promo/create.ts b/packages/backend/src/server/api/endpoints/admin/promo/create.ts
index ab69dfba96..339b7a8aa4 100644
--- a/packages/backend/src/server/api/endpoints/admin/promo/create.ts
+++ b/packages/backend/src/server/api/endpoints/admin/promo/create.ts
@@ -55,7 +55,7 @@ export default class extends Endpoint { // eslint-
throw e;
});
- const exist = await this.promoNotesRepository.exist({ where: { noteId: note.id } });
+ const exist = await this.promoNotesRepository.exists({ where: { noteId: note.id } });
if (exist) {
throw new ApiError(meta.errors.alreadyPromoted);
diff --git a/packages/backend/src/server/api/endpoints/admin/roles/users.ts b/packages/backend/src/server/api/endpoints/admin/roles/users.ts
index 66f4d9d26b..1e05685991 100644
--- a/packages/backend/src/server/api/endpoints/admin/roles/users.ts
+++ b/packages/backend/src/server/api/endpoints/admin/roles/users.ts
@@ -40,7 +40,7 @@ export const meta = {
},
required: ['id', 'createdAt', 'user'],
},
- }
+ },
} as const;
export const paramDef = {
@@ -92,7 +92,7 @@ export default class extends Endpoint { // eslint-
return await Promise.all(assigns.map(async assign => ({
id: assign.id,
createdAt: this.idService.parse(assign.id).date.toISOString(),
- user: await this.userEntityService.pack(assign.user!, me, { detail: true }),
+ user: await this.userEntityService.pack(assign.user!, me, { schema: 'UserDetailed' }),
expiresAt: assign.expiresAt?.toISOString() ?? null,
})));
});
diff --git a/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts b/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts
index f3601be9bb..51b5a02600 100644
--- a/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts
+++ b/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts
@@ -50,7 +50,7 @@ export const meta = {
user: {
type: 'object',
optional: false, nullable: false,
- ref: 'UserDetailed',
+ ref: 'UserDetailedNotMe',
},
},
},
diff --git a/packages/backend/src/server/api/endpoints/admin/show-users.ts b/packages/backend/src/server/api/endpoints/admin/show-users.ts
index 1d31e5e80f..80611266df 100644
--- a/packages/backend/src/server/api/endpoints/admin/show-users.ts
+++ b/packages/backend/src/server/api/endpoints/admin/show-users.ts
@@ -114,7 +114,7 @@ export default class extends Endpoint { // eslint-
const users = await query.getMany();
- return await this.userEntityService.packMany(users, me, { detail: true });
+ return await this.userEntityService.packMany(users, me, { schema: 'UserDetailed' });
});
}
}
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 d76d3dfeea..ce8c8a505d 100644
--- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts
@@ -41,6 +41,11 @@ export const paramDef = {
type: 'string',
},
},
+ prohibitedWords: {
+ 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 },
@@ -177,6 +182,9 @@ export default class extends Endpoint { // eslint-
if (Array.isArray(ps.sensitiveWords)) {
set.sensitiveWords = ps.sensitiveWords.filter(Boolean);
}
+ if (Array.isArray(ps.prohibitedWords)) {
+ set.prohibitedWords = ps.prohibitedWords.filter(Boolean);
+ }
if (Array.isArray(ps.silencedHosts)) {
let lastValue = '';
set.silencedHosts = ps.silencedHosts.sort().filter((h) => {
diff --git a/packages/backend/src/server/api/endpoints/ap/show.ts b/packages/backend/src/server/api/endpoints/ap/show.ts
index 7e5c7a917c..4c1236eaa1 100644
--- a/packages/backend/src/server/api/endpoints/ap/show.ts
+++ b/packages/backend/src/server/api/endpoints/ap/show.ts
@@ -148,7 +148,7 @@ export default class extends Endpoint { // eslint-
if (user != null) {
return {
type: 'User',
- object: await this.userEntityService.pack(user, me, { detail: true }),
+ object: await this.userEntityService.pack(user, me, { schema: 'UserDetailedNotMe' }),
};
} else if (note != null) {
try {
diff --git a/packages/backend/src/server/api/endpoints/auth/accept.ts b/packages/backend/src/server/api/endpoints/auth/accept.ts
index e0baeb3565..602c34b1e6 100644
--- a/packages/backend/src/server/api/endpoints/auth/accept.ts
+++ b/packages/backend/src/server/api/endpoints/auth/accept.ts
@@ -62,7 +62,7 @@ export default class extends Endpoint { // eslint-
const accessToken = secureRndstr(32);
// Fetch exist access token
- const exist = await this.accessTokensRepository.exist({
+ const exist = await this.accessTokensRepository.exists({
where: {
appId: session.appId,
userId: me.id,
diff --git a/packages/backend/src/server/api/endpoints/auth/session/userkey.ts b/packages/backend/src/server/api/endpoints/auth/session/userkey.ts
index ffddda090b..eeb580cead 100644
--- a/packages/backend/src/server/api/endpoints/auth/session/userkey.ts
+++ b/packages/backend/src/server/api/endpoints/auth/session/userkey.ts
@@ -112,7 +112,7 @@ export default class extends Endpoint { // eslint-
return {
accessToken: accessToken.token,
user: await this.userEntityService.pack(session.userId, null, {
- detail: true,
+ schema: 'UserDetailedNotMe',
}),
};
});
diff --git a/packages/backend/src/server/api/endpoints/blocking/create.ts b/packages/backend/src/server/api/endpoints/blocking/create.ts
index 3c7d7ac8cd..ea7d2076b0 100644
--- a/packages/backend/src/server/api/endpoints/blocking/create.ts
+++ b/packages/backend/src/server/api/endpoints/blocking/create.ts
@@ -88,7 +88,7 @@ export default class extends Endpoint { // eslint-
});
// Check if already blocking
- const exist = await this.blockingsRepository.exist({
+ const exist = await this.blockingsRepository.exists({
where: {
blockerId: blocker.id,
blockeeId: blockee.id,
@@ -102,7 +102,7 @@ export default class extends Endpoint { // eslint-
await this.userBlockingService.block(blocker, blockee);
return await this.userEntityService.pack(blockee.id, blocker, {
- detail: true,
+ schema: 'UserDetailedNotMe',
});
});
}
diff --git a/packages/backend/src/server/api/endpoints/blocking/delete.ts b/packages/backend/src/server/api/endpoints/blocking/delete.ts
index 0ce334d559..b0d66fd05c 100644
--- a/packages/backend/src/server/api/endpoints/blocking/delete.ts
+++ b/packages/backend/src/server/api/endpoints/blocking/delete.ts
@@ -88,7 +88,7 @@ export default class extends Endpoint { // eslint-
});
// Check not blocking
- const exist = await this.blockingsRepository.exist({
+ const exist = await this.blockingsRepository.exists({
where: {
blockerId: blocker.id,
blockeeId: blockee.id,
@@ -103,7 +103,7 @@ export default class extends Endpoint { // eslint-
await this.userBlockingService.unblock(blocker, blockee);
return await this.userEntityService.pack(blockee.id, blocker, {
- detail: true,
+ schema: 'UserDetailedNotMe',
});
});
}
diff --git a/packages/backend/src/server/api/endpoints/bubble-game/ranking.ts b/packages/backend/src/server/api/endpoints/bubble-game/ranking.ts
index 9c057760ca..731bcc5b65 100644
--- a/packages/backend/src/server/api/endpoints/bubble-game/ranking.ts
+++ b/packages/backend/src/server/api/endpoints/bubble-game/ranking.ts
@@ -61,7 +61,7 @@ export default class extends Endpoint { // eslint-
relations: ['user'],
});
- const users = await this.userEntityService.packMany(records.map(r => r.user!), null, { detail: false });
+ const users = await this.userEntityService.packMany(records.map(r => r.user!), null);
return records.map(r => ({
id: r.id,
diff --git a/packages/backend/src/server/api/endpoints/clips/favorite.ts b/packages/backend/src/server/api/endpoints/clips/favorite.ts
index 015b2cfa85..b4c6a4940b 100644
--- a/packages/backend/src/server/api/endpoints/clips/favorite.ts
+++ b/packages/backend/src/server/api/endpoints/clips/favorite.ts
@@ -62,7 +62,7 @@ export default class extends Endpoint { // eslint-
throw new ApiError(meta.errors.noSuchClip);
}
- const exist = await this.clipFavoritesRepository.exist({
+ const exist = await this.clipFavoritesRepository.exists({
where: {
clipId: clip.id,
userId: me.id,
diff --git a/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts b/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts
index 85e6312b6a..8c1f491f8d 100644
--- a/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts
+++ b/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts
@@ -38,7 +38,7 @@ export default class extends Endpoint { // eslint-
private driveFilesRepository: DriveFilesRepository,
) {
super(meta, paramDef, async (ps, me) => {
- const exist = await this.driveFilesRepository.exist({
+ const exist = await this.driveFilesRepository.exists({
where: {
md5: ps.md5,
userId: me.id,
diff --git a/packages/backend/src/server/api/endpoints/federation/users.ts b/packages/backend/src/server/api/endpoints/federation/users.ts
index d97171865a..df8b66ab44 100644
--- a/packages/backend/src/server/api/endpoints/federation/users.ts
+++ b/packages/backend/src/server/api/endpoints/federation/users.ts
@@ -54,7 +54,7 @@ export default class extends Endpoint { // eslint-
.limit(ps.limit)
.getMany();
- return await this.userEntityService.packMany(users, me, { detail: true });
+ return await this.userEntityService.packMany(users, me, { schema: 'UserDetailedNotMe' });
});
}
}
diff --git a/packages/backend/src/server/api/endpoints/flash/like.ts b/packages/backend/src/server/api/endpoints/flash/like.ts
index 1003249c0c..5878200828 100644
--- a/packages/backend/src/server/api/endpoints/flash/like.ts
+++ b/packages/backend/src/server/api/endpoints/flash/like.ts
@@ -70,7 +70,7 @@ export default class extends Endpoint { // eslint-
}
// if already liked
- const exist = await this.flashLikesRepository.exist({
+ const exist = await this.flashLikesRepository.exists({
where: {
flashId: flash.id,
userId: me.id,
diff --git a/packages/backend/src/server/api/endpoints/following/create.ts b/packages/backend/src/server/api/endpoints/following/create.ts
index 9037944ef9..1d0691407d 100644
--- a/packages/backend/src/server/api/endpoints/following/create.ts
+++ b/packages/backend/src/server/api/endpoints/following/create.ts
@@ -101,7 +101,7 @@ export default class extends Endpoint { // eslint-
});
// Check if already following
- const exist = await this.followingsRepository.exist({
+ const exist = await this.followingsRepository.exists({
where: {
followerId: follower.id,
followeeId: followee.id,
diff --git a/packages/backend/src/server/api/endpoints/following/delete.ts b/packages/backend/src/server/api/endpoints/following/delete.ts
index f44692ba6d..f761968c90 100644
--- a/packages/backend/src/server/api/endpoints/following/delete.ts
+++ b/packages/backend/src/server/api/endpoints/following/delete.ts
@@ -85,7 +85,7 @@ export default class extends Endpoint { // eslint-
});
// Check not following
- const exist = await this.followingsRepository.exist({
+ const exist = await this.followingsRepository.exists({
where: {
followerId: follower.id,
followeeId: followee.id,
diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/like.ts b/packages/backend/src/server/api/endpoints/gallery/posts/like.ts
index cc424261b4..576cff4e91 100644
--- a/packages/backend/src/server/api/endpoints/gallery/posts/like.ts
+++ b/packages/backend/src/server/api/endpoints/gallery/posts/like.ts
@@ -72,7 +72,7 @@ export default class extends Endpoint { // eslint-
}
// if already liked
- const exist = await this.galleryLikesRepository.exist({
+ const exist = await this.galleryLikesRepository.exists({
where: {
postId: post.id,
userId: me.id,
diff --git a/packages/backend/src/server/api/endpoints/hashtags/users.ts b/packages/backend/src/server/api/endpoints/hashtags/users.ts
index 8302d2380f..5071dd22b7 100644
--- a/packages/backend/src/server/api/endpoints/hashtags/users.ts
+++ b/packages/backend/src/server/api/endpoints/hashtags/users.ts
@@ -76,7 +76,7 @@ export default class extends Endpoint { // eslint-
const users = await query.limit(ps.limit).getMany();
- return await this.userEntityService.packMany(users, me, { detail: true });
+ return await this.userEntityService.packMany(users, me, { schema: 'UserDetailed' });
});
}
}
diff --git a/packages/backend/src/server/api/endpoints/i.ts b/packages/backend/src/server/api/endpoints/i.ts
index c24e049180..c613794589 100644
--- a/packages/backend/src/server/api/endpoints/i.ts
+++ b/packages/backend/src/server/api/endpoints/i.ts
@@ -71,8 +71,8 @@ export default class extends Endpoint { // eslint-
userProfile.loggedInDates = [...userProfile.loggedInDates, today];
}
- return await this.userEntityService.pack(userProfile.user!, userProfile.user!, {
- detail: true,
+ return await this.userEntityService.pack(userProfile.user!, userProfile.user!, {
+ schema: 'MeDetailed',
includeSecrets: isSecure,
userProfile,
});
diff --git a/packages/backend/src/server/api/endpoints/i/2fa/done.ts b/packages/backend/src/server/api/endpoints/i/2fa/done.ts
index 9f8e2894b8..7aaf3982d1 100644
--- a/packages/backend/src/server/api/endpoints/i/2fa/done.ts
+++ b/packages/backend/src/server/api/endpoints/i/2fa/done.ts
@@ -64,7 +64,7 @@ export default class extends Endpoint { // eslint-
// Publish meUpdated event
this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, {
- detail: true,
+ schema: 'MeDetailed',
includeSecrets: true,
}));
diff --git a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts
index a7be47fd0f..a88a1e50de 100644
--- a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts
+++ b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts
@@ -111,7 +111,7 @@ export default class extends Endpoint {
// Publish meUpdated event
this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, {
- detail: true,
+ schema: 'MeDetailed',
includeSecrets: true,
}));
diff --git a/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts b/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts
index 2ed701014d..b68f23bf8a 100644
--- a/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts
+++ b/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts
@@ -74,7 +74,7 @@ export default class extends Endpoint { // eslint-
// Publish meUpdated event
this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, {
- detail: true,
+ schema: 'MeDetailed',
includeSecrets: true,
}));
});
diff --git a/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts
index da8ac98556..74a9cd77f0 100644
--- a/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts
+++ b/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts
@@ -97,7 +97,7 @@ export default class extends Endpoint { // eslint-
// Publish meUpdated event
this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, {
- detail: true,
+ schema: 'MeDetailed',
includeSecrets: true,
}));
diff --git a/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts b/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts
index 338f12c5cd..6508c8b4e7 100644
--- a/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts
+++ b/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts
@@ -76,7 +76,7 @@ export default class extends Endpoint { // eslint-
// Publish meUpdated event
this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, {
- detail: true,
+ schema: 'MeDetailed',
includeSecrets: true,
}));
});
diff --git a/packages/backend/src/server/api/endpoints/i/2fa/update-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/update-key.ts
index 1a140c1d05..8d894e8f85 100644
--- a/packages/backend/src/server/api/endpoints/i/2fa/update-key.ts
+++ b/packages/backend/src/server/api/endpoints/i/2fa/update-key.ts
@@ -69,7 +69,7 @@ export default class extends Endpoint { // eslint-
// Publish meUpdated event
this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, {
- detail: true,
+ schema: 'MeDetailed',
includeSecrets: true,
}));
diff --git a/packages/backend/src/server/api/endpoints/i/import-antennas.ts b/packages/backend/src/server/api/endpoints/i/import-antennas.ts
index 71db8710af..771f3395ce 100644
--- a/packages/backend/src/server/api/endpoints/i/import-antennas.ts
+++ b/packages/backend/src/server/api/endpoints/i/import-antennas.ts
@@ -71,7 +71,7 @@ export default class extends Endpoint {
private downloadService: DownloadService,
) {
super(meta, paramDef, async (ps, me) => {
- const userExist = await this.usersRepository.exist({ where: { id: me.id } });
+ const userExist = await this.usersRepository.exists({ where: { id: me.id } });
if (!userExist) throw new ApiError(meta.errors.noSuchUser);
const file = await this.driveFilesRepository.findOneBy({ id: ps.fileId });
if (file === null) throw new ApiError(meta.errors.noSuchFile);
diff --git a/packages/backend/src/server/api/endpoints/i/pin.ts b/packages/backend/src/server/api/endpoints/i/pin.ts
index c89cdfa3a4..71182cc29a 100644
--- a/packages/backend/src/server/api/endpoints/i/pin.ts
+++ b/packages/backend/src/server/api/endpoints/i/pin.ts
@@ -66,8 +66,8 @@ export default class extends Endpoint { // eslint-
throw err;
});
- return await this.userEntityService.pack(me.id, me, {
- detail: true,
+ return await this.userEntityService.pack(me.id, me, {
+ schema: 'MeDetailed',
});
});
}
diff --git a/packages/backend/src/server/api/endpoints/i/revoke-token.ts b/packages/backend/src/server/api/endpoints/i/revoke-token.ts
index 98d866f867..545d16082c 100644
--- a/packages/backend/src/server/api/endpoints/i/revoke-token.ts
+++ b/packages/backend/src/server/api/endpoints/i/revoke-token.ts
@@ -34,7 +34,7 @@ export default class extends Endpoint { // eslint-
) {
super(meta, paramDef, async (ps, me) => {
if (ps.tokenId) {
- const tokenExist = await this.accessTokensRepository.exist({ where: { id: ps.tokenId } });
+ const tokenExist = await this.accessTokensRepository.exists({ where: { id: ps.tokenId } });
if (tokenExist) {
await this.accessTokensRepository.delete({
@@ -43,7 +43,7 @@ export default class extends Endpoint { // eslint-
});
}
} else if (ps.token) {
- const tokenExist = await this.accessTokensRepository.exist({ where: { token: ps.token } });
+ const tokenExist = await this.accessTokensRepository.exists({ where: { token: ps.token } });
if (tokenExist) {
await this.accessTokensRepository.delete({
diff --git a/packages/backend/src/server/api/endpoints/i/unpin.ts b/packages/backend/src/server/api/endpoints/i/unpin.ts
index b59c0e954f..1e5f66f4a8 100644
--- a/packages/backend/src/server/api/endpoints/i/unpin.ts
+++ b/packages/backend/src/server/api/endpoints/i/unpin.ts
@@ -51,8 +51,8 @@ export default class extends Endpoint { // eslint-
throw err;
});
- return await this.userEntityService.pack(me.id, me, {
- detail: true,
+ return await this.userEntityService.pack(me.id, me, {
+ schema: 'MeDetailed',
});
});
}
diff --git a/packages/backend/src/server/api/endpoints/i/update-email.ts b/packages/backend/src/server/api/endpoints/i/update-email.ts
index 52977f5a07..cc3ec11708 100644
--- a/packages/backend/src/server/api/endpoints/i/update-email.ts
+++ b/packages/backend/src/server/api/endpoints/i/update-email.ts
@@ -43,7 +43,7 @@ export const meta = {
res: {
type: 'object',
- ref: 'UserDetailed',
+ ref: 'MeDetailed',
},
} as const;
@@ -106,7 +106,7 @@ export default class extends Endpoint { // eslint-
});
const iObj = await this.userEntityService.pack(me.id, me, {
- detail: true,
+ schema: 'MeDetailed',
includeSecrets: true,
});
diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts
index eed3082258..584321557b 100644
--- a/packages/backend/src/server/api/endpoints/i/update.ts
+++ b/packages/backend/src/server/api/endpoints/i/update.ts
@@ -446,8 +446,8 @@ export default class extends Endpoint { // eslint-
verifiedLinks: [],
});
- const iObj = await this.userEntityService.pack(user.id, user, {
- detail: true,
+ const iObj = await this.userEntityService.pack(user.id, user, {
+ schema: 'MeDetailed',
includeSecrets: isSecure,
});
diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts
index 529e82678d..c6489f67ac 100644
--- a/packages/backend/src/server/api/endpoints/meta.ts
+++ b/packages/backend/src/server/api/endpoints/meta.ts
@@ -6,11 +6,12 @@
import { IsNull, LessThanOrEqual, MoreThan, Brackets } from 'typeorm';
import { Inject, Injectable } from '@nestjs/common';
import JSON5 from 'json5';
-import type { AdsRepository, UsersRepository } from '@/models/_.js';
+import type { AdsRepository } from '@/models/_.js';
import { MAX_NOTE_TEXT_LENGTH } from '@/const.js';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { MetaService } from '@/core/MetaService.js';
+import { InstanceActorService } from '@/core/InstanceActorService.js';
import type { Config } from '@/config.js';
import { DI } from '@/di-symbols.js';
import { DEFAULT_POLICIES } from '@/core/RoleService.js';
@@ -303,6 +304,11 @@ export const meta = {
type: 'string',
optional: false, nullable: true,
},
+ policies: {
+ type: 'object',
+ optional: false, nullable: false,
+ ref: 'RolePolicies',
+ },
},
},
} as const;
@@ -321,14 +327,12 @@ export default class extends Endpoint { // eslint-
@Inject(DI.config)
private config: Config,
- @Inject(DI.usersRepository)
- private usersRepository: UsersRepository,
-
@Inject(DI.adsRepository)
private adsRepository: AdsRepository,
private userEntityService: UserEntityService,
private metaService: MetaService,
+ private instanceActorService: InstanceActorService,
) {
super(meta, paramDef, async (ps, me) => {
const instance = await this.metaService.fetch(true);
@@ -407,9 +411,7 @@ export default class extends Endpoint { // eslint-
...(ps.detail ? {
cacheRemoteFiles: instance.cacheRemoteFiles,
cacheRemoteSensitiveFiles: instance.cacheRemoteSensitiveFiles,
- requireSetup: (await this.usersRepository.countBy({
- host: IsNull(),
- })) === 0,
+ requireSetup: !await this.instanceActorService.realLocalUsersPresent(),
} : {}),
};
diff --git a/packages/backend/src/server/api/endpoints/mute/create.ts b/packages/backend/src/server/api/endpoints/mute/create.ts
index 49c2b5707d..1d931150e1 100644
--- a/packages/backend/src/server/api/endpoints/mute/create.ts
+++ b/packages/backend/src/server/api/endpoints/mute/create.ts
@@ -83,7 +83,7 @@ export default class extends Endpoint { // eslint-
});
// Check if already muting
- const exist = await this.mutingsRepository.exist({
+ const exist = await this.mutingsRepository.exists({
where: {
muterId: muter.id,
muteeId: mutee.id,
diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts
index 29a0f7418c..50969c71cc 100644
--- a/packages/backend/src/server/api/endpoints/notes/create.ts
+++ b/packages/backend/src/server/api/endpoints/notes/create.ts
@@ -17,6 +17,8 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { NoteCreateService } from '@/core/NoteCreateService.js';
import { DI } from '@/di-symbols.js';
import { isPureRenote } from '@/misc/is-pure-renote.js';
+import { MetaService } from '@/core/MetaService.js';
+import { UtilityService } from '@/core/UtilityService.js';
import { ApiError } from '../../error.js';
export const meta = {
@@ -111,6 +113,12 @@ export const meta = {
code: 'CANNOT_RENOTE_OUTSIDE_OF_CHANNEL',
id: '33510210-8452-094c-6227-4a6c05d99f00',
},
+
+ containsProhibitedWords: {
+ message: 'Cannot post because it contains prohibited words.',
+ code: 'CONTAINS_PROHIBITED_WORDS',
+ id: 'aa6e01d3-a85c-669d-758a-76aab43af334',
+ },
},
} as const;
@@ -260,7 +268,7 @@ export default class extends Endpoint { // eslint-
// Check blocking
if (renote.userId !== me.id) {
- const blockExist = await this.blockingsRepository.exist({
+ const blockExist = await this.blockingsRepository.exists({
where: {
blockerId: renote.userId,
blockeeId: me.id,
@@ -308,7 +316,7 @@ export default class extends Endpoint { // eslint-
// Check blocking
if (reply.userId !== me.id) {
- const blockExist = await this.blockingsRepository.exist({
+ const blockExist = await this.blockingsRepository.exists({
where: {
blockerId: reply.userId,
blockeeId: me.id,
@@ -340,31 +348,40 @@ export default class extends Endpoint { // eslint-
}
// 投稿を作成
- const note = await this.noteCreateService.create(me, {
- createdAt: new Date(),
- files: files,
- poll: ps.poll ? {
- choices: ps.poll.choices,
- multiple: ps.poll.multiple ?? false,
- expiresAt: ps.poll.expiresAt ? new Date(ps.poll.expiresAt) : null,
- } : undefined,
- text: ps.text ?? undefined,
- reply,
- renote,
- cw: ps.cw,
- localOnly: ps.localOnly,
- reactionAcceptance: ps.reactionAcceptance,
- visibility: ps.visibility,
- visibleUsers,
- channel,
- apMentions: ps.noExtractMentions ? [] : undefined,
- apHashtags: ps.noExtractHashtags ? [] : undefined,
- apEmojis: ps.noExtractEmojis ? [] : undefined,
- });
+ try {
+ const note = await this.noteCreateService.create(me, {
+ createdAt: new Date(),
+ files: files,
+ poll: ps.poll ? {
+ choices: ps.poll.choices,
+ multiple: ps.poll.multiple ?? false,
+ expiresAt: ps.poll.expiresAt ? new Date(ps.poll.expiresAt) : null,
+ } : undefined,
+ text: ps.text ?? undefined,
+ reply,
+ renote,
+ cw: ps.cw,
+ localOnly: ps.localOnly,
+ reactionAcceptance: ps.reactionAcceptance,
+ visibility: ps.visibility,
+ visibleUsers,
+ channel,
+ apMentions: ps.noExtractMentions ? [] : undefined,
+ apHashtags: ps.noExtractHashtags ? [] : undefined,
+ apEmojis: ps.noExtractEmojis ? [] : undefined,
+ });
- return {
- createdNote: await this.noteEntityService.pack(note, me),
- };
+ return {
+ createdNote: await this.noteEntityService.pack(note, me),
+ };
+ } catch (e) {
+ // TODO: 他のErrorもここでキャッチしてエラーメッセージを当てるようにしたい
+ if (e instanceof NoteCreateService.ContainsProhibitedWordsError) {
+ throw new ApiError(meta.errors.containsProhibitedWords);
+ }
+
+ throw e;
+ }
});
}
}
diff --git a/packages/backend/src/server/api/endpoints/notes/favorites/create.ts b/packages/backend/src/server/api/endpoints/notes/favorites/create.ts
index ed3dce7f35..bfa621aa38 100644
--- a/packages/backend/src/server/api/endpoints/notes/favorites/create.ts
+++ b/packages/backend/src/server/api/endpoints/notes/favorites/create.ts
@@ -67,7 +67,7 @@ export default class extends Endpoint { // eslint-
});
// if already favorited
- const exist = await this.noteFavoritesRepository.exist({
+ const exist = await this.noteFavoritesRepository.exists({
where: {
noteId: note.id,
userId: me.id,
diff --git a/packages/backend/src/server/api/endpoints/page-push.ts b/packages/backend/src/server/api/endpoints/page-push.ts
index 0a68516586..49fcaf061b 100644
--- a/packages/backend/src/server/api/endpoints/page-push.ts
+++ b/packages/backend/src/server/api/endpoints/page-push.ts
@@ -55,7 +55,7 @@ export default class extends Endpoint { // eslint-
var: ps.var,
userId: me.id,
user: await this.userEntityService.pack(me.id, { id: page.userId }, {
- detail: true,
+ schema: 'UserDetailed',
}),
});
});
diff --git a/packages/backend/src/server/api/endpoints/pages/like.ts b/packages/backend/src/server/api/endpoints/pages/like.ts
index 8c18982b50..bee60f080d 100644
--- a/packages/backend/src/server/api/endpoints/pages/like.ts
+++ b/packages/backend/src/server/api/endpoints/pages/like.ts
@@ -70,7 +70,7 @@ export default class extends Endpoint { // eslint-
}
// if already liked
- const exist = await this.pageLikesRepository.exist({
+ const exist = await this.pageLikesRepository.exists({
where: {
pageId: page.id,
userId: me.id,
diff --git a/packages/backend/src/server/api/endpoints/pinned-users.ts b/packages/backend/src/server/api/endpoints/pinned-users.ts
index 390042c815..415633e8b6 100644
--- a/packages/backend/src/server/api/endpoints/pinned-users.ts
+++ b/packages/backend/src/server/api/endpoints/pinned-users.ts
@@ -52,7 +52,7 @@ export default class extends Endpoint { // eslint-
host: acct.host ?? IsNull(),
})));
- return await this.userEntityService.packMany(users.filter(x => x !== null) as MiUser[], me, { detail: true });
+ return await this.userEntityService.packMany(users.filter(x => x !== null) as MiUser[], me, { schema: 'UserDetailed' });
});
}
}
diff --git a/packages/backend/src/server/api/endpoints/promo/read.ts b/packages/backend/src/server/api/endpoints/promo/read.ts
index f427939a7a..4899408ddd 100644
--- a/packages/backend/src/server/api/endpoints/promo/read.ts
+++ b/packages/backend/src/server/api/endpoints/promo/read.ts
@@ -49,7 +49,7 @@ export default class extends Endpoint { // eslint-
throw err;
});
- const exist = await this.promoReadsRepository.exist({
+ const exist = await this.promoReadsRepository.exists({
where: {
noteId: note.id,
userId: me.id,
diff --git a/packages/backend/src/server/api/endpoints/retention.ts b/packages/backend/src/server/api/endpoints/retention.ts
index dac6d65407..2631693139 100644
--- a/packages/backend/src/server/api/endpoints/retention.ts
+++ b/packages/backend/src/server/api/endpoints/retention.ts
@@ -14,6 +14,32 @@ export const meta = {
requireCredential: false,
res: {
+ type: 'array',
+ items: {
+ type: 'object',
+ properties: {
+ createdAt: {
+ type: 'string',
+ format: 'date-time',
+ },
+ users: {
+ type: 'number',
+ },
+ data: {
+ type: 'object',
+ additionalProperties: {
+ anyOf: [{
+ type: 'number',
+ }],
+ },
+ },
+ },
+ required: [
+ 'createdAt',
+ 'users',
+ 'data',
+ ],
+ },
},
allowGet: true,
diff --git a/packages/backend/src/server/api/endpoints/roles/users.ts b/packages/backend/src/server/api/endpoints/roles/users.ts
index d304d075b2..2e43af5c52 100644
--- a/packages/backend/src/server/api/endpoints/roles/users.ts
+++ b/packages/backend/src/server/api/endpoints/roles/users.ts
@@ -33,11 +33,11 @@ export const meta = {
properties: {
id: {
type: 'string',
- format: 'misskey:id'
+ format: 'misskey:id',
},
user: {
type: 'object',
- ref: 'User'
+ ref: 'UserDetailed',
},
},
required: ['id', 'user'],
@@ -94,7 +94,7 @@ export default class extends Endpoint { // eslint-
return await Promise.all(assigns.map(async assign => ({
id: assign.id,
- user: await this.userEntityService.pack(assign.user!, me, { detail: true }),
+ user: await this.userEntityService.pack(assign.user!, me, { schema: 'UserDetailed' }),
})));
});
}
diff --git a/packages/backend/src/server/api/endpoints/users.ts b/packages/backend/src/server/api/endpoints/users.ts
index 8dc5841314..b886dd4a60 100644
--- a/packages/backend/src/server/api/endpoints/users.ts
+++ b/packages/backend/src/server/api/endpoints/users.ts
@@ -89,7 +89,7 @@ export default class extends Endpoint { // eslint-
const users = await query.getMany();
- return await this.userEntityService.packMany(users, me, { detail: true });
+ return await this.userEntityService.packMany(users, me, { schema: 'UserDetailed' });
});
}
}
diff --git a/packages/backend/src/server/api/endpoints/users/followers.ts b/packages/backend/src/server/api/endpoints/users/followers.ts
index 5706e46b96..314a45ed61 100644
--- a/packages/backend/src/server/api/endpoints/users/followers.ts
+++ b/packages/backend/src/server/api/endpoints/users/followers.ts
@@ -101,7 +101,7 @@ export default class extends Endpoint { // eslint-
if (me == null) {
throw new ApiError(meta.errors.forbidden);
} else if (me.id !== user.id) {
- const isFollowing = await this.followingsRepository.exist({
+ const isFollowing = await this.followingsRepository.exists({
where: {
followeeId: user.id,
followerId: me.id,
diff --git a/packages/backend/src/server/api/endpoints/users/following.ts b/packages/backend/src/server/api/endpoints/users/following.ts
index 794fb04f10..86f55c5a12 100644
--- a/packages/backend/src/server/api/endpoints/users/following.ts
+++ b/packages/backend/src/server/api/endpoints/users/following.ts
@@ -109,7 +109,7 @@ export default class extends Endpoint { // eslint-
if (me == null) {
throw new ApiError(meta.errors.forbidden);
} else if (me.id !== user.id) {
- const isFollowing = await this.followingsRepository.exist({
+ const isFollowing = await this.followingsRepository.exists({
where: {
followeeId: user.id,
followerId: me.id,
diff --git a/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts b/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts
index d6fb65cecb..6c04a06cbc 100644
--- a/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts
+++ b/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts
@@ -122,7 +122,7 @@ export default class extends Endpoint { // eslint-
// Make replies object (includes weights)
const repliesObj = await Promise.all(topRepliedUsers.map(async (user) => ({
- user: await this.userEntityService.pack(user, me, { detail: true }),
+ user: await this.userEntityService.pack(user, me, { schema: 'UserDetailed' }),
weight: repliedUsers[user] / peak,
})));
diff --git a/packages/backend/src/server/api/endpoints/users/lists/create-from-public.ts b/packages/backend/src/server/api/endpoints/users/lists/create-from-public.ts
index fa2e3338b8..dd9b459a1f 100644
--- a/packages/backend/src/server/api/endpoints/users/lists/create-from-public.ts
+++ b/packages/backend/src/server/api/endpoints/users/lists/create-from-public.ts
@@ -90,7 +90,7 @@ export default class extends Endpoint { // eslint-
private roleService: RoleService,
) {
super(meta, paramDef, async (ps, me) => {
- const listExist = await this.userListsRepository.exist({
+ const listExist = await this.userListsRepository.exists({
where: {
id: ps.listId,
isPublic: true,
@@ -121,7 +121,7 @@ export default class extends Endpoint { // eslint-
});
if (currentUser.id !== me.id) {
- const blockExist = await this.blockingsRepository.exist({
+ const blockExist = await this.blockingsRepository.exists({
where: {
blockerId: currentUser.id,
blockeeId: me.id,
@@ -132,7 +132,7 @@ export default class extends Endpoint { // eslint-
}
}
- const exist = await this.userListMembershipsRepository.exist({
+ const exist = await this.userListMembershipsRepository.exists({
where: {
userListId: userList.id,
userId: currentUser.id,
diff --git a/packages/backend/src/server/api/endpoints/users/lists/favorite.ts b/packages/backend/src/server/api/endpoints/users/lists/favorite.ts
index 864cdc2ee0..e5b3a73b55 100644
--- a/packages/backend/src/server/api/endpoints/users/lists/favorite.ts
+++ b/packages/backend/src/server/api/endpoints/users/lists/favorite.ts
@@ -47,7 +47,7 @@ export default class extends Endpoint {
private idService: IdService,
) {
super(meta, paramDef, async (ps, me) => {
- const userListExist = await this.userListsRepository.exist({
+ const userListExist = await this.userListsRepository.exists({
where: {
id: ps.listId,
isPublic: true,
@@ -58,7 +58,7 @@ export default class extends Endpoint {
throw new ApiError(meta.errors.noSuchList);
}
- const exist = await this.userListFavoritesRepository.exist({
+ const exist = await this.userListFavoritesRepository.exists({
where: {
userId: me.id,
userListId: ps.listId,
diff --git a/packages/backend/src/server/api/endpoints/users/lists/get-memberships.ts b/packages/backend/src/server/api/endpoints/users/lists/get-memberships.ts
index 985141515e..1fd3232de6 100644
--- a/packages/backend/src/server/api/endpoints/users/lists/get-memberships.ts
+++ b/packages/backend/src/server/api/endpoints/users/lists/get-memberships.ts
@@ -46,7 +46,7 @@ export const meta = {
},
user: {
type: 'object',
- ref: 'User',
+ ref: 'UserLite',
},
withReplies: {
type: 'boolean',
diff --git a/packages/backend/src/server/api/endpoints/users/lists/push.ts b/packages/backend/src/server/api/endpoints/users/lists/push.ts
index c4ceec575b..0984270943 100644
--- a/packages/backend/src/server/api/endpoints/users/lists/push.ts
+++ b/packages/backend/src/server/api/endpoints/users/lists/push.ts
@@ -104,7 +104,7 @@ export default class extends Endpoint { // eslint-
// Check blocking
if (user.id !== me.id) {
- const blockExist = await this.blockingsRepository.exist({
+ const blockExist = await this.blockingsRepository.exists({
where: {
blockerId: user.id,
blockeeId: me.id,
@@ -115,7 +115,7 @@ export default class extends Endpoint { // eslint-
}
}
- const exist = await this.userListMembershipsRepository.exist({
+ const exist = await this.userListMembershipsRepository.exists({
where: {
userListId: userList.id,
userId: user.id,
diff --git a/packages/backend/src/server/api/endpoints/users/lists/show.ts b/packages/backend/src/server/api/endpoints/users/lists/show.ts
index df44870b04..10efbaafd8 100644
--- a/packages/backend/src/server/api/endpoints/users/lists/show.ts
+++ b/packages/backend/src/server/api/endpoints/users/lists/show.ts
@@ -74,7 +74,7 @@ export default class extends Endpoint {
userListId: ps.listId,
});
if (me !== null) {
- additionalProperties.isLiked = await this.userListFavoritesRepository.exist({
+ additionalProperties.isLiked = await this.userListFavoritesRepository.exists({
where: {
userId: me.id,
userListId: ps.listId,
diff --git a/packages/backend/src/server/api/endpoints/users/lists/unfavorite.ts b/packages/backend/src/server/api/endpoints/users/lists/unfavorite.ts
index d51d57343e..0935584cbc 100644
--- a/packages/backend/src/server/api/endpoints/users/lists/unfavorite.ts
+++ b/packages/backend/src/server/api/endpoints/users/lists/unfavorite.ts
@@ -45,7 +45,7 @@ export default class extends Endpoint {
private userListFavoritesRepository: UserListFavoritesRepository,
) {
super(meta, paramDef, async (ps, me) => {
- const userListExist = await this.userListsRepository.exist({
+ const userListExist = await this.userListsRepository.exists({
where: {
id: ps.listId,
isPublic: true,
diff --git a/packages/backend/src/server/api/endpoints/users/reactions.ts b/packages/backend/src/server/api/endpoints/users/reactions.ts
index 372ab80c4c..69a842dbfb 100644
--- a/packages/backend/src/server/api/endpoints/users/reactions.ts
+++ b/packages/backend/src/server/api/endpoints/users/reactions.ts
@@ -9,6 +9,9 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
import { QueryService } from '@/core/QueryService.js';
import { NoteReactionEntityService } from '@/core/entities/NoteReactionEntityService.js';
import { DI } from '@/di-symbols.js';
+import { CacheService } from '@/core/CacheService.js';
+import { UserEntityService } from '@/core/entities/UserEntityService.js';
+import { RoleService } from '@/core/RoleService.js';
import { ApiError } from '../../error.js';
export const meta = {
@@ -34,6 +37,11 @@ export const meta = {
code: 'REACTIONS_NOT_PUBLIC',
id: '673a7dd2-6924-1093-e0c0-e68456ceae5c',
},
+ isRemoteUser: {
+ message: 'Currently unavailable to display reactions of remote users. See https://github.com/misskey-dev/misskey/issues/12964',
+ code: 'IS_REMOTE_USER',
+ id: '6b95fa98-8cf9-2350-e284-f0ffdb54a805',
+ },
},
} as const;
@@ -59,14 +67,24 @@ export default class extends Endpoint { // eslint-
@Inject(DI.noteReactionsRepository)
private noteReactionsRepository: NoteReactionsRepository,
+ private cacheService: CacheService,
+ private userEntityService: UserEntityService,
private noteReactionEntityService: NoteReactionEntityService,
private queryService: QueryService,
+ private roleService: RoleService,
) {
super(meta, paramDef, async (ps, me) => {
- const profile = await this.userProfilesRepository.findOneByOrFail({ userId: ps.userId });
+ const iAmModerator = me ? await this.roleService.isModerator(me) : false; // Moderators can see reactions of all users
+ if (!iAmModerator) {
+ const user = await this.cacheService.findUserById(ps.userId);
+ if (this.userEntityService.isRemoteUser(user)) {
+ throw new ApiError(meta.errors.isRemoteUser);
+ }
- if ((me == null || me.id !== ps.userId) && !profile.publicReactions) {
- throw new ApiError(meta.errors.reactionsNotPublic);
+ const profile = await this.userProfilesRepository.findOneByOrFail({ userId: ps.userId });
+ if ((me == null || me.id !== ps.userId) && !profile.publicReactions) {
+ throw new ApiError(meta.errors.reactionsNotPublic);
+ }
}
const query = this.queryService.makePaginationQuery(this.noteReactionsRepository.createQueryBuilder('reaction'),
diff --git a/packages/backend/src/server/api/endpoints/users/recommendation.ts b/packages/backend/src/server/api/endpoints/users/recommendation.ts
index 1b30e99b15..c73495e3c7 100644
--- a/packages/backend/src/server/api/endpoints/users/recommendation.ts
+++ b/packages/backend/src/server/api/endpoints/users/recommendation.ts
@@ -76,7 +76,7 @@ export default class extends Endpoint { // eslint-
const users = await query.limit(ps.limit).offset(ps.offset).getMany();
- return await this.userEntityService.packMany(users, me, { detail: true });
+ return await this.userEntityService.packMany(users, me, { schema: 'UserDetailed' });
});
}
}
diff --git a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts
index 4bf25d9fbb..7d36f6f932 100644
--- a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts
+++ b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts
@@ -131,7 +131,7 @@ export default class extends Endpoint { // eslint-
.getMany();
}
- return await this.userEntityService.packMany(users, me, { detail: !!ps.detail });
+ return await this.userEntityService.packMany(users, me, { schema: ps.detail ? 'UserDetailed' : 'UserLite' });
});
}
}
diff --git a/packages/backend/src/server/api/endpoints/users/search.ts b/packages/backend/src/server/api/endpoints/users/search.ts
index 32b5c12372..bc86136592 100644
--- a/packages/backend/src/server/api/endpoints/users/search.ts
+++ b/packages/backend/src/server/api/endpoints/users/search.ts
@@ -141,7 +141,7 @@ export default class extends Endpoint { // eslint-
}
}
- return await this.userEntityService.packMany(users, me, { detail: ps.detail });
+ return await this.userEntityService.packMany(users, me, { schema: ps.detail ? 'UserDetailed' : 'UserLite' });
});
}
}
diff --git a/packages/backend/src/server/api/endpoints/users/show.ts b/packages/backend/src/server/api/endpoints/users/show.ts
index 389497301d..4cf3858494 100644
--- a/packages/backend/src/server/api/endpoints/users/show.ts
+++ b/packages/backend/src/server/api/endpoints/users/show.ts
@@ -116,7 +116,7 @@ export default class extends Endpoint { // eslint-
}
return await Promise.all(_users.map(u => this.userEntityService.pack(u, me, {
- detail: true,
+ schema: 'UserDetailed',
})));
} else {
// Lookup user
@@ -146,7 +146,7 @@ export default class extends Endpoint { // eslint-
}
return await this.userEntityService.pack(user, me, {
- detail: true,
+ schema: 'UserDetailed',
});
}
});
diff --git a/packages/backend/src/server/api/openapi/gen-spec.ts b/packages/backend/src/server/api/openapi/gen-spec.ts
index 971a6116bf..5dbd4a01e1 100644
--- a/packages/backend/src/server/api/openapi/gen-spec.ts
+++ b/packages/backend/src/server/api/openapi/gen-spec.ts
@@ -6,9 +6,9 @@
import type { Config } from '@/config.js';
import endpoints, { IEndpoint } from '../endpoints.js';
import { errors as basicErrors } from './errors.js';
-import { schemas, convertSchemaToOpenApiSchema } from './schemas.js';
+import { getSchemas, convertSchemaToOpenApiSchema } from './schemas.js';
-export function genOpenapiSpec(config: Config) {
+export function genOpenapiSpec(config: Config, includeSelfRef = false) {
const spec = {
openapi: '3.1.0',
@@ -30,7 +30,7 @@ export function genOpenapiSpec(config: Config) {
paths: {} as any,
components: {
- schemas: schemas,
+ schemas: getSchemas(includeSelfRef),
securitySchemes: {
bearerAuth: {
@@ -56,7 +56,7 @@ export function genOpenapiSpec(config: Config) {
}
}
- const resSchema = endpoint.meta.res ? convertSchemaToOpenApiSchema(endpoint.meta.res, 'res') : {};
+ const resSchema = endpoint.meta.res ? convertSchemaToOpenApiSchema(endpoint.meta.res, 'res', includeSelfRef) : {};
let desc = (endpoint.meta.description ? endpoint.meta.description : 'No description provided.') + '\n\n';
@@ -71,7 +71,7 @@ export function genOpenapiSpec(config: Config) {
}
const requestType = endpoint.meta.requireFile ? 'multipart/form-data' : 'application/json';
- const schema = { ...convertSchemaToOpenApiSchema(endpoint.params, 'param') };
+ const schema = { ...convertSchemaToOpenApiSchema(endpoint.params, 'param', false) };
if (endpoint.meta.requireFile) {
schema.properties = {
diff --git a/packages/backend/src/server/api/openapi/schemas.ts b/packages/backend/src/server/api/openapi/schemas.ts
index a862a7b742..61ce913b74 100644
--- a/packages/backend/src/server/api/openapi/schemas.ts
+++ b/packages/backend/src/server/api/openapi/schemas.ts
@@ -6,10 +6,10 @@
import type { Schema } from '@/misc/json-schema.js';
import { refs } from '@/misc/json-schema.js';
-export function convertSchemaToOpenApiSchema(schema: Schema, type: 'param' | 'res') {
+export function convertSchemaToOpenApiSchema(schema: Schema, type: 'param' | 'res', includeSelfRef: boolean): any {
// optional, nullable, refはスキーマ定義に含まれないので分離しておく
// eslint-disable-next-line @typescript-eslint/no-unused-vars
- const { optional, nullable, ref, ...res }: any = schema;
+ const { optional, nullable, ref, selfRef, ...res }: any = schema;
if (schema.type === 'object' && schema.properties) {
if (type === 'res') {
@@ -21,20 +21,20 @@ export function convertSchemaToOpenApiSchema(schema: Schema, type: 'param' | 're
}
for (const k of Object.keys(schema.properties)) {
- res.properties[k] = convertSchemaToOpenApiSchema(schema.properties[k], type);
+ res.properties[k] = convertSchemaToOpenApiSchema(schema.properties[k], type, includeSelfRef);
}
}
if (schema.type === 'array' && schema.items) {
- res.items = convertSchemaToOpenApiSchema(schema.items, type);
+ res.items = convertSchemaToOpenApiSchema(schema.items, type, includeSelfRef);
}
for (const o of ['anyOf', 'oneOf', 'allOf'] as const) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- if (o in schema) res[o] = schema[o]!.map(schema => convertSchemaToOpenApiSchema(schema, type));
+ if (o in schema) res[o] = schema[o]!.map(schema => convertSchemaToOpenApiSchema(schema, type, includeSelfRef));
}
- if (type === 'res' && schema.ref) {
+ if (type === 'res' && schema.ref && (!schema.selfRef || includeSelfRef)) {
const $ref = `#/components/schemas/${schema.ref}`;
if (schema.nullable || schema.optional) {
res.allOf = [{ $ref }];
@@ -54,35 +54,37 @@ export function convertSchemaToOpenApiSchema(schema: Schema, type: 'param' | 're
return res;
}
-export const schemas = {
- Error: {
- type: 'object',
- properties: {
- error: {
- type: 'object',
- description: 'An error object.',
- properties: {
- code: {
- type: 'string',
- description: 'An error code. Unique within the endpoint.',
- },
- message: {
- type: 'string',
- description: 'An error message.',
- },
- id: {
- type: 'string',
- format: 'uuid',
- description: 'An error ID. This ID is static.',
+export function getSchemas(includeSelfRef: boolean) {
+ return {
+ Error: {
+ type: 'object',
+ properties: {
+ error: {
+ type: 'object',
+ description: 'An error object.',
+ properties: {
+ code: {
+ type: 'string',
+ description: 'An error code. Unique within the endpoint.',
+ },
+ message: {
+ type: 'string',
+ description: 'An error message.',
+ },
+ id: {
+ type: 'string',
+ format: 'uuid',
+ description: 'An error ID. This ID is static.',
+ },
},
+ required: ['code', 'id', 'message'],
},
- required: ['code', 'id', 'message'],
},
+ required: ['error'],
},
- required: ['error'],
- },
- ...Object.fromEntries(
- Object.entries(refs).map(([key, schema]) => [key, convertSchemaToOpenApiSchema(schema, 'res')]),
- ),
-};
+ ...Object.fromEntries(
+ Object.entries(refs).map(([key, schema]) => [key, convertSchemaToOpenApiSchema(schema, 'res', includeSelfRef)]),
+ ),
+ };
+}
diff --git a/packages/backend/src/server/api/stream/channels/user-list.ts b/packages/backend/src/server/api/stream/channels/user-list.ts
index e0245814c4..0434833df8 100644
--- a/packages/backend/src/server/api/stream/channels/user-list.ts
+++ b/packages/backend/src/server/api/stream/channels/user-list.ts
@@ -43,7 +43,7 @@ class UserListChannel extends Channel {
this.withRenotes = params.withRenotes ?? true;
// Check existence and owner
- const listExist = await this.userListsRepository.exist({
+ const listExist = await this.userListsRepository.exists({
where: {
id: this.listId,
userId: this.user!.id,
diff --git a/packages/backend/test/e2e/note.ts b/packages/backend/test/e2e/note.ts
index 0280b051f5..1bc8cb591c 100644
--- a/packages/backend/test/e2e/note.ts
+++ b/packages/backend/test/e2e/note.ts
@@ -16,12 +16,14 @@ describe('Note', () => {
let alice: misskey.entities.SignupResponse;
let bob: misskey.entities.SignupResponse;
+ let tom: misskey.entities.SignupResponse;
beforeAll(async () => {
const connection = await initTestDb(true);
Notes = connection.getRepository(MiNote);
alice = await signup({ username: 'alice' });
bob = await signup({ username: 'bob' });
+ tom = await signup({ username: 'tom', host: 'example.com' });
}, 1000 * 60 * 2);
test('投稿できる', async () => {
@@ -607,6 +609,77 @@ describe('Note', () => {
assert.strictEqual(note2.status, 200);
assert.strictEqual(note2.body.createdNote.visibility, 'home');
});
+
+ test('禁止ワードを含む投稿はエラーになる (単語指定)', async () => {
+ const prohibited = await api('admin/update-meta', {
+ prohibitedWords: [
+ 'test',
+ ],
+ }, alice);
+
+ assert.strictEqual(prohibited.status, 204);
+
+ await new Promise(x => setTimeout(x, 2));
+
+ const note1 = await api('/notes/create', {
+ text: 'hogetesthuge',
+ }, alice);
+
+ assert.strictEqual(note1.status, 400);
+ assert.strictEqual(note1.body.error.code, 'CONTAINS_PROHIBITED_WORDS');
+ });
+
+ test('禁止ワードを含む投稿はエラーになる (正規表現)', async () => {
+ const prohibited = await api('admin/update-meta', {
+ prohibitedWords: [
+ '/Test/i',
+ ],
+ }, alice);
+
+ assert.strictEqual(prohibited.status, 204);
+
+ const note2 = await api('/notes/create', {
+ text: 'hogetesthuge',
+ }, alice);
+
+ assert.strictEqual(note2.status, 400);
+ assert.strictEqual(note2.body.error.code, 'CONTAINS_PROHIBITED_WORDS');
+ });
+
+ test('禁止ワードを含む投稿はエラーになる (スペースアンド)', async () => {
+ const prohibited = await api('admin/update-meta', {
+ prohibitedWords: [
+ 'Test hoge',
+ ],
+ }, alice);
+
+ assert.strictEqual(prohibited.status, 204);
+
+ const note2 = await api('/notes/create', {
+ text: 'hogeTesthuge',
+ }, alice);
+
+ assert.strictEqual(note2.status, 400);
+ assert.strictEqual(note2.body.error.code, 'CONTAINS_PROHIBITED_WORDS');
+ });
+
+ test('禁止ワードを含んでいてもリモートノートはエラーにならない', async () => {
+ const prohibited = await api('admin/update-meta', {
+ prohibitedWords: [
+ 'test',
+ ],
+ }, alice);
+
+ assert.strictEqual(prohibited.status, 204);
+
+ await new Promise(x => setTimeout(x, 2));
+
+ const note1 = await api('/notes/create', {
+ text: 'hogetesthuge',
+ }, tom);
+
+ assert.strictEqual(note1.status, 200);
+ });
});
describe('notes/delete', () => {
diff --git a/packages/backend/test/unit/ApMfmService.ts b/packages/backend/test/unit/ApMfmService.ts
new file mode 100644
index 0000000000..2b79041c86
--- /dev/null
+++ b/packages/backend/test/unit/ApMfmService.ts
@@ -0,0 +1,44 @@
+import * as assert from 'assert';
+import { Test } from '@nestjs/testing';
+
+import { CoreModule } from '@/core/CoreModule.js';
+import { ApMfmService } from '@/core/activitypub/ApMfmService.js';
+import { GlobalModule } from '@/GlobalModule.js';
+import { MiNote } from '@/models/Note.js';
+
+describe('ApMfmService', () => {
+ let apMfmService: ApMfmService;
+
+ beforeAll(async () => {
+ const app = await Test.createTestingModule({
+ imports: [GlobalModule, CoreModule],
+ }).compile();
+ apMfmService = app.get(ApMfmService);
+ });
+
+ describe('getNoteHtml', () => {
+ test('Do not provide _misskey_content for simple text', () => {
+ const note: MiNote = {
+ text: 'テキスト #タグ @mention 🍊 :emoji: https://example.com',
+ mentionedRemoteUsers: '[]',
+ } as any;
+
+ const { content, noMisskeyContent } = apMfmService.getNoteHtml(note);
+
+ assert.equal(noMisskeyContent, true, 'noMisskeyContent');
+ assert.equal(content, 'テキスト #タグ @mention 🍊 :emoji: https://example.com
', 'content');
+ });
+
+ test('Provide _misskey_content for MFM', () => {
+ const note: MiNote = {
+ text: '$[tada foo]',
+ mentionedRemoteUsers: '[]',
+ } as any;
+
+ const { content, noMisskeyContent } = apMfmService.getNoteHtml(note);
+
+ assert.equal(noMisskeyContent, false, 'noMisskeyContent');
+ assert.equal(content, 'foo
', 'content');
+ });
+ });
+});
diff --git a/packages/backend/test/unit/MfmService.ts b/packages/backend/test/unit/MfmService.ts
index bb8e6981d5..c3827b20d2 100644
--- a/packages/backend/test/unit/MfmService.ts
+++ b/packages/backend/test/unit/MfmService.ts
@@ -33,6 +33,12 @@ describe('MfmService', () => {
const output = 'foo
bar
baz
';
assert.equal(mfmService.toHtml(mfm.parse(input)), output);
});
+
+ test('Do not generate unnecessary span', () => {
+ const input = 'foo $[tada bar]';
+ const output = 'foo bar
';
+ assert.equal(mfmService.toHtml(mfm.parse(input)), output);
+ });
});
describe('fromHtml', () => {
diff --git a/packages/frontend/.storybook/mocks.ts b/packages/frontend/.storybook/mocks.ts
index 80e5157c5a..f0feff9f78 100644
--- a/packages/frontend/.storybook/mocks.ts
+++ b/packages/frontend/.storybook/mocks.ts
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { type SharedOptions, rest } from 'msw';
+import { type SharedOptions, http, HttpResponse } from 'msw';
export const onUnhandledRequest = ((req, print) => {
if (req.url.hostname !== 'localhost' || /^\/(?:client-assets\/|fluent-emojis?\/|iframe.html$|node_modules\/|src\/|sb-|static-assets\/|vite\/)/.test(req.url.pathname)) {
@@ -13,19 +13,31 @@ export const onUnhandledRequest = ((req, print) => {
}) satisfies SharedOptions['onUnhandledRequest'];
export const commonHandlers = [
- rest.get('/fluent-emoji/:codepoints.png', async (req, res, ctx) => {
- const { codepoints } = req.params;
+ http.get('/fluent-emoji/:codepoints.png', async ({ params }) => {
+ const { codepoints } = params;
const value = await fetch(`https://raw.githubusercontent.com/misskey-dev/emojis/main/dist/${codepoints}.png`).then((response) => response.blob());
- return res(ctx.set('Content-Type', 'image/png'), ctx.body(value));
+ return new HttpResponse(value, {
+ headers: {
+ 'Content-Type': 'image/png',
+ },
+ });
}),
- rest.get('/fluent-emojis/:codepoints.png', async (req, res, ctx) => {
- const { codepoints } = req.params;
+ http.get('/fluent-emojis/:codepoints.png', async ({ params }) => {
+ const { codepoints } = params;
const value = await fetch(`https://raw.githubusercontent.com/misskey-dev/emojis/main/dist/${codepoints}.png`).then((response) => response.blob());
- return res(ctx.set('Content-Type', 'image/png'), ctx.body(value));
+ return new HttpResponse(value, {
+ headers: {
+ 'Content-Type': 'image/png',
+ },
+ });
}),
- rest.get('/twemoji/:codepoints.svg', async (req, res, ctx) => {
- const { codepoints } = req.params;
+ http.get('/twemoji/:codepoints.svg', async ({ params }) => {
+ const { codepoints } = params;
const value = await fetch(`https://unpkg.com/@discordapp/twemoji@15.0.2/dist/svg/${codepoints}.svg`).then((response) => response.blob());
- return res(ctx.set('Content-Type', 'image/svg+xml'), ctx.body(value));
+ return new HttpResponse(value, {
+ headers: {
+ 'Content-Type': 'image/svg+xml',
+ },
+ });
}),
];
diff --git a/packages/frontend/@types/global.d.ts b/packages/frontend/@types/global.d.ts
index 7d9335cc52..936e74decf 100644
--- a/packages/frontend/@types/global.d.ts
+++ b/packages/frontend/@types/global.d.ts
@@ -16,3 +16,8 @@ declare const _DATA_TRANSFER_DECK_COLUMN_: string;
// for dev-mode
declare const _LANGS_FULL_: string[][];
+
+// TagCanvas
+interface Window {
+ TagCanvas: any;
+}
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index aa0f9d4fb6..5a3a228f12 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -20,7 +20,7 @@
"@discordapp/twemoji": "15.0.2",
"@github/webauthn-json": "2.1.1",
"@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
- "@misskey-dev/browser-image-resizer": "2.2.1-misskey.10",
+ "@misskey-dev/browser-image-resizer": "2024.1.0",
"@rollup/plugin-json": "6.1.0",
"@rollup/plugin-replace": "5.0.5",
"@rollup/pluginutils": "5.1.0",
@@ -28,8 +28,8 @@
"@tabler/icons-webfont": "2.44.0",
"@twemoji/parser": "15.0.0",
"@vitejs/plugin-vue": "5.0.3",
- "@vue/compiler-sfc": "3.4.3",
- "aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.0.6",
+ "@vue/compiler-sfc": "3.4.18",
+ "aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.2",
"astring": "1.8.6",
"broadcast-channel": "7.0.0",
"buraha": "0.0.1",
@@ -39,7 +39,7 @@
"chartjs-chart-matrix": "2.0.1",
"chartjs-plugin-gradient": "0.6.1",
"chartjs-plugin-zoom": "2.0.1",
- "chromatic": "10.3.1",
+ "chromatic": "10.6.1",
"compare-versions": "6.1.0",
"cropperjs": "2.0.0-beta.4",
"date-fns": "2.30.0",
@@ -60,10 +60,10 @@
"rollup": "4.9.6",
"sanitize-html": "2.11.0",
"sass": "1.70.0",
- "shiki": "0.14.7",
+ "shiki": "1.0.0-beta.3",
"strict-event-emitter-types": "2.0.0",
"textarea-caret": "3.1.0",
- "three": "0.160.0",
+ "three": "0.160.1",
"throttle-debounce": "5.0.0",
"tinycolor2": "1.6.0",
"tsc-alias": "1.8.8",
@@ -71,13 +71,13 @@
"typescript": "5.3.3",
"uuid": "9.0.1",
"v-code-diff": "1.7.2",
- "vite": "5.0.12",
- "vue": "3.4.15",
+ "vite": "5.1.0",
+ "vue": "3.4.18",
"vuedraggable": "next"
},
"devDependencies": {
- "@misskey-dev/eslint-plugin": "^1.0.0",
- "@misskey-dev/summaly": "^5.0.3",
+ "@misskey-dev/eslint-plugin": "1.0.0",
+ "@misskey-dev/summaly": "5.0.3",
"@storybook/addon-actions": "7.6.10",
"@storybook/addon-essentials": "7.6.10",
"@storybook/addon-interactions": "7.6.10",
@@ -96,25 +96,25 @@
"@storybook/types": "7.6.10",
"@storybook/vue3": "7.6.10",
"@storybook/vue3-vite": "7.6.10",
- "@testing-library/vue": "8.0.1",
+ "@testing-library/vue": "8.0.2",
"@types/escape-regexp": "0.0.3",
"@types/estree": "1.0.5",
"@types/matter-js": "0.19.6",
"@types/micromatch": "4.0.6",
- "@types/node": "20.11.5",
+ "@types/node": "20.11.17",
"@types/punycode": "2.1.3",
"@types/sanitize-html": "2.9.5",
"@types/throttle-debounce": "5.0.2",
"@types/tinycolor2": "1.4.6",
- "@types/uuid": "9.0.7",
+ "@types/uuid": "9.0.8",
"@types/ws": "8.5.10",
"@typescript-eslint/eslint-plugin": "6.18.1",
"@typescript-eslint/parser": "6.18.1",
"@vitest/coverage-v8": "0.34.6",
- "@vue/runtime-core": "3.4.15",
+ "@vue/runtime-core": "3.4.18",
"acorn": "8.11.3",
"cross-env": "7.0.3",
- "cypress": "13.6.3",
+ "cypress": "13.6.4",
"eslint": "8.56.0",
"eslint-plugin-import": "2.29.1",
"eslint-plugin-vue": "9.20.1",
@@ -122,10 +122,10 @@
"happy-dom": "10.0.3",
"intersection-observer": "0.12.2",
"micromatch": "4.0.5",
- "msw": "2.1.2",
- "msw-storybook-addon": "1.10.0",
+ "msw": "2.1.7",
+ "msw-storybook-addon": "2.0.0-beta.1",
"nodemon": "3.0.3",
- "prettier": "3.2.4",
+ "prettier": "3.2.5",
"react": "18.2.0",
"react-dom": "18.2.0",
"start-server-and-test": "2.0.3",
@@ -134,7 +134,8 @@
"vite-plugin-turbosnap": "1.0.3",
"vitest": "0.34.6",
"vitest-fetch-mock": "0.2.2",
- "vue-eslint-parser": "9.4.0",
+ "vue-component-type-helpers": "^1.8.27",
+ "vue-eslint-parser": "9.4.2",
"vue-tsc": "1.8.27"
}
}
diff --git a/packages/frontend/src/boot/common.ts b/packages/frontend/src/boot/common.ts
index c67911c9c3..295585af57 100644
--- a/packages/frontend/src/boot/common.ts
+++ b/packages/frontend/src/boot/common.ts
@@ -22,7 +22,7 @@ import { getAccountFromId } from '@/scripts/get-account-from-id.js';
import { deckStore } from '@/ui/deck/deck-store.js';
import { miLocalStorage } from '@/local-storage.js';
import { fetchCustomEmojis } from '@/custom-emojis.js';
-import { setupRouter } from '@/global/router/definition.js';
+import { setupRouter } from '@/router/definition.js';
export async function common(createVue: () => App) {
console.info(`Misskey v${version}`);
@@ -60,12 +60,6 @@ export async function common(createVue: () => App) {
});
}
- const splash = document.getElementById('splash');
- // 念のためnullチェック(HTMLが古い場合があるため(そのうち消す))
- if (splash) splash.addEventListener('transitionend', () => {
- splash.remove();
- });
-
let isClientUpdated = false;
//#region クライアントが更新されたかチェック
@@ -289,5 +283,10 @@ function removeSplash() {
if (splash) {
splash.style.opacity = '0';
splash.style.pointerEvents = 'none';
+
+ // transitionendイベントが発火しない場合があるため
+ window.setTimeout(() => {
+ splash.remove();
+ }, 1000);
}
}
diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts
index eb0ac43da7..fc7c2f136a 100644
--- a/packages/frontend/src/boot/main-boot.ts
+++ b/packages/frontend/src/boot/main-boot.ts
@@ -19,7 +19,7 @@ import { claimAchievement, claimedAchievements } from '@/scripts/achievements.js
import { initializeSw } from '@/scripts/initialize-sw.js';
import { deckStore } from '@/ui/deck/deck-store.js';
import { emojiPicker } from '@/scripts/emoji-picker.js';
-import { mainRouter } from '@/global/router/main.js';
+import { mainRouter } from '@/router/main.js';
export async function mainBoot() {
const { isClientUpdated } = await common(() => createApp(
@@ -81,13 +81,18 @@ export async function mainBoot() {
// ▼南半球
if (month === 7 || month === 8) {
const SnowfallEffect = (await import('@/scripts/snowfall-effect.js')).SnowfallEffect;
- new SnowfallEffect().render();
+ new SnowfallEffect({}).render();
}
} else {
// ▼北半球
if (month === 12 || month === 1) {
const SnowfallEffect = (await import('@/scripts/snowfall-effect.js')).SnowfallEffect;
- new SnowfallEffect().render();
+ new SnowfallEffect({}).render();
+ } else if (month === 3 || month === 4) {
+ const SakuraEffect = (await import('@/scripts/snowfall-effect.js')).SnowfallEffect;
+ new SakuraEffect({
+ sakura: true,
+ }).render();
}
}
}
diff --git a/packages/frontend/src/components/MkAbuseReport.stories.impl.ts b/packages/frontend/src/components/MkAbuseReport.stories.impl.ts
index 77e7c84d5c..dc2697f25c 100644
--- a/packages/frontend/src/components/MkAbuseReport.stories.impl.ts
+++ b/packages/frontend/src/components/MkAbuseReport.stories.impl.ts
@@ -6,7 +6,7 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */
import { action } from '@storybook/addon-actions';
import { StoryObj } from '@storybook/vue3';
-import { rest } from 'msw';
+import { HttpResponse, http } from 'msw';
import { abuseUserReport } from '../../.storybook/fakes.js';
import { commonHandlers } from '../../.storybook/mocks.js';
import MkAbuseReport from './MkAbuseReport.vue';
@@ -44,9 +44,9 @@ export const Default = {
msw: {
handlers: [
...commonHandlers,
- rest.post('/api/admin/resolve-abuse-user-report', async (req, res, ctx) => {
- action('POST /api/admin/resolve-abuse-user-report')(await req.json());
- return res(ctx.json({}));
+ http.post('/api/admin/resolve-abuse-user-report', async ({ request }) => {
+ action('POST /api/admin/resolve-abuse-user-report')(await request.json());
+ return HttpResponse.json({});
}),
],
},
diff --git a/packages/frontend/src/components/MkAbuseReportWindow.stories.impl.ts b/packages/frontend/src/components/MkAbuseReportWindow.stories.impl.ts
index dc842b3d1b..771452cb5f 100644
--- a/packages/frontend/src/components/MkAbuseReportWindow.stories.impl.ts
+++ b/packages/frontend/src/components/MkAbuseReportWindow.stories.impl.ts
@@ -6,7 +6,7 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */
import { action } from '@storybook/addon-actions';
import { StoryObj } from '@storybook/vue3';
-import { rest } from 'msw';
+import { HttpResponse, http } from 'msw';
import { userDetailed } from '../../.storybook/fakes.js';
import { commonHandlers } from '../../.storybook/mocks.js';
import MkAbuseReportWindow from './MkAbuseReportWindow.vue';
@@ -44,9 +44,9 @@ export const Default = {
msw: {
handlers: [
...commonHandlers,
- rest.post('/api/users/report-abuse', async (req, res, ctx) => {
- action('POST /api/users/report-abuse')(await req.json());
- return res(ctx.json({}));
+ http.post('/api/users/report-abuse', async ({ request }) => {
+ action('POST /api/users/report-abuse')(await request.json());
+ return HttpResponse.json({});
}),
],
},
diff --git a/packages/frontend/src/components/MkAbuseReportWindow.vue b/packages/frontend/src/components/MkAbuseReportWindow.vue
index 7814681ea2..39745a97ce 100644
--- a/packages/frontend/src/components/MkAbuseReportWindow.vue
+++ b/packages/frontend/src/components/MkAbuseReportWindow.vue
@@ -39,7 +39,7 @@ import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
const props = defineProps<{
- user: Misskey.entities.User;
+ user: Misskey.entities.UserDetailed;
initialComment?: string;
}>();
diff --git a/packages/frontend/src/components/MkAchievements.stories.impl.ts b/packages/frontend/src/components/MkAchievements.stories.impl.ts
index 6d972467b1..81e9529de2 100644
--- a/packages/frontend/src/components/MkAchievements.stories.impl.ts
+++ b/packages/frontend/src/components/MkAchievements.stories.impl.ts
@@ -5,7 +5,7 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */
import { StoryObj } from '@storybook/vue3';
-import { rest } from 'msw';
+import { HttpResponse, http } from 'msw';
import { userDetailed } from '../../.storybook/fakes.js';
import { commonHandlers } from '../../.storybook/mocks.js';
import MkAchievements from './MkAchievements.vue';
@@ -39,8 +39,8 @@ export const Empty = {
msw: {
handlers: [
...commonHandlers,
- rest.post('/api/users/achievements', (req, res, ctx) => {
- return res(ctx.json([]));
+ http.post('/api/users/achievements', () => {
+ return HttpResponse.json([]);
}),
],
},
@@ -52,8 +52,8 @@ export const All = {
msw: {
handlers: [
...commonHandlers,
- rest.post('/api/users/achievements', (req, res, ctx) => {
- return res(ctx.json(ACHIEVEMENT_TYPES.map((name) => ({ name, unlockedAt: 0 }))));
+ http.post('/api/users/achievements', () => {
+ return HttpResponse.json(ACHIEVEMENT_TYPES.map((name) => ({ name, unlockedAt: 0 })));
}),
],
},
diff --git a/packages/frontend/src/components/MkAchievements.vue b/packages/frontend/src/components/MkAchievements.vue
index 1137eaf970..ff8a9fa1a5 100644
--- a/packages/frontend/src/components/MkAchievements.vue
+++ b/packages/frontend/src/components/MkAchievements.vue
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-
+
-
{{ c.text }}
-
+
{{ c.text }}
+
{{ c.text }}
{{ button.text }}
@@ -20,19 +20,19 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ c.label }}
{{ c.caption }}
-
+
{{ c.label }}
{{ c.caption }}
-
+
{{ c.label }}
{{ c.caption }}
-
+
{{ c.label }}
{{ c.caption }}
-
+
{{ c.label }}
{{ c.caption }}
@@ -42,8 +42,8 @@ SPDX-License-Identifier: AGPL-3.0-only
@@ -52,7 +52,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-
+
@@ -68,7 +68,7 @@ import MkInput from '@/components/MkInput.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import MkTextarea from '@/components/MkTextarea.vue';
import MkSelect from '@/components/MkSelect.vue';
-import { AsUiComponent } from '@/scripts/aiscript/ui.js';
+import { AsUiComponent, AsUiRoot, AsUiPostFormButton } from '@/scripts/aiscript/ui.js';
import MkFolder from '@/components/MkFolder.vue';
import MkPostForm from '@/components/MkPostForm.vue';
@@ -85,20 +85,32 @@ const props = withDefaults(defineProps<{
const c = props.component;
function g(id) {
- return props.components.find(x => x.value.id === id).value;
+ const v = props.components.find(x => x.value.id === id)?.value;
+ if (v) return v;
+
+ return {
+ id: 'dummy',
+ type: 'root',
+ children: [],
+ } as AsUiRoot;
}
-const valueForSwitch = ref(c.default ?? false);
+const valueForSwitch = ref('default' in c && typeof c.default === 'boolean' ? c.default : false);
function onSwitchUpdate(v) {
valueForSwitch.value = v;
- if (c.onChange) c.onChange(v);
+ if ('onChange' in c && c.onChange) {
+ c.onChange(v as never);
+ }
}
function openPostForm() {
+ const form = (c as AsUiPostFormButton).form;
+ if (!form) return;
+
os.post({
- initialText: c.form.text,
- initialCw: c.form.cw,
+ initialText: form.text,
+ initialCw: form.cw,
instant: true,
});
}
diff --git a/packages/frontend/src/components/MkAutocomplete.stories.impl.ts b/packages/frontend/src/components/MkAutocomplete.stories.impl.ts
index 969519386f..3ca8c5b864 100644
--- a/packages/frontend/src/components/MkAutocomplete.stories.impl.ts
+++ b/packages/frontend/src/components/MkAutocomplete.stories.impl.ts
@@ -8,7 +8,7 @@ import { action } from '@storybook/addon-actions';
import { expect } from '@storybook/jest';
import { userEvent, waitFor, within } from '@storybook/testing-library';
import { StoryObj } from '@storybook/vue3';
-import { rest } from 'msw';
+import { HttpResponse, http } from 'msw';
import { userDetailed } from '../../.storybook/fakes.js';
import { commonHandlers } from '../../.storybook/mocks.js';
import MkAutocomplete from './MkAutocomplete.vue';
@@ -99,11 +99,11 @@ export const User = {
msw: {
handlers: [
...commonHandlers,
- rest.post('/api/users/search-by-username-and-host', (req, res, ctx) => {
- return res(ctx.json([
+ http.post('/api/users/search-by-username-and-host', () => {
+ return HttpResponse.json([
userDetailed('44', 'mizuki', 'misskey-hub.net', 'Mizuki'),
userDetailed('49', 'momoko', 'misskey-hub.net', 'Momoko'),
- ]));
+ ]);
}),
],
},
@@ -132,12 +132,12 @@ export const Hashtag = {
msw: {
handlers: [
...commonHandlers,
- rest.post('/api/hashtags/search', (req, res, ctx) => {
- return res(ctx.json([
+ http.post('/api/hashtags/search', () => {
+ return HttpResponse.json([
'気象警報注意報',
'気象警報',
'気象情報',
- ]));
+ ]);
}),
],
},
diff --git a/packages/frontend/src/components/MkAutocomplete.vue b/packages/frontend/src/components/MkAutocomplete.vue
index 9d03eab2cb..a428d85c76 100644
--- a/packages/frontend/src/components/MkAutocomplete.vue
+++ b/packages/frontend/src/components/MkAutocomplete.vue
@@ -431,7 +431,7 @@ function applySelect() {
function chooseUser() {
props.close();
- os.selectUser().then(user => {
+ os.selectUser({ includeSelf: true }).then(user => {
complete('user', user);
props.textarea.focus();
});
diff --git a/packages/frontend/src/components/MkAvatars.stories.impl.ts b/packages/frontend/src/components/MkAvatars.stories.impl.ts
index d41b64695f..a9b4540ca9 100644
--- a/packages/frontend/src/components/MkAvatars.stories.impl.ts
+++ b/packages/frontend/src/components/MkAvatars.stories.impl.ts
@@ -5,7 +5,7 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */
import { StoryObj } from '@storybook/vue3';
-import { rest } from 'msw';
+import { HttpResponse, http } from 'msw';
import { userDetailed } from '../../.storybook/fakes.js';
import { commonHandlers } from '../../.storybook/mocks.js';
import MkAvatars from './MkAvatars.vue';
@@ -38,12 +38,12 @@ export const Default = {
msw: {
handlers: [
...commonHandlers,
- rest.post('/api/users/show', (req, res, ctx) => {
- return res(ctx.json([
+ http.post('/api/users/show', () => {
+ return HttpResponse.json([
userDetailed('17'),
userDetailed('20'),
userDetailed('18'),
- ]));
+ ]);
}),
],
},
diff --git a/packages/frontend/src/components/MkButton.vue b/packages/frontend/src/components/MkButton.vue
index 8d4631968d..70de6a851a 100644
--- a/packages/frontend/src/components/MkButton.vue
+++ b/packages/frontend/src/components/MkButton.vue
@@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
diff --git a/packages/frontend/src/components/MkCaptcha.vue b/packages/frontend/src/components/MkCaptcha.vue
index f60c721eae..7aa08cf51f 100644
--- a/packages/frontend/src/components/MkCaptcha.vue
+++ b/packages/frontend/src/components/MkCaptcha.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-
{{ i18n.ts.waiting }}
+
Loading
@@ -17,7 +17,6 @@ SPDX-License-Identifier: AGPL-3.0-only
-
diff --git a/packages/frontend/src/components/MkCode.vue b/packages/frontend/src/components/MkCode.vue
index 251e6ade00..6c14738937 100644
--- a/packages/frontend/src/components/MkCode.vue
+++ b/packages/frontend/src/components/MkCode.vue
@@ -53,7 +53,6 @@ function copy() {
}
.codeBlockCopyButton {
- color: #D4D4D4;
position: absolute;
top: 8px;
right: 8px;
@@ -67,8 +66,7 @@ function copy() {
.codeBlockFallbackRoot {
display: block;
overflow-wrap: anywhere;
- color: #D4D4D4;
- background: #1E1E1E;
+ background: var(--bg);
padding: 1em;
margin: .5em 0;
overflow: auto;
@@ -93,8 +91,8 @@ function copy() {
border-radius: 8px;
padding: 24px;
margin-top: 4px;
- color: #D4D4D4;
- background: #1E1E1E;
+ color: var(--fg);
+ background: var(--bg);
}
.codePlaceholderContainer {
diff --git a/packages/frontend/src/components/MkCodeEditor.vue b/packages/frontend/src/components/MkCodeEditor.vue
index c8c3deb610..3cf8234e72 100644
--- a/packages/frontend/src/components/MkCodeEditor.vue
+++ b/packages/frontend/src/components/MkCodeEditor.vue
@@ -196,10 +196,11 @@ watch(v, newValue => {
resize: none;
text-align: left;
color: transparent;
- caret-color: rgb(225, 228, 232);
+ caret-color: var(--fg);
background-color: transparent;
border: 0;
border-radius: 6px;
+ box-sizing: border-box;
outline: 0;
min-width: calc(100% - 24px);
height: 100%;
@@ -210,6 +211,6 @@ watch(v, newValue => {
}
.textarea::selection {
- color: #fff;
+ color: var(--bg);
}
diff --git a/packages/frontend/src/components/MkCodeInline.vue b/packages/frontend/src/components/MkCodeInline.vue
index 5340c1fd5f..6a9d97ab5a 100644
--- a/packages/frontend/src/components/MkCodeInline.vue
+++ b/packages/frontend/src/components/MkCodeInline.vue
@@ -18,8 +18,7 @@ const props = defineProps<{
display: inline-block;
font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;
overflow-wrap: anywhere;
- color: #D4D4D4;
- background: #1E1E1E;
+ background: var(--bg);
padding: .1em;
border-radius: .3em;
}
diff --git a/packages/frontend/src/components/MkColorInput.vue b/packages/frontend/src/components/MkColorInput.vue
index a7a3eff5af..118aeeeb37 100644
--- a/packages/frontend/src/components/MkColorInput.vue
+++ b/packages/frontend/src/components/MkColorInput.vue
@@ -41,8 +41,8 @@ const { modelValue } = toRefs(props);
const v = ref(modelValue.value);
const inputEl = shallowRef
();
-const onInput = (ev: KeyboardEvent) => {
- emit('update:modelValue', v.value);
+const onInput = () => {
+ emit('update:modelValue', v.value ?? '');
};
diff --git a/packages/frontend/src/components/MkContextMenu.vue b/packages/frontend/src/components/MkContextMenu.vue
index e29cf472f7..d330d66b28 100644
--- a/packages/frontend/src/components/MkContextMenu.vue
+++ b/packages/frontend/src/components/MkContextMenu.vue
@@ -44,8 +44,8 @@ onMounted(() => {
let left = props.ev.pageX + 1; // 間違って右ダブルクリックした場合に意図せずアイテムがクリックされるのを防ぐため + 1
let top = props.ev.pageY + 1; // 間違って右ダブルクリックした場合に意図せずアイテムがクリックされるのを防ぐため + 1
- const width = rootEl.value.offsetWidth;
- const height = rootEl.value.offsetHeight;
+ const width = rootEl.value!.offsetWidth;
+ const height = rootEl.value!.offsetHeight;
if (left + width - window.pageXOffset >= (window.innerWidth - SCROLLBAR_THICKNESS)) {
left = (window.innerWidth - SCROLLBAR_THICKNESS) - width + window.pageXOffset;
@@ -63,8 +63,10 @@ onMounted(() => {
left = 0;
}
- rootEl.value.style.top = `${top}px`;
- rootEl.value.style.left = `${left}px`;
+ if (rootEl.value) {
+ rootEl.value.style.top = `${top}px`;
+ rootEl.value.style.left = `${left}px`;
+ }
document.body.addEventListener('mousedown', onMousedown);
});
diff --git a/packages/frontend/src/components/MkCropperDialog.vue b/packages/frontend/src/components/MkCropperDialog.vue
index 0a1ddd3171..745453646c 100644
--- a/packages/frontend/src/components/MkCropperDialog.vue
+++ b/packages/frontend/src/components/MkCropperDialog.vue
@@ -63,18 +63,25 @@ const loading = ref(true);
const ok = async () => {
const promise = new Promise(async (res) => {
- const croppedCanvas = await cropper?.getCropperSelection()?.$toCanvas();
+ const croppedImage = await cropper?.getCropperImage();
+ const croppedSection = await cropper?.getCropperSelection();
+
+ // 拡大率を計算し、(ほぼ)元の大きさに戻す
+ const zoomedRate = croppedImage.getBoundingClientRect().width / croppedImage.clientWidth;
+ const widthToRender = croppedSection.getBoundingClientRect().width / zoomedRate;
+
+ const croppedCanvas = await croppedSection?.$toCanvas({ width: widthToRender });
croppedCanvas?.toBlob(blob => {
if (!blob) return;
const formData = new FormData();
formData.append('file', blob);
formData.append('name', `cropped_${props.file.name}`);
formData.append('isSensitive', props.file.isSensitive ? 'true' : 'false');
- formData.append('comment', props.file.comment ?? 'null');
+ if (props.file.comment) { formData.append('comment', props.file.comment);}
formData.append('i', $i!.token);
- if (props.uploadFolder || props.uploadFolder === null) {
- formData.append('folderId', props.uploadFolder ?? 'null');
- } else if (defaultStore.state.uploadFolder) {
+ if (props.uploadFolder) {
+ formData.append('folderId', props.uploadFolder);
+ } else if (props.uploadFolder !== null && defaultStore.state.uploadFolder) {
formData.append('folderId', defaultStore.state.uploadFolder);
}
diff --git a/packages/frontend/src/components/MkCwButton.vue b/packages/frontend/src/components/MkCwButton.vue
index ca19a2122d..c7395ad3d5 100644
--- a/packages/frontend/src/components/MkCwButton.vue
+++ b/packages/frontend/src/components/MkCwButton.vue
@@ -10,6 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
diff --git a/packages/frontend/src/components/MkDialog.vue b/packages/frontend/src/components/MkDialog.vue
index 3fc9f0e357..706c3d5f8e 100644
--- a/packages/frontend/src/components/MkDialog.vue
+++ b/packages/frontend/src/components/MkDialog.vue
@@ -30,8 +30,8 @@ SPDX-License-Identifier: AGPL-3.0-only
-
-
+
+
@@ -125,7 +125,7 @@ const selectedValue = ref(props.select?.default ?? null);
const okButtonDisabledReason = computed(() => {
if (props.input) {
if (props.input.minLength) {
- if ((inputValue.value || inputValue.value === '') && (inputValue.value as string).length < props.input.minLength) {
+ if (inputValue.value == null || (inputValue.value as string).length < props.input.minLength) {
return 'charactersBelow';
}
}
diff --git a/packages/frontend/src/components/MkDrive.file.vue b/packages/frontend/src/components/MkDrive.file.vue
index 8a74319f29..4ff1ae30ab 100644
--- a/packages/frontend/src/components/MkDrive.file.vue
+++ b/packages/frontend/src/components/MkDrive.file.vue
@@ -47,7 +47,7 @@ import { i18n } from '@/i18n.js';
import { $i } from '@/account.js';
import { getDriveFileMenu } from '@/scripts/get-drive-file-menu.js';
import { deviceKind } from '@/scripts/device-kind.js';
-import { useRouter } from '@/global/router/supplier.js';
+import { useRouter } from '@/router/supplier.js';
const router = useRouter();
diff --git a/packages/frontend/src/components/MkDrive.folder.vue b/packages/frontend/src/components/MkDrive.folder.vue
index 0d02aa5cb7..b3569bd009 100644
--- a/packages/frontend/src/components/MkDrive.folder.vue
+++ b/packages/frontend/src/components/MkDrive.folder.vue
@@ -205,7 +205,7 @@ function onDragend() {
}
function go() {
- emit('move', props.folder.id);
+ emit('move', props.folder);
}
function rename() {
diff --git a/packages/frontend/src/components/MkDrive.vue b/packages/frontend/src/components/MkDrive.vue
index 560d5502d4..80206f86f7 100644
--- a/packages/frontend/src/components/MkDrive.vue
+++ b/packages/frontend/src/components/MkDrive.vue
@@ -98,6 +98,7 @@ SPDX-License-Identifier: AGPL-3.0-only
import { nextTick, onActivated, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue';
import * as Misskey from 'misskey-js';
import MkButton from './MkButton.vue';
+import type { MenuItem } from '@/types/menu.js';
import XNavFolder from '@/components/MkDrive.navFolder.vue';
import XFolder from '@/components/MkDrive.folder.vue';
import XFile from '@/components/MkDrive.file.vue';
@@ -427,7 +428,7 @@ function chooseFolder(folderToChoose: Misskey.entities.DriveFolder) {
}
}
-function move(target?: Misskey.entities.DriveFolder) {
+function move(target?: Misskey.entities.DriveFolder | Misskey.entities.DriveFolder['id' | 'parentId']) {
if (!target) {
goRoot();
return;
@@ -613,7 +614,7 @@ function fetchMoreFiles() {
}
function getMenu() {
- return [{
+ const menu: MenuItem[] = [{
type: 'switch',
text: i18n.ts.keepOriginalUploading,
ref: keepOriginal,
@@ -634,7 +635,7 @@ function getMenu() {
}, folder.value ? {
text: i18n.ts.renameFolder,
icon: 'ti ti-forms',
- action: () => { renameFolder(folder.value); },
+ action: () => { if (folder.value) renameFolder(folder.value); },
} : undefined, folder.value ? {
text: i18n.ts.deleteFolder,
icon: 'ti ti-trash',
@@ -644,6 +645,8 @@ function getMenu() {
icon: 'ti ti-folder-plus',
action: () => { createFolder(); },
}];
+
+ return menu;
}
function showMenu(ev: MouseEvent) {
diff --git a/packages/frontend/src/components/MkEmojiPicker.section.vue b/packages/frontend/src/components/MkEmojiPicker.section.vue
index 14f3f5770f..43a6664d11 100644
--- a/packages/frontend/src/components/MkEmojiPicker.section.vue
+++ b/packages/frontend/src/components/MkEmojiPicker.section.vue
@@ -27,7 +27,7 @@ SPDX-License-Identifier: AGPL-3.0-only
- (:{{ customEmojiTree.length }} :{{ emojis.length }})
+ (:{{ customEmojiTree?.length }} :{{ emojis.length }})
import { ref, computed, Ref } from 'vue';
import { CustomEmojiFolderTree, getEmojiName } from '@/scripts/emojilist.js';
-import { i18n } from '../i18n.js';
+import { i18n } from '@/i18n.js';
import { customEmojis } from '@/custom-emojis.js';
import MkEmojiPickerSection from '@/components/MkEmojiPicker.section.vue';
@@ -87,7 +87,7 @@ function computeButtonTitle(ev: MouseEvent): void {
elm.title = getEmojiName(emoji) ?? emoji;
}
-function nestedChosen(emoji: any, ev?: MouseEvent) {
+function nestedChosen(emoji: any, ev: MouseEvent) {
emit('chosen', emoji, ev);
}
diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue
index 84424c58ed..f5ab7a2e29 100644
--- a/packages/frontend/src/components/MkEmojiPicker.vue
+++ b/packages/frontend/src/components/MkEmojiPicker.vue
@@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-
+