Improve error handling of API (#4345)

* wip

* wip

* wip

* Update attached_notes.ts

* wip

* Refactor

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* Update call.ts

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* ✌️

* Fix
This commit is contained in:
syuilo
2019-02-22 11:46:58 +09:00
committed by GitHub
parent fc52e95ad0
commit 2756f553c6
181 changed files with 2010 additions and 1322 deletions

View File

@@ -15,11 +15,11 @@ export const meta = {
}
};
export default define(meta, (ps, user) => new Promise(async (res, rej) => {
export default define(meta, async (ps, user) => {
const _token = ps.token.replace(/\s/g, '');
if (user.twoFactorTempSecret == null) {
return rej('二段階認証の設定が開始されていません');
throw new Error('二段階認証の設定が開始されていません');
}
const verified = (speakeasy as any).totp.verify({
@@ -29,7 +29,7 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => {
});
if (!verified) {
return rej('not verified');
throw new Error('not verified');
}
await User.update(user._id, {
@@ -39,5 +39,5 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => {
}
});
res();
}));
return;
});

View File

@@ -18,12 +18,12 @@ export const meta = {
}
};
export default define(meta, (ps, user) => new Promise(async (res, rej) => {
export default define(meta, async (ps, user) => {
// Compare password
const same = await bcrypt.compare(ps.password, user.password);
if (!same) {
return rej('incorrect password');
throw new Error('incorrect password');
}
// Generate user's secret key
@@ -38,17 +38,17 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => {
});
// Get the data URL of the authenticator URL
QRCode.toDataURL(speakeasy.otpauthURL({
const dataUrl = await QRCode.toDataURL(speakeasy.otpauthURL({
secret: secret.base32,
encoding: 'base32',
label: user.username,
issuer: config.host
}), (err, data_url) => {
res({
qr: data_url,
secret: secret.base32,
label: user.username,
issuer: config.host
});
});
}));
}));
return {
qr: dataUrl,
secret: secret.base32,
label: user.username,
issuer: config.host
};
});

View File

@@ -15,12 +15,12 @@ export const meta = {
}
};
export default define(meta, (ps, user) => new Promise(async (res, rej) => {
export default define(meta, async (ps, user) => {
// Compare password
const same = await bcrypt.compare(ps.password, user.password);
if (!same) {
return rej('incorrect password');
throw new Error('incorrect password');
}
await User.update(user._id, {
@@ -30,5 +30,5 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => {
}
});
res();
}));
return;
});

View File

@@ -26,7 +26,7 @@ export const meta = {
}
};
export default define(meta, (ps, user) => new Promise(async (res, rej) => {
export default define(meta, async (ps, user) => {
// Get tokens
const tokens = await AccessToken
.find({
@@ -39,7 +39,7 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => {
}
});
res(await Promise.all(tokens.map(token => pack(token.appId, user, {
return await Promise.all(tokens.map(token => pack(token.appId, user, {
detail: true
}))));
}));
})));
});

View File

@@ -19,12 +19,12 @@ export const meta = {
}
};
export default define(meta, (ps, user) => new Promise(async (res, rej) => {
export default define(meta, async (ps, user) => {
// Compare password
const same = await bcrypt.compare(ps.currentPassword, user.password);
if (!same) {
return rej('incorrect password');
throw new Error('incorrect password');
}
// Generate hash of password
@@ -37,5 +37,5 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => {
}
});
res();
}));
return;
});

View File

@@ -10,12 +10,12 @@ export const meta = {
}
};
export default define(meta, (ps, user) => new Promise(async (res, rej) => {
export default define(meta, async (ps, user) => {
await User.update({ _id: user._id }, {
$set: {
pendingReceivedFollowRequestsCount: 0
}
});
res();
}));
return;
});

View File

