Use PostgreSQL instead of MongoDB (#4572)
* wip * Update note.ts * Update timeline.ts * Update core.ts * wip * Update generate-visibility-query.ts * wip * wip * wip * wip * wip * Update global-timeline.ts * wip * wip * wip * Update vote.ts * wip * wip * Update create.ts * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * Update files.ts * wip * wip * Update CONTRIBUTING.md * wip * wip * wip * wip * wip * wip * wip * wip * Update read-notification.ts * wip * wip * wip * wip * wip * wip * wip * Update cancel.ts * wip * wip * wip * Update show.ts * wip * wip * Update gen-id.ts * Update create.ts * Update id.ts * wip * wip * wip * wip * wip * wip * wip * Docker: Update files about Docker (#4599) * Docker: Use cache if files used by `yarn install` was not updated This patch reduces the number of times to installing node_modules. For example, `yarn install` step will be skipped when only ".config/default.yml" is updated. * Docker: Migrate MongoDB to Postgresql Misskey uses Postgresql as a database instead of Mongodb since version 11. * Docker: Uncomment about data persistence This patch will save a lot of databases. * wip * wip * wip * Update activitypub.ts * wip * wip * wip * Update logs.ts * wip * Update drive-file.ts * Update register.ts * wip * wip * Update mentions.ts * wip * wip * wip * Update recommendation.ts * wip * Update index.ts * wip * Update recommendation.ts * Doc: Update docker.ja.md and docker.en.md (#1) (#4608) Update how to set up misskey. * wip * ✌️ * wip * Update note.ts * Update postgre.ts * wip * wip * wip * wip * Update add-file.ts * wip * wip * wip * Clean up * Update logs.ts * wip * 🍕 * wip * Ad notes * wip * Update api-visibility.ts * Update note.ts * Update add-file.ts * tests * tests * Update postgre.ts * Update utils.ts * wip * wip * Refactor * wip * Refactor * wip * wip * Update show-users.ts * Update update-instance.ts * wip * Update feed.ts * Update outbox.ts * Update outbox.ts * Update user.ts * wip * Update list.ts * Update update-hashtag.ts * wip * Update update-hashtag.ts * Refactor * Update update.ts * wip * wip * ✌️ * clean up * docs * Update push.ts * wip * Update api.ts * wip * ✌️ * Update make-pagination-query.ts * ✌️ * Delete hashtags.ts * Update instances.ts * Update instances.ts * Update create.ts * Update search.ts * Update reversi-game.ts * Update signup.ts * Update user.ts * id * Update example.yml * 🎨 * objectid * fix * reversi * reversi * Fix bug of chart engine * Add test of chart engine * Improve test * Better testing * Improve chart engine * Refactor * Add test of chart engine * Refactor * Add chart test * Fix bug * コミットし忘れ * Refactoring * ✌️ * Add tests * Add test * Extarct note tests * Refactor * 存在しないユーザーにメンションできなくなっていた問題を修正 * Fix bug * Update update-meta.ts * Fix bug * Update mention.vue * Fix bug * Update meta.ts * Update CONTRIBUTING.md * Fix bug * Fix bug * Fix bug * Clean up * Clean up * Update notification.ts * Clean up * Add mute tests * Add test * Refactor * Add test * Fix test * Refactor * Refactor * Add tests * Update utils.ts * Update utils.ts * Fix test * Update package.json * Update update.ts * Update manifest.ts * Fix bug * Fix bug * Add test * 🎨 * Update endpoint permissions * Updaye permisison * Update person.ts #4299 * データベースと同期しないように * Fix bug * Fix bug * Update reversi-game.ts * Use a feature of Node v11.7.0 to extract a public key (#4644) * wip * wip * ✌️ * Refactoring #1540 * test * test * test * test * test * test * test * Fix bug * Fix test * 🍣 * wip * #4471 * Add test for #4335 * Refactor * Fix test * Add tests * 🕓 * Fix bug * Add test * Add test * rename * Fix bug
This commit is contained in:
parent
13caf37991
commit
f0a29721c9
592 changed files with 13463 additions and 14147 deletions
32
src/models/repositories/abuse-user-report.ts
Normal file
32
src/models/repositories/abuse-user-report.ts
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import { EntityRepository, Repository } from 'typeorm';
|
||||
import { Users } from '..';
|
||||
import rap from '@prezzemolo/rap';
|
||||
import { AbuseUserReport } from '../entities/abuse-user-report';
|
||||
|
||||
@EntityRepository(AbuseUserReport)
|
||||
export class AbuseUserReportRepository extends Repository<AbuseUserReport> {
|
||||
public packMany(
|
||||
reports: any[],
|
||||
) {
|
||||
return Promise.all(reports.map(x => this.pack(x)));
|
||||
}
|
||||
|
||||
public async pack(
|
||||
src: AbuseUserReport['id'] | AbuseUserReport,
|
||||
) {
|
||||
const report = typeof src === 'object' ? src : await this.findOne(src);
|
||||
|
||||
return await rap({
|
||||
id: report.id,
|
||||
createdAt: report.createdAt,
|
||||
reporterId: report.reporterId,
|
||||
userId: report.userId,
|
||||
reporter: Users.pack(report.reporter || report.reporterId, null, {
|
||||
detail: true
|
||||
}),
|
||||
user: Users.pack(report.user || report.userId, null, {
|
||||
detail: true
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
36
src/models/repositories/app.ts
Normal file
36
src/models/repositories/app.ts
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import { EntityRepository, Repository } from 'typeorm';
|
||||
import { App } from '../entities/app';
|
||||
import { AccessTokens } from '..';
|
||||
|
||||
@EntityRepository(App)
|
||||
export class AppRepository extends Repository<App> {
|
||||
public async pack(
|
||||
src: App['id'] | App,
|
||||
me?: any,
|
||||
options?: {
|
||||
detail?: boolean,
|
||||
includeSecret?: boolean,
|
||||
includeProfileImageIds?: boolean
|
||||
}
|
||||
) {
|
||||
const opts = Object.assign({
|
||||
detail: false,
|
||||
includeSecret: false,
|
||||
includeProfileImageIds: false
|
||||
}, options);
|
||||
|
||||
const app = typeof src === 'object' ? src : await this.findOne(src);
|
||||
|
||||
return {
|
||||
id: app.id,
|
||||
name: app.name,
|
||||
...(opts.includeSecret ? { secret: app.secret } : {}),
|
||||
...(me ? {
|
||||
isAuthorized: await AccessTokens.count({
|
||||
appId: app.id,
|
||||
userId: me,
|
||||
}).then(count => count > 0)
|
||||
} : {})
|
||||
};
|
||||
}
|
||||
}
|
||||
19
src/models/repositories/auth-session.ts
Normal file
19
src/models/repositories/auth-session.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import { EntityRepository, Repository } from 'typeorm';
|
||||
import { Apps } from '..';
|
||||
import rap from '@prezzemolo/rap';
|
||||
import { AuthSession } from '../entities/auth-session';
|
||||
|
||||
@EntityRepository(AuthSession)
|
||||
export class AuthSessionRepository extends Repository<AuthSession> {
|
||||
public async pack(
|
||||
src: AuthSession['id'] | AuthSession,
|
||||
me?: any
|
||||
) {
|
||||
const session = typeof src === 'object' ? src : await this.findOne(src);
|
||||
|
||||
return await rap({
|
||||
id: session.id,
|
||||
app: Apps.pack(session.appId, me)
|
||||
});
|
||||
}
|
||||
}
|
||||
28
src/models/repositories/blocking.ts
Normal file
28
src/models/repositories/blocking.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import { EntityRepository, Repository } from 'typeorm';
|
||||
import { Users } from '..';
|
||||
import rap from '@prezzemolo/rap';
|
||||
import { Blocking } from '../entities/blocking';
|
||||
|
||||
@EntityRepository(Blocking)
|
||||
export class BlockingRepository extends Repository<Blocking> {
|
||||
public packMany(
|
||||
blockings: any[],
|
||||
me: any
|
||||
) {
|
||||
return Promise.all(blockings.map(x => this.pack(x, me)));
|
||||
}
|
||||
|
||||
public async pack(
|
||||
src: Blocking['id'] | Blocking,
|
||||
me?: any
|
||||
) {
|
||||
const blocking = typeof src === 'object' ? src : await this.findOne(src);
|
||||
|
||||
return await rap({
|
||||
id: blocking.id,
|
||||
blockee: Users.pack(blocking.blockeeId, me, {
|
||||
detail: true
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
113
src/models/repositories/drive-file.ts
Normal file
113
src/models/repositories/drive-file.ts
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
import { EntityRepository, Repository } from 'typeorm';
|
||||
import { DriveFile } from '../entities/drive-file';
|
||||
import { Users, DriveFolders } from '..';
|
||||
import rap from '@prezzemolo/rap';
|
||||
import { User } from '../entities/user';
|
||||
|
||||
@EntityRepository(DriveFile)
|
||||
export class DriveFileRepository extends Repository<DriveFile> {
|
||||
public validateFileName(name: string): boolean {
|
||||
return (
|
||||
(name.trim().length > 0) &&
|
||||
(name.length <= 200) &&
|
||||
(name.indexOf('\\') === -1) &&
|
||||
(name.indexOf('/') === -1) &&
|
||||
(name.indexOf('..') === -1)
|
||||
);
|
||||
}
|
||||
|
||||
public getPublicUrl(file: DriveFile, thumbnail = false): string {
|
||||
if (thumbnail) {
|
||||
return file.thumbnailUrl || file.webpublicUrl || file.url;
|
||||
} else {
|
||||
return file.webpublicUrl || file.thumbnailUrl || file.url;
|
||||
}
|
||||
}
|
||||
|
||||
public async clacDriveUsageOf(user: User['id'] | User): Promise<number> {
|
||||
const id = typeof user === 'object' ? user.id : user;
|
||||
|
||||
const { sum } = await this
|
||||
.createQueryBuilder('file')
|
||||
.where('file.userId = :id', { id: id })
|
||||
.select('SUM(file.size)', 'sum')
|
||||
.getRawOne();
|
||||
|
||||
return parseInt(sum, 10) || 0;
|
||||
}
|
||||
|
||||
public async clacDriveUsageOfHost(host: string): Promise<number> {
|
||||
const { sum } = await this
|
||||
.createQueryBuilder('file')
|
||||
.where('file.userHost = :host', { host: host })
|
||||
.select('SUM(file.size)', 'sum')
|
||||
.getRawOne();
|
||||
|
||||
return parseInt(sum, 10) || 0;
|
||||
}
|
||||
|
||||
public async clacDriveUsageOfLocal(): Promise<number> {
|
||||
const { sum } = await this
|
||||
.createQueryBuilder('file')
|
||||
.where('file.userHost IS NULL')
|
||||
.select('SUM(file.size)', 'sum')
|
||||
.getRawOne();
|
||||
|
||||
return parseInt(sum, 10) || 0;
|
||||
}
|
||||
|
||||
public async clacDriveUsageOfRemote(): Promise<number> {
|
||||
const { sum } = await this
|
||||
.createQueryBuilder('file')
|
||||
.where('file.userHost IS NOT NULL')
|
||||
.select('SUM(file.size)', 'sum')
|
||||
.getRawOne();
|
||||
|
||||
return parseInt(sum, 10) || 0;
|
||||
}
|
||||
|
||||
public packMany(
|
||||
files: any[],
|
||||
options?: {
|
||||
detail?: boolean
|
||||
self?: boolean,
|
||||
withUser?: boolean,
|
||||
}
|
||||
) {
|
||||
return Promise.all(files.map(f => this.pack(f, options)));
|
||||
}
|
||||
|
||||
public async pack(
|
||||
src: DriveFile['id'] | DriveFile,
|
||||
options?: {
|
||||
detail?: boolean,
|
||||
self?: boolean,
|
||||
withUser?: boolean,
|
||||
}
|
||||
) {
|
||||
const opts = Object.assign({
|
||||
detail: false,
|
||||
self: false
|
||||
}, options);
|
||||
|
||||
const file = typeof src === 'object' ? src : await this.findOne(src);
|
||||
|
||||
return await rap({
|
||||
id: file.id,
|
||||
createdAt: file.createdAt,
|
||||
name: file.name,
|
||||
type: file.type,
|
||||
md5: file.md5,
|
||||
size: file.size,
|
||||
isSensitive: file.isSensitive,
|
||||
properties: file.properties,
|
||||
url: opts.self ? file.url : this.getPublicUrl(file, false),
|
||||
thumbnailUrl: this.getPublicUrl(file, true),
|
||||
folderId: file.folderId,
|
||||
folder: opts.detail && file.folderId ? DriveFolders.pack(file.folderId, {
|
||||
detail: true
|
||||
}) : null,
|
||||
user: opts.withUser ? Users.pack(file.userId) : null
|
||||
});
|
||||
}
|
||||
}
|
||||
49
src/models/repositories/drive-folder.ts
Normal file
49
src/models/repositories/drive-folder.ts
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import { EntityRepository, Repository } from 'typeorm';
|
||||
import { DriveFolders, DriveFiles } from '..';
|
||||
import rap from '@prezzemolo/rap';
|
||||
import { DriveFolder } from '../entities/drive-folder';
|
||||
|
||||
@EntityRepository(DriveFolder)
|
||||
export class DriveFolderRepository extends Repository<DriveFolder> {
|
||||
public validateFolderName(name: string): boolean {
|
||||
return (
|
||||
(name.trim().length > 0) &&
|
||||
(name.length <= 200)
|
||||
);
|
||||
}
|
||||
|
||||
public async pack(
|
||||
src: DriveFolder['id'] | DriveFolder,
|
||||
options?: {
|
||||
detail: boolean
|
||||
}
|
||||
): Promise<Record<string, any>> {
|
||||
const opts = Object.assign({
|
||||
detail: false
|
||||
}, options);
|
||||
|
||||
const folder = typeof src === 'object' ? src : await this.findOne(src);
|
||||
|
||||
return await rap({
|
||||
id: folder.id,
|
||||
createdAt: folder.createdAt,
|
||||
name: folder.name,
|
||||
parentId: folder.parentId,
|
||||
|
||||
...(opts.detail ? {
|
||||
foldersCount: DriveFolders.count({
|
||||
parentId: folder.id
|
||||
}),
|
||||
filesCount: DriveFiles.count({
|
||||
folderId: folder.id
|
||||
}),
|
||||
|
||||
...(folder.parentId ? {
|
||||
parent: this.pack(folder.parentId, {
|
||||
detail: true
|
||||
})
|
||||
} : {})
|
||||
} : {})
|
||||
});
|
||||
}
|
||||
}
|
||||
19
src/models/repositories/follow-request.ts
Normal file
19
src/models/repositories/follow-request.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import { EntityRepository, Repository } from 'typeorm';
|
||||
import { FollowRequest } from '../entities/follow-request';
|
||||
import { Users } from '..';
|
||||
|
||||
@EntityRepository(FollowRequest)
|
||||
export class FollowRequestRepository extends Repository<FollowRequest> {
|
||||
public async pack(
|
||||
src: FollowRequest['id'] | FollowRequest,
|
||||
me?: any
|
||||
) {
|
||||
const request = typeof src === 'object' ? src : await this.findOne(src);
|
||||
|
||||
return {
|
||||
id: request.id,
|
||||
follower: await Users.pack(request.followerId, me),
|
||||
followee: await Users.pack(request.followeeId, me),
|
||||
};
|
||||
}
|
||||
}
|
||||
44
src/models/repositories/following.ts
Normal file
44
src/models/repositories/following.ts
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import { EntityRepository, Repository } from 'typeorm';
|
||||
import { Users } from '..';
|
||||
import rap from '@prezzemolo/rap';
|
||||
import { Following } from '../entities/following';
|
||||
|
||||
@EntityRepository(Following)
|
||||
export class FollowingRepository extends Repository<Following> {
|
||||
public packMany(
|
||||
followings: any[],
|
||||
me?: any,
|
||||
opts?: {
|
||||
populateFollowee?: boolean;
|
||||
populateFollower?: boolean;
|
||||
}
|
||||
) {
|
||||
return Promise.all(followings.map(x => this.pack(x, me, opts)));
|
||||
}
|
||||
|
||||
public async pack(
|
||||
src: Following['id'] | Following,
|
||||
me?: any,
|
||||
opts?: {
|
||||
populateFollowee?: boolean;
|
||||
populateFollower?: boolean;
|
||||
}
|
||||
) {
|
||||
const following = typeof src === 'object' ? src : await this.findOne(src);
|
||||
|
||||
if (opts == null) opts = {};
|
||||
|
||||
return await rap({
|
||||
id: following.id,
|
||||
createdAt: following.createdAt,
|
||||
followeeId: following.followeeId,
|
||||
followerId: following.followerId,
|
||||
followee: opts.populateFollowee ? Users.pack(following.followee || following.followeeId, me, {
|
||||
detail: true
|
||||
}) : null,
|
||||
follower: opts.populateFollower ? Users.pack(following.follower || following.followerId, me, {
|
||||
detail: true
|
||||
}) : null,
|
||||
});
|
||||
}
|
||||
}
|
||||
49
src/models/repositories/games/reversi/game.ts
Normal file
49
src/models/repositories/games/reversi/game.ts
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import { EntityRepository, Repository } from 'typeorm';
|
||||
import { Users } from '../../..';
|
||||
import { ReversiGame } from '../../../entities/games/reversi/game';
|
||||
|
||||
@EntityRepository(ReversiGame)
|
||||
export class ReversiGameRepository extends Repository<ReversiGame> {
|
||||
public async pack(
|
||||
src: ReversiGame['id'] | ReversiGame,
|
||||
me?: any,
|
||||
options?: {
|
||||
detail?: boolean
|
||||
}
|
||||
) {
|
||||
const opts = Object.assign({
|
||||
detail: true
|
||||
}, options);
|
||||
|
||||
const game = typeof src === 'object' ? src : await this.findOne(src);
|
||||
const meId = me ? typeof me === 'string' ? me : me.id : null;
|
||||
|
||||
return {
|
||||
id: game.id,
|
||||
createdAt: game.createdAt,
|
||||
startedAt: game.startedAt,
|
||||
isStarted: game.isStarted,
|
||||
isEnded: game.isEnded,
|
||||
form1: game.form1,
|
||||
form2: game.form2,
|
||||
user1Accepted: game.user1Accepted,
|
||||
user2Accepted: game.user2Accepted,
|
||||
user1Id: game.user1Id,
|
||||
user2Id: game.user2Id,
|
||||
user1: await Users.pack(game.user1Id, meId),
|
||||
user2: await Users.pack(game.user2Id, meId),
|
||||
winnerId: game.winnerId,
|
||||
winner: game.winnerId ? await Users.pack(game.winnerId, meId) : null,
|
||||
surrendered: game.surrendered,
|
||||
black: game.black,
|
||||
bw: game.bw,
|
||||
isLlotheo: game.isLlotheo,
|
||||
canPutEverywhere: game.canPutEverywhere,
|
||||
loopedBoard: game.loopedBoard,
|
||||
...(opts.detail ? {
|
||||
logs: game.logs,
|
||||
map: game.map,
|
||||
} : {})
|
||||
};
|
||||
}
|
||||
}
|
||||
27
src/models/repositories/games/reversi/matching.ts
Normal file
27
src/models/repositories/games/reversi/matching.ts
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import { EntityRepository, Repository } from 'typeorm';
|
||||
import rap from '@prezzemolo/rap';
|
||||
import { ReversiMatching } from '../../../entities/games/reversi/matching';
|
||||
import { Users } from '../../..';
|
||||
|
||||
@EntityRepository(ReversiMatching)
|
||||
export class ReversiMatchingRepository extends Repository<ReversiMatching> {
|
||||
public async pack(
|
||||
src: ReversiMatching['id'] | ReversiMatching,
|
||||
me: any
|
||||
) {
|
||||
const matching = typeof src === 'object' ? src : await this.findOne(src);
|
||||
|
||||
return await rap({
|
||||
id: matching.id,
|
||||
createdAt: matching.createdAt,
|
||||
parentId: matching.parentId,
|
||||
parent: Users.pack(matching.parentId, me, {
|
||||
detail: true
|
||||
}),
|
||||
childId: matching.childId,
|
||||
child: Users.pack(matching.childId, me, {
|
||||
detail: true
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
37
src/models/repositories/messaging-message.ts
Normal file
37
src/models/repositories/messaging-message.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import { EntityRepository, Repository } from 'typeorm';
|
||||
import { MessagingMessage } from '../entities/messaging-message';
|
||||
import { Users, DriveFiles } from '..';
|
||||
|
||||
@EntityRepository(MessagingMessage)
|
||||
export class MessagingMessageRepository extends Repository<MessagingMessage> {
|
||||
public isValidText(text: string): boolean {
|
||||
return text.trim().length <= 1000 && text.trim() != '';
|
||||
}
|
||||
|
||||
public async pack(
|
||||
src: MessagingMessage['id'] | MessagingMessage,
|
||||
me?: any,
|
||||
options?: {
|
||||
populateRecipient: boolean
|
||||
}
|
||||
) {
|
||||
const opts = options || {
|
||||
populateRecipient: true
|
||||
};
|
||||
|
||||
const message = typeof src === 'object' ? src : await this.findOne(src);
|
||||
|
||||
return {
|
||||
id: message.id,
|
||||
createdAt: message.createdAt,
|
||||
text: message.text,
|
||||
userId: message.userId,
|
||||
user: await Users.pack(message.user || message.userId, me),
|
||||
recipientId: message.recipientId,
|
||||
recipient: opts.populateRecipient ? await Users.pack(message.recipient || message.recipientId, me) : null,
|
||||
fileId: message.fileId,
|
||||
file: message.fileId ? await DriveFiles.pack(message.fileId) : null,
|
||||
isRead: message.isRead
|
||||
};
|
||||
}
|
||||
}
|
||||
28
src/models/repositories/muting.ts
Normal file
28
src/models/repositories/muting.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import { EntityRepository, Repository } from 'typeorm';
|
||||
import { Users } from '..';
|
||||
import rap from '@prezzemolo/rap';
|
||||
import { Muting } from '../entities/muting';
|
||||
|
||||
@EntityRepository(Muting)
|
||||
export class MutingRepository extends Repository<Muting> {
|
||||
public packMany(
|
||||
mutings: any[],
|
||||
me: any
|
||||
) {
|
||||
return Promise.all(mutings.map(x => this.pack(x, me)));
|
||||
}
|
||||
|
||||
public async pack(
|
||||
src: Muting['id'] | Muting,
|
||||
me?: any
|
||||
) {
|
||||
const muting = typeof src === 'object' ? src : await this.findOne(src);
|
||||
|
||||
return await rap({
|
||||
id: muting.id,
|
||||
mutee: Users.pack(muting.muteeId, me, {
|
||||
detail: true
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
25
src/models/repositories/note-favorite.ts
Normal file
25
src/models/repositories/note-favorite.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import { EntityRepository, Repository } from 'typeorm';
|
||||
import { NoteFavorite } from '../entities/note-favorite';
|
||||
import { Notes } from '..';
|
||||
|
||||
@EntityRepository(NoteFavorite)
|
||||
export class NoteFavoriteRepository extends Repository<NoteFavorite> {
|
||||
public packMany(
|
||||
favorites: any[],
|
||||
me: any
|
||||
) {
|
||||
return Promise.all(favorites.map(x => this.pack(x, me)));
|
||||
}
|
||||
|
||||
public async pack(
|
||||
src: NoteFavorite['id'] | NoteFavorite,
|
||||
me?: any
|
||||
) {
|
||||
const favorite = typeof src === 'object' ? src : await this.findOne(src);
|
||||
|
||||
return {
|
||||
id: favorite.id,
|
||||
note: await Notes.pack(favorite.note || favorite.noteId, me),
|
||||
};
|
||||
}
|
||||
}
|
||||
18
src/models/repositories/note-reaction.ts
Normal file
18
src/models/repositories/note-reaction.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import { EntityRepository, Repository } from 'typeorm';
|
||||
import { NoteReaction } from '../entities/note-reaction';
|
||||
import { Users } from '..';
|
||||
|
||||
@EntityRepository(NoteReaction)
|
||||
export class NoteReactionRepository extends Repository<NoteReaction> {
|
||||
public async pack(
|
||||
src: NoteReaction['id'] | NoteReaction,
|
||||
me?: any
|
||||
) {
|
||||
const reaction = typeof src === 'object' ? src : await this.findOne(src);
|
||||
|
||||
return {
|
||||
id: reaction.id,
|
||||
user: await Users.pack(reaction.userId, me),
|
||||
};
|
||||
}
|
||||
}
|
||||
210
src/models/repositories/note.ts
Normal file
210
src/models/repositories/note.ts
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
import { EntityRepository, Repository, In } from 'typeorm';
|
||||
import { Note } from '../entities/note';
|
||||
import { User } from '../entities/user';
|
||||
import { unique, concat } from '../../prelude/array';
|
||||
import { nyaize } from '../../misc/nyaize';
|
||||
import { Emojis, Users, Apps, PollVotes, DriveFiles, NoteReactions, Followings, Polls } from '..';
|
||||
import rap from '@prezzemolo/rap';
|
||||
|
||||
@EntityRepository(Note)
|
||||
export class NoteRepository extends Repository<Note> {
|
||||
public validateCw(x: string) {
|
||||
return x.trim().length <= 100;
|
||||
}
|
||||
|
||||
private async hideNote(packedNote: any, meId: User['id']) {
|
||||
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: any) => meId === id)) {
|
||||
// 自分へのメンション
|
||||
hide = false;
|
||||
} else {
|
||||
// フォロワーかどうか
|
||||
const following = await Followings.findOne({
|
||||
followeeId: packedNote.userId,
|
||||
followerId: meId
|
||||
});
|
||||
|
||||
if (following == null) {
|
||||
hide = true;
|
||||
} else {
|
||||
hide = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hide) {
|
||||
packedNote.visibleUserIds = null;
|
||||
packedNote.fileIds = [];
|
||||
packedNote.files = [];
|
||||
packedNote.text = null;
|
||||
packedNote.poll = null;
|
||||
packedNote.cw = null;
|
||||
packedNote.tags = [];
|
||||
packedNote.geo = null;
|
||||
packedNote.isHidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
public packMany(
|
||||
notes: (Note['id'] | Note)[],
|
||||
me?: User['id'] | User,
|
||||
options?: {
|
||||
detail?: boolean;
|
||||
skipHide?: boolean;
|
||||
}
|
||||
) {
|
||||
return Promise.all(notes.map(n => this.pack(n, me, options)));
|
||||
}
|
||||
|
||||
public async pack(
|
||||
src: Note['id'] | Note,
|
||||
me?: User['id'] | User,
|
||||
options?: {
|
||||
detail?: boolean;
|
||||
skipHide?: boolean;
|
||||
}
|
||||
): Promise<Record<string, any>> {
|
||||
const opts = Object.assign({
|
||||
detail: true,
|
||||
skipHide: false
|
||||
}, options);
|
||||
|
||||
const meId = me ? typeof me === 'string' ? me : me.id : null;
|
||||
const note = typeof src === 'object' ? src : await this.findOne(src);
|
||||
const host = note.userHost;
|
||||
|
||||
async function populatePoll() {
|
||||
const poll = await Polls.findOne({ noteId: note.id });
|
||||
const choices = poll.choices.map(c => ({
|
||||
text: c,
|
||||
votes: poll.votes[poll.choices.indexOf(c)],
|
||||
isVoted: false
|
||||
}));
|
||||
|
||||
if (poll.multiple) {
|
||||
const votes = await PollVotes.find({
|
||||
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.findOne({
|
||||
userId: meId,
|
||||
noteId: note.id
|
||||
});
|
||||
|
||||
if (vote) {
|
||||
choices[vote.choice].isVoted = true;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
multiple: poll.multiple,
|
||||
expiresAt: poll.expiresAt,
|
||||
choices
|
||||
};
|
||||
}
|
||||
|
||||
async function populateMyReaction() {
|
||||
const reaction = await NoteReactions.findOne({
|
||||
userId: meId,
|
||||
noteId: note.id,
|
||||
});
|
||||
|
||||
if (reaction) {
|
||||
return reaction.reaction;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
let text = note.text;
|
||||
|
||||
if (note.name) {
|
||||
text = `【${note.name}】\n${note.text}`;
|
||||
}
|
||||
|
||||
const reactionEmojis = unique(concat([note.emojis, Object.keys(note.reactions)]));
|
||||
|
||||
const packed = await rap({
|
||||
id: note.id,
|
||||
createdAt: note.createdAt,
|
||||
app: note.appId ? Apps.pack(note.appId) : null,
|
||||
userId: note.userId,
|
||||
user: Users.pack(note.user || note.userId, meId),
|
||||
text: text,
|
||||
cw: note.cw,
|
||||
visibility: note.visibility,
|
||||
visibleUserIds: note.visibleUserIds,
|
||||
viaMobile: note.viaMobile,
|
||||
reactions: note.reactions,
|
||||
emojis: reactionEmojis.length > 0 ? Emojis.find({
|
||||
name: In(reactionEmojis),
|
||||
host: host
|
||||
}) : [],
|
||||
tags: note.tags,
|
||||
fileIds: note.fileIds,
|
||||
files: DriveFiles.packMany(note.fileIds),
|
||||
replyId: note.replyId,
|
||||
renoteId: note.renoteId,
|
||||
|
||||
...(opts.detail ? {
|
||||
reply: note.replyId ? this.pack(note.replyId, meId, {
|
||||
detail: false
|
||||
}) : null,
|
||||
|
||||
renote: note.renoteId ? this.pack(note.renoteId, meId, {
|
||||
detail: false
|
||||
}) : null,
|
||||
|
||||
poll: note.hasPoll ? populatePoll() : null,
|
||||
|
||||
...(meId ? {
|
||||
myReaction: populateMyReaction()
|
||||
} : {})
|
||||
} : {})
|
||||
});
|
||||
|
||||
if (packed.user.isCat && packed.text) {
|
||||
packed.text = nyaize(packed.text);
|
||||
}
|
||||
|
||||
if (!opts.skipHide) {
|
||||
await this.hideNote(packed, meId);
|
||||
}
|
||||
|
||||
return packed;
|
||||
}
|
||||
}
|
||||
47
src/models/repositories/notification.ts
Normal file
47
src/models/repositories/notification.ts
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import { EntityRepository, Repository } from 'typeorm';
|
||||
import { Users, Notes } from '..';
|
||||
import rap from '@prezzemolo/rap';
|
||||
import { Notification } from '../entities/notification';
|
||||
|
||||
@EntityRepository(Notification)
|
||||
export class NotificationRepository extends Repository<Notification> {
|
||||
public packMany(
|
||||
notifications: any[],
|
||||
) {
|
||||
return Promise.all(notifications.map(x => this.pack(x)));
|
||||
}
|
||||
|
||||
public async pack(
|
||||
src: Notification['id'] | Notification,
|
||||
) {
|
||||
const notification = typeof src === 'object' ? src : await this.findOne(src);
|
||||
|
||||
return await rap({
|
||||
id: notification.id,
|
||||
createdAt: notification.createdAt,
|
||||
type: notification.type,
|
||||
userId: notification.notifierId,
|
||||
user: Users.pack(notification.notifier || notification.notifierId),
|
||||
...(notification.type === 'mention' ? {
|
||||
note: Notes.pack(notification.note || notification.noteId),
|
||||
} : {}),
|
||||
...(notification.type === 'reply' ? {
|
||||
note: Notes.pack(notification.note || notification.noteId),
|
||||
} : {}),
|
||||
...(notification.type === 'renote' ? {
|
||||
note: Notes.pack(notification.note || notification.noteId),
|
||||
} : {}),
|
||||
...(notification.type === 'quote' ? {
|
||||
note: Notes.pack(notification.note || notification.noteId),
|
||||
} : {}),
|
||||
...(notification.type === 'reaction' ? {
|
||||
note: Notes.pack(notification.note || notification.noteId),
|
||||
reaction: notification.reaction
|
||||
} : {}),
|
||||
...(notification.type === 'pollVote' ? {
|
||||
note: Notes.pack(notification.note || notification.noteId),
|
||||
choice: notification.choice
|
||||
} : {})
|
||||
});
|
||||
}
|
||||
}
|
||||
11
src/models/repositories/signin.ts
Normal file
11
src/models/repositories/signin.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import { EntityRepository, Repository } from 'typeorm';
|
||||
import { Signin } from '../entities/signin';
|
||||
|
||||
@EntityRepository(Signin)
|
||||
export class SigninRepository extends Repository<Signin> {
|
||||
public async pack(
|
||||
src: any,
|
||||
) {
|
||||
return src;
|
||||
}
|
||||
}
|
||||
16
src/models/repositories/user-list.ts
Normal file
16
src/models/repositories/user-list.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import { EntityRepository, Repository } from 'typeorm';
|
||||
import { UserList } from '../entities/user-list';
|
||||
|
||||
@EntityRepository(UserList)
|
||||
export class UserListRepository extends Repository<UserList> {
|
||||
public async pack(
|
||||
src: any,
|
||||
) {
|
||||
const userList = typeof src === 'object' ? src : await this.findOne(src);
|
||||
|
||||
return {
|
||||
id: userList.id,
|
||||
name: userList.name
|
||||
};
|
||||
}
|
||||
}
|
||||
198
src/models/repositories/user.ts
Normal file
198
src/models/repositories/user.ts
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
import { EntityRepository, Repository, In } from 'typeorm';
|
||||
import { User, ILocalUser, IRemoteUser } from '../entities/user';
|
||||
import { Emojis, Notes, NoteUnreads, FollowRequests, Notifications, MessagingMessages, UserNotePinings, Followings, Blockings, Mutings } from '..';
|
||||
import rap from '@prezzemolo/rap';
|
||||
|
||||
@EntityRepository(User)
|
||||
export class UserRepository extends Repository<User> {
|
||||
public async getRelation(me: User['id'], target: User['id']) {
|
||||
const [following1, following2, followReq1, followReq2, toBlocking, fromBlocked, mute] = await Promise.all([
|
||||
Followings.findOne({
|
||||
followerId: me,
|
||||
followeeId: target
|
||||
}),
|
||||
Followings.findOne({
|
||||
followerId: target,
|
||||
followeeId: me
|
||||
}),
|
||||
FollowRequests.findOne({
|
||||
followerId: me,
|
||||
followeeId: target
|
||||
}),
|
||||
FollowRequests.findOne({
|
||||
followerId: target,
|
||||
followeeId: me
|
||||
}),
|
||||
Blockings.findOne({
|
||||
blockerId: me,
|
||||
blockeeId: target
|
||||
}),
|
||||
Blockings.findOne({
|
||||
blockerId: target,
|
||||
blockeeId: me
|
||||
}),
|
||||
Mutings.findOne({
|
||||
muterId: me,
|
||||
muteeId: target
|
||||
})
|
||||
]);
|
||||
|
||||
return {
|
||||
id: target,
|
||||
isFollowing: following1 != null,
|
||||
hasPendingFollowRequestFromYou: followReq1 != null,
|
||||
hasPendingFollowRequestToYou: followReq2 != null,
|
||||
isFollowed: following2 != null,
|
||||
isBlocking: toBlocking != null,
|
||||
isBlocked: fromBlocked != null,
|
||||
isMuted: mute != null
|
||||
};
|
||||
}
|
||||
|
||||
public packMany(
|
||||
users: (User['id'] | User)[],
|
||||
me?: User['id'] | User,
|
||||
options?: {
|
||||
detail?: boolean,
|
||||
includeSecrets?: boolean,
|
||||
includeHasUnreadNotes?: boolean
|
||||
}
|
||||
) {
|
||||
return Promise.all(users.map(u => this.pack(u, me, options)));
|
||||
}
|
||||
|
||||
public async pack(
|
||||
src: User['id'] | User,
|
||||
me?: User['id'] | User,
|
||||
options?: {
|
||||
detail?: boolean,
|
||||
includeSecrets?: boolean,
|
||||
includeHasUnreadNotes?: boolean
|
||||
}
|
||||
): Promise<Record<string, any>> {
|
||||
const opts = Object.assign({
|
||||
detail: false,
|
||||
includeSecrets: false
|
||||
}, options);
|
||||
|
||||
const user = typeof src === 'object' ? src : await this.findOne(src);
|
||||
const meId = me ? typeof me === 'string' ? me : me.id : null;
|
||||
|
||||
const relation = meId && (meId !== user.id) && opts.detail ? await this.getRelation(meId, user.id) : null;
|
||||
const pins = opts.detail ? await UserNotePinings.find({ userId: user.id }) : [];
|
||||
|
||||
return await rap({
|
||||
id: user.id,
|
||||
name: user.name,
|
||||
username: user.username,
|
||||
host: user.host,
|
||||
avatarUrl: user.avatarUrl,
|
||||
bannerUrl: user.bannerUrl,
|
||||
avatarColor: user.avatarColor,
|
||||
bannerColor: user.bannerColor,
|
||||
isAdmin: user.isAdmin,
|
||||
|
||||
// カスタム絵文字添付
|
||||
emojis: user.emojis.length > 0 ? Emojis.find({
|
||||
where: {
|
||||
name: In(user.emojis),
|
||||
host: user.host
|
||||
},
|
||||
select: ['name', 'host', 'url', 'aliases']
|
||||
}) : [],
|
||||
|
||||
...(opts.includeHasUnreadNotes ? {
|
||||
hasUnreadSpecifiedNotes: NoteUnreads.count({
|
||||
where: { userId: user.id, isSpecified: true },
|
||||
take: 1
|
||||
}).then(count => count > 0),
|
||||
hasUnreadMentions: NoteUnreads.count({
|
||||
where: { userId: user.id },
|
||||
take: 1
|
||||
}).then(count => count > 0),
|
||||
} : {}),
|
||||
|
||||
...(opts.detail ? {
|
||||
description: user.description,
|
||||
location: user.location,
|
||||
birthday: user.birthday,
|
||||
followersCount: user.followersCount,
|
||||
followingCount: user.followingCount,
|
||||
notesCount: user.notesCount,
|
||||
pinnedNoteIds: pins.map(pin => pin.noteId),
|
||||
pinnedNotes: Notes.packMany(pins.map(pin => pin.noteId), meId, {
|
||||
detail: true
|
||||
}),
|
||||
} : {}),
|
||||
|
||||
...(opts.detail && meId === user.id ? {
|
||||
avatarId: user.avatarId,
|
||||
bannerId: user.bannerId,
|
||||
autoWatch: user.autoWatch,
|
||||
alwaysMarkNsfw: user.alwaysMarkNsfw,
|
||||
carefulBot: user.carefulBot,
|
||||
hasUnreadMessagingMessage: MessagingMessages.count({
|
||||
where: {
|
||||
recipientId: user.id,
|
||||
isRead: false
|
||||
},
|
||||
take: 1
|
||||
}).then(count => count > 0),
|
||||
hasUnreadNotification: Notifications.count({
|
||||
where: {
|
||||
userId: user.id,
|
||||
isRead: false
|
||||
},
|
||||
take: 1
|
||||
}).then(count => count > 0),
|
||||
pendingReceivedFollowRequestsCount: FollowRequests.count({
|
||||
followeeId: user.id
|
||||
}),
|
||||
} : {}),
|
||||
|
||||
...(relation ? {
|
||||
isFollowing: relation.isFollowing,
|
||||
isFollowed: relation.isFollowed,
|
||||
hasPendingFollowRequestFromYou: relation.hasPendingFollowRequestFromYou,
|
||||
hasPendingFollowRequestToYou: relation.hasPendingFollowRequestToYou,
|
||||
isBlocking: relation.isBlocking,
|
||||
isBlocked: relation.isBlocked,
|
||||
isMuted: relation.isMuted,
|
||||
} : {})
|
||||
});
|
||||
}
|
||||
|
||||
public isLocalUser(user: User): user is ILocalUser {
|
||||
return user.host === null;
|
||||
}
|
||||
|
||||
public isRemoteUser(user: User): user is IRemoteUser {
|
||||
return !this.isLocalUser(user);
|
||||
}
|
||||
|
||||
//#region Validators
|
||||
public validateUsername(username: string, remote = false): boolean {
|
||||
return typeof username == 'string' && (remote ? /^\w([\w-]*\w)?$/ : /^\w{1,20}$/).test(username);
|
||||
}
|
||||
|
||||
public validatePassword(password: string): boolean {
|
||||
return typeof password == 'string' && password != '';
|
||||
}
|
||||
|
||||
public isValidName(name?: string): boolean {
|
||||
return name === null || (typeof name == 'string' && name.length < 50 && name.trim() != '');
|
||||
}
|
||||
|
||||
public isValidDescription(description: string): boolean {
|
||||
return typeof description == 'string' && description.length < 500 && description.trim() != '';
|
||||
}
|
||||
|
||||
public isValidLocation(location: string): boolean {
|
||||
return typeof location == 'string' && location.length < 50 && location.trim() != '';
|
||||
}
|
||||
|
||||
public isValidBirthday(birthday: string): boolean {
|
||||
return typeof birthday == 'string' && /^([0-9]{4})\-([0-9]{2})-([0-9]{2})$/.test(birthday);
|
||||
}
|
||||
//#endregion
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue