なんかもうめっちゃ変えた

This commit is contained in:
syuilo
2022-09-18 03:27:08 +09:00
committed by GitHub
parent d9ab03f086
commit b75184ec8e
946 changed files with 41219 additions and 28839 deletions

View File

@@ -1,6 +1,6 @@
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
import { User } from './user.js';
import { id } from '../id.js';
import { User } from './User.js';
@Entity()
export class AbuseUserReport {
@@ -52,7 +52,7 @@ export class AbuseUserReport {
public resolved: boolean;
@Column('boolean', {
default: false
default: false,
})
public forwarded: boolean;

View File

@@ -1,7 +1,7 @@
import { Entity, PrimaryColumn, Index, Column, ManyToOne, JoinColumn } from 'typeorm';
import { User } from './user.js';
import { App } from './app.js';
import { id } from '../id.js';
import { User } from './User.js';
import { App } from './App.js';
@Entity()
export class AccessToken {

View File

@@ -1,7 +1,7 @@
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
import { User } from './user.js';
import { Announcement } from './announcement.js';
import { id } from '../id.js';
import { User } from './User.js';
import { Announcement } from './Announcement.js';
@Entity()
@Index(['userId', 'announcementId'], { unique: true })

View File

@@ -1,8 +1,8 @@
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
import { User } from './user.js';
import { id } from '../id.js';
import { UserList } from './user-list.js';
import { UserGroupJoining } from './user-group-joining.js';
import { User } from './User.js';
import { UserList } from './UserList.js';
import { UserGroupJoining } from './UserGroupJoining.js';
@Entity()
export class Antenna {

View File

@@ -1,7 +1,7 @@
import { Entity, Index, JoinColumn, Column, ManyToOne, PrimaryColumn } from 'typeorm';
import { Note } from './note.js';
import { Antenna } from './antenna.js';
import { id } from '../id.js';
import { Note } from './Note.js';
import { Antenna } from './Antenna.js';
@Entity()
@Index(['noteId', 'antennaId'], { unique: true })

View File

@@ -1,6 +1,6 @@
import { Entity, PrimaryColumn, Column, Index, ManyToOne } from 'typeorm';
import { User } from './user.js';
import { id } from '../id.js';
import { User } from './User.js';
@Entity()
export class App {

View File

@@ -1,6 +1,6 @@
import { PrimaryColumn, Entity, JoinColumn, Column, ManyToOne, Index } from 'typeorm';
import { User } from './user.js';
import { id } from '../id.js';
import { User } from './User.js';
@Entity()
export class AttestationChallenge {

View File

@@ -1,7 +1,7 @@
import { Entity, PrimaryColumn, Index, Column, ManyToOne, JoinColumn } from 'typeorm';
import { User } from './user.js';
import { App } from './app.js';
import { id } from '../id.js';
import { User } from './User.js';
import { App } from './App.js';
@Entity()
export class AuthSession {

View File

@@ -1,6 +1,6 @@
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
import { User } from './user.js';
import { id } from '../id.js';
import { User } from './User.js';
@Entity()
@Index(['blockerId', 'blockeeId'], { unique: true })

View File

@@ -1,7 +1,7 @@
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
import { User } from './user.js';
import { id } from '../id.js';
import { DriveFile } from './drive-file.js';
import { User } from './User.js';
import { DriveFile } from './DriveFile.js';
@Entity()
export class Channel {

View File

@@ -1,7 +1,7 @@
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
import { User } from './user.js';
import { id } from '../id.js';
import { Channel } from './channel.js';
import { User } from './User.js';
import { Channel } from './Channel.js';
@Entity()
@Index(['followerId', 'followeeId'], { unique: true })

View File

@@ -1,7 +1,7 @@
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
import { Note } from './note.js';
import { Channel } from './channel.js';
import { id } from '../id.js';
import { Note } from './Note.js';
import { Channel } from './Channel.js';
@Entity()
@Index(['channelId', 'noteId'], { unique: true })

View File

@@ -1,6 +1,6 @@
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
import { User } from './user.js';
import { id } from '../id.js';
import { User } from './User.js';
@Entity()
export class Clip {

View File

@@ -1,7 +1,7 @@
import { Entity, Index, JoinColumn, Column, ManyToOne, PrimaryColumn } from 'typeorm';
import { Note } from './note.js';
import { Clip } from './clip.js';
import { id } from '../id.js';
import { Note } from './Note.js';
import { Clip } from './Clip.js';
@Entity()
@Index(['noteId', 'clipId'], { unique: true })

View File

@@ -1,7 +1,7 @@
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
import { id } from '../id.js';
import { User } from './user.js';
import { DriveFolder } from './drive-folder.js';
import { User } from './User.js';
import { DriveFolder } from './DriveFolder.js';
@Entity()
@Index(['userId', 'folderId', 'id'])

View File

@@ -1,6 +1,6 @@
import { JoinColumn, ManyToOne, Entity, PrimaryColumn, Index, Column } from 'typeorm';
import { User } from './user.js';
import { id } from '../id.js';
import { User } from './User.js';
@Entity()
export class DriveFolder {

View File

@@ -1,6 +1,6 @@
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
import { User } from './user.js';
import { id } from '../id.js';
import { User } from './User.js';
@Entity()
@Index(['followerId', 'followeeId'], { unique: true })

View File

@@ -1,6 +1,6 @@
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
import { User } from './user.js';
import { id } from '../id.js';
import { User } from './User.js';
@Entity()
@Index(['followerId', 'followeeId'], { unique: true })

View File

@@ -1,7 +1,7 @@
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
import { User } from './user.js';
import { id } from '../id.js';
import { GalleryPost } from './gallery-post.js';
import { User } from './User.js';
import { GalleryPost } from './GalleryPost.js';
@Entity()
@Index(['userId', 'postId'], { unique: true })

View File

@@ -1,7 +1,7 @@
import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typeorm';
import { User } from './user.js';
import { id } from '../id.js';
import { DriveFile } from './drive-file.js';
import { User } from './User.js';
import type { DriveFile } from './DriveFile.js';
@Entity()
export class GalleryPost {

View File

@@ -1,6 +1,6 @@
import { Entity, PrimaryColumn, Index, Column } from 'typeorm';
import { User } from './user.js';
import { id } from '../id.js';
import type { User } from './User.js';
@Entity()
export class Hashtag {

View File

@@ -1,8 +1,8 @@
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
import { User } from './user.js';
import { DriveFile } from './drive-file.js';
import { id } from '../id.js';
import { UserGroup } from './user-group.js';
import { User } from './User.js';
import { DriveFile } from './DriveFile.js';
import { UserGroup } from './UserGroup.js';
@Entity()
export class MessagingMessage {

View File

@@ -1,7 +1,7 @@
import { Entity, Column, PrimaryColumn, ManyToOne, JoinColumn } from 'typeorm';
import { id } from '../id.js';
import { User } from './user.js';
import { Clip } from './clip.js';
import { User } from './User.js';
import type { Clip } from './Clip.js';
@Entity()
export class Meta {

View File

@@ -1,6 +1,6 @@
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
import { User } from './user.js';
import { id } from '../id.js';
import { User } from './User.js';
@Entity()
export class ModerationLog {

View File

@@ -1,8 +1,8 @@
import { Entity, Index, JoinColumn, Column, ManyToOne, PrimaryColumn } from 'typeorm';
import { Note } from './note.js';
import { User } from './user.js';
import { id } from '../id.js';
import { mutedNoteReasons } from '../../types.js';
import { Note } from './Note.js';
import { User } from './User.js';
@Entity()
@Index(['noteId', 'userId'], { unique: true })

View File

@@ -1,6 +1,6 @@
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
import { User } from './user.js';
import { id } from '../id.js';
import { User } from './User.js';
@Entity()
@Index(['muterId', 'muteeId'], { unique: true })

View File

@@ -1,9 +1,9 @@
import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typeorm';
import { User } from './user.js';
import { DriveFile } from './drive-file.js';
import { id } from '../id.js';
import { noteVisibilities } from '../../types.js';
import { Channel } from './channel.js';
import { User } from './User.js';
import { Channel } from './Channel.js';
import type { DriveFile } from './DriveFile.js';
@Entity()
@Index('IDX_NOTE_TAGS', { synchronize: false })

View File

@@ -1,7 +1,7 @@
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
import { Note } from './note.js';
import { User } from './user.js';
import { id } from '../id.js';
import { Note } from './Note.js';
import { User } from './User.js';
@Entity()
@Index(['userId', 'noteId'], { unique: true })

View File

@@ -1,7 +1,7 @@
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
import { User } from './user.js';
import { Note } from './note.js';
import { id } from '../id.js';
import { User } from './User.js';
import { Note } from './Note.js';
@Entity()
@Index(['userId', 'noteId'], { unique: true })

View File

@@ -1,7 +1,7 @@
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
import { User } from './user.js';
import { Note } from './note.js';
import { id } from '../id.js';
import { User } from './User.js';
import { Note } from './Note.js';
@Entity()
@Index(['userId', 'threadId'], { unique: true })

View File

@@ -1,8 +1,8 @@
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
import { User } from './user.js';
import { Note } from './note.js';
import { id } from '../id.js';
import { Channel } from './channel.js';
import { User } from './User.js';
import { Note } from './Note.js';
import type { Channel } from './Channel.js';
@Entity()
@Index(['userId', 'noteId'], { unique: true })

View File

@@ -1,11 +1,11 @@
import { Entity, Index, JoinColumn, ManyToOne, Column, PrimaryColumn } from 'typeorm';
import { User } from './user.js';
import { id } from '../id.js';
import { Note } from './note.js';
import { FollowRequest } from './follow-request.js';
import { UserGroupInvitation } from './user-group-invitation.js';
import { AccessToken } from './access-token.js';
import { notificationTypes } from '@/types.js';
import { id } from '../id.js';
import { User } from './User.js';
import { Note } from './Note.js';
import { FollowRequest } from './FollowRequest.js';
import { UserGroupInvitation } from './UserGroupInvitation.js';
import { AccessToken } from './AccessToken.js';
@Entity()
export class Notification {

View File

@@ -1,7 +1,7 @@
import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typeorm';
import { User } from './user.js';
import { id } from '../id.js';
import { DriveFile } from './drive-file.js';
import { User } from './User.js';
import { DriveFile } from './DriveFile.js';
@Entity()
@Index(['userId', 'name'], { unique: true })

View File

@@ -1,7 +1,7 @@
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
import { User } from './user.js';
import { id } from '../id.js';
import { Page } from './page.js';
import { User } from './User.js';
import { Page } from './Page.js';
@Entity()
@Index(['userId', 'pageId'], { unique: true })

View File

@@ -1,6 +1,6 @@
import { PrimaryColumn, Entity, Index, Column, ManyToOne, JoinColumn } from 'typeorm';
import { id } from '../id.js';
import { User } from './user.js';
import { User } from './User.js';
@Entity()
export class PasswordResetRequest {

View File

@@ -1,8 +1,8 @@
import { PrimaryColumn, Entity, Index, JoinColumn, Column, OneToOne } from 'typeorm';
import { id } from '../id.js';
import { Note } from './note.js';
import { User } from './user.js';
import { noteVisibilities } from '../../types.js';
import { Note } from './Note.js';
import type { User } from './User.js';
@Entity()
export class Poll {

View File

@@ -1,7 +1,7 @@
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
import { User } from './user.js';
import { Note } from './note.js';
import { id } from '../id.js';
import { User } from './User.js';
import { Note } from './Note.js';
@Entity()
@Index(['userId', 'noteId', 'choice'], { unique: true })

View File

@@ -1,7 +1,7 @@
import { PrimaryColumn, Entity, Index, JoinColumn, Column, OneToOne } from 'typeorm';
import { Note } from './note.js';
import { User } from './user.js';
import { id } from '../id.js';
import { Note } from './Note.js';
import type { User } from './User.js';
@Entity()
export class PromoNote {

View File

@@ -1,7 +1,7 @@
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
import { Note } from './note.js';
import { User } from './user.js';
import { id } from '../id.js';
import { Note } from './Note.js';
import { User } from './User.js';
@Entity()
@Index(['userId', 'noteId'], { unique: true })

View File

@@ -1,6 +1,6 @@
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
import { User } from './user.js';
import { id } from '../id.js';
import { User } from './User.js';
// TODO: 同じdomain、同じscope、同じkeyのレコードは二つ以上存在しないように制約付けたい
@Entity()

View File

@@ -1,6 +1,6 @@
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
import { User } from './user.js';
import { id } from '../id.js';
import { User } from './User.js';
@Entity()
export class Signin {

View File

@@ -1,6 +1,6 @@
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
import { User } from './user.js';
import { id } from '../id.js';
import { User } from './User.js';
@Entity()
export class SwSubscription {

View File

@@ -1,6 +1,6 @@
import { Entity, Column, Index, OneToOne, JoinColumn, PrimaryColumn } from 'typeorm';
import { id } from '../id.js';
import { DriveFile } from './drive-file.js';
import { DriveFile } from './DriveFile.js';
@Entity()
@Index(['usernameLower', 'host'], { unique: true })
@@ -246,3 +246,10 @@ export type CacheableLocalUser = ILocalUser;
export type CacheableRemoteUser = IRemoteUser;
export type CacheableUser = CacheableLocalUser | CacheableRemoteUser;
export const localUsernameSchema = { type: 'string', pattern: /^\w{1,20}$/.toString().slice(1, -1) } as const;
export const passwordSchema = { type: 'string', minLength: 1 } as const;
export const nameSchema = { type: 'string', minLength: 1, maxLength: 50 } as const;
export const descriptionSchema = { type: 'string', minLength: 1, maxLength: 500 } as const;
export const locationSchema = { type: 'string', minLength: 1, maxLength: 50 } as const;
export const birthdaySchema = { type: 'string', pattern: /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/.toString().slice(1, -1) } as const;

View File

@@ -1,6 +1,6 @@
import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typeorm';
import { User } from './user.js';
import { id } from '../id.js';
import { User } from './User.js';
@Entity()
export class UserGroup {

View File

@@ -1,7 +1,7 @@
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
import { User } from './user.js';
import { UserGroup } from './user-group.js';
import { id } from '../id.js';
import { User } from './User.js';
import { UserGroup } from './UserGroup.js';
@Entity()
@Index(['userId', 'userGroupId'], { unique: true })

View File

@@ -1,7 +1,7 @@
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
import { User } from './user.js';
import { UserGroup } from './user-group.js';
import { id } from '../id.js';
import { User } from './User.js';
import { UserGroup } from './UserGroup.js';
@Entity()
@Index(['userId', 'userGroupId'], { unique: true })

View File

@@ -1,7 +1,7 @@
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';
import { id } from '../id.js';
import { Note } from './note.js';
import { User } from './user.js';
import { Note } from './Note.js';
import type { User } from './User.js';
@Entity()
@Index(['userId', 'ip'], { unique: true })

View File

@@ -1,6 +1,6 @@
import { PrimaryColumn, Entity, JoinColumn, Column, OneToOne } from 'typeorm';
import { User } from './user.js';
import { id } from '../id.js';
import { User } from './User.js';
@Entity()
export class UserKeypair {

View File

@@ -1,6 +1,6 @@
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
import { User } from './user.js';
import { id } from '../id.js';
import { User } from './User.js';
@Entity()
export class UserList {

View File

@@ -1,7 +1,7 @@
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
import { User } from './user.js';
import { UserList } from './user-list.js';
import { id } from '../id.js';
import { User } from './User.js';
import { UserList } from './UserList.js';
@Entity()
@Index(['userId', 'userListId'], { unique: true })

View File

@@ -1,7 +1,7 @@
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
import { Note } from './note.js';
import { User } from './user.js';
import { id } from '../id.js';
import { Note } from './Note.js';
import { User } from './User.js';
@Entity()
@Index(['userId', 'noteId'], { unique: true })

View File

@@ -1,8 +1,8 @@
import { Entity, Column, Index, OneToOne, JoinColumn, PrimaryColumn } from 'typeorm';
import { ffVisibility, notificationTypes } from '@/types.js';
import { id } from '../id.js';
import { User } from './user.js';
import { Page } from './page.js';
import { User } from './User.js';
import { Page } from './Page.js';
// TODO: このテーブルで管理している情報すべてレジストリで管理するようにしても良いかも
// ただ、「emailVerified が true なユーザーを find する」のようなクエリは書けなくなるからウーン

View File

@@ -1,6 +1,6 @@
import { PrimaryColumn, Entity, Index, JoinColumn, Column, OneToOne } from 'typeorm';
import { User } from './user.js';
import { id } from '../id.js';
import { User } from './User.js';
@Entity()
export class UserPublickey {

View File

@@ -1,6 +1,6 @@
import { PrimaryColumn, Entity, JoinColumn, Column, ManyToOne, Index } from 'typeorm';
import { User } from './user.js';
import { id } from '../id.js';
import { User } from './User.js';
@Entity()
export class UserSecurityKey {

View File

@@ -1,6 +1,6 @@
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
import { User } from './user.js';
import { id } from '../id.js';
import { User } from './User.js';
export const webhookEventTypes = ['mention', 'unfollow', 'follow', 'followed', 'note', 'reply', 'renote', 'reaction'] as const;

View File

@@ -1,52 +0,0 @@
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
import { User } from './user.js';
import { Note } from './note.js';
import { id } from '../id.js';
@Entity()
@Index(['userId', 'noteId'], { unique: true })
export class NoteWatching {
@PrimaryColumn(id())
public id: string;
@Index()
@Column('timestamp with time zone', {
comment: 'The created date of the NoteWatching.',
})
public createdAt: Date;
@Index()
@Column({
...id(),
comment: 'The watcher ID.',
})
public userId: User['id'];
@ManyToOne(type => User, {
onDelete: 'CASCADE',
})
@JoinColumn()
public user: User | null;
@Index()
@Column({
...id(),
comment: 'The target Note ID.',
})
public noteId: Note['id'];
@ManyToOne(type => Note, {
onDelete: 'CASCADE',
})
@JoinColumn()
public note: Note | null;
//#region Denormalized fields
@Index()
@Column({
...id(),
comment: '[Denormalized]',
})
public noteUserId: Note['userId'];
//#endregion
}

View File

@@ -1,133 +1,194 @@
import { } from 'typeorm';
import { db } from '@/db/postgre.js';
import { AbuseUserReport } from '@/models/entities/AbuseUserReport.js';
import { AccessToken } from '@/models/entities/AccessToken.js';
import { Ad } from '@/models/entities/Ad.js';
import { Announcement } from '@/models/entities/Announcement.js';
import { AnnouncementRead } from '@/models/entities/AnnouncementRead.js';
import { Antenna } from '@/models/entities/Antenna.js';
import { AntennaNote } from '@/models/entities/AntennaNote.js';
import { App } from '@/models/entities/App.js';
import { AttestationChallenge } from '@/models/entities/AttestationChallenge.js';
import { AuthSession } from '@/models/entities/AuthSession.js';
import { Blocking } from '@/models/entities/Blocking.js';
import { ChannelFollowing } from '@/models/entities/ChannelFollowing.js';
import { ChannelNotePining } from '@/models/entities/ChannelNotePining.js';
import { Clip } from '@/models/entities/Clip.js';
import { ClipNote } from '@/models/entities/ClipNote.js';
import { DriveFile } from '@/models/entities/DriveFile.js';
import { DriveFolder } from '@/models/entities/DriveFolder.js';
import { Emoji } from '@/models/entities/Emoji.js';
import { Following } from '@/models/entities/Following.js';
import { FollowRequest } from '@/models/entities/FollowRequest.js';
import { GalleryLike } from '@/models/entities/GalleryLike.js';
import { GalleryPost } from '@/models/entities/GalleryPost.js';
import { Hashtag } from '@/models/entities/Hashtag.js';
import { Instance } from '@/models/entities/Instance.js';
import { MessagingMessage } from '@/models/entities/MessagingMessage.js';
import { Meta } from '@/models/entities/Meta.js';
import { ModerationLog } from '@/models/entities/ModerationLog.js';
import { MutedNote } from '@/models/entities/MutedNote.js';
import { Muting } from '@/models/entities/Muting.js';
import { Note } from '@/models/entities/Note.js';
import { NoteFavorite } from '@/models/entities/NoteFavorite.js';
import { NoteReaction } from '@/models/entities/NoteReaction.js';
import { NoteThreadMuting } from '@/models/entities/NoteThreadMuting.js';
import { NoteUnread } from '@/models/entities/NoteUnread.js';
import { Notification } from '@/models/entities/Notification.js';
import { Page } from '@/models/entities/Page.js';
import { PageLike } from '@/models/entities/PageLike.js';
import { PasswordResetRequest } from '@/models/entities/PasswordResetRequest.js';
import { Poll } from '@/models/entities/Poll.js';
import { PollVote } from '@/models/entities/PollVote.js';
import { PromoNote } from '@/models/entities/PromoNote.js';
import { PromoRead } from '@/models/entities/PromoRead.js';
import { RegistrationTicket } from '@/models/entities/RegistrationTickets.js';
import { RegistryItem } from '@/models/entities/RegistryItem.js';
import { Relay } from '@/models/entities/Relay.js';
import { Signin } from '@/models/entities/Signin.js';
import { SwSubscription } from '@/models/entities/SwSubscription.js';
import { UsedUsername } from '@/models/entities/UsedUsername.js';
import { User } from '@/models/entities/User.js';
import { UserGroup } from '@/models/entities/UserGroup.js';
import { UserGroupInvitation } from '@/models/entities/UserGroupInvitation.js';
import { UserGroupJoining } from '@/models/entities/UserGroupJoining.js';
import { UserIp } from '@/models/entities/UserIp.js';
import { UserKeypair } from '@/models/entities/UserKeypair.js';
import { UserList } from '@/models/entities/UserList.js';
import { UserListJoining } from '@/models/entities/UserListJoining.js';
import { UserNotePining } from '@/models/entities/UserNotePining.js';
import { UserPending } from '@/models/entities/UserPending.js';
import { UserProfile } from '@/models/entities/UserProfile.js';
import { UserPublickey } from '@/models/entities/UserPublickey.js';
import { UserSecurityKey } from '@/models/entities/UserSecurityKey.js';
import { Webhook } from '@/models/entities/Webhook.js';
import { Channel } from '@/models/entities/Channel.js';
import type { Repository } from 'typeorm';
import { Announcement } from './entities/announcement.js';
import { AnnouncementRead } from './entities/announcement-read.js';
import { Instance } from './entities/instance.js';
import { Poll } from './entities/poll.js';
import { PollVote } from './entities/poll-vote.js';
import { Meta } from './entities/meta.js';
import { SwSubscription } from './entities/sw-subscription.js';
import { NoteWatching } from './entities/note-watching.js';
import { NoteThreadMuting } from './entities/note-thread-muting.js';
import { NoteUnread } from './entities/note-unread.js';
import { RegistrationTicket } from './entities/registration-tickets.js';
import { UserRepository } from './repositories/user.js';
import { NoteRepository } from './repositories/note.js';
import { DriveFileRepository } from './repositories/drive-file.js';
import { DriveFolderRepository } from './repositories/drive-folder.js';
import { AccessToken } from './entities/access-token.js';
import { UserNotePining } from './entities/user-note-pining.js';
import { SigninRepository } from './repositories/signin.js';
import { MessagingMessageRepository } from './repositories/messaging-message.js';
import { UserListRepository } from './repositories/user-list.js';
import { UserListJoining } from './entities/user-list-joining.js';
import { UserGroupRepository } from './repositories/user-group.js';
import { UserGroupJoining } from './entities/user-group-joining.js';
import { UserGroupInvitationRepository } from './repositories/user-group-invitation.js';
import { FollowRequestRepository } from './repositories/follow-request.js';
import { MutingRepository } from './repositories/muting.js';
import { BlockingRepository } from './repositories/blocking.js';
import { NoteReactionRepository } from './repositories/note-reaction.js';
import { NotificationRepository } from './repositories/notification.js';
import { NoteFavoriteRepository } from './repositories/note-favorite.js';
import { UserPublickey } from './entities/user-publickey.js';
import { UserKeypair } from './entities/user-keypair.js';
import { AppRepository } from './repositories/app.js';
import { FollowingRepository } from './repositories/following.js';
import { AbuseUserReportRepository } from './repositories/abuse-user-report.js';
import { AuthSessionRepository } from './repositories/auth-session.js';
import { UserProfile } from './entities/user-profile.js';
import { AttestationChallenge } from './entities/attestation-challenge.js';
import { UserSecurityKey } from './entities/user-security-key.js';
import { HashtagRepository } from './repositories/hashtag.js';
import { PageRepository } from './repositories/page.js';
import { PageLikeRepository } from './repositories/page-like.js';
import { GalleryPostRepository } from './repositories/gallery-post.js';
import { GalleryLikeRepository } from './repositories/gallery-like.js';
import { ModerationLogRepository } from './repositories/moderation-logs.js';
import { UsedUsername } from './entities/used-username.js';
import { ClipRepository } from './repositories/clip.js';
import { ClipNote } from './entities/clip-note.js';
import { AntennaRepository } from './repositories/antenna.js';
import { AntennaNote } from './entities/antenna-note.js';
import { PromoNote } from './entities/promo-note.js';
import { PromoRead } from './entities/promo-read.js';
import { EmojiRepository } from './repositories/emoji.js';
import { RelayRepository } from './repositories/relay.js';
import { ChannelRepository } from './repositories/channel.js';
import { MutedNote } from './entities/muted-note.js';
import { ChannelFollowing } from './entities/channel-following.js';
import { ChannelNotePining } from './entities/channel-note-pining.js';
import { RegistryItem } from './entities/registry-item.js';
import { Ad } from './entities/ad.js';
import { PasswordResetRequest } from './entities/password-reset-request.js';
import { UserPending } from './entities/user-pending.js';
import { InstanceRepository } from './repositories/instance.js';
import { Webhook } from './entities/webhook.js';
import { UserIp } from './entities/user-ip.js';
export {
AbuseUserReport,
AccessToken,
Ad,
Announcement,
AnnouncementRead,
Antenna,
AntennaNote,
App,
AttestationChallenge,
AuthSession,
Blocking,
ChannelFollowing,
ChannelNotePining,
Clip,
ClipNote,
DriveFile,
DriveFolder,
Emoji,
Following,
FollowRequest,
GalleryLike,
GalleryPost,
Hashtag,
Instance,
MessagingMessage,
Meta,
ModerationLog,
MutedNote,
Muting,
Note,
NoteFavorite,
NoteReaction,
NoteThreadMuting,
NoteUnread,
Notification,
Page,
PageLike,
PasswordResetRequest,
Poll,
PollVote,
PromoNote,
PromoRead,
RegistrationTicket,
RegistryItem,
Relay,
Signin,
SwSubscription,
UsedUsername,
User,
UserGroup,
UserGroupInvitation,
UserGroupJoining,
UserIp,
UserKeypair,
UserList,
UserListJoining,
UserNotePining,
UserPending,
UserProfile,
UserPublickey,
UserSecurityKey,
Webhook,
Channel,
};
export const Announcements = db.getRepository(Announcement);
export const AnnouncementReads = db.getRepository(AnnouncementRead);
export const Apps = (AppRepository);
export const Notes = (NoteRepository);
export const NoteFavorites = (NoteFavoriteRepository);
export const NoteWatchings = db.getRepository(NoteWatching);
export const NoteThreadMutings = db.getRepository(NoteThreadMuting);
export const NoteReactions = (NoteReactionRepository);
export const NoteUnreads = db.getRepository(NoteUnread);
export const Polls = db.getRepository(Poll);
export const PollVotes = db.getRepository(PollVote);
export const Users = (UserRepository);
export const UserProfiles = db.getRepository(UserProfile);
export const UserKeypairs = db.getRepository(UserKeypair);
export const UserPendings = db.getRepository(UserPending);
export const AttestationChallenges = db.getRepository(AttestationChallenge);
export const UserSecurityKeys = db.getRepository(UserSecurityKey);
export const UserPublickeys = db.getRepository(UserPublickey);
export const UserLists = (UserListRepository);
export const UserListJoinings = db.getRepository(UserListJoining);
export const UserGroups = (UserGroupRepository);
export const UserGroupJoinings = db.getRepository(UserGroupJoining);
export const UserGroupInvitations = (UserGroupInvitationRepository);
export const UserNotePinings = db.getRepository(UserNotePining);
export const UserIps = db.getRepository(UserIp);
export const UsedUsernames = db.getRepository(UsedUsername);
export const Followings = (FollowingRepository);
export const FollowRequests = (FollowRequestRepository);
export const Instances = (InstanceRepository);
export const Emojis = (EmojiRepository);
export const DriveFiles = (DriveFileRepository);
export const DriveFolders = (DriveFolderRepository);
export const Notifications = (NotificationRepository);
export const Metas = db.getRepository(Meta);
export const Mutings = (MutingRepository);
export const Blockings = (BlockingRepository);
export const SwSubscriptions = db.getRepository(SwSubscription);
export const Hashtags = (HashtagRepository);
export const AbuseUserReports = (AbuseUserReportRepository);
export const RegistrationTickets = db.getRepository(RegistrationTicket);
export const AuthSessions = (AuthSessionRepository);
export const AccessTokens = db.getRepository(AccessToken);
export const Signins = (SigninRepository);
export const MessagingMessages = (MessagingMessageRepository);
export const Pages = (PageRepository);
export const PageLikes = (PageLikeRepository);
export const GalleryPosts = (GalleryPostRepository);
export const GalleryLikes = (GalleryLikeRepository);
export const ModerationLogs = (ModerationLogRepository);
export const Clips = (ClipRepository);
export const ClipNotes = db.getRepository(ClipNote);
export const Antennas = (AntennaRepository);
export const AntennaNotes = db.getRepository(AntennaNote);
export const PromoNotes = db.getRepository(PromoNote);
export const PromoReads = db.getRepository(PromoRead);
export const Relays = (RelayRepository);
export const MutedNotes = db.getRepository(MutedNote);
export const Channels = (ChannelRepository);
export const ChannelFollowings = db.getRepository(ChannelFollowing);
export const ChannelNotePinings = db.getRepository(ChannelNotePining);
export const RegistryItems = db.getRepository(RegistryItem);
export const Webhooks = db.getRepository(Webhook);
export const Ads = db.getRepository(Ad);
export const PasswordResetRequests = db.getRepository(PasswordResetRequest);
export type AbuseUserReportsRepository = Repository<AbuseUserReport>;
export type AccessTokensRepository = Repository<AccessToken>;
export type AdsRepository = Repository<Ad>;
export type AnnouncementsRepository = Repository<Announcement>;
export type AnnouncementReadsRepository = Repository<AnnouncementRead>;
export type AntennasRepository = Repository<Antenna>;
export type AntennaNotesRepository = Repository<AntennaNote>;
export type AppsRepository = Repository<App>;
export type AttestationChallengesRepository = Repository<AttestationChallenge>;
export type AuthSessionsRepository = Repository<AuthSession>;
export type BlockingsRepository = Repository<Blocking>;
export type ChannelFollowingsRepository = Repository<ChannelFollowing>;
export type ChannelNotePiningsRepository = Repository<ChannelNotePining>;
export type ClipsRepository = Repository<Clip>;
export type ClipNotesRepository = Repository<ClipNote>;
export type DriveFilesRepository = Repository<DriveFile>;
export type DriveFoldersRepository = Repository<DriveFolder>;
export type EmojisRepository = Repository<Emoji>;
export type FollowingsRepository = Repository<Following>;
export type FollowRequestsRepository = Repository<FollowRequest>;
export type GalleryLikesRepository = Repository<GalleryLike>;
export type GalleryPostsRepository = Repository<GalleryPost>;
export type HashtagsRepository = Repository<Hashtag>;
export type InstancesRepository = Repository<Instance>;
export type MessagingMessagesRepository = Repository<MessagingMessage>;
export type MetasRepository = Repository<Meta>;
export type ModerationLogsRepository = Repository<ModerationLog>;
export type MutedNotesRepository = Repository<MutedNote>;
export type MutingsRepository = Repository<Muting>;
export type NotesRepository = Repository<Note>;
export type NoteFavoritesRepository = Repository<NoteFavorite>;
export type NoteReactionsRepository = Repository<NoteReaction>;
export type NoteThreadMutingsRepository = Repository<NoteThreadMuting>;
export type NoteUnreadsRepository = Repository<NoteUnread>;
export type NotificationsRepository = Repository<Notification>;
export type PagesRepository = Repository<Page>;
export type PageLikesRepository = Repository<PageLike>;
export type PasswordResetRequestsRepository = Repository<PasswordResetRequest>;
export type PollsRepository = Repository<Poll>;
export type PollVotesRepository = Repository<PollVote>;
export type PromoNotesRepository = Repository<PromoNote>;
export type PromoReadsRepository = Repository<PromoRead>;
export type RegistrationTicketsRepository = Repository<RegistrationTicket>;
export type RegistryItemsRepository = Repository<RegistryItem>;
export type RelaysRepository = Repository<Relay>;
export type SigninsRepository = Repository<Signin>;
export type SwSubscriptionsRepository = Repository<SwSubscription>;
export type UsedUsernamesRepository = Repository<UsedUsername>;
export type UsersRepository = Repository<User>;
export type UserGroupsRepository = Repository<UserGroup>;
export type UserGroupInvitationsRepository = Repository<UserGroupInvitation>;
export type UserGroupJoiningsRepository = Repository<UserGroupJoining>;
export type UserIpsRepository = Repository<UserIp>;
export type UserKeypairsRepository = Repository<UserKeypair>;
export type UserListsRepository = Repository<UserList>;
export type UserListJoiningsRepository = Repository<UserListJoining>;
export type UserNotePiningsRepository = Repository<UserNotePining>;
export type UserPendingsRepository = Repository<UserPending>;
export type UserProfilesRepository = Repository<UserProfile>;
export type UserPublickeysRepository = Repository<UserPublickey>;
export type UserSecurityKeysRepository = Repository<UserSecurityKey>;
export type WebhooksRepository = Repository<Webhook>;
export type ChannelsRepository = Repository<Channel>;

View File

@@ -1,38 +0,0 @@
import { db } from '@/db/postgre.js';
import { Users } from '../index.js';
import { AbuseUserReport } from '@/models/entities/abuse-user-report.js';
import { awaitAll } from '@/prelude/await-all.js';
export const AbuseUserReportRepository = db.getRepository(AbuseUserReport).extend({
async pack(
src: AbuseUserReport['id'] | AbuseUserReport,
) {
const report = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
return await awaitAll({
id: report.id,
createdAt: report.createdAt.toISOString(),
comment: report.comment,
resolved: report.resolved,
reporterId: report.reporterId,
targetUserId: report.targetUserId,
assigneeId: report.assigneeId,
reporter: Users.pack(report.reporter || report.reporterId, null, {
detail: true,
}),
targetUser: Users.pack(report.targetUser || report.targetUserId, null, {
detail: true,
}),
assignee: report.assigneeId ? Users.pack(report.assignee || report.assigneeId, null, {
detail: true,
}) : null,
forwarded: report.forwarded,
});
},
packMany(
reports: any[],
) {
return Promise.all(reports.map(x => this.pack(x)));
},
});

View File

@@ -1,32 +0,0 @@
import { db } from '@/db/postgre.js';
import { Antenna } from '@/models/entities/antenna.js';
import { Packed } from '@/misc/schema.js';
import { AntennaNotes, UserGroupJoinings } from '../index.js';
export const AntennaRepository = db.getRepository(Antenna).extend({
async pack(
src: Antenna['id'] | Antenna,
): Promise<Packed<'Antenna'>> {
const antenna = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
const hasUnreadNote = (await AntennaNotes.findOneBy({ antennaId: antenna.id, read: false })) != null;
const userGroupJoining = antenna.userGroupJoiningId ? await UserGroupJoinings.findOneBy({ id: antenna.userGroupJoiningId }) : null;
return {
id: antenna.id,
createdAt: antenna.createdAt.toISOString(),
name: antenna.name,
keywords: antenna.keywords,
excludeKeywords: antenna.excludeKeywords,
src: antenna.src,
userListId: antenna.userListId,
userGroupId: userGroupJoining ? userGroupJoining.userGroupId : null,
users: antenna.users,
caseSensitive: antenna.caseSensitive,
notify: antenna.notify,
withReplies: antenna.withReplies,
withFile: antenna.withFile,
hasUnreadNote,
};
},
});

View File

@@ -1,39 +0,0 @@
import { db } from '@/db/postgre.js';
import { App } from '@/models/entities/app.js';
import { AccessTokens } from '../index.js';
import { Packed } from '@/misc/schema.js';
import { User } from '../entities/user.js';
export const AppRepository = db.getRepository(App).extend({
async pack(
src: App['id'] | App,
me?: { id: User['id'] } | null | undefined,
options?: {
detail?: boolean,
includeSecret?: boolean,
includeProfileImageIds?: boolean
}
): Promise<Packed<'App'>> {
const opts = Object.assign({
detail: false,
includeSecret: false,
includeProfileImageIds: false,
}, options);
const app = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
return {
id: app.id,
name: app.name,
callbackUrl: app.callbackUrl,
permission: app.permission,
...(opts.includeSecret ? { secret: app.secret } : {}),
...(me ? {
isAuthorized: await AccessTokens.countBy({
appId: app.id,
userId: me.id,
}).then(count => count > 0),
} : {}),
};
},
});

View File

@@ -1,20 +0,0 @@
import { db } from '@/db/postgre.js';
import { Apps } from '../index.js';
import { AuthSession } from '@/models/entities/auth-session.js';
import { awaitAll } from '@/prelude/await-all.js';
import { User } from '@/models/entities/user.js';
export const AuthSessionRepository = db.getRepository(AuthSession).extend({
async pack(
src: AuthSession['id'] | AuthSession,
me?: { id: User['id'] } | null | undefined
) {
const session = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
return await awaitAll({
id: session.id,
app: Apps.pack(session.appId, me),
token: session.token,
});
},
});

View File

@@ -1,31 +0,0 @@
import { db } from '@/db/postgre.js';
import { Users } from '../index.js';
import { Blocking } from '@/models/entities/blocking.js';
import { awaitAll } from '@/prelude/await-all.js';
import { Packed } from '@/misc/schema.js';
import { User } from '@/models/entities/user.js';
export const BlockingRepository = db.getRepository(Blocking).extend({
async pack(
src: Blocking['id'] | Blocking,
me?: { id: User['id'] } | null | undefined
): Promise<Packed<'Blocking'>> {
const blocking = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
return await awaitAll({
id: blocking.id,
createdAt: blocking.createdAt.toISOString(),
blockeeId: blocking.blockeeId,
blockee: Users.pack(blocking.blockeeId, me, {
detail: true,
}),
});
},
packMany(
blockings: any[],
me: { id: User['id'] }
) {
return Promise.all(blockings.map(x => this.pack(x, me)));
},
});

View File

@@ -1,41 +0,0 @@
import { db } from '@/db/postgre.js';
import { Channel } from '@/models/entities/channel.js';
import { Packed } from '@/misc/schema.js';
import { DriveFiles, ChannelFollowings, NoteUnreads } from '../index.js';
import { User } from '@/models/entities/user.js';
export const ChannelRepository = db.getRepository(Channel).extend({
async pack(
src: Channel['id'] | Channel,
me?: { id: User['id'] } | null | undefined,
): Promise<Packed<'Channel'>> {
const channel = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
const meId = me ? me.id : null;
const banner = channel.bannerId ? await DriveFiles.findOneBy({ id: channel.bannerId }) : null;
const hasUnreadNote = meId ? (await NoteUnreads.findOneBy({ noteChannelId: channel.id, userId: meId })) != null : undefined;
const following = meId ? await ChannelFollowings.findOneBy({
followerId: meId,
followeeId: channel.id,
}) : null;
return {
id: channel.id,
createdAt: channel.createdAt.toISOString(),
lastNotedAt: channel.lastNotedAt ? channel.lastNotedAt.toISOString() : null,
name: channel.name,
description: channel.description,
userId: channel.userId,
bannerUrl: banner ? DriveFiles.getPublicUrl(banner, false) : null,
usersCount: channel.usersCount,
notesCount: channel.notesCount,
...(me ? {
isFollowing: following != null,
hasUnreadNote,
} : {}),
};
},
});

View File

@@ -1,30 +0,0 @@
import { db } from '@/db/postgre.js';
import { Clip } from '@/models/entities/clip.js';
import { Packed } from '@/misc/schema.js';
import { Users } from '../index.js';
import { awaitAll } from '@/prelude/await-all.js';
export const ClipRepository = db.getRepository(Clip).extend({
async pack(
src: Clip['id'] | Clip,
): Promise<Packed<'Clip'>> {
const clip = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
return await awaitAll({
id: clip.id,
createdAt: clip.createdAt.toISOString(),
userId: clip.userId,
user: Users.pack(clip.user || clip.userId),
name: clip.name,
description: clip.description,
isPublic: clip.isPublic,
});
},
packMany(
clips: Clip[],
) {
return Promise.all(clips.map(x => this.pack(x)));
},
});

View File

@@ -1,188 +0,0 @@
import { db } from '@/db/postgre.js';
import { DriveFile } from '@/models/entities/drive-file.js';
import { User } from '@/models/entities/user.js';
import { toPuny } from '@/misc/convert-host.js';
import { awaitAll, Promiseable } from '@/prelude/await-all.js';
import { Packed } from '@/misc/schema.js';
import config from '@/config/index.js';
import { query, appendQuery } from '@/prelude/url.js';
import { Meta } from '@/models/entities/meta.js';
import { fetchMeta } from '@/misc/fetch-meta.js';
import { Users, DriveFolders } from '../index.js';
type PackOptions = {
detail?: boolean,
self?: boolean,
withUser?: boolean,
};
export const DriveFileRepository = db.getRepository(DriveFile).extend({
validateFileName(name: string): boolean {
return (
(name.trim().length > 0) &&
(name.length <= 200) &&
(name.indexOf('\\') === -1) &&
(name.indexOf('/') === -1) &&
(name.indexOf('..') === -1)
);
},
getPublicProperties(file: DriveFile): DriveFile['properties'] {
if (file.properties.orientation != null) {
// TODO
//const properties = structuredClone(file.properties);
const properties = JSON.parse(JSON.stringify(file.properties));
if (file.properties.orientation >= 5) {
[properties.width, properties.height] = [properties.height, properties.width];
}
properties.orientation = undefined;
return properties;
}
return file.properties;
},
getPublicUrl(file: DriveFile, thumbnail = false): string | null {
// リモートかつメディアプロキシ
if (file.uri != null && file.userHost != null && config.mediaProxy != null) {
return appendQuery(config.mediaProxy, query({
url: file.uri,
thumbnail: thumbnail ? '1' : undefined,
}));
}
// リモートかつ期限切れはローカルプロキシを試みる
if (file.uri != null && file.isLink && config.proxyRemoteFiles) {
const key = thumbnail ? file.thumbnailAccessKey : file.webpublicAccessKey;
if (key && !key.match('/')) { // 古いものはここにオブジェクトストレージキーが入ってるので除外
return `${config.url}/files/${key}`;
}
}
const isImage = file.type && ['image/png', 'image/apng', 'image/gif', 'image/jpeg', 'image/webp', 'image/svg+xml'].includes(file.type);
return thumbnail ? (file.thumbnailUrl || (isImage ? (file.webpublicUrl || file.url) : null)) : (file.webpublicUrl || file.url);
},
async calcDriveUsageOf(user: User['id'] | { id: User['id'] }): Promise<number> {
const id = typeof user === 'object' ? user.id : user;
const { sum } = await this
.createQueryBuilder('file')
.where('file.userId = :id', { id: id })
.andWhere('file.isLink = FALSE')
.select('SUM(file.size)', 'sum')
.getRawOne();
return parseInt(sum, 10) || 0;
},
async calcDriveUsageOfHost(host: string): Promise<number> {
const { sum } = await this
.createQueryBuilder('file')
.where('file.userHost = :host', { host: toPuny(host) })
.andWhere('file.isLink = FALSE')
.select('SUM(file.size)', 'sum')
.getRawOne();
return parseInt(sum, 10) || 0;
},
async calcDriveUsageOfLocal(): Promise<number> {
const { sum } = await this
.createQueryBuilder('file')
.where('file.userHost IS NULL')
.andWhere('file.isLink = FALSE')
.select('SUM(file.size)', 'sum')
.getRawOne();
return parseInt(sum, 10) || 0;
},
async calcDriveUsageOfRemote(): Promise<number> {
const { sum } = await this
.createQueryBuilder('file')
.where('file.userHost IS NOT NULL')
.andWhere('file.isLink = FALSE')
.select('SUM(file.size)', 'sum')
.getRawOne();
return parseInt(sum, 10) || 0;
},
async pack(
src: DriveFile['id'] | DriveFile,
options?: PackOptions,
): Promise<Packed<'DriveFile'>> {
const opts = Object.assign({
detail: false,
self: false,
}, options);
const file = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
return await awaitAll<Packed<'DriveFile'>>({
id: file.id,
createdAt: file.createdAt.toISOString(),
name: file.name,
type: file.type,
md5: file.md5,
size: file.size,
isSensitive: file.isSensitive,
blurhash: file.blurhash,
properties: opts.self ? file.properties : this.getPublicProperties(file),
url: opts.self ? file.url : this.getPublicUrl(file, false),
thumbnailUrl: this.getPublicUrl(file, true),
comment: file.comment,
folderId: file.folderId,
folder: opts.detail && file.folderId ? DriveFolders.pack(file.folderId, {
detail: true,
}) : null,
userId: opts.withUser ? file.userId : null,
user: (opts.withUser && file.userId) ? Users.pack(file.userId) : null,
});
},
async packNullable(
src: DriveFile['id'] | DriveFile,
options?: PackOptions,
): Promise<Packed<'DriveFile'> | null> {
const opts = Object.assign({
detail: false,
self: false,
}, options);
const file = typeof src === 'object' ? src : await this.findOneBy({ id: src });
if (file == null) return null;
return await awaitAll<Packed<'DriveFile'>>({
id: file.id,
createdAt: file.createdAt.toISOString(),
name: file.name,
type: file.type,
md5: file.md5,
size: file.size,
isSensitive: file.isSensitive,
blurhash: file.blurhash,
properties: opts.self ? file.properties : this.getPublicProperties(file),
url: opts.self ? file.url : this.getPublicUrl(file, false),
thumbnailUrl: this.getPublicUrl(file, true),
comment: file.comment,
folderId: file.folderId,
folder: opts.detail && file.folderId ? DriveFolders.pack(file.folderId, {
detail: true,
}) : null,
userId: opts.withUser ? file.userId : null,
user: (opts.withUser && file.userId) ? Users.pack(file.userId) : null,
});
},
async packMany(
files: (DriveFile['id'] | DriveFile)[],
options?: PackOptions,
): Promise<Packed<'DriveFile'>[]> {
const items = await Promise.all(files.map(f => this.packNullable(f, options)));
return items.filter((x): x is Packed<'DriveFile'> => x != null);
},
});

View File

@@ -1,42 +0,0 @@
import { db } from '@/db/postgre.js';
import { DriveFolders, DriveFiles } from '../index.js';
import { DriveFolder } from '@/models/entities/drive-folder.js';
import { awaitAll } from '@/prelude/await-all.js';
import { Packed } from '@/misc/schema.js';
export const DriveFolderRepository = db.getRepository(DriveFolder).extend({
async pack(
src: DriveFolder['id'] | DriveFolder,
options?: {
detail: boolean
}
): Promise<Packed<'DriveFolder'>> {
const opts = Object.assign({
detail: false,
}, options);
const folder = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
return await awaitAll({
id: folder.id,
createdAt: folder.createdAt.toISOString(),
name: folder.name,
parentId: folder.parentId,
...(opts.detail ? {
foldersCount: DriveFolders.countBy({
parentId: folder.id,
}),
filesCount: DriveFiles.countBy({
folderId: folder.id,
}),
...(folder.parentId ? {
parent: this.pack(folder.parentId, {
detail: true,
}),
} : {}),
} : {}),
});
},
});

View File

@@ -1,27 +0,0 @@
import { db } from '@/db/postgre.js';
import { Emoji } from '@/models/entities/emoji.js';
import { Packed } from '@/misc/schema.js';
export const EmojiRepository = db.getRepository(Emoji).extend({
async pack(
src: Emoji['id'] | Emoji,
): Promise<Packed<'Emoji'>> {
const emoji = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
return {
id: emoji.id,
aliases: emoji.aliases,
name: emoji.name,
category: emoji.category,
host: emoji.host,
// || emoji.originalUrl してるのは後方互換性のため
url: emoji.publicUrl || emoji.originalUrl,
};
},
packMany(
emojis: any[],
) {
return Promise.all(emojis.map(x => this.pack(x)));
},
});

View File

@@ -1,19 +0,0 @@
import { db } from '@/db/postgre.js';
import { FollowRequest } from '@/models/entities/follow-request.js';
import { Users } from '../index.js';
import { User } from '@/models/entities/user.js';
export const FollowRequestRepository = db.getRepository(FollowRequest).extend({
async pack(
src: FollowRequest['id'] | FollowRequest,
me?: { id: User['id'] } | null | undefined
) {
const request = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
return {
id: request.id,
follower: await Users.pack(request.followerId, me),
followee: await Users.pack(request.followeeId, me),
};
},
});

View File

@@ -1,85 +0,0 @@
import { db } from '@/db/postgre.js';
import { Users } from '../index.js';
import { Following } from '@/models/entities/following.js';
import { awaitAll } from '@/prelude/await-all.js';
import { Packed } from '@/misc/schema.js';
import { User } from '@/models/entities/user.js';
type LocalFollowerFollowing = Following & {
followerHost: null;
followerInbox: null;
followerSharedInbox: null;
};
type RemoteFollowerFollowing = Following & {
followerHost: string;
followerInbox: string;
followerSharedInbox: string;
};
type LocalFolloweeFollowing = Following & {
followeeHost: null;
followeeInbox: null;
followeeSharedInbox: null;
};
type RemoteFolloweeFollowing = Following & {
followeeHost: string;
followeeInbox: string;
followeeSharedInbox: string;
};
export const FollowingRepository = db.getRepository(Following).extend({
isLocalFollower(following: Following): following is LocalFollowerFollowing {
return following.followerHost == null;
},
isRemoteFollower(following: Following): following is RemoteFollowerFollowing {
return following.followerHost != null;
},
isLocalFollowee(following: Following): following is LocalFolloweeFollowing {
return following.followeeHost == null;
},
isRemoteFollowee(following: Following): following is RemoteFolloweeFollowing {
return following.followeeHost != null;
},
async pack(
src: Following['id'] | Following,
me?: { id: User['id'] } | null | undefined,
opts?: {
populateFollowee?: boolean;
populateFollower?: boolean;
}
): Promise<Packed<'Following'>> {
const following = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
if (opts == null) opts = {};
return await awaitAll({
id: following.id,
createdAt: following.createdAt.toISOString(),
followeeId: following.followeeId,
followerId: following.followerId,
followee: opts.populateFollowee ? Users.pack(following.followee || following.followeeId, me, {
detail: true,
}) : undefined,
follower: opts.populateFollower ? Users.pack(following.follower || following.followerId, me, {
detail: true,
}) : undefined,
});
},
packMany(
followings: any[],
me?: { id: User['id'] } | null | undefined,
opts?: {
populateFollowee?: boolean;
populateFollower?: boolean;
}
) {
return Promise.all(followings.map(x => this.pack(x, me, opts)));
},
});

View File

@@ -1,24 +0,0 @@
import { db } from '@/db/postgre.js';
import { GalleryLike } from '@/models/entities/gallery-like.js';
import { GalleryPosts } from '../index.js';
export const GalleryLikeRepository = db.getRepository(GalleryLike).extend({
async pack(
src: GalleryLike['id'] | GalleryLike,
me?: any
) {
const like = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
return {
id: like.id,
post: await GalleryPosts.pack(like.post || like.postId, me),
};
},
packMany(
likes: any[],
me: any
) {
return Promise.all(likes.map(x => this.pack(x, me)));
},
});

View File

@@ -1,39 +0,0 @@
import { db } from '@/db/postgre.js';
import { GalleryPost } from '@/models/entities/gallery-post.js';
import { Packed } from '@/misc/schema.js';
import { Users, DriveFiles, GalleryLikes } from '../index.js';
import { awaitAll } from '@/prelude/await-all.js';
import { User } from '@/models/entities/user.js';
export const GalleryPostRepository = db.getRepository(GalleryPost).extend({
async pack(
src: GalleryPost['id'] | GalleryPost,
me?: { id: User['id'] } | null | undefined,
): Promise<Packed<'GalleryPost'>> {
const meId = me ? me.id : null;
const post = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
return await awaitAll({
id: post.id,
createdAt: post.createdAt.toISOString(),
updatedAt: post.updatedAt.toISOString(),
userId: post.userId,
user: Users.pack(post.user || post.userId, me),
title: post.title,
description: post.description,
fileIds: post.fileIds,
files: DriveFiles.packMany(post.fileIds),
tags: post.tags.length > 0 ? post.tags : undefined,
isSensitive: post.isSensitive,
likedCount: post.likedCount,
isLiked: meId ? await GalleryLikes.findOneBy({ postId: post.id, userId: meId }).then(x => x != null) : undefined,
});
},
packMany(
posts: GalleryPost[],
me?: { id: User['id'] } | null | undefined,
) {
return Promise.all(posts.map(x => this.pack(x, me)));
},
});

View File

@@ -1,25 +0,0 @@
import { db } from '@/db/postgre.js';
import { Hashtag } from '@/models/entities/hashtag.js';
import { Packed } from '@/misc/schema.js';
export const HashtagRepository = db.getRepository(Hashtag).extend({
async pack(
src: Hashtag,
): Promise<Packed<'Hashtag'>> {
return {
tag: src.name,
mentionedUsersCount: src.mentionedUsersCount,
mentionedLocalUsersCount: src.mentionedLocalUsersCount,
mentionedRemoteUsersCount: src.mentionedRemoteUsersCount,
attachedUsersCount: src.attachedUsersCount,
attachedLocalUsersCount: src.attachedLocalUsersCount,
attachedRemoteUsersCount: src.attachedRemoteUsersCount,
};
},
packMany(
hashtags: Hashtag[],
) {
return Promise.all(hashtags.map(x => this.pack(x)));
},
});

View File

@@ -1,43 +0,0 @@
import { db } from '@/db/postgre.js';
import { Instance } from '@/models/entities/instance.js';
import { Packed } from '@/misc/schema.js';
import { fetchMeta } from '@/misc/fetch-meta.js';
export const InstanceRepository = db.getRepository(Instance).extend({
async pack(
instance: Instance,
): Promise<Packed<'FederationInstance'>> {
const meta = await fetchMeta();
return {
id: instance.id,
caughtAt: instance.caughtAt.toISOString(),
host: instance.host,
usersCount: instance.usersCount,
notesCount: instance.notesCount,
followingCount: instance.followingCount,
followersCount: instance.followersCount,
latestRequestSentAt: instance.latestRequestSentAt ? instance.latestRequestSentAt.toISOString() : null,
lastCommunicatedAt: instance.lastCommunicatedAt.toISOString(),
isNotResponding: instance.isNotResponding,
isSuspended: instance.isSuspended,
isBlocked: meta.blockedHosts.includes(instance.host),
softwareName: instance.softwareName,
softwareVersion: instance.softwareVersion,
openRegistrations: instance.openRegistrations,
name: instance.name,
description: instance.description,
maintainerName: instance.maintainerName,
maintainerEmail: instance.maintainerEmail,
iconUrl: instance.iconUrl,
faviconUrl: instance.faviconUrl,
themeColor: instance.themeColor,
infoUpdatedAt: instance.infoUpdatedAt ? instance.infoUpdatedAt.toISOString() : null,
};
},
packMany(
instances: Instance[],
) {
return Promise.all(instances.map(x => this.pack(x)));
},
});

View File

@@ -1,39 +0,0 @@
import { db } from '@/db/postgre.js';
import { MessagingMessage } from '@/models/entities/messaging-message.js';
import { Users, DriveFiles, UserGroups } from '../index.js';
import { Packed } from '@/misc/schema.js';
import { User } from '@/models/entities/user.js';
export const MessagingMessageRepository = db.getRepository(MessagingMessage).extend({
async pack(
src: MessagingMessage['id'] | MessagingMessage,
me?: { id: User['id'] } | null | undefined,
options?: {
populateRecipient?: boolean,
populateGroup?: boolean,
}
): Promise<Packed<'MessagingMessage'>> {
const opts = options || {
populateRecipient: true,
populateGroup: true,
};
const message = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
return {
id: message.id,
createdAt: message.createdAt.toISOString(),
text: message.text,
userId: message.userId,
user: await Users.pack(message.user || message.userId, me),
recipientId: message.recipientId,
recipient: message.recipientId && opts.populateRecipient ? await Users.pack(message.recipient || message.recipientId, me) : undefined,
groupId: message.groupId,
group: message.groupId && opts.populateGroup ? await UserGroups.pack(message.group || message.groupId) : undefined,
fileId: message.fileId,
file: message.fileId ? await DriveFiles.pack(message.fileId) : null,
isRead: message.isRead,
reads: message.reads,
};
},
});

View File

@@ -1,29 +0,0 @@
import { db } from '@/db/postgre.js';
import { Users } from '../index.js';
import { ModerationLog } from '@/models/entities/moderation-log.js';
import { awaitAll } from '@/prelude/await-all.js';
export const ModerationLogRepository = db.getRepository(ModerationLog).extend({
async pack(
src: ModerationLog['id'] | ModerationLog,
) {
const log = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
return await awaitAll({
id: log.id,
createdAt: log.createdAt.toISOString(),
type: log.type,
info: log.info,
userId: log.userId,
user: Users.pack(log.user || log.userId, null, {
detail: true,
}),
});
},
packMany(
reports: any[],
) {
return Promise.all(reports.map(x => this.pack(x)));
},
});

View File

@@ -1,32 +0,0 @@
import { db } from '@/db/postgre.js';
import { Users } from '../index.js';
import { Muting } from '@/models/entities/muting.js';
import { awaitAll } from '@/prelude/await-all.js';
import { Packed } from '@/misc/schema.js';
import { User } from '@/models/entities/user.js';
export const MutingRepository = db.getRepository(Muting).extend({
async pack(
src: Muting['id'] | Muting,
me?: { id: User['id'] } | null | undefined
): Promise<Packed<'Muting'>> {
const muting = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
return await awaitAll({
id: muting.id,
createdAt: muting.createdAt.toISOString(),
expiresAt: muting.expiresAt ? muting.expiresAt.toISOString() : null,
muteeId: muting.muteeId,
mutee: Users.pack(muting.muteeId, me, {
detail: true,
}),
});
},
packMany(
mutings: any[],
me: { id: User['id'] }
) {
return Promise.all(mutings.map(x => this.pack(x, me)));
},
});

View File

@@ -1,27 +0,0 @@
import { db } from '@/db/postgre.js';
import { NoteFavorite } from '@/models/entities/note-favorite.js';
import { Notes } from '../index.js';
import { User } from '@/models/entities/user.js';
export const NoteFavoriteRepository = db.getRepository(NoteFavorite).extend({
async pack(
src: NoteFavorite['id'] | NoteFavorite,
me?: { id: User['id'] } | null | undefined
) {
const favorite = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
return {
id: favorite.id,
createdAt: favorite.createdAt.toISOString(),
noteId: favorite.noteId,
note: await Notes.pack(favorite.note || favorite.noteId, me),
};
},
packMany(
favorites: any[],
me: { id: User['id'] }
) {
return Promise.all(favorites.map(x => this.pack(x, me)));
},
});

View File

@@ -1,32 +0,0 @@
import { db } from '@/db/postgre.js';
import { NoteReaction } from '@/models/entities/note-reaction.js';
import { Notes, Users } from '../index.js';
import { Packed } from '@/misc/schema.js';
import { convertLegacyReaction } from '@/misc/reaction-lib.js';
import { User } from '@/models/entities/user.js';
export const NoteReactionRepository = db.getRepository(NoteReaction).extend({
async pack(
src: NoteReaction['id'] | NoteReaction,
me?: { id: User['id'] } | null | undefined,
options?: {
withNote: boolean;
},
): Promise<Packed<'NoteReaction'>> {
const opts = Object.assign({
withNote: false,
}, options);
const reaction = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
return {
id: reaction.id,
createdAt: reaction.createdAt.toISOString(),
user: await Users.pack(reaction.user ?? reaction.userId, me),
type: convertLegacyReaction(reaction.reaction),
...(opts.withNote ? {
note: await Notes.pack(reaction.note ?? reaction.noteId, me),
} : {}),
};
},
});

View File

@@ -1,326 +0,0 @@
import { In } from 'typeorm';
import * as mfm from 'mfm-js';
import { Note } from '@/models/entities/note.js';
import { User } from '@/models/entities/user.js';
import { Users, PollVotes, DriveFiles, NoteReactions, Followings, Polls, Channels } from '../index.js';
import { Packed } from '@/misc/schema.js';
import { nyaize } from '@/misc/nyaize.js';
import { awaitAll } from '@/prelude/await-all.js';
import { convertLegacyReaction, convertLegacyReactions, decodeReaction } from '@/misc/reaction-lib.js';
import { NoteReaction } from '@/models/entities/note-reaction.js';
import { aggregateNoteEmojis, populateEmojis, prefetchEmojis } from '@/misc/populate-emojis.js';
import { db } from '@/db/postgre.js';
async function hideNote(packedNote: Packed<'Note'>, meId: User['id'] | null) {
// TODO: isVisibleForMe を使うようにしても良さそう(型違うけど)
let hide = false;
// visibility が specified かつ自分が指定されていなかったら非表示
if (packedNote.visibility === 'specified') {
if (meId == null) {
hide = true;
} else if (meId === packedNote.userId) {
hide = false;
} else {
// 指定されているかどうか
const specified = packedNote.visibleUserIds!.some((id: any) => meId === id);
if (specified) {
hide = false;
} else {
hide = true;
}
}
}
// visibility が followers かつ自分が投稿者のフォロワーでなかったら非表示
if (packedNote.visibility === 'followers') {
if (meId == null) {
hide = true;
} else if (meId === packedNote.userId) {
hide = false;
} else if (packedNote.reply && (meId === packedNote.reply.userId)) {
// 自分の投稿に対するリプライ
hide = false;
} else if (packedNote.mentions && packedNote.mentions.some(id => meId === id)) {
// 自分へのメンション
hide = false;
} else {
// フォロワーかどうか
const following = await Followings.findOneBy({
followeeId: packedNote.userId,
followerId: meId,
});
if (following == null) {
hide = true;
} else {
hide = false;
}
}
}
if (hide) {
packedNote.visibleUserIds = undefined;
packedNote.fileIds = [];
packedNote.files = [];
packedNote.text = null;
packedNote.poll = undefined;
packedNote.cw = null;
packedNote.isHidden = true;
}
}
async function populatePoll(note: Note, meId: User['id'] | null) {
const poll = await Polls.findOneByOrFail({ noteId: note.id });
const choices = poll.choices.map(c => ({
text: c,
votes: poll.votes[poll.choices.indexOf(c)],
isVoted: false,
}));
if (meId) {
if (poll.multiple) {
const votes = await PollVotes.findBy({
userId: meId,
noteId: note.id,
});
const myChoices = votes.map(v => v.choice);
for (const myChoice of myChoices) {
choices[myChoice].isVoted = true;
}
} else {
const vote = await PollVotes.findOneBy({
userId: meId,
noteId: note.id,
});
if (vote) {
choices[vote.choice].isVoted = true;
}
}
}
return {
multiple: poll.multiple,
expiresAt: poll.expiresAt,
choices,
};
}
async function populateMyReaction(note: Note, meId: User['id'], _hint_?: {
myReactions: Map<Note['id'], NoteReaction | null>;
}) {
if (_hint_?.myReactions) {
const reaction = _hint_.myReactions.get(note.id);
if (reaction) {
return convertLegacyReaction(reaction.reaction);
} else if (reaction === null) {
return undefined;
}
// 実装上抜けがあるだけかもしれないので、「ヒントに含まれてなかったら(=undefinedなら)return」のようにはしない
}
const reaction = await NoteReactions.findOneBy({
userId: meId,
noteId: note.id,
});
if (reaction) {
return convertLegacyReaction(reaction.reaction);
}
return undefined;
}
export const NoteRepository = db.getRepository(Note).extend({
async isVisibleForMe(note: Note, meId: User['id'] | null): Promise<boolean> {
// This code must always be synchronized with the checks in generateVisibilityQuery.
// visibility が specified かつ自分が指定されていなかったら非表示
if (note.visibility === 'specified') {
if (meId == null) {
return false;
} else if (meId === note.userId) {
return true;
} else {
// 指定されているかどうか
return note.visibleUserIds.some((id: any) => meId === id);
}
}
// visibility が followers かつ自分が投稿者のフォロワーでなかったら非表示
if (note.visibility === 'followers') {
if (meId == null) {
return false;
} else if (meId === note.userId) {
return true;
} else if (note.reply && (meId === note.reply.userId)) {
// 自分の投稿に対するリプライ
return true;
} else if (note.mentions && note.mentions.some(id => meId === id)) {
// 自分へのメンション
return true;
} else {
// フォロワーかどうか
const [following, user] = await Promise.all([
Followings.count({
where: {
followeeId: note.userId,
followerId: meId,
},
take: 1,
}),
Users.findOneByOrFail({ id: meId }),
]);
/* If we know the following, everyhting is fine.
But if we do not know the following, it might be that both the
author of the note and the author of the like are remote users,
in which case we can never know the following. Instead we have
to assume that the users are following each other.
*/
return following > 0 || (note.userHost != null && user.host != null);
}
}
return true;
},
async pack(
src: Note['id'] | Note,
me?: { id: User['id'] } | null | undefined,
options?: {
detail?: boolean;
skipHide?: boolean;
_hint_?: {
myReactions: Map<Note['id'], NoteReaction | null>;
};
}
): Promise<Packed<'Note'>> {
const opts = Object.assign({
detail: true,
skipHide: false,
}, options);
const meId = me ? me.id : null;
const note = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
const host = note.userHost;
let text = note.text;
if (note.name && (note.url ?? note.uri)) {
text = `${note.name}\n${(note.text || '').trim()}\n\n${note.url ?? note.uri}`;
}
const channel = note.channelId
? note.channel
? note.channel
: await Channels.findOneBy({ id: note.channelId })
: null;
const reactionEmojiNames = Object.keys(note.reactions).filter(x => x?.startsWith(':')).map(x => decodeReaction(x).reaction).map(x => x.replace(/:/g, ''));
const packed: Packed<'Note'> = await awaitAll({
id: note.id,
createdAt: note.createdAt.toISOString(),
userId: note.userId,
user: Users.pack(note.user ?? note.userId, me, {
detail: false,
}),
text: text,
cw: note.cw,
visibility: note.visibility,
localOnly: note.localOnly || undefined,
visibleUserIds: note.visibility === 'specified' ? note.visibleUserIds : undefined,
renoteCount: note.renoteCount,
repliesCount: note.repliesCount,
reactions: convertLegacyReactions(note.reactions),
tags: note.tags.length > 0 ? note.tags : undefined,
emojis: populateEmojis(note.emojis.concat(reactionEmojiNames), host),
fileIds: note.fileIds,
files: DriveFiles.packMany(note.fileIds),
replyId: note.replyId,
renoteId: note.renoteId,
channelId: note.channelId || undefined,
channel: channel ? {
id: channel.id,
name: channel.name,
} : undefined,
mentions: note.mentions.length > 0 ? note.mentions : undefined,
uri: note.uri || undefined,
url: note.url || undefined,
...(opts.detail ? {
reply: note.replyId ? this.pack(note.reply || note.replyId, me, {
detail: false,
_hint_: options?._hint_,
}) : undefined,
renote: note.renoteId ? this.pack(note.renote || note.renoteId, me, {
detail: true,
_hint_: options?._hint_,
}) : undefined,
poll: note.hasPoll ? populatePoll(note, meId) : undefined,
...(meId ? {
myReaction: populateMyReaction(note, meId, options?._hint_),
} : {}),
} : {}),
});
if (packed.user.isCat && packed.text) {
const tokens = packed.text ? mfm.parse(packed.text) : [];
mfm.inspect(tokens, node => {
if (node.type === 'text') {
// TODO: quoteなtextはskip
node.props.text = nyaize(node.props.text);
}
});
packed.text = mfm.toString(tokens);
}
if (!opts.skipHide) {
await hideNote(packed, meId);
}
return packed;
},
async packMany(
notes: Note[],
me?: { id: User['id'] } | null | undefined,
options?: {
detail?: boolean;
skipHide?: boolean;
}
) {
if (notes.length === 0) return [];
const meId = me ? me.id : null;
const myReactionsMap = new Map<Note['id'], NoteReaction | null>();
if (meId) {
const renoteIds = notes.filter(n => n.renoteId != null).map(n => n.renoteId!);
const targets = [...notes.map(n => n.id), ...renoteIds];
const myReactions = await NoteReactions.findBy({
userId: meId,
noteId: In(targets),
});
for (const target of targets) {
myReactionsMap.set(target, myReactions.find(reaction => reaction.noteId === target) || null);
}
}
await prefetchEmojis(aggregateNoteEmojis(notes));
return await Promise.all(notes.map(n => this.pack(n, me, {
...options,
_hint_: {
myReactions: myReactionsMap,
},
})));
},
});

View File

@@ -1,115 +0,0 @@
import { In, Repository } from 'typeorm';
import { Users, Notes, UserGroupInvitations, AccessTokens, NoteReactions } from '../index.js';
import { Notification } from '@/models/entities/notification.js';
import { awaitAll } from '@/prelude/await-all.js';
import { Packed } from '@/misc/schema.js';
import { Note } from '@/models/entities/note.js';
import { NoteReaction } from '@/models/entities/note-reaction.js';
import { User } from '@/models/entities/user.js';
import { aggregateNoteEmojis, prefetchEmojis } from '@/misc/populate-emojis.js';
import { notificationTypes } from '@/types.js';
import { db } from '@/db/postgre.js';
export const NotificationRepository = db.getRepository(Notification).extend({
async pack(
src: Notification['id'] | Notification,
options: {
_hintForEachNotes_?: {
myReactions: Map<Note['id'], NoteReaction | null>;
};
}
): Promise<Packed<'Notification'>> {
const notification = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
const token = notification.appAccessTokenId ? await AccessTokens.findOneByOrFail({ id: notification.appAccessTokenId }) : null;
return await awaitAll({
id: notification.id,
createdAt: notification.createdAt.toISOString(),
type: notification.type,
isRead: notification.isRead,
userId: notification.notifierId,
user: notification.notifierId ? Users.pack(notification.notifier || notification.notifierId) : null,
...(notification.type === 'mention' ? {
note: Notes.pack(notification.note || notification.noteId!, { id: notification.notifieeId }, {
detail: true,
_hint_: options._hintForEachNotes_,
}),
} : {}),
...(notification.type === 'reply' ? {
note: Notes.pack(notification.note || notification.noteId!, { id: notification.notifieeId }, {
detail: true,
_hint_: options._hintForEachNotes_,
}),
} : {}),
...(notification.type === 'renote' ? {
note: Notes.pack(notification.note || notification.noteId!, { id: notification.notifieeId }, {
detail: true,
_hint_: options._hintForEachNotes_,
}),
} : {}),
...(notification.type === 'quote' ? {
note: Notes.pack(notification.note || notification.noteId!, { id: notification.notifieeId }, {
detail: true,
_hint_: options._hintForEachNotes_,
}),
} : {}),
...(notification.type === 'reaction' ? {
note: Notes.pack(notification.note || notification.noteId!, { id: notification.notifieeId }, {
detail: true,
_hint_: options._hintForEachNotes_,
}),
reaction: notification.reaction,
} : {}),
...(notification.type === 'pollVote' ? {
note: Notes.pack(notification.note || notification.noteId!, { id: notification.notifieeId }, {
detail: true,
_hint_: options._hintForEachNotes_,
}),
choice: notification.choice,
} : {}),
...(notification.type === 'pollEnded' ? {
note: Notes.pack(notification.note || notification.noteId!, { id: notification.notifieeId }, {
detail: true,
_hint_: options._hintForEachNotes_,
}),
} : {}),
...(notification.type === 'groupInvited' ? {
invitation: UserGroupInvitations.pack(notification.userGroupInvitationId!),
} : {}),
...(notification.type === 'app' ? {
body: notification.customBody,
header: notification.customHeader || token?.name,
icon: notification.customIcon || token?.iconUrl,
} : {}),
});
},
async packMany(
notifications: Notification[],
meId: User['id']
) {
if (notifications.length === 0) return [];
const notes = notifications.filter(x => x.note != null).map(x => x.note!);
const noteIds = notes.map(n => n.id);
const myReactionsMap = new Map<Note['id'], NoteReaction | null>();
const renoteIds = notes.filter(n => n.renoteId != null).map(n => n.renoteId!);
const targets = [...noteIds, ...renoteIds];
const myReactions = await NoteReactions.findBy({
userId: meId,
noteId: In(targets),
});
for (const target of targets) {
myReactionsMap.set(target, myReactions.find(reaction => reaction.noteId === target) || null);
}
await prefetchEmojis(aggregateNoteEmojis(notes));
return await Promise.all(notifications.map(x => this.pack(x, {
_hintForEachNotes_: {
myReactions: myReactionsMap,
},
})));
},
});

View File

@@ -1,25 +0,0 @@
import { db } from '@/db/postgre.js';
import { PageLike } from '@/models/entities/page-like.js';
import { Pages } from '../index.js';
import { User } from '@/models/entities/user.js';
export const PageLikeRepository = db.getRepository(PageLike).extend({
async pack(
src: PageLike['id'] | PageLike,
me?: { id: User['id'] } | null | undefined
) {
const like = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
return {
id: like.id,
page: await Pages.pack(like.page || like.pageId, me),
};
},
packMany(
likes: any[],
me: { id: User['id'] }
) {
return Promise.all(likes.map(x => this.pack(x, me)));
},
});

View File

@@ -1,88 +0,0 @@
import { db } from '@/db/postgre.js';
import { Page } from '@/models/entities/page.js';
import { Packed } from '@/misc/schema.js';
import { awaitAll } from '@/prelude/await-all.js';
import { DriveFile } from '@/models/entities/drive-file.js';
import { User } from '@/models/entities/user.js';
import { Users, DriveFiles, PageLikes } from '../index.js';
export const PageRepository = db.getRepository(Page).extend({
async pack(
src: Page['id'] | Page,
me?: { id: User['id'] } | null | undefined,
): Promise<Packed<'Page'>> {
const meId = me ? me.id : null;
const page = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
const attachedFiles: Promise<DriveFile | null>[] = [];
const collectFile = (xs: any[]) => {
for (const x of xs) {
if (x.type === 'image') {
attachedFiles.push(DriveFiles.findOneBy({
id: x.fileId,
userId: page.userId,
}));
}
if (x.children) {
collectFile(x.children);
}
}
};
collectFile(page.content);
// 後方互換性のため
let migrated = false;
const migrate = (xs: any[]) => {
for (const x of xs) {
if (x.type === 'input') {
if (x.inputType === 'text') {
x.type = 'textInput';
}
if (x.inputType === 'number') {
x.type = 'numberInput';
if (x.default) x.default = parseInt(x.default, 10);
}
migrated = true;
}
if (x.children) {
migrate(x.children);
}
}
};
migrate(page.content);
if (migrated) {
this.update(page.id, {
content: page.content,
});
}
return await awaitAll({
id: page.id,
createdAt: page.createdAt.toISOString(),
updatedAt: page.updatedAt.toISOString(),
userId: page.userId,
user: Users.pack(page.user || page.userId, me), // { detail: true } すると無限ループするので注意
content: page.content,
variables: page.variables,
title: page.title,
name: page.name,
summary: page.summary,
hideTitleWhenPinned: page.hideTitleWhenPinned,
alignCenter: page.alignCenter,
font: page.font,
script: page.script,
eyeCatchingImageId: page.eyeCatchingImageId,
eyeCatchingImage: page.eyeCatchingImageId ? await DriveFiles.pack(page.eyeCatchingImageId) : null,
attachedFiles: DriveFiles.packMany((await Promise.all(attachedFiles)).filter((x): x is DriveFile => x != null)),
likedCount: page.likedCount,
isLiked: meId ? await PageLikes.findOneBy({ pageId: page.id, userId: meId }).then(x => x != null) : undefined,
});
},
packMany(
pages: Page[],
me?: { id: User['id'] } | null | undefined,
) {
return Promise.all(pages.map(x => this.pack(x, me)));
},
});

View File

@@ -1,5 +0,0 @@
import { db } from '@/db/postgre.js';
import { Relay } from '@/models/entities/relay.js';
export const RelayRepository = db.getRepository(Relay).extend({
});

View File

@@ -1,10 +0,0 @@
import { db } from '@/db/postgre.js';
import { Signin } from '@/models/entities/signin.js';
export const SigninRepository = db.getRepository(Signin).extend({
async pack(
src: Signin,
) {
return src;
},
});

View File

@@ -1,22 +0,0 @@
import { db } from '@/db/postgre.js';
import { UserGroupInvitation } from '@/models/entities/user-group-invitation.js';
import { UserGroups } from '../index.js';
export const UserGroupInvitationRepository = db.getRepository(UserGroupInvitation).extend({
async pack(
src: UserGroupInvitation['id'] | UserGroupInvitation,
) {
const invitation = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
return {
id: invitation.id,
group: await UserGroups.pack(invitation.userGroup || invitation.userGroupId),
};
},
packMany(
invitations: any[],
) {
return Promise.all(invitations.map(x => this.pack(x)));
},
});

View File

@@ -1,24 +0,0 @@
import { db } from '@/db/postgre.js';
import { UserGroup } from '@/models/entities/user-group.js';
import { UserGroupJoinings } from '../index.js';
import { Packed } from '@/misc/schema.js';
export const UserGroupRepository = db.getRepository(UserGroup).extend({
async pack(
src: UserGroup['id'] | UserGroup,
): Promise<Packed<'UserGroup'>> {
const userGroup = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
const users = await UserGroupJoinings.findBy({
userGroupId: userGroup.id,
});
return {
id: userGroup.id,
createdAt: userGroup.createdAt.toISOString(),
name: userGroup.name,
ownerId: userGroup.userId,
userIds: users.map(x => x.userId),
};
},
});

View File

@@ -1,23 +0,0 @@
import { db } from '@/db/postgre.js';
import { UserList } from '@/models/entities/user-list.js';
import { UserListJoinings } from '../index.js';
import { Packed } from '@/misc/schema.js';
export const UserListRepository = db.getRepository(UserList).extend({
async pack(
src: UserList['id'] | UserList,
): Promise<Packed<'UserList'>> {
const userList = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
const users = await UserListJoinings.findBy({
userListId: userList.id,
});
return {
id: userList.id,
createdAt: userList.createdAt.toISOString(),
name: userList.name,
userIds: users.map(x => x.userId),
};
},
});

View File

@@ -1,436 +0,0 @@
import { EntityRepository, Repository, In, Not } from 'typeorm';
import Ajv from 'ajv';
import { User, ILocalUser, IRemoteUser } from '@/models/entities/user.js';
import config from '@/config/index.js';
import { Packed } from '@/misc/schema.js';
import { awaitAll, Promiseable } from '@/prelude/await-all.js';
import { populateEmojis } from '@/misc/populate-emojis.js';
import { getAntennas } from '@/misc/antenna-cache.js';
import { USER_ACTIVE_THRESHOLD, USER_ONLINE_THRESHOLD } from '@/const.js';
import { Cache } from '@/misc/cache.js';
import { db } from '@/db/postgre.js';
import { Instance } from '../entities/instance.js';
import { Notes, NoteUnreads, FollowRequests, Notifications, MessagingMessages, UserNotePinings, Followings, Blockings, Mutings, UserProfiles, UserSecurityKeys, UserGroupJoinings, Pages, Announcements, AnnouncementReads, Antennas, AntennaNotes, ChannelFollowings, Instances, DriveFiles } from '../index.js';
const userInstanceCache = new Cache<Instance | null>(1000 * 60 * 60 * 3);
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'>;
const ajv = new Ajv();
const localUsernameSchema = { type: 'string', pattern: /^\w{1,20}$/.toString().slice(1, -1) } as const;
const passwordSchema = { type: 'string', minLength: 1 } as const;
const nameSchema = { type: 'string', minLength: 1, maxLength: 50 } as const;
const descriptionSchema = { type: 'string', minLength: 1, maxLength: 500 } as const;
const locationSchema = { type: 'string', minLength: 1, maxLength: 50 } as const;
const birthdaySchema = { type: 'string', pattern: /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/.toString().slice(1, -1) } as const;
function isLocalUser(user: User): user is ILocalUser;
function isLocalUser<T extends { host: User['host'] }>(user: T): user is T & { host: null; };
function isLocalUser(user: User | { host: User['host'] }): boolean {
return user.host == null;
}
function isRemoteUser(user: User): user is IRemoteUser;
function isRemoteUser<T extends { host: User['host'] }>(user: T): user is T & { host: string; };
function isRemoteUser(user: User | { host: User['host'] }): boolean {
return !isLocalUser(user);
}
export const UserRepository = db.getRepository(User).extend({
localUsernameSchema,
passwordSchema,
nameSchema,
descriptionSchema,
locationSchema,
birthdaySchema,
//#region Validators
validateLocalUsername: ajv.compile(localUsernameSchema),
validatePassword: ajv.compile(passwordSchema),
validateName: ajv.compile(nameSchema),
validateDescription: ajv.compile(descriptionSchema),
validateLocation: ajv.compile(locationSchema),
validateBirthday: ajv.compile(birthdaySchema),
//#endregion
async getRelation(me: User['id'], target: User['id']) {
return awaitAll({
id: target,
isFollowing: Followings.count({
where: {
followerId: me,
followeeId: target,
},
take: 1,
}).then(n => n > 0),
isFollowed: Followings.count({
where: {
followerId: target,
followeeId: me,
},
take: 1,
}).then(n => n > 0),
hasPendingFollowRequestFromYou: FollowRequests.count({
where: {
followerId: me,
followeeId: target,
},
take: 1,
}).then(n => n > 0),
hasPendingFollowRequestToYou: FollowRequests.count({
where: {
followerId: target,
followeeId: me,
},
take: 1,
}).then(n => n > 0),
isBlocking: Blockings.count({
where: {
blockerId: me,
blockeeId: target,
},
take: 1,
}).then(n => n > 0),
isBlocked: Blockings.count({
where: {
blockerId: target,
blockeeId: me,
},
take: 1,
}).then(n => n > 0),
isMuted: Mutings.count({
where: {
muterId: me,
muteeId: target,
},
take: 1,
}).then(n => n > 0),
});
},
async getHasUnreadMessagingMessage(userId: User['id']): Promise<boolean> {
const mute = await Mutings.findBy({
muterId: userId,
});
const joinings = await UserGroupJoinings.findBy({ userId: userId });
const groupQs = Promise.all(joinings.map(j => MessagingMessages.createQueryBuilder('message')
.where('message.groupId = :groupId', { groupId: j.userGroupId })
.andWhere('message.userId != :userId', { userId: userId })
.andWhere('NOT (:userId = ANY(message.reads))', { userId: userId })
.andWhere('message.createdAt > :joinedAt', { joinedAt: j.createdAt }) // 自分が加入する前の会話については、未読扱いしない
.getOne().then(x => x != null)));
const [withUser, withGroups] = await Promise.all([
MessagingMessages.count({
where: {
recipientId: userId,
isRead: false,
...(mute.length > 0 ? { userId: Not(In(mute.map(x => x.muteeId))) } : {}),
},
take: 1,
}).then(count => count > 0),
groupQs,
]);
return withUser || withGroups.some(x => x);
},
async getHasUnreadAnnouncement(userId: User['id']): Promise<boolean> {
const reads = await AnnouncementReads.findBy({
userId: userId,
});
const count = await Announcements.countBy(reads.length > 0 ? {
id: Not(In(reads.map(read => read.announcementId))),
} : {});
return count > 0;
},
async getHasUnreadAntenna(userId: User['id']): Promise<boolean> {
const myAntennas = (await getAntennas()).filter(a => a.userId === userId);
const unread = myAntennas.length > 0 ? await AntennaNotes.findOneBy({
antennaId: In(myAntennas.map(x => x.id)),
read: false,
}) : null;
return unread != null;
},
async getHasUnreadChannel(userId: User['id']): Promise<boolean> {
const channels = await ChannelFollowings.findBy({ followerId: userId });
const unread = channels.length > 0 ? await NoteUnreads.findOneBy({
userId: userId,
noteChannelId: In(channels.map(x => x.followeeId)),
}) : null;
return unread != null;
},
async getHasUnreadNotification(userId: User['id']): Promise<boolean> {
const mute = await Mutings.findBy({
muterId: userId,
});
const mutedUserIds = mute.map(m => m.muteeId);
const count = await Notifications.count({
where: {
notifieeId: userId,
...(mutedUserIds.length > 0 ? { notifierId: Not(In(mutedUserIds)) } : {}),
isRead: false,
},
take: 1,
});
return count > 0;
},
async getHasPendingReceivedFollowRequest(userId: User['id']): Promise<boolean> {
const count = await FollowRequests.countBy({
followeeId: userId,
});
return count > 0;
},
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();
return (
elapsed < USER_ONLINE_THRESHOLD ? 'online' :
elapsed < USER_ACTIVE_THRESHOLD ? 'active' :
'offline'
);
},
async getAvatarUrl(user: User): Promise<string> {
if (user.avatar) {
return DriveFiles.getPublicUrl(user.avatar, true) || this.getIdenticonUrl(user.id);
} else if (user.avatarId) {
const avatar = await DriveFiles.findOneByOrFail({ id: user.avatarId });
return DriveFiles.getPublicUrl(avatar, true) || this.getIdenticonUrl(user.id);
} else {
return this.getIdenticonUrl(user.id);
}
},
getAvatarUrlSync(user: User): string {
if (user.avatar) {
return DriveFiles.getPublicUrl(user.avatar, true) || this.getIdenticonUrl(user.id);
} else {
return this.getIdenticonUrl(user.id);
}
},
getIdenticonUrl(userId: User['id']): string {
return `${config.url}/identicon/${userId}`;
},
async pack<ExpectsMe extends boolean | null = null, D extends boolean = false>(
src: User['id'] | User,
me?: { id: User['id'] } | null | undefined,
options?: {
detail?: D,
includeSecrets?: boolean,
},
): Promise<IsMeAndIsUserDetailed<ExpectsMe, D>> {
const opts = Object.assign({
detail: false,
includeSecrets: false,
}, options);
let user: User;
if (typeof src === 'object') {
user = src;
if (src.avatar === undefined && src.avatarId) src.avatar = await DriveFiles.findOneBy({ id: src.avatarId }) ?? null;
if (src.banner === undefined && src.bannerId) src.banner = await DriveFiles.findOneBy({ id: src.bannerId }) ?? null;
} else {
user = await this.findOneOrFail({
where: { id: src },
relations: {
avatar: true,
banner: true,
},
});
}
const meId = me ? me.id : null;
const isMe = meId === user.id;
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')
.orderBy('pin.id', 'DESC')
.getMany() : [];
const profile = opts.detail ? await UserProfiles.findOneByOrFail({ userId: user.id }) : null;
const followingCount = profile == null ? null :
(profile.ffVisibility === 'public') || isMe ? user.followingCount :
(profile.ffVisibility === 'followers') && (relation && relation.isFollowing) ? user.followingCount :
null;
const followersCount = profile == null ? null :
(profile.ffVisibility === 'public') || isMe ? user.followersCount :
(profile.ffVisibility === 'followers') && (relation && relation.isFollowing) ? user.followersCount :
null;
const falsy = opts.detail ? false : undefined;
const packed = {
id: user.id,
name: user.name,
username: user.username,
host: user.host,
avatarUrl: this.getAvatarUrlSync(user),
avatarBlurhash: user.avatar?.blurhash || null,
avatarColor: null, // 後方互換性のため
isAdmin: user.isAdmin || falsy,
isModerator: user.isModerator || falsy,
isBot: user.isBot || falsy,
isCat: user.isCat || falsy,
instance: user.host ? userInstanceCache.fetch(user.host,
() => Instances.findOneBy({ host: user.host! }),
v => v != null,
).then(instance => instance ? {
name: instance.name,
softwareName: instance.softwareName,
softwareVersion: instance.softwareVersion,
iconUrl: instance.iconUrl,
faviconUrl: instance.faviconUrl,
themeColor: instance.themeColor,
} : undefined) : undefined,
emojis: populateEmojis(user.emojis, user.host),
onlineStatus: this.getOnlineStatus(user),
driveCapacityOverrideMb: user.driveCapacityOverrideMb,
...(opts.detail ? {
url: profile!.url,
uri: user.uri,
createdAt: user.createdAt.toISOString(),
updatedAt: user.updatedAt ? user.updatedAt.toISOString() : null,
lastFetchedAt: user.lastFetchedAt ? user.lastFetchedAt.toISOString() : null,
bannerUrl: user.banner ? DriveFiles.getPublicUrl(user.banner, false) : null,
bannerBlurhash: user.banner?.blurhash || null,
bannerColor: null, // 後方互換性のため
isLocked: user.isLocked,
isSilenced: user.isSilenced || falsy,
isSuspended: user.isSuspended || falsy,
description: profile!.description,
location: profile!.location,
birthday: profile!.birthday,
lang: profile!.lang,
fields: profile!.fields,
followersCount: followersCount || 0,
followingCount: followingCount || 0,
notesCount: user.notesCount,
pinnedNoteIds: pins.map(pin => pin.noteId),
pinnedNotes: Notes.packMany(pins.map(pin => pin.note!), me, {
detail: true,
}),
pinnedPageId: profile!.pinnedPageId,
pinnedPage: profile!.pinnedPageId ? Pages.pack(profile!.pinnedPageId, me) : null,
publicReactions: profile!.publicReactions,
ffVisibility: profile!.ffVisibility,
twoFactorEnabled: profile!.twoFactorEnabled,
usePasswordLessLogin: profile!.usePasswordLessLogin,
securityKeys: profile!.twoFactorEnabled
? UserSecurityKeys.countBy({
userId: user.id,
}).then(result => result >= 1)
: false,
} : {}),
...(opts.detail && isMe ? {
avatarId: user.avatarId,
bannerId: user.bannerId,
injectFeaturedNote: profile!.injectFeaturedNote,
receiveAnnouncementEmail: profile!.receiveAnnouncementEmail,
alwaysMarkNsfw: profile!.alwaysMarkNsfw,
autoSensitive: profile!.autoSensitive,
carefulBot: profile!.carefulBot,
autoAcceptFollowed: profile!.autoAcceptFollowed,
noCrawle: profile!.noCrawle,
isExplorable: user.isExplorable,
isDeleted: user.isDeleted,
hideOnlineStatus: user.hideOnlineStatus,
hasUnreadSpecifiedNotes: NoteUnreads.count({
where: { userId: user.id, isSpecified: true },
take: 1,
}).then(count => count > 0),
hasUnreadMentions: NoteUnreads.count({
where: { userId: user.id, isMentioned: true },
take: 1,
}).then(count => count > 0),
hasUnreadAnnouncement: this.getHasUnreadAnnouncement(user.id),
hasUnreadAntenna: this.getHasUnreadAntenna(user.id),
hasUnreadChannel: this.getHasUnreadChannel(user.id),
hasUnreadMessagingMessage: this.getHasUnreadMessagingMessage(user.id),
hasUnreadNotification: this.getHasUnreadNotification(user.id),
hasPendingReceivedFollowRequest: this.getHasPendingReceivedFollowRequest(user.id),
integrations: profile!.integrations,
mutedWords: profile!.mutedWords,
mutedInstances: profile!.mutedInstances,
mutingNotificationTypes: profile!.mutingNotificationTypes,
emailNotificationTypes: profile!.emailNotificationTypes,
showTimelineReplies: user.showTimelineReplies || falsy,
} : {}),
...(opts.includeSecrets ? {
email: profile!.email,
emailVerified: profile!.emailVerified,
securityKeysList: profile!.twoFactorEnabled
? UserSecurityKeys.find({
where: {
userId: user.id,
},
select: {
id: true,
name: true,
lastUsed: true,
},
})
: [],
} : {}),
...(relation ? {
isFollowing: relation.isFollowing,
isFollowed: relation.isFollowed,
hasPendingFollowRequestFromYou: relation.hasPendingFollowRequestFromYou,
hasPendingFollowRequestToYou: relation.hasPendingFollowRequestToYou,
isBlocking: relation.isBlocking,
isBlocked: relation.isBlocked,
isMuted: relation.isMuted,
} : {}),
} as Promiseable<Packed<'User'>> as Promiseable<IsMeAndIsUserDetailed<ExpectsMe, D>>;
return await awaitAll(packed);
},
packMany<D extends boolean = false>(
users: (User['id'] | User)[],
me?: { id: User['id'] } | null | undefined,
options?: {
detail?: D,
includeSecrets?: boolean,
},
): Promise<IsUserDetailed<D>[]> {
return Promise.all(users.map(u => this.pack(u, me, options)));
},
isLocalUser,
isRemoteUser,
});

View File

@@ -1,5 +1,3 @@
import config from '@/config/index.js';
export const packedFederationInstanceSchema = {
type: 'object',
properties: {
@@ -64,7 +62,6 @@ export const packedFederationInstanceSchema = {
softwareVersion: {
type: 'string',
optional: false, nullable: true,
example: config.version,
},
openRegistrations: {
type: 'boolean',