summaryrefslogtreecommitdiff
path: root/packages/backend/src/server/api/common
diff options
context:
space:
mode:
Diffstat (limited to 'packages/backend/src/server/api/common')
-rw-r--r--packages/backend/src/server/api/common/GetterService.ts71
-rw-r--r--packages/backend/src/server/api/common/generate-block-query.ts42
-rw-r--r--packages/backend/src/server/api/common/generate-channel-query.ts24
-rw-r--r--packages/backend/src/server/api/common/generate-muted-note-query.ts13
-rw-r--r--packages/backend/src/server/api/common/generate-muted-note-thread-query.ts17
-rw-r--r--packages/backend/src/server/api/common/generate-muted-user-query.ts57
-rw-r--r--packages/backend/src/server/api/common/generate-native-user-token.ts3
-rw-r--r--packages/backend/src/server/api/common/generate-replies-query.ts27
-rw-r--r--packages/backend/src/server/api/common/generate-visibility-query.ts42
-rw-r--r--packages/backend/src/server/api/common/getters.ts56
-rw-r--r--packages/backend/src/server/api/common/inject-featured.ts4
-rw-r--r--packages/backend/src/server/api/common/inject-promo.ts4
-rw-r--r--packages/backend/src/server/api/common/is-native-token.ts1
-rw-r--r--packages/backend/src/server/api/common/make-pagination-query.ts28
-rw-r--r--packages/backend/src/server/api/common/read-messaging-message.ts151
-rw-r--r--packages/backend/src/server/api/common/read-notification.ts50
-rw-r--r--packages/backend/src/server/api/common/signin.ts44
-rw-r--r--packages/backend/src/server/api/common/signup.ts114
18 files changed, 75 insertions, 673 deletions
diff --git a/packages/backend/src/server/api/common/GetterService.ts b/packages/backend/src/server/api/common/GetterService.ts
new file mode 100644
index 0000000000..a6b60d1f5a
--- /dev/null
+++ b/packages/backend/src/server/api/common/GetterService.ts
@@ -0,0 +1,71 @@
+import { Inject, Injectable } from '@nestjs/common';
+import { DI } from '@/di-symbols.js';
+import type { NotesRepository, UsersRepository } from '@/models/index.js';
+import { IdentifiableError } from '@/misc/identifiable-error.js';
+import type { User } from '@/models/entities/User.js';
+import type { Note } from '@/models/entities/Note.js';
+
+@Injectable()
+export class GetterService {
+ constructor(
+ @Inject(DI.usersRepository)
+ private usersRepository: UsersRepository,
+
+ @Inject(DI.notesRepository)
+ private notesRepository: NotesRepository,
+ ) {
+ }
+
+ /**
+ * Get note for API processing
+ */
+ public async getNote(noteId: Note['id']) {
+ const note = await this.notesRepository.findOneBy({ id: noteId });
+
+ if (note == null) {
+ throw new IdentifiableError('9725d0ce-ba28-4dde-95a7-2cbb2c15de24', 'No such note.');
+ }
+
+ return note;
+ }
+
+ /**
+ * Get user for API processing
+ */
+ public async getUser(userId: User['id']) {
+ const user = await this.usersRepository.findOneBy({ id: userId });
+
+ if (user == null) {
+ throw new IdentifiableError('15348ddd-432d-49c2-8a5a-8069753becff', 'No such user.');
+ }
+
+ return user;
+ }
+
+ /**
+ * Get remote user for API processing
+ */
+ public async getRemoteUser(userId: User['id']) {
+ const user = await this.getUser(userId);
+
+ if (!this.userEntityService.isRemoteUser(user)) {
+ throw new Error('user is not a remote user');
+ }
+
+ return user;
+ }
+
+ /**
+ * Get local user for API processing
+ */
+ public async getLocalUser(userId: User['id']) {
+ const user = await this.getUser(userId);
+
+ if (!this.userEntityService.isLocalUser(user)) {
+ throw new Error('user is not a local user');
+ }
+
+ return user;
+ }
+}
+
diff --git a/packages/backend/src/server/api/common/generate-block-query.ts b/packages/backend/src/server/api/common/generate-block-query.ts
deleted file mode 100644
index 60db1e731b..0000000000
--- a/packages/backend/src/server/api/common/generate-block-query.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-import { User } from '@/models/entities/user.js';
-import { Blockings } from '@/models/index.js';
-import { Brackets, SelectQueryBuilder } from 'typeorm';
-
-// ここでいうBlockedは被Blockedの意
-export function generateBlockedUserQuery(q: SelectQueryBuilder<any>, me: { id: User['id'] }) {
- const blockingQuery = Blockings.createQueryBuilder('blocking')
- .select('blocking.blockerId')
- .where('blocking.blockeeId = :blockeeId', { blockeeId: me.id });
-
- // 投稿の作者にブロックされていない かつ
- // 投稿の返信先の作者にブロックされていない かつ
- // 投稿の引用元の作者にブロックされていない
- q
- .andWhere(`note.userId NOT IN (${ blockingQuery.getQuery() })`)
- .andWhere(new Brackets(qb => { qb
- .where(`note.replyUserId IS NULL`)
- .orWhere(`note.replyUserId NOT IN (${ blockingQuery.getQuery() })`);
- }))
- .andWhere(new Brackets(qb => { qb
- .where(`note.renoteUserId IS NULL`)
- .orWhere(`note.renoteUserId NOT IN (${ blockingQuery.getQuery() })`);
- }));
-
- q.setParameters(blockingQuery.getParameters());
-}
-
-export function generateBlockQueryForUsers(q: SelectQueryBuilder<any>, me: { id: User['id'] }) {
- const blockingQuery = Blockings.createQueryBuilder('blocking')
- .select('blocking.blockeeId')
- .where('blocking.blockerId = :blockerId', { blockerId: me.id });
-
- const blockedQuery = Blockings.createQueryBuilder('blocking')
- .select('blocking.blockerId')
- .where('blocking.blockeeId = :blockeeId', { blockeeId: me.id });
-
- q.andWhere(`user.id NOT IN (${ blockingQuery.getQuery() })`);
- q.setParameters(blockingQuery.getParameters());
-
- q.andWhere(`user.id NOT IN (${ blockedQuery.getQuery() })`);
- q.setParameters(blockedQuery.getParameters());
-}
diff --git a/packages/backend/src/server/api/common/generate-channel-query.ts b/packages/backend/src/server/api/common/generate-channel-query.ts
deleted file mode 100644
index 333bb73b86..0000000000
--- a/packages/backend/src/server/api/common/generate-channel-query.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import { User } from '@/models/entities/user.js';
-import { ChannelFollowings } from '@/models/index.js';
-import { Brackets, SelectQueryBuilder } from 'typeorm';
-
-export function generateChannelQuery(q: SelectQueryBuilder<any>, me?: { id: User['id'] } | null) {
- if (me == null) {
- q.andWhere('note.channelId IS NULL');
- } else {
- q.leftJoinAndSelect('note.channel', 'channel');
-
- const channelFollowingQuery = ChannelFollowings.createQueryBuilder('channelFollowing')
- .select('channelFollowing.followeeId')
- .where('channelFollowing.followerId = :followerId', { followerId: me.id });
-
- q.andWhere(new Brackets(qb => { qb
- // チャンネルのノートではない
- .where('note.channelId IS NULL')
- // または自分がフォローしているチャンネルのノート
- .orWhere(`note.channelId IN (${ channelFollowingQuery.getQuery() })`);
- }));
-
- q.setParameters(channelFollowingQuery.getParameters());
- }
-}
diff --git a/packages/backend/src/server/api/common/generate-muted-note-query.ts b/packages/backend/src/server/api/common/generate-muted-note-query.ts
deleted file mode 100644
index f544e334d3..0000000000
--- a/packages/backend/src/server/api/common/generate-muted-note-query.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { User } from '@/models/entities/user.js';
-import { MutedNotes } from '@/models/index.js';
-import { SelectQueryBuilder } from 'typeorm';
-
-export function generateMutedNoteQuery(q: SelectQueryBuilder<any>, me: { id: User['id'] }) {
- const mutedQuery = MutedNotes.createQueryBuilder('muted')
- .select('muted.noteId')
- .where('muted.userId = :userId', { userId: me.id });
-
- q.andWhere(`note.id NOT IN (${ mutedQuery.getQuery() })`);
-
- q.setParameters(mutedQuery.getParameters());
-}
diff --git a/packages/backend/src/server/api/common/generate-muted-note-thread-query.ts b/packages/backend/src/server/api/common/generate-muted-note-thread-query.ts
deleted file mode 100644
index 7263ea2e60..0000000000
--- a/packages/backend/src/server/api/common/generate-muted-note-thread-query.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { User } from '@/models/entities/user.js';
-import { NoteThreadMutings } from '@/models/index.js';
-import { Brackets, SelectQueryBuilder } from 'typeorm';
-
-export function generateMutedNoteThreadQuery(q: SelectQueryBuilder<any>, me: { id: User['id'] }) {
- const mutedQuery = NoteThreadMutings.createQueryBuilder('threadMuted')
- .select('threadMuted.threadId')
- .where('threadMuted.userId = :userId', { userId: me.id });
-
- q.andWhere(`note.id NOT IN (${ mutedQuery.getQuery() })`);
- q.andWhere(new Brackets(qb => { qb
- .where(`note.threadId IS NULL`)
- .orWhere(`note.threadId NOT IN (${ mutedQuery.getQuery() })`);
- }));
-
- q.setParameters(mutedQuery.getParameters());
-}
diff --git a/packages/backend/src/server/api/common/generate-muted-user-query.ts b/packages/backend/src/server/api/common/generate-muted-user-query.ts
deleted file mode 100644
index 470ece1a62..0000000000
--- a/packages/backend/src/server/api/common/generate-muted-user-query.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-import { SelectQueryBuilder, Brackets } from 'typeorm';
-import { User } from '@/models/entities/user.js';
-import { Mutings, UserProfiles } from '@/models/index.js';
-
-export function generateMutedUserQuery(q: SelectQueryBuilder<any>, me: { id: User['id'] }, exclude?: User) {
- const mutingQuery = Mutings.createQueryBuilder('muting')
- .select('muting.muteeId')
- .where('muting.muterId = :muterId', { muterId: me.id });
-
- if (exclude) {
- mutingQuery.andWhere('muting.muteeId != :excludeId', { excludeId: exclude.id });
- }
-
- const mutingInstanceQuery = UserProfiles.createQueryBuilder('user_profile')
- .select('user_profile.mutedInstances')
- .where('user_profile.userId = :muterId', { muterId: me.id });
-
- // 投稿の作者をミュートしていない かつ
- // 投稿の返信先の作者をミュートしていない かつ
- // 投稿の引用元の作者をミュートしていない
- q
- .andWhere(`note.userId NOT IN (${ mutingQuery.getQuery() })`)
- .andWhere(new Brackets(qb => { qb
- .where('note.replyUserId IS NULL')
- .orWhere(`note.replyUserId NOT IN (${ mutingQuery.getQuery() })`);
- }))
- .andWhere(new Brackets(qb => { qb
- .where('note.renoteUserId IS NULL')
- .orWhere(`note.renoteUserId NOT IN (${ mutingQuery.getQuery() })`);
- }))
- // mute instances
- .andWhere(new Brackets(qb => { qb
- .andWhere('note.userHost IS NULL')
- .orWhere(`NOT ((${ mutingInstanceQuery.getQuery() })::jsonb ? note.userHost)`);
- }))
- .andWhere(new Brackets(qb => { qb
- .where('note.replyUserHost IS NULL')
- .orWhere(`NOT ((${ mutingInstanceQuery.getQuery() })::jsonb ? note.replyUserHost)`);
- }))
- .andWhere(new Brackets(qb => { qb
- .where('note.renoteUserHost IS NULL')
- .orWhere(`NOT ((${ mutingInstanceQuery.getQuery() })::jsonb ? note.renoteUserHost)`);
- }));
-
- q.setParameters(mutingQuery.getParameters());
- q.setParameters(mutingInstanceQuery.getParameters());
-}
-
-export function generateMutedUserQueryForUsers(q: SelectQueryBuilder<any>, me: { id: User['id'] }) {
- const mutingQuery = Mutings.createQueryBuilder('muting')
- .select('muting.muteeId')
- .where('muting.muterId = :muterId', { muterId: me.id });
-
- q.andWhere(`user.id NOT IN (${ mutingQuery.getQuery() })`);
-
- q.setParameters(mutingQuery.getParameters());
-}
diff --git a/packages/backend/src/server/api/common/generate-native-user-token.ts b/packages/backend/src/server/api/common/generate-native-user-token.ts
deleted file mode 100644
index 5d8a4c5378..0000000000
--- a/packages/backend/src/server/api/common/generate-native-user-token.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-import { secureRndstr } from '@/misc/secure-rndstr.js';
-
-export default () => secureRndstr(16, true);
diff --git a/packages/backend/src/server/api/common/generate-replies-query.ts b/packages/backend/src/server/api/common/generate-replies-query.ts
deleted file mode 100644
index 301782eab9..0000000000
--- a/packages/backend/src/server/api/common/generate-replies-query.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { User } from '@/models/entities/user.js';
-import { Brackets, SelectQueryBuilder } from 'typeorm';
-
-export function generateRepliesQuery(q: SelectQueryBuilder<any>, me?: Pick<User, 'id' | 'showTimelineReplies'> | null) {
- if (me == null) {
- q.andWhere(new Brackets(qb => { qb
- .where(`note.replyId IS NULL`) // 返信ではない
- .orWhere(new Brackets(qb => { qb // 返信だけど投稿者自身への返信
- .where(`note.replyId IS NOT NULL`)
- .andWhere('note.replyUserId = note.userId');
- }));
- }));
- } else if (!me.showTimelineReplies) {
- q.andWhere(new Brackets(qb => { qb
- .where(`note.replyId IS NULL`) // 返信ではない
- .orWhere('note.replyUserId = :meId', { meId: me.id }) // 返信だけど自分のノートへの返信
- .orWhere(new Brackets(qb => { qb // 返信だけど自分の行った返信
- .where(`note.replyId IS NOT NULL`)
- .andWhere('note.userId = :meId', { meId: me.id });
- }))
- .orWhere(new Brackets(qb => { qb // 返信だけど投稿者自身への返信
- .where(`note.replyId IS NOT NULL`)
- .andWhere('note.replyUserId = note.userId');
- }));
- }));
- }
-}
diff --git a/packages/backend/src/server/api/common/generate-visibility-query.ts b/packages/backend/src/server/api/common/generate-visibility-query.ts
deleted file mode 100644
index b50b6812f4..0000000000
--- a/packages/backend/src/server/api/common/generate-visibility-query.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-import { User } from '@/models/entities/user.js';
-import { Followings } from '@/models/index.js';
-import { Brackets, SelectQueryBuilder } from 'typeorm';
-
-export function generateVisibilityQuery(q: SelectQueryBuilder<any>, me?: { id: User['id'] } | null) {
- // This code must always be synchronized with the checks in Notes.isVisibleForMe.
- if (me == null) {
- q.andWhere(new Brackets(qb => { qb
- .where(`note.visibility = 'public'`)
- .orWhere(`note.visibility = 'home'`);
- }));
- } else {
- const followingQuery = Followings.createQueryBuilder('following')
- .select('following.followeeId')
- .where('following.followerId = :meId');
-
- q.andWhere(new Brackets(qb => { qb
- // 公開投稿である
- .where(new Brackets(qb => { qb
- .where(`note.visibility = 'public'`)
- .orWhere(`note.visibility = 'home'`);
- }))
- // または 自分自身
- .orWhere('note.userId = :meId')
- // または 自分宛て
- .orWhere(':meId = ANY(note.visibleUserIds)')
- .orWhere(':meId = ANY(note.mentions)')
- .orWhere(new Brackets(qb => { qb
- // または フォロワー宛ての投稿であり、
- .where(`note.visibility = 'followers'`)
- .andWhere(new Brackets(qb => { qb
- // 自分がフォロワーである
- .where(`note.userId IN (${ followingQuery.getQuery() })`)
- // または 自分の投稿へのリプライ
- .orWhere('note.replyUserId = :meId');
- }));
- }));
- }));
-
- q.setParameters({ meId: me.id });
- }
-}
diff --git a/packages/backend/src/server/api/common/getters.ts b/packages/backend/src/server/api/common/getters.ts
deleted file mode 100644
index 783ea9ef75..0000000000
--- a/packages/backend/src/server/api/common/getters.ts
+++ /dev/null
@@ -1,56 +0,0 @@
-import { IdentifiableError } from '@/misc/identifiable-error.js';
-import { User } from '@/models/entities/user.js';
-import { Note } from '@/models/entities/note.js';
-import { Notes, Users } from '@/models/index.js';
-
-/**
- * Get note for API processing
- */
-export async function getNote(noteId: Note['id']) {
- const note = await Notes.findOneBy({ id: noteId });
-
- if (note == null) {
- throw new IdentifiableError('9725d0ce-ba28-4dde-95a7-2cbb2c15de24', 'No such note.');
- }
-
- return note;
-}
-
-/**
- * Get user for API processing
- */
-export async function getUser(userId: User['id']) {
- const user = await Users.findOneBy({ id: userId });
-
- if (user == null) {
- throw new IdentifiableError('15348ddd-432d-49c2-8a5a-8069753becff', 'No such user.');
- }
-
- return user;
-}
-
-/**
- * Get remote user for API processing
- */
-export async function getRemoteUser(userId: User['id']) {
- const user = await getUser(userId);
-
- if (!Users.isRemoteUser(user)) {
- throw new Error('user is not a remote user');
- }
-
- return user;
-}
-
-/**
- * Get local user for API processing
- */
-export async function getLocalUser(userId: User['id']) {
- const user = await getUser(userId);
-
- if (!Users.isLocalUser(user)) {
- throw new Error('user is not a local user');
- }
-
- return user;
-}
diff --git a/packages/backend/src/server/api/common/inject-featured.ts b/packages/backend/src/server/api/common/inject-featured.ts
index f7cdd365ed..75126fa304 100644
--- a/packages/backend/src/server/api/common/inject-featured.ts
+++ b/packages/backend/src/server/api/common/inject-featured.ts
@@ -1,6 +1,6 @@
import rndstr from 'rndstr';
-import { Note } from '@/models/entities/note.js';
-import { User } from '@/models/entities/user.js';
+import { Note } from '@/models/entities/Note.js';
+import { User } from '@/models/entities/User.js';
import { Notes, UserProfiles, NoteReactions } from '@/models/index.js';
import { generateMutedUserQuery } from './generate-muted-user-query.js';
import { generateBlockedUserQuery } from './generate-block-query.js';
diff --git a/packages/backend/src/server/api/common/inject-promo.ts b/packages/backend/src/server/api/common/inject-promo.ts
index b0da8118b4..454f5dbb0e 100644
--- a/packages/backend/src/server/api/common/inject-promo.ts
+++ b/packages/backend/src/server/api/common/inject-promo.ts
@@ -1,6 +1,6 @@
import rndstr from 'rndstr';
-import { Note } from '@/models/entities/note.js';
-import { User } from '@/models/entities/user.js';
+import { Note } from '@/models/entities/Note.js';
+import { User } from '@/models/entities/User.js';
import { PromoReads, PromoNotes, Notes, Users } from '@/models/index.js';
export async function injectPromo(timeline: Note[], user?: User | null) {
diff --git a/packages/backend/src/server/api/common/is-native-token.ts b/packages/backend/src/server/api/common/is-native-token.ts
deleted file mode 100644
index 2833c570c8..0000000000
--- a/packages/backend/src/server/api/common/is-native-token.ts
+++ /dev/null
@@ -1 +0,0 @@
-export default (token: string) => token.length === 16;
diff --git a/packages/backend/src/server/api/common/make-pagination-query.ts b/packages/backend/src/server/api/common/make-pagination-query.ts
deleted file mode 100644
index 51c11e5dff..0000000000
--- a/packages/backend/src/server/api/common/make-pagination-query.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import { SelectQueryBuilder } from 'typeorm';
-
-export function makePaginationQuery<T>(q: SelectQueryBuilder<T>, sinceId?: string, untilId?: string, sinceDate?: number, untilDate?: number) {
- if (sinceId && untilId) {
- q.andWhere(`${q.alias}.id > :sinceId`, { sinceId: sinceId });
- q.andWhere(`${q.alias}.id < :untilId`, { untilId: untilId });
- q.orderBy(`${q.alias}.id`, 'DESC');
- } else if (sinceId) {
- q.andWhere(`${q.alias}.id > :sinceId`, { sinceId: sinceId });
- q.orderBy(`${q.alias}.id`, 'ASC');
- } else if (untilId) {
- q.andWhere(`${q.alias}.id < :untilId`, { untilId: untilId });
- q.orderBy(`${q.alias}.id`, 'DESC');
- } else if (sinceDate && untilDate) {
- q.andWhere(`${q.alias}.createdAt > :sinceDate`, { sinceDate: new Date(sinceDate) });
- q.andWhere(`${q.alias}.createdAt < :untilDate`, { untilDate: new Date(untilDate) });
- q.orderBy(`${q.alias}.createdAt`, 'DESC');
- } else if (sinceDate) {
- q.andWhere(`${q.alias}.createdAt > :sinceDate`, { sinceDate: new Date(sinceDate) });
- q.orderBy(`${q.alias}.createdAt`, 'ASC');
- } else if (untilDate) {
- q.andWhere(`${q.alias}.createdAt < :untilDate`, { untilDate: new Date(untilDate) });
- q.orderBy(`${q.alias}.createdAt`, 'DESC');
- } else {
- q.orderBy(`${q.alias}.id`, 'DESC');
- }
- return q;
-}
diff --git a/packages/backend/src/server/api/common/read-messaging-message.ts b/packages/backend/src/server/api/common/read-messaging-message.ts
deleted file mode 100644
index c4c18ffa06..0000000000
--- a/packages/backend/src/server/api/common/read-messaging-message.ts
+++ /dev/null
@@ -1,151 +0,0 @@
-import { publishMainStream, publishGroupMessagingStream } from '@/services/stream.js';
-import { publishMessagingStream } from '@/services/stream.js';
-import { publishMessagingIndexStream } from '@/services/stream.js';
-import { pushNotification } from '@/services/push-notification.js';
-import { User, IRemoteUser } from '@/models/entities/user.js';
-import { MessagingMessage } from '@/models/entities/messaging-message.js';
-import { MessagingMessages, UserGroupJoinings, Users } from '@/models/index.js';
-import { In } from 'typeorm';
-import { IdentifiableError } from '@/misc/identifiable-error.js';
-import { UserGroup } from '@/models/entities/user-group.js';
-import { toArray } from '@/prelude/array.js';
-import { renderReadActivity } from '@/remote/activitypub/renderer/read.js';
-import { renderActivity } from '@/remote/activitypub/renderer/index.js';
-import { deliver } from '@/queue/index.js';
-import orderedCollection from '@/remote/activitypub/renderer/ordered-collection.js';
-
-/**
- * Mark messages as read
- */
-export async function readUserMessagingMessage(
- userId: User['id'],
- otherpartyId: User['id'],
- messageIds: MessagingMessage['id'][]
-) {
- if (messageIds.length === 0) return;
-
- const messages = await MessagingMessages.findBy({
- id: In(messageIds),
- });
-
- for (const message of messages) {
- if (message.recipientId !== userId) {
- throw new IdentifiableError('e140a4bf-49ce-4fb6-b67c-b78dadf6b52f', 'Access denied (user).');
- }
- }
-
- // Update documents
- await MessagingMessages.update({
- id: In(messageIds),
- userId: otherpartyId,
- recipientId: userId,
- isRead: false,
- }, {
- isRead: true,
- });
-
- // Publish event
- publishMessagingStream(otherpartyId, userId, 'read', messageIds);
- publishMessagingIndexStream(userId, 'read', messageIds);
-
- if (!await Users.getHasUnreadMessagingMessage(userId)) {
- // 全ての(いままで未読だった)自分宛てのメッセージを(これで)読みましたよというイベントを発行
- publishMainStream(userId, 'readAllMessagingMessages');
- pushNotification(userId, 'readAllMessagingMessages', undefined);
- } else {
- // そのユーザーとのメッセージで未読がなければイベント発行
- const count = await MessagingMessages.count({
- where: {
- userId: otherpartyId,
- recipientId: userId,
- isRead: false,
- },
- take: 1
- });
-
- if (!count) {
- pushNotification(userId, 'readAllMessagingMessagesOfARoom', { userId: otherpartyId });
- }
- }
-}
-
-/**
- * Mark messages as read
- */
-export async function readGroupMessagingMessage(
- userId: User['id'],
- groupId: UserGroup['id'],
- messageIds: MessagingMessage['id'][]
-) {
- if (messageIds.length === 0) return;
-
- // check joined
- const joining = await UserGroupJoinings.findOneBy({
- userId: userId,
- userGroupId: groupId,
- });
-
- if (joining == null) {
- throw new IdentifiableError('930a270c-714a-46b2-b776-ad27276dc569', 'Access denied (group).');
- }
-
- const messages = await MessagingMessages.findBy({
- id: In(messageIds),
- });
-
- const reads: MessagingMessage['id'][] = [];
-
- for (const message of messages) {
- if (message.userId === userId) continue;
- if (message.reads.includes(userId)) continue;
-
- // Update document
- await MessagingMessages.createQueryBuilder().update()
- .set({
- reads: (() => `array_append("reads", '${joining.userId}')`) as any,
- })
- .where('id = :id', { id: message.id })
- .execute();
-
- reads.push(message.id);
- }
-
- // Publish event
- publishGroupMessagingStream(groupId, 'read', {
- ids: reads,
- userId: userId,
- });
- publishMessagingIndexStream(userId, 'read', reads);
-
- if (!await Users.getHasUnreadMessagingMessage(userId)) {
- // 全ての(いままで未読だった)自分宛てのメッセージを(これで)読みましたよというイベントを発行
- publishMainStream(userId, 'readAllMessagingMessages');
- pushNotification(userId, 'readAllMessagingMessages', undefined);
- } else {
- // そのグループにおいて未読がなければイベント発行
- const unreadExist = await MessagingMessages.createQueryBuilder('message')
- .where(`message.groupId = :groupId`, { groupId: groupId })
- .andWhere('message.userId != :userId', { userId: userId })
- .andWhere('NOT (:userId = ANY(message.reads))', { userId: userId })
- .andWhere('message.createdAt > :joinedAt', { joinedAt: joining.createdAt }) // 自分が加入する前の会話については、未読扱いしない
- .getOne().then(x => x != null);
-
- if (!unreadExist) {
- pushNotification(userId, 'readAllMessagingMessagesOfARoom', { groupId });
- }
- }
-}
-
-export async function deliverReadActivity(user: { id: User['id']; host: null; }, recipient: IRemoteUser, messages: MessagingMessage | MessagingMessage[]) {
- messages = toArray(messages).filter(x => x.uri);
- const contents = messages.map(x => renderReadActivity(user, x));
-
- if (contents.length > 1) {
- const collection = orderedCollection(null, contents.length, undefined, undefined, contents);
- deliver(user, renderActivity(collection), recipient.inbox);
- } else {
- for (const content of contents) {
- deliver(user, renderActivity(content), recipient.inbox);
- }
- }
-}
diff --git a/packages/backend/src/server/api/common/read-notification.ts b/packages/backend/src/server/api/common/read-notification.ts
deleted file mode 100644
index b0d38a9e39..0000000000
--- a/packages/backend/src/server/api/common/read-notification.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-import { In } from 'typeorm';
-import { publishMainStream } from '@/services/stream.js';
-import { pushNotification } from '@/services/push-notification.js';
-import { User } from '@/models/entities/user.js';
-import { Notification } from '@/models/entities/notification.js';
-import { Notifications, Users } from '@/models/index.js';
-
-export async function readNotification(
- userId: User['id'],
- notificationIds: Notification['id'][],
-) {
- if (notificationIds.length === 0) return;
-
- // Update documents
- const result = await Notifications.update({
- notifieeId: userId,
- id: In(notificationIds),
- isRead: false,
- }, {
- isRead: true,
- });
-
- if (result.affected === 0) return;
-
- if (!await Users.getHasUnreadNotification(userId)) return postReadAllNotifications(userId);
- else return postReadNotifications(userId, notificationIds);
-}
-
-export async function readNotificationByQuery(
- userId: User['id'],
- query: Record<string, any>,
-) {
- const notificationIds = await Notifications.findBy({
- ...query,
- notifieeId: userId,
- isRead: false,
- }).then(notifications => notifications.map(notification => notification.id));
-
- return readNotification(userId, notificationIds);
-}
-
-function postReadAllNotifications(userId: User['id']) {
- publishMainStream(userId, 'readAllNotifications');
- return pushNotification(userId, 'readAllNotifications', undefined);
-}
-
-function postReadNotifications(userId: User['id'], notificationIds: Notification['id'][]) {
- publishMainStream(userId, 'readNotifications', notificationIds);
- return pushNotification(userId, 'readNotifications', { notificationIds });
-}
diff --git a/packages/backend/src/server/api/common/signin.ts b/packages/backend/src/server/api/common/signin.ts
deleted file mode 100644
index 038fd8d96e..0000000000
--- a/packages/backend/src/server/api/common/signin.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import Koa from 'koa';
-
-import config from '@/config/index.js';
-import { ILocalUser } from '@/models/entities/user.js';
-import { Signins } from '@/models/index.js';
-import { genId } from '@/misc/gen-id.js';
-import { publishMainStream } from '@/services/stream.js';
-
-export default function(ctx: Koa.Context, user: ILocalUser, redirect = false) {
- if (redirect) {
- //#region Cookie
- ctx.cookies.set('igi', user.token!, {
- path: '/',
- // SEE: https://github.com/koajs/koa/issues/974
- // When using a SSL proxy it should be configured to add the "X-Forwarded-Proto: https" header
- secure: config.url.startsWith('https'),
- httpOnly: false,
- });
- //#endregion
-
- ctx.redirect(config.url);
- } else {
- ctx.body = {
- id: user.id,
- i: user.token,
- };
- ctx.status = 200;
- }
-
- (async () => {
- // Append signin history
- const record = await Signins.insert({
- id: genId(),
- createdAt: new Date(),
- userId: user.id,
- ip: ctx.ip,
- headers: ctx.headers,
- success: true,
- }).then(x => Signins.findOneByOrFail(x.identifiers[0]));
-
- // Publish signin event
- publishMainStream(user.id, 'signin', await Signins.pack(record));
- })();
-}
diff --git a/packages/backend/src/server/api/common/signup.ts b/packages/backend/src/server/api/common/signup.ts
deleted file mode 100644
index abc142472a..0000000000
--- a/packages/backend/src/server/api/common/signup.ts
+++ /dev/null
@@ -1,114 +0,0 @@
-import bcrypt from 'bcryptjs';
-import { generateKeyPair } from 'node:crypto';
-import generateUserToken from './generate-native-user-token.js';
-import { User } from '@/models/entities/user.js';
-import { Users, UsedUsernames } from '@/models/index.js';
-import { UserProfile } from '@/models/entities/user-profile.js';
-import { IsNull } from 'typeorm';
-import { genId } from '@/misc/gen-id.js';
-import { toPunyNullable } from '@/misc/convert-host.js';
-import { UserKeypair } from '@/models/entities/user-keypair.js';
-import { usersChart } from '@/services/chart/index.js';
-import { UsedUsername } from '@/models/entities/used-username.js';
-import { db } from '@/db/postgre.js';
-
-export async function signup(opts: {
- username: User['username'];
- password?: string | null;
- passwordHash?: UserProfile['password'] | null;
- host?: string | null;
-}) {
- const { username, password, passwordHash, host } = opts;
- let hash = passwordHash;
-
- // Validate username
- if (!Users.validateLocalUsername(username)) {
- throw new Error('INVALID_USERNAME');
- }
-
- if (password != null && passwordHash == null) {
- // Validate password
- if (!Users.validatePassword(password)) {
- throw new Error('INVALID_PASSWORD');
- }
-
- // Generate hash of password
- const salt = await bcrypt.genSalt(8);
- hash = await bcrypt.hash(password, salt);
- }
-
- // Generate secret
- const secret = generateUserToken();
-
- // Check username duplication
- if (await Users.findOneBy({ usernameLower: username.toLowerCase(), host: IsNull() })) {
- throw new Error('DUPLICATED_USERNAME');
- }
-
- // Check deleted username duplication
- if (await UsedUsernames.findOneBy({ username: username.toLowerCase() })) {
- throw new Error('USED_USERNAME');
- }
-
- const keyPair = await new Promise<string[]>((res, rej) =>
- generateKeyPair('rsa', {
- modulusLength: 4096,
- publicKeyEncoding: {
- type: 'spki',
- format: 'pem',
- },
- privateKeyEncoding: {
- type: 'pkcs8',
- format: 'pem',
- cipher: undefined,
- passphrase: undefined,
- },
- } as any, (err, publicKey, privateKey) =>
- err ? rej(err) : res([publicKey, privateKey])
- ));
-
- let account!: User;
-
- // Start transaction
- await db.transaction(async transactionalEntityManager => {
- const exist = await transactionalEntityManager.findOneBy(User, {
- usernameLower: username.toLowerCase(),
- host: IsNull(),
- });
-
- if (exist) throw new Error(' the username is already used');
-
- account = await transactionalEntityManager.save(new User({
- id: genId(),
- createdAt: new Date(),
- username: username,
- usernameLower: username.toLowerCase(),
- host: toPunyNullable(host),
- token: secret,
- isAdmin: (await Users.countBy({
- host: IsNull(),
- })) === 0,
- }));
-
- await transactionalEntityManager.save(new UserKeypair({
- publicKey: keyPair[0],
- privateKey: keyPair[1],
- userId: account.id,
- }));
-
- await transactionalEntityManager.save(new UserProfile({
- userId: account.id,
- autoAcceptFollowed: true,
- password: hash,
- }));
-
- await transactionalEntityManager.save(new UsedUsername({
- createdAt: new Date(),
- username: username.toLowerCase(),
- }));
- });
-
- usersChart.update(account, true);
-
- return { account, secret };
-}