@@ -18,12 +18,12 @@ export const meta = {
}
};
export default define(meta, (ps, user) => new Promise(async (res, rej) => {
export default define(meta, async (ps, user) => {
// Compare password
const same = await bcrypt.compare(ps.password, user.password);
if (!same) {
return rej('incorrect password');
throw new Error('incorrect password');
}
await User.update({ _id: user._id }, {
@@ -49,5 +49,5 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => {
createDeleteNotesJob(user);
createDeleteDriveFilesJob(user);
res();
}));
return;
});

View File

@@ -11,8 +11,8 @@ export const meta = {
},
};
export default define(meta, (ps, user) => new Promise(async (res, rej) => {
export default define(meta, async (ps, user) => {
createExportBlockingJob(user);
res();
}));
return;
});

View File

@@ -11,8 +11,8 @@ export const meta = {
},
};
export default define(meta, (ps, user) => new Promise(async (res, rej) => {
export default define(meta, async (ps, user) => {
createExportFollowingJob(user);
res();
}));
return;
});

View File

@@ -11,8 +11,8 @@ export const meta = {
},
};
export default define(meta, (ps, user) => new Promise(async (res, rej) => {
export default define(meta, async (ps, user) => {
createExportMuteJob(user);
res();
}));
return;
});

View File

@@ -11,8 +11,8 @@ export const meta = {
},
};
export default define(meta, (ps, user) => new Promise(async (res, rej) => {
export default define(meta, async (ps, user) => {
createExportNotesJob(user);
res();
}));
return;
});

View File

@@ -31,12 +31,7 @@ export const meta = {
}
};
export default define(meta, (ps, user) => new Promise(async (res, rej) => {
// Check if both of sinceId and untilId is specified
if (ps.sinceId && ps.untilId) {
return rej('cannot set sinceId and untilId');
}
export default define(meta, async (ps, user) => {
const query = {
userId: user._id
} as any;
@@ -63,5 +58,5 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => {
sort: sort
});
res(await packMany(favorites, user));
}));
return await packMany(favorites, user);
});

View File

@@ -55,12 +55,7 @@ export const meta = {
}
};
export default define(meta, (ps, user) => new Promise(async (res, rej) => {
// Check if both of sinceId and untilId is specified
if (ps.sinceId && ps.untilId) {
return rej('cannot set sinceId and untilId');
}
export default define(meta, async (ps, user) => {
const hideUserIds = await getHideUserIds(user);
const query = {
@@ -114,10 +109,10 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => {
sort: sort
});
res(await packMany(notifications));
// Mark all as read
if (notifications.length > 0 && ps.markAsRead) {
read(user._id, notifications);
}
}));
return await packMany(notifications);
});

View File

