diff options
| author | syuilo <syuilotan@yahoo.co.jp> | 2019-04-14 20:38:55 +0900 |
|---|---|---|
| committer | syuilo <syuilotan@yahoo.co.jp> | 2019-04-14 20:38:55 +0900 |
| commit | d66e4b7ff97d512e2a2523815e2eef170456b37f (patch) | |
| tree | 59ae1a102d88b5c2c2236b734ea4a584b4f9ba46 /src/models/entities | |
| parent | 10.100.0 (diff) | |
| parent | 11.0.0 (diff) | |
| download | misskey-d66e4b7ff97d512e2a2523815e2eef170456b37f.tar.gz misskey-d66e4b7ff97d512e2a2523815e2eef170456b37f.tar.bz2 misskey-d66e4b7ff97d512e2a2523815e2eef170456b37f.zip | |
Merge branch 'develop'
Diffstat (limited to 'src/models/entities')
36 files changed, 2754 insertions, 0 deletions
diff --git a/src/models/entities/abuse-user-report.ts b/src/models/entities/abuse-user-report.ts new file mode 100644 index 0000000000..43ab56023a --- /dev/null +++ b/src/models/entities/abuse-user-report.ts @@ -0,0 +1,41 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from './user'; +import { id } from '../id'; + +@Entity() +@Index(['userId', 'reporterId'], { unique: true }) +export class AbuseUserReport { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column('timestamp with time zone', { + comment: 'The created date of the AbuseUserReport.' + }) + public createdAt: Date; + + @Index() + @Column(id()) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public user: User | null; + + @Index() + @Column(id()) + public reporterId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public reporter: User | null; + + @Column('varchar', { + length: 512, + }) + public comment: string; +} diff --git a/src/models/entities/access-token.ts b/src/models/entities/access-token.ts new file mode 100644 index 0000000000..d08930cf5a --- /dev/null +++ b/src/models/entities/access-token.ts @@ -0,0 +1,45 @@ +import { Entity, PrimaryColumn, Index, Column, ManyToOne, JoinColumn, RelationId } from 'typeorm'; +import { User } from './user'; +import { App } from './app'; +import { id } from '../id'; + +@Entity() +export class AccessToken { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone', { + comment: 'The created date of the AccessToken.' + }) + public createdAt: Date; + + @Index() + @Column('varchar', { + length: 128 + }) + public token: string; + + @Index() + @Column('varchar', { + length: 128 + }) + public hash: string; + + @RelationId((self: AccessToken) => self.user) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public user: User | null; + + @Column(id()) + public appId: App['id']; + + @ManyToOne(type => App, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public app: App | null; +} diff --git a/src/models/entities/app.ts b/src/models/entities/app.ts new file mode 100644 index 0000000000..ea87546311 --- /dev/null +++ b/src/models/entities/app.ts @@ -0,0 +1,60 @@ +import { Entity, PrimaryColumn, Column, Index, ManyToOne } from 'typeorm'; +import { User } from './user'; +import { id } from '../id'; + +@Entity() +export class App { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column('timestamp with time zone', { + comment: 'The created date of the App.' + }) + public createdAt: Date; + + @Index() + @Column({ + ...id(), + nullable: true, + comment: 'The owner ID.' + }) + public userId: User['id'] | null; + + @ManyToOne(type => User, { + onDelete: 'SET NULL', + nullable: true, + }) + public user: User | null; + + @Index() + @Column('varchar', { + length: 64, + comment: 'The secret key of the App.' + }) + public secret: string; + + @Column('varchar', { + length: 128, + comment: 'The name of the App.' + }) + public name: string; + + @Column('varchar', { + length: 512, + comment: 'The description of the App.' + }) + public description: string; + + @Column('varchar', { + length: 64, array: true, + comment: 'The permission of the App.' + }) + public permission: string[]; + + @Column('varchar', { + length: 512, nullable: true, + comment: 'The callbackUrl of the App.' + }) + public callbackUrl: string | null; +} diff --git a/src/models/entities/auth-session.ts b/src/models/entities/auth-session.ts new file mode 100644 index 0000000000..4eec27e3f6 --- /dev/null +++ b/src/models/entities/auth-session.ts @@ -0,0 +1,43 @@ +import { Entity, PrimaryColumn, Index, Column, ManyToOne, JoinColumn } from 'typeorm'; +import { User } from './user'; +import { App } from './app'; +import { id } from '../id'; + +@Entity() +export class AuthSession { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone', { + comment: 'The created date of the AuthSession.' + }) + public createdAt: Date; + + @Index() + @Column('varchar', { + length: 128 + }) + public token: string; + + @Column({ + ...id(), + nullable: true + }) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + nullable: true + }) + @JoinColumn() + public user: User | null; + + @Column(id()) + public appId: App['id']; + + @ManyToOne(type => App, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public app: App | null; +} diff --git a/src/models/entities/blocking.ts b/src/models/entities/blocking.ts new file mode 100644 index 0000000000..48487cb086 --- /dev/null +++ b/src/models/entities/blocking.ts @@ -0,0 +1,42 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from './user'; +import { id } from '../id'; + +@Entity() +@Index(['blockerId', 'blockeeId'], { unique: true }) +export class Blocking { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column('timestamp with time zone', { + comment: 'The created date of the Blocking.' + }) + public createdAt: Date; + + @Index() + @Column({ + ...id(), + comment: 'The blockee user ID.' + }) + public blockeeId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public blockee: User | null; + + @Index() + @Column({ + ...id(), + comment: 'The blocker user ID.' + }) + public blockerId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public blocker: User | null; +} diff --git a/src/models/entities/drive-file.ts b/src/models/entities/drive-file.ts new file mode 100644 index 0000000000..130af39ede --- /dev/null +++ b/src/models/entities/drive-file.ts @@ -0,0 +1,154 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from './user'; +import { DriveFolder } from './drive-folder'; +import { id } from '../id'; + +@Entity() +export class DriveFile { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column('timestamp with time zone', { + comment: 'The created date of the DriveFile.' + }) + public createdAt: Date; + + @Index() + @Column({ + ...id(), + nullable: true, + comment: 'The owner ID.' + }) + public userId: User['id'] | null; + + @ManyToOne(type => User, { + onDelete: 'SET NULL' + }) + @JoinColumn() + public user: User | null; + + @Index() + @Column('varchar', { + length: 128, nullable: true, + comment: 'The host of owner. It will be null if the user in local.' + }) + public userHost: string | null; + + @Index() + @Column('varchar', { + length: 32, + comment: 'The MD5 hash of the DriveFile.' + }) + public md5: string; + + @Column('varchar', { + length: 256, + comment: 'The file name of the DriveFile.' + }) + public name: string; + + @Index() + @Column('varchar', { + length: 128, + comment: 'The content type (MIME) of the DriveFile.' + }) + public type: string; + + @Column('integer', { + comment: 'The file size (bytes) of the DriveFile.' + }) + public size: number; + + @Column('varchar', { + length: 512, nullable: true, + comment: 'The comment of the DriveFile.' + }) + public comment: string | null; + + @Column('jsonb', { + default: {}, + comment: 'The any properties of the DriveFile. For example, it includes image width/height.' + }) + public properties: Record<string, any>; + + @Column('boolean') + public storedInternal: boolean; + + @Column('varchar', { + length: 512, + comment: 'The URL of the DriveFile.' + }) + public url: string; + + @Column('varchar', { + length: 512, nullable: true, + comment: 'The URL of the thumbnail of the DriveFile.' + }) + public thumbnailUrl: string | null; + + @Column('varchar', { + length: 512, nullable: true, + comment: 'The URL of the webpublic of the DriveFile.' + }) + public webpublicUrl: string | null; + + @Index({ unique: true }) + @Column('varchar', { + length: 256, nullable: true, + }) + public accessKey: string | null; + + @Index({ unique: true }) + @Column('varchar', { + length: 256, nullable: true, + }) + public thumbnailAccessKey: string | null; + + @Index({ unique: true }) + @Column('varchar', { + length: 256, nullable: true, + }) + public webpublicAccessKey: string | null; + + @Index() + @Column('varchar', { + length: 512, nullable: true, + comment: 'The URI of the DriveFile. it will be null when the DriveFile is local.' + }) + public uri: string | null; + + @Column('varchar', { + length: 512, nullable: true, + }) + public src: string | null; + + @Index() + @Column({ + ...id(), + nullable: true, + comment: 'The parent folder ID. If null, it means the DriveFile is located in root.' + }) + public folderId: DriveFolder['id'] | null; + + @ManyToOne(type => DriveFolder, { + onDelete: 'SET NULL' + }) + @JoinColumn() + public folder: DriveFolder | null; + + @Column('boolean', { + default: false, + comment: 'Whether the DriveFile is NSFW.' + }) + public isSensitive: boolean; + + /** + * 外部の(信頼されていない)URLへの直リンクか否か + */ + @Column('boolean', { + default: false, + comment: 'Whether the DriveFile is direct link to remote server.' + }) + public isLink: boolean; +} diff --git a/src/models/entities/drive-folder.ts b/src/models/entities/drive-folder.ts new file mode 100644 index 0000000000..a80d075855 --- /dev/null +++ b/src/models/entities/drive-folder.ts @@ -0,0 +1,49 @@ +import { JoinColumn, ManyToOne, Entity, PrimaryColumn, Index, Column } from 'typeorm'; +import { User } from './user'; +import { id } from '../id'; + +@Entity() +export class DriveFolder { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column('timestamp with time zone', { + comment: 'The created date of the DriveFolder.' + }) + public createdAt: Date; + + @Column('varchar', { + length: 128, + comment: 'The name of the DriveFolder.' + }) + public name: string; + + @Index() + @Column({ + ...id(), + nullable: true, + comment: 'The owner ID.' + }) + public userId: User['id'] | null; + + @ManyToOne(type => User, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public user: User | null; + + @Index() + @Column({ + ...id(), + nullable: true, + comment: 'The parent folder ID. If null, it means the DriveFolder is located in root.' + }) + public parentId: DriveFolder['id'] | null; + + @ManyToOne(type => DriveFolder, { + onDelete: 'SET NULL' + }) + @JoinColumn() + public parent: DriveFolder | null; +} diff --git a/src/models/entities/emoji.ts b/src/models/entities/emoji.ts new file mode 100644 index 0000000000..020636a7fb --- /dev/null +++ b/src/models/entities/emoji.ts @@ -0,0 +1,46 @@ +import { PrimaryColumn, Entity, Index, Column } from 'typeorm'; +import { id } from '../id'; + +@Entity() +@Index(['name', 'host'], { unique: true }) +export class Emoji { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone', { + nullable: true + }) + public updatedAt: Date | null; + + @Index() + @Column('varchar', { + length: 128 + }) + public name: string; + + @Index() + @Column('varchar', { + length: 128, nullable: true + }) + public host: string | null; + + @Column('varchar', { + length: 512, + }) + public url: string; + + @Column('varchar', { + length: 512, nullable: true + }) + public uri: string | null; + + @Column('varchar', { + length: 64, nullable: true + }) + public type: string | null; + + @Column('varchar', { + array: true, length: 128, default: '{}' + }) + public aliases: string[]; +} diff --git a/src/models/entities/follow-request.ts b/src/models/entities/follow-request.ts new file mode 100644 index 0000000000..22ec263962 --- /dev/null +++ b/src/models/entities/follow-request.ts @@ -0,0 +1,85 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from './user'; +import { id } from '../id'; + +@Entity() +@Index(['followerId', 'followeeId'], { unique: true }) +export class FollowRequest { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone', { + comment: 'The created date of the FollowRequest.' + }) + public createdAt: Date; + + @Index() + @Column({ + ...id(), + comment: 'The followee user ID.' + }) + public followeeId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public followee: User | null; + + @Index() + @Column({ + ...id(), + comment: 'The follower user ID.' + }) + public followerId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public follower: User | null; + + @Column('varchar', { + length: 128, nullable: true, + comment: 'id of Follow Activity.' + }) + public requestId: string | null; + + //#region Denormalized fields + @Column('varchar', { + length: 128, nullable: true, + comment: '[Denormalized]' + }) + public followerHost: string | null; + + @Column('varchar', { + length: 512, nullable: true, + comment: '[Denormalized]' + }) + public followerInbox: string | null; + + @Column('varchar', { + length: 512, nullable: true, + comment: '[Denormalized]' + }) + public followerSharedInbox: string | null; + + @Column('varchar', { + length: 128, nullable: true, + comment: '[Denormalized]' + }) + public followeeHost: string | null; + + @Column('varchar', { + length: 512, nullable: true, + comment: '[Denormalized]' + }) + public followeeInbox: string | null; + + @Column('varchar', { + length: 512, nullable: true, + comment: '[Denormalized]' + }) + public followeeSharedInbox: string | null; + //#endregion +} diff --git a/src/models/entities/following.ts b/src/models/entities/following.ts new file mode 100644 index 0000000000..ee3286a1a1 --- /dev/null +++ b/src/models/entities/following.ts @@ -0,0 +1,80 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from './user'; +import { id } from '../id'; + +@Entity() +@Index(['followerId', 'followeeId'], { unique: true }) +export class Following { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column('timestamp with time zone', { + comment: 'The created date of the Following.' + }) + public createdAt: Date; + + @Index() + @Column({ + ...id(), + comment: 'The followee user ID.' + }) + public followeeId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public followee: User | null; + + @Index() + @Column({ + ...id(), + comment: 'The follower user ID.' + }) + public followerId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public follower: User | null; + + //#region Denormalized fields + @Column('varchar', { + length: 128, nullable: true, + comment: '[Denormalized]' + }) + public followerHost: string | null; + + @Column('varchar', { + length: 512, nullable: true, + comment: '[Denormalized]' + }) + public followerInbox: string | null; + + @Column('varchar', { + length: 512, nullable: true, + comment: '[Denormalized]' + }) + public followerSharedInbox: string | null; + + @Column('varchar', { + length: 128, nullable: true, + comment: '[Denormalized]' + }) + public followeeHost: string | null; + + @Column('varchar', { + length: 512, nullable: true, + comment: '[Denormalized]' + }) + public followeeInbox: string | null; + + @Column('varchar', { + length: 512, nullable: true, + comment: '[Denormalized]' + }) + public followeeSharedInbox: string | null; + //#endregion +} diff --git a/src/models/entities/games/reversi/game.ts b/src/models/entities/games/reversi/game.ts new file mode 100644 index 0000000000..9deacaf5c6 --- /dev/null +++ b/src/models/entities/games/reversi/game.ts @@ -0,0 +1,133 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from '../../user'; +import { id } from '../../../id'; + +@Entity() +export class ReversiGame { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column('timestamp with time zone', { + comment: 'The created date of the ReversiGame.' + }) + public createdAt: Date; + + @Column('timestamp with time zone', { + nullable: true, + comment: 'The started date of the ReversiGame.' + }) + public startedAt: Date | null; + + @Column(id()) + public user1Id: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public user1: User | null; + + @Column(id()) + public user2Id: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public user2: User | null; + + @Column('boolean', { + default: false, + }) + public user1Accepted: boolean; + + @Column('boolean', { + default: false, + }) + public user2Accepted: boolean; + + /** + * どちらのプレイヤーが先行(黒)か + * 1 ... user1 + * 2 ... user2 + */ + @Column('integer', { + nullable: true, + }) + public black: number | null; + + @Column('boolean', { + default: false, + }) + public isStarted: boolean; + + @Column('boolean', { + default: false, + }) + public isEnded: boolean; + + @Column({ + ...id(), + nullable: true + }) + public winnerId: User['id'] | null; + + @Column({ + ...id(), + nullable: true + }) + public surrendered: User['id'] | null; + + @Column('jsonb', { + default: [], + }) + public logs: { + at: Date; + color: boolean; + pos: number; + }[]; + + @Column('varchar', { + array: true, length: 64, + }) + public map: string[]; + + @Column('varchar', { + length: 32 + }) + public bw: string; + + @Column('boolean', { + default: false, + }) + public isLlotheo: boolean; + + @Column('boolean', { + default: false, + }) + public canPutEverywhere: boolean; + + @Column('boolean', { + default: false, + }) + public loopedBoard: boolean; + + @Column('jsonb', { + nullable: true, default: null, + }) + public form1: any | null; + + @Column('jsonb', { + nullable: true, default: null, + }) + public form2: any | null; + + /** + * ログのposを文字列としてすべて連結したもののCRC32値 + */ + @Column('varchar', { + length: 32, nullable: true + }) + public crc32: string | null; +} diff --git a/src/models/entities/games/reversi/matching.ts b/src/models/entities/games/reversi/matching.ts new file mode 100644 index 0000000000..477a29316e --- /dev/null +++ b/src/models/entities/games/reversi/matching.ts @@ -0,0 +1,35 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from '../../user'; +import { id } from '../../../id'; + +@Entity() +export class ReversiMatching { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column('timestamp with time zone', { + comment: 'The created date of the ReversiMatching.' + }) + public createdAt: Date; + + @Index() + @Column(id()) + public parentId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public parent: User | null; + + @Index() + @Column(id()) + public childId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public child: User | null; +} diff --git a/src/models/entities/hashtag.ts b/src/models/entities/hashtag.ts new file mode 100644 index 0000000000..842cdaa562 --- /dev/null +++ b/src/models/entities/hashtag.ts @@ -0,0 +1,87 @@ +import { Entity, PrimaryColumn, Index, Column } from 'typeorm'; +import { User } from './user'; +import { id } from '../id'; + +@Entity() +export class Hashtag { + @PrimaryColumn(id()) + public id: string; + + @Index({ unique: true }) + @Column('varchar', { + length: 128 + }) + public name: string; + + @Column({ + ...id(), + array: true, + }) + public mentionedUserIds: User['id'][]; + + @Index() + @Column('integer', { + default: 0 + }) + public mentionedUsersCount: number; + + @Column({ + ...id(), + array: true, + }) + public mentionedLocalUserIds: User['id'][]; + + @Index() + @Column('integer', { + default: 0 + }) + public mentionedLocalUsersCount: number; + + @Column({ + ...id(), + array: true, + }) + public mentionedRemoteUserIds: User['id'][]; + + @Index() + @Column('integer', { + default: 0 + }) + public mentionedRemoteUsersCount: number; + + @Column({ + ...id(), + array: true, + }) + public attachedUserIds: User['id'][]; + + @Index() + @Column('integer', { + default: 0 + }) + public attachedUsersCount: number; + + @Column({ + ...id(), + array: true, + }) + public attachedLocalUserIds: User['id'][]; + + @Index() + @Column('integer', { + default: 0 + }) + public attachedLocalUsersCount: number; + + @Column({ + ...id(), + array: true, + }) + public attachedRemoteUserIds: User['id'][]; + + @Index() + @Column('integer', { + default: 0 + }) + public attachedRemoteUsersCount: number; +} diff --git a/src/models/entities/instance.ts b/src/models/entities/instance.ts new file mode 100644 index 0000000000..977054263c --- /dev/null +++ b/src/models/entities/instance.ts @@ -0,0 +1,132 @@ +import { Entity, PrimaryColumn, Index, Column } from 'typeorm'; +import { id } from '../id'; + +@Entity() +export class Instance { + @PrimaryColumn(id()) + public id: string; + + /** + * このインスタンスを捕捉した日時 + */ + @Index() + @Column('timestamp with time zone', { + comment: 'The caught date of the Instance.' + }) + public caughtAt: Date; + + /** + * ホスト + */ + @Index({ unique: true }) + @Column('varchar', { + length: 128, + comment: 'The host of the Instance.' + }) + public host: string; + + /** + * インスタンスのシステム (MastodonとかMisskeyとかPleromaとか) + */ + @Column('varchar', { + length: 64, nullable: true, + comment: 'The system of the Instance.' + }) + public system: string | null; + + /** + * インスタンスのユーザー数 + */ + @Column('integer', { + default: 0, + comment: 'The count of the users of the Instance.' + }) + public usersCount: number; + + /** + * インスタンスの投稿数 + */ + @Column('integer', { + default: 0, + comment: 'The count of the notes of the Instance.' + }) + public notesCount: number; + + /** + * このインスタンスのユーザーからフォローされている、自インスタンスのユーザーの数 + */ + @Column('integer', { + default: 0, + }) + public followingCount: number; + + /** + * このインスタンスのユーザーをフォローしている、自インスタンスのユーザーの数 + */ + @Column('integer', { + default: 0, + }) + public followersCount: number; + + /** + * ドライブ使用量 + */ + @Column('integer', { + default: 0, + }) + public driveUsage: number; + + /** + * ドライブのファイル数 + */ + @Column('integer', { + default: 0, + }) + public driveFiles: number; + + /** + * 直近のリクエスト送信日時 + */ + @Column('timestamp with time zone', { + nullable: true, + }) + public latestRequestSentAt: Date | null; + + /** + * 直近のリクエスト送信時のHTTPステータスコード + */ + @Column('integer', { + nullable: true, + }) + public latestStatus: number | null; + + /** + * 直近のリクエスト受信日時 + */ + @Column('timestamp with time zone', { + nullable: true, + }) + public latestRequestReceivedAt: Date | null; + + /** + * このインスタンスと最後にやり取りした日時 + */ + @Column('timestamp with time zone') + public lastCommunicatedAt: Date; + + /** + * このインスタンスと不通かどうか + */ + @Column('boolean', { + default: false + }) + public isNotResponding: boolean; + + /** + * このインスタンスが閉鎖済みとしてマークされているか + */ + @Column('boolean', { + default: false + }) + public isMarkedAsClosed: boolean; +} diff --git a/src/models/entities/log.ts b/src/models/entities/log.ts new file mode 100644 index 0000000000..99e1e8947e --- /dev/null +++ b/src/models/entities/log.ts @@ -0,0 +1,46 @@ +import { Entity, PrimaryColumn, Index, Column } from 'typeorm'; +import { id } from '../id'; + +@Entity() +export class Log { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column('timestamp with time zone', { + comment: 'The created date of the Log.' + }) + public createdAt: Date; + + @Index() + @Column('varchar', { + length: 64, array: true, default: '{}' + }) + public domain: string[]; + + @Index() + @Column('enum', { + enum: ['error', 'warning', 'info', 'success', 'debug'] + }) + public level: string; + + @Column('varchar', { + length: 8 + }) + public worker: string; + + @Column('varchar', { + length: 128 + }) + public machine: string; + + @Column('varchar', { + length: 1024 + }) + public message: string; + + @Column('jsonb', { + default: {} + }) + public data: Record<string, any>; +} diff --git a/src/models/entities/messaging-message.ts b/src/models/entities/messaging-message.ts new file mode 100644 index 0000000000..d3c3eab3a2 --- /dev/null +++ b/src/models/entities/messaging-message.ts @@ -0,0 +1,64 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from './user'; +import { DriveFile } from './drive-file'; +import { id } from '../id'; + +@Entity() +export class MessagingMessage { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column('timestamp with time zone', { + comment: 'The created date of the MessagingMessage.' + }) + public createdAt: Date; + + @Index() + @Column({ + ...id(), + comment: 'The sender user ID.' + }) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public user: User | null; + + @Index() + @Column({ + ...id(), + comment: 'The recipient user ID.' + }) + public recipientId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public recipient: User | null; + + @Column('varchar', { + length: 4096, nullable: true + }) + public text: string | null; + + @Column('boolean', { + default: false, + }) + public isRead: boolean; + + @Column({ + ...id(), + nullable: true, + }) + public fileId: DriveFile['id'] | null; + + @ManyToOne(type => DriveFile, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public file: DriveFile | null; +} diff --git a/src/models/entities/meta.ts b/src/models/entities/meta.ts new file mode 100644 index 0000000000..f3ac23bac7 --- /dev/null +++ b/src/models/entities/meta.ts @@ -0,0 +1,264 @@ +import { Entity, Column, PrimaryColumn } from 'typeorm'; +import { id } from '../id'; + +@Entity() +export class Meta { + @PrimaryColumn(id()) + public id: string; + + @Column('varchar', { + length: 128, nullable: true + }) + public name: string | null; + + @Column('varchar', { + length: 1024, nullable: true + }) + public description: string | null; + + /** + * メンテナの名前 + */ + @Column('varchar', { + length: 128, nullable: true + }) + public maintainerName: string | null; + + /** + * メンテナの連絡先 + */ + @Column('varchar', { + length: 128, nullable: true + }) + public maintainerEmail: string | null; + + @Column('jsonb', { + default: [], + }) + public announcements: Record<string, any>[]; + + @Column('boolean', { + default: false, + }) + public disableRegistration: boolean; + + @Column('boolean', { + default: false, + }) + public disableLocalTimeline: boolean; + + @Column('boolean', { + default: false, + }) + public disableGlobalTimeline: boolean; + + @Column('boolean', { + default: true, + }) + public enableEmojiReaction: boolean; + + @Column('boolean', { + default: false, + }) + public useStarForReactionFallback: boolean; + + @Column('varchar', { + length: 64, array: true, default: '{}' + }) + public langs: string[]; + + @Column('varchar', { + length: 256, array: true, default: '{}' + }) + public hiddenTags: string[]; + + @Column('varchar', { + length: 256, array: true, default: '{}' + }) + public blockedHosts: string[]; + + @Column('varchar', { + length: 512, + nullable: true, + default: '/assets/ai.png' + }) + public mascotImageUrl: string | null; + + @Column('varchar', { + length: 512, + nullable: true + }) + public bannerUrl: string | null; + + @Column('varchar', { + length: 512, + nullable: true, + default: 'https://xn--931a.moe/aiart/yubitun.png' + }) + public errorImageUrl: string | null; + + @Column('varchar', { + length: 512, + nullable: true + }) + public iconUrl: string | null; + + @Column('boolean', { + default: true, + }) + public cacheRemoteFiles: boolean; + + @Column('varchar', { + length: 128, + nullable: true + }) + public proxyAccount: string | null; + + @Column('boolean', { + default: false, + }) + public enableRecaptcha: boolean; + + @Column('varchar', { + length: 64, + nullable: true + }) + public recaptchaSiteKey: string | null; + + @Column('varchar', { + length: 64, + nullable: true + }) + public recaptchaSecretKey: string | null; + + @Column('integer', { + default: 1024, + comment: 'Drive capacity of a local user (MB)' + }) + public localDriveCapacityMb: number; + + @Column('integer', { + default: 32, + comment: 'Drive capacity of a remote user (MB)' + }) + public remoteDriveCapacityMb: number; + + @Column('integer', { + default: 500, + comment: 'Max allowed note text length in characters' + }) + public maxNoteTextLength: number; + + @Column('varchar', { + length: 128, + nullable: true + }) + public summalyProxy: string | null; + + @Column('boolean', { + default: false, + }) + public enableEmail: boolean; + + @Column('varchar', { + length: 128, + nullable: true + }) + public email: string | null; + + @Column('boolean', { + default: false, + }) + public smtpSecure: boolean; + + @Column('varchar', { + length: 128, + nullable: true + }) + public smtpHost: string | null; + + @Column('integer', { + nullable: true + }) + public smtpPort: number | null; + + @Column('varchar', { + length: 128, + nullable: true + }) + public smtpUser: string | null; + + @Column('varchar', { + length: 128, + nullable: true + }) + public smtpPass: string | null; + + @Column('boolean', { + default: false, + }) + public enableServiceWorker: boolean; + + @Column('varchar', { + length: 128, + nullable: true + }) + public swPublicKey: string | null; + + @Column('varchar', { + length: 128, + nullable: true + }) + public swPrivateKey: string | null; + + @Column('boolean', { + default: false, + }) + public enableTwitterIntegration: boolean; + + @Column('varchar', { + length: 128, + nullable: true + }) + public twitterConsumerKey: string | null; + + @Column('varchar', { + length: 128, + nullable: true + }) + public twitterConsumerSecret: string | null; + + @Column('boolean', { + default: false, + }) + public enableGithubIntegration: boolean; + + @Column('varchar', { + length: 128, + nullable: true + }) + public githubClientId: string | null; + + @Column('varchar', { + length: 128, + nullable: true + }) + public githubClientSecret: string | null; + + @Column('boolean', { + default: false, + }) + public enableDiscordIntegration: boolean; + + @Column('varchar', { + length: 128, + nullable: true + }) + public discordClientId: string | null; + + @Column('varchar', { + length: 128, + nullable: true + }) + public discordClientSecret: string | null; +} diff --git a/src/models/entities/muting.ts b/src/models/entities/muting.ts new file mode 100644 index 0000000000..0084213bcc --- /dev/null +++ b/src/models/entities/muting.ts @@ -0,0 +1,42 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from './user'; +import { id } from '../id'; + +@Entity() +@Index(['muterId', 'muteeId'], { unique: true }) +export class Muting { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column('timestamp with time zone', { + comment: 'The created date of the Muting.' + }) + public createdAt: Date; + + @Index() + @Column({ + ...id(), + comment: 'The mutee user ID.' + }) + public muteeId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public mutee: User | null; + + @Index() + @Column({ + ...id(), + comment: 'The muter user ID.' + }) + public muterId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public muter: User | null; +} diff --git a/src/models/entities/note-favorite.ts b/src/models/entities/note-favorite.ts new file mode 100644 index 0000000000..0713c3ae56 --- /dev/null +++ b/src/models/entities/note-favorite.ts @@ -0,0 +1,35 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { Note } from './note'; +import { User } from './user'; +import { id } from '../id'; + +@Entity() +@Index(['userId', 'noteId'], { unique: true }) +export class NoteFavorite { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone', { + comment: 'The created date of the NoteFavorite.' + }) + public createdAt: Date; + + @Index() + @Column(id()) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public user: User | null; + + @Column(id()) + public noteId: Note['id']; + + @ManyToOne(type => Note, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public note: Note | null; +} diff --git a/src/models/entities/note-reaction.ts b/src/models/entities/note-reaction.ts new file mode 100644 index 0000000000..1ce5d841fb --- /dev/null +++ b/src/models/entities/note-reaction.ts @@ -0,0 +1,42 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from './user'; +import { Note } from './note'; +import { id } from '../id'; + +@Entity() +@Index(['userId', 'noteId'], { unique: true }) +export class NoteReaction { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column('timestamp with time zone', { + comment: 'The created date of the NoteReaction.' + }) + public createdAt: Date; + + @Index() + @Column(id()) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public user: User | null; + + @Index() + @Column(id()) + public noteId: Note['id']; + + @ManyToOne(type => Note, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public note: Note | null; + + @Column('varchar', { + length: 32 + }) + public reaction: string; +} diff --git a/src/models/entities/note-unread.ts b/src/models/entities/note-unread.ts new file mode 100644 index 0000000000..2d18728256 --- /dev/null +++ b/src/models/entities/note-unread.ts @@ -0,0 +1,43 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from './user'; +import { Note } from './note'; +import { id } from '../id'; + +@Entity() +@Index(['userId', 'noteId'], { unique: true }) +export class NoteUnread { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column(id()) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public user: User | null; + + @Index() + @Column(id()) + public noteId: Note['id']; + + @ManyToOne(type => Note, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public note: Note | null; + + @Column({ + ...id(), + comment: '[Denormalized]' + }) + public noteUserId: User['id']; + + /** + * ダイレクト投稿か + */ + @Column('boolean') + public isSpecified: boolean; +} diff --git a/src/models/entities/note-watching.ts b/src/models/entities/note-watching.ts new file mode 100644 index 0000000000..741a1c0c8b --- /dev/null +++ b/src/models/entities/note-watching.ts @@ -0,0 +1,52 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from './user'; +import { Note } from './note'; +import { id } from '../id'; + +@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 +} diff --git a/src/models/entities/note.ts b/src/models/entities/note.ts new file mode 100644 index 0000000000..969363da3b --- /dev/null +++ b/src/models/entities/note.ts @@ -0,0 +1,231 @@ +import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typeorm'; +import { User } from './user'; +import { App } from './app'; +import { DriveFile } from './drive-file'; +import { id } from '../id'; + +@Entity() +export class Note { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column('timestamp with time zone', { + comment: 'The created date of the Note.' + }) + public createdAt: Date; + + @Index() + @Column({ + ...id(), + nullable: true, + comment: 'The ID of reply target.' + }) + public replyId: Note['id'] | null; + + @ManyToOne(type => Note, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public reply: Note | null; + + @Index() + @Column({ + ...id(), + nullable: true, + comment: 'The ID of renote target.' + }) + public renoteId: Note['id'] | null; + + @ManyToOne(type => Note, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public renote: Note | null; + + @Column({ + type: 'text', nullable: true + }) + public text: string | null; + + @Column('varchar', { + length: 256, nullable: true + }) + public name: string | null; + + @Column('varchar', { + length: 512, nullable: true + }) + public cw: string | null; + + @Column({ + ...id(), + nullable: true + }) + public appId: App['id'] | null; + + @ManyToOne(type => App, { + onDelete: 'SET NULL' + }) + @JoinColumn() + public app: App | null; + + @Index() + @Column({ + ...id(), + comment: 'The ID of author.' + }) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public user: User | null; + + @Column('boolean', { + default: false + }) + public viaMobile: boolean; + + @Column('boolean', { + default: false + }) + public localOnly: boolean; + + @Column('integer', { + default: 0 + }) + public renoteCount: number; + + @Column('integer', { + default: 0 + }) + public repliesCount: number; + + @Column('jsonb', { + default: {} + }) + public reactions: Record<string, number>; + + /** + * public ... 公開 + * home ... ホームタイムライン(ユーザーページのタイムライン含む)のみに流す + * followers ... フォロワーのみ + * specified ... visibleUserIds で指定したユーザーのみ + */ + @Column('enum', { enum: ['public', 'home', 'followers', 'specified'] }) + public visibility: 'public' | 'home' | 'followers' | 'specified'; + + @Index({ unique: true }) + @Column('varchar', { + length: 512, nullable: true, + comment: 'The URI of a note. it will be null when the note is local.' + }) + public uri: string | null; + + @Column('integer', { + default: 0, select: false + }) + public score: number; + + @Column({ + ...id(), + array: true, default: '{}' + }) + public fileIds: DriveFile['id'][]; + + @Column('varchar', { + length: 256, array: true, default: '{}' + }) + public attachedFileTypes: string[]; + + @Index() + @Column({ + ...id(), + array: true, default: '{}' + }) + public visibleUserIds: User['id'][]; + + @Index() + @Column({ + ...id(), + array: true, default: '{}' + }) + public mentions: User['id'][]; + + @Column('text', { + default: '[]' + }) + public mentionedRemoteUsers: string; + + @Column('varchar', { + length: 128, array: true, default: '{}' + }) + public emojis: string[]; + + @Index() + @Column('varchar', { + length: 128, array: true, default: '{}' + }) + public tags: string[]; + + @Column('boolean', { + default: false + }) + public hasPoll: boolean; + + @Column('jsonb', { + nullable: true, default: null + }) + public geo: any | null; + + //#region Denormalized fields + @Index() + @Column('varchar', { + length: 128, nullable: true, + comment: '[Denormalized]' + }) + public userHost: string | null; + + @Column({ + ...id(), + nullable: true, + comment: '[Denormalized]' + }) + public replyUserId: User['id'] | null; + + @Column('varchar', { + length: 128, nullable: true, + comment: '[Denormalized]' + }) + public replyUserHost: string | null; + + @Column({ + ...id(), + nullable: true, + comment: '[Denormalized]' + }) + public renoteUserId: User['id'] | null; + + @Column('varchar', { + length: 128, nullable: true, + comment: '[Denormalized]' + }) + public renoteUserHost: string | null; + //#endregion + + constructor(data: Partial<Note>) { + if (data == null) return; + + for (const [k, v] of Object.entries(data)) { + (this as any)[k] = v; + } + } +} + +export type IMentionedRemoteUsers = { + uri: string; + username: string; + host: string; +}[]; diff --git a/src/models/entities/notification.ts b/src/models/entities/notification.ts new file mode 100644 index 0000000000..627a57bece --- /dev/null +++ b/src/models/entities/notification.ts @@ -0,0 +1,94 @@ +import { Entity, Index, JoinColumn, ManyToOne, Column, PrimaryColumn } from 'typeorm'; +import { User } from './user'; +import { id } from '../id'; +import { Note } from './note'; + +@Entity() +export class Notification { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column('timestamp with time zone', { + comment: 'The created date of the Notification.' + }) + public createdAt: Date; + + /** + * 通知の受信者 + */ + @Index() + @Column({ + ...id(), + comment: 'The ID of recipient user of the Notification.' + }) + public notifieeId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public notifiee: User | null; + + /** + * 通知の送信者(initiator) + */ + @Column({ + ...id(), + comment: 'The ID of sender user of the Notification.' + }) + public notifierId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public notifier: User | null; + + /** + * 通知の種類。 + * follow - フォローされた + * mention - 投稿で自分が言及された + * reply - (自分または自分がWatchしている)投稿が返信された + * renote - (自分または自分がWatchしている)投稿がRenoteされた + * quote - (自分または自分がWatchしている)投稿が引用Renoteされた + * reaction - (自分または自分がWatchしている)投稿にリアクションされた + * pollVote - (自分または自分がWatchしている)投稿の投票に投票された + */ + @Column('varchar', { + length: 32, + comment: 'The type of the Notification.' + }) + public type: string; + + /** + * 通知が読まれたかどうか + */ + @Column('boolean', { + default: false, + comment: 'Whether the Notification is read.' + }) + public isRead: boolean; + + @Column({ + ...id(), + nullable: true + }) + public noteId: Note['id'] | null; + + @ManyToOne(type => Note, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public note: Note | null; + + @Column('varchar', { + length: 128, nullable: true + }) + public reaction: string; + + @Column('integer', { + nullable: true + }) + public choice: number; +} diff --git a/src/models/entities/poll-vote.ts b/src/models/entities/poll-vote.ts new file mode 100644 index 0000000000..709376f909 --- /dev/null +++ b/src/models/entities/poll-vote.ts @@ -0,0 +1,40 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from './user'; +import { Note } from './note'; +import { id } from '../id'; + +@Entity() +@Index(['userId', 'noteId', 'choice'], { unique: true }) +export class PollVote { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column('timestamp with time zone', { + comment: 'The created date of the PollVote.' + }) + public createdAt: Date; + + @Index() + @Column(id()) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public user: User | null; + + @Index() + @Column(id()) + public noteId: Note['id']; + + @ManyToOne(type => Note, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public note: Note | null; + + @Column('integer') + public choice: number; +} diff --git a/src/models/entities/poll.ts b/src/models/entities/poll.ts new file mode 100644 index 0000000000..6bb67163a2 --- /dev/null +++ b/src/models/entities/poll.ts @@ -0,0 +1,71 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, OneToOne } from 'typeorm'; +import { id } from '../id'; +import { Note } from './note'; +import { User } from './user'; + +@Entity() +export class Poll { + @PrimaryColumn(id()) + public noteId: Note['id']; + + @OneToOne(type => Note, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public note: Note | null; + + @Column('timestamp with time zone', { + nullable: true + }) + public expiresAt: Date | null; + + @Column('boolean') + public multiple: boolean; + + @Column('varchar', { + length: 128, array: true, default: '{}' + }) + public choices: string[]; + + @Column('integer', { + array: true, + }) + public votes: number[]; + + //#region Denormalized fields + @Column('enum', { + enum: ['public', 'home', 'followers', 'specified'], + comment: '[Denormalized]' + }) + public noteVisibility: 'public' | 'home' | 'followers' | 'specified'; + + @Index() + @Column({ + ...id(), + comment: '[Denormalized]' + }) + public userId: User['id']; + + @Index() + @Column('varchar', { + length: 128, nullable: true, + comment: '[Denormalized]' + }) + public userHost: string | null; + //#endregion + + constructor(data: Partial<Poll>) { + if (data == null) return; + + for (const [k, v] of Object.entries(data)) { + (this as any)[k] = v; + } + } +} + +export type IPoll = { + choices: string[]; + votes?: number[]; + multiple: boolean; + expiresAt: Date | null; +}; diff --git a/src/models/entities/registration-tickets.ts b/src/models/entities/registration-tickets.ts new file mode 100644 index 0000000000..d962f78a78 --- /dev/null +++ b/src/models/entities/registration-tickets.ts @@ -0,0 +1,17 @@ +import { PrimaryColumn, Entity, Index, Column } from 'typeorm'; +import { id } from '../id'; + +@Entity() +export class RegistrationTicket { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone') + public createdAt: Date; + + @Index({ unique: true }) + @Column('varchar', { + length: 64, + }) + public code: string; +} diff --git a/src/models/entities/signin.ts b/src/models/entities/signin.ts new file mode 100644 index 0000000000..7e047084b1 --- /dev/null +++ b/src/models/entities/signin.ts @@ -0,0 +1,35 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from './user'; +import { id } from '../id'; + +@Entity() +export class Signin { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone', { + comment: 'The created date of the Signin.' + }) + public createdAt: Date; + + @Index() + @Column(id()) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public user: User | null; + + @Column('varchar', { + length: 128, + }) + public ip: string; + + @Column('jsonb') + public headers: Record<string, any>; + + @Column('boolean') + public success: boolean; +} diff --git a/src/models/entities/sw-subscription.ts b/src/models/entities/sw-subscription.ts new file mode 100644 index 0000000000..7c3f6f0a6c --- /dev/null +++ b/src/models/entities/sw-subscription.ts @@ -0,0 +1,37 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from './user'; +import { id } from '../id'; + +@Entity() +export class SwSubscription { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone') + public createdAt: Date; + + @Index() + @Column(id()) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public user: User | null; + + @Column('varchar', { + length: 512, + }) + public endpoint: string; + + @Column('varchar', { + length: 256, + }) + public auth: string; + + @Column('varchar', { + length: 128, + }) + public publickey: string; +} diff --git a/src/models/entities/user-keypair.ts b/src/models/entities/user-keypair.ts new file mode 100644 index 0000000000..603321d758 --- /dev/null +++ b/src/models/entities/user-keypair.ts @@ -0,0 +1,33 @@ +import { PrimaryColumn, Entity, JoinColumn, Column, OneToOne } from 'typeorm'; +import { User } from './user'; +import { id } from '../id'; + +@Entity() +export class UserKeypair { + @PrimaryColumn(id()) + public userId: User['id']; + + @OneToOne(type => User, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public user: User | null; + + @Column('varchar', { + length: 4096, + }) + public publicKey: string; + + @Column('varchar', { + length: 4096, + }) + public privateKey: string; + + constructor(data: Partial<UserKeypair>) { + if (data == null) return; + + for (const [k, v] of Object.entries(data)) { + (this as any)[k] = v; + } + } +} diff --git a/src/models/entities/user-list-joining.ts b/src/models/entities/user-list-joining.ts new file mode 100644 index 0000000000..8af4efb6a7 --- /dev/null +++ b/src/models/entities/user-list-joining.ts @@ -0,0 +1,41 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from './user'; +import { UserList } from './user-list'; +import { id } from '../id'; + +@Entity() +export class UserListJoining { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone', { + comment: 'The created date of the UserListJoining.' + }) + public createdAt: Date; + + @Index() + @Column({ + ...id(), + comment: 'The user ID.' + }) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public user: User | null; + + @Index() + @Column({ + ...id(), + comment: 'The list ID.' + }) + public userListId: UserList['id']; + + @ManyToOne(type => UserList, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public userList: UserList | null; +} diff --git a/src/models/entities/user-list.ts b/src/models/entities/user-list.ts new file mode 100644 index 0000000000..35a83ef8c3 --- /dev/null +++ b/src/models/entities/user-list.ts @@ -0,0 +1,33 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from './user'; +import { id } from '../id'; + +@Entity() +export class UserList { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone', { + comment: 'The created date of the UserList.' + }) + public createdAt: Date; + + @Index() + @Column({ + ...id(), + comment: 'The owner ID.' + }) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public user: User | null; + + @Column('varchar', { + length: 128, + comment: 'The name of the UserList.' + }) + public name: string; +} diff --git a/src/models/entities/user-note-pinings.ts b/src/models/entities/user-note-pinings.ts new file mode 100644 index 0000000000..04a6f8f645 --- /dev/null +++ b/src/models/entities/user-note-pinings.ts @@ -0,0 +1,35 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { Note } from './note'; +import { User } from './user'; +import { id } from '../id'; + +@Entity() +@Index(['userId', 'noteId'], { unique: true }) +export class UserNotePining { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone', { + comment: 'The created date of the UserNotePinings.' + }) + public createdAt: Date; + + @Index() + @Column(id()) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public user: User | null; + + @Column(id()) + public noteId: Note['id']; + + @ManyToOne(type => Note, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public note: Note | null; +} diff --git a/src/models/entities/user-profile.ts b/src/models/entities/user-profile.ts new file mode 100644 index 0000000000..a2d7b8d2c2 --- /dev/null +++ b/src/models/entities/user-profile.ts @@ -0,0 +1,209 @@ +import { Entity, Column, Index, OneToOne, JoinColumn, PrimaryColumn } from 'typeorm'; +import { id } from '../id'; +import { User } from './user'; + +@Entity() +export class UserProfile { + @PrimaryColumn(id()) + public userId: User['id']; + + @OneToOne(type => User, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public user: User | null; + + @Column('varchar', { + length: 128, nullable: true, + comment: 'The location of the User.' + }) + public location: string | null; + + @Column('char', { + length: 10, nullable: true, + comment: 'The birthday (YYYY-MM-DD) of the User.' + }) + public birthday: string | null; + + @Column('varchar', { + length: 1024, nullable: true, + comment: 'The description (bio) of the User.' + }) + public description: string | null; + + @Column('jsonb', { + default: [], + }) + public fields: { + name: string; + value: string; + }[]; + + @Column('varchar', { + length: 512, nullable: true, + comment: 'Remote URL of the user.' + }) + public url: string | null; + + @Column('varchar', { + length: 128, nullable: true, + comment: 'The email address of the User.' + }) + public email: string | null; + + @Column('varchar', { + length: 128, nullable: true, + }) + public emailVerifyCode: string | null; + + @Column('boolean', { + default: false, + }) + public emailVerified: boolean; + + @Column('varchar', { + length: 128, nullable: true, + }) + public twoFactorTempSecret: string | null; + + @Column('varchar', { + length: 128, nullable: true, + }) + public twoFactorSecret: string | null; + + @Column('boolean', { + default: false, + }) + public twoFactorEnabled: boolean; + + @Column('varchar', { + length: 128, nullable: true, + comment: 'The password hash of the User. It will be null if the origin of the user is local.' + }) + public password: string | null; + + @Column('jsonb', { + default: {}, + comment: 'The client-specific data of the User.' + }) + public clientData: Record<string, any>; + + @Column('boolean', { + default: false, + }) + public autoWatch: boolean; + + @Column('boolean', { + default: false, + }) + public autoAcceptFollowed: boolean; + + @Column('boolean', { + default: false, + }) + public alwaysMarkNsfw: boolean; + + @Column('boolean', { + default: false, + }) + public carefulBot: boolean; + + //#region Linking + @Column('boolean', { + default: false, + }) + public twitter: boolean; + + @Column('varchar', { + length: 64, nullable: true, default: null, + }) + public twitterAccessToken: string | null; + + @Column('varchar', { + length: 64, nullable: true, default: null, + }) + public twitterAccessTokenSecret: string | null; + + @Column('varchar', { + length: 64, nullable: true, default: null, + }) + public twitterUserId: string | null; + + @Column('varchar', { + length: 64, nullable: true, default: null, + }) + public twitterScreenName: string | null; + + @Column('boolean', { + default: false, + }) + public github: boolean; + + @Column('varchar', { + length: 64, nullable: true, default: null, + }) + public githubAccessToken: string | null; + + @Column('integer', { + nullable: true, default: null, + }) + public githubId: number | null; + + @Column('varchar', { + length: 64, nullable: true, default: null, + }) + public githubLogin: string | null; + + @Column('boolean', { + default: false, + }) + public discord: boolean; + + @Column('varchar', { + length: 64, nullable: true, default: null, + }) + public discordAccessToken: string | null; + + @Column('varchar', { + length: 64, nullable: true, default: null, + }) + public discordRefreshToken: string | null; + + @Column('integer', { + nullable: true, default: null, + }) + public discordExpiresDate: number | null; + + @Column('varchar', { + length: 64, nullable: true, default: null, + }) + public discordId: string | null; + + @Column('varchar', { + length: 64, nullable: true, default: null, + }) + public discordUsername: string | null; + + @Column('varchar', { + length: 64, nullable: true, default: null, + }) + public discordDiscriminator: string | null; + //#endregion + + //#region Denormalized fields + @Index() + @Column('varchar', { + length: 128, nullable: true, + comment: '[Denormalized]' + }) + public userHost: string | null; + //#endregion + + constructor(data: Partial<UserProfile>) { + if (data == null) return; + + for (const [k, v] of Object.entries(data)) { + (this as any)[k] = v; + } + } +} diff --git a/src/models/entities/user-publickey.ts b/src/models/entities/user-publickey.ts new file mode 100644 index 0000000000..21edc3e9e2 --- /dev/null +++ b/src/models/entities/user-publickey.ts @@ -0,0 +1,34 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, OneToOne } from 'typeorm'; +import { User } from './user'; +import { id } from '../id'; + +@Entity() +export class UserPublickey { + @PrimaryColumn(id()) + public userId: User['id']; + + @OneToOne(type => User, { + onDelete: 'CASCADE' + }) + @JoinColumn() + public user: User | null; + + @Index({ unique: true }) + @Column('varchar', { + length: 256, + }) + public keyId: string; + + @Column('varchar', { + length: 4096, + }) + public keyPem: string; + + constructor(data: Partial<UserPublickey>) { + if (data == null) return; + + for (const [k, v] of Object.entries(data)) { + (this as any)[k] = v; + } + } +} diff --git a/src/models/entities/user.ts b/src/models/entities/user.ts new file mode 100644 index 0000000000..e40c32a76f --- /dev/null +++ b/src/models/entities/user.ts @@ -0,0 +1,224 @@ +import { Entity, Column, Index, OneToOne, JoinColumn, PrimaryColumn } from 'typeorm'; +import { DriveFile } from './drive-file'; +import { id } from '../id'; + +@Entity() +@Index(['usernameLower', 'host'], { unique: true }) +export class User { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column('timestamp with time zone', { + comment: 'The created date of the User.' + }) + public createdAt: Date; + + @Index() + @Column('timestamp with time zone', { + nullable: true, + comment: 'The updated date of the User.' + }) + public updatedAt: Date | null; + + @Column('timestamp with time zone', { + nullable: true + }) + public lastFetchedAt: Date | null; + + @Column('varchar', { + length: 128, + comment: 'The username of the User.' + }) + public username: string; + + @Index() + @Column('varchar', { + length: 128, select: false, + comment: 'The username (lowercased) of the User.' + }) + public usernameLower: string; + + @Column('varchar', { + length: 128, nullable: true, + comment: 'The name of the User.' + }) + public name: string | null; + + @Column('integer', { + default: 0, + comment: 'The count of followers.' + }) + public followersCount: number; + + @Column('integer', { + default: 0, + comment: 'The count of following.' + }) + public followingCount: number; + + @Column('integer', { + default: 0, + comment: 'The count of notes.' + }) + public notesCount: number; + + @Column({ + ...id(), + nullable: true, + comment: 'The ID of avatar DriveFile.' + }) + public avatarId: DriveFile['id'] | null; + + @OneToOne(type => DriveFile, { + onDelete: 'SET NULL' + }) + @JoinColumn() + public avatar: DriveFile | null; + + @Column({ + ...id(), + nullable: true, + comment: 'The ID of banner DriveFile.' + }) + public bannerId: DriveFile['id'] | null; + + @OneToOne(type => DriveFile, { + onDelete: 'SET NULL' + }) + @JoinColumn() + public banner: DriveFile | null; + + @Index() + @Column('varchar', { + length: 128, array: true, default: '{}' + }) + public tags: string[]; + + @Column('varchar', { + length: 512, nullable: true, + }) + public avatarUrl: string | null; + + @Column('varchar', { + length: 512, nullable: true, + }) + public bannerUrl: string | null; + + @Column('varchar', { + length: 32, nullable: true, + }) + public avatarColor: string | null; + + @Column('varchar', { + length: 32, nullable: true, + }) + public bannerColor: string | null; + + @Column('boolean', { + default: false, + comment: 'Whether the User is suspended.' + }) + public isSuspended: boolean; + + @Column('boolean', { + default: false, + comment: 'Whether the User is silenced.' + }) + public isSilenced: boolean; + + @Column('boolean', { + default: false, + comment: 'Whether the User is locked.' + }) + public isLocked: boolean; + + @Column('boolean', { + default: false, + comment: 'Whether the User is a bot.' + }) + public isBot: boolean; + + @Column('boolean', { + default: false, + comment: 'Whether the User is a cat.' + }) + public isCat: boolean; + + @Column('boolean', { + default: false, + comment: 'Whether the User is the admin.' + }) + public isAdmin: boolean; + + @Column('boolean', { + default: false, + comment: 'Whether the User is a moderator.' + }) + public isModerator: boolean; + + @Column('boolean', { + default: false, + }) + public isVerified: boolean; + + @Column('varchar', { + length: 128, array: true, default: '{}' + }) + public emojis: string[]; + + @Index() + @Column('varchar', { + length: 128, nullable: true, + comment: 'The host of the User. It will be null if the origin of the user is local.' + }) + public host: string | null; + + @Column('varchar', { + length: 512, nullable: true, + comment: 'The inbox URL of the User. It will be null if the origin of the user is local.' + }) + public inbox: string | null; + + @Column('varchar', { + length: 512, nullable: true, + comment: 'The sharedInbox URL of the User. It will be null if the origin of the user is local.' + }) + public sharedInbox: string | null; + + @Column('varchar', { + length: 512, nullable: true, + comment: 'The featured URL of the User. It will be null if the origin of the user is local.' + }) + public featured: string | null; + + @Index() + @Column('varchar', { + length: 512, nullable: true, + comment: 'The URI of the User. It will be null if the origin of the user is local.' + }) + public uri: string | null; + + @Index({ unique: true }) + @Column('char', { + length: 16, nullable: true, unique: true, + comment: 'The native access token of the User. It will be null if the origin of the user is local.' + }) + public token: string | null; + + constructor(data: Partial<User>) { + if (data == null) return; + + for (const [k, v] of Object.entries(data)) { + (this as any)[k] = v; + } + } +} + +export interface ILocalUser extends User { + host: null; +} + +export interface IRemoteUser extends User { + host: string; +} |