summaryrefslogtreecommitdiff
path: root/src/server
diff options
context:
space:
mode:
Diffstat (limited to 'src/server')
-rw-r--r--src/server/activitypub.ts5
-rw-r--r--src/server/api/authenticate.ts13
-rw-r--r--src/server/api/common/inject-featured.ts2
-rw-r--r--src/server/api/define.ts1
-rw-r--r--src/server/api/endpoints/admin/invite.ts2
-rw-r--r--src/server/api/endpoints/admin/promo/create.ts2
-rw-r--r--src/server/api/endpoints/antennas/notes.ts6
-rw-r--r--src/server/api/endpoints/auth/accept.ts2
-rw-r--r--src/server/api/endpoints/channels/follow.ts5
-rw-r--r--src/server/api/endpoints/channels/timeline.ts6
-rw-r--r--src/server/api/endpoints/channels/unfollow.ts3
-rw-r--r--src/server/api/endpoints/clips/add-note.ts2
-rw-r--r--src/server/api/endpoints/clips/notes.ts6
-rw-r--r--src/server/api/endpoints/i.ts27
-rw-r--r--src/server/api/endpoints/i/notifications.ts10
-rw-r--r--src/server/api/endpoints/i/read-announcement.ts2
-rw-r--r--src/server/api/endpoints/i/update.ts3
-rw-r--r--src/server/api/endpoints/miauth/gen-token.ts2
-rw-r--r--src/server/api/endpoints/mute/create.ts3
-rw-r--r--src/server/api/endpoints/mute/delete.ts3
-rw-r--r--src/server/api/endpoints/notes.ts6
-rw-r--r--src/server/api/endpoints/notes/children.ts6
-rw-r--r--src/server/api/endpoints/notes/favorites/create.ts2
-rw-r--r--src/server/api/endpoints/notes/featured.ts6
-rw-r--r--src/server/api/endpoints/notes/global-timeline.ts11
-rw-r--r--src/server/api/endpoints/notes/hybrid-timeline.ts11
-rw-r--r--src/server/api/endpoints/notes/local-timeline.ts11
-rw-r--r--src/server/api/endpoints/notes/mentions.ts10
-rw-r--r--src/server/api/endpoints/notes/renotes.ts6
-rw-r--r--src/server/api/endpoints/notes/replies.ts6
-rw-r--r--src/server/api/endpoints/notes/search-by-tag.ts6
-rw-r--r--src/server/api/endpoints/notes/search.ts6
-rw-r--r--src/server/api/endpoints/notes/timeline.ts11
-rw-r--r--src/server/api/endpoints/notes/user-list-timeline.ts6
-rw-r--r--src/server/api/endpoints/pages/like.ts2
-rw-r--r--src/server/api/endpoints/promo/read.ts2
-rw-r--r--src/server/api/endpoints/sw/register.ts2
-rw-r--r--src/server/api/endpoints/users/followers.ts3
-rw-r--r--src/server/api/endpoints/users/following.ts3
-rw-r--r--src/server/api/endpoints/users/groups/create.ts2
-rw-r--r--src/server/api/endpoints/users/groups/invitations/accept.ts2
-rw-r--r--src/server/api/endpoints/users/notes.ts6
-rw-r--r--src/server/api/private/signin.ts4
-rw-r--r--src/server/api/stream/channels/antenna.ts2
-rw-r--r--src/server/api/stream/channels/channel.ts2
-rw-r--r--src/server/api/stream/channels/global-timeline.ts2
-rw-r--r--src/server/api/stream/channels/hashtag.ts2
-rw-r--r--src/server/api/stream/channels/home-timeline.ts2
-rw-r--r--src/server/api/stream/channels/hybrid-timeline.ts2
-rw-r--r--src/server/api/stream/channels/local-timeline.ts2
-rw-r--r--src/server/api/stream/channels/main.ts8
-rw-r--r--src/server/api/stream/index.ts112
-rw-r--r--src/server/web/index.ts12
-rw-r--r--src/server/web/views/flush.pug59
54 files changed, 300 insertions, 140 deletions
diff --git a/src/server/activitypub.ts b/src/server/activitypub.ts
index bf71258625..694807239b 100644
--- a/src/server/activitypub.ts
+++ b/src/server/activitypub.ts
@@ -13,10 +13,11 @@ import Following from './activitypub/following';
import Featured from './activitypub/featured';
import { inbox as processInbox } from '../queue';
import { isSelfHost } from '../misc/convert-host';
-import { Notes, Users, Emojis, UserKeypairs, NoteReactions } from '../models';
+import { Notes, Users, Emojis, NoteReactions } from '../models';
import { ILocalUser, User } from '../models/entities/user';
import { In } from 'typeorm';
import { renderLike } from '../remote/activitypub/renderer/like';
+import { getUserKeypair } from '../misc/keypair-store';
// Init router
const router = new Router();
@@ -135,7 +136,7 @@ router.get('/users/:user/publickey', async ctx => {
return;
}
- const keypair = await UserKeypairs.findOneOrFail(user.id);
+ const keypair = await getUserKeypair(user.id);
if (Users.isLocalUser(user)) {
ctx.body = renderActivity(renderKey(user, keypair));
diff --git a/src/server/api/authenticate.ts b/src/server/api/authenticate.ts
index 0374ca35ea..9c9ef74352 100644
--- a/src/server/api/authenticate.ts
+++ b/src/server/api/authenticate.ts
@@ -2,6 +2,11 @@ import isNativeToken from './common/is-native-token';
import { User } from '../../models/entities/user';
import { Users, AccessTokens, Apps } from '../../models';
import { AccessToken } from '../../models/entities/access-token';
+import { Cache } from '../../misc/cache';
+
+// TODO: TypeORMのカスタムキャッシュプロバイダを使っても良いかも
+// ref. https://github.com/typeorm/typeorm/blob/master/docs/caching.md
+const cache = new Cache<User>(1000 * 60 * 60);
export default async (token: string): Promise<[User | null | undefined, AccessToken | null | undefined]> => {
if (token == null) {
@@ -9,6 +14,11 @@ export default async (token: string): Promise<[User | null | undefined, AccessTo
}
if (isNativeToken(token)) {
+ const cached = cache.get(token);
+ if (cached) {
+ return [cached, null];
+ }
+
// Fetch user
const user = await Users
.findOne({ token });
@@ -17,8 +27,11 @@ export default async (token: string): Promise<[User | null | undefined, AccessTo
throw new Error('user not found');
}
+ cache.set(token, user);
+
return [user, null];
} else {
+ // TODO: cache
const accessToken = await AccessTokens.findOne({
where: [{
hash: token.toLowerCase() // app
diff --git a/src/server/api/common/inject-featured.ts b/src/server/api/common/inject-featured.ts
index 3f47c13385..bbed7f69cb 100644
--- a/src/server/api/common/inject-featured.ts
+++ b/src/server/api/common/inject-featured.ts
@@ -23,7 +23,7 @@ export async function injectFeatured(timeline: Note[], user?: User | null) {
.andWhere(`note.score > 0`)
.andWhere(`note.createdAt > :date`, { date: new Date(Date.now() - day) })
.andWhere(`note.visibility = 'public'`)
- .leftJoinAndSelect('note.user', 'user');
+ .innerJoinAndSelect('note.user', 'user');
if (user) {
query.andWhere('note.userId != :userId', { userId: user.id });
diff --git a/src/server/api/define.ts b/src/server/api/define.ts
index 1c7ee26479..4e59357c13 100644
--- a/src/server/api/define.ts
+++ b/src/server/api/define.ts
@@ -18,6 +18,7 @@ type executor<T extends IEndpointMeta> =
(params: Params<T>, user: T['requireCredential'] extends true ? ILocalUser : ILocalUser | null, token: AccessToken | null, file?: any, cleanup?: Function) =>
Promise<T['res'] extends undefined ? Response : SchemaType<NonNullable<T['res']>>>;
+// TODO: API関数に user まるごと渡すのではなくユーザーIDなどの最小限のプロパティだけ渡すようにしたい(キャッシュとか考えないでよくなるため)
export default function <T extends IEndpointMeta>(meta: T, cb: executor<T>)
: (params: any, user: T['requireCredential'] extends true ? ILocalUser : ILocalUser | null, token: AccessToken | null, file?: any) => Promise<any> {
return (params: any, user: T['requireCredential'] extends true ? ILocalUser : ILocalUser | null, token: AccessToken | null, file?: any) => {
diff --git a/src/server/api/endpoints/admin/invite.ts b/src/server/api/endpoints/admin/invite.ts
index 4529d16adf..987105791f 100644
--- a/src/server/api/endpoints/admin/invite.ts
+++ b/src/server/api/endpoints/admin/invite.ts
@@ -38,7 +38,7 @@ export default define(meta, async () => {
chars: '2-9A-HJ-NP-Z', // [0-9A-Z] w/o [01IO] (32 patterns)
});
- await RegistrationTickets.save({
+ await RegistrationTickets.insert({
id: genId(),
createdAt: new Date(),
code,
diff --git a/src/server/api/endpoints/admin/promo/create.ts b/src/server/api/endpoints/admin/promo/create.ts
index 8b96d563c2..aa22e68ebd 100644
--- a/src/server/api/endpoints/admin/promo/create.ts
+++ b/src/server/api/endpoints/admin/promo/create.ts
@@ -53,7 +53,7 @@ export default define(meta, async (ps, user) => {
throw new ApiError(meta.errors.alreadyPromoted);
}
- await PromoNotes.save({
+ await PromoNotes.insert({
noteId: note.id,
createdAt: new Date(),
expiresAt: new Date(ps.expiresAt),
diff --git a/src/server/api/endpoints/antennas/notes.ts b/src/server/api/endpoints/antennas/notes.ts
index 750fc080cf..6fd3cf2df5 100644
--- a/src/server/api/endpoints/antennas/notes.ts
+++ b/src/server/api/endpoints/antennas/notes.ts
@@ -73,7 +73,11 @@ export default define(meta, async (ps, user) => {
const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId)
.andWhere(`note.id IN (${ antennaQuery.getQuery() })`)
- .leftJoinAndSelect('note.user', 'user')
+ .innerJoinAndSelect('note.user', 'user')
+ .leftJoinAndSelect('note.reply', 'reply')
+ .leftJoinAndSelect('note.renote', 'renote')
+ .leftJoinAndSelect('reply.user', 'replyUser')
+ .leftJoinAndSelect('renote.user', 'renoteUser')
.setParameters(antennaQuery.getParameters());
generateVisibilityQuery(query, user);
diff --git a/src/server/api/endpoints/auth/accept.ts b/src/server/api/endpoints/auth/accept.ts
index 6d4d31fa1e..444053a946 100644
--- a/src/server/api/endpoints/auth/accept.ts
+++ b/src/server/api/endpoints/auth/accept.ts
@@ -58,7 +58,7 @@ export default define(meta, async (ps, user) => {
const now = new Date();
// Insert access token doc
- await AccessTokens.save({
+ await AccessTokens.insert({
id: genId(),
createdAt: now,
lastUsedAt: now,
diff --git a/src/server/api/endpoints/channels/follow.ts b/src/server/api/endpoints/channels/follow.ts
index bf2f2bbb57..c5976a8a34 100644
--- a/src/server/api/endpoints/channels/follow.ts
+++ b/src/server/api/endpoints/channels/follow.ts
@@ -4,6 +4,7 @@ import define from '../../define';
import { ApiError } from '../../error';
import { Channels, ChannelFollowings } from '../../../../models';
import { genId } from '../../../../misc/gen-id';
+import { publishUserEvent } from '../../../../services/stream';
export const meta = {
tags: ['channels'],
@@ -36,10 +37,12 @@ export default define(meta, async (ps, user) => {
throw new ApiError(meta.errors.noSuchChannel);
}
- await ChannelFollowings.save({
+ await ChannelFollowings.insert({
id: genId(),
createdAt: new Date(),
followerId: user.id,
followeeId: channel.id,
});
+
+ publishUserEvent(user.id, 'followChannel', channel);
});
diff --git a/src/server/api/endpoints/channels/timeline.ts b/src/server/api/endpoints/channels/timeline.ts
index acb34f124d..00a7cd86d5 100644
--- a/src/server/api/endpoints/channels/timeline.ts
+++ b/src/server/api/endpoints/channels/timeline.ts
@@ -87,7 +87,11 @@ export default define(meta, async (ps, user) => {
//#region Construct query
const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate)
.andWhere('note.channelId = :channelId', { channelId: channel.id })
- .leftJoinAndSelect('note.user', 'user')
+ .innerJoinAndSelect('note.user', 'user')
+ .leftJoinAndSelect('note.reply', 'reply')
+ .leftJoinAndSelect('note.renote', 'renote')
+ .leftJoinAndSelect('reply.user', 'replyUser')
+ .leftJoinAndSelect('renote.user', 'renoteUser')
.leftJoinAndSelect('note.channel', 'channel');
//#endregion
diff --git a/src/server/api/endpoints/channels/unfollow.ts b/src/server/api/endpoints/channels/unfollow.ts
index 8cab5c36a6..3eb0f1519b 100644
--- a/src/server/api/endpoints/channels/unfollow.ts
+++ b/src/server/api/endpoints/channels/unfollow.ts
@@ -3,6 +3,7 @@ import { ID } from '../../../../misc/cafy-id';
import define from '../../define';
import { ApiError } from '../../error';
import { Channels, ChannelFollowings } from '../../../../models';
+import { publishUserEvent } from '../../../../services/stream';
export const meta = {
tags: ['channels'],
@@ -39,4 +40,6 @@ export default define(meta, async (ps, user) => {
followerId: user.id,
followeeId: channel.id,
});
+
+ publishUserEvent(user.id, 'unfollowChannel', channel);
});
diff --git a/src/server/api/endpoints/clips/add-note.ts b/src/server/api/endpoints/clips/add-note.ts
index 4f5cc649e3..ee6a117b2d 100644
--- a/src/server/api/endpoints/clips/add-note.ts
+++ b/src/server/api/endpoints/clips/add-note.ts
@@ -68,7 +68,7 @@ export default define(meta, async (ps, user) => {
throw new ApiError(meta.errors.alreadyClipped);
}
- await ClipNotes.save({
+ await ClipNotes.insert({
id: genId(),
noteId: note.id,
clipId: clip.id
diff --git a/src/server/api/endpoints/clips/notes.ts b/src/server/api/endpoints/clips/notes.ts
index 6a507e2036..676629c328 100644
--- a/src/server/api/endpoints/clips/notes.ts
+++ b/src/server/api/endpoints/clips/notes.ts
@@ -71,7 +71,11 @@ export default define(meta, async (ps, user) => {
const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId)
.andWhere(`note.id IN (${ clipQuery.getQuery() })`)
- .leftJoinAndSelect('note.user', 'user')
+ .innerJoinAndSelect('note.user', 'user')
+ .leftJoinAndSelect('note.reply', 'reply')
+ .leftJoinAndSelect('note.renote', 'renote')
+ .leftJoinAndSelect('reply.user', 'replyUser')
+ .leftJoinAndSelect('renote.user', 'renoteUser')
.setParameters(clipQuery.getParameters());
if (user) {
diff --git a/src/server/api/endpoints/i.ts b/src/server/api/endpoints/i.ts
index e5b65e0930..87f6ae778d 100644
--- a/src/server/api/endpoints/i.ts
+++ b/src/server/api/endpoints/i.ts
@@ -1,6 +1,5 @@
import define from '../define';
-import { RegistryItems, UserProfiles, Users } from '../../../models';
-import { genId } from '../../../misc/gen-id';
+import { Users } from '../../../models';
export const meta = {
desc: {
@@ -23,28 +22,8 @@ export const meta = {
export default define(meta, async (ps, user, token) => {
const isSecure = token == null;
- // TODO: そのうち消す
- const profile = await UserProfiles.findOneOrFail(user.id);
- for (const [k, v] of Object.entries(profile.clientData)) {
- await RegistryItems.insert({
- id: genId(),
- createdAt: new Date(),
- updatedAt: new Date(),
- userId: user.id,
- domain: null,
- scope: ['client', 'base'],
- key: k,
- value: v
- });
- }
- await UserProfiles.createQueryBuilder().update()
- .set({
- clientData: {},
- })
- .where('userId = :id', { id: user.id })
- .execute();
-
- return await Users.pack(user, user, {
+ // ここで渡ってきている user はキャッシュされていて古い可能性もあるので id だけ渡す
+ return await Users.pack(user.id, user, {
detail: true,
includeSecrets: isSecure
});
diff --git a/src/server/api/endpoints/i/notifications.ts b/src/server/api/endpoints/i/notifications.ts
index 0e09bc73fd..812a4bd1dd 100644
--- a/src/server/api/endpoints/i/notifications.ts
+++ b/src/server/api/endpoints/i/notifications.ts
@@ -85,7 +85,13 @@ export default define(meta, async (ps, user) => {
const query = makePaginationQuery(Notifications.createQueryBuilder('notification'), ps.sinceId, ps.untilId)
.andWhere(`notification.notifieeId = :meId`, { meId: user.id })
- .leftJoinAndSelect('notification.notifier', 'notifier');
+ .leftJoinAndSelect('notification.notifier', 'notifier')
+ .leftJoinAndSelect('notification.note', 'note')
+ .leftJoinAndSelect('note.user', 'user')
+ .leftJoinAndSelect('note.reply', 'reply')
+ .leftJoinAndSelect('note.renote', 'renote')
+ .leftJoinAndSelect('reply.user', 'replyUser')
+ .leftJoinAndSelect('renote.user', 'renoteUser');
query.andWhere(`notification.notifierId NOT IN (${ mutingQuery.getQuery() })`);
query.setParameters(mutingQuery.getParameters());
@@ -110,5 +116,5 @@ export default define(meta, async (ps, user) => {
readNotification(user.id, notifications.map(x => x.id));
}
- return await Notifications.packMany(notifications);
+ return await Notifications.packMany(notifications, user.id);
});
diff --git a/src/server/api/endpoints/i/read-announcement.ts b/src/server/api/endpoints/i/read-announcement.ts
index 4a4a021af9..d6acb3d2e6 100644
--- a/src/server/api/endpoints/i/read-announcement.ts
+++ b/src/server/api/endpoints/i/read-announcement.ts
@@ -52,7 +52,7 @@ export default define(meta, async (ps, user) => {
}
// Create read
- await AnnouncementReads.save({
+ await AnnouncementReads.insert({
id: genId(),
createdAt: new Date(),
announcementId: ps.announcementId,
diff --git a/src/server/api/endpoints/i/update.ts b/src/server/api/endpoints/i/update.ts
index a1faf8f1c2..92be2e9e6d 100644
--- a/src/server/api/endpoints/i/update.ts
+++ b/src/server/api/endpoints/i/update.ts
@@ -1,6 +1,6 @@
import $ from 'cafy';
import { ID } from '../../../../misc/cafy-id';
-import { publishMainStream } from '../../../../services/stream';
+import { publishMainStream, publishUserEvent } from '../../../../services/stream';
import acceptAllFollowRequests from '../../../../services/following/requests/accept-all';
import { publishToFollowers } from '../../../../services/i/update';
import define from '../../define';
@@ -317,6 +317,7 @@ export default define(meta, async (ps, user, token) => {
// Publish meUpdated event
publishMainStream(user.id, 'meUpdated', iObj);
+ publishUserEvent(user.id, 'updateUserProfile', await UserProfiles.findOne(user.id));
// 鍵垢を解除したとき、溜まっていたフォローリクエストがあるならすべて承認
if (user.isLocked && ps.isLocked === false) {
diff --git a/src/server/api/endpoints/miauth/gen-token.ts b/src/server/api/endpoints/miauth/gen-token.ts
index 0634debb1e..401ed16389 100644
--- a/src/server/api/endpoints/miauth/gen-token.ts
+++ b/src/server/api/endpoints/miauth/gen-token.ts
@@ -52,7 +52,7 @@ export default define(meta, async (ps, user) => {
const now = new Date();
// Insert access token doc
- await AccessTokens.save({
+ await AccessTokens.insert({
id: genId(),
createdAt: now,
lastUsedAt: now,
diff --git a/src/server/api/endpoints/mute/create.ts b/src/server/api/endpoints/mute/create.ts
index 437ad96107..ebfc6028ed 100644
--- a/src/server/api/endpoints/mute/create.ts
+++ b/src/server/api/endpoints/mute/create.ts
@@ -6,6 +6,7 @@ import { getUser } from '../../common/getters';
import { genId } from '../../../../misc/gen-id';
import { Mutings, NoteWatchings } from '../../../../models';
import { Muting } from '../../../../models/entities/muting';
+import { publishUserEvent } from '../../../../services/stream';
export const meta = {
desc: {
@@ -82,6 +83,8 @@ export default define(meta, async (ps, user) => {
muteeId: mutee.id,
} as Muting);
+ publishUserEvent(user.id, 'mute', mutee);
+
NoteWatchings.delete({
userId: muter.id,
noteUserId: mutee.id
diff --git a/src/server/api/endpoints/mute/delete.ts b/src/server/api/endpoints/mute/delete.ts
index 217352acb4..67a59e3ae4 100644
--- a/src/server/api/endpoints/mute/delete.ts
+++ b/src/server/api/endpoints/mute/delete.ts
@@ -4,6 +4,7 @@ import define from '../../define';
import { ApiError } from '../../error';
import { getUser } from '../../common/getters';
import { Mutings } from '../../../../models';
+import { publishUserEvent } from '../../../../services/stream';
export const meta = {
desc: {
@@ -76,4 +77,6 @@ export default define(meta, async (ps, user) => {
await Mutings.delete({
id: exist.id
});
+
+ publishUserEvent(user.id, 'unmute', mutee);
});
diff --git a/src/server/api/endpoints/notes.ts b/src/server/api/endpoints/notes.ts
index fab8455d78..30e6e92fec 100644
--- a/src/server/api/endpoints/notes.ts
+++ b/src/server/api/endpoints/notes.ts
@@ -76,7 +76,11 @@ export default define(meta, async (ps) => {
const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId)
.andWhere(`note.visibility = 'public'`)
.andWhere(`note.localOnly = FALSE`)
- .leftJoinAndSelect('note.user', 'user');
+ .innerJoinAndSelect('note.user', 'user')
+ .leftJoinAndSelect('note.reply', 'reply')
+ .leftJoinAndSelect('note.renote', 'renote')
+ .leftJoinAndSelect('reply.user', 'replyUser')
+ .leftJoinAndSelect('renote.user', 'renoteUser');
if (ps.local) {
query.andWhere('note.userHost IS NULL');
diff --git a/src/server/api/endpoints/notes/children.ts b/src/server/api/endpoints/notes/children.ts
index 0875e0f240..072a25e024 100644
--- a/src/server/api/endpoints/notes/children.ts
+++ b/src/server/api/endpoints/notes/children.ts
@@ -64,7 +64,11 @@ export default define(meta, async (ps, user) => {
}));
}));
}))
- .leftJoinAndSelect('note.user', 'user');
+ .innerJoinAndSelect('note.user', 'user')
+ .leftJoinAndSelect('note.reply', 'reply')
+ .leftJoinAndSelect('note.renote', 'renote')
+ .leftJoinAndSelect('reply.user', 'replyUser')
+ .leftJoinAndSelect('renote.user', 'renoteUser');
generateVisibilityQuery(query, user);
if (user) generateMutedUserQuery(query, user);
diff --git a/src/server/api/endpoints/notes/favorites/create.ts b/src/server/api/endpoints/notes/favorites/create.ts
index 952bbfd0eb..d66ce37a46 100644
--- a/src/server/api/endpoints/notes/favorites/create.ts
+++ b/src/server/api/endpoints/notes/favorites/create.ts
@@ -61,7 +61,7 @@ export default define(meta, async (ps, user) => {
}
// Create favorite
- await NoteFavorites.save({
+ await NoteFavorites.insert({
id: genId(),
createdAt: new Date(),
noteId: note.id,
diff --git a/src/server/api/endpoints/notes/featured.ts b/src/server/api/endpoints/notes/featured.ts
index 4dda7d0edb..b3dffa4272 100644
--- a/src/server/api/endpoints/notes/featured.ts
+++ b/src/server/api/endpoints/notes/featured.ts
@@ -49,7 +49,11 @@ export default define(meta, async (ps, user) => {
.andWhere(`note.score > 0`)
.andWhere(`note.createdAt > :date`, { date: new Date(Date.now() - day) })
.andWhere(`note.visibility = 'public'`)
- .leftJoinAndSelect('note.user', 'user');
+ .innerJoinAndSelect('note.user', 'user')
+ .leftJoinAndSelect('note.reply', 'reply')
+ .leftJoinAndSelect('note.renote', 'renote')
+ .leftJoinAndSelect('reply.user', 'replyUser')
+ .leftJoinAndSelect('renote.user', 'renoteUser');
if (user) generateMutedUserQuery(query, user);
diff --git a/src/server/api/endpoints/notes/global-timeline.ts b/src/server/api/endpoints/notes/global-timeline.ts
index 6d99f1fdbc..64fc3cbf6c 100644
--- a/src/server/api/endpoints/notes/global-timeline.ts
+++ b/src/server/api/endpoints/notes/global-timeline.ts
@@ -8,8 +8,6 @@ import { Notes } from '../../../../models';
import { generateMutedUserQuery } from '../../common/generate-muted-user-query';
import { activeUsersChart } from '../../../../services/chart';
import { generateRepliesQuery } from '../../common/generate-replies-query';
-import { injectPromo } from '../../common/inject-promo';
-import { injectFeatured } from '../../common/inject-featured';
import { generateMutedNoteQuery } from '../../common/generate-muted-note-query';
export const meta = {
@@ -81,7 +79,11 @@ export default define(meta, async (ps, user) => {
ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate)
.andWhere('note.visibility = \'public\'')
.andWhere('note.channelId IS NULL')
- .leftJoinAndSelect('note.user', 'user');
+ .innerJoinAndSelect('note.user', 'user')
+ .leftJoinAndSelect('note.reply', 'reply')
+ .leftJoinAndSelect('note.renote', 'renote')
+ .leftJoinAndSelect('reply.user', 'replyUser')
+ .leftJoinAndSelect('renote.user', 'renoteUser');
generateRepliesQuery(query, user);
if (user) generateMutedUserQuery(query, user);
@@ -94,9 +96,6 @@ export default define(meta, async (ps, user) => {
const timeline = await query.take(ps.limit!).getMany();
- await injectPromo(timeline, user);
- await injectFeatured(timeline, user);
-
process.nextTick(() => {
if (user) {
activeUsersChart.update(user);
diff --git a/src/server/api/endpoints/notes/hybrid-timeline.ts b/src/server/api/endpoints/notes/hybrid-timeline.ts
index 2b91b8c67b..19c4593f5b 100644
--- a/src/server/api/endpoints/notes/hybrid-timeline.ts
+++ b/src/server/api/endpoints/notes/hybrid-timeline.ts
@@ -10,8 +10,6 @@ import { generateVisibilityQuery } from '../../common/generate-visibility-query'
import { generateMutedUserQuery } from '../../common/generate-muted-user-query';
import { activeUsersChart } from '../../../../services/chart';
import { generateRepliesQuery } from '../../common/generate-replies-query';
-import { injectPromo } from '../../common/inject-promo';
-import { injectFeatured } from '../../common/inject-featured';
import { generateMutedNoteQuery } from '../../common/generate-muted-note-query';
import { generateChannelQuery } from '../../common/generate-channel-query';
@@ -129,7 +127,11 @@ export default define(meta, async (ps, user) => {
qb.where(`((note.userId IN (${ followingQuery.getQuery() })) OR (note.userId = :meId))`, { meId: user.id })
.orWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)');
}))
- .leftJoinAndSelect('note.user', 'user')
+ .innerJoinAndSelect('note.user', 'user')
+ .leftJoinAndSelect('note.reply', 'reply')
+ .leftJoinAndSelect('note.renote', 'renote')
+ .leftJoinAndSelect('reply.user', 'replyUser')
+ .leftJoinAndSelect('renote.user', 'renoteUser')
.setParameters(followingQuery.getParameters());
generateChannelQuery(query, user);
@@ -175,9 +177,6 @@ export default define(meta, async (ps, user) => {
const timeline = await query.take(ps.limit!).getMany();
- await injectPromo(timeline, user);
- await injectFeatured(timeline, user);
-
process.nextTick(() => {
if (user) {
activeUsersChart.update(user);
diff --git a/src/server/api/endpoints/notes/local-timeline.ts b/src/server/api/endpoints/notes/local-timeline.ts
index 51e35e6241..546d3619f7 100644
--- a/src/server/api/endpoints/notes/local-timeline.ts
+++ b/src/server/api/endpoints/notes/local-timeline.ts
@@ -10,8 +10,6 @@ import { generateVisibilityQuery } from '../../common/generate-visibility-query'
import { activeUsersChart } from '../../../../services/chart';
import { Brackets } from 'typeorm';
import { generateRepliesQuery } from '../../common/generate-replies-query';
-import { injectPromo } from '../../common/inject-promo';
-import { injectFeatured } from '../../common/inject-featured';
import { generateMutedNoteQuery } from '../../common/generate-muted-note-query';
import { generateChannelQuery } from '../../common/generate-channel-query';
@@ -98,7 +96,11 @@ export default define(meta, async (ps, user) => {
const query = makePaginationQuery(Notes.createQueryBuilder('note'),
ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate)
.andWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)')
- .leftJoinAndSelect('note.user', 'user');
+ .innerJoinAndSelect('note.user', 'user')
+ .leftJoinAndSelect('note.reply', 'reply')
+ .leftJoinAndSelect('note.renote', 'renote')
+ .leftJoinAndSelect('reply.user', 'replyUser')
+ .leftJoinAndSelect('renote.user', 'renoteUser');
generateChannelQuery(query, user);
generateRepliesQuery(query, user);
@@ -128,9 +130,6 @@ export default define(meta, async (ps, user) => {
const timeline = await query.take(ps.limit!).getMany();
- await injectPromo(timeline, user);
- await injectFeatured(timeline, user);
-
process.nextTick(() => {
if (user) {
activeUsersChart.update(user);
diff --git a/src/server/api/endpoints/notes/mentions.ts b/src/server/api/endpoints/notes/mentions.ts
index 8a9d295d38..30368ea578 100644
--- a/src/server/api/endpoints/notes/mentions.ts
+++ b/src/server/api/endpoints/notes/mentions.ts
@@ -63,7 +63,11 @@ export default define(meta, async (ps, user) => {
.where(`:meId = ANY(note.mentions)`, { meId: user.id })
.orWhere(`:meId = ANY(note.visibleUserIds)`, { meId: user.id });
}))
- .leftJoinAndSelect('note.user', 'user');
+ .innerJoinAndSelect('note.user', 'user')
+ .leftJoinAndSelect('note.reply', 'reply')
+ .leftJoinAndSelect('note.renote', 'renote')
+ .leftJoinAndSelect('reply.user', 'replyUser')
+ .leftJoinAndSelect('renote.user', 'renoteUser');
generateVisibilityQuery(query, user);
generateMutedUserQuery(query, user);
@@ -79,9 +83,7 @@ export default define(meta, async (ps, user) => {
const mentions = await query.take(ps.limit!).getMany();
- for (const note of mentions) {
- read(user.id, note.id);
- }
+ read(user.id, mentions.map(note => note.id));
return await Notes.packMany(mentions, user);
});
diff --git a/src/server/api/endpoints/notes/renotes.ts b/src/server/api/endpoints/notes/renotes.ts
index 31c24f294a..dcda213918 100644
--- a/src/server/api/endpoints/notes/renotes.ts
+++ b/src/server/api/endpoints/notes/renotes.ts
@@ -68,7 +68,11 @@ export default define(meta, async (ps, user) => {
const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId)
.andWhere(`note.renoteId = :renoteId`, { renoteId: note.id })
- .leftJoinAndSelect('note.user', 'user');
+ .innerJoinAndSelect('note.user', 'user')
+ .leftJoinAndSelect('note.reply', 'reply')
+ .leftJoinAndSelect('note.renote', 'renote')
+ .leftJoinAndSelect('reply.user', 'replyUser')
+ .leftJoinAndSelect('renote.user', 'renoteUser');
generateVisibilityQuery(query, user);
if (user) generateMutedUserQuery(query, user);
diff --git a/src/server/api/endpoints/notes/replies.ts b/src/server/api/endpoints/notes/replies.ts
index 9fad74c78e..6f33e2f233 100644
--- a/src/server/api/endpoints/notes/replies.ts
+++ b/src/server/api/endpoints/notes/replies.ts
@@ -59,7 +59,11 @@ export const meta = {
export default define(meta, async (ps, user) => {
const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId)
.andWhere('note.replyId = :replyId', { replyId: ps.noteId })
- .leftJoinAndSelect('note.user', 'user');
+ .innerJoinAndSelect('note.user', 'user')
+ .leftJoinAndSelect('note.reply', 'reply')
+ .leftJoinAndSelect('note.renote', 'renote')
+ .leftJoinAndSelect('reply.user', 'replyUser')
+ .leftJoinAndSelect('renote.user', 'renoteUser');
generateVisibilityQuery(query, user);
if (user) generateMutedUserQuery(query, user);
diff --git a/src/server/api/endpoints/notes/search-by-tag.ts b/src/server/api/endpoints/notes/search-by-tag.ts
index e0f7f4d62c..47b41d9294 100644
--- a/src/server/api/endpoints/notes/search-by-tag.ts
+++ b/src/server/api/endpoints/notes/search-by-tag.ts
@@ -95,7 +95,11 @@ export const meta = {
export default define(meta, async (ps, me) => {
const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId)
- .leftJoinAndSelect('note.user', 'user');
+ .innerJoinAndSelect('note.user', 'user')
+ .leftJoinAndSelect('note.reply', 'reply')
+ .leftJoinAndSelect('note.renote', 'renote')
+ .leftJoinAndSelect('reply.user', 'replyUser')
+ .leftJoinAndSelect('renote.user', 'renoteUser');
generateVisibilityQuery(query, me);
if (me) generateMutedUserQuery(query, me);
diff --git a/src/server/api/endpoints/notes/search.ts b/src/server/api/endpoints/notes/search.ts
index 1aca056299..230d2b0294 100644
--- a/src/server/api/endpoints/notes/search.ts
+++ b/src/server/api/endpoints/notes/search.ts
@@ -79,7 +79,11 @@ export default define(meta, async (ps, me) => {
query
.andWhere('note.text ILIKE :q', { q: `%${ps.query}%` })
- .leftJoinAndSelect('note.user', 'user');
+ .innerJoinAndSelect('note.user', 'user')
+ .leftJoinAndSelect('note.reply', 'reply')
+ .leftJoinAndSelect('note.renote', 'renote')
+ .leftJoinAndSelect('reply.user', 'replyUser')
+ .leftJoinAndSelect('renote.user', 'renoteUser');
generateVisibilityQuery(query, me);
if (me) generateMutedUserQuery(query, me);
diff --git a/src/server/api/endpoints/notes/timeline.ts b/src/server/api/endpoints/notes/timeline.ts
index f09f3d1733..d025944cc2 100644
--- a/src/server/api/endpoints/notes/timeline.ts
+++ b/src/server/api/endpoints/notes/timeline.ts
@@ -8,8 +8,6 @@ import { generateMutedUserQuery } from '../../common/generate-muted-user-query';
import { activeUsersChart } from '../../../../services/chart';
import { Brackets } from 'typeorm';
import { generateRepliesQuery } from '../../common/generate-replies-query';
-import { injectPromo } from '../../common/inject-promo';
-import { injectFeatured } from '../../common/inject-featured';
import { generateMutedNoteQuery } from '../../common/generate-muted-note-query';
import { generateChannelQuery } from '../../common/generate-channel-query';
@@ -122,7 +120,11 @@ export default define(meta, async (ps, user) => {
.where('note.userId = :meId', { meId: user.id });
if (hasFollowing) qb.orWhere(`note.userId IN (${ followingQuery.getQuery() })`);
}))
- .leftJoinAndSelect('note.user', 'user')
+ .innerJoinAndSelect('note.user', 'user')
+ .leftJoinAndSelect('note.reply', 'reply')
+ .leftJoinAndSelect('note.renote', 'renote')
+ .leftJoinAndSelect('reply.user', 'replyUser')
+ .leftJoinAndSelect('renote.user', 'renoteUser')
.setParameters(followingQuery.getParameters());
generateChannelQuery(query, user);
@@ -168,9 +170,6 @@ export default define(meta, async (ps, user) => {
const timeline = await query.take(ps.limit!).getMany();
- await injectPromo(timeline, user);
- await injectFeatured(timeline, user);
-
process.nextTick(() => {
if (user) {
activeUsersChart.update(user);
diff --git a/src/server/api/endpoints/notes/user-list-timeline.ts b/src/server/api/endpoints/notes/user-list-timeline.ts
index b0ff499d95..9ffb38bddc 100644
--- a/src/server/api/endpoints/notes/user-list-timeline.ts
+++ b/src/server/api/endpoints/notes/user-list-timeline.ts
@@ -130,7 +130,11 @@ export default define(meta, async (ps, user) => {
const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId)
.andWhere(`note.userId IN (${ listQuery.getQuery() })`)
- .leftJoinAndSelect('note.user', 'user')
+ .innerJoinAndSelect('note.user', 'user')
+ .leftJoinAndSelect('note.reply', 'reply')
+ .leftJoinAndSelect('note.renote', 'renote')
+ .leftJoinAndSelect('reply.user', 'replyUser')
+ .leftJoinAndSelect('renote.user', 'renoteUser')
.setParameters(listQuery.getParameters());
generateVisibilityQuery(query, user);
diff --git a/src/server/api/endpoints/pages/like.ts b/src/server/api/endpoints/pages/like.ts
index 5c7e13f1c8..3fc2b6ca23 100644
--- a/src/server/api/endpoints/pages/like.ts
+++ b/src/server/api/endpoints/pages/like.ts
@@ -68,7 +68,7 @@ export default define(meta, async (ps, user) => {
}
// Create like
- await PageLikes.save({
+ await PageLikes.insert({
id: genId(),
createdAt: new Date(),
pageId: page.id,
diff --git a/src/server/api/endpoints/promo/read.ts b/src/server/api/endpoints/promo/read.ts
index 57eb0681e5..63c90e5d7f 100644
--- a/src/server/api/endpoints/promo/read.ts
+++ b/src/server/api/endpoints/promo/read.ts
@@ -46,7 +46,7 @@ export default define(meta, async (ps, user) => {
return;
}
- await PromoReads.save({
+ await PromoReads.insert({
id: genId(),
createdAt: new Date(),
noteId: note.id,
diff --git a/src/server/api/endpoints/sw/register.ts b/src/server/api/endpoints/sw/register.ts
index ceb70a9274..9fc70b5609 100644
--- a/src/server/api/endpoints/sw/register.ts
+++ b/src/server/api/endpoints/sw/register.ts
@@ -58,7 +58,7 @@ export default define(meta, async (ps, user) => {
};
}
- await SwSubscriptions.save({
+ await SwSubscriptions.insert({
id: genId(),
createdAt: new Date(),
userId: user.id,
diff --git a/src/server/api/endpoints/users/followers.ts b/src/server/api/endpoints/users/followers.ts
index bd4a2739c6..fb83d7beb8 100644
--- a/src/server/api/endpoints/users/followers.ts
+++ b/src/server/api/endpoints/users/followers.ts
@@ -76,7 +76,8 @@ export default define(meta, async (ps, me) => {
}
const query = makePaginationQuery(Followings.createQueryBuilder('following'), ps.sinceId, ps.untilId)
- .andWhere(`following.followeeId = :userId`, { userId: user.id });
+ .andWhere(`following.followeeId = :userId`, { userId: user.id })
+ .innerJoinAndSelect('following.follower', 'follower');
const followings = await query
.take(ps.limit!)
diff --git a/src/server/api/endpoints/users/following.ts b/src/server/api/endpoints/users/following.ts
index 9efb8bfc93..d5e8dc1f92 100644
--- a/src/server/api/endpoints/users/following.ts
+++ b/src/server/api/endpoints/users/following.ts
@@ -76,7 +76,8 @@ export default define(meta, async (ps, me) => {
}
const query = makePaginationQuery(Followings.createQueryBuilder('following'), ps.sinceId, ps.untilId)
- .andWhere(`following.followerId = :userId`, { userId: user.id });
+ .andWhere(`following.followerId = :userId`, { userId: user.id })
+ .innerJoinAndSelect('following.followee', 'followee');
const followings = await query
.take(ps.limit!)
diff --git a/src/server/api/endpoints/users/groups/create.ts b/src/server/api/endpoints/users/groups/create.ts
index ca011d5cd6..78d2714874 100644
--- a/src/server/api/endpoints/users/groups/create.ts
+++ b/src/server/api/endpoints/users/groups/create.ts
@@ -39,7 +39,7 @@ export default define(meta, async (ps, user) => {
} as UserGroup);
// Push the owner
- await UserGroupJoinings.save({
+ await UserGroupJoinings.insert({
id: genId(),
createdAt: new Date(),
userId: user.id,
diff --git a/src/server/api/endpoints/users/groups/invitations/accept.ts b/src/server/api/endpoints/users/groups/invitations/accept.ts
index e86709f83b..2fa22bcf7e 100644
--- a/src/server/api/endpoints/users/groups/invitations/accept.ts
+++ b/src/server/api/endpoints/users/groups/invitations/accept.ts
@@ -52,7 +52,7 @@ export default define(meta, async (ps, user) => {
}
// Push the user
- await UserGroupJoinings.save({
+ await UserGroupJoinings.insert({
id: genId(),
createdAt: new Date(),
userId: user.id,
diff --git a/src/server/api/endpoints/users/notes.ts b/src/server/api/endpoints/users/notes.ts
index 33e3ecb03f..fc5998c378 100644
--- a/src/server/api/endpoints/users/notes.ts
+++ b/src/server/api/endpoints/users/notes.ts
@@ -131,7 +131,11 @@ export default define(meta, async (ps, me) => {
//#region Construct query
const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate)
.andWhere('note.userId = :userId', { userId: user.id })
- .leftJoinAndSelect('note.user', 'user');
+ .innerJoinAndSelect('note.user', 'user')
+ .leftJoinAndSelect('note.reply', 'reply')
+ .leftJoinAndSelect('note.renote', 'renote')
+ .leftJoinAndSelect('reply.user', 'replyUser')
+ .leftJoinAndSelect('renote.user', 'renoteUser');
generateVisibilityQuery(query, me);
if (me) generateMutedUserQuery(query, me, user);
diff --git a/src/server/api/private/signin.ts b/src/server/api/private/signin.ts
index 7a5efc6cc9..d8f2e6d516 100644
--- a/src/server/api/private/signin.ts
+++ b/src/server/api/private/signin.ts
@@ -53,7 +53,7 @@ export default async (ctx: Koa.Context) => {
async function fail(status?: number, failure?: { error: string }) {
// Append signin history
- await Signins.save({
+ await Signins.insert({
id: genId(),
createdAt: new Date(),
userId: user.id,
@@ -198,7 +198,7 @@ export default async (ctx: Koa.Context) => {
const challengeId = genId();
- await AttestationChallenges.save({
+ await AttestationChallenges.insert({
userId: user.id,
id: challengeId,
challenge: hash(Buffer.from(challenge, 'utf-8')).toString('hex'),
diff --git a/src/server/api/stream/channels/antenna.ts b/src/server/api/stream/channels/antenna.ts
index b5a792f814..36a474f2ac 100644
--- a/src/server/api/stream/channels/antenna.ts
+++ b/src/server/api/stream/channels/antenna.ts
@@ -27,6 +27,8 @@ export default class extends Channel {
// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
if (isMutedUserRelated(note, this.muting)) return;
+ this.connection.cacheNote(note);
+
this.send('note', note);
} else {
this.send(type, body);
diff --git a/src/server/api/stream/channels/channel.ts b/src/server/api/stream/channels/channel.ts
index aa570d1ef4..47a52465b2 100644
--- a/src/server/api/stream/channels/channel.ts
+++ b/src/server/api/stream/channels/channel.ts
@@ -43,6 +43,8 @@ export default class extends Channel {
// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
if (isMutedUserRelated(note, this.muting)) return;
+ this.connection.cacheNote(note);
+
this.send('note', note);
}
diff --git a/src/server/api/stream/channels/global-timeline.ts b/src/server/api/stream/channels/global-timeline.ts
index 8c97e67226..8353f45323 100644
--- a/src/server/api/stream/channels/global-timeline.ts
+++ b/src/server/api/stream/channels/global-timeline.ts
@@ -56,6 +56,8 @@ export default class extends Channel {
// そのためレコードが存在するかのチェックでは不十分なので、改めてcheckWordMuteを呼んでいる
if (this.userProfile && await checkWordMute(note, this.user, this.userProfile.mutedWords)) return;
+ this.connection.cacheNote(note);
+
this.send('note', note);
}
diff --git a/src/server/api/stream/channels/hashtag.ts b/src/server/api/stream/channels/hashtag.ts
index 41447039d5..1b7f8efcc1 100644
--- a/src/server/api/stream/channels/hashtag.ts
+++ b/src/server/api/stream/channels/hashtag.ts
@@ -37,6 +37,8 @@ export default class extends Channel {
// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
if (isMutedUserRelated(note, this.muting)) return;
+ this.connection.cacheNote(note);
+
this.send('note', note);
}
diff --git a/src/server/api/stream/channels/home-timeline.ts b/src/server/api/stream/channels/home-timeline.ts
index 6cfa6eae7b..59ba31c316 100644
--- a/src/server/api/stream/channels/home-timeline.ts
+++ b/src/server/api/stream/channels/home-timeline.ts
@@ -64,6 +64,8 @@ export default class extends Channel {
// そのためレコードが存在するかのチェックでは不十分なので、改めてcheckWordMuteを呼んでいる
if (this.userProfile && await checkWordMute(note, this.user, this.userProfile.mutedWords)) return;
+ this.connection.cacheNote(note);
+
this.send('note', note);
}
diff --git a/src/server/api/stream/channels/hybrid-timeline.ts b/src/server/api/stream/channels/hybrid-timeline.ts
index a9e577cacb..9715e9973f 100644
--- a/src/server/api/stream/channels/hybrid-timeline.ts
+++ b/src/server/api/stream/channels/hybrid-timeline.ts
@@ -73,6 +73,8 @@ export default class extends Channel {
// そのためレコードが存在するかのチェックでは不十分なので、改めてcheckWordMuteを呼んでいる
if (this.userProfile && await checkWordMute(note, this.user, this.userProfile.mutedWords)) return;
+ this.connection.cacheNote(note);
+
this.send('note', note);
}
diff --git a/src/server/api/stream/channels/local-timeline.ts b/src/server/api/stream/channels/local-timeline.ts
index a3a5e491fc..e159c72d60 100644
--- a/src/server/api/stream/channels/local-timeline.ts
+++ b/src/server/api/stream/channels/local-timeline.ts
@@ -58,6 +58,8 @@ export default class extends Channel {
// そのためレコードが存在するかのチェックでは不十分なので、改めてcheckWordMuteを呼んでいる
if (this.userProfile && await checkWordMute(note, this.user, this.userProfile.mutedWords)) return;
+ this.connection.cacheNote(note);
+
this.send('note', note);
}
diff --git a/src/server/api/stream/channels/main.ts b/src/server/api/stream/channels/main.ts
index b69c2ec355..780bc0b89f 100644
--- a/src/server/api/stream/channels/main.ts
+++ b/src/server/api/stream/channels/main.ts
@@ -18,18 +18,22 @@ export default class extends Channel {
case 'notification': {
if (this.muting.has(body.userId)) return;
if (body.note && body.note.isHidden) {
- body.note = await Notes.pack(body.note.id, this.user, {
+ const note = await Notes.pack(body.note.id, this.user, {
detail: true
});
+ this.connection.cacheNote(note);
+ body.note = note;
}
break;
}
case 'mention': {
if (this.muting.has(body.userId)) return;
if (body.isHidden) {
- body = await Notes.pack(body.id, this.user, {
+ const note = await Notes.pack(body.id, this.user, {
detail: true
});
+ this.connection.cacheNote(note);
+ body = note;
}
break;
}
diff --git a/src/server/api/stream/index.ts b/src/server/api/stream/index.ts
index c56a0a157b..99ae558696 100644
--- a/src/server/api/stream/index.ts
+++ b/src/server/api/stream/index.ts
@@ -14,6 +14,7 @@ import { AccessToken } from '../../../models/entities/access-token';
import { UserProfile } from '../../../models/entities/user-profile';
import { publishChannelStream, publishGroupMessagingStream, publishMessagingStream } from '../../../services/stream';
import { UserGroup } from '../../../models/entities/user-group';
+import { PackedNote } from '../../../models/repositories/note';
/**
* Main stream connection
@@ -29,10 +30,7 @@ export default class Connection {
public subscriber: EventEmitter;
private channels: Channel[] = [];
private subscribingNotes: any = {};
- private followingClock: ReturnType<typeof setInterval>;
- private mutingClock: ReturnType<typeof setInterval>;
- private followingChannelsClock: ReturnType<typeof setInterval>;
- private userProfileClock: ReturnType<typeof setInterval>;
+ private cachedNotes: PackedNote[] = [];
constructor(
wsConnection: websocket.connection,
@@ -53,16 +51,49 @@ export default class Connection {
if (this.user) {
this.updateFollowing();
- this.followingClock = setInterval(this.updateFollowing, 5000);
-
this.updateMuting();
- this.mutingClock = setInterval(this.updateMuting, 5000);
-
this.updateFollowingChannels();
- this.followingChannelsClock = setInterval(this.updateFollowingChannels, 5000);
-
this.updateUserProfile();
- this.userProfileClock = setInterval(this.updateUserProfile, 5000);
+
+ this.subscriber.on(`user:${this.user.id}`, ({ type, body }) => {
+ this.onUserEvent(type, body);
+ });
+ }
+ }
+
+ @autobind
+ private onUserEvent(type: string, body: any) {
+ switch (type) {
+ case 'follow':
+ this.following.add(body.id);
+ break;
+
+ case 'unfollow':
+ this.following.delete(body.id);
+ break;
+
+ case 'mute':
+ this.muting.add(body.id);
+ break;
+
+ case 'unmute':
+ this.muting.delete(body.id);
+ break;
+
+ case 'followChannel':
+ this.followingChannels.add(body.id);
+ break;
+
+ case 'unfollowChannel':
+ this.followingChannels.delete(body.id);
+ break;
+
+ case 'updateUserProfile':
+ this.userProfile = body;
+ break;
+
+ default:
+ break;
}
}
@@ -86,9 +117,9 @@ export default class Connection {
switch (type) {
case 'api': this.onApiRequest(body); break;
case 'readNotification': this.onReadNotification(body); break;
- case 'subNote': this.onSubscribeNote(body, true); break;
- case 'sn': this.onSubscribeNote(body, true); break; // alias
- case 's': this.onSubscribeNote(body, false); break;
+ case 'subNote': this.onSubscribeNote(body); break;
+ case 's': this.onSubscribeNote(body); break; // alias
+ case 'sr': this.onSubscribeNote(body); this.readNote(body); break;
case 'unsubNote': this.onUnsubscribeNote(body); break;
case 'un': this.onUnsubscribeNote(body); break; // alias
case 'connect': this.onChannelConnectRequested(body); break;
@@ -109,6 +140,48 @@ export default class Connection {
this.sendMessageToWs(type, body);
}
+ @autobind
+ public cacheNote(note: PackedNote) {
+ const add = (note: PackedNote) => {
+ const existIndex = this.cachedNotes.findIndex(n => n.id === note.id);
+ if (existIndex > -1) {
+ this.cachedNotes[existIndex] = note;
+ return;
+ }
+
+ this.cachedNotes.unshift(note);
+ if (this.cachedNotes.length > 32) {
+ this.cachedNotes.splice(32);
+ }
+ };
+
+ add(note);
+ if (note.reply) add(note.reply);
+ if (note.renote) add(note.renote);
+ }
+
+ @autobind
+ private readNote(body: any) {
+ const id = body.id;
+
+ const note = this.cachedNotes.find(n => n.id === id);
+ if (note == null) return;
+
+ if (this.user && (note.userId !== this.user.id)) {
+ if (note.mentions && note.mentions.includes(this.user.id)) {
+ readNote(this.user.id, [note]);
+ } else if (note.visibleUserIds && note.visibleUserIds.includes(this.user.id)) {
+ readNote(this.user.id, [note]);
+ }
+
+ if (this.followingChannels.has(note.channelId)) {
+ // TODO
+ }
+
+ // TODO: アンテナの既読処理
+ }
+ }
+
/**
* APIリクエスト要求時
*/
@@ -145,7 +218,7 @@ export default class Connection {
* 投稿購読要求時
*/
@autobind
- private onSubscribeNote(payload: any, read: boolean) {
+ private onSubscribeNote(payload: any) {
if (!payload.id) return;
if (this.subscribingNotes[payload.id] == null) {
@@ -157,10 +230,6 @@ export default class Connection {
if (this.subscribingNotes[payload.id] === 1) {
this.subscriber.on(`noteStream:${payload.id}`, this.onNoteStreamMessage);
}
-
- if (this.user && read) {
- readNote(this.user.id, payload.id);
- }
}
/**
@@ -335,10 +404,5 @@ export default class Connection {
for (const c of this.channels.filter(c => c.dispose)) {
if (c.dispose) c.dispose();
}
-
- if (this.followingClock) clearInterval(this.followingClock);
- if (this.mutingClock) clearInterval(this.mutingClock);
- if (this.followingChannelsClock) clearInterval(this.followingChannelsClock);
- if (this.userProfileClock) clearInterval(this.userProfileClock);
}
}
diff --git a/src/server/web/index.ts b/src/server/web/index.ts
index 7b0b82eedf..ea356206ff 100644
--- a/src/server/web/index.ts
+++ b/src/server/web/index.ts
@@ -29,6 +29,7 @@ const markdown = MarkdownIt({
});
const staticAssets = `${__dirname}/../../../assets/`;
+const docAssets = `${__dirname}/../../../src/docs/`;
const assets = `${__dirname}/../../assets/`;
// Init app
@@ -44,7 +45,7 @@ app.use(views(__dirname + '/views', {
}));
// Serve favicon
-app.use(favicon(`${__dirname}/../../../assets/favicon.png`));
+app.use(favicon(`${__dirname}/../../../assets/favicon.ico`));
// Common request handler
app.use(async (ctx, next) => {
@@ -65,6 +66,13 @@ router.get('/static-assets/(.*)', async ctx => {
});
});
+router.get('/doc-assets/(.*)', async ctx => {
+ await send(ctx as any, ctx.path.replace('/doc-assets/', ''), {
+ root: docAssets,
+ maxage: ms('7 days'),
+ });
+});
+
router.get('/assets/(.*)', async ctx => {
await send(ctx as any, ctx.path.replace('/assets/', ''), {
root: assets,
@@ -75,7 +83,7 @@ router.get('/assets/(.*)', async ctx => {
// Apple touch icon
router.get('/apple-touch-icon.png', async ctx => {
await send(ctx as any, '/apple-touch-icon.png', {
- root: assets
+ root: staticAssets
});
});
diff --git a/src/server/web/views/flush.pug b/src/server/web/views/flush.pug
index 59fed1f15d..ec585a34db 100644
--- a/src/server/web/views/flush.pug
+++ b/src/server/web/views/flush.pug
@@ -4,35 +4,44 @@ html
#msg
script.
const msg = document.getElementById('msg');
+ const successText = `\nSuccess Flush! <a href="/">Back to Misskey</a>\n成功しました。<a href="/">Misskeyを開き直してください。</a>`;
- try {
- localStorage.clear();
- message('localStorage cleared');
+ message('Start flushing.');
- const delidb = indexedDB.deleteDatabase('MisskeyClient');
- delidb.onsuccess = () => message('indexedDB cleared');
+ (async function() {
+ try {
+ localStorage.clear();
+ message('localStorage cleared.');
- if (navigator.serviceWorker.controller) {
- navigator.serviceWorker.controller.postMessage('clear');
- navigator.serviceWorker.getRegistrations()
- .then(registrations => {
- return Promise.all(registrations.map(registration => registration.unregister()));
- })
- .then(() => {
- message('Success Flush! Please reopen Misskey.\n成功しました。Misskeyを開き直してください。');
- })
- .catch(e => { throw Error(e) });
- } else {
- message('Success Flush! Please reopen Misskey.\n成功しました。Misskeyを開き直してください。');
+ const idbPromises = ['MisskeyClient', 'keyval-store'].map((name, i, arr) => new Promise((res, rej) => {
+ const delidb = indexedDB.deleteDatabase(name);
+ delidb.onsuccess = () => res(message(`indexedDB "${name}" cleared. (${i + 1}/${arr.length})`));
+ delidb.onerror = e => rej(e)
+ }));
+
+ await Promise.all(idbPromises);
+
+ if (navigator.serviceWorker.controller) {
+ navigator.serviceWorker.controller.postMessage('clear');
+ await navigator.serviceWorker.getRegistrations()
+ .then(registrations => {
+ return Promise.all(registrations.map(registration => registration.unregister()));
+ })
+ .catch(e => { throw Error(e) });
+ }
+
+ message(successText);
+ } catch (e) {
+ message(`\n${e}\n\nFlush Failed. <a href="/flush">Please retry.</a>\n失敗しました。<a href="/flush">もう一度試してみてください。</a>`);
+ message(`\nIf you retry more than 3 times, clear the browser cache or contact to instance admin.\n3回以上試しても失敗する場合、ブラウザのキャッシュを消去し、それでもだめならインスタンス管理者に連絡してみてください。\n`)
+
+ console.error(e);
+ setTimeout(() => {
+ location = '/';
+ }, 10000)
}
- } catch (e) {
- console.error(e);
- message(`${e}¥n¥nFlush Failed. Please reopen Misskey.\n失敗しました。Misskeyを開き直してください。`);
- setTimeout(() => {
- location = '/';
- }, 10000)
- }
+ })();
function message(text) {
- msg.insertAdjacentHTML('beforeend', `<p>[${(new Date()).toString()}] ${text.replace(/¥n/g,'<br>')}</p>`)
+ msg.insertAdjacentHTML('beforeend', `<p>[${(new Date()).toString()}] ${text.replace(/\n/g,'<br>')}</p>`)
}