@@ -3,6 +3,7 @@ import ID, { transform } from '../../../../misc/cafy-id';
import { pack } from '../../../../models/user';
import { addPinned } from '../../../../services/i/pin';
import define from '../../define';
import { ApiError } from '../../error';
export const meta = {
stability: 'stable',
@@ -24,22 +25,38 @@ export const meta = {
'en-US': 'Target note ID'
}
}
},
errors: {
noSuchNote: {
message: 'No such note.',
code: 'NO_SUCH_NOTE',
id: '56734f8b-3928-431e-bf80-6ff87df40cb3'
},
pinLimitExceeded: {
message: 'You can not pin notes any more.',
code: 'PIN_LIMIT_EXCEEDED',
id: '72dab508-c64d-498f-8740-a8eec1ba385a'
},
alreadyPinned: {
message: 'That note has already been pinned.',
code: 'ALREADY_PINNED',
id: '8b18c2b7-68fe-4edb-9892-c0cbaeb6c913'
},
}
};
export default define(meta, (ps, user) => new Promise(async (res, rej) => {
// Processing
try {
await addPinned(user, ps.noteId);
} catch (e) {
return rej(e.message);
}
// Serialize
const iObj = await pack(user, user, {
detail: true
export default define(meta, async (ps, user) => {
await addPinned(user, ps.noteId).catch(e => {
if (e.id === '70c4e51f-5bea-449c-a030-53bee3cce202') throw new ApiError(meta.errors.noSuchNote);
if (e.id === '15a018eb-58e5-4da1-93be-330fcc5e4e1a') throw new ApiError(meta.errors.pinLimitExceeded);
if (e.id === '23f0cf4e-59a3-4276-a91d-61a5891c1514') throw new ApiError(meta.errors.alreadyPinned);
throw e;
});
// Send response
res(iObj);
}));
return await pack(user, user, {
detail: true
});
});

View File

@@ -17,7 +17,7 @@ export const meta = {
}
};
export default define(meta, (ps, user) => new Promise(async (res, rej) => {
export default define(meta, async (ps, user) => {
// Update documents
await Message.update({
recipientId: user._id,
@@ -38,5 +38,5 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => {
publishMainStream(user._id, 'readAllMessagingMessages');
res();
}));
return;
});

View File

@@ -17,7 +17,7 @@ export const meta = {
}
};
export default define(meta, (ps, user) => new Promise(async (res, rej) => {
export default define(meta, async (ps, user) => {
// Remove documents
await NoteUnread.remove({
userId: user._id
@@ -34,5 +34,5 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => {
publishMainStream(user._id, 'readAllUnreadMentions');
publishMainStream(user._id, 'readAllUnreadSpecifiedNotes');
res();
}));
return;
});

View File

@@ -17,12 +17,12 @@ export const meta = {
}
};
export default define(meta, (ps, user) => new Promise(async (res, rej) => {
export default define(meta, async (ps, user) => {
// Compare password
const same = await bcrypt.compare(ps.password, user.password);
if (!same) {
return rej('incorrect password');
throw new Error('incorrect password');
}
// Generate secret
@@ -34,8 +34,8 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => {
}
});
res();
// Publish event
publishMainStream(user._id, 'myTokenRegenerated');
}));
return;
});

View File

@@ -26,12 +26,7 @@ export const meta = {
}
};
export default define(meta, (ps, user) => new Promise(async (res, rej) => {
// Check if both of sinceId and untilId is specified
if (ps.sinceId && ps.untilId) {
return rej('cannot set sinceId and untilId');
}
export default define(meta, async (ps, user) => {
const query = {
userId: user._id
} as any;
@@ -57,6 +52,5 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => {
sort: sort
});
// Serialize
res(await Promise.all(history.map(record => pack(record))));
}));
return await Promise.all(history.map(record => pack(record)));
});

View File

@@ -3,6 +3,7 @@ import ID, { transform } from '../../../../misc/cafy-id';
import { pack } from '../../../../models/user';
import { removePinned } from '../../../../services/i/pin';
import define from '../../define';
import { ApiError } from '../../error';
export const meta = {
stability: 'stable',
@@ -24,22 +25,24 @@ export const meta = {
'en-US': 'Target note ID'
}
}
},
errors: {
noSuchNote: {
message: 'No such note.',
code: 'NO_SUCH_NOTE',
id: '454170ce-9d63-4a43-9da1-ea10afe81e21'
},
}
};
export default define(meta, (ps, user) => new Promise(async (res, rej) => {
// Processing
try {
await removePinned(user, ps.noteId);
} catch (e) {
return rej(e.message);
}
// Serialize
const iObj = await pack(user, user, {
detail: true
export default define(meta, async (ps, user) => {
await removePinned(user, ps.noteId).catch(e => {
if (e.id === 'b302d4cf-c050-400a-bbb3-be208681f40c') throw new ApiError(meta.errors.noSuchNote);
throw e;
});
// Send response
res(iObj);
}));
return await pack(user, user, {
detail: true
});
});

View File

@@ -12,6 +12,7 @@ import extractEmojis from '../../../../misc/extract-emojis';
import extractHashtags from '../../../../misc/extract-hashtags';
import * as langmap from 'langmap';
import { updateHashtag } from '../../../../services/update-hashtag';
import { ApiError } from '../../error';
export const meta = {
desc: {
@@ -131,10 +132,36 @@ export const meta = {
'ja-JP': 'アップロードするメディアをデフォルトで「閲覧注意」として設定するか'
}
},
},
errors: {
noSuchAvatar: {
message: 'No such avatar file.',
code: 'NO_SUCH_AVATAR',
id: '539f3a45-f215-4f81-a9a8-31293640207f'
},
noSuchBanner: {
message: 'No such banner file.',
code: 'NO_SUCH_BANNER',
id: '0d8f5629-f210-41c2-9433-735831a58595'
},
avatarNotAnImage: {
message: 'The file specified as an avatar is not an image.',
code: 'AVATAR_NOT_AN_IMAGE',
id: 'f419f9f8-2f4d-46b1-9fb4-49d3a2fd7191'
},
bannerNotAnImage: {
message: 'The file specified as a banner is not an image.',
code: 'BANNER_NOT_AN_IMAGE',
id: '75aedb19-2afd-4e6d-87fc-67941256fa60'
},
}
};
export default define(meta, (ps, user, app) => new Promise(async (res, rej) => {
export default define(meta, async (ps, user, app) => {
const isSecure = user != null && app == null;
const updates = {} as any;
@@ -160,8 +187,8 @@ export default define(meta, (ps, user, app) => new Promise(async (res, rej) => {
_id: ps.avatarId
});
if (avatar == null) return rej('avatar not found');
if (!avatar.contentType.startsWith('image/')) return rej('avatar not an image');
if (avatar == null) throw new ApiError(meta.errors.noSuchAvatar);
if (!avatar.contentType.startsWith('image/')) throw new ApiError(meta.errors.avatarNotAnImage);
updates.avatarUrl = getDriveFileUrl(avatar, true);
@@ -175,8 +202,8 @@ export default define(meta, (ps, user, app) => new Promise(async (res, rej) => {
_id: ps.bannerId
});
if (banner == null) return rej('banner not found');
if (!banner.contentType.startsWith('image/')) return rej('banner not an image');
if (banner == null) throw new ApiError(meta.errors.noSuchBanner);
if (!banner.contentType.startsWith('image/')) throw new ApiError(meta.errors.bannerNotAnImage);
updates.bannerUrl = getDriveFileUrl(banner, false);
@@ -194,7 +221,7 @@ export default define(meta, (ps, user, app) => new Promise(async (res, rej) => {
_id: ps.wallpaperId
});
if (wallpaper == null) return rej('wallpaper not found');
if (wallpaper == null) throw new Error('wallpaper not found');
updates.wallpaperUrl = getDriveFileUrl(wallpaper);
@@ -233,15 +260,11 @@ export default define(meta, (ps, user, app) => new Promise(async (res, rej) => {
$set: updates
});
// Serialize
const iObj = await pack(user._id, user, {
detail: true,
includeSecrets: isSecure
});
// Send response
res(iObj);
// Publish meUpdated event
publishMainStream(user._id, 'meUpdated', iObj);
@@ -252,4 +275,6 @@ export default define(meta, (ps, user, app) => new Promise(async (res, rej) => {
// フォロワーにUpdateを配信
publishToFollowers(user._id);
}));
return iObj;
});

View File

@@ -19,7 +19,7 @@ export const meta = {
}
};
export default define(meta, (ps, user) => new Promise(async (res, rej) => {
export default define(meta, async (ps, user) => {
const x: any = {};
x[`clientSettings.${ps.name}`] = ps.value;
@@ -27,11 +27,11 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => {
$set: x
});
res();
// Publish event
publishMainStream(user._id, 'clientSettingUpdated', {
key: ps.name,
value: ps.value
});
}));
return;
});

View File

@@ -31,12 +31,12 @@ export const meta = {
}
};
export default define(meta, (ps, user) => new Promise(async (res, rej) => {
export default define(meta, async (ps, user) => {
// Compare password
const same = await bcrypt.compare(ps.password, user.password);
if (!same) {
return rej('incorrect password');
throw new Error('incorrect password');
}
await User.update(user._id, {
@@ -47,15 +47,11 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => {
}
});
// Serialize
const iObj = await pack(user._id, user, {
detail: true,
includeSecrets: true
});
// Send response
res(iObj);
// Publish meUpdated event
publishMainStream(user._id, 'meUpdated', iObj);
@@ -99,4 +95,6 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => {
apiLogger.info('Message sent: %s', info.messageId);
});
}
}));
return iObj;
});

View File

@@ -20,14 +20,14 @@ export const meta = {
}
};
export default define(meta, (ps, user) => new Promise(async (res, rej) => {
export default define(meta, async (ps, user) => {
await User.update(user._id, {
$set: {
'clientSettings.home': ps.home
}
});
res();
publishMainStream(user._id, 'homeUpdated', ps.home);
}));
return;
});

View File

@@ -19,14 +19,14 @@ export const meta = {
}
};
export default define(meta, (ps, user) => new Promise(async (res, rej) => {
export default define(meta, async (ps, user) => {
await User.update(user._id, {
$set: {
'clientSettings.mobileHome': ps.home
}
});
res();
publishMainStream(user._id, 'mobileHomeUpdated', ps.home);
}));
return;
});

View File

@@ -19,8 +19,8 @@ export const meta = {
}
};
export default define(meta, (ps, user) => new Promise(async (res, rej) => {
if (ps.id == null && ps.data == null) return rej('you need to set id and data params if home param unset');
export default define(meta, async (ps, user) => {
if (ps.id == null && ps.data == null) throw new Error('you need to set id and data params if home param unset');
let widget;
@@ -81,8 +81,8 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => {
id: ps.id, data: ps.data
});
res();
return;
} else {
rej('widget not found');
throw new Error('widget not found');
}
}));
});