mirror of
https://github.com/sim1222/misskey.git
synced 2025-08-04 07:26:29 +09:00
refactor: APIエンドポイントファイルの定義を良い感じにする (#8154)
* Fix API Schema Error * Delete SimpleSchema/SimpleObj and Move schemas to dedicated files * Userのスキーマを分割してみる * define packMany type * add , * Ensure enum schema and Make "as const" put once * test? * Revert "test?" This reverts commit 97dc9bfa70851bfb7d1cf38e883f8df20fb78b79. * Revert "Fix API Schema Error" This reverts commit 21b6176d974ed8e3eb73723ad21a105c5d297323. * ✌️ * clean up * test? * wip * wip * better schema def * ✌️ * fix * add minLength property * wip * wip * wip * anyOf/oneOf/allOfに対応? ~ relation.ts * refactor! * Define MinimumSchema * wip * wip * anyOf/oneOf/allOfが動作するようにUnionSchemaTypeを修正 * anyOf/oneOf/allOfが動作するようにUnionSchemaTypeを修正 * Update packages/backend/src/misc/schema.ts Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com> * fix * array oneOfをより正確な型に * array oneOfをより正確な型に * wip * ✌️ * なんかもういろいろ * remove * very good schema * api schema * wip * refactor: awaitAllの型定義を変えてみる * fix * specify types in awaitAll * specify types in awaitAll * ✌️ * wip * ... * ✌️ * AllowDateはやめておく * 不必要なoptional: false, nullable: falseを廃止 * Packedが展開されないように * 続packed * wip * define note type * wip * UserDetailedをMeDetailedかUserDetailedNotMeかを区別できるように * wip * wip * wip specify user type of other schemas * ok * convertSchemaToOpenApiSchemaを改修 * convertSchemaToOpenApiSchemaを改修 * Fix * fix * ✌️ * wip * 分割代入ではなくallOfで定義するように Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com>
This commit is contained in:
@ -4,11 +4,19 @@ import { User, ILocalUser, IRemoteUser } from '@/models/entities/user';
|
||||
import { Notes, NoteUnreads, FollowRequests, Notifications, MessagingMessages, UserNotePinings, Followings, Blockings, Mutings, UserProfiles, UserSecurityKeys, UserGroupJoinings, Pages, Announcements, AnnouncementReads, Antennas, AntennaNotes, ChannelFollowings, Instances } from '../index';
|
||||
import config from '@/config/index';
|
||||
import { Packed } from '@/misc/schema';
|
||||
import { awaitAll } from '@/prelude/await-all';
|
||||
import { awaitAll, Promiseable } from '@/prelude/await-all';
|
||||
import { populateEmojis } from '@/misc/populate-emojis';
|
||||
import { getAntennas } from '@/misc/antenna-cache';
|
||||
import { USER_ACTIVE_THRESHOLD, USER_ONLINE_THRESHOLD } from '@/const';
|
||||
|
||||
type IsUserDetailed<Detailed extends boolean> = Detailed extends true ? Packed<'UserDetailed'> : Packed<'UserLite'>;
|
||||
type IsMeAndIsUserDetailed<ExpectsMe extends boolean | null, Detailed extends boolean> =
|
||||
Detailed extends true ?
|
||||
ExpectsMe extends true ? Packed<'MeDetailed'> :
|
||||
ExpectsMe extends false ? Packed<'UserDetailedNotMe'> :
|
||||
Packed<'UserDetailed'> :
|
||||
Packed<'UserLite'>;
|
||||
|
||||
@EntityRepository(User)
|
||||
export class UserRepository extends Repository<User> {
|
||||
public async getRelation(me: User['id'], target: User['id']) {
|
||||
@ -144,7 +152,7 @@ export class UserRepository extends Repository<User> {
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
public getOnlineStatus(user: User): string {
|
||||
public getOnlineStatus(user: User): 'unknown' | 'online' | 'active' | 'offline' {
|
||||
if (user.hideOnlineStatus) return 'unknown';
|
||||
if (user.lastActiveDate == null) return 'unknown';
|
||||
const elapsed = Date.now() - user.lastActiveDate.getTime();
|
||||
@ -163,14 +171,14 @@ export class UserRepository extends Repository<User> {
|
||||
}
|
||||
}
|
||||
|
||||
public async pack(
|
||||
public async pack<ExpectsMe extends boolean | null = null, D extends boolean = false>(
|
||||
src: User['id'] | User,
|
||||
me?: { id: User['id'] } | null | undefined,
|
||||
options?: {
|
||||
detail?: boolean,
|
||||
detail?: D,
|
||||
includeSecrets?: boolean,
|
||||
}
|
||||
): Promise<Packed<'User'>> {
|
||||
): Promise<IsMeAndIsUserDetailed<ExpectsMe, D>> {
|
||||
const opts = Object.assign({
|
||||
detail: false,
|
||||
includeSecrets: false,
|
||||
@ -178,8 +186,9 @@ export class UserRepository extends Repository<User> {
|
||||
|
||||
const user = typeof src === 'object' ? src : await this.findOneOrFail(src);
|
||||
const meId = me ? me.id : null;
|
||||
const isMe = meId === user.id;
|
||||
|
||||
const relation = meId && (meId !== user.id) && opts.detail ? await this.getRelation(meId, user.id) : null;
|
||||
const relation = meId && !isMe && opts.detail ? await this.getRelation(meId, user.id) : null;
|
||||
const pins = opts.detail ? await UserNotePinings.createQueryBuilder('pin')
|
||||
.where('pin.userId = :userId', { userId: user.id })
|
||||
.innerJoinAndSelect('pin.note', 'note')
|
||||
@ -188,12 +197,12 @@ export class UserRepository extends Repository<User> {
|
||||
const profile = opts.detail ? await UserProfiles.findOneOrFail(user.id) : null;
|
||||
|
||||
const followingCount = profile == null ? null :
|
||||
(profile.ffVisibility === 'public') || (meId === user.id) ? user.followingCount :
|
||||
(profile.ffVisibility === 'public') || isMe ? user.followingCount :
|
||||
(profile.ffVisibility === 'followers') && (relation && relation.isFollowing) ? user.followingCount :
|
||||
null;
|
||||
|
||||
const followersCount = profile == null ? null :
|
||||
(profile.ffVisibility === 'public') || (meId === user.id) ? user.followersCount :
|
||||
(profile.ffVisibility === 'public') || isMe ? user.followersCount :
|
||||
(profile.ffVisibility === 'followers') && (relation && relation.isFollowing) ? user.followersCount :
|
||||
null;
|
||||
|
||||
@ -227,12 +236,11 @@ export class UserRepository extends Repository<User> {
|
||||
uri: user.uri,
|
||||
createdAt: user.createdAt.toISOString(),
|
||||
updatedAt: user.updatedAt ? user.updatedAt.toISOString() : null,
|
||||
lastFetchedAt: user.lastFetchedAt?.toISOString(),
|
||||
lastFetchedAt: user.lastFetchedAt ? user.lastFetchedAt.toISOString() : null,
|
||||
bannerUrl: user.bannerUrl,
|
||||
bannerBlurhash: user.bannerBlurhash,
|
||||
bannerColor: null, // 後方互換性のため
|
||||
isLocked: user.isLocked,
|
||||
isModerator: user.isModerator || falsy,
|
||||
isSilenced: user.isSilenced || falsy,
|
||||
isSuspended: user.isSuspended || falsy,
|
||||
description: profile!.description,
|
||||
@ -260,7 +268,7 @@ export class UserRepository extends Repository<User> {
|
||||
: false,
|
||||
} : {}),
|
||||
|
||||
...(opts.detail && meId === user.id ? {
|
||||
...(opts.detail && isMe ? {
|
||||
avatarId: user.avatarId,
|
||||
bannerId: user.bannerId,
|
||||
injectFeaturedNote: profile!.injectFeaturedNote,
|
||||
@ -315,19 +323,19 @@ export class UserRepository extends Repository<User> {
|
||||
isBlocked: relation.isBlocked,
|
||||
isMuted: relation.isMuted,
|
||||
} : {}),
|
||||
};
|
||||
} as Promiseable<Packed<'User'>> as Promiseable<IsMeAndIsUserDetailed<ExpectsMe, D>>;
|
||||
|
||||
return await awaitAll(packed);
|
||||
}
|
||||
|
||||
public packMany(
|
||||
public packMany<D extends boolean = false>(
|
||||
users: (User['id'] | User)[],
|
||||
me?: { id: User['id'] } | null | undefined,
|
||||
options?: {
|
||||
detail?: boolean,
|
||||
detail?: D,
|
||||
includeSecrets?: boolean,
|
||||
}
|
||||
) {
|
||||
): Promise<IsUserDetailed<D>[]> {
|
||||
return Promise.all(users.map(u => this.pack(u, me, options)));
|
||||
}
|
||||
|
||||
@ -352,313 +360,3 @@ export class UserRepository extends Repository<User> {
|
||||
public validateBirthday = $.str.match(/^([0-9]{4})-([0-9]{2})-([0-9]{2})$/);
|
||||
//#endregion
|
||||
}
|
||||
|
||||
export const packedUserSchema = {
|
||||
type: 'object' as const,
|
||||
nullable: false as const, optional: false as const,
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string' as const,
|
||||
nullable: false as const, optional: false as const,
|
||||
format: 'id',
|
||||
example: 'xxxxxxxxxx',
|
||||
},
|
||||
name: {
|
||||
type: 'string' as const,
|
||||
nullable: true as const, optional: false as const,
|
||||
example: '藍',
|
||||
},
|
||||
username: {
|
||||
type: 'string' as const,
|
||||
nullable: false as const, optional: false as const,
|
||||
example: 'ai',
|
||||
},
|
||||
host: {
|
||||
type: 'string' as const,
|
||||
nullable: true as const, optional: false as const,
|
||||
example: 'misskey.example.com',
|
||||
},
|
||||
avatarUrl: {
|
||||
type: 'string' as const,
|
||||
format: 'url',
|
||||
nullable: true as const, optional: false as const,
|
||||
},
|
||||
avatarBlurhash: {
|
||||
type: 'any' as const,
|
||||
nullable: true as const, optional: false as const,
|
||||
},
|
||||
avatarColor: {
|
||||
type: 'any' as const,
|
||||
nullable: true as const, optional: false as const,
|
||||
default: null,
|
||||
},
|
||||
isAdmin: {
|
||||
type: 'boolean' as const,
|
||||
nullable: false as const, optional: true as const,
|
||||
default: false,
|
||||
},
|
||||
isModerator: {
|
||||
type: 'boolean' as const,
|
||||
nullable: false as const, optional: true as const,
|
||||
default: false,
|
||||
},
|
||||
isBot: {
|
||||
type: 'boolean' as const,
|
||||
nullable: false as const, optional: true as const,
|
||||
},
|
||||
isCat: {
|
||||
type: 'boolean' as const,
|
||||
nullable: false as const, optional: true as const,
|
||||
},
|
||||
emojis: {
|
||||
type: 'array' as const,
|
||||
nullable: false as const, optional: false as const,
|
||||
items: {
|
||||
type: 'object' as const,
|
||||
nullable: false as const, optional: false as const,
|
||||
properties: {
|
||||
name: {
|
||||
type: 'string' as const,
|
||||
nullable: false as const, optional: false as const,
|
||||
},
|
||||
url: {
|
||||
type: 'string' as const,
|
||||
nullable: false as const, optional: false as const,
|
||||
format: 'url',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
url: {
|
||||
type: 'string' as const,
|
||||
format: 'url',
|
||||
nullable: true as const, optional: true as const,
|
||||
},
|
||||
createdAt: {
|
||||
type: 'string' as const,
|
||||
nullable: false as const, optional: true as const,
|
||||
format: 'date-time',
|
||||
},
|
||||
updatedAt: {
|
||||
type: 'string' as const,
|
||||
nullable: true as const, optional: true as const,
|
||||
format: 'date-time',
|
||||
},
|
||||
bannerUrl: {
|
||||
type: 'string' as const,
|
||||
format: 'url',
|
||||
nullable: true as const, optional: true as const,
|
||||
},
|
||||
bannerBlurhash: {
|
||||
type: 'any' as const,
|
||||
nullable: true as const, optional: true as const,
|
||||
},
|
||||
bannerColor: {
|
||||
type: 'any' as const,
|
||||
nullable: true as const, optional: true as const,
|
||||
default: null,
|
||||
},
|
||||
isLocked: {
|
||||
type: 'boolean' as const,
|
||||
nullable: false as const, optional: true as const,
|
||||
},
|
||||
isSuspended: {
|
||||
type: 'boolean' as const,
|
||||
nullable: false as const, optional: true as const,
|
||||
example: false,
|
||||
},
|
||||
description: {
|
||||
type: 'string' as const,
|
||||
nullable: true as const, optional: true as const,
|
||||
example: 'Hi masters, I am Ai!',
|
||||
},
|
||||
location: {
|
||||
type: 'string' as const,
|
||||
nullable: true as const, optional: true as const,
|
||||
},
|
||||
birthday: {
|
||||
type: 'string' as const,
|
||||
nullable: true as const, optional: true as const,
|
||||
example: '2018-03-12',
|
||||
},
|
||||
fields: {
|
||||
type: 'array' as const,
|
||||
nullable: false as const, optional: true as const,
|
||||
items: {
|
||||
type: 'object' as const,
|
||||
nullable: false as const, optional: false as const,
|
||||
properties: {
|
||||
name: {
|
||||
type: 'string' as const,
|
||||
nullable: false as const, optional: false as const,
|
||||
},
|
||||
value: {
|
||||
type: 'string' as const,
|
||||
nullable: false as const, optional: false as const,
|
||||
},
|
||||
},
|
||||
maxLength: 4,
|
||||
},
|
||||
},
|
||||
followersCount: {
|
||||
type: 'number' as const,
|
||||
nullable: false as const, optional: true as const,
|
||||
},
|
||||
followingCount: {
|
||||
type: 'number' as const,
|
||||
nullable: false as const, optional: true as const,
|
||||
},
|
||||
notesCount: {
|
||||
type: 'number' as const,
|
||||
nullable: false as const, optional: true as const,
|
||||
},
|
||||
pinnedNoteIds: {
|
||||
type: 'array' as const,
|
||||
nullable: false as const, optional: true as const,
|
||||
items: {
|
||||
type: 'string' as const,
|
||||
nullable: false as const, optional: false as const,
|
||||
format: 'id',
|
||||
},
|
||||
},
|
||||
pinnedNotes: {
|
||||
type: 'array' as const,
|
||||
nullable: false as const, optional: true as const,
|
||||
items: {
|
||||
type: 'object' as const,
|
||||
nullable: false as const, optional: false as const,
|
||||
ref: 'Note' as const,
|
||||
},
|
||||
},
|
||||
pinnedPageId: {
|
||||
type: 'string' as const,
|
||||
nullable: true as const, optional: true as const,
|
||||
},
|
||||
pinnedPage: {
|
||||
type: 'object' as const,
|
||||
nullable: true as const, optional: true as const,
|
||||
ref: 'Page' as const,
|
||||
},
|
||||
twoFactorEnabled: {
|
||||
type: 'boolean' as const,
|
||||
nullable: false as const, optional: true as const,
|
||||
default: false,
|
||||
},
|
||||
usePasswordLessLogin: {
|
||||
type: 'boolean' as const,
|
||||
nullable: false as const, optional: true as const,
|
||||
default: false,
|
||||
},
|
||||
securityKeys: {
|
||||
type: 'boolean' as const,
|
||||
nullable: false as const, optional: true as const,
|
||||
default: false,
|
||||
},
|
||||
avatarId: {
|
||||
type: 'string' as const,
|
||||
nullable: true as const, optional: true as const,
|
||||
format: 'id',
|
||||
},
|
||||
bannerId: {
|
||||
type: 'string' as const,
|
||||
nullable: true as const, optional: true as const,
|
||||
format: 'id',
|
||||
},
|
||||
autoWatch: {
|
||||
type: 'boolean' as const,
|
||||
nullable: false as const, optional: true as const,
|
||||
},
|
||||
injectFeaturedNote: {
|
||||
type: 'boolean' as const,
|
||||
nullable: false as const, optional: true as const,
|
||||
},
|
||||
alwaysMarkNsfw: {
|
||||
type: 'boolean' as const,
|
||||
nullable: false as const, optional: true as const,
|
||||
},
|
||||
carefulBot: {
|
||||
type: 'boolean' as const,
|
||||
nullable: false as const, optional: true as const,
|
||||
},
|
||||
autoAcceptFollowed: {
|
||||
type: 'boolean' as const,
|
||||
nullable: false as const, optional: true as const,
|
||||
},
|
||||
hasUnreadSpecifiedNotes: {
|
||||
type: 'boolean' as const,
|
||||
nullable: false as const, optional: true as const,
|
||||
},
|
||||
hasUnreadMentions: {
|
||||
type: 'boolean' as const,
|
||||
nullable: false as const, optional: true as const,
|
||||
},
|
||||
hasUnreadAnnouncement: {
|
||||
type: 'boolean' as const,
|
||||
nullable: false as const, optional: true as const,
|
||||
},
|
||||
hasUnreadAntenna: {
|
||||
type: 'boolean' as const,
|
||||
nullable: false as const, optional: true as const,
|
||||
},
|
||||
hasUnreadChannel: {
|
||||
type: 'boolean' as const,
|
||||
nullable: false as const, optional: true as const,
|
||||
},
|
||||
hasUnreadMessagingMessage: {
|
||||
type: 'boolean' as const,
|
||||
nullable: false as const, optional: true as const,
|
||||
},
|
||||
hasUnreadNotification: {
|
||||
type: 'boolean' as const,
|
||||
nullable: false as const, optional: true as const,
|
||||
},
|
||||
hasPendingReceivedFollowRequest: {
|
||||
type: 'boolean' as const,
|
||||
nullable: false as const, optional: true as const,
|
||||
},
|
||||
integrations: {
|
||||
type: 'object' as const,
|
||||
nullable: false as const, optional: true as const,
|
||||
},
|
||||
mutedWords: {
|
||||
type: 'array' as const,
|
||||
nullable: false as const, optional: true as const,
|
||||
},
|
||||
mutedInstances: {
|
||||
type: 'array' as const,
|
||||
nullable: false as const, optional: true as const,
|
||||
},
|
||||
mutingNotificationTypes: {
|
||||
type: 'array' as const,
|
||||
nullable: false as const, optional: true as const,
|
||||
},
|
||||
isFollowing: {
|
||||
type: 'boolean' as const,
|
||||
optional: true as const, nullable: false as const,
|
||||
},
|
||||
hasPendingFollowRequestFromYou: {
|
||||
type: 'boolean' as const,
|
||||
optional: true as const, nullable: false as const,
|
||||
},
|
||||
hasPendingFollowRequestToYou: {
|
||||
type: 'boolean' as const,
|
||||
optional: true as const, nullable: false as const,
|
||||
},
|
||||
isFollowed: {
|
||||
type: 'boolean' as const,
|
||||
optional: true as const, nullable: false as const,
|
||||
},
|
||||
isBlocking: {
|
||||
type: 'boolean' as const,
|
||||
optional: true as const, nullable: false as const,
|
||||
},
|
||||
isBlocked: {
|
||||
type: 'boolean' as const,
|
||||
optional: true as const, nullable: false as const,
|
||||
},
|
||||
isMuted: {
|
||||
type: 'boolean' as const,
|
||||
optional: true as const, nullable: false as const,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
Reference in New Issue
Block a user