= {
export type SchemaType =
p['type'] extends 'number' ? number :
p['type'] extends 'string' ? string :
- p['type'] extends 'array' ? MyType
[] :
- p['type'] extends 'object' ? ObjType
:
+ p['type'] extends 'array' ? MyType>[] :
+ p['type'] extends 'object' ? ObjType> :
any;
export function convertOpenApiSchema(schema: Schema) {
diff --git a/src/models/entities/poll.ts b/src/models/entities/poll.ts
index c0ad5547bd..6bb67163a2 100644
--- a/src/models/entities/poll.ts
+++ b/src/models/entities/poll.ts
@@ -67,5 +67,5 @@ export type IPoll = {
choices: string[];
votes?: number[];
multiple: boolean;
- expiresAt: Date;
+ expiresAt: Date | null;
};
diff --git a/src/models/repositories/abuse-user-report.ts b/src/models/repositories/abuse-user-report.ts
index c72a582c04..f619d6e37f 100644
--- a/src/models/repositories/abuse-user-report.ts
+++ b/src/models/repositories/abuse-user-report.ts
@@ -2,6 +2,7 @@ import { EntityRepository, Repository } from 'typeorm';
import { Users } from '..';
import rap from '@prezzemolo/rap';
import { AbuseUserReport } from '../entities/abuse-user-report';
+import { ensure } from '../../prelude/ensure';
@EntityRepository(AbuseUserReport)
export class AbuseUserReportRepository extends Repository {
@@ -14,7 +15,7 @@ export class AbuseUserReportRepository extends Repository {
public async pack(
src: AbuseUserReport['id'] | AbuseUserReport,
) {
- const report = typeof src === 'object' ? src : await this.findOne(src);
+ const report = typeof src === 'object' ? src : await this.findOne(src).then(ensure);
return await rap({
id: report.id,
diff --git a/src/models/repositories/app.ts b/src/models/repositories/app.ts
index 2e3323baf8..a0c0cf68cb 100644
--- a/src/models/repositories/app.ts
+++ b/src/models/repositories/app.ts
@@ -1,6 +1,7 @@
import { EntityRepository, Repository } from 'typeorm';
import { App } from '../entities/app';
import { AccessTokens } from '..';
+import { ensure } from '../../prelude/ensure';
@EntityRepository(App)
export class AppRepository extends Repository {
@@ -19,7 +20,7 @@ export class AppRepository extends Repository {
includeProfileImageIds: false
}, options);
- const app = typeof src === 'object' ? src : await this.findOne(src);
+ const app = typeof src === 'object' ? src : await this.findOne(src).then(ensure);
return {
id: app.id,
diff --git a/src/models/repositories/auth-session.ts b/src/models/repositories/auth-session.ts
index 76e3ddf9ab..540c5466f5 100644
--- a/src/models/repositories/auth-session.ts
+++ b/src/models/repositories/auth-session.ts
@@ -2,6 +2,7 @@ import { EntityRepository, Repository } from 'typeorm';
import { Apps } from '..';
import rap from '@prezzemolo/rap';
import { AuthSession } from '../entities/auth-session';
+import { ensure } from '../../prelude/ensure';
@EntityRepository(AuthSession)
export class AuthSessionRepository extends Repository {
@@ -9,7 +10,7 @@ export class AuthSessionRepository extends Repository {
src: AuthSession['id'] | AuthSession,
me?: any
) {
- const session = typeof src === 'object' ? src : await this.findOne(src);
+ const session = typeof src === 'object' ? src : await this.findOne(src).then(ensure);
return await rap({
id: session.id,
diff --git a/src/models/repositories/blocking.ts b/src/models/repositories/blocking.ts
index 81f3866131..e18aa591f3 100644
--- a/src/models/repositories/blocking.ts
+++ b/src/models/repositories/blocking.ts
@@ -2,6 +2,7 @@ import { EntityRepository, Repository } from 'typeorm';
import { Users } from '..';
import rap from '@prezzemolo/rap';
import { Blocking } from '../entities/blocking';
+import { ensure } from '../../prelude/ensure';
@EntityRepository(Blocking)
export class BlockingRepository extends Repository {
@@ -16,7 +17,7 @@ export class BlockingRepository extends Repository {
src: Blocking['id'] | Blocking,
me?: any
) {
- const blocking = typeof src === 'object' ? src : await this.findOne(src);
+ const blocking = typeof src === 'object' ? src : await this.findOne(src).then(ensure);
return await rap({
id: blocking.id,
diff --git a/src/models/repositories/drive-file.ts b/src/models/repositories/drive-file.ts
index 817677fa3b..003e350713 100644
--- a/src/models/repositories/drive-file.ts
+++ b/src/models/repositories/drive-file.ts
@@ -4,6 +4,7 @@ import { Users, DriveFolders } from '..';
import rap from '@prezzemolo/rap';
import { User } from '../entities/user';
import { toPuny } from '../../misc/convert-host';
+import { ensure } from '../../prelude/ensure';
@EntityRepository(DriveFile)
export class DriveFileRepository extends Repository {
@@ -91,7 +92,7 @@ export class DriveFileRepository extends Repository {
self: false
}, options);
- const file = typeof src === 'object' ? src : await this.findOne(src);
+ const file = typeof src === 'object' ? src : await this.findOne(src).then(ensure);
return await rap({
id: file.id,
@@ -108,7 +109,7 @@ export class DriveFileRepository extends Repository {
folder: opts.detail && file.folderId ? DriveFolders.pack(file.folderId, {
detail: true
}) : null,
- user: opts.withUser ? Users.pack(file.userId) : null
+ user: opts.withUser ? Users.pack(file.userId!) : null
});
}
}
diff --git a/src/models/repositories/drive-folder.ts b/src/models/repositories/drive-folder.ts
index faf0f353aa..ce88adefa4 100644
--- a/src/models/repositories/drive-folder.ts
+++ b/src/models/repositories/drive-folder.ts
@@ -2,6 +2,7 @@ import { EntityRepository, Repository } from 'typeorm';
import { DriveFolders, DriveFiles } from '..';
import rap from '@prezzemolo/rap';
import { DriveFolder } from '../entities/drive-folder';
+import { ensure } from '../../prelude/ensure';
@EntityRepository(DriveFolder)
export class DriveFolderRepository extends Repository {
@@ -22,7 +23,7 @@ export class DriveFolderRepository extends Repository {
detail: false
}, options);
- const folder = typeof src === 'object' ? src : await this.findOne(src);
+ const folder = typeof src === 'object' ? src : await this.findOne(src).then(ensure);
return await rap({
id: folder.id,
diff --git a/src/models/repositories/follow-request.ts b/src/models/repositories/follow-request.ts
index bead093b21..451ed8e2d5 100644
--- a/src/models/repositories/follow-request.ts
+++ b/src/models/repositories/follow-request.ts
@@ -1,6 +1,7 @@
import { EntityRepository, Repository } from 'typeorm';
import { FollowRequest } from '../entities/follow-request';
import { Users } from '..';
+import { ensure } from '../../prelude/ensure';
@EntityRepository(FollowRequest)
export class FollowRequestRepository extends Repository {
@@ -8,7 +9,7 @@ export class FollowRequestRepository extends Repository {
src: FollowRequest['id'] | FollowRequest,
me?: any
) {
- const request = typeof src === 'object' ? src : await this.findOne(src);
+ const request = typeof src === 'object' ? src : await this.findOne(src).then(ensure);
return {
id: request.id,
diff --git a/src/models/repositories/following.ts b/src/models/repositories/following.ts
index 02253d272d..3fff57866f 100644
--- a/src/models/repositories/following.ts
+++ b/src/models/repositories/following.ts
@@ -2,9 +2,50 @@ import { EntityRepository, Repository } from 'typeorm';
import { Users } from '..';
import rap from '@prezzemolo/rap';
import { Following } from '../entities/following';
+import { ensure } from '../../prelude/ensure';
+
+type LocalFollowerFollowing = Following & {
+ followerHost: null;
+ followerInbox: null;
+ followerSharedInbox: null;
+};
+
+type RemoteFollowerFollowing = Following & {
+ followerHost: string;
+ followerInbox: string;
+ followerSharedInbox: string;
+};
+
+type LocalFolloweeFollowing = Following & {
+ followeeHost: null;
+ followeeInbox: null;
+ followeeSharedInbox: null;
+};
+
+type RemoteFolloweeFollowing = Following & {
+ followeeHost: string;
+ followeeInbox: string;
+ followeeSharedInbox: string;
+};
@EntityRepository(Following)
export class FollowingRepository extends Repository {
+ public isLocalFollower(following: Following): following is LocalFollowerFollowing {
+ return following.followerHost == null;
+ }
+
+ public isRemoteFollower(following: Following): following is RemoteFollowerFollowing {
+ return following.followerHost != null;
+ }
+
+ public isLocalFollowee(following: Following): following is LocalFolloweeFollowing {
+ return following.followeeHost == null;
+ }
+
+ public isRemoteFollowee(following: Following): following is RemoteFolloweeFollowing {
+ return following.followeeHost != null;
+ }
+
public packMany(
followings: any[],
me?: any,
@@ -24,7 +65,7 @@ export class FollowingRepository extends Repository {
populateFollower?: boolean;
}
) {
- const following = typeof src === 'object' ? src : await this.findOne(src);
+ const following = typeof src === 'object' ? src : await this.findOne(src).then(ensure);
if (opts == null) opts = {};
diff --git a/src/models/repositories/games/reversi/game.ts b/src/models/repositories/games/reversi/game.ts
index f0cb6ff905..c380f5251e 100644
--- a/src/models/repositories/games/reversi/game.ts
+++ b/src/models/repositories/games/reversi/game.ts
@@ -1,6 +1,7 @@
import { EntityRepository, Repository } from 'typeorm';
import { Users } from '../../..';
import { ReversiGame } from '../../../entities/games/reversi/game';
+import { ensure } from '../../../../prelude/ensure';
@EntityRepository(ReversiGame)
export class ReversiGameRepository extends Repository {
@@ -15,7 +16,7 @@ export class ReversiGameRepository extends Repository {
detail: true
}, options);
- const game = typeof src === 'object' ? src : await this.findOne(src);
+ const game = typeof src === 'object' ? src : await this.findOne(src).then(ensure);
const meId = me ? typeof me === 'string' ? me : me.id : null;
return {
diff --git a/src/models/repositories/games/reversi/matching.ts b/src/models/repositories/games/reversi/matching.ts
index 3612ac5c47..4d99c6ef76 100644
--- a/src/models/repositories/games/reversi/matching.ts
+++ b/src/models/repositories/games/reversi/matching.ts
@@ -2,6 +2,7 @@ import { EntityRepository, Repository } from 'typeorm';
import rap from '@prezzemolo/rap';
import { ReversiMatching } from '../../../entities/games/reversi/matching';
import { Users } from '../../..';
+import { ensure } from '../../../../prelude/ensure';
@EntityRepository(ReversiMatching)
export class ReversiMatchingRepository extends Repository {
@@ -9,7 +10,7 @@ export class ReversiMatchingRepository extends Repository {
src: ReversiMatching['id'] | ReversiMatching,
me: any
) {
- const matching = typeof src === 'object' ? src : await this.findOne(src);
+ const matching = typeof src === 'object' ? src : await this.findOne(src).then(ensure);
return await rap({
id: matching.id,
diff --git a/src/models/repositories/messaging-message.ts b/src/models/repositories/messaging-message.ts
index b87b30388a..6659273539 100644
--- a/src/models/repositories/messaging-message.ts
+++ b/src/models/repositories/messaging-message.ts
@@ -1,6 +1,7 @@
import { EntityRepository, Repository } from 'typeorm';
import { MessagingMessage } from '../entities/messaging-message';
import { Users, DriveFiles } from '..';
+import { ensure } from '../../prelude/ensure';
@EntityRepository(MessagingMessage)
export class MessagingMessageRepository extends Repository {
@@ -19,7 +20,7 @@ export class MessagingMessageRepository extends Repository {
populateRecipient: true
};
- const message = typeof src === 'object' ? src : await this.findOne(src);
+ const message = typeof src === 'object' ? src : await this.findOne(src).then(ensure);
return {
id: message.id,
diff --git a/src/models/repositories/muting.ts b/src/models/repositories/muting.ts
index cd98cb4fec..1812e2e713 100644
--- a/src/models/repositories/muting.ts
+++ b/src/models/repositories/muting.ts
@@ -2,6 +2,7 @@ import { EntityRepository, Repository } from 'typeorm';
import { Users } from '..';
import rap from '@prezzemolo/rap';
import { Muting } from '../entities/muting';
+import { ensure } from '../../prelude/ensure';
@EntityRepository(Muting)
export class MutingRepository extends Repository {
@@ -16,7 +17,7 @@ export class MutingRepository extends Repository {
src: Muting['id'] | Muting,
me?: any
) {
- const muting = typeof src === 'object' ? src : await this.findOne(src);
+ const muting = typeof src === 'object' ? src : await this.findOne(src).then(ensure);
return await rap({
id: muting.id,
diff --git a/src/models/repositories/note-favorite.ts b/src/models/repositories/note-favorite.ts
index 4526461e69..f428903c13 100644
--- a/src/models/repositories/note-favorite.ts
+++ b/src/models/repositories/note-favorite.ts
@@ -1,6 +1,7 @@
import { EntityRepository, Repository } from 'typeorm';
import { NoteFavorite } from '../entities/note-favorite';
import { Notes } from '..';
+import { ensure } from '../../prelude/ensure';
@EntityRepository(NoteFavorite)
export class NoteFavoriteRepository extends Repository {
@@ -15,7 +16,7 @@ export class NoteFavoriteRepository extends Repository {
src: NoteFavorite['id'] | NoteFavorite,
me?: any
) {
- const favorite = typeof src === 'object' ? src : await this.findOne(src);
+ const favorite = typeof src === 'object' ? src : await this.findOne(src).then(ensure);
return {
id: favorite.id,
diff --git a/src/models/repositories/note-reaction.ts b/src/models/repositories/note-reaction.ts
index 7189da8e20..28191d4ab0 100644
--- a/src/models/repositories/note-reaction.ts
+++ b/src/models/repositories/note-reaction.ts
@@ -1,6 +1,7 @@
import { EntityRepository, Repository } from 'typeorm';
import { NoteReaction } from '../entities/note-reaction';
import { Users } from '..';
+import { ensure } from '../../prelude/ensure';
@EntityRepository(NoteReaction)
export class NoteReactionRepository extends Repository {
@@ -8,7 +9,7 @@ export class NoteReactionRepository extends Repository {
src: NoteReaction['id'] | NoteReaction,
me?: any
) {
- const reaction = typeof src === 'object' ? src : await this.findOne(src);
+ const reaction = typeof src === 'object' ? src : await this.findOne(src).then(ensure);
return {
id: reaction.id,
diff --git a/src/models/repositories/note.ts b/src/models/repositories/note.ts
index 01bf8cf87c..77cf00849f 100644
--- a/src/models/repositories/note.ts
+++ b/src/models/repositories/note.ts
@@ -5,6 +5,7 @@ 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';
+import { ensure } from '../../prelude/ensure';
@EntityRepository(Note)
export class NoteRepository extends Repository {
@@ -12,7 +13,7 @@ export class NoteRepository extends Repository {
return x.trim().length <= 100;
}
- private async hideNote(packedNote: any, meId: User['id']) {
+ private async hideNote(packedNote: any, meId: User['id'] | null) {
let hide = false;
// visibility が specified かつ自分が指定されていなかったら非表示
@@ -75,7 +76,7 @@ export class NoteRepository extends Repository {
public packMany(
notes: (Note['id'] | Note)[],
- me?: User['id'] | User,
+ me?: User['id'] | User | null | undefined,
options?: {
detail?: boolean;
skipHide?: boolean;
@@ -86,7 +87,7 @@ export class NoteRepository extends Repository {
public async pack(
src: Note['id'] | Note,
- me?: User['id'] | User,
+ me?: User['id'] | User | null | undefined,
options?: {
detail?: boolean;
skipHide?: boolean;
@@ -98,11 +99,11 @@ export class NoteRepository extends Repository {
}, options);
const meId = me ? typeof me === 'string' ? me : me.id : null;
- const note = typeof src === 'object' ? src : await this.findOne(src);
+ const note = typeof src === 'object' ? src : await this.findOne(src).then(ensure);
const host = note.userHost;
async function populatePoll() {
- const poll = await Polls.findOne({ noteId: note.id });
+ const poll = await Polls.findOne({ noteId: note.id }).then(ensure);
const choices = poll.choices.map(c => ({
text: c,
votes: poll.votes[poll.choices.indexOf(c)],
@@ -111,7 +112,7 @@ export class NoteRepository extends Repository {
if (poll.multiple) {
const votes = await PollVotes.find({
- userId: meId,
+ userId: meId!,
noteId: note.id
});
@@ -121,7 +122,7 @@ export class NoteRepository extends Repository {
}
} else {
const vote = await PollVotes.findOne({
- userId: meId,
+ userId: meId!,
noteId: note.id
});
@@ -139,7 +140,7 @@ export class NoteRepository extends Repository {
async function populateMyReaction() {
const reaction = await NoteReactions.findOne({
- userId: meId,
+ userId: meId!,
noteId: note.id,
});
diff --git a/src/models/repositories/notification.ts b/src/models/repositories/notification.ts
index 9bc569cd3f..4781d4c065 100644
--- a/src/models/repositories/notification.ts
+++ b/src/models/repositories/notification.ts
@@ -2,6 +2,7 @@ import { EntityRepository, Repository } from 'typeorm';
import { Users, Notes } from '..';
import rap from '@prezzemolo/rap';
import { Notification } from '../entities/notification';
+import { ensure } from '../../prelude/ensure';
@EntityRepository(Notification)
export class NotificationRepository extends Repository {
@@ -14,7 +15,7 @@ export class NotificationRepository extends Repository {
public async pack(
src: Notification['id'] | Notification,
) {
- const notification = typeof src === 'object' ? src : await this.findOne(src);
+ const notification = typeof src === 'object' ? src : await this.findOne(src).then(ensure);
return await rap({
id: notification.id,
@@ -23,23 +24,23 @@ export class NotificationRepository extends Repository {
userId: notification.notifierId,
user: Users.pack(notification.notifier || notification.notifierId),
...(notification.type === 'mention' ? {
- note: Notes.pack(notification.note || notification.noteId),
+ note: Notes.pack(notification.note || notification.noteId!),
} : {}),
...(notification.type === 'reply' ? {
- note: Notes.pack(notification.note || notification.noteId),
+ note: Notes.pack(notification.note || notification.noteId!),
} : {}),
...(notification.type === 'renote' ? {
- note: Notes.pack(notification.note || notification.noteId),
+ note: Notes.pack(notification.note || notification.noteId!),
} : {}),
...(notification.type === 'quote' ? {
- note: Notes.pack(notification.note || notification.noteId),
+ note: Notes.pack(notification.note || notification.noteId!),
} : {}),
...(notification.type === 'reaction' ? {
- note: Notes.pack(notification.note || notification.noteId),
+ note: Notes.pack(notification.note || notification.noteId!),
reaction: notification.reaction
} : {}),
...(notification.type === 'pollVote' ? {
- note: Notes.pack(notification.note || notification.noteId),
+ note: Notes.pack(notification.note || notification.noteId!),
choice: notification.choice
} : {})
});
diff --git a/src/models/repositories/user-list.ts b/src/models/repositories/user-list.ts
index 921c18ca7a..fbf81b8886 100644
--- a/src/models/repositories/user-list.ts
+++ b/src/models/repositories/user-list.ts
@@ -1,12 +1,13 @@
import { EntityRepository, Repository } from 'typeorm';
import { UserList } from '../entities/user-list';
+import { ensure } from '../../prelude/ensure';
@EntityRepository(UserList)
export class UserListRepository extends Repository {
public async pack(
- src: any,
+ src: UserList['id'] | UserList,
) {
- const userList = typeof src === 'object' ? src : await this.findOne(src);
+ const userList = typeof src === 'object' ? src : await this.findOne(src).then(ensure);
return {
id: userList.id,
diff --git a/src/models/repositories/user.ts b/src/models/repositories/user.ts
index d7f2c3d040..cddb77ffae 100644
--- a/src/models/repositories/user.ts
+++ b/src/models/repositories/user.ts
@@ -2,6 +2,7 @@ import { EntityRepository, Repository, In } from 'typeorm';
import { User, ILocalUser, IRemoteUser } from '../entities/user';
import { Emojis, Notes, NoteUnreads, FollowRequests, Notifications, MessagingMessages, UserNotePinings, Followings, Blockings, Mutings, UserProfiles } from '..';
import rap from '@prezzemolo/rap';
+import { ensure } from '../../prelude/ensure';
@EntityRepository(User)
export class UserRepository extends Repository {
@@ -51,7 +52,7 @@ export class UserRepository extends Repository {
public packMany(
users: (User['id'] | User)[],
- me?: User['id'] | User,
+ me?: User['id'] | User | null | undefined,
options?: {
detail?: boolean,
includeSecrets?: boolean,
@@ -63,7 +64,7 @@ export class UserRepository extends Repository {
public async pack(
src: User['id'] | User,
- me?: User['id'] | User,
+ me?: User['id'] | User | null | undefined,
options?: {
detail?: boolean,
includeSecrets?: boolean,
@@ -75,12 +76,12 @@ export class UserRepository extends Repository {
includeSecrets: false
}, options);
- const user = typeof src === 'object' ? src : await this.findOne(src);
+ const user = typeof src === 'object' ? src : await this.findOne(src).then(ensure);
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 }) : [];
- const profile = opts.detail ? await UserProfiles.findOne({ userId: user.id }) : null;
+ const profile = opts.detail ? await UserProfiles.findOne({ userId: user.id }).then(ensure) : null;
return await rap({
id: user.id,
@@ -117,12 +118,12 @@ export class UserRepository extends Repository {
} : {}),
...(opts.detail ? {
- url: profile.url,
+ url: profile!.url,
createdAt: user.createdAt,
updatedAt: user.updatedAt,
- description: profile.description,
- location: profile.location,
- birthday: profile.birthday,
+ description: profile!.description,
+ location: profile!.location,
+ birthday: profile!.birthday,
followersCount: user.followersCount,
followingCount: user.followingCount,
notesCount: user.notesCount,
@@ -135,9 +136,9 @@ export class UserRepository extends Repository {
...(opts.detail && meId === user.id ? {
avatarId: user.avatarId,
bannerId: user.bannerId,
- autoWatch: profile.autoWatch,
- alwaysMarkNsfw: profile.alwaysMarkNsfw,
- carefulBot: profile.carefulBot,
+ autoWatch: profile!.autoWatch,
+ alwaysMarkNsfw: profile!.alwaysMarkNsfw,
+ carefulBot: profile!.carefulBot,
hasUnreadMessagingMessage: MessagingMessages.count({
where: {
recipientId: user.id,
@@ -158,9 +159,9 @@ export class UserRepository extends Repository {
} : {}),
...(opts.includeSecrets ? {
- clientData: profile.clientData,
- email: profile.email,
- emailVerified: profile.emailVerified,
+ clientData: profile!.clientData,
+ email: profile!.email,
+ emailVerified: profile!.emailVerified,
} : {}),
...(relation ? {
diff --git a/src/prelude/ensure.ts b/src/prelude/ensure.ts
new file mode 100644
index 0000000000..90bf05538a
--- /dev/null
+++ b/src/prelude/ensure.ts
@@ -0,0 +1,7 @@
+export function ensure(x: T): NonNullable {
+ if (x == null) {
+ throw 'ぬるぽ';
+ } else {
+ return x!;
+ }
+}
diff --git a/src/queue/index.ts b/src/queue/index.ts
index 728c43c6ac..1ab59fd18f 100644
--- a/src/queue/index.ts
+++ b/src/queue/index.ts
@@ -12,7 +12,7 @@ import { queueLogger } from './logger';
import { DriveFile } from '../models/entities/drive-file';
function initializeQueue(name: string) {
- return new Queue(name, config.redis != null ? {
+ return new Queue(name, {
redis: {
port: config.redis.port,
host: config.redis.host,
@@ -20,7 +20,7 @@ function initializeQueue(name: string) {
db: config.redis.db || 0,
},
prefix: config.redis.prefix ? `${config.redis.prefix}:queue` : 'queue'
- } : null);
+ });
}
export const deliverQueue = initializeQueue('deliver');
diff --git a/src/queue/processors/db/delete-drive-files.ts b/src/queue/processors/db/delete-drive-files.ts
index 5f347fb588..491734acc6 100644
--- a/src/queue/processors/db/delete-drive-files.ts
+++ b/src/queue/processors/db/delete-drive-files.ts
@@ -10,9 +10,11 @@ const logger = queueLogger.createSubLogger('delete-drive-files');
export async function deleteDriveFiles(job: Bull.Job, done: any): Promise {
logger.info(`Deleting drive files of ${job.data.user.id} ...`);
- const user = await Users.findOne({
- id: job.data.user.id
- });
+ const user = await Users.findOne(job.data.user.id);
+ if (user == null) {
+ done();
+ return;
+ }
let deletedCount = 0;
let ended = false;
diff --git a/src/queue/processors/db/export-blocking.ts b/src/queue/processors/db/export-blocking.ts
index c12aa4fca3..44025ec960 100644
--- a/src/queue/processors/db/export-blocking.ts
+++ b/src/queue/processors/db/export-blocking.ts
@@ -14,9 +14,11 @@ const logger = queueLogger.createSubLogger('export-blocking');
export async function exportBlocking(job: Bull.Job, done: any): Promise {
logger.info(`Exporting blocking of ${job.data.user.id} ...`);
- const user = await Users.findOne({
- id: job.data.user.id
- });
+ const user = await Users.findOne(job.data.user.id);
+ if (user == null) {
+ done();
+ return;
+ }
// Create temp file
const [path, cleanup] = await new Promise<[string, any]>((res, rej) => {
@@ -56,6 +58,10 @@ export async function exportBlocking(job: Bull.Job, done: any): Promise {
for (const block of blockings) {
const u = await Users.findOne({ id: block.blockeeId });
+ if (u == null) {
+ exportedCount++; continue;
+ }
+
const content = getFullApAccount(u.username, u.host);
await new Promise((res, rej) => {
stream.write(content + '\n', err => {
diff --git a/src/queue/processors/db/export-following.ts b/src/queue/processors/db/export-following.ts
index fb30df79fe..81dcf8f93e 100644
--- a/src/queue/processors/db/export-following.ts
+++ b/src/queue/processors/db/export-following.ts
@@ -14,9 +14,11 @@ const logger = queueLogger.createSubLogger('export-following');
export async function exportFollowing(job: Bull.Job, done: any): Promise {
logger.info(`Exporting following of ${job.data.user.id} ...`);
- const user = await Users.findOne({
- id: job.data.user.id
- });
+ const user = await Users.findOne(job.data.user.id);
+ if (user == null) {
+ done();
+ return;
+ }
// Create temp file
const [path, cleanup] = await new Promise<[string, any]>((res, rej) => {
@@ -56,6 +58,10 @@ export async function exportFollowing(job: Bull.Job, done: any): Promise {
for (const following of followings) {
const u = await Users.findOne({ id: following.followeeId });
+ if (u == null) {
+ exportedCount++; continue;
+ }
+
const content = getFullApAccount(u.username, u.host);
await new Promise((res, rej) => {
stream.write(content + '\n', err => {
diff --git a/src/queue/processors/db/export-mute.ts b/src/queue/processors/db/export-mute.ts
index 3aed526dc5..f810b6ee8d 100644
--- a/src/queue/processors/db/export-mute.ts
+++ b/src/queue/processors/db/export-mute.ts
@@ -14,9 +14,11 @@ const logger = queueLogger.createSubLogger('export-mute');
export async function exportMute(job: Bull.Job, done: any): Promise {
logger.info(`Exporting mute of ${job.data.user.id} ...`);
- const user = await Users.findOne({
- id: job.data.user.id
- });
+ const user = await Users.findOne(job.data.user.id);
+ if (user == null) {
+ done();
+ return;
+ }
// Create temp file
const [path, cleanup] = await new Promise<[string, any]>((res, rej) => {
@@ -56,6 +58,10 @@ export async function exportMute(job: Bull.Job, done: any): Promise {
for (const mute of mutes) {
const u = await Users.findOne({ id: mute.muteeId });
+ if (u == null) {
+ exportedCount++; continue;
+ }
+
const content = getFullApAccount(u.username, u.host);
await new Promise((res, rej) => {
stream.write(content + '\n', err => {
diff --git a/src/queue/processors/db/export-notes.ts b/src/queue/processors/db/export-notes.ts
index 92867ad82e..eaa5caf63f 100644
--- a/src/queue/processors/db/export-notes.ts
+++ b/src/queue/processors/db/export-notes.ts
@@ -9,15 +9,18 @@ import { Users, Notes, Polls } from '../../../models';
import { MoreThan } from 'typeorm';
import { Note } from '../../../models/entities/note';
import { Poll } from '../../../models/entities/poll';
+import { ensure } from '../../../prelude/ensure';
const logger = queueLogger.createSubLogger('export-notes');
export async function exportNotes(job: Bull.Job, done: any): Promise {
logger.info(`Exporting notes of ${job.data.user.id} ...`);
- const user = await Users.findOne({
- id: job.data.user.id
- });
+ const user = await Users.findOne(job.data.user.id);
+ if (user == null) {
+ done();
+ return;
+ }
// Create temp file
const [path, cleanup] = await new Promise<[string, any]>((res, rej) => {
@@ -67,9 +70,9 @@ export async function exportNotes(job: Bull.Job, done: any): Promise {
cursor = notes[notes.length - 1].id;
for (const note of notes) {
- let poll: Poll;
+ let poll: Poll | undefined;
if (note.hasPoll) {
- poll = await Polls.findOne({ noteId: note.id });
+ poll = await Polls.findOne({ noteId: note.id }).then(ensure);
}
const content = JSON.stringify(serialize(note, poll));
await new Promise((res, rej) => {
@@ -114,7 +117,7 @@ export async function exportNotes(job: Bull.Job, done: any): Promise {
done();
}
-function serialize(note: Note, poll: Poll): any {
+function serialize(note: Note, poll: Poll | null = null): any {
return {
id: note.id,
text: note.text,
diff --git a/src/queue/processors/db/export-user-lists.ts b/src/queue/processors/db/export-user-lists.ts
index f3987cb0d2..5cd978c1aa 100644
--- a/src/queue/processors/db/export-user-lists.ts
+++ b/src/queue/processors/db/export-user-lists.ts
@@ -14,9 +14,11 @@ const logger = queueLogger.createSubLogger('export-user-lists');
export async function exportUserLists(job: Bull.Job, done: any): Promise {
logger.info(`Exporting user lists of ${job.data.user.id} ...`);
- const user = await Users.findOne({
- id: job.data.user.id
- });
+ const user = await Users.findOne(job.data.user.id);
+ if (user == null) {
+ done();
+ return;
+ }
const lists = await UserLists.find({
userId: user.id
diff --git a/src/queue/processors/db/import-following.ts b/src/queue/processors/db/import-following.ts
index aae24b22d6..8de3193e46 100644
--- a/src/queue/processors/db/import-following.ts
+++ b/src/queue/processors/db/import-following.ts
@@ -13,13 +13,19 @@ const logger = queueLogger.createSubLogger('import-following');
export async function importFollowing(job: Bull.Job, done: any): Promise {
logger.info(`Importing following of ${job.data.user.id} ...`);
- const user = await Users.findOne({
- id: job.data.user.id
- });
+ const user = await Users.findOne(job.data.user.id);
+ if (user == null) {
+ done();
+ return;
+ }
const file = await DriveFiles.findOne({
id: job.data.fileId
});
+ if (file == null) {
+ done();
+ return;
+ }
const csv = await downloadTextFile(file.url);
@@ -31,11 +37,11 @@ export async function importFollowing(job: Bull.Job, done: any): Promise {
try {
const { username, host } = parseAcct(line.trim());
- let target = isSelfHost(host) ? await Users.findOne({
+ let target = isSelfHost(host!) ? await Users.findOne({
host: null,
usernameLower: username.toLowerCase()
}) : await Users.findOne({
- host: toPuny(host),
+ host: toPuny(host!),
usernameLower: username.toLowerCase()
});
diff --git a/src/queue/processors/db/import-user-lists.ts b/src/queue/processors/db/import-user-lists.ts
index c7273ea6b4..1e852be945 100644
--- a/src/queue/processors/db/import-user-lists.ts
+++ b/src/queue/processors/db/import-user-lists.ts
@@ -14,13 +14,19 @@ const logger = queueLogger.createSubLogger('import-user-lists');
export async function importUserLists(job: Bull.Job, done: any): Promise {
logger.info(`Importing user lists of ${job.data.user.id} ...`);
- const user = await Users.findOne({
- id: job.data.user.id
- });
+ const user = await Users.findOne(job.data.user.id);
+ if (user == null) {
+ done();
+ return;
+ }
const file = await DriveFiles.findOne({
id: job.data.fileId
});
+ if (file == null) {
+ done();
+ return;
+ }
const csv = await downloadTextFile(file.url);
@@ -43,22 +49,20 @@ export async function importUserLists(job: Bull.Job, done: any): Promise {
});
}
- let target = isSelfHost(host) ? await Users.findOne({
+ let target = isSelfHost(host!) ? await Users.findOne({
host: null,
usernameLower: username.toLowerCase()
}) : await Users.findOne({
- host: toPuny(host),
+ host: toPuny(host!),
usernameLower: username.toLowerCase()
});
- if (host == null && target == null) continue;
-
- if (await UserListJoinings.findOne({ userListId: list.id, userId: target.id }) != null) continue;
-
if (target == null) {
target = await resolveUser(username, host);
}
+ if (await UserListJoinings.findOne({ userListId: list.id, userId: target.id }) != null) continue;
+
pushUserToUserList(target, list);
}
diff --git a/src/queue/processors/deliver.ts b/src/queue/processors/deliver.ts
index b9701c0c65..8837c80d87 100644
--- a/src/queue/processors/deliver.ts
+++ b/src/queue/processors/deliver.ts
@@ -7,7 +7,7 @@ import { instanceChart } from '../../services/chart';
const logger = new Logger('deliver');
-let latest: string = null;
+let latest: string | null = null;
export default async (job: Bull.Job) => {
const { host } = new URL(job.data.to);
diff --git a/src/queue/processors/inbox.ts b/src/queue/processors/inbox.ts
index 35b0ce5386..4deaef2ae3 100644
--- a/src/queue/processors/inbox.ts
+++ b/src/queue/processors/inbox.ts
@@ -14,6 +14,7 @@ import { UserPublickey } from '../../models/entities/user-publickey';
import fetchMeta from '../../misc/fetch-meta';
import { toPuny } from '../../misc/convert-host';
import { validActor } from '../../remote/activitypub/type';
+import { ensure } from '../../prelude/ensure';
const logger = new Logger('inbox');
@@ -35,7 +36,7 @@ export default async (job: Bull.Job): Promise => {
if (keyIdLower.startsWith('acct:')) {
const acct = parseAcct(keyIdLower.slice('acct:'.length));
- const host = toPuny(acct.host);
+ const host = acct.host ? toPuny(acct.host) : null;
const username = toPuny(acct.username);
if (host === null) {
@@ -64,9 +65,7 @@ export default async (job: Bull.Job): Promise => {
host: host
}) as IRemoteUser;
- key = await UserPublickeys.findOne({
- userId: user.id
- });
+ key = await UserPublickeys.findOne(user.id).then(ensure);
} else {
// アクティビティ内のホストの検証
const host = toPuny(new URL(signature.keyId).hostname);
@@ -87,7 +86,7 @@ export default async (job: Bull.Job): Promise => {
key = await UserPublickeys.findOne({
keyId: signature.keyId
- });
+ }).then(ensure);
user = await Users.findOne(key.userId) as IRemoteUser;
}
diff --git a/src/remote/activitypub/kernel/accept/follow.ts b/src/remote/activitypub/kernel/accept/follow.ts
index 816fcbadbf..f3e517ad9f 100644
--- a/src/remote/activitypub/kernel/accept/follow.ts
+++ b/src/remote/activitypub/kernel/accept/follow.ts
@@ -6,9 +6,10 @@ import { Users } from '../../../../models';
export default async (actor: IRemoteUser, activity: IFollow): Promise => {
const id = typeof activity.actor == 'string' ? activity.actor : activity.actor.id;
+ if (id == null) throw 'missing id';
if (!id.startsWith(config.url + '/')) {
- return null;
+ return;
}
const follower = await Users.findOne({
diff --git a/src/remote/activitypub/kernel/add/index.ts b/src/remote/activitypub/kernel/add/index.ts
index d16f0a4a0d..a5b2687416 100644
--- a/src/remote/activitypub/kernel/add/index.ts
+++ b/src/remote/activitypub/kernel/add/index.ts
@@ -14,6 +14,7 @@ export default async (actor: IRemoteUser, activity: IAdd): Promise => {
if (activity.target === actor.featured) {
const note = await resolveNote(activity.object);
+ if (note == null) throw new Error('note not found');
await addPinned(actor, note.id);
return;
}
diff --git a/src/remote/activitypub/kernel/announce/note.ts b/src/remote/activitypub/kernel/announce/note.ts
index 403fc66bed..f9822c5187 100644
--- a/src/remote/activitypub/kernel/announce/note.ts
+++ b/src/remote/activitypub/kernel/announce/note.ts
@@ -53,16 +53,16 @@ export default async function(resolver: Resolver, actor: IRemoteUser, activity:
logger.info(`Creating the (Re)Note: ${uri}`);
//#region Visibility
- const visibility = getVisibility(activity.to, activity.cc, actor);
+ const visibility = getVisibility(activity.to || [], activity.cc || [], actor);
let visibleUsers: User[] = [];
if (visibility == 'specified') {
- visibleUsers = await Promise.all(note.to.map(uri => resolvePerson(uri)));
+ visibleUsers = await Promise.all((note.to || []).map(uri => resolvePerson(uri)));
}
//#endergion
await post(actor, {
- createdAt: new Date(activity.published),
+ createdAt: activity.published ? new Date(activity.published) : null,
renote,
visibility,
visibleUsers,
@@ -75,9 +75,6 @@ type visibility = 'public' | 'home' | 'followers' | 'specified';
function getVisibility(to: string[], cc: string[], actor: IRemoteUser): visibility {
const PUBLIC = 'https://www.w3.org/ns/activitystreams#Public';
- to = to || [];
- cc = cc || [];
-
if (to.includes(PUBLIC)) {
return 'public';
} else if (cc.includes(PUBLIC)) {
diff --git a/src/remote/activitypub/kernel/block/index.ts b/src/remote/activitypub/kernel/block/index.ts
index 48e251dd9b..19e33eb7dd 100644
--- a/src/remote/activitypub/kernel/block/index.ts
+++ b/src/remote/activitypub/kernel/block/index.ts
@@ -9,13 +9,14 @@ const logger = apLogger;
export default async (actor: IRemoteUser, activity: IBlock): Promise => {
const id = typeof activity.object == 'string' ? activity.object : activity.object.id;
+ if (id == null) throw 'missing id';
const uri = activity.id || activity;
logger.info(`Block: ${uri}`);
if (!id.startsWith(config.url + '/')) {
- return null;
+ return;
}
const blockee = await Users.findOne(id.split('/').pop());
diff --git a/src/remote/activitypub/kernel/follow.ts b/src/remote/activitypub/kernel/follow.ts
index e6c8833f3a..d37404502f 100644
--- a/src/remote/activitypub/kernel/follow.ts
+++ b/src/remote/activitypub/kernel/follow.ts
@@ -6,9 +6,10 @@ import { Users } from '../../../models';
export default async (actor: IRemoteUser, activity: IFollow): Promise => {
const id = typeof activity.object == 'string' ? activity.object : activity.object.id;
+ if (id == null) throw 'missing id';
if (!id.startsWith(config.url + '/')) {
- return null;
+ return;
}
const followee = await Users.findOne(id.split('/').pop());
diff --git a/src/remote/activitypub/kernel/index.ts b/src/remote/activitypub/kernel/index.ts
index 4a57d0675e..d1251817fa 100644
--- a/src/remote/activitypub/kernel/index.ts
+++ b/src/remote/activitypub/kernel/index.ts
@@ -71,7 +71,7 @@ const self = async (actor: IRemoteUser, activity: Object): Promise => {
default:
apLogger.warn(`unknown activity type: ${(activity as any).type}`);
- return null;
+ return;
}
};
diff --git a/src/remote/activitypub/kernel/like.ts b/src/remote/activitypub/kernel/like.ts
index 86dd8fb33d..d4fa7bf387 100644
--- a/src/remote/activitypub/kernel/like.ts
+++ b/src/remote/activitypub/kernel/like.ts
@@ -5,6 +5,7 @@ import { Notes } from '../../../models';
export default async (actor: IRemoteUser, activity: ILike) => {
const id = typeof activity.object == 'string' ? activity.object : activity.object.id;
+ if (id == null) throw 'missing id';
// Transform:
// https://misskey.ex/notes/xxxx to
diff --git a/src/remote/activitypub/kernel/reject/follow.ts b/src/remote/activitypub/kernel/reject/follow.ts
index b06ae6fb96..91689339ab 100644
--- a/src/remote/activitypub/kernel/reject/follow.ts
+++ b/src/remote/activitypub/kernel/reject/follow.ts
@@ -6,9 +6,10 @@ import { Users } from '../../../../models';
export default async (actor: IRemoteUser, activity: IFollow): Promise => {
const id = typeof activity.actor == 'string' ? activity.actor : activity.actor.id;
+ if (id == null) throw 'missing id';
if (!id.startsWith(config.url + '/')) {
- return null;
+ return;
}
const follower = await Users.findOne(id.split('/').pop());
diff --git a/src/remote/activitypub/kernel/remove/index.ts b/src/remote/activitypub/kernel/remove/index.ts
index ae33be59dc..32b8d66471 100644
--- a/src/remote/activitypub/kernel/remove/index.ts
+++ b/src/remote/activitypub/kernel/remove/index.ts
@@ -14,6 +14,7 @@ export default async (actor: IRemoteUser, activity: IRemove): Promise => {
if (activity.target === actor.featured) {
const note = await resolveNote(activity.object);
+ if (note == null) throw new Error('note not found');
await removePinned(actor, note.id);
return;
}
diff --git a/src/remote/activitypub/kernel/undo/block.ts b/src/remote/activitypub/kernel/undo/block.ts
index c916a00737..9c277ed7d2 100644
--- a/src/remote/activitypub/kernel/undo/block.ts
+++ b/src/remote/activitypub/kernel/undo/block.ts
@@ -9,13 +9,14 @@ const logger = apLogger;
export default async (actor: IRemoteUser, activity: IBlock): Promise => {
const id = typeof activity.object == 'string' ? activity.object : activity.object.id;
+ if (id == null) throw 'missing id';
const uri = activity.id || activity;
logger.info(`UnBlock: ${uri}`);
if (!id.startsWith(config.url + '/')) {
- return null;
+ return;
}
const blockee = await Users.findOne(id.split('/').pop());
diff --git a/src/remote/activitypub/kernel/undo/follow.ts b/src/remote/activitypub/kernel/undo/follow.ts
index cc63a740b1..ce84d0c791 100644
--- a/src/remote/activitypub/kernel/undo/follow.ts
+++ b/src/remote/activitypub/kernel/undo/follow.ts
@@ -7,9 +7,10 @@ import { Users, FollowRequests, Followings } from '../../../../models';
export default async (actor: IRemoteUser, activity: IFollow): Promise => {
const id = typeof activity.object == 'string' ? activity.object : activity.object.id;
+ if (id == null) throw 'missing id';
if (!id.startsWith(config.url + '/')) {
- return null;
+ return;
}
const followee = await Users.findOne(id.split('/').pop());
diff --git a/src/remote/activitypub/kernel/undo/index.ts b/src/remote/activitypub/kernel/undo/index.ts
index 6376ab93a8..5f2e58c3bf 100644
--- a/src/remote/activitypub/kernel/undo/index.ts
+++ b/src/remote/activitypub/kernel/undo/index.ts
@@ -39,6 +39,4 @@ export default async (actor: IRemoteUser, activity: IUndo): Promise => {
undoLike(actor, object as ILike);
break;
}
-
- return null;
};
diff --git a/src/remote/activitypub/kernel/undo/like.ts b/src/remote/activitypub/kernel/undo/like.ts
index f337a0173e..75879d697a 100644
--- a/src/remote/activitypub/kernel/undo/like.ts
+++ b/src/remote/activitypub/kernel/undo/like.ts
@@ -8,6 +8,7 @@ import { Notes } from '../../../../models';
*/
export default async (actor: IRemoteUser, activity: ILike): Promise => {
const id = typeof activity.object == 'string' ? activity.object : activity.object.id;
+ if (id == null) throw 'missing id';
const noteId = id.split('/').pop();
diff --git a/src/remote/activitypub/models/image.ts b/src/remote/activitypub/models/image.ts
index c9991dba3b..f8b35ea21c 100644
--- a/src/remote/activitypub/models/image.ts
+++ b/src/remote/activitypub/models/image.ts
@@ -5,6 +5,7 @@ import fetchMeta from '../../../misc/fetch-meta';
import { apLogger } from '../logger';
import { DriveFile } from '../../../models/entities/drive-file';
import { DriveFiles } from '../../../models';
+import { ensure } from '../../../prelude/ensure';
const logger = apLogger;
@@ -14,7 +15,7 @@ const logger = apLogger;
export async function createImage(actor: IRemoteUser, value: any): Promise {
// 投稿者が凍結されていたらスキップ
if (actor.isSuspended) {
- return null;
+ throw new Error('actor has been suspended');
}
const image = await new Resolver().resolve(value) as any;
@@ -28,17 +29,7 @@ export async function createImage(actor: IRemoteUser, value: any): Promise= 400 && e < 500) {
- logger.warn(`Ignored image: ${image.url} - ${e}`);
- return null;
- }
- throw e;
- }
+ let file = await uploadFromUrl(image.url, actor, null, image.url, image.sensitive, false, !cache);
if (file.isLink) {
// URLが異なっている場合、同じ画像が以前に異なるURLで登録されていたということなので、
@@ -49,7 +40,7 @@ export async function createImage(actor: IRemoteUser, value: any): Promise {
+export async function fetchNote(value: string | IObject, resolver?: Resolver): Promise {
const uri = typeof value == 'string' ? value : value.id;
+ if (uri == null) throw 'missing uri';
// URIがこのサーバーを指しているならデータベースからフェッチ
if (uri.startsWith(config.url + '/')) {
const id = uri.split('/').pop();
- return await Notes.findOne(id);
+ return await Notes.findOne(id).then(x => x || null);
}
//#region このサーバーに既に登録されていたらそれを返す
@@ -52,7 +54,7 @@ export async function fetchNote(value: string | IObject, resolver?: Resolver): P
/**
* Noteを作成します。
*/
-export async function createNote(value: any, resolver?: Resolver, silent = false): Promise {
+export async function createNote(value: any, resolver?: Resolver, silent = false): Promise {
if (resolver == null) resolver = new Resolver();
const object: any = await resolver.resolve(value);
@@ -65,7 +67,7 @@ export async function createNote(value: any, resolver?: Resolver, silent = false
value: value,
object: object
});
- return null;
+ throw 'invalid note';
}
const note: INote = object;
@@ -75,11 +77,11 @@ export async function createNote(value: any, resolver?: Resolver, silent = false
logger.info(`Creating the Note: ${note.id}`);
// 投稿者をフェッチ
- const actor = await resolvePerson(note.attributedTo, null, resolver) as IRemoteUser;
+ const actor = await resolvePerson(note.attributedTo, resolver) as IRemoteUser;
// 投稿者が凍結されていたらスキップ
if (actor.isSuspended) {
- return null;
+ throw 'actor has been suspended';
}
//#region Visibility
@@ -95,9 +97,9 @@ export async function createNote(value: any, resolver?: Resolver, silent = false
visibility = 'followers';
} else {
visibility = 'specified';
- visibleUsers = await Promise.all(note.to.map(uri => resolvePerson(uri, null, resolver)));
+ visibleUsers = await Promise.all(note.to.map(uri => resolvePerson(uri, resolver)));
}
-}
+ }
//#endergion
const apMentions = await extractMentionedUsers(actor, note.to, note.cc, resolver);
@@ -118,7 +120,7 @@ export async function createNote(value: any, resolver?: Resolver, silent = false
: [];
// リプライ
- const reply: Note = note.inReplyTo
+ const reply: Note | undefined | null = note.inReplyTo
? await resolveNote(note.inReplyTo, resolver).catch(e => {
// 4xxの場合はリプライしてないことにする
if (e.statusCode >= 400 && e.statusCode < 500) {
@@ -131,7 +133,7 @@ export async function createNote(value: any, resolver?: Resolver, silent = false
: null;
// 引用
- let quote: Note;
+ let quote: Note | undefined | null;
if (note._misskey_quote && typeof note._misskey_quote == 'string') {
quote = await resolveNote(note._misskey_quote).catch(e => {
@@ -152,7 +154,8 @@ export async function createNote(value: any, resolver?: Resolver, silent = false
// vote
if (reply && reply.hasPoll) {
- const poll = await Polls.findOne({ noteId: reply.id });
+ const poll = await Polls.findOne({ noteId: reply.id }).then(ensure);
+
const tryCreateVote = async (name: string, index: number): Promise => {
if (poll.expiresAt && Date.now() > new Date(poll.expiresAt).getTime()) {
logger.warn(`vote to expired poll from AP: actor=${actor.username}@${actor.host}, note=${note.id}, choice=${name}`);
@@ -180,7 +183,7 @@ export async function createNote(value: any, resolver?: Resolver, silent = false
}
}
- const emojis = await extractEmojis(note.tag, actor.host).catch(e => {
+ const emojis = await extractEmojis(note.tag || [], actor.host).catch(e => {
logger.info(`extractEmojis: ${e}`);
return [] as Emoji[];
});
@@ -196,7 +199,7 @@ export async function createNote(value: any, resolver?: Resolver, silent = false
}
return await post(actor, {
- createdAt: new Date(note.published),
+ createdAt: note.published ? new Date(note.published) : null,
files,
reply,
renote: quote,
@@ -223,8 +226,9 @@ export async function createNote(value: any, resolver?: Resolver, silent = false
* Misskeyに対象のNoteが登録されていればそれを返し、そうでなければ
* リモートサーバーからフェッチしてMisskeyに登録しそれを返します。
*/
-export async function resolveNote(value: string | IObject, resolver?: Resolver): Promise {
+export async function resolveNote(value: string | IObject, resolver?: Resolver): Promise {
const uri = typeof value == 'string' ? value : value.id;
+ if (uri == null) throw 'missing uri';
// ブロックしてたら中断
// TODO: いちいちデータベースにアクセスするのはコスト高そうなのでどっかにキャッシュしておく
@@ -244,75 +248,79 @@ export async function resolveNote(value: string | IObject, resolver?: Resolver):
// 添付されてきたNote Objectは偽装されている可能性があるため、常にuriを指定してサーバーフェッチを行う。
return await createNote(uri, resolver).catch(e => {
if (e.name === 'duplicated') {
- return fetchNote(uri);
+ return fetchNote(uri).then(note => {
+ if (note == null) {
+ throw 'something happened';
+ } else {
+ return note;
+ }
+ });
} else {
throw e;
}
});
}
-export async function extractEmojis(tags: ITag[], host: string) {
+export async function extractEmojis(tags: ITag[], host: string): Promise {
host = toPuny(host);
if (!tags) return [];
- const eomjiTags = tags.filter(tag => tag.type === 'Emoji' && tag.icon && tag.icon.url);
+ const eomjiTags = tags.filter(tag => tag.type === 'Emoji' && tag.icon && tag.icon.url && tag.name);
- return await Promise.all(
- eomjiTags.map(async tag => {
- const name = tag.name.replace(/^:/, '').replace(/:$/, '');
+ return await Promise.all(eomjiTags.map(async tag => {
+ const name = tag.name!.replace(/^:/, '').replace(/:$/, '');
- const exists = await Emojis.findOne({
- host,
- name
- });
+ const exists = await Emojis.findOne({
+ host,
+ name
+ });
- if (exists) {
- if ((tag.updated != null && exists.updatedAt == null)
- || (tag.id != null && exists.uri == null)
- || (tag.updated != null && exists.updatedAt != null && new Date(tag.updated) > exists.updatedAt)
- ) {
- await Emojis.update({
- host,
- name,
- }, {
- uri: tag.id,
- url: tag.icon.url,
- updatedAt: new Date(tag.updated),
- });
+ if (exists) {
+ if ((tag.updated != null && exists.updatedAt == null)
+ || (tag.id != null && exists.uri == null)
+ || (tag.updated != null && exists.updatedAt != null && new Date(tag.updated) > exists.updatedAt)
+ ) {
+ await Emojis.update({
+ host,
+ name,
+ }, {
+ uri: tag.id,
+ url: tag.icon!.url,
+ updatedAt: new Date(tag.updated!),
+ });
- return await Emojis.findOne({
- host,
- name
- });
- }
-
- return exists;
+ return await Emojis.findOne({
+ host,
+ name
+ }) as Emoji;
}
- logger.info(`register emoji host=${host}, name=${name}`);
+ return exists;
+ }
- return await Emojis.save({
- id: genId(),
- host,
- name,
- uri: tag.id,
- url: tag.icon.url,
- updatedAt: tag.updated ? new Date(tag.updated) : undefined,
- aliases: []
- } as Emoji);
- })
- );
+ logger.info(`register emoji host=${host}, name=${name}`);
+
+ return await Emojis.save({
+ id: genId(),
+ host,
+ name,
+ uri: tag.id,
+ url: tag.icon!.url,
+ updatedAt: tag.updated ? new Date(tag.updated) : undefined,
+ aliases: []
+ } as Partial);
+ }));
}
async function extractMentionedUsers(actor: IRemoteUser, to: string[], cc: string[], resolver: Resolver) {
const ignoreUris = ['https://www.w3.org/ns/activitystreams#Public', `${actor.uri}/followers`];
const uris = difference(unique(concat([to || [], cc || []])), ignoreUris);
- const limit = promiseLimit(2);
+ const limit = promiseLimit(2);
const users = await Promise.all(
- uris.map(uri => limit(() => resolvePerson(uri, null, resolver).catch(() => null)) as Promise)
+ uris.map(uri => limit(() => resolvePerson(uri, resolver).catch(() => null)) as Promise)
);
- return users.filter(x => x != null);
+ return users.filter(x => x != null) as User[];
}
diff --git a/src/remote/activitypub/models/person.ts b/src/remote/activitypub/models/person.ts
index e7021956de..9465cf0cd0 100644
--- a/src/remote/activitypub/models/person.ts
+++ b/src/remote/activitypub/models/person.ts
@@ -26,6 +26,7 @@ import { toPuny } from '../../../misc/convert-host';
import { UserProfile } from '../../../models/entities/user-profile';
import { validActor } from '../../../remote/activitypub/type';
import { getConnection } from 'typeorm';
+import { ensure } from '../../../prelude/ensure';
const logger = apLogger;
/**
@@ -86,13 +87,13 @@ function validatePerson(x: any, uri: string) {
*
* Misskeyに対象のPersonが登録されていればそれを返します。
*/
-export async function fetchPerson(uri: string, resolver?: Resolver): Promise {
+export async function fetchPerson(uri: string, resolver?: Resolver): Promise {
if (typeof uri !== 'string') throw 'uri is not string';
// URIがこのサーバーを指しているならデータベースからフェッチ
if (uri.startsWith(config.url + '/')) {
const id = uri.split('/').pop();
- return await Users.findOne(id);
+ return await Users.findOne(id).then(x => x || null);
}
//#region このサーバーに既に登録されていたらそれを返す
@@ -128,7 +129,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise tag.toLowerCase());
@@ -161,7 +162,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise !tags.includes(x))) updateHashtag(user, tag, true, false);
+ for (const tag of tags) updateHashtag(user!, tag, true, true);
+ for (const tag of (user!.tags || []).filter(x => !tags.includes(x))) updateHashtag(user!, tag, true, false);
//#region アイコンとヘッダー画像をフェッチ
- const [avatar, banner] = (await Promise.all([
+ const [avatar, banner] = (await Promise.all([
person.icon,
person.image
].map(img =>
img == null
? Promise.resolve(null)
- : resolveImage(user, img).catch(() => null)
+ : resolveImage(user!, img).catch(() => null)
)));
const avatarId = avatar ? avatar.id : null;
@@ -210,9 +211,9 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise {
+ const emojis = await extractEmojis(person.tag || [], host).catch(e => {
logger.info(`extractEmojis: ${e}`);
return [] as Emoji[];
});
const emojiNames = emojis.map(emoji => emoji.name);
- await Users.update(user.id, {
+ await Users.update(user!.id, {
emojis: emojiNames
});
//#endregion
- await updateFeatured(user.id).catch(err => logger.error(err));
+ await updateFeatured(user!.id).catch(err => logger.error(err));
- return user;
+ return user!;
}
/**
@@ -254,7 +255,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise {
+export async function updatePerson(uri: string, resolver?: Resolver | null, hint?: object): Promise {
if (typeof uri !== 'string') throw 'uri is not string';
// URIがこのサーバーを指しているならスキップ
@@ -290,7 +291,7 @@ export async function updatePerson(uri: string, resolver?: Resolver, hint?: obje
logger.info(`Updating the Person: ${person.id}`);
// アイコンとヘッダー画像をフェッチ
- const [avatar, banner] = (await Promise.all([
+ const [avatar, banner] = (await Promise.all([
person.icon,
person.image
].map(img =>
@@ -300,14 +301,14 @@ export async function updatePerson(uri: string, resolver?: Resolver, hint?: obje
)));
// カスタム絵文字取得
- const emojis = await extractEmojis(person.tag, exist.host).catch(e => {
+ const emojis = await extractEmojis(person.tag || [], exist.host).catch(e => {
logger.info(`extractEmojis: ${e}`);
return [] as Emoji[];
});
const emojiNames = emojis.map(emoji => emoji.name);
- const { fields, services } = analyzeAttachments(person.attachment);
+ const { fields, services } = analyzeAttachments(person.attachment || []);
const tags = extractHashtags(person.tag).map(tag => tag.toLowerCase());
@@ -317,7 +318,7 @@ export async function updatePerson(uri: string, resolver?: Resolver, hint?: obje
sharedInbox: person.sharedInbox || (person.endpoints ? person.endpoints.sharedInbox : undefined),
featured: person.featured,
emojis: emojiNames,
- description: fromHtml(person.summary),
+ description: person.summary ? fromHtml(person.summary) : null,
name: person.name,
url: person.url,
endpoints: person.endpoints,
@@ -326,7 +327,6 @@ export async function updatePerson(uri: string, resolver?: Resolver, hint?: obje
isBot: object.type == 'Service',
isCat: (person as any).isCat === true,
isLocked: person.manuallyApprovesFollowers,
- createdAt: new Date(Date.parse(person.published)) || null,
} as Partial;
if (avatar) {
@@ -379,7 +379,7 @@ export async function updatePerson(uri: string, resolver?: Resolver, hint?: obje
* Misskeyに対象のPersonが登録されていればそれを返し、そうでなければ
* リモートサーバーからフェッチしてMisskeyに登録しそれを返します。
*/
-export async function resolvePerson(uri: string, verifier?: string, resolver?: Resolver): Promise {
+export async function resolvePerson(uri: string, resolver?: Resolver): Promise {
if (typeof uri !== 'string') throw 'uri is not string';
//#region このサーバーに既に登録されていたらそれを返す
@@ -439,21 +439,24 @@ export function analyzeAttachments(attachments: ITag[]) {
}[] = [];
const services: { [x: string]: any } = {};
- if (Array.isArray(attachments))
- for (const attachment of attachments.filter(isPropertyValue))
- if (isPropertyValue(attachment.identifier))
- addService(services, attachment.identifier);
- else
+ if (Array.isArray(attachments)) {
+ for (const attachment of attachments.filter(isPropertyValue)) {
+ if (isPropertyValue(attachment.identifier!)) {
+ addService(services, attachment.identifier!);
+ } else {
fields.push({
- name: attachment.name,
- value: fromHtml(attachment.value)
+ name: attachment.name!,
+ value: fromHtml(attachment.value!)
});
+ }
+ }
+ }
return { fields, services };
}
export async function updateFeatured(userId: User['id']) {
- const user = await Users.findOne(userId);
+ const user = await Users.findOne(userId).then(ensure);
if (!Users.isRemoteUser(user)) return;
if (!user.featured) return;
@@ -471,18 +474,18 @@ export async function updateFeatured(userId: User['id']) {
if (!Array.isArray(items)) throw new Error(`Collection items is not an array`);
// Resolve and regist Notes
- const limit = promiseLimit(2);
+ const limit = promiseLimit(2);
const featuredNotes = await Promise.all(items
.filter(item => item.type === 'Note')
.slice(0, 5)
- .map(item => limit(() => resolveNote(item, resolver)) as Promise));
+ .map(item => limit(() => resolveNote(item, resolver))));
for (const note of featuredNotes.filter(note => note != null)) {
UserNotePinings.save({
id: genId(),
createdAt: new Date(),
userId: user.id,
- noteId: note.id
+ noteId: note!.id
} as UserNotePining);
}
}
diff --git a/src/remote/activitypub/models/question.ts b/src/remote/activitypub/models/question.ts
index 2ff8e21ab5..708cdc2a66 100644
--- a/src/remote/activitypub/models/question.ts
+++ b/src/remote/activitypub/models/question.ts
@@ -14,10 +14,10 @@ export async function extractPollFromQuestion(source: string | IQuestion): Promi
throw 'invalid question';
}
- const choices = question[multiple ? 'anyOf' : 'oneOf']
- .map((x, i) => x.name);
+ const choices = question[multiple ? 'anyOf' : 'oneOf']!
+ .map((x, i) => x.name!);
- const votes = question[multiple ? 'anyOf' : 'oneOf']
+ const votes = question[multiple ? 'anyOf' : 'oneOf']!
.map((x, i) => x.replies && x.replies.totalItems || x._misskey_votes || 0);
return {
@@ -60,7 +60,7 @@ export async function updateQuestion(value: any) {
for (const choice of poll.choices) {
const oldCount = poll.votes[poll.choices.indexOf(choice)];
- const newCount = apChoices.filter(ap => ap.name === choice)[0].replies.totalItems;
+ const newCount = apChoices!.filter(ap => ap.name === choice)[0].replies!.totalItems;
if (oldCount != newCount) {
changed = true;
diff --git a/src/remote/activitypub/models/tag.ts b/src/remote/activitypub/models/tag.ts
index 0a1e6e29f9..8d2008d1d9 100644
--- a/src/remote/activitypub/models/tag.ts
+++ b/src/remote/activitypub/models/tag.ts
@@ -14,13 +14,13 @@ export type ITag = {
identifier?: IIdentifier;
};
-export function extractHashtags(tags: ITag[]) {
- if (!tags) return [];
+export function extractHashtags(tags: ITag[] | null | undefined): string[] {
+ if (tags == null) return [];
const hashtags = tags.filter(tag => tag.type === 'Hashtag' && typeof tag.name == 'string');
return hashtags.map(tag => {
- const m = tag.name.match(/^#(.+)/);
+ const m = tag.name ? tag.name.match(/^#(.+)/) : null;
return m ? m[1] : null;
- }).filter(x => x != null);
+ }).filter(x => x != null) as string[];
}
diff --git a/src/remote/activitypub/renderer/block.ts b/src/remote/activitypub/renderer/block.ts
index 946c45a813..c29a9aea82 100644
--- a/src/remote/activitypub/renderer/block.ts
+++ b/src/remote/activitypub/renderer/block.ts
@@ -1,7 +1,7 @@
import config from '../../../config';
import { ILocalUser, IRemoteUser } from '../../../models/entities/user';
-export default (blocker?: ILocalUser, blockee?: IRemoteUser) => ({
+export default (blocker: ILocalUser, blockee: IRemoteUser) => ({
type: 'Block',
actor: `${config.url}/users/${blocker.id}`,
object: blockee.uri
diff --git a/src/remote/activitypub/renderer/follow-user.ts b/src/remote/activitypub/renderer/follow-user.ts
index 9446be3c86..6d354803e5 100644
--- a/src/remote/activitypub/renderer/follow-user.ts
+++ b/src/remote/activitypub/renderer/follow-user.ts
@@ -1,12 +1,13 @@
import config from '../../../config';
import { Users } from '../../../models';
import { User } from '../../../models/entities/user';
+import { ensure } from '../../../prelude/ensure';
/**
* Convert (local|remote)(Follower|Followee)ID to URL
* @param id Follower|Followee ID
*/
export default async function renderFollowUser(id: User['id']): Promise {
- const user = await Users.findOne(id);
+ const user = await Users.findOne(id).then(ensure);
return Users.isLocalUser(user) ? `${config.url}/users/${user.id}` : user.uri;
}
diff --git a/src/remote/activitypub/renderer/note.ts b/src/remote/activitypub/renderer/note.ts
index 58ee4fb52c..c66af2667b 100644
--- a/src/remote/activitypub/renderer/note.ts
+++ b/src/remote/activitypub/renderer/note.ts
@@ -10,6 +10,7 @@ import { DriveFiles, Notes, Users, Emojis, Polls } from '../../../models';
import { In } from 'typeorm';
import { Emoji } from '../../../models/entities/emoji';
import { Poll } from '../../../models/entities/poll';
+import { ensure } from '../../../prelude/ensure';
export default async function renderNote(note: Note, dive = true): Promise {
const promisedFiles: Promise = note.fileIds.length > 0
@@ -17,15 +18,15 @@ export default async function renderNote(note: Note, dive = true): Promise
: Promise.resolve([]);
let inReplyTo;
- let inReplyToNote: Note;
+ let inReplyToNote: Note | undefined;
if (note.replyId) {
inReplyToNote = await Notes.findOne(note.replyId);
- if (inReplyToNote !== null) {
+ if (inReplyToNote != null) {
const inReplyToUser = await Users.findOne(inReplyToNote.userId);
- if (inReplyToUser !== null) {
+ if (inReplyToUser != null) {
if (inReplyToNote.uri) {
inReplyTo = inReplyToNote.uri;
} else {
@@ -51,9 +52,7 @@ export default async function renderNote(note: Note, dive = true): Promise
}
}
- const user = await Users.findOne({
- id: note.userId
- });
+ const user = await Users.findOne(note.userId).then(ensure);
const attributedTo = `${config.url}/users/${user.id}`;
@@ -85,13 +84,13 @@ export default async function renderNote(note: Note, dive = true): Promise
const files = await promisedFiles;
let text = note.text;
- let poll: Poll;
+ let poll: Poll | undefined;
if (note.hasPoll) {
poll = await Polls.findOne({ noteId: note.id });
}
- let question: string;
+ let question: string | undefined;
if (poll) {
if (text == null) text = '';
const url = `${config.url}/notes/${note.id}`;
@@ -144,7 +143,7 @@ export default async function renderNote(note: Note, dive = true): Promise
name: text,
replies: {
type: 'Collection',
- totalItems: poll.votes[i]
+ totalItems: poll!.votes[i]
}
}))
} : {};
@@ -179,5 +178,5 @@ export async function getEmojis(names: string[]): Promise {
}))
);
- return emojis.filter(emoji => emoji != null);
+ return emojis.filter(emoji => emoji != null) as Emoji[];
}
diff --git a/src/remote/activitypub/renderer/ordered-collection-page.ts b/src/remote/activitypub/renderer/ordered-collection-page.ts
index 83af07870e..2433358646 100644
--- a/src/remote/activitypub/renderer/ordered-collection-page.ts
+++ b/src/remote/activitypub/renderer/ordered-collection-page.ts
@@ -7,7 +7,7 @@
* @param prev URL of prev page (optional)
* @param next URL of next page (optional)
*/
-export default function(id: string, totalItems: any, orderedItems: any, partOf: string, prev: string, next: string) {
+export default function(id: string, totalItems: any, orderedItems: any, partOf: string, prev?: string, next?: string) {
const page = {
id,
partOf,
diff --git a/src/remote/activitypub/renderer/person.ts b/src/remote/activitypub/renderer/person.ts
index e561e47c68..3fb164ef4e 100644
--- a/src/remote/activitypub/renderer/person.ts
+++ b/src/remote/activitypub/renderer/person.ts
@@ -9,14 +9,15 @@ import renderEmoji from './emoji';
import { IIdentifier } from '../models/identifier';
import renderHashtag from './hashtag';
import { DriveFiles, UserProfiles, UserKeypairs } from '../../../models';
+import { ensure } from '../../../prelude/ensure';
export async function renderPerson(user: ILocalUser) {
const id = `${config.url}/users/${user.id}`;
const [avatar, banner, profile] = await Promise.all([
- DriveFiles.findOne(user.avatarId),
- DriveFiles.findOne(user.bannerId),
- UserProfiles.findOne({ userId: user.id })
+ user.avatarId ? DriveFiles.findOne(user.avatarId) : Promise.resolve(undefined),
+ user.bannerId ? DriveFiles.findOne(user.bannerId) : Promise.resolve(undefined),
+ UserProfiles.findOne({ userId: user.id }).then(ensure)
]);
const attachment: {
@@ -76,9 +77,7 @@ export async function renderPerson(user: ILocalUser) {
...hashtagTags,
];
- const keypair = await UserKeypairs.findOne({
- userId: user.id
- });
+ const keypair = await UserKeypairs.findOne(user.id).then(ensure);
return {
type: user.isBot ? 'Service' : 'Person',
@@ -94,8 +93,8 @@ export async function renderPerson(user: ILocalUser) {
preferredUsername: user.username,
name: user.name,
summary: toHtml(parse(profile.description)),
- icon: user.avatarId && renderImage(avatar),
- image: user.bannerId && renderImage(banner),
+ icon: avatar ? renderImage(avatar) : null,
+ image: banner ? renderImage(banner) : null,
tag,
manuallyApprovesFollowers: user.isLocked,
publicKey: renderKey(user, keypair),
diff --git a/src/remote/activitypub/request.ts b/src/remote/activitypub/request.ts
index 8aca5e8102..7dc48c15e3 100644
--- a/src/remote/activitypub/request.ts
+++ b/src/remote/activitypub/request.ts
@@ -12,6 +12,7 @@ import { apLogger } from './logger';
import { UserKeypairs } from '../../models';
import fetchMeta from '../../misc/fetch-meta';
import { toPuny } from '../../misc/convert-host';
+import { ensure } from '../../prelude/ensure';
export const logger = apLogger.createSubLogger('deliver');
@@ -38,7 +39,7 @@ export default async (user: ILocalUser, url: string, object: any) => {
const keypair = await UserKeypairs.findOne({
userId: user.id
- });
+ }).then(ensure);
const _ = new Promise((resolve, reject) => {
const req = request({
@@ -56,7 +57,7 @@ export default async (user: ILocalUser, url: string, object: any) => {
'Digest': `SHA-256=${hash}`
}
}, res => {
- if (res.statusCode >= 400) {
+ if (res.statusCode! >= 400) {
logger.warn(`${url} --> ${res.statusCode}`);
reject(res);
} else {
@@ -73,7 +74,7 @@ export default async (user: ILocalUser, url: string, object: any) => {
});
// Signature: Signature ... => Signature: ...
- let sig = req.getHeader('Signature').toString();
+ let sig = req.getHeader('Signature')!.toString();
sig = sig.replace(/^Signature /, '');
req.setHeader('Signature', sig);
@@ -112,7 +113,7 @@ async function resolveAddr(domain: string) {
function resolveAddrInner(domain: string, options: IRunOptions = {}): Promise {
return new Promise((res, rej) => {
- lookup(domain, options, (error: any, address: string | string[]) => {
+ lookup(domain, options, (error, address) => {
if (error) return rej(error);
return res(Array.isArray(address) ? address[0] : address);
});
diff --git a/src/remote/resolve-user.ts b/src/remote/resolve-user.ts
index 6a8ce45c91..9b518f5e81 100644
--- a/src/remote/resolve-user.ts
+++ b/src/remote/resolve-user.ts
@@ -10,18 +10,31 @@ import { toPuny } from '../misc/convert-host';
const logger = remoteLogger.createSubLogger('resolve-user');
-export async function resolveUser(username: string, host: string, option?: any, resync = false): Promise {
+export async function resolveUser(username: string, host: string | null, option?: any, resync = false): Promise {
const usernameLower = username.toLowerCase();
- host = toPuny(host);
if (host == null) {
logger.info(`return local user: ${usernameLower}`);
- return await Users.findOne({ usernameLower, host: null });
+ return await Users.findOne({ usernameLower, host: null }).then(u => {
+ if (u == null) {
+ throw 'user not found';
+ } else {
+ return u;
+ }
+ });
}
+ host = toPuny(host);
+
if (config.host == host) {
logger.info(`return local user: ${usernameLower}`);
- return await Users.findOne({ usernameLower, host: null });
+ return await Users.findOne({ usernameLower, host: null }).then(u => {
+ if (u == null) {
+ throw 'user not found';
+ } else {
+ return u;
+ }
+ });
}
const user = await Users.findOne({ usernameLower, host }, option);
@@ -63,7 +76,13 @@ export async function resolveUser(username: string, host: string, option?: any,
await updatePerson(self.href);
logger.info(`return resynced remote user: ${acctLower}`);
- return await Users.findOne({ uri: self.href });
+ return await Users.findOne({ uri: self.href }).then(u => {
+ if (u == null) {
+ throw 'user not found';
+ } else {
+ return u;
+ }
+ });
}
logger.info(`return existing remote user: ${acctLower}`);
@@ -76,7 +95,7 @@ async function resolveSelf(acctLower: string) {
logger.error(`Failed to WebFinger for ${chalk.yellow(acctLower)}: ${ e.statusCode || e.message }`);
throw new Error(`Failed to WebFinger for ${acctLower}: ${ e.statusCode || e.message }`);
});
- const self = finger.links.find(link => link.rel && link.rel.toLowerCase() === 'self');
+ const self = finger.links.find(link => link.rel != null && link.rel.toLowerCase() === 'self');
if (!self) {
logger.error(`Failed to WebFinger for ${chalk.yellow(acctLower)}: self link not found`);
throw new Error('self link not found');
diff --git a/src/remote/webfinger.ts b/src/remote/webfinger.ts
index 67535e37db..800673943b 100644
--- a/src/remote/webfinger.ts
+++ b/src/remote/webfinger.ts
@@ -5,7 +5,7 @@ import { query as urlQuery } from '../prelude/url';
type ILink = {
href: string;
- rel: string;
+ rel?: string;
};
type IWebFinger = {
diff --git a/src/server/activitypub.ts b/src/server/activitypub.ts
index 3b39977d47..12fccbfa7d 100644
--- a/src/server/activitypub.ts
+++ b/src/server/activitypub.ts
@@ -17,6 +17,7 @@ import { isSelfHost } from '../misc/convert-host';
import { Notes, Users, Emojis, UserKeypairs, Polls } from '../models';
import { ILocalUser, User } from '../models/entities/user';
import { In } from 'typeorm';
+import { ensure } from '../prelude/ensure';
// Init router
const router = new Router();
@@ -123,8 +124,8 @@ router.get('/questions/:question', async (ctx, next) => {
return;
}
- const user = await Users.findOne(pollNote.userId);
- const poll = await Polls.findOne({ noteId: pollNote.id });
+ const user = await Users.findOne(pollNote.userId).then(ensure);
+ const poll = await Polls.findOne({ noteId: pollNote.id }).then(ensure);
ctx.body = renderActivity(await renderQuestion(user as ILocalUser, pollNote, poll));
setResponseType(ctx);
@@ -156,9 +157,7 @@ router.get('/users/:user/publickey', async ctx => {
return;
}
- const keypair = await UserKeypairs.findOne({
- userId: user.id
- });
+ const keypair = await UserKeypairs.findOne(user.id).then(ensure);
if (Users.isLocalUser(user)) {
ctx.body = renderActivity(renderKey(user, keypair));
@@ -189,7 +188,7 @@ router.get('/users/:user', async (ctx, next) => {
const user = await Users.findOne({
id: userId,
host: null
- });
+ }).then(ensure);
await userInfo(ctx, user);
});
@@ -200,7 +199,7 @@ router.get('/@:user', async (ctx, next) => {
const user = await Users.findOne({
usernameLower: ctx.params.user.toLowerCase(),
host: null
- });
+ }).then(ensure);
await userInfo(ctx, user);
});
diff --git a/src/server/activitypub/featured.ts b/src/server/activitypub/featured.ts
index f43312d79a..86ec1000c7 100644
--- a/src/server/activitypub/featured.ts
+++ b/src/server/activitypub/featured.ts
@@ -5,6 +5,7 @@ import renderOrderedCollection from '../../remote/activitypub/renderer/ordered-c
import { setResponseType } from '../activitypub';
import renderNote from '../../remote/activitypub/renderer/note';
import { Users, Notes, UserNotePinings } from '../../models';
+import { ensure } from '../../prelude/ensure';
export default async (ctx: Router.IRouterContext) => {
const userId = ctx.params.user;
@@ -22,13 +23,14 @@ export default async (ctx: Router.IRouterContext) => {
const pinings = await UserNotePinings.find({ userId: user.id });
- const pinnedNotes = await Promise.all(pinings.map(pining => Notes.findOne(pining.noteId)));
+ const pinnedNotes = await Promise.all(pinings.map(pining =>
+ Notes.findOne(pining.noteId).then(ensure)));
const renderedNotes = await Promise.all(pinnedNotes.map(note => renderNote(note)));
const rendered = renderOrderedCollection(
`${config.url}/users/${userId}/collections/featured`,
- renderedNotes.length, null, null, renderedNotes
+ renderedNotes.length, undefined, undefined, renderedNotes
);
ctx.body = renderActivity(rendered);
diff --git a/src/server/activitypub/followers.ts b/src/server/activitypub/followers.ts
index 62c54399ed..e48dc57f7a 100644
--- a/src/server/activitypub/followers.ts
+++ b/src/server/activitypub/followers.ts
@@ -69,18 +69,18 @@ export default async (ctx: Router.IRouterContext) => {
cursor
})}`,
user.followersCount, renderedFollowers, partOf,
- null,
+ undefined,
inStock ? `${partOf}?${url.query({
page: 'true',
cursor: followings[followings.length - 1].id
- })}` : null
+ })}` : undefined
);
ctx.body = renderActivity(rendered);
setResponseType(ctx);
} else {
// index page
- const rendered = renderOrderedCollection(partOf, user.followersCount, `${partOf}?page=true`, null);
+ const rendered = renderOrderedCollection(partOf, user.followersCount, `${partOf}?page=true`);
ctx.body = renderActivity(rendered);
ctx.set('Cache-Control', 'private, max-age=0, must-revalidate');
setResponseType(ctx);
diff --git a/src/server/activitypub/following.ts b/src/server/activitypub/following.ts
index 4894aac1f8..4a7314e0ce 100644
--- a/src/server/activitypub/following.ts
+++ b/src/server/activitypub/following.ts
@@ -70,18 +70,18 @@ export default async (ctx: Router.IRouterContext) => {
cursor
})}`,
user.followingCount, renderedFollowees, partOf,
- null,
+ undefined,
inStock ? `${partOf}?${url.query({
page: 'true',
cursor: followings[followings.length - 1].id
- })}` : null
+ })}` : undefined
);
ctx.body = renderActivity(rendered);
setResponseType(ctx);
} else {
// index page
- const rendered = renderOrderedCollection(partOf, user.followingCount, `${partOf}?page=true`, null);
+ const rendered = renderOrderedCollection(partOf, user.followingCount, `${partOf}?page=true`);
ctx.body = renderActivity(rendered);
ctx.set('Cache-Control', 'private, max-age=0, must-revalidate');
setResponseType(ctx);
diff --git a/src/server/activitypub/outbox.ts b/src/server/activitypub/outbox.ts
index 377f43c986..118d8f00a9 100644
--- a/src/server/activitypub/outbox.ts
+++ b/src/server/activitypub/outbox.ts
@@ -15,6 +15,7 @@ import { Users, Notes } from '../../models';
import { makePaginationQuery } from '../api/common/make-pagination-query';
import { Brackets } from 'typeorm';
import { Note } from '../../models/entities/note';
+import { ensure } from '../../prelude/ensure';
export default async (ctx: Router.IRouterContext) => {
const userId = ctx.params.user;
@@ -73,11 +74,11 @@ export default async (ctx: Router.IRouterContext) => {
notes.length ? `${partOf}?${url.query({
page: 'true',
since_id: notes[0].id
- })}` : null,
+ })}` : undefined,
notes.length ? `${partOf}?${url.query({
page: 'true',
until_id: notes[notes.length - 1].id
- })}` : null
+ })}` : undefined
);
ctx.body = renderActivity(rendered);
@@ -99,9 +100,9 @@ export default async (ctx: Router.IRouterContext) => {
* Pack Create or Announce Activity
* @param note Note
*/
-export async function packActivity(note: Note): Promise