From 2756f553c68082342a784ef716c62da6cea6f3ca Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 22 Feb 2019 11:46:58 +0900 Subject: [PATCH] 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 * :v: * Fix --- src/misc/identifiable-error.ts | 13 ++++ src/server/activitypub/followers.ts | 1 - src/server/activitypub/following.ts | 1 - src/server/activitypub/outbox.ts | 1 - src/server/api/api-handler.ts | 21 ++++--- src/server/api/call.ts | 60 +++++++++++++----- src/server/api/common/getters.ts | 9 +-- src/server/api/define.ts | 26 +++++--- .../api/endpoints/admin/abuse-user-reports.ts | 10 +-- src/server/api/endpoints/admin/drive/files.ts | 6 +- .../api/endpoints/admin/drive/show-file.ts | 17 +++-- src/server/api/endpoints/admin/emoji/add.ts | 8 +-- src/server/api/endpoints/admin/emoji/list.ts | 8 +-- .../api/endpoints/admin/emoji/remove.ts | 8 +-- .../api/endpoints/admin/emoji/update.ts | 8 +-- .../admin/federation/remove-all-following.ts | 6 +- .../admin/federation/update-instance.ts | 8 +-- src/server/api/endpoints/admin/invite.ts | 8 +-- .../api/endpoints/admin/moderators/add.ts | 8 +-- .../api/endpoints/admin/moderators/remove.ts | 8 +-- src/server/api/endpoints/admin/queue/clear.ts | 6 +- .../admin/remove-abuse-user-report.ts | 8 +-- .../api/endpoints/admin/reset-password.ts | 22 +++---- src/server/api/endpoints/admin/show-user.ts | 10 +-- src/server/api/endpoints/admin/show-users.ts | 6 +- .../api/endpoints/admin/silence-user.ts | 10 +-- .../api/endpoints/admin/suspend-user.ts | 20 +++--- .../api/endpoints/admin/unsilence-user.ts | 8 +-- .../api/endpoints/admin/unsuspend-user.ts | 16 ++--- .../api/endpoints/admin/unverify-user.ts | 16 ++--- src/server/api/endpoints/admin/update-meta.ts | 6 +- .../api/endpoints/admin/update-remote-user.ts | 7 ++- src/server/api/endpoints/admin/verify-user.ts | 16 ++--- .../api/endpoints/aggregation/hashtags.ts | 8 +-- src/server/api/endpoints/ap/show.ts | 22 +++++-- src/server/api/endpoints/app/create.ts | 9 ++- src/server/api/endpoints/app/show.ts | 20 ++++-- src/server/api/endpoints/auth/accept.ts | 18 ++++-- .../api/endpoints/auth/session/generate.ts | 20 ++++-- src/server/api/endpoints/auth/session/show.ts | 18 ++++-- .../api/endpoints/auth/session/userkey.ts | 36 ++++++++--- src/server/api/endpoints/blocking/create.ts | 36 ++++++++--- src/server/api/endpoints/blocking/delete.ts | 36 ++++++++--- src/server/api/endpoints/blocking/list.ts | 11 +--- .../api/endpoints/charts/active-users.ts | 8 +-- src/server/api/endpoints/charts/drive.ts | 8 +-- src/server/api/endpoints/charts/federation.ts | 8 +-- src/server/api/endpoints/charts/hashtag.ts | 8 +-- src/server/api/endpoints/charts/instance.ts | 8 +-- src/server/api/endpoints/charts/network.ts | 8 +-- src/server/api/endpoints/charts/notes.ts | 8 +-- src/server/api/endpoints/charts/user/drive.ts | 8 +-- .../api/endpoints/charts/user/following.ts | 8 +-- src/server/api/endpoints/charts/user/notes.ts | 8 +-- .../api/endpoints/charts/user/reactions.ts | 8 +-- src/server/api/endpoints/charts/users.ts | 8 +-- src/server/api/endpoints/drive.ts | 51 ++++++++------- src/server/api/endpoints/drive/files.ts | 11 +--- .../endpoints/drive/files/attached_notes.ts | 19 ++++-- .../endpoints/drive/files/check_existence.ts | 10 +-- .../api/endpoints/drive/files/create.ts | 25 +++++--- .../api/endpoints/drive/files/delete.ts | 25 ++++++-- src/server/api/endpoints/drive/files/find.ts | 6 +- src/server/api/endpoints/drive/files/show.ts | 36 ++++++++--- .../api/endpoints/drive/files/update.ts | 37 ++++++++--- .../endpoints/drive/files/upload_from_url.ts | 6 +- src/server/api/endpoints/drive/folders.ts | 11 +--- .../api/endpoints/drive/folders/create.ts | 21 ++++--- .../api/endpoints/drive/folders/delete.ts | 25 ++++++-- .../api/endpoints/drive/folders/find.ts | 6 +- .../api/endpoints/drive/folders/show.ts | 20 ++++-- .../api/endpoints/drive/folders/update.ts | 37 ++++++++--- src/server/api/endpoints/drive/stream.ts | 11 +--- .../api/endpoints/federation/instances.ts | 6 +- .../api/endpoints/federation/show-instance.ts | 6 +- src/server/api/endpoints/following/create.ts | 51 ++++++++++++--- src/server/api/endpoints/following/delete.ts | 35 ++++++++--- .../endpoints/following/requests/accept.ts | 17 +++-- .../endpoints/following/requests/cancel.ts | 26 ++++++-- .../api/endpoints/following/requests/list.ts | 9 +-- .../endpoints/following/requests/reject.ts | 17 +++-- .../api/endpoints/games/reversi/games.ts | 14 ++--- .../api/endpoints/games/reversi/games/show.ts | 19 ++++-- .../games/reversi/games/surrender.ts | 33 ++++++++-- .../endpoints/games/reversi/invitations.ts | 7 +-- .../api/endpoints/games/reversi/match.ts | 36 +++++++---- .../endpoints/games/reversi/match/cancel.ts | 6 +- src/server/api/endpoints/hashtags/list.ts | 6 +- src/server/api/endpoints/hashtags/search.ts | 6 +- src/server/api/endpoints/hashtags/trend.ts | 8 +-- src/server/api/endpoints/hashtags/users.ts | 6 +- src/server/api/endpoints/i.ts | 9 ++- src/server/api/endpoints/i/2fa/done.ts | 10 +-- src/server/api/endpoints/i/2fa/register.ts | 24 +++---- src/server/api/endpoints/i/2fa/unregister.ts | 8 +-- src/server/api/endpoints/i/authorized_apps.ts | 8 +-- src/server/api/endpoints/i/change_password.ts | 8 +-- .../i/clear-follow-request-notification.ts | 6 +- src/server/api/endpoints/i/delete-account.ts | 8 +-- src/server/api/endpoints/i/export-blocking.ts | 6 +- .../api/endpoints/i/export-following.ts | 6 +- src/server/api/endpoints/i/export-mute.ts | 6 +- src/server/api/endpoints/i/export-notes.ts | 6 +- src/server/api/endpoints/i/favorites.ts | 11 +--- src/server/api/endpoints/i/notifications.ts | 13 ++-- src/server/api/endpoints/i/pin.ts | 45 +++++++++----- .../i/read_all_messaging_messages.ts | 6 +- .../api/endpoints/i/read_all_unread_notes.ts | 6 +- .../api/endpoints/i/regenerate_token.ts | 10 +-- src/server/api/endpoints/i/signin_history.ts | 12 +--- src/server/api/endpoints/i/unpin.ts | 31 +++++----- src/server/api/endpoints/i/update.ts | 47 ++++++++++---- .../api/endpoints/i/update_client_setting.ts | 8 +-- src/server/api/endpoints/i/update_email.ts | 12 ++-- src/server/api/endpoints/i/update_home.ts | 8 +-- .../api/endpoints/i/update_mobile_home.ts | 8 +-- src/server/api/endpoints/i/update_widget.ts | 10 +-- src/server/api/endpoints/messaging/history.ts | 6 +- .../api/endpoints/messaging/messages.ts | 32 +++++----- .../endpoints/messaging/messages/create.ts | 46 ++++++++++---- .../endpoints/messaging/messages/delete.ts | 17 +++-- .../api/endpoints/messaging/messages/read.ts | 17 +++-- src/server/api/endpoints/meta.ts | 6 +- src/server/api/endpoints/mute/create.ts | 33 ++++++++-- src/server/api/endpoints/mute/delete.ts | 33 ++++++++-- src/server/api/endpoints/mute/list.ts | 11 +--- src/server/api/endpoints/my/apps.ts | 10 ++- src/server/api/endpoints/notes.ts | 23 +++---- .../api/endpoints/notes/conversation.ts | 26 +++++--- src/server/api/endpoints/notes/create.ts | 62 ++++++++++++++----- src/server/api/endpoints/notes/delete.ts | 35 +++++++---- .../api/endpoints/notes/favorites/create.ts | 26 ++++++-- .../api/endpoints/notes/favorites/delete.ts | 26 ++++++-- src/server/api/endpoints/notes/featured.ts | 41 ++++++------ .../api/endpoints/notes/global-timeline.ts | 36 ++++++----- .../api/endpoints/notes/hybrid-timeline.ts | 38 ++++++------ .../api/endpoints/notes/local-timeline.ts | 38 ++++++------ src/server/api/endpoints/notes/mentions.ts | 22 +++---- .../endpoints/notes/polls/recommendation.ts | 49 +++++++-------- src/server/api/endpoints/notes/polls/vote.ts | 46 +++++++++++--- src/server/api/endpoints/notes/reactions.ts | 44 +++++++------ .../api/endpoints/notes/reactions/create.ts | 44 +++++++++---- .../api/endpoints/notes/reactions/delete.ts | 36 +++++++---- src/server/api/endpoints/notes/renotes.ts | 40 ++++++------ src/server/api/endpoints/notes/replies.ts | 6 +- src/server/api/endpoints/notes/search.ts | 61 +++++++++--------- .../api/endpoints/notes/search_by_tag.ts | 22 +++---- src/server/api/endpoints/notes/show.ts | 31 ++++++---- src/server/api/endpoints/notes/state.ts | 8 +-- src/server/api/endpoints/notes/timeline.ts | 26 +++----- .../api/endpoints/notes/user-list-timeline.ts | 32 +++++----- .../api/endpoints/notes/watching/create.ts | 28 +++++---- .../api/endpoints/notes/watching/delete.ts | 28 +++++---- .../notifications/mark_all_as_read.ts | 19 +++--- src/server/api/endpoints/stats.ts | 6 +- src/server/api/endpoints/sw/register.ts | 12 ++-- .../api/endpoints/username/available.ts | 24 ++++--- src/server/api/endpoints/users.ts | 6 +- src/server/api/endpoints/users/followers.ts | 20 ++++-- src/server/api/endpoints/users/following.ts | 20 ++++-- .../users/get_frequently_replied_users.ts | 19 ++++-- .../api/endpoints/users/lists/create.ts | 8 +-- .../api/endpoints/users/lists/delete.ts | 17 +++-- src/server/api/endpoints/users/lists/list.ts | 7 +-- src/server/api/endpoints/users/lists/pull.ts | 25 ++++++-- src/server/api/endpoints/users/lists/push.ts | 33 +++++++--- src/server/api/endpoints/users/lists/show.ts | 17 +++-- .../api/endpoints/users/lists/update.ts | 19 ++++-- src/server/api/endpoints/users/notes.ts | 58 ++++++----------- .../api/endpoints/users/recommendation.ts | 51 ++++++++------- src/server/api/endpoints/users/relation.ts | 6 +- .../api/endpoints/users/report-abuse.ts | 34 +++++++--- src/server/api/endpoints/users/search.ts | 6 +- src/server/api/endpoints/users/show.ts | 44 ++++++++----- src/server/api/error.ts | 23 +++++++ src/services/following/create.ts | 5 +- src/services/following/requests/cancel.ts | 3 +- src/services/i/pin.ts | 9 +-- src/services/note/polls/vote.ts | 1 - src/services/note/reaction/create.ts | 31 +++++----- src/services/note/reaction/delete.ts | 11 ++-- 181 files changed, 2010 insertions(+), 1322 deletions(-) create mode 100644 src/misc/identifiable-error.ts create mode 100644 src/server/api/error.ts diff --git a/src/misc/identifiable-error.ts b/src/misc/identifiable-error.ts new file mode 100644 index 000000000..1edd26cd1 --- /dev/null +++ b/src/misc/identifiable-error.ts @@ -0,0 +1,13 @@ +/** + * ID付きエラー + */ +export class IdentifiableError extends Error { + public message: string; + public id: string; + + constructor(id: string, message?: string) { + super(message); + this.message = message; + this.id = id; + } +} diff --git a/src/server/activitypub/followers.ts b/src/server/activitypub/followers.ts index 563b3da65..002576b2e 100644 --- a/src/server/activitypub/followers.ts +++ b/src/server/activitypub/followers.ts @@ -48,7 +48,6 @@ export default async (ctx: Router.IRouterContext) => { const partOf = `${config.url}/users/${userId}/followers`; if (page) { - // Construct query const query = { followeeId: user._id } as any; diff --git a/src/server/activitypub/following.ts b/src/server/activitypub/following.ts index f23e177e2..0d7486f68 100644 --- a/src/server/activitypub/following.ts +++ b/src/server/activitypub/following.ts @@ -48,7 +48,6 @@ export default async (ctx: Router.IRouterContext) => { const partOf = `${config.url}/users/${userId}/following`; if (page) { - // Construct query const query = { followerId: user._id } as any; diff --git a/src/server/activitypub/outbox.ts b/src/server/activitypub/outbox.ts index 8b65ce993..ff8f884b1 100644 --- a/src/server/activitypub/outbox.ts +++ b/src/server/activitypub/outbox.ts @@ -78,7 +78,6 @@ export default async (ctx: Router.IRouterContext) => { } //#endregion - // Issue query const notes = await Note .find(query, { limit: limit, diff --git a/src/server/api/api-handler.ts b/src/server/api/api-handler.ts index 0eef9ab5c..39caba6b1 100644 --- a/src/server/api/api-handler.ts +++ b/src/server/api/api-handler.ts @@ -5,18 +5,17 @@ import authenticate from './authenticate'; import call from './call'; import { IUser } from '../../models/user'; import { IApp } from '../../models/app'; +import { ApiError } from './error'; export default async (endpoint: IEndpoint, ctx: Koa.BaseContext) => { const body = ctx.is('multipart/form-data') ? (ctx.req as any).body : ctx.request.body; - const reply = (x?: any, y?: any) => { - if (x === undefined) { + const reply = (x?: any, y?: ApiError) => { + if (x == null) { ctx.status = 204; } else if (typeof x === 'number') { ctx.status = x; - ctx.body = { - error: x === 500 ? 'INTERNAL_ERROR' : y - }; + ctx.body = y; } else { ctx.body = x; } @@ -29,7 +28,11 @@ export default async (endpoint: IEndpoint, ctx: Koa.BaseContext) => { try { [user, app] = await authenticate(body['i']); } catch (e) { - reply(403, 'AUTHENTICATION_FAILED'); + reply(403, new ApiError({ + message: 'Authentication failed. Please ensure your token is correct.', + code: 'AUTHENTICATION_FAILED', + id: 'b0a7f5f8-dc2f-4171-b91f-de88ad238e14' + })); return; } @@ -39,7 +42,11 @@ export default async (endpoint: IEndpoint, ctx: Koa.BaseContext) => { try { res = await call(endpoint.name, user, app, body, (ctx.req as any).file); } catch (e) { - reply(400, e); + if (e.kind == 'client') { + reply(400, e); + } else { + reply(500, e); + } return; } diff --git a/src/server/api/call.ts b/src/server/api/call.ts index 8efc67960..2b447b30f 100644 --- a/src/server/api/call.ts +++ b/src/server/api/call.ts @@ -2,6 +2,15 @@ import limiter from './limiter'; import { IUser } from '../../models/user'; import { IApp } from '../../models/app'; import endpoints from './endpoints'; +import { ApiError } from './error'; +import { apiLogger } from './logger'; +import { Response } from './define'; + +const accessDenied = { + message: 'Access denied.', + code: 'ACCESS_DENIED', + id: '56f35758-7dd5-468b-8439-5d6fb8ec9b8e' +}; export default async (endpoint: string, user: IUser, app: IApp, data: any, file?: any) => { const isSecure = user != null && app == null; @@ -9,31 +18,43 @@ export default async (endpoint: string, user: IUser, app: IApp, data: any, file? const ep = endpoints.find(e => e.name === endpoint); if (ep == null) { - throw 'ENDPOINT_NOT_FOUND'; + throw new ApiError({ + message: 'No such endpoint.', + code: 'NO_SUCH_ENDPOINT', + id: 'f8080b67-5f9c-4eb7-8c18-7f1eeae8f709', + }); } if (ep.meta.secure && !isSecure) { - throw 'ACCESS_DENIED'; + throw new ApiError(accessDenied); } if (ep.meta.requireCredential && user == null) { - throw 'CREDENTIAL_REQUIRED'; + throw new ApiError({ + message: 'Credential required.', + code: 'CREDENTIAL_REQUIRED', + id: '1384574d-a912-4b81-8601-c7b1c4085df1', + }); } if (ep.meta.requireCredential && user.isSuspended) { - throw 'YOUR_ACCOUNT_HAS_BEEN_SUSPENDED'; + throw new ApiError(accessDenied, { reason: 'Your account has been suspended.' }); } if (ep.meta.requireAdmin && !user.isAdmin) { - throw 'YOU_ARE_NOT_ADMIN'; + throw new ApiError(accessDenied, { reason: 'You are not the admin.' }); } if (ep.meta.requireModerator && !user.isAdmin && !user.isModerator) { - throw 'YOU_ARE_NOT_MODERATOR'; + throw new ApiError(accessDenied, { reason: 'You are not a moderator.' }); } if (app && ep.meta.kind && !app.permission.some(p => p === ep.meta.kind)) { - throw 'PERMISSION_DENIED'; + throw new ApiError({ + message: 'Your app does not have the necessary permissions to use this endpoint.', + code: 'PERMISSION_DENIED', + id: '1370e5b7-d4eb-4566-bb1d-7748ee6a1838', + }); } if (ep.meta.requireCredential && ep.meta.limit) { @@ -41,24 +62,31 @@ export default async (endpoint: string, user: IUser, app: IApp, data: any, file? await limiter(ep, user); // Rate limit } catch (e) { // drop request if limit exceeded - throw 'RATE_LIMIT_EXCEEDED'; + throw new ApiError({ + message: 'Rate limit exceeded. Please try again later.', + code: 'RATE_LIMIT_EXCEEDED', + id: 'd5826d14-3982-4d2e-8011-b9e9f02499ef', + }); } } - let res; + let res: Response; // API invoking try { res = await ep.exec(data, user, app, file); } catch (e) { - if (e && e.name == 'INVALID_PARAM') { - throw { - code: e.name, - param: e.param, - reason: e.message - }; - } else { + if (e instanceof ApiError) { throw e; + } else { + apiLogger.error(e); + throw new ApiError(null, { + e: { + message: e.message, + code: e.code, + stack: e.stack + } + }); } } diff --git a/src/server/api/common/getters.ts b/src/server/api/common/getters.ts index 1cd054cab..f74f6130c 100644 --- a/src/server/api/common/getters.ts +++ b/src/server/api/common/getters.ts @@ -1,6 +1,7 @@ import * as mongo from 'mongodb'; -import Note from "../../../models/note"; -import User, { isRemoteUser, isLocalUser } from "../../../models/user"; +import Note from '../../../models/note'; +import User, { isRemoteUser, isLocalUser } from '../../../models/user'; +import { IdentifiableError } from '../../../misc/identifiable-error'; /** * Get valied note for API processing @@ -12,7 +13,7 @@ export async function getValiedNote(noteId: mongo.ObjectID) { }); if (note === null) { - throw 'note not found'; + throw new IdentifiableError('9725d0ce-ba28-4dde-95a7-2cbb2c15de24', 'No such note.'); } return note; @@ -27,7 +28,7 @@ export async function getUser(userId: mongo.ObjectID) { }); if (user == null) { - throw 'user not found'; + throw new IdentifiableError('15348ddd-432d-49c2-8a5a-8069753becff', 'No such user.'); } return user; diff --git a/src/server/api/define.ts b/src/server/api/define.ts index de9a8caf9..f2fababc3 100644 --- a/src/server/api/define.ts +++ b/src/server/api/define.ts @@ -2,6 +2,7 @@ import * as fs from 'fs'; import { ILocalUser } from '../../models/user'; import { IApp } from '../../models/app'; import { IEndpointMeta } from './endpoints'; +import { ApiError } from './error'; type Params = { [P in keyof T['params']]: T['params'][P]['transform'] extends Function @@ -9,13 +10,19 @@ type Params = { : ReturnType[0]; }; -export default function (meta: T, cb: (params: Params, user: ILocalUser, app: IApp, file?: any, cleanup?: Function) => Promise): (params: any, user: ILocalUser, app: IApp, file?: any) => Promise { +export type Response = Record | void; + +export default function (meta: T, cb: (params: Params, user: ILocalUser, app: IApp, file?: any, cleanup?: Function) => Promise): (params: any, user: ILocalUser, app: IApp, file?: any) => Promise { return (params: any, user: ILocalUser, app: IApp, file?: any) => { function cleanup() { fs.unlink(file.path, () => {}); } - if (meta.requireFile && file == null) return Promise.reject('file required'); + if (meta.requireFile && file == null) return Promise.reject(new ApiError({ + message: 'File required.', + code: 'FILE_REQUIRED', + id: '4267801e-70d1-416a-b011-4ee502885d8b', + })); const [ps, pserr] = getParams(meta, params); if (pserr) { @@ -27,17 +34,22 @@ export default function (meta: T, cb: (params: Params(defs: T, params: any): [Params, Error] { +function getParams(defs: T, params: any): [Params, ApiError] { if (defs.params == null) return [params, null]; const x: any = {}; - let err: Error = null; + let err: ApiError = null; Object.entries(defs.params).some(([k, def]) => { const [v, e] = def.validator.get(params[k]); if (e) { - err = new Error(e.message); - err.name = 'INVALID_PARAM'; - (err as any).param = k; + err = new ApiError({ + message: 'Invalid param.', + code: 'INVALID_PARAM', + id: '3d81ceae-475f-4600-b2a8-2bc116157532', + }, { + param: k, + reason: e.message + }); return true; } else { if (v === undefined && def.hasOwnProperty('default')) { diff --git a/src/server/api/endpoints/admin/abuse-user-reports.ts b/src/server/api/endpoints/admin/abuse-user-reports.ts index fc95dd4bb..602171abb 100644 --- a/src/server/api/endpoints/admin/abuse-user-reports.ts +++ b/src/server/api/endpoints/admin/abuse-user-reports.ts @@ -25,11 +25,7 @@ export const meta = { } }; -export default define(meta, (ps) => new Promise(async (res, rej) => { - if (ps.sinceId && ps.untilId) { - return rej('cannot set sinceId and untilId'); - } - +export default define(meta, async (ps) => { const sort = { _id: -1 }; @@ -51,5 +47,5 @@ export default define(meta, (ps) => new Promise(async (res, rej) => { sort: sort }); - res(await packMany(reports)); -})); + return await packMany(reports); +}); diff --git a/src/server/api/endpoints/admin/drive/files.ts b/src/server/api/endpoints/admin/drive/files.ts index 3a837e418..318b2de1b 100644 --- a/src/server/api/endpoints/admin/drive/files.ts +++ b/src/server/api/endpoints/admin/drive/files.ts @@ -46,7 +46,7 @@ const sort: any = { // < https://github.com/Microsoft/TypeScript/issues/1863 [fallback]: { _id: -1 } }; -export default define(meta, (ps, me) => new Promise(async (res, rej) => { +export default define(meta, async (ps, me) => { const q = { 'metadata.deletedAt': { $exists: false }, } as any; @@ -61,5 +61,5 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { skip: ps.offset }); - res(await packMany(files, { detail: true, withUser: true, self: true })); -})); + return await packMany(files, { detail: true, withUser: true, self: true }); +}); diff --git a/src/server/api/endpoints/admin/drive/show-file.ts b/src/server/api/endpoints/admin/drive/show-file.ts index 6dfab1964..9b993584e 100644 --- a/src/server/api/endpoints/admin/drive/show-file.ts +++ b/src/server/api/endpoints/admin/drive/show-file.ts @@ -2,6 +2,7 @@ import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id'; import define from '../../../define'; import DriveFile from '../../../../../models/drive-file'; +import { ApiError } from '../../../error'; export const meta = { requireCredential: true, @@ -12,17 +13,25 @@ export const meta = { validator: $.type(ID), transform: transform, }, + }, + + errors: { + noSuchFile: { + message: 'No such file.', + code: 'NO_SUCH_FILE', + id: 'caf3ca38-c6e5-472e-a30c-b05377dcc240' + } } }; -export default define(meta, (ps, me) => new Promise(async (res, rej) => { +export default define(meta, async (ps, me) => { const file = await DriveFile.findOne({ _id: ps.fileId }); if (file == null) { - return rej('file not found'); + throw new ApiError(meta.errors.noSuchFile); } - res(file); -})); + return file; +}); diff --git a/src/server/api/endpoints/admin/emoji/add.ts b/src/server/api/endpoints/admin/emoji/add.ts index cab384164..b84e9d34d 100644 --- a/src/server/api/endpoints/admin/emoji/add.ts +++ b/src/server/api/endpoints/admin/emoji/add.ts @@ -26,7 +26,7 @@ export const meta = { } }; -export default define(meta, (ps) => new Promise(async (res, rej) => { +export default define(meta, async (ps) => { const emoji = await Emoji.insert({ updatedAt: new Date(), name: ps.name, @@ -35,7 +35,7 @@ export default define(meta, (ps) => new Promise(async (res, rej) => { url: ps.url }); - res({ + return { id: emoji._id - }); -})); + }; +}); diff --git a/src/server/api/endpoints/admin/emoji/list.ts b/src/server/api/endpoints/admin/emoji/list.ts index 624fa7845..f51a536fa 100644 --- a/src/server/api/endpoints/admin/emoji/list.ts +++ b/src/server/api/endpoints/admin/emoji/list.ts @@ -18,16 +18,16 @@ export const meta = { } }; -export default define(meta, (ps) => new Promise(async (res, rej) => { +export default define(meta, async (ps) => { const emojis = await Emoji.find({ host: ps.host }); - res(emojis.map(e => ({ + return emojis.map(e => ({ id: e._id, name: e.name, aliases: e.aliases, host: e.host, url: e.url - }))); -})); + })); +}); diff --git a/src/server/api/endpoints/admin/emoji/remove.ts b/src/server/api/endpoints/admin/emoji/remove.ts index 1d6ed11b6..9699a0012 100644 --- a/src/server/api/endpoints/admin/emoji/remove.ts +++ b/src/server/api/endpoints/admin/emoji/remove.ts @@ -18,14 +18,14 @@ export const meta = { } }; -export default define(meta, (ps) => new Promise(async (res, rej) => { +export default define(meta, async (ps) => { const emoji = await Emoji.findOne({ _id: ps.id }); - if (emoji == null) return rej('emoji not found'); + if (emoji == null) throw new Error('emoji not found'); await Emoji.remove({ _id: emoji._id }); - res(); -})); + return; +}); diff --git a/src/server/api/endpoints/admin/emoji/update.ts b/src/server/api/endpoints/admin/emoji/update.ts index cbcc07fd4..7d065eadd 100644 --- a/src/server/api/endpoints/admin/emoji/update.ts +++ b/src/server/api/endpoints/admin/emoji/update.ts @@ -30,12 +30,12 @@ export const meta = { } }; -export default define(meta, (ps) => new Promise(async (res, rej) => { +export default define(meta, async (ps) => { const emoji = await Emoji.findOne({ _id: ps.id }); - if (emoji == null) return rej('emoji not found'); + if (emoji == null) throw new Error('emoji not found'); await Emoji.update({ _id: emoji._id }, { $set: { @@ -46,5 +46,5 @@ export default define(meta, (ps) => new Promise(async (res, rej) => { } }); - res(); -})); + return; +}); diff --git a/src/server/api/endpoints/admin/federation/remove-all-following.ts b/src/server/api/endpoints/admin/federation/remove-all-following.ts index 8daff139b..1a272aeb0 100644 --- a/src/server/api/endpoints/admin/federation/remove-all-following.ts +++ b/src/server/api/endpoints/admin/federation/remove-all-following.ts @@ -15,7 +15,7 @@ export const meta = { } }; -export default define(meta, (ps, me) => new Promise(async (res, rej) => { +export default define(meta, async (ps, me) => { const followings = await Following.find({ '_follower.host': ps.host }); @@ -29,5 +29,5 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { deleteFollowing(pair[0], pair[1]); } - res(); -})); + return; +}); diff --git a/src/server/api/endpoints/admin/federation/update-instance.ts b/src/server/api/endpoints/admin/federation/update-instance.ts index 579b437aa..50eb4ba13 100644 --- a/src/server/api/endpoints/admin/federation/update-instance.ts +++ b/src/server/api/endpoints/admin/federation/update-instance.ts @@ -21,11 +21,11 @@ export const meta = { } }; -export default define(meta, (ps, me) => new Promise(async (res, rej) => { +export default define(meta, async (ps, me) => { const instance = await Instance.findOne({ host: ps.host }); if (instance == null) { - return rej('instance not found'); + throw new Error('instance not found'); } Instance.update({ host: ps.host }, { @@ -35,5 +35,5 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { } }); - res(); -})); + return; +}); diff --git a/src/server/api/endpoints/admin/invite.ts b/src/server/api/endpoints/admin/invite.ts index ebfcb8445..3c2b32349 100644 --- a/src/server/api/endpoints/admin/invite.ts +++ b/src/server/api/endpoints/admin/invite.ts @@ -13,7 +13,7 @@ export const meta = { params: {} }; -export default define(meta, (ps) => new Promise(async (res, rej) => { +export default define(meta, async (ps) => { const code = rndstr({ length: 5, chars: '0-9' }); await RegistrationTicket.insert({ @@ -21,7 +21,7 @@ export default define(meta, (ps) => new Promise(async (res, rej) => { code: code }); - res({ + return { code: code - }); -})); + }; +}); diff --git a/src/server/api/endpoints/admin/moderators/add.ts b/src/server/api/endpoints/admin/moderators/add.ts index 4b4675c56..58515381d 100644 --- a/src/server/api/endpoints/admin/moderators/add.ts +++ b/src/server/api/endpoints/admin/moderators/add.ts @@ -24,13 +24,13 @@ export const meta = { } }; -export default define(meta, (ps) => new Promise(async (res, rej) => { +export default define(meta, async (ps) => { const user = await User.findOne({ _id: ps.userId }); if (user == null) { - return rej('user not found'); + throw new Error('user not found'); } await User.update({ @@ -41,5 +41,5 @@ export default define(meta, (ps) => new Promise(async (res, rej) => { } }); - res(); -})); + return; +}); diff --git a/src/server/api/endpoints/admin/moderators/remove.ts b/src/server/api/endpoints/admin/moderators/remove.ts index 2b9da61bb..75627fb63 100644 --- a/src/server/api/endpoints/admin/moderators/remove.ts +++ b/src/server/api/endpoints/admin/moderators/remove.ts @@ -24,13 +24,13 @@ export const meta = { } }; -export default define(meta, (ps) => new Promise(async (res, rej) => { +export default define(meta, async (ps) => { const user = await User.findOne({ _id: ps.userId }); if (user == null) { - return rej('user not found'); + throw new Error('user not found'); } await User.update({ @@ -41,5 +41,5 @@ export default define(meta, (ps) => new Promise(async (res, rej) => { } }); - res(); -})); + return; +}); diff --git a/src/server/api/endpoints/admin/queue/clear.ts b/src/server/api/endpoints/admin/queue/clear.ts index 4da8d2c72..9a1e56595 100644 --- a/src/server/api/endpoints/admin/queue/clear.ts +++ b/src/server/api/endpoints/admin/queue/clear.ts @@ -8,8 +8,8 @@ export const meta = { params: {} }; -export default define(meta, (ps) => new Promise(async (res, rej) => { +export default define(meta, async (ps) => { destroy(); - res(); -})); + return; +}); diff --git a/src/server/api/endpoints/admin/remove-abuse-user-report.ts b/src/server/api/endpoints/admin/remove-abuse-user-report.ts index 4d068a410..c0c40c85d 100644 --- a/src/server/api/endpoints/admin/remove-abuse-user-report.ts +++ b/src/server/api/endpoints/admin/remove-abuse-user-report.ts @@ -15,18 +15,18 @@ export const meta = { } }; -export default define(meta, (ps) => new Promise(async (res, rej) => { +export default define(meta, async (ps) => { const report = await AbuseUserReport.findOne({ _id: ps.reportId }); if (report == null) { - return rej('report not found'); + throw new Error('report not found'); } await AbuseUserReport.remove({ _id: report._id }); - res(); -})); + return; +}); diff --git a/src/server/api/endpoints/admin/reset-password.ts b/src/server/api/endpoints/admin/reset-password.ts index c072c12e0..b274dc2a7 100644 --- a/src/server/api/endpoints/admin/reset-password.ts +++ b/src/server/api/endpoints/admin/reset-password.ts @@ -25,17 +25,17 @@ export const meta = { } }; -export default define(meta, (ps) => new Promise(async (res, rej) => { +export default define(meta, async (ps) => { const user = await User.findOne({ _id: ps.userId }); if (user == null) { - return rej('user not found'); + throw new Error('user not found'); } if (user.isAdmin) { - return rej('cannot reset password of admin'); + throw new Error('cannot reset password of admin'); } const passwd = rndstr('a-zA-Z0-9', 8); @@ -46,12 +46,12 @@ export default define(meta, (ps) => new Promise(async (res, rej) => { await User.findOneAndUpdate({ _id: user._id }, { - $set: { - password: hash - } - }); - - res({ - password: passwd + $set: { + password: hash + } }); -})); + + return { + password: passwd + }; +}); diff --git a/src/server/api/endpoints/admin/show-user.ts b/src/server/api/endpoints/admin/show-user.ts index 490b68535..5fcf4c386 100644 --- a/src/server/api/endpoints/admin/show-user.ts +++ b/src/server/api/endpoints/admin/show-user.ts @@ -23,18 +23,18 @@ export const meta = { } }; -export default define(meta, (ps, me) => new Promise(async (res, rej) => { +export default define(meta, async (ps, me) => { const user = await User.findOne({ _id: ps.userId }); if (user == null) { - return rej('user not found'); + throw new Error('user not found'); } if (me.isModerator && user.isAdmin) { - return rej('cannot show info of admin'); + throw new Error('cannot show info of admin'); } - res(user); -})); + return user; +}); diff --git a/src/server/api/endpoints/admin/show-users.ts b/src/server/api/endpoints/admin/show-users.ts index 3646f96d7..9d1c5ea98 100644 --- a/src/server/api/endpoints/admin/show-users.ts +++ b/src/server/api/endpoints/admin/show-users.ts @@ -63,7 +63,7 @@ const sort: any = { // < https://github.com/Microsoft/TypeScript/issues/1863 [fallback]: { _id: -1 } }; -export default define(meta, (ps, me) => new Promise(async (res, rej) => { +export default define(meta, async (ps, me) => { const q = { $and: [] } as any; @@ -99,5 +99,5 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { skip: ps.offset }); - res(await Promise.all(users.map(user => pack(user, me, { detail: true })))); -})); + return await Promise.all(users.map(user => pack(user, me, { detail: true }))); +}); diff --git a/src/server/api/endpoints/admin/silence-user.ts b/src/server/api/endpoints/admin/silence-user.ts index 7b1090a89..532025563 100644 --- a/src/server/api/endpoints/admin/silence-user.ts +++ b/src/server/api/endpoints/admin/silence-user.ts @@ -24,17 +24,17 @@ export const meta = { } }; -export default define(meta, (ps) => new Promise(async (res, rej) => { +export default define(meta, async (ps) => { const user = await User.findOne({ _id: ps.userId }); if (user == null) { - return rej('user not found'); + throw new Error('user not found'); } if (user.isAdmin) { - return rej('cannot silence admin'); + throw new Error('cannot silence admin'); } await User.findOneAndUpdate({ @@ -45,5 +45,5 @@ export default define(meta, (ps) => new Promise(async (res, rej) => { } }); - res(); -})); + return; +}); diff --git a/src/server/api/endpoints/admin/suspend-user.ts b/src/server/api/endpoints/admin/suspend-user.ts index 2ec519688..321c3955e 100644 --- a/src/server/api/endpoints/admin/suspend-user.ts +++ b/src/server/api/endpoints/admin/suspend-user.ts @@ -24,30 +24,30 @@ export const meta = { } }; -export default define(meta, (ps) => new Promise(async (res, rej) => { +export default define(meta, async (ps) => { const user = await User.findOne({ _id: ps.userId }); if (user == null) { - return rej('user not found'); + throw new Error('user not found'); } if (user.isAdmin) { - return rej('cannot suspend admin'); + throw new Error('cannot suspend admin'); } if (user.isModerator) { - return rej('cannot suspend moderator'); + throw new Error('cannot suspend moderator'); } await User.findOneAndUpdate({ _id: user._id }, { - $set: { - isSuspended: true - } - }); + $set: { + isSuspended: true + } + }); - res(); -})); + return; +}); diff --git a/src/server/api/endpoints/admin/unsilence-user.ts b/src/server/api/endpoints/admin/unsilence-user.ts index a01bfbb6d..bb039eb8d 100644 --- a/src/server/api/endpoints/admin/unsilence-user.ts +++ b/src/server/api/endpoints/admin/unsilence-user.ts @@ -24,13 +24,13 @@ export const meta = { } }; -export default define(meta, (ps) => new Promise(async (res, rej) => { +export default define(meta, async (ps) => { const user = await User.findOne({ _id: ps.userId }); if (user == null) { - return rej('user not found'); + throw new Error('user not found'); } await User.findOneAndUpdate({ @@ -41,5 +41,5 @@ export default define(meta, (ps) => new Promise(async (res, rej) => { } }); - res(); -})); + return; +}); diff --git a/src/server/api/endpoints/admin/unsuspend-user.ts b/src/server/api/endpoints/admin/unsuspend-user.ts index 4b5324626..4732e4436 100644 --- a/src/server/api/endpoints/admin/unsuspend-user.ts +++ b/src/server/api/endpoints/admin/unsuspend-user.ts @@ -24,22 +24,22 @@ export const meta = { } }; -export default define(meta, (ps) => new Promise(async (res, rej) => { +export default define(meta, async (ps) => { const user = await User.findOne({ _id: ps.userId }); if (user == null) { - return rej('user not found'); + throw new Error('user not found'); } await User.findOneAndUpdate({ _id: user._id }, { - $set: { - isSuspended: false - } - }); + $set: { + isSuspended: false + } + }); - res(); -})); + return; +}); diff --git a/src/server/api/endpoints/admin/unverify-user.ts b/src/server/api/endpoints/admin/unverify-user.ts index 3e044ffed..857cceb1e 100644 --- a/src/server/api/endpoints/admin/unverify-user.ts +++ b/src/server/api/endpoints/admin/unverify-user.ts @@ -24,22 +24,22 @@ export const meta = { } }; -export default define(meta, (ps) => new Promise(async (res, rej) => { +export default define(meta, async (ps) => { const user = await User.findOne({ _id: ps.userId }); if (user == null) { - return rej('user not found'); + throw new Error('user not found'); } await User.findOneAndUpdate({ _id: user._id }, { - $set: { - isVerified: false - } - }); + $set: { + isVerified: false + } + }); - res(); -})); + return; +}); diff --git a/src/server/api/endpoints/admin/update-meta.ts b/src/server/api/endpoints/admin/update-meta.ts index da2ee3692..1fb87d0a9 100644 --- a/src/server/api/endpoints/admin/update-meta.ts +++ b/src/server/api/endpoints/admin/update-meta.ts @@ -323,7 +323,7 @@ export const meta = { } }; -export default define(meta, (ps) => new Promise(async (res, rej) => { +export default define(meta, async (ps) => { const set = {} as any; if (ps.broadcasts) { @@ -506,5 +506,5 @@ export default define(meta, (ps) => new Promise(async (res, rej) => { $set: set }, { upsert: true }); - res(); -})); + return; +}); diff --git a/src/server/api/endpoints/admin/update-remote-user.ts b/src/server/api/endpoints/admin/update-remote-user.ts index 9288ce1fb..8d1db33ba 100644 --- a/src/server/api/endpoints/admin/update-remote-user.ts +++ b/src/server/api/endpoints/admin/update-remote-user.ts @@ -26,9 +26,10 @@ export const meta = { } }; -export default define(meta, (ps) => new Promise((res, rej) => { - updatePersonById(ps.userId).then(() => res(), e => rej(e)); -})); +export default define(meta, async (ps) => { + await updatePersonById(ps.userId); + return; +}); async function updatePersonById(userId: mongo.ObjectID) { const user = await getRemoteUser(userId); diff --git a/src/server/api/endpoints/admin/verify-user.ts b/src/server/api/endpoints/admin/verify-user.ts index 996e044d1..77db8e6e6 100644 --- a/src/server/api/endpoints/admin/verify-user.ts +++ b/src/server/api/endpoints/admin/verify-user.ts @@ -24,22 +24,22 @@ export const meta = { } }; -export default define(meta, (ps) => new Promise(async (res, rej) => { +export default define(meta, async (ps) => { const user = await User.findOne({ _id: ps.userId }); if (user == null) { - return rej('user not found'); + throw new Error('user not found'); } await User.findOneAndUpdate({ _id: user._id }, { - $set: { - isVerified: true - } - }); + $set: { + isVerified: true + } + }); - res(); -})); + return; +}); diff --git a/src/server/api/endpoints/aggregation/hashtags.ts b/src/server/api/endpoints/aggregation/hashtags.ts index 99ef8901d..6de36a1d1 100644 --- a/src/server/api/endpoints/aggregation/hashtags.ts +++ b/src/server/api/endpoints/aggregation/hashtags.ts @@ -6,7 +6,7 @@ export const meta = { requireCredential: false, }; -export default define(meta, (ps) => new Promise(async (res, rej) => { +export default define(meta, async (ps) => { const instance = await fetchMeta(); const hidedTags = instance.hidedTags.map(t => t.toLowerCase()); @@ -40,7 +40,7 @@ export default define(meta, (ps) => new Promise(async (res, rej) => { //#endregion if (data.length == 0) { - return res([]); + return []; } let tags: { @@ -66,5 +66,5 @@ export default define(meta, (ps) => new Promise(async (res, rej) => { tags = tags.slice(0, 30); - res(tags); -})); + return tags; +}); diff --git a/src/server/api/endpoints/ap/show.ts b/src/server/api/endpoints/ap/show.ts index 3b4021e0a..8dd9f96f9 100644 --- a/src/server/api/endpoints/ap/show.ts +++ b/src/server/api/endpoints/ap/show.ts @@ -7,6 +7,7 @@ import { createPerson } from '../../../../remote/activitypub/models/person'; import Note, { pack as packNote, INote } from '../../../../models/note'; import { createNote } from '../../../../remote/activitypub/models/note'; import Resolver from '../../../../remote/activitypub/resolver'; +import { ApiError } from '../../error'; export const meta = { desc: { @@ -23,13 +24,24 @@ export const meta = { } }, }, + + errors: { + noSuchObject: { + message: 'No such object.', + code: 'NO_SUCH_OBJECT', + id: 'dc94d745-1262-4e63-a17d-fecaa57efc82' + } + } }; -export default define(meta, (ps) => new Promise((res, rej) => { - fetchAny(ps.uri) - .then(object => object != null ? res(object) : rej('object not found')) - .catch(e => rej(e)); -})); +export default define(meta, async (ps) => { + const object = await fetchAny(ps.uri); + if (object) { + return object; + } else { + throw new ApiError(meta.errors.noSuchObject); + } +}); /*** * URIからUserかNoteを解決する diff --git a/src/server/api/endpoints/app/create.ts b/src/server/api/endpoints/app/create.ts index 849530c20..ef8bd6607 100644 --- a/src/server/api/endpoints/app/create.ts +++ b/src/server/api/endpoints/app/create.ts @@ -27,7 +27,7 @@ export const meta = { } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { // Generate secret const secret = rndstr('a-zA-Z0-9', 32); @@ -42,9 +42,8 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { secret: secret }); - // Response - res(await pack(app, null, { + return await pack(app, null, { detail: true, includeSecret: true - })); -})); + }); +}); diff --git a/src/server/api/endpoints/app/show.ts b/src/server/api/endpoints/app/show.ts index cb47b40ab..72755ce01 100644 --- a/src/server/api/endpoints/app/show.ts +++ b/src/server/api/endpoints/app/show.ts @@ -2,6 +2,7 @@ import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id'; import App, { pack } from '../../../../models/app'; import define from '../../define'; +import { ApiError } from '../../error'; export const meta = { params: { @@ -9,22 +10,29 @@ export const meta = { validator: $.type(ID), transform: transform }, + }, + + errors: { + noSuchApp: { + message: 'No such app.', + code: 'NO_SUCH_APP', + id: 'dce83913-2dc6-4093-8a7b-71dbb11718a3' + } } }; -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; // Lookup app const ap = await App.findOne({ _id: ps.appId }); if (ap === null) { - return rej('app not found'); + throw new ApiError(meta.errors.noSuchApp); } - // Send response - res(await pack(ap, user, { + return await pack(ap, user, { detail: true, includeSecret: isSecure && ap.userId.equals(user._id) - })); -})); + }); +}); diff --git a/src/server/api/endpoints/auth/accept.ts b/src/server/api/endpoints/auth/accept.ts index 3e45a2801..964ee6559 100644 --- a/src/server/api/endpoints/auth/accept.ts +++ b/src/server/api/endpoints/auth/accept.ts @@ -5,6 +5,7 @@ import App from '../../../../models/app'; import AuthSess from '../../../../models/auth-session'; import AccessToken from '../../../../models/access-token'; import define from '../../define'; +import { ApiError } from '../../error'; export const meta = { requireCredential: true, @@ -15,16 +16,24 @@ export const meta = { token: { validator: $.str } + }, + + errors: { + noSuchSession: { + message: 'No such session.', + code: 'NO_SUCH_SESSION', + id: '9c72d8de-391a-43c1-9d06-08d29efde8df' + }, } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { // Fetch token const session = await AuthSess .findOne({ token: ps.token }); if (session === null) { - return rej('session not found'); + throw new ApiError(meta.errors.noSuchSession); } // Generate access token @@ -64,6 +73,5 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { } }); - // Response - res(); -})); + return; +}); diff --git a/src/server/api/endpoints/auth/session/generate.ts b/src/server/api/endpoints/auth/session/generate.ts index 5ef3404b7..9f0f18471 100644 --- a/src/server/api/endpoints/auth/session/generate.ts +++ b/src/server/api/endpoints/auth/session/generate.ts @@ -4,6 +4,7 @@ import App from '../../../../../models/app'; import AuthSess from '../../../../../models/auth-session'; import config from '../../../../../config'; import define from '../../../define'; +import { ApiError } from '../../../error'; export const meta = { requireCredential: false, @@ -12,17 +13,25 @@ export const meta = { appSecret: { validator: $.str } + }, + + errors: { + noSuchApp: { + message: 'No such app.', + code: 'NO_SUCH_APP', + id: '92f93e63-428e-4f2f-a5a4-39e1407fe998' + } } }; -export default define(meta, (ps) => new Promise(async (res, rej) => { +export default define(meta, async (ps) => { // Lookup app const app = await App.findOne({ secret: ps.appSecret }); if (app == null) { - return rej('app not found'); + throw new ApiError(meta.errors.noSuchApp); } // Generate token @@ -35,9 +44,8 @@ export default define(meta, (ps) => new Promise(async (res, rej) => { token: token }); - // Response - res({ + return { token: doc.token, url: `${config.auth_url}/${doc.token}` - }); -})); + }; +}); diff --git a/src/server/api/endpoints/auth/session/show.ts b/src/server/api/endpoints/auth/session/show.ts index cc63c43ad..5e9e68c2c 100644 --- a/src/server/api/endpoints/auth/session/show.ts +++ b/src/server/api/endpoints/auth/session/show.ts @@ -1,6 +1,7 @@ import $ from 'cafy'; import AuthSess, { pack } from '../../../../../models/auth-session'; import define from '../../../define'; +import { ApiError } from '../../../error'; export const meta = { requireCredential: false, @@ -9,19 +10,26 @@ export const meta = { token: { validator: $.str } + }, + + errors: { + noSuchSession: { + message: 'No such session.', + code: 'NO_SUCH_SESSION', + id: 'bd72c97d-eba7-4adb-a467-f171b8847250' + } } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { // Lookup session const session = await AuthSess.findOne({ token: ps.token }); if (session == null) { - return rej('session not found'); + throw new ApiError(meta.errors.noSuchSession); } - // Response - res(await pack(session, user)); -})); + return await pack(session, user); +}); diff --git a/src/server/api/endpoints/auth/session/userkey.ts b/src/server/api/endpoints/auth/session/userkey.ts index 9f924d27a..17d62886a 100644 --- a/src/server/api/endpoints/auth/session/userkey.ts +++ b/src/server/api/endpoints/auth/session/userkey.ts @@ -4,6 +4,7 @@ import AuthSess from '../../../../../models/auth-session'; import AccessToken from '../../../../../models/access-token'; import { pack } from '../../../../../models/user'; import define from '../../../define'; +import { ApiError } from '../../../error'; export const meta = { requireCredential: false, @@ -16,17 +17,37 @@ export const meta = { token: { validator: $.str } + }, + + errors: { + noSuchApp: { + message: 'No such app.', + code: 'NO_SUCH_APP', + id: 'fcab192a-2c5a-43b7-8ad8-9b7054d8d40d' + }, + + noSuchSession: { + message: 'No such session.', + code: 'NO_SUCH_SESSION', + id: '5b5a1503-8bc8-4bd0-8054-dc189e8cdcb3' + }, + + pendingSession: { + message: 'This session is not completed yet.', + code: 'PENDING_SESSION', + id: '8c8a4145-02cc-4cca-8e66-29ba60445a8e' + } } }; -export default define(meta, (ps) => new Promise(async (res, rej) => { +export default define(meta, async (ps) => { // Lookup app const app = await App.findOne({ secret: ps.appSecret }); if (app == null) { - return rej('app not found'); + throw new ApiError(meta.errors.noSuchApp); } // Fetch token @@ -37,11 +58,11 @@ export default define(meta, (ps) => new Promise(async (res, rej) => { }); if (session === null) { - return rej('session not found'); + throw new ApiError(meta.errors.noSuchSession); } if (session.userId == null) { - return rej('this session is not allowed yet'); + throw new ApiError(meta.errors.pendingSession); } // Lookup access token @@ -61,11 +82,10 @@ export default define(meta, (ps) => new Promise(async (res, rej) => { _id: session._id }); - // Response - res({ + return { accessToken: accessToken.token, user: await pack(session.userId, null, { detail: true }) - }); -})); + }; +}); diff --git a/src/server/api/endpoints/blocking/create.ts b/src/server/api/endpoints/blocking/create.ts index 6fa7efb55..7e15b2d7f 100644 --- a/src/server/api/endpoints/blocking/create.ts +++ b/src/server/api/endpoints/blocking/create.ts @@ -5,6 +5,7 @@ import User, { pack } from '../../../../models/user'; import Blocking from '../../../../models/blocking'; import create from '../../../../services/blocking/create'; import define from '../../define'; +import { ApiError } from '../../error'; export const meta = { stability: 'stable', @@ -32,15 +33,35 @@ export const meta = { 'en-US': 'Target user ID' } } + }, + + errors: { + noSuchUser: { + message: 'No such user.', + code: 'NO_SUCH_USER', + id: '7cc4f851-e2f1-4621-9633-ec9e1d00c01e' + }, + + blockeeIsYourself: { + message: 'Blockee is yourself.', + code: 'BLOCKEE_IS_YOURSELF', + id: '88b19138-f28d-42c0-8499-6a31bbd0fdc6' + }, + + alreadyBlocking: { + message: 'You are already blocking that user.', + code: 'ALREADY_BLOCKING', + id: '787fed64-acb9-464a-82eb-afbd745b9614' + }, } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { const blocker = user; // 自分自身 if (user._id.equals(ps.userId)) { - return rej('blockee is yourself'); + throw new ApiError(meta.errors.blockeeIsYourself); } // Get blockee @@ -54,7 +75,7 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }); if (blockee === null) { - return rej('user not found'); + throw new ApiError(meta.errors.noSuchUser); } // Check if already blocking @@ -64,14 +85,13 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }); if (exist !== null) { - return rej('already blocking'); + throw new ApiError(meta.errors.alreadyBlocking); } // Create blocking await create(blocker, blockee); - // Send response - res(await pack(blockee._id, user, { + return await pack(blockee._id, user, { detail: true - })); -})); + }); +}); diff --git a/src/server/api/endpoints/blocking/delete.ts b/src/server/api/endpoints/blocking/delete.ts index c176da1ea..475f2c30a 100644 --- a/src/server/api/endpoints/blocking/delete.ts +++ b/src/server/api/endpoints/blocking/delete.ts @@ -5,6 +5,7 @@ import User, { pack } from '../../../../models/user'; import Blocking from '../../../../models/blocking'; import deleteBlocking from '../../../../services/blocking/delete'; import define from '../../define'; +import { ApiError } from '../../error'; export const meta = { stability: 'stable', @@ -32,15 +33,35 @@ export const meta = { 'en-US': 'Target user ID' } } + }, + + errors: { + noSuchUser: { + message: 'No such user.', + code: 'NO_SUCH_USER', + id: '8621d8bf-c358-4303-a066-5ea78610eb3f' + }, + + blockeeIsYourself: { + message: 'Blockee is yourself.', + code: 'BLOCKEE_IS_YOURSELF', + id: '06f6fac6-524b-473c-a354-e97a40ae6eac' + }, + + notBlocking: { + message: 'You are not blocking that user.', + code: 'NOT_BLOCKING', + id: '291b2efa-60c6-45c0-9f6a-045c8f9b02cd' + }, } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { const blocker = user; // Check if the blockee is yourself if (user._id.equals(ps.userId)) { - return rej('blockee is yourself'); + throw new ApiError(meta.errors.blockeeIsYourself); } // Get blockee @@ -54,7 +75,7 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }); if (blockee === null) { - return rej('user not found'); + throw new ApiError(meta.errors.noSuchUser); } // Check not blocking @@ -64,14 +85,13 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }); if (exist === null) { - return rej('already not blocking'); + throw new ApiError(meta.errors.notBlocking); } // Delete blocking await deleteBlocking(blocker, blockee); - // Send response - res(await pack(blockee._id, user, { + return await pack(blockee._id, user, { detail: true - })); -})); + }); +}); diff --git a/src/server/api/endpoints/blocking/list.ts b/src/server/api/endpoints/blocking/list.ts index 79eaaece8..088b2c488 100644 --- a/src/server/api/endpoints/blocking/list.ts +++ b/src/server/api/endpoints/blocking/list.ts @@ -31,12 +31,7 @@ export const meta = { } }; -export default define(meta, (ps, me) => 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, me) => { const query = { blockerId: me._id } as any; @@ -62,5 +57,5 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { sort: sort }); - res(await packMany(blockings, me)); -})); + return await packMany(blockings, me); +}); diff --git a/src/server/api/endpoints/charts/active-users.ts b/src/server/api/endpoints/charts/active-users.ts index 9adf7bd38..11f86d0ba 100644 --- a/src/server/api/endpoints/charts/active-users.ts +++ b/src/server/api/endpoints/charts/active-users.ts @@ -27,8 +27,6 @@ export const meta = { } }; -export default define(meta, (ps) => new Promise(async (res, rej) => { - const stats = await activeUsersChart.getChart(ps.span as any, ps.limit); - - res(stats); -})); +export default define(meta, async (ps) => { + return await activeUsersChart.getChart(ps.span as any, ps.limit); +}); diff --git a/src/server/api/endpoints/charts/drive.ts b/src/server/api/endpoints/charts/drive.ts index 0f1106e12..9244ef748 100644 --- a/src/server/api/endpoints/charts/drive.ts +++ b/src/server/api/endpoints/charts/drive.ts @@ -27,8 +27,6 @@ export const meta = { } }; -export default define(meta, (ps) => new Promise(async (res, rej) => { - const stats = await driveChart.getChart(ps.span as any, ps.limit); - - res(stats); -})); +export default define(meta, async (ps) => { + return await driveChart.getChart(ps.span as any, ps.limit); +}); diff --git a/src/server/api/endpoints/charts/federation.ts b/src/server/api/endpoints/charts/federation.ts index 6b9cfc451..c98c9f6cb 100644 --- a/src/server/api/endpoints/charts/federation.ts +++ b/src/server/api/endpoints/charts/federation.ts @@ -27,8 +27,6 @@ export const meta = { } }; -export default define(meta, (ps) => new Promise(async (res, rej) => { - const stats = await federationChart.getChart(ps.span as any, ps.limit); - - res(stats); -})); +export default define(meta, async (ps) => { + return await federationChart.getChart(ps.span as any, ps.limit); +}); diff --git a/src/server/api/endpoints/charts/hashtag.ts b/src/server/api/endpoints/charts/hashtag.ts index e1127fe38..5905fdc1a 100644 --- a/src/server/api/endpoints/charts/hashtag.ts +++ b/src/server/api/endpoints/charts/hashtag.ts @@ -34,8 +34,6 @@ export const meta = { } }; -export default define(meta, (ps) => new Promise(async (res, rej) => { - const stats = await hashtagChart.getChart(ps.span as any, ps.limit, ps.tag); - - res(stats); -})); +export default define(meta, async (ps) => { + return await hashtagChart.getChart(ps.span as any, ps.limit, ps.tag); +}); diff --git a/src/server/api/endpoints/charts/instance.ts b/src/server/api/endpoints/charts/instance.ts index 9b5f33024..e47334c7c 100644 --- a/src/server/api/endpoints/charts/instance.ts +++ b/src/server/api/endpoints/charts/instance.ts @@ -35,8 +35,6 @@ export const meta = { } }; -export default define(meta, (ps) => new Promise(async (res, rej) => { - const stats = await instanceChart.getChart(ps.span as any, ps.limit, ps.host); - - res(stats); -})); +export default define(meta, async (ps) => { + return await instanceChart.getChart(ps.span as any, ps.limit, ps.host); +}); diff --git a/src/server/api/endpoints/charts/network.ts b/src/server/api/endpoints/charts/network.ts index db13f1303..fa1917f75 100644 --- a/src/server/api/endpoints/charts/network.ts +++ b/src/server/api/endpoints/charts/network.ts @@ -27,8 +27,6 @@ export const meta = { } }; -export default define(meta, (ps) => new Promise(async (res, rej) => { - const stats = await networkChart.getChart(ps.span as any, ps.limit); - - res(stats); -})); +export default define(meta, async (ps) => { + return await networkChart.getChart(ps.span as any, ps.limit); +}); diff --git a/src/server/api/endpoints/charts/notes.ts b/src/server/api/endpoints/charts/notes.ts index 4877df608..006a8d6e2 100644 --- a/src/server/api/endpoints/charts/notes.ts +++ b/src/server/api/endpoints/charts/notes.ts @@ -27,8 +27,6 @@ export const meta = { } }; -export default define(meta, (ps) => new Promise(async (res, rej) => { - const stats = await notesChart.getChart(ps.span as any, ps.limit); - - res(stats); -})); +export default define(meta, async (ps) => { + return await notesChart.getChart(ps.span as any, ps.limit); +}); diff --git a/src/server/api/endpoints/charts/user/drive.ts b/src/server/api/endpoints/charts/user/drive.ts index 556651d33..6383ddf2f 100644 --- a/src/server/api/endpoints/charts/user/drive.ts +++ b/src/server/api/endpoints/charts/user/drive.ts @@ -37,8 +37,6 @@ export const meta = { } }; -export default define(meta, (ps) => new Promise(async (res, rej) => { - const stats = await perUserDriveChart.getChart(ps.span as any, ps.limit, ps.userId); - - res(stats); -})); +export default define(meta, async (ps) => { + return await perUserDriveChart.getChart(ps.span as any, ps.limit, ps.userId); +}); diff --git a/src/server/api/endpoints/charts/user/following.ts b/src/server/api/endpoints/charts/user/following.ts index ccc83952f..9443b56a4 100644 --- a/src/server/api/endpoints/charts/user/following.ts +++ b/src/server/api/endpoints/charts/user/following.ts @@ -37,8 +37,6 @@ export const meta = { } }; -export default define(meta, (ps) => new Promise(async (res, rej) => { - const stats = await perUserFollowingChart.getChart(ps.span as any, ps.limit, ps.userId); - - res(stats); -})); +export default define(meta, async (ps) => { + return await perUserFollowingChart.getChart(ps.span as any, ps.limit, ps.userId); +}); diff --git a/src/server/api/endpoints/charts/user/notes.ts b/src/server/api/endpoints/charts/user/notes.ts index 3296771c6..da642fc76 100644 --- a/src/server/api/endpoints/charts/user/notes.ts +++ b/src/server/api/endpoints/charts/user/notes.ts @@ -37,8 +37,6 @@ export const meta = { } }; -export default define(meta, (ps) => new Promise(async (res, rej) => { - const stats = await perUserNotesChart.getChart(ps.span as any, ps.limit, ps.userId); - - res(stats); -})); +export default define(meta, async (ps) => { + return await perUserNotesChart.getChart(ps.span as any, ps.limit, ps.userId); +}); diff --git a/src/server/api/endpoints/charts/user/reactions.ts b/src/server/api/endpoints/charts/user/reactions.ts index be228b5f7..e440b3ae3 100644 --- a/src/server/api/endpoints/charts/user/reactions.ts +++ b/src/server/api/endpoints/charts/user/reactions.ts @@ -37,8 +37,6 @@ export const meta = { } }; -export default define(meta, (ps) => new Promise(async (res, rej) => { - const stats = await perUserReactionsChart.getChart(ps.span as any, ps.limit, ps.userId); - - res(stats); -})); +export default define(meta, async (ps) => { + return await perUserReactionsChart.getChart(ps.span as any, ps.limit, ps.userId); +}); diff --git a/src/server/api/endpoints/charts/users.ts b/src/server/api/endpoints/charts/users.ts index e474e2997..ea32b7afd 100644 --- a/src/server/api/endpoints/charts/users.ts +++ b/src/server/api/endpoints/charts/users.ts @@ -27,8 +27,6 @@ export const meta = { } }; -export default define(meta, (ps) => new Promise(async (res, rej) => { - const stats = await usersChart.getChart(ps.span as any, ps.limit); - - res(stats); -})); +export default define(meta, async (ps) => { + return await usersChart.getChart(ps.span as any, ps.limit); +}); diff --git a/src/server/api/endpoints/drive.ts b/src/server/api/endpoints/drive.ts index 3480fe600..54afe55cb 100644 --- a/src/server/api/endpoints/drive.ts +++ b/src/server/api/endpoints/drive.ts @@ -13,35 +13,34 @@ export const meta = { kind: 'drive-read' }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { const instance = await fetchMeta(); // Calculate drive usage - const usage = await DriveFile - .aggregate([{ - $match: { - 'metadata.userId': user._id, - 'metadata.deletedAt': { $exists: false } - } - }, { - $project: { - length: true - } - }, { - $group: { - _id: null, - usage: { $sum: '$length' } - } - }]) - .then((aggregates: any[]) => { - if (aggregates.length > 0) { - return aggregates[0].usage; - } - return 0; - }); + const usage = await DriveFile.aggregate([{ + $match: { + 'metadata.userId': user._id, + 'metadata.deletedAt': { $exists: false } + } + }, { + $project: { + length: true + } + }, { + $group: { + _id: null, + usage: { $sum: '$length' } + } + }]) + .then((aggregates: any[]) => { + if (aggregates.length > 0) { + return aggregates[0].usage; + } + return 0; + }); - res({ + return { capacity: 1024 * 1024 * instance.localDriveCapacityMb, usage: usage - }); -})); + }; +}); diff --git a/src/server/api/endpoints/drive/files.ts b/src/server/api/endpoints/drive/files.ts index 6b9631391..1c12f0dc0 100644 --- a/src/server/api/endpoints/drive/files.ts +++ b/src/server/api/endpoints/drive/files.ts @@ -41,12 +41,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 sort = { _id: -1 }; @@ -78,5 +73,5 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { sort: sort }); - res(await packMany(files, { detail: false, self: true })); -})); + return await packMany(files, { detail: false, self: true }); +}); diff --git a/src/server/api/endpoints/drive/files/attached_notes.ts b/src/server/api/endpoints/drive/files/attached_notes.ts index 6442b1519..506444b18 100644 --- a/src/server/api/endpoints/drive/files/attached_notes.ts +++ b/src/server/api/endpoints/drive/files/attached_notes.ts @@ -3,6 +3,7 @@ import ID, { transform } from '../../../../../misc/cafy-id'; import DriveFile from '../../../../../models/drive-file'; import define from '../../../define'; import { packMany } from '../../../../../models/note'; +import { ApiError } from '../../../error'; export const meta = { stability: 'stable', @@ -25,10 +26,18 @@ export const meta = { 'en-US': 'Target file ID' } } + }, + + errors: { + noSuchFile: { + message: 'No such file.', + code: 'NO_SUCH_FILE', + id: 'c118ece3-2e4b-4296-99d1-51756e32d232', + } } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { // Fetch file const file = await DriveFile .findOne({ @@ -38,10 +47,10 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }); if (file === null) { - return rej('file-not-found'); + throw new ApiError(meta.errors.noSuchFile); } - res(await packMany(file.metadata.attachedNoteIds || [], user, { + return await packMany(file.metadata.attachedNoteIds || [], user, { detail: true - })); -})); + }); +}); diff --git a/src/server/api/endpoints/drive/files/check_existence.ts b/src/server/api/endpoints/drive/files/check_existence.ts index 6e986d417..aafd46fb2 100644 --- a/src/server/api/endpoints/drive/files/check_existence.ts +++ b/src/server/api/endpoints/drive/files/check_existence.ts @@ -22,16 +22,12 @@ export const meta = { } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { const file = await DriveFile.findOne({ md5: ps.md5, 'metadata.userId': user._id, 'metadata.deletedAt': { $exists: false } }); - if (file === null) { - res({ file: null }); - } else { - res({ file: await pack(file, { self: true }) }); - } -})); + return { file: file ? await pack(file, { self: true }) : null }; +}); diff --git a/src/server/api/endpoints/drive/files/create.ts b/src/server/api/endpoints/drive/files/create.ts index 3611652b6..c96e4bbf1 100644 --- a/src/server/api/endpoints/drive/files/create.ts +++ b/src/server/api/endpoints/drive/files/create.ts @@ -5,6 +5,7 @@ import { validateFileName, pack } from '../../../../../models/drive-file'; import create from '../../../../../services/drive/add-file'; import define from '../../../define'; import { apiLogger } from '../../../logger'; +import { ApiError } from '../../../error'; export const meta = { desc: { @@ -51,10 +52,18 @@ export const meta = { 'ja-JP': 'true にすると、同じハッシュを持つファイルが既にアップロードされていても強制的にファイルを作成します。', } } + }, + + errors: { + invalidFileName: { + message: 'Invalid file name.', + code: 'INVALID_FILE_NAME', + id: 'f449b209-0c60-4e51-84d5-29486263bfd4' + } } }; -export default define(meta, (ps, user, app, file, cleanup) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user, app, file, cleanup) => { // Get 'name' parameter let name = file.originalname; if (name !== undefined && name !== null) { @@ -64,7 +73,7 @@ export default define(meta, (ps, user, app, file, cleanup) => new Promise(async } else if (name === 'blob') { name = null; } else if (!validateFileName(name)) { - return rej('invalid name'); + throw new ApiError(meta.errors.invalidFileName); } } else { name = null; @@ -73,15 +82,11 @@ export default define(meta, (ps, user, app, file, cleanup) => new Promise(async try { // Create file const driveFile = await create(user, file.path, name, null, ps.folderId, ps.force, false, null, null, ps.isSensitive); - - cleanup(); - - res(pack(driveFile, { self: true })); + return pack(driveFile, { self: true }); } catch (e) { apiLogger.error(e); - + throw new ApiError(); + } finally { cleanup(); - - rej(e); } -})); +}); diff --git a/src/server/api/endpoints/drive/files/delete.ts b/src/server/api/endpoints/drive/files/delete.ts index c322587d8..2dc493efe 100644 --- a/src/server/api/endpoints/drive/files/delete.ts +++ b/src/server/api/endpoints/drive/files/delete.ts @@ -4,6 +4,7 @@ import DriveFile from '../../../../../models/drive-file'; import del from '../../../../../services/drive/delete-file'; import { publishDriveStream } from '../../../../../services/stream'; import define from '../../../define'; +import { ApiError } from '../../../error'; export const meta = { stability: 'stable', @@ -26,10 +27,24 @@ export const meta = { 'en-US': 'Target file ID' } } + }, + + errors: { + noSuchFile: { + message: 'No such file.', + code: 'NO_SUCH_FILE', + id: '908939ec-e52b-4458-b395-1025195cea58' + }, + + accessDenied: { + message: 'Access denied.', + code: 'ACCESS_DENIED', + id: '5eb8d909-2540-4970-90b8-dd6f86088121' + }, } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { // Fetch file const file = await DriveFile .findOne({ @@ -37,11 +52,11 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }); if (file === null) { - return rej('file-not-found'); + throw new ApiError(meta.errors.noSuchFile); } if (!user.isAdmin && !user.isModerator && !file.metadata.userId.equals(user._id)) { - return rej('access denied'); + throw new ApiError(meta.errors.accessDenied); } // Delete @@ -50,5 +65,5 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { // Publish fileDeleted event publishDriveStream(user._id, 'fileDeleted', file._id); - res(); -})); + return; +}); diff --git a/src/server/api/endpoints/drive/files/find.ts b/src/server/api/endpoints/drive/files/find.ts index 0ebd5b789..6eb909b9d 100644 --- a/src/server/api/endpoints/drive/files/find.ts +++ b/src/server/api/endpoints/drive/files/find.ts @@ -24,7 +24,7 @@ export const meta = { } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { const files = await DriveFile .find({ filename: ps.name, @@ -32,5 +32,5 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { 'metadata.folderId': ps.folderId }); - res(await Promise.all(files.map(file => pack(file, { self: true })))); -})); + return await Promise.all(files.map(file => pack(file, { self: true }))); +}); diff --git a/src/server/api/endpoints/drive/files/show.ts b/src/server/api/endpoints/drive/files/show.ts index 994583bd0..f844d434f 100644 --- a/src/server/api/endpoints/drive/files/show.ts +++ b/src/server/api/endpoints/drive/files/show.ts @@ -4,6 +4,7 @@ import ID, { transform } from '../../../../../misc/cafy-id'; import DriveFile, { pack, IDriveFile } from '../../../../../models/drive-file'; import define from '../../../define'; import config from '../../../../../config'; +import { ApiError } from '../../../error'; export const meta = { stability: 'stable', @@ -34,10 +35,30 @@ export const meta = { 'en-US': 'Target file URL' } } + }, + + errors: { + noSuchFile: { + message: 'No such file.', + code: 'NO_SUCH_FILE', + id: '067bc436-2718-4795-b0fb-ecbe43949e31' + }, + + accessDenied: { + message: 'Access denied.', + code: 'ACCESS_DENIED', + id: '25b73c73-68b1-41d0-bad1-381cfdf6579f' + }, + + fileIdOrUrlRequired: { + message: 'fileId or url required.', + code: 'INVALID_PARAM', + id: '89674805-722c-440c-8d88-5641830dc3e4' + } } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { let file: IDriveFile; if (ps.fileId) { @@ -69,22 +90,19 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }); } } else { - return rej('fileId or url required'); + throw new ApiError(meta.errors.fileIdOrUrlRequired); } if (!user.isAdmin && !user.isModerator && !file.metadata.userId.equals(user._id)) { - return rej('access denied'); + throw new ApiError(meta.errors.accessDenied); } if (file === null) { - return rej('file-not-found'); + throw new ApiError(meta.errors.noSuchFile); } - // Serialize - const _file = await pack(file, { + return await pack(file, { detail: true, self: true }); - - res(_file); -})); +}); diff --git a/src/server/api/endpoints/drive/files/update.ts b/src/server/api/endpoints/drive/files/update.ts index 79acbec3c..f24cc6bcd 100644 --- a/src/server/api/endpoints/drive/files/update.ts +++ b/src/server/api/endpoints/drive/files/update.ts @@ -5,6 +5,7 @@ import DriveFile, { validateFileName, pack } from '../../../../../models/drive-f import { publishDriveStream } from '../../../../../services/stream'; import define from '../../../define'; import Note from '../../../../../models/note'; +import { ApiError } from '../../../error'; export const meta = { desc: { @@ -51,10 +52,30 @@ export const meta = { 'en-US': 'Whether this media is NSFW' } } + }, + + errors: { + noSuchFile: { + message: 'No such file.', + code: 'NO_SUCH_FILE', + id: 'e7778c7e-3af9-49cd-9690-6dbc3e6c972d' + }, + + accessDenied: { + message: 'Access denied.', + code: 'ACCESS_DENIED', + id: '01a53b27-82fc-445b-a0c1-b558465a8ed2' + }, + + noSuchFolder: { + message: 'No such folder.', + code: 'NO_SUCH_FOLDER', + id: 'ea8fb7a5-af77-4a08-b608-c0218176cd73' + }, } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { // Fetch file const file = await DriveFile .findOne({ @@ -62,11 +83,11 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }); if (file === null) { - return rej('file-not-found'); + throw new ApiError(meta.errors.noSuchFile); } if (!user.isAdmin && !user.isModerator && !file.metadata.userId.equals(user._id)) { - return rej('access denied'); + throw new ApiError(meta.errors.accessDenied); } if (ps.name) file.filename = ps.name; @@ -85,7 +106,7 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }); if (folder === null) { - return rej('folder-not-found'); + throw new ApiError(meta.errors.noSuchFolder); } file.metadata.folderId = folder._id; @@ -114,12 +135,10 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { } }); - // Serialize const fileObj = await pack(file, { self: true }); - // Response - res(fileObj); - // Publish fileUpdated event publishDriveStream(user._id, 'fileUpdated', fileObj); -})); + + return fileObj; +}); diff --git a/src/server/api/endpoints/drive/files/upload_from_url.ts b/src/server/api/endpoints/drive/files/upload_from_url.ts index d79b32173..8d25530dd 100644 --- a/src/server/api/endpoints/drive/files/upload_from_url.ts +++ b/src/server/api/endpoints/drive/files/upload_from_url.ts @@ -50,6 +50,6 @@ export const meta = { } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { - res(pack(await uploadFromUrl(ps.url, user, ps.folderId, null, ps.isSensitive, ps.force), { self: true })); -})); +export default define(meta, async (ps, user) => { + return await pack(await uploadFromUrl(ps.url, user, ps.folderId, null, ps.isSensitive, ps.force), { self: true }); +}); diff --git a/src/server/api/endpoints/drive/folders.ts b/src/server/api/endpoints/drive/folders.ts index bdbbf35ff..698206085 100644 --- a/src/server/api/endpoints/drive/folders.ts +++ b/src/server/api/endpoints/drive/folders.ts @@ -37,12 +37,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 sort = { _id: -1 }; @@ -67,5 +62,5 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { sort: sort }); - res(await Promise.all(folders.map(folder => pack(folder)))); -})); + return await Promise.all(folders.map(folder => pack(folder))); +}); diff --git a/src/server/api/endpoints/drive/folders/create.ts b/src/server/api/endpoints/drive/folders/create.ts index f4b520be2..a54c780b8 100644 --- a/src/server/api/endpoints/drive/folders/create.ts +++ b/src/server/api/endpoints/drive/folders/create.ts @@ -3,6 +3,7 @@ import ID, { transform } from '../../../../../misc/cafy-id'; import DriveFolder, { isValidFolderName, pack } from '../../../../../models/drive-folder'; import { publishDriveStream } from '../../../../../services/stream'; import define from '../../../define'; +import { ApiError } from '../../../error'; export const meta = { stability: 'stable', @@ -34,10 +35,18 @@ export const meta = { 'en-US': 'Parent folder ID' } } + }, + + errors: { + noSuchFolder: { + message: 'No such folder.', + code: 'NO_SUCH_FOLDER', + id: '53326628-a00d-40a6-a3cd-8975105c0f95' + }, } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { // If the parent folder is specified let parent = null; if (ps.parentId) { @@ -49,7 +58,7 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }); if (parent === null) { - return rej('parent-not-found'); + throw new ApiError(meta.errors.noSuchFolder); } } @@ -61,12 +70,10 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { userId: user._id }); - // Serialize const folderObj = await pack(folder); - // Response - res(folderObj); - // Publish folderCreated event publishDriveStream(user._id, 'folderCreated', folderObj); -})); + + return folderObj; +}); diff --git a/src/server/api/endpoints/drive/folders/delete.ts b/src/server/api/endpoints/drive/folders/delete.ts index 228e262cc..26a71d57d 100644 --- a/src/server/api/endpoints/drive/folders/delete.ts +++ b/src/server/api/endpoints/drive/folders/delete.ts @@ -4,6 +4,7 @@ import DriveFolder from '../../../../../models/drive-folder'; import define from '../../../define'; import { publishDriveStream } from '../../../../../services/stream'; import DriveFile from '../../../../../models/drive-file'; +import { ApiError } from '../../../error'; export const meta = { stability: 'stable', @@ -26,10 +27,24 @@ export const meta = { 'en-US': 'Target folder ID' } } + }, + + errors: { + noSuchFolder: { + message: 'No such folder.', + code: 'NO_SUCH_FOLDER', + id: '1069098f-c281-440f-b085-f9932edbe091' + }, + + hasChildFilesOrFolders: { + message: 'This folder has child files or folders.', + code: 'HAS_CHILD_FILES_OR_FOLDERS', + id: 'b0fc8a17-963c-405d-bfbc-859a487295e1' + }, } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { // Get folder const folder = await DriveFolder .findOne({ @@ -38,7 +53,7 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }); if (folder === null) { - return rej('folder-not-found'); + throw new ApiError(meta.errors.noSuchFolder); } const [childFoldersCount, childFilesCount] = await Promise.all([ @@ -47,7 +62,7 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { ]); if (childFoldersCount !== 0 || childFilesCount !== 0) { - return rej('has-child-contents'); + throw new ApiError(meta.errors.hasChildFilesOrFolders); } await DriveFolder.remove({ _id: folder._id }); @@ -55,5 +70,5 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { // Publish folderCreated event publishDriveStream(user._id, 'folderDeleted', folder._id); - res(); -})); + return; +}); diff --git a/src/server/api/endpoints/drive/folders/find.ts b/src/server/api/endpoints/drive/folders/find.ts index bbf496150..25d89f9fb 100644 --- a/src/server/api/endpoints/drive/folders/find.ts +++ b/src/server/api/endpoints/drive/folders/find.ts @@ -24,7 +24,7 @@ export const meta = { } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { const folders = await DriveFolder .find({ name: ps.name, @@ -32,5 +32,5 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { parentId: ps.parentId }); - res(await Promise.all(folders.map(folder => pack(folder)))); -})); + return await Promise.all(folders.map(folder => pack(folder))); +}); diff --git a/src/server/api/endpoints/drive/folders/show.ts b/src/server/api/endpoints/drive/folders/show.ts index 9a7a11c6d..0d98158b9 100644 --- a/src/server/api/endpoints/drive/folders/show.ts +++ b/src/server/api/endpoints/drive/folders/show.ts @@ -2,6 +2,7 @@ import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id'; import DriveFolder, { pack } from '../../../../../models/drive-folder'; import define from '../../../define'; +import { ApiError } from '../../../error'; export const meta = { stability: 'stable', @@ -24,10 +25,18 @@ export const meta = { 'en-US': 'Target folder ID' } } + }, + + errors: { + noSuchFolder: { + message: 'No such folder.', + code: 'NO_SUCH_FOLDER', + id: 'd74ab9eb-bb09-4bba-bf24-fb58f761e1e9' + }, } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { // Get folder const folder = await DriveFolder .findOne({ @@ -36,11 +45,10 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }); if (folder === null) { - return rej('folder-not-found'); + throw new ApiError(meta.errors.noSuchFolder); } - // Serialize - res(await pack(folder, { + return await pack(folder, { detail: true - })); -})); + }); +}); diff --git a/src/server/api/endpoints/drive/folders/update.ts b/src/server/api/endpoints/drive/folders/update.ts index 9c515474c..585664ba0 100644 --- a/src/server/api/endpoints/drive/folders/update.ts +++ b/src/server/api/endpoints/drive/folders/update.ts @@ -3,6 +3,7 @@ import ID, { transform } from '../../../../../misc/cafy-id'; import DriveFolder, { isValidFolderName, pack } from '../../../../../models/drive-folder'; import { publishDriveStream } from '../../../../../services/stream'; import define from '../../../define'; +import { ApiError } from '../../../error'; export const meta = { stability: 'stable', @@ -42,10 +43,30 @@ export const meta = { 'en-US': 'Parent folder ID' } } + }, + + errors: { + noSuchFolder: { + message: 'No such folder.', + code: 'NO_SUCH_FOLDER', + id: 'f7974dac-2c0d-4a27-926e-23583b28e98e' + }, + + noSuchParentFolder: { + message: 'No such parent folder.', + code: 'NO_SUCH_PARENT_FOLDER', + id: 'ce104e3a-faaf-49d5-b459-10ff0cbbcaa1' + }, + + recursiveNesting: { + message: 'It can not be structured like nesting folders recursively.', + code: 'NO_SUCH_PARENT_FOLDER', + id: 'ce104e3a-faaf-49d5-b459-10ff0cbbcaa1' + }, } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { // Fetch folder const folder = await DriveFolder .findOne({ @@ -54,7 +75,7 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }); if (folder === null) { - return rej('folder-not-found'); + throw new ApiError(meta.errors.noSuchFolder); } if (ps.name) folder.name = ps.name; @@ -71,7 +92,7 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }); if (parent === null) { - return rej('parent-folder-not-found'); + throw new ApiError(meta.errors.noSuchParentFolder); } // Check if the circular reference will occur @@ -95,7 +116,7 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { if (parent.parentId !== null) { if (await checkCircle(parent.parentId)) { - return rej('detected-circular-definition'); + throw new ApiError(meta.errors.recursiveNesting); } } @@ -111,12 +132,10 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { } }); - // Serialize const folderObj = await pack(folder); - // Response - res(folderObj); - // Publish folderUpdated event publishDriveStream(user._id, 'folderUpdated', folderObj); -})); + + return folderObj; +}); diff --git a/src/server/api/endpoints/drive/stream.ts b/src/server/api/endpoints/drive/stream.ts index c48ed8c37..d5872470f 100644 --- a/src/server/api/endpoints/drive/stream.ts +++ b/src/server/api/endpoints/drive/stream.ts @@ -30,12 +30,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 sort = { _id: -1 }; @@ -66,5 +61,5 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { sort: sort }); - res(await packMany(files, { self: true })); -})); + return await packMany(files, { self: true }); +}); diff --git a/src/server/api/endpoints/federation/instances.ts b/src/server/api/endpoints/federation/instances.ts index 3df24cc84..34da9dff7 100644 --- a/src/server/api/endpoints/federation/instances.ts +++ b/src/server/api/endpoints/federation/instances.ts @@ -34,7 +34,7 @@ export const meta = { } }; -export default define(meta, (ps, me) => new Promise(async (res, rej) => { +export default define(meta, async (ps, me) => { let sort; if (ps.sort) { @@ -122,5 +122,5 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { skip: ps.offset }); - res(instances); -})); + return instances; +}); diff --git a/src/server/api/endpoints/federation/show-instance.ts b/src/server/api/endpoints/federation/show-instance.ts index ad31c56af..e443abcda 100644 --- a/src/server/api/endpoints/federation/show-instance.ts +++ b/src/server/api/endpoints/federation/show-instance.ts @@ -12,9 +12,9 @@ export const meta = { } }; -export default define(meta, (ps, me) => new Promise(async (res, rej) => { +export default define(meta, async (ps, me) => { const instance = await Instance .findOne({ host: ps.host }); - res(instance); -})); + return instance; +}); diff --git a/src/server/api/endpoints/following/create.ts b/src/server/api/endpoints/following/create.ts index b12b17d67..b4a58b996 100644 --- a/src/server/api/endpoints/following/create.ts +++ b/src/server/api/endpoints/following/create.ts @@ -5,6 +5,7 @@ import User, { pack } from '../../../../models/user'; import Following from '../../../../models/following'; import create from '../../../../services/following/create'; import define from '../../define'; +import { ApiError } from '../../error'; export const meta = { stability: 'stable', @@ -32,15 +33,47 @@ export const meta = { 'en-US': 'Target user ID' } } + }, + + errors: { + noSuchUser: { + message: 'No such user.', + code: 'NO_SUCH_USER', + id: 'fcd2eef9-a9b2-4c4f-8624-038099e90aa5' + }, + + followeeIsYourself: { + message: 'Followee is yourself.', + code: 'FOLLOWEE_IS_YOURSELF', + id: '26fbe7bb-a331-4857-af17-205b426669a9' + }, + + alreadyFollowing: { + message: 'You are already following that user.', + code: 'ALREADY_FOLLOWING', + id: '35387507-38c7-4cb9-9197-300b93783fa0' + }, + + blocking: { + message: 'You are blocking that user.', + code: 'BLOCKING', + id: '4e2206ec-aa4f-4960-b865-6c23ac38e2d9' + }, + + blocked: { + message: 'You are blocked by that user.', + code: 'BLOCKED', + id: 'c4ab57cc-4e41-45e9-bfd9-584f61e35ce0' + }, } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { const follower = user; // 自分自身 if (user._id.equals(ps.userId)) { - return rej('followee is yourself'); + throw new ApiError(meta.errors.followeeIsYourself); } // Get followee @@ -54,7 +87,7 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }); if (followee === null) { - return rej('user not found'); + throw new ApiError(meta.errors.noSuchUser); } // Check if already following @@ -64,16 +97,16 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }); if (exist !== null) { - return rej('already following'); + throw new ApiError(meta.errors.alreadyFollowing); } - // Create following try { await create(follower, followee); } catch (e) { - return rej(e && e.message ? e.message : e); + if (e.id === '710e8fb0-b8c3-4922-be49-d5d93d8e6a6e') throw new ApiError(meta.errors.blocking); + if (e.id === '3338392a-f764-498d-8855-db939dcf8c48') throw new ApiError(meta.errors.blocked); + throw e; } - // Send response - res(await pack(followee._id, user)); -})); + return await pack(followee._id, user); +}); diff --git a/src/server/api/endpoints/following/delete.ts b/src/server/api/endpoints/following/delete.ts index dcc32079c..6b6fac071 100644 --- a/src/server/api/endpoints/following/delete.ts +++ b/src/server/api/endpoints/following/delete.ts @@ -5,6 +5,7 @@ import User, { pack } from '../../../../models/user'; import Following from '../../../../models/following'; import deleteFollowing from '../../../../services/following/delete'; import define from '../../define'; +import { ApiError } from '../../error'; export const meta = { stability: 'stable', @@ -32,15 +33,35 @@ export const meta = { 'en-US': 'Target user ID' } } + }, + + errors: { + noSuchUser: { + message: 'No such user.', + code: 'NO_SUCH_USER', + id: '5b12c78d-2b28-4dca-99d2-f56139b42ff8' + }, + + followeeIsYourself: { + message: 'Followee is yourself.', + code: 'FOLLOWEE_IS_YOURSELF', + id: 'd9e400b9-36b0-4808-b1d8-79e707f1296c' + }, + + notFollowing: { + message: 'You are not following that user.', + code: 'NOT_FOLLOWING', + id: '5dbf82f5-c92b-40b1-87d1-6c8c0741fd09' + }, } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { const follower = user; // Check if the followee is yourself if (user._id.equals(ps.userId)) { - return rej('followee is yourself'); + throw new ApiError(meta.errors.followeeIsYourself); } // Get followee @@ -54,7 +75,7 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }); if (followee === null) { - return rej('user not found'); + throw new ApiError(meta.errors.noSuchUser); } // Check not following @@ -64,12 +85,10 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }); if (exist === null) { - return rej('already not following'); + throw new ApiError(meta.errors.notFollowing); } - // Delete following await deleteFollowing(follower, followee); - // Send response - res(await pack(followee._id, user)); -})); + return await pack(followee._id, user); +}); diff --git a/src/server/api/endpoints/following/requests/accept.ts b/src/server/api/endpoints/following/requests/accept.ts index 4d817ad98..2aa043427 100644 --- a/src/server/api/endpoints/following/requests/accept.ts +++ b/src/server/api/endpoints/following/requests/accept.ts @@ -3,6 +3,7 @@ import ID, { transform } from '../../../../../misc/cafy-id'; import acceptFollowRequest from '../../../../../services/following/requests/accept'; import User from '../../../../../models/user'; import define from '../../../define'; +import { ApiError } from '../../../error'; export const meta = { desc: { @@ -23,20 +24,28 @@ export const meta = { 'en-US': 'Target user ID' } } + }, + + errors: { + noSuchUser: { + message: 'No such user.', + code: 'NO_SUCH_USER', + id: '66ce1645-d66c-46bb-8b79-96739af885bd' + }, } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { // Fetch follower const follower = await User.findOne({ _id: ps.userId }); if (follower === null) { - return rej('follower not found'); + throw new ApiError(meta.errors.noSuchUser); } await acceptFollowRequest(user, follower); - res(); -})); + return; +}); diff --git a/src/server/api/endpoints/following/requests/cancel.ts b/src/server/api/endpoints/following/requests/cancel.ts index dff255eff..bca9feaba 100644 --- a/src/server/api/endpoints/following/requests/cancel.ts +++ b/src/server/api/endpoints/following/requests/cancel.ts @@ -3,6 +3,7 @@ import ID, { transform } from '../../../../../misc/cafy-id'; import cancelFollowRequest from '../../../../../services/following/requests/cancel'; import User, { pack } from '../../../../../models/user'; import define from '../../../define'; +import { ApiError } from '../../../error'; export const meta = { desc: { @@ -23,24 +24,39 @@ export const meta = { 'en-US': 'Target user ID' } } + }, + + errors: { + noSuchUser: { + message: 'No such user.', + code: 'NO_SUCH_USER', + id: '4e68c551-fc4c-4e46-bb41-7d4a37bf9dab' + }, + + followRequestNotFound: { + message: 'Follow request not found.', + code: 'FOLLOW_REQUEST_NOT_FOUND', + id: '089b125b-d338-482a-9a09-e2622ac9f8d4' + }, } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { // Fetch followee const followee = await User.findOne({ _id: ps.userId }); if (followee === null) { - return rej('followee not found'); + throw new ApiError(meta.errors.noSuchUser); } try { await cancelFollowRequest(followee, user); } catch (e) { - return rej(e); + if (e.id === '17447091-ce07-46dd-b331-c1fd4f15b1e7') throw new ApiError(meta.errors.followRequestNotFound); + throw e; } - res(await pack(followee._id, user)); -})); + return await pack(followee._id, user); +}); diff --git a/src/server/api/endpoints/following/requests/list.ts b/src/server/api/endpoints/following/requests/list.ts index eee98a365..f66027764 100644 --- a/src/server/api/endpoints/following/requests/list.ts +++ b/src/server/api/endpoints/following/requests/list.ts @@ -1,5 +1,3 @@ -// import $ from 'cafy'; -// import ID, { transform } from '../../../../../cafy-id'; import FollowRequest, { pack } from '../../../../../models/follow-request'; import define from '../../../define'; @@ -14,11 +12,10 @@ export const meta = { kind: 'following-read' }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { const reqs = await FollowRequest.find({ followeeId: user._id }); - // Send response - res(await Promise.all(reqs.map(req => pack(req)))); -})); + return await Promise.all(reqs.map(req => pack(req))); +}); diff --git a/src/server/api/endpoints/following/requests/reject.ts b/src/server/api/endpoints/following/requests/reject.ts index 59257d137..760661b55 100644 --- a/src/server/api/endpoints/following/requests/reject.ts +++ b/src/server/api/endpoints/following/requests/reject.ts @@ -3,6 +3,7 @@ import ID, { transform } from '../../../../../misc/cafy-id'; import rejectFollowRequest from '../../../../../services/following/requests/reject'; import User from '../../../../../models/user'; import define from '../../../define'; +import { ApiError } from '../../../error'; export const meta = { desc: { @@ -23,20 +24,28 @@ export const meta = { 'en-US': 'Target user ID' } } + }, + + errors: { + noSuchUser: { + message: 'No such user.', + code: 'NO_SUCH_USER', + id: 'abc2ffa6-25b2-4380-ba99-321ff3a94555' + }, } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { // Fetch follower const follower = await User.findOne({ _id: ps.userId }); if (follower === null) { - return rej('follower not found'); + throw new ApiError(meta.errors.noSuchUser); } await rejectFollowRequest(user, follower); - res(); -})); + return; +}); diff --git a/src/server/api/endpoints/games/reversi/games.ts b/src/server/api/endpoints/games/reversi/games.ts index fd9308f7e..b188ba996 100644 --- a/src/server/api/endpoints/games/reversi/games.ts +++ b/src/server/api/endpoints/games/reversi/games.ts @@ -27,12 +27,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 q: any = ps.my ? { isStarted: true, $or: [{ @@ -65,8 +60,7 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { limit: ps.limit }); - // Reponse - res(Promise.all(games.map(async (g) => await pack(g, user, { + return await Promise.all(games.map((g) => pack(g, user, { detail: false - })))); -})); + }))); +}); diff --git a/src/server/api/endpoints/games/reversi/games/show.ts b/src/server/api/endpoints/games/reversi/games/show.ts index 212d72bb7..d70ab8de9 100644 --- a/src/server/api/endpoints/games/reversi/games/show.ts +++ b/src/server/api/endpoints/games/reversi/games/show.ts @@ -3,6 +3,7 @@ import ID, { transform } from '../../../../../../misc/cafy-id'; import ReversiGame, { pack } from '../../../../../../models/games/reversi/game'; import Reversi from '../../../../../../games/reversi/core'; import define from '../../../../define'; +import { ApiError } from '../../../../error'; export const meta = { params: { @@ -10,14 +11,22 @@ export const meta = { validator: $.type(ID), transform: transform, }, + }, + + errors: { + noSuchGame: { + message: 'No such game.', + code: 'NO_SUCH_GAME', + id: 'f13a03db-fae1-46c9-87f3-43c8165419e1' + }, } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { const game = await ReversiGame.findOne({ _id: ps.gameId }); if (game == null) { - return rej('game not found'); + throw new ApiError(meta.errors.noSuchGame); } const o = new Reversi(game.settings.map, { @@ -31,8 +40,8 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { const packed = await pack(game, user); - res(Object.assign({ + return Object.assign({ board: o.board, turn: o.turn - }, packed)); -})); + }, packed); +}); diff --git a/src/server/api/endpoints/games/reversi/games/surrender.ts b/src/server/api/endpoints/games/reversi/games/surrender.ts index a19743fee..954ae07eb 100644 --- a/src/server/api/endpoints/games/reversi/games/surrender.ts +++ b/src/server/api/endpoints/games/reversi/games/surrender.ts @@ -3,6 +3,7 @@ import ID, { transform } from '../../../../../../misc/cafy-id'; import ReversiGame, { pack } from '../../../../../../models/games/reversi/game'; import { publishReversiGameStream } from '../../../../../../services/stream'; import define from '../../../../define'; +import { ApiError } from '../../../../error'; export const meta = { desc: { @@ -19,22 +20,42 @@ export const meta = { 'ja-JP': '投了したい対局' } } + }, + + errors: { + noSuchGame: { + message: 'No such game.', + code: 'NO_SUCH_GAME', + id: 'ace0b11f-e0a6-4076-a30d-e8284c81b2df' + }, + + alreadyEnded: { + message: 'That game has already ended.', + code: 'ALREADY_ENDED', + id: '6c2ad4a6-cbf1-4a5b-b187-b772826cfc6d' + }, + + accessDenied: { + message: 'Access denied.', + code: 'ACCESS_DENIED', + id: '6e04164b-a992-4c93-8489-2123069973e1' + }, } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { const game = await ReversiGame.findOne({ _id: ps.gameId }); if (game == null) { - return rej('game not found'); + throw new ApiError(meta.errors.noSuchGame); } if (game.isEnded) { - return rej('this game is already ended'); + throw new ApiError(meta.errors.alreadyEnded); } if (!game.user1Id.equals(user._id) && !game.user2Id.equals(user._id)) { - return rej('access denied'); + throw new ApiError(meta.errors.accessDenied); } const winnerId = game.user1Id.equals(user._id) ? game.user2Id : game.user1Id; @@ -54,5 +75,5 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { game: await pack(game._id, user) }); - res(); -})); + return; +}); diff --git a/src/server/api/endpoints/games/reversi/invitations.ts b/src/server/api/endpoints/games/reversi/invitations.ts index d808ff2e7..034c84bdd 100644 --- a/src/server/api/endpoints/games/reversi/invitations.ts +++ b/src/server/api/endpoints/games/reversi/invitations.ts @@ -5,7 +5,7 @@ export const meta = { requireCredential: true }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { // Find session const invitations = await Matching.find({ childId: user._id @@ -15,6 +15,5 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { } }); - // Reponse - res(Promise.all(invitations.map(async (i) => await packMatching(i, user)))); -})); + return await Promise.all(invitations.map((i) => packMatching(i, user))); +}); diff --git a/src/server/api/endpoints/games/reversi/match.ts b/src/server/api/endpoints/games/reversi/match.ts index febf81506..4fd4aedc1 100644 --- a/src/server/api/endpoints/games/reversi/match.ts +++ b/src/server/api/endpoints/games/reversi/match.ts @@ -6,6 +6,7 @@ import User from '../../../../../models/user'; import { publishMainStream, publishReversiStream } from '../../../../../services/stream'; import { eighteight } from '../../../../../games/reversi/maps'; import define from '../../../define'; +import { ApiError } from '../../../error'; export const meta = { requireCredential: true, @@ -19,13 +20,27 @@ export const meta = { 'en-US': 'Target user ID' } }, + }, + + errors: { + noSuchUser: { + message: 'No such user.', + code: 'NO_SUCH_USER', + id: '0b4f0559-b484-4e31-9581-3f73cee89b28' + }, + + isYourself: { + message: 'Target user is yourself.', + code: 'TARGET_IS_YOURSELF', + id: '96fd7bd6-d2bc-426c-a865-d055dcd2828e' + }, } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { // Myself if (ps.userId.equals(user._id)) { - return rej('invalid userId param'); + throw new ApiError(meta.errors.isYourself); } // Find session @@ -57,9 +72,6 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { } }); - // Reponse - res(await packGame(game, user)); - publishReversiStream(exist.parentId, 'matched', await packGame(game, exist.parentId)); const other = await Matching.count({ @@ -69,6 +81,8 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { if (other == 0) { publishMainStream(user._id, 'reversiNoInvites'); } + + return await packGame(game, user); } else { // Fetch child const child = await User.findOne({ @@ -80,7 +94,7 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }); if (child === null) { - return rej('user not found'); + throw new ApiError(meta.errors.noSuchUser); } // 以前のセッションはすべて削除しておく @@ -95,14 +109,10 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { childId: child._id }); - // Reponse - res(); - const packed = await packMatching(matching, child); - - // 招待 publishReversiStream(child._id, 'invited', packed); - publishMainStream(child._id, 'reversiInvited', packed); + + return; } -})); +}); diff --git a/src/server/api/endpoints/games/reversi/match/cancel.ts b/src/server/api/endpoints/games/reversi/match/cancel.ts index ac51e416e..03b76babd 100644 --- a/src/server/api/endpoints/games/reversi/match/cancel.ts +++ b/src/server/api/endpoints/games/reversi/match/cancel.ts @@ -5,10 +5,10 @@ export const meta = { requireCredential: true }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { await Matching.remove({ parentId: user._id }); - res(); -})); + return; +}); diff --git a/src/server/api/endpoints/hashtags/list.ts b/src/server/api/endpoints/hashtags/list.ts index 5f197a872..bfeba1d16 100644 --- a/src/server/api/endpoints/hashtags/list.ts +++ b/src/server/api/endpoints/hashtags/list.ts @@ -60,7 +60,7 @@ const sort: any = { '-attachedRemoteUsers': { attachedRemoteUsersCount: 1 }, }; -export default define(meta, (ps, me) => new Promise(async (res, rej) => { +export default define(meta, async (ps, me) => { const q = {} as any; if (ps.attachedToUserOnly) q.attachedUsersCount = { $ne: 0 }; if (ps.attachedToLocalUserOnly) q.attachedLocalUsersCount = { $ne: 0 }; @@ -80,5 +80,5 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { } }); - res(tags); -})); + return tags; +}); diff --git a/src/server/api/endpoints/hashtags/search.ts b/src/server/api/endpoints/hashtags/search.ts index dee776c1f..eff50f413 100644 --- a/src/server/api/endpoints/hashtags/search.ts +++ b/src/server/api/endpoints/hashtags/search.ts @@ -36,7 +36,7 @@ export const meta = { } }; -export default define(meta, (ps) => new Promise(async (res, rej) => { +export default define(meta, async (ps) => { const hashtags = await Hashtag .find({ tag: new RegExp('^' + escapeRegexp(ps.query.toLowerCase())) @@ -48,5 +48,5 @@ export default define(meta, (ps) => new Promise(async (res, rej) => { skip: ps.offset }); - res(hashtags.map(tag => tag.tag)); -})); + return hashtags.map(tag => tag.tag); +}); diff --git a/src/server/api/endpoints/hashtags/trend.ts b/src/server/api/endpoints/hashtags/trend.ts index d4efe5d91..bf036642d 100644 --- a/src/server/api/endpoints/hashtags/trend.ts +++ b/src/server/api/endpoints/hashtags/trend.ts @@ -19,7 +19,7 @@ export const meta = { requireCredential: false, }; -export default define(meta, () => new Promise(async (res, rej) => { +export default define(meta, async () => { const instance = await fetchMeta(); const hidedTags = instance.hidedTags.map(t => t.toLowerCase()); @@ -49,7 +49,7 @@ export default define(meta, () => new Promise(async (res, rej) => { //#endregion if (data.length == 0) { - return res([]); + return []; } const tags: { @@ -141,5 +141,5 @@ export default define(meta, () => new Promise(async (res, rej) => { usersCount: totalCounts[i].length })); - res(stats); -})); + return stats; +}); diff --git a/src/server/api/endpoints/hashtags/users.ts b/src/server/api/endpoints/hashtags/users.ts index be6b53b88..b77bdf6aa 100644 --- a/src/server/api/endpoints/hashtags/users.ts +++ b/src/server/api/endpoints/hashtags/users.ts @@ -54,7 +54,7 @@ const sort: any = { '-updatedAt': { updatedAt: 1 }, }; -export default define(meta, (ps, me) => new Promise(async (res, rej) => { +export default define(meta, async (ps, me) => { const q = { tags: ps.tag, $and: [] @@ -79,5 +79,5 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { sort: sort[ps.sort], }); - res(await Promise.all(users.map(user => pack(user, me, { detail: true })))); -})); + return await Promise.all(users.map(user => pack(user, me, { detail: true }))); +}); diff --git a/src/server/api/endpoints/i.ts b/src/server/api/endpoints/i.ts index ab85b17d0..7ef4ddd26 100644 --- a/src/server/api/endpoints/i.ts +++ b/src/server/api/endpoints/i.ts @@ -18,13 +18,12 @@ export const meta = { } }; -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; - // Serialize - res(await pack(user, user, { + return await pack(user, user, { detail: true, includeHasUnreadNotes: true, includeSecrets: isSecure - })); -})); + }); +}); diff --git a/src/server/api/endpoints/i/2fa/done.ts b/src/server/api/endpoints/i/2fa/done.ts index 2ee0c8886..556354c38 100644 --- a/src/server/api/endpoints/i/2fa/done.ts +++ b/src/server/api/endpoints/i/2fa/done.ts @@ -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; +}); diff --git a/src/server/api/endpoints/i/2fa/register.ts b/src/server/api/endpoints/i/2fa/register.ts index 38e3ca780..302b51ec0 100644 --- a/src/server/api/endpoints/i/2fa/register.ts +++ b/src/server/api/endpoints/i/2fa/register.ts @@ -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 + }; +}); diff --git a/src/server/api/endpoints/i/2fa/unregister.ts b/src/server/api/endpoints/i/2fa/unregister.ts index 2cb48d93c..37b263919 100644 --- a/src/server/api/endpoints/i/2fa/unregister.ts +++ b/src/server/api/endpoints/i/2fa/unregister.ts @@ -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; +}); diff --git a/src/server/api/endpoints/i/authorized_apps.ts b/src/server/api/endpoints/i/authorized_apps.ts index 3e60afe69..cb8be9ed9 100644 --- a/src/server/api/endpoints/i/authorized_apps.ts +++ b/src/server/api/endpoints/i/authorized_apps.ts @@ -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 - })))); -})); + }))); +}); diff --git a/src/server/api/endpoints/i/change_password.ts b/src/server/api/endpoints/i/change_password.ts index a2cfc6f70..8ab286b4b 100644 --- a/src/server/api/endpoints/i/change_password.ts +++ b/src/server/api/endpoints/i/change_password.ts @@ -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; +}); diff --git a/src/server/api/endpoints/i/clear-follow-request-notification.ts b/src/server/api/endpoints/i/clear-follow-request-notification.ts index af4302621..2c656965d 100644 --- a/src/server/api/endpoints/i/clear-follow-request-notification.ts +++ b/src/server/api/endpoints/i/clear-follow-request-notification.ts @@ -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; +}); diff --git a/src/server/api/endpoints/i/delete-account.ts b/src/server/api/endpoints/i/delete-account.ts index 6cab3d139..e1b3093d1 100644 --- a/src/server/api/endpoints/i/delete-account.ts +++ b/src/server/api/endpoints/i/delete-account.ts @@ -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; +}); diff --git a/src/server/api/endpoints/i/export-blocking.ts b/src/server/api/endpoints/i/export-blocking.ts index 12a08c6d7..346b29c79 100644 --- a/src/server/api/endpoints/i/export-blocking.ts +++ b/src/server/api/endpoints/i/export-blocking.ts @@ -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; +}); diff --git a/src/server/api/endpoints/i/export-following.ts b/src/server/api/endpoints/i/export-following.ts index e819898d9..5977b0310 100644 --- a/src/server/api/endpoints/i/export-following.ts +++ b/src/server/api/endpoints/i/export-following.ts @@ -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; +}); diff --git a/src/server/api/endpoints/i/export-mute.ts b/src/server/api/endpoints/i/export-mute.ts index c719a1f6a..22ceff363 100644 --- a/src/server/api/endpoints/i/export-mute.ts +++ b/src/server/api/endpoints/i/export-mute.ts @@ -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; +}); diff --git a/src/server/api/endpoints/i/export-notes.ts b/src/server/api/endpoints/i/export-notes.ts index 6da6e68b9..2881aa269 100644 --- a/src/server/api/endpoints/i/export-notes.ts +++ b/src/server/api/endpoints/i/export-notes.ts @@ -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; +}); diff --git a/src/server/api/endpoints/i/favorites.ts b/src/server/api/endpoints/i/favorites.ts index c476844b4..b9bef38c6 100644 --- a/src/server/api/endpoints/i/favorites.ts +++ b/src/server/api/endpoints/i/favorites.ts @@ -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); +}); diff --git a/src/server/api/endpoints/i/notifications.ts b/src/server/api/endpoints/i/notifications.ts index 6287e4262..693d27996 100644 --- a/src/server/api/endpoints/i/notifications.ts +++ b/src/server/api/endpoints/i/notifications.ts @@ -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); +}); diff --git a/src/server/api/endpoints/i/pin.ts b/src/server/api/endpoints/i/pin.ts index 79a3e42fb..0b6b1b0db 100644 --- a/src/server/api/endpoints/i/pin.ts +++ b/src/server/api/endpoints/i/pin.ts @@ -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 + }); +}); diff --git a/src/server/api/endpoints/i/read_all_messaging_messages.ts b/src/server/api/endpoints/i/read_all_messaging_messages.ts index 739acf93c..6bc6e16b4 100644 --- a/src/server/api/endpoints/i/read_all_messaging_messages.ts +++ b/src/server/api/endpoints/i/read_all_messaging_messages.ts @@ -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; +}); diff --git a/src/server/api/endpoints/i/read_all_unread_notes.ts b/src/server/api/endpoints/i/read_all_unread_notes.ts index 509450e43..16db17cb0 100644 --- a/src/server/api/endpoints/i/read_all_unread_notes.ts +++ b/src/server/api/endpoints/i/read_all_unread_notes.ts @@ -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; +}); diff --git a/src/server/api/endpoints/i/regenerate_token.ts b/src/server/api/endpoints/i/regenerate_token.ts index 16c298318..ad10b99b3 100644 --- a/src/server/api/endpoints/i/regenerate_token.ts +++ b/src/server/api/endpoints/i/regenerate_token.ts @@ -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; +}); diff --git a/src/server/api/endpoints/i/signin_history.ts b/src/server/api/endpoints/i/signin_history.ts index cdffc6cc3..87160a9f9 100644 --- a/src/server/api/endpoints/i/signin_history.ts +++ b/src/server/api/endpoints/i/signin_history.ts @@ -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))); +}); diff --git a/src/server/api/endpoints/i/unpin.ts b/src/server/api/endpoints/i/unpin.ts index 405c3cb9c..472e81156 100644 --- a/src/server/api/endpoints/i/unpin.ts +++ b/src/server/api/endpoints/i/unpin.ts @@ -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 + }); +}); diff --git a/src/server/api/endpoints/i/update.ts b/src/server/api/endpoints/i/update.ts index 1de7a807c..df7da4677 100644 --- a/src/server/api/endpoints/i/update.ts +++ b/src/server/api/endpoints/i/update.ts @@ -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; +}); diff --git a/src/server/api/endpoints/i/update_client_setting.ts b/src/server/api/endpoints/i/update_client_setting.ts index 2157986f1..79cd04e16 100644 --- a/src/server/api/endpoints/i/update_client_setting.ts +++ b/src/server/api/endpoints/i/update_client_setting.ts @@ -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; +}); diff --git a/src/server/api/endpoints/i/update_email.ts b/src/server/api/endpoints/i/update_email.ts index 8781c8ac9..c90462d85 100644 --- a/src/server/api/endpoints/i/update_email.ts +++ b/src/server/api/endpoints/i/update_email.ts @@ -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; +}); diff --git a/src/server/api/endpoints/i/update_home.ts b/src/server/api/endpoints/i/update_home.ts index 805c52d72..e2c319887 100644 --- a/src/server/api/endpoints/i/update_home.ts +++ b/src/server/api/endpoints/i/update_home.ts @@ -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; +}); diff --git a/src/server/api/endpoints/i/update_mobile_home.ts b/src/server/api/endpoints/i/update_mobile_home.ts index 0e1c3313e..642e2b3e0 100644 --- a/src/server/api/endpoints/i/update_mobile_home.ts +++ b/src/server/api/endpoints/i/update_mobile_home.ts @@ -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; +}); diff --git a/src/server/api/endpoints/i/update_widget.ts b/src/server/api/endpoints/i/update_widget.ts index e3e91c1f0..67d342278 100644 --- a/src/server/api/endpoints/i/update_widget.ts +++ b/src/server/api/endpoints/i/update_widget.ts @@ -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'); } -})); +}); diff --git a/src/server/api/endpoints/messaging/history.ts b/src/server/api/endpoints/messaging/history.ts index 408e2d42a..e42bf79c5 100644 --- a/src/server/api/endpoints/messaging/history.ts +++ b/src/server/api/endpoints/messaging/history.ts @@ -21,7 +21,7 @@ export const meta = { } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { const mute = await Mute.find({ muterId: user._id, deletedAt: { $exists: false } @@ -58,5 +58,5 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { } } - res(await Promise.all(history.map(h => pack(h._id, user)))); -})); + return await Promise.all(history.map(h => pack(h._id, user))); +}); diff --git a/src/server/api/endpoints/messaging/messages.ts b/src/server/api/endpoints/messaging/messages.ts index 4c1b7206d..9b27d4cac 100644 --- a/src/server/api/endpoints/messaging/messages.ts +++ b/src/server/api/endpoints/messaging/messages.ts @@ -5,6 +5,7 @@ import User from '../../../../models/user'; import { pack } from '../../../../models/messaging-message'; import read from '../../common/read-messaging-message'; import define from '../../define'; +import { ApiError } from '../../error'; export const meta = { desc: { @@ -45,15 +46,18 @@ export const meta = { validator: $.optional.bool, default: true } + }, + + errors: { + noSuchUser: { + message: 'No such user.', + code: 'NO_SUCH_USER', + id: '11795c64-40ea-4198-b06e-3c873ed9039d' + }, } }; -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) => { // Fetch recipient const recipient = await User.findOne({ _id: ps.userId @@ -64,7 +68,7 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }); if (recipient === null) { - return rej('user not found'); + throw new ApiError(meta.errors.noSuchUser); } const query = { @@ -98,16 +102,12 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { sort: sort }); - res(await Promise.all(messages.map(message => pack(message, user, { - populateRecipient: false - })))); - - if (messages.length === 0) { - return; - } - // Mark all as read if (ps.markAsRead) { read(user._id, recipient._id, messages); } -})); + + return await Promise.all(messages.map(message => pack(message, user, { + populateRecipient: false + }))); +}); diff --git a/src/server/api/endpoints/messaging/messages/create.ts b/src/server/api/endpoints/messaging/messages/create.ts index 96a047a9b..3885d2583 100644 --- a/src/server/api/endpoints/messaging/messages/create.ts +++ b/src/server/api/endpoints/messaging/messages/create.ts @@ -10,6 +10,7 @@ import { publishMainStream } from '../../../../../services/stream'; import { publishMessagingStream, publishMessagingIndexStream } from '../../../../../services/stream'; import pushSw from '../../../../../services/push-notification'; import define from '../../../define'; +import { ApiError } from '../../../error'; export const meta = { desc: { @@ -39,13 +40,39 @@ export const meta = { validator: $.optional.type(ID), transform: transform, } + }, + + errors: { + recipientIsYourself: { + message: 'You can not send a message to yourself.', + code: 'RECIPIENT_IS_YOURSELF', + id: '17e2ba79-e22a-4cbc-bf91-d327643f4a7e' + }, + + noSuchUser: { + message: 'No such user.', + code: 'NO_SUCH_USER', + id: '11795c64-40ea-4198-b06e-3c873ed9039d' + }, + + noSuchFile: { + message: 'No such file.', + code: 'NO_SUCH_FILE', + id: '4372b8e2-185d-4146-8749-2f68864a3e5f' + }, + + contentRequired: { + message: 'Content required. You need to set text or fileId.', + code: 'CONTENT_REQUIRED', + id: '25587321-b0e6-449c-9239-f8925092942c' + } } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { // Myself if (ps.userId.equals(user._id)) { - return rej('cannot send message to myself'); + throw new ApiError(meta.errors.recipientIsYourself); } // Fetch recipient @@ -58,7 +85,7 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }); if (recipient === null) { - return rej('user not found'); + throw new ApiError(meta.errors.noSuchUser); } let file = null; @@ -69,16 +96,15 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }); if (file === null) { - return rej('file not found'); + throw new ApiError(meta.errors.noSuchFile); } } // テキストが無いかつ添付ファイルも無かったらエラー if (ps.text == null && file == null) { - return rej('text or file is required'); + throw new ApiError(meta.errors.contentRequired); } - // メッセージを作成 const message = await Message.insert({ createdAt: new Date(), fileId: file ? file._id : undefined, @@ -88,12 +114,8 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { isRead: false }); - // Serialize const messageObj = await pack(message); - // Reponse - res(messageObj); - // 自分のストリーム publishMessagingStream(message.userId, message.recipientId, 'message', messageObj); publishMessagingIndexStream(message.userId, 'message', messageObj); @@ -131,4 +153,6 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { pushSw(message.recipientId, 'unreadMessagingMessage', messageObj); } }, 2000); -})); + + return messageObj; +}); diff --git a/src/server/api/endpoints/messaging/messages/delete.ts b/src/server/api/endpoints/messaging/messages/delete.ts index 4f3fabb4c..5bc492c19 100644 --- a/src/server/api/endpoints/messaging/messages/delete.ts +++ b/src/server/api/endpoints/messaging/messages/delete.ts @@ -4,6 +4,7 @@ import Message from '../../../../../models/messaging-message'; import define from '../../../define'; import { publishMessagingStream } from '../../../../../services/stream'; import * as ms from 'ms'; +import { ApiError } from '../../../error'; export const meta = { stability: 'stable', @@ -32,17 +33,25 @@ export const meta = { 'en-US': 'Target message ID.' } } + }, + + errors: { + noSuchMessage: { + message: 'No such message.', + code: 'NO_SUCH_MESSAGE', + id: '54b5b326-7925-42cf-8019-130fda8b56af' + }, } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { const message = await Message.findOne({ _id: ps.messageId, userId: user._id }); if (message === null) { - return rej('message not found'); + throw new ApiError(meta.errors.noSuchMessage); } await Message.remove({ _id: message._id }); @@ -50,5 +59,5 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { publishMessagingStream(message.userId, message.recipientId, 'deleted', message._id); publishMessagingStream(message.recipientId, message.userId, 'deleted', message._id); - res(); -})); + return; +}); diff --git a/src/server/api/endpoints/messaging/messages/read.ts b/src/server/api/endpoints/messaging/messages/read.ts index 8343ea28c..98f195d75 100644 --- a/src/server/api/endpoints/messaging/messages/read.ts +++ b/src/server/api/endpoints/messaging/messages/read.ts @@ -3,6 +3,7 @@ import ID, { transform } from '../../../../../misc/cafy-id'; import Message from '../../../../../models/messaging-message'; import read from '../../../common/read-messaging-message'; import define from '../../../define'; +import { ApiError } from '../../../error'; export const meta = { desc: { @@ -23,20 +24,28 @@ export const meta = { 'en-US': 'The ID of a message that you want to mark as read' } } + }, + + errors: { + noSuchMessage: { + message: 'No such message.', + code: 'NO_SUCH_MESSAGE', + id: '86d56a2f-a9c3-4afb-b13c-3e9bfef9aa14' + }, } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { const message = await Message.findOne({ _id: ps.messageId, recipientId: user._id }); if (message == null) { - return rej('message not found'); + throw new ApiError(meta.errors.noSuchMessage); } read(user._id, message.userId, message); - res(); -})); + return; +}); diff --git a/src/server/api/endpoints/meta.ts b/src/server/api/endpoints/meta.ts index b9f458e9a..82769506c 100644 --- a/src/server/api/endpoints/meta.ts +++ b/src/server/api/endpoints/meta.ts @@ -24,7 +24,7 @@ export const meta = { }, }; -export default define(meta, (ps, me) => new Promise(async (res, rej) => { +export default define(meta, async (ps, me) => { const instance = await fetchMeta(); const emojis = await Emoji.find({ host: null }, { @@ -118,5 +118,5 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { response.swPrivateKey = instance.swPrivateKey; } - res(response); -})); + return response; +}); diff --git a/src/server/api/endpoints/mute/create.ts b/src/server/api/endpoints/mute/create.ts index d25887fb4..166900e62 100644 --- a/src/server/api/endpoints/mute/create.ts +++ b/src/server/api/endpoints/mute/create.ts @@ -3,6 +3,7 @@ import ID, { transform } from '../../../../misc/cafy-id'; import User from '../../../../models/user'; import Mute from '../../../../models/mute'; import define from '../../define'; +import { ApiError } from '../../error'; export const meta = { desc: { @@ -23,15 +24,35 @@ export const meta = { 'en-US': 'Target user ID' } }, + }, + + errors: { + noSuchUser: { + message: 'No such user.', + code: 'NO_SUCH_USER', + id: '6fef56f3-e765-4957-88e5-c6f65329b8a5' + }, + + muteeIsYourself: { + message: 'Mutee is yourself.', + code: 'MUTEE_IS_YOURSELF', + id: 'a4619cb2-5f23-484b-9301-94c903074e10' + }, + + alreadyMuting: { + message: 'You are already muting that user.', + code: 'ALREADY_MUTING', + id: '7e7359cb-160c-4956-b08f-4d1c653cd007' + }, } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { const muter = user; // 自分自身 if (user._id.equals(ps.userId)) { - return rej('mutee is yourself'); + throw new ApiError(meta.errors.muteeIsYourself); } // Get mutee @@ -45,7 +66,7 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }); if (mutee === null) { - return rej('user not found'); + throw new ApiError(meta.errors.noSuchUser); } // Check if already muting @@ -55,7 +76,7 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }); if (exist !== null) { - return rej('already muting'); + throw new ApiError(meta.errors.alreadyMuting); } // Create mute @@ -65,5 +86,5 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { muteeId: mutee._id, }); - res(); -})); + return; +}); diff --git a/src/server/api/endpoints/mute/delete.ts b/src/server/api/endpoints/mute/delete.ts index 4c59ede6d..1d74c45ef 100644 --- a/src/server/api/endpoints/mute/delete.ts +++ b/src/server/api/endpoints/mute/delete.ts @@ -3,6 +3,7 @@ import ID, { transform } from '../../../../misc/cafy-id'; import User from '../../../../models/user'; import Mute from '../../../../models/mute'; import define from '../../define'; +import { ApiError } from '../../error'; export const meta = { desc: { @@ -23,15 +24,35 @@ export const meta = { 'en-US': 'Target user ID' } }, + }, + + errors: { + noSuchUser: { + message: 'No such user.', + code: 'NO_SUCH_USER', + id: 'b851d00b-8ab1-4a56-8b1b-e24187cb48ef' + }, + + muteeIsYourself: { + message: 'Mutee is yourself.', + code: 'MUTEE_IS_YOURSELF', + id: 'f428b029-6b39-4d48-a1d2-cc1ae6dd5cf9' + }, + + notMuting: { + message: 'You are not muting that user.', + code: 'NOT_MUTING', + id: '5467d020-daa9-4553-81e1-135c0c35a96d' + }, } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { const muter = user; // Check if the mutee is yourself if (user._id.equals(ps.userId)) { - return rej('mutee is yourself'); + throw new ApiError(meta.errors.muteeIsYourself); } // Get mutee @@ -45,7 +66,7 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }); if (mutee === null) { - return rej('user not found'); + throw new ApiError(meta.errors.noSuchUser); } // Check not muting @@ -55,7 +76,7 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }); if (exist === null) { - return rej('already not muting'); + throw new ApiError(meta.errors.notMuting); } // Delete mute @@ -63,5 +84,5 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { _id: exist._id }); - res(); -})); + return; +}); diff --git a/src/server/api/endpoints/mute/list.ts b/src/server/api/endpoints/mute/list.ts index ffe1f971d..84699c237 100644 --- a/src/server/api/endpoints/mute/list.ts +++ b/src/server/api/endpoints/mute/list.ts @@ -31,12 +31,7 @@ export const meta = { } }; -export default define(meta, (ps, me) => 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, me) => { const query = { muterId: me._id } as any; @@ -62,5 +57,5 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { sort: sort }); - res(await packMany(mutes, me)); -})); + return await packMany(mutes, me); +}); diff --git a/src/server/api/endpoints/my/apps.ts b/src/server/api/endpoints/my/apps.ts index 0a9570739..0eeeb5f85 100644 --- a/src/server/api/endpoints/my/apps.ts +++ b/src/server/api/endpoints/my/apps.ts @@ -23,12 +23,11 @@ export const meta = { } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { const query = { userId: user._id }; - // Execute query const apps = await App .find(query, { limit: ps.limit, @@ -38,8 +37,7 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { } }); - // Reply - res(await Promise.all(apps.map(app => pack(app, user, { + return await Promise.all(apps.map(app => pack(app, user, { detail: true - })))); -})); + }))); +}); diff --git a/src/server/api/endpoints/notes.ts b/src/server/api/endpoints/notes.ts index f95ed19d2..f8d6fdd2b 100644 --- a/src/server/api/endpoints/notes.ts +++ b/src/server/api/endpoints/notes.ts @@ -68,13 +68,7 @@ export const meta = { } }; -export default define(meta, (ps) => 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'); - } - - // Construct query +export default define(meta, async (ps) => { const sort = { _id: -1 }; @@ -119,13 +113,10 @@ export default define(meta, (ps) => new Promise(async (res, rej) => { // query.isBot = bot; //} - // Issue query - const notes = await Note - .find(query, { - limit: ps.limit, - sort: sort - }); + const notes = await Note.find(query, { + limit: ps.limit, + sort: sort + }); - // Serialize - res(await packMany(notes)); -})); + return await packMany(notes); +}); diff --git a/src/server/api/endpoints/notes/conversation.ts b/src/server/api/endpoints/notes/conversation.ts index 848cf9e16..ec371ecf1 100644 --- a/src/server/api/endpoints/notes/conversation.ts +++ b/src/server/api/endpoints/notes/conversation.ts @@ -2,6 +2,8 @@ import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id'; import Note, { packMany, INote } from '../../../../models/note'; import define from '../../define'; +import { ApiError } from '../../error'; +import { getValiedNote } from '../../common/getters'; export const meta = { desc: { @@ -30,19 +32,23 @@ export const meta = { validator: $.optional.num.min(0), default: 0 }, + }, + + errors: { + noSuchNote: { + message: 'No such note.', + code: 'NO_SUCH_NOTE', + id: 'e1035875-9551-45ec-afa8-1ded1fcb53c8' + } } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { - // Lookup note - const note = await Note.findOne({ - _id: ps.noteId +export default define(meta, async (ps, user) => { + const note = await getValiedNote(ps.noteId).catch(e => { + if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw e; }); - if (note === null) { - return rej('note not found'); - } - const conversation: INote[] = []; let i = 0; @@ -67,5 +73,5 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { await get(note.replyId); } - res(await packMany(conversation, user)); -})); + return await packMany(conversation, user); +}); diff --git a/src/server/api/endpoints/notes/create.ts b/src/server/api/endpoints/notes/create.ts index 47eebc186..d24cd7b12 100644 --- a/src/server/api/endpoints/notes/create.ts +++ b/src/server/api/endpoints/notes/create.ts @@ -8,6 +8,7 @@ import DriveFile, { IDriveFile } from '../../../../models/drive-file'; import create from '../../../../services/note/create'; import define from '../../define'; import fetchMeta from '../../../../misc/fetch-meta'; +import { ApiError } from '../../error'; let maxNoteTextLength = 1000; @@ -180,10 +181,42 @@ export const meta = { } } } + }, + + errors: { + noSuchRenoteTarget: { + message: 'No such renote target.', + code: 'NO_SUCH_RENOTE_TARGET', + id: 'b5c90186-4ab0-49c8-9bba-a1f76c282ba4' + }, + + cannotReRenote: { + message: 'You can not Renote a pure Renote.', + code: 'CANNOT_RENOTE_TO_A_PURE_RENOTE', + id: 'fd4cc33e-2a37-48dd-99cc-9b806eb2031a' + }, + + noSuchReplyTarget: { + message: 'No such reply target.', + code: 'NO_SUCH_REPLY_TARGET', + id: '749ee0f6-d3da-459a-bf02-282e2da4292c' + }, + + cannotReplyToPureRenote: { + message: 'You can not reply to a pure Renote.', + code: 'CANNOT_REPLY_TO_A_PURE_RENOTE', + id: '3ac74a84-8fd5-4bb0-870f-01804f82ce15' + }, + + contentRequired: { + message: 'Content required. You need to set text, fileIds, renoteId or poll.', + code: 'CONTENT_REQUIRED', + id: '6f57e42b-c348-439b-bc45-993995cc515a' + }, } }; -export default define(meta, (ps, user, app) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user, app) => { let visibleUsers: IUser[] = []; if (ps.visibleUserIds) { visibleUsers = await Promise.all(ps.visibleUserIds.map(id => User.findOne({ @@ -212,9 +245,9 @@ export default define(meta, (ps, user, app) => new Promise(async (res, rej) => { }); if (renote == null) { - return rej('renoteee is not found'); + throw new ApiError(meta.errors.noSuchRenoteTarget); } else if (renote.renoteId && !renote.text && !renote.fileIds) { - return rej('cannot renote to renote'); + throw new ApiError(meta.errors.cannotReRenote); } } @@ -226,12 +259,12 @@ export default define(meta, (ps, user, app) => new Promise(async (res, rej) => { }); if (reply === null) { - return rej('in reply to note is not found'); + throw new ApiError(meta.errors.noSuchReplyTarget); } // 返信対象が引用でないRenoteだったらエラー if (reply.renoteId && !reply.text && !reply.fileIds) { - return rej('cannot reply to renote'); + throw new ApiError(meta.errors.cannotReplyToPureRenote); } } @@ -245,7 +278,7 @@ export default define(meta, (ps, user, app) => new Promise(async (res, rej) => { // テキストが無いかつ添付ファイルが無いかつRenoteも無いかつ投票も無かったらエラー if (!(ps.text || files.length || renote || ps.poll)) { - return rej('text, fileIds, renoteId or poll is required'); + throw new ApiError(meta.errors.contentRequired); } // 後方互換性のため @@ -254,7 +287,7 @@ export default define(meta, (ps, user, app) => new Promise(async (res, rej) => { } // 投稿を作成 - create(user, { + const note = await create(user, { createdAt: new Date(), files: files, poll: ps.poll, @@ -271,14 +304,9 @@ export default define(meta, (ps, user, app) => new Promise(async (res, rej) => { apHashtags: ps.noExtractHashtags ? [] : undefined, apEmojis: ps.noExtractEmojis ? [] : undefined, geo: ps.geo - }) - .then(note => pack(note, user)) - .then(noteObj => { - res({ - createdNote: noteObj - }); - }) - .catch(e => { - rej(e); }); -})); + + return { + createdNote: await pack(note, user) + }; +}); diff --git a/src/server/api/endpoints/notes/delete.ts b/src/server/api/endpoints/notes/delete.ts index 261c7ad54..42f749418 100644 --- a/src/server/api/endpoints/notes/delete.ts +++ b/src/server/api/endpoints/notes/delete.ts @@ -1,10 +1,11 @@ import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id'; -import Note from '../../../../models/note'; import deleteNote from '../../../../services/note/delete'; import User from '../../../../models/user'; import define from '../../define'; import * as ms from 'ms'; +import { getValiedNote } from '../../common/getters'; +import { ApiError } from '../../error'; export const meta = { stability: 'stable', @@ -33,24 +34,32 @@ export const meta = { 'en-US': 'Target note ID.' } } + }, + + errors: { + noSuchNote: { + message: 'No such note.', + code: 'NO_SUCH_NOTE', + id: '490be23f-8c1f-4796-819f-94cb4f9d1630' + }, + + accessDenied: { + message: 'Access denied.', + code: 'ACCESS_DENIED', + id: 'fe8d7103-0ea8-4ec3-814d-f8b401dc69e9' + } } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { - // Fetch note - const note = await Note.findOne({ - _id: ps.noteId +export default define(meta, async (ps, user) => { + const note = await getValiedNote(ps.noteId).catch(e => { + if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw e; }); - if (note === null) { - return rej('note not found'); - } - if (!user.isAdmin && !user.isModerator && !note.userId.equals(user._id)) { - return rej('access denied'); + throw new ApiError(meta.errors.accessDenied); } await deleteNote(await User.findOne({ _id: note.userId }), note); - - res(); -})); +}); diff --git a/src/server/api/endpoints/notes/favorites/create.ts b/src/server/api/endpoints/notes/favorites/create.ts index 8ec0891a3..bdb4aff72 100644 --- a/src/server/api/endpoints/notes/favorites/create.ts +++ b/src/server/api/endpoints/notes/favorites/create.ts @@ -3,6 +3,7 @@ import ID, { transform } from '../../../../../misc/cafy-id'; import Favorite from '../../../../../models/favorite'; import Note from '../../../../../models/note'; import define from '../../../define'; +import { ApiError } from '../../../error'; export const meta = { stability: 'stable', @@ -25,17 +26,31 @@ export const meta = { 'en-US': 'Target note ID.' } } + }, + + errors: { + noSuchNote: { + message: 'No such note.', + code: 'NO_SUCH_NOTE', + id: '6dd26674-e060-4816-909a-45ba3f4da458' + }, + + alreadyFavorited: { + message: 'The note has already been marked as a favorite.', + code: 'ALREADY_FAVORITED', + id: 'a402c12b-34dd-41d2-97d8-4d2ffd96a1a6' + }, } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { // Get favoritee const note = await Note.findOne({ _id: ps.noteId }); if (note === null) { - return rej('note not found'); + throw new ApiError(meta.errors.noSuchNote); } // if already favorited @@ -45,7 +60,7 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }); if (exist !== null) { - return rej('already favorited'); + throw new ApiError(meta.errors.alreadyFavorited); } // Create favorite @@ -55,6 +70,5 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { userId: user._id }); - // Send response - res(); -})); + return; +}); diff --git a/src/server/api/endpoints/notes/favorites/delete.ts b/src/server/api/endpoints/notes/favorites/delete.ts index f0b9e249f..700eafdaf 100644 --- a/src/server/api/endpoints/notes/favorites/delete.ts +++ b/src/server/api/endpoints/notes/favorites/delete.ts @@ -3,6 +3,7 @@ import ID, { transform } from '../../../../../misc/cafy-id'; import Favorite from '../../../../../models/favorite'; import Note from '../../../../../models/note'; import define from '../../../define'; +import { ApiError } from '../../../error'; export const meta = { stability: 'stable', @@ -25,17 +26,31 @@ export const meta = { 'en-US': 'Target note ID.' } } + }, + + errors: { + noSuchNote: { + message: 'No such note.', + code: 'NO_SUCH_NOTE', + id: '80848a2c-398f-4343-baa9-df1d57696c56' + }, + + notFavorited: { + message: 'You have not marked that note a favorite.', + code: 'NOT_FAVORITED', + id: 'b625fc69-635e-45e9-86f4-dbefbef35af5' + }, } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { // Get favoritee const note = await Note.findOne({ _id: ps.noteId }); if (note === null) { - return rej('note not found'); + throw new ApiError(meta.errors.noSuchNote); } // if already favorited @@ -45,7 +60,7 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }); if (exist === null) { - return rej('already not favorited'); + throw new ApiError(meta.errors.notFavorited); } // Delete favorite @@ -53,6 +68,5 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { _id: exist._id }); - // Send response - res(); -})); + return; +}); diff --git a/src/server/api/endpoints/notes/featured.ts b/src/server/api/endpoints/notes/featured.ts index 7a3c2b76e..08cc677ab 100644 --- a/src/server/api/endpoints/notes/featured.ts +++ b/src/server/api/endpoints/notes/featured.ts @@ -23,29 +23,28 @@ export const meta = { } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { const day = 1000 * 60 * 60 * 24 * 2; const hideUserIds = await getHideUserIds(user); - const notes = await Note - .find({ - createdAt: { - $gt: new Date(Date.now() - day) - }, - deletedAt: null, - visibility: { $in: ['public', 'home'] }, - '_user.host': null, - ...(hideUserIds && hideUserIds.length > 0 ? { userId: { $nin: hideUserIds } } : {}) - }, { - limit: ps.limit, - sort: { - score: -1 - }, - hint: { - score: -1 - } - }); + const notes = await Note.find({ + createdAt: { + $gt: new Date(Date.now() - day) + }, + deletedAt: null, + visibility: { $in: ['public', 'home'] }, + '_user.host': null, + ...(hideUserIds && hideUserIds.length > 0 ? { userId: { $nin: hideUserIds } } : {}) + }, { + limit: ps.limit, + sort: { + score: -1 + }, + hint: { + score: -1 + } + }); - res(await packMany(notes, user)); -})); + return await packMany(notes, user); +}); diff --git a/src/server/api/endpoints/notes/global-timeline.ts b/src/server/api/endpoints/notes/global-timeline.ts index 11825f288..8670f8edb 100644 --- a/src/server/api/endpoints/notes/global-timeline.ts +++ b/src/server/api/endpoints/notes/global-timeline.ts @@ -3,9 +3,9 @@ import ID, { transform } from '../../../../misc/cafy-id'; import Note from '../../../../models/note'; import { packMany } from '../../../../models/note'; import define from '../../define'; -import { countIf } from '../../../../prelude/array'; import fetchMeta from '../../../../misc/fetch-meta'; import { getHideUserIds } from '../../common/get-hide-users'; +import { ApiError } from '../../error'; export const meta = { desc: { @@ -49,22 +49,25 @@ export const meta = { untilDate: { validator: $.optional.num }, + }, + + errors: { + gtlDisabled: { + message: 'Global timeline has been disabled.', + code: 'GTL_DISABLED', + id: '0332fc13-6ab2-4427-ae80-a9fadffd1a6b' + }, } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { - const meta = await fetchMeta(); - if (meta.disableGlobalTimeline) { +export default define(meta, async (ps, user) => { + const m = await fetchMeta(); + if (m.disableGlobalTimeline) { if (user == null || (!user.isAdmin && !user.isModerator)) { - return rej('global timeline disabled'); + throw new ApiError(meta.errors.gtlDisabled); } } - // Check if only one of sinceId, untilId, sinceDate, untilDate specified - if (countIf(x => x != null, [ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate]) > 1) { - return rej('only one of sinceId, untilId, sinceDate, untilDate can be specified'); - } - // 隠すユーザーを取得 const hideUserIds = await getHideUserIds(user); @@ -123,11 +126,10 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { } //#endregion - const timeline = await Note - .find(query, { - limit: ps.limit, - sort: sort - }); + const timeline = await Note.find(query, { + limit: ps.limit, + sort: sort + }); - res(await packMany(timeline, user)); -})); + return await packMany(timeline, user); +}); diff --git a/src/server/api/endpoints/notes/hybrid-timeline.ts b/src/server/api/endpoints/notes/hybrid-timeline.ts index 77ccf1abc..f3050caec 100644 --- a/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -4,10 +4,10 @@ import Note from '../../../../models/note'; import { getFriends } from '../../common/get-friends'; import { packMany } from '../../../../models/note'; import define from '../../define'; -import { countIf } from '../../../../prelude/array'; import fetchMeta from '../../../../misc/fetch-meta'; import activeUsersChart from '../../../../services/chart/active-users'; import { getHideUserIds } from '../../common/get-hide-users'; +import { ApiError } from '../../error'; export const meta = { desc: { @@ -90,18 +90,21 @@ export const meta = { 'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します (このパラメータは廃止予定です。代わりに withFiles を使ってください。)' } }, + }, + + errors: { + stlDisabled: { + message: 'Social timeline has been disabled.', + code: 'STL_DISABLED', + id: '620763f4-f621-4533-ab33-0577a1a3c342' + }, } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { - const meta = await fetchMeta(); - if (meta.disableLocalTimeline && !user.isAdmin && !user.isModerator) { - return rej('local timeline disabled'); - } - - // Check if only one of sinceId, untilId, sinceDate, untilDate specified - if (countIf(x => x != null, [ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate]) > 1) { - return rej('only one of sinceId, untilId, sinceDate, untilDate can be specified'); +export default define(meta, async (ps, user) => { + const m = await fetchMeta(); + if (m.disableLocalTimeline && !user.isAdmin && !user.isModerator) { + throw new ApiError(meta.errors.stlDisabled); } const [followings, hideUserIds] = await Promise.all([ @@ -266,13 +269,12 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { } //#endregion - const timeline = await Note - .find(query, { - limit: ps.limit, - sort: sort - }); - - res(await packMany(timeline, user)); + const timeline = await Note.find(query, { + limit: ps.limit, + sort: sort + }); activeUsersChart.update(user); -})); + + return await packMany(timeline, user); +}); diff --git a/src/server/api/endpoints/notes/local-timeline.ts b/src/server/api/endpoints/notes/local-timeline.ts index 34695e1bc..44328ebed 100644 --- a/src/server/api/endpoints/notes/local-timeline.ts +++ b/src/server/api/endpoints/notes/local-timeline.ts @@ -3,10 +3,10 @@ import ID, { transform } from '../../../../misc/cafy-id'; import Note from '../../../../models/note'; import { packMany } from '../../../../models/note'; import define from '../../define'; -import { countIf } from '../../../../prelude/array'; import fetchMeta from '../../../../misc/fetch-meta'; import activeUsersChart from '../../../../services/chart/active-users'; import { getHideUserIds } from '../../common/get-hide-users'; +import { ApiError } from '../../error'; export const meta = { desc: { @@ -65,22 +65,25 @@ export const meta = { untilDate: { validator: $.optional.num, }, + }, + + errors: { + ltlDisabled: { + message: 'Local timeline has been disabled.', + code: 'LTL_DISABLED', + id: '45a6eb02-7695-4393-b023-dd3be9aaaefd' + }, } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { - const meta = await fetchMeta(); - if (meta.disableLocalTimeline) { +export default define(meta, async (ps, user) => { + const m = await fetchMeta(); + if (m.disableLocalTimeline) { if (user == null || (!user.isAdmin && !user.isModerator)) { - return rej('local timeline disabled'); + throw new ApiError(meta.errors.ltlDisabled); } } - // Check if only one of sinceId, untilId, sinceDate, untilDate specified - if (countIf(x => x != null, [ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate]) > 1) { - return rej('only one of sinceId, untilId, sinceDate, untilDate can be specified'); - } - // 隠すユーザーを取得 const hideUserIds = await getHideUserIds(user); @@ -157,15 +160,14 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { } //#endregion - const timeline = await Note - .find(query, { - limit: ps.limit, - sort: sort - }); - - res(await packMany(timeline, user)); + const timeline = await Note.find(query, { + limit: ps.limit, + sort: sort + }); if (user) { activeUsersChart.update(user); } -})); + + return await packMany(timeline, user); +}); diff --git a/src/server/api/endpoints/notes/mentions.ts b/src/server/api/endpoints/notes/mentions.ts index 3935a9359..090f184f7 100644 --- a/src/server/api/endpoints/notes/mentions.ts +++ b/src/server/api/endpoints/notes/mentions.ts @@ -42,12 +42,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 followings = await getFriends(user._id); @@ -131,15 +126,14 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }; } - const mentions = await Note - .find(query, { - limit: ps.limit, - sort: sort - }); - - res(await packMany(mentions, user)); + const mentions = await Note.find(query, { + limit: ps.limit, + sort: sort + }); for (const note of mentions) { read(user._id, note._id); } -})); + + return await packMany(mentions, user); +}); diff --git a/src/server/api/endpoints/notes/polls/recommendation.ts b/src/server/api/endpoints/notes/polls/recommendation.ts index 8e11e6529..61a1840b8 100644 --- a/src/server/api/endpoints/notes/polls/recommendation.ts +++ b/src/server/api/endpoints/notes/polls/recommendation.ts @@ -25,7 +25,7 @@ export const meta = { } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { // Get votes const votes = await Vote.find({ userId: user._id @@ -41,29 +41,28 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { // 隠すユーザーを取得 const hideUserIds = await getHideUserIds(user); - const notes = await Note - .find({ - '_user.host': null, - _id: { - $nin: nin - }, - userId: { - $ne: user._id, - $nin: hideUserIds - }, - poll: { - $exists: true, - $ne: null - } - }, { - limit: ps.limit, - skip: ps.offset, - sort: { - _id: -1 - } - }); + const notes = await Note.find({ + '_user.host': null, + _id: { + $nin: nin + }, + userId: { + $ne: user._id, + $nin: hideUserIds + }, + poll: { + $exists: true, + $ne: null + } + }, { + limit: ps.limit, + skip: ps.offset, + sort: { + _id: -1 + } + }); - res(await Promise.all(notes.map(note => pack(note, user, { + return await Promise.all(notes.map(note => pack(note, user, { detail: true - })))); -})); + }))); +}); diff --git a/src/server/api/endpoints/notes/polls/vote.ts b/src/server/api/endpoints/notes/polls/vote.ts index 68c3898e0..fe0fffec6 100644 --- a/src/server/api/endpoints/notes/polls/vote.ts +++ b/src/server/api/endpoints/notes/polls/vote.ts @@ -9,6 +9,7 @@ import notify from '../../../../../services/create-notification'; import define from '../../../define'; import createNote from '../../../../../services/note/create'; import User from '../../../../../models/user'; +import { ApiError } from '../../../error'; export const meta = { desc: { @@ -33,24 +34,52 @@ export const meta = { choice: { validator: $.num }, + }, + + errors: { + noSuchNote: { + message: 'No such note.', + code: 'NO_SUCH_NOTE', + id: 'ecafbd2e-c283-4d6d-aecb-1a0a33b75396' + }, + + noPoll: { + message: 'The note does not attach a poll.', + code: 'NO_POLL', + id: '5f979967-52d9-4314-a911-1c673727f92f' + }, + + invalidChoice: { + message: 'Choice ID is invalid.', + code: 'INVALID_CHOICE', + id: 'e0cc9a04-f2e8-41e4-a5f1-4127293260cc' + }, + + alreadyVoted: { + message: 'You have already voted.', + code: 'ALREADY_VOTED', + id: '0963fc77-efac-419b-9424-b391608dc6d8' + }, } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { // Get votee const note = await Note.findOne({ _id: ps.noteId }); if (note === null) { - return rej('note not found'); + throw new ApiError(meta.errors.noSuchNote); } if (note.poll == null) { - return rej('poll not found'); + throw new ApiError(meta.errors.noPoll); } - if (!note.poll.choices.some(x => x.id == ps.choice)) return rej('invalid choice param'); + if (!note.poll.choices.some(x => x.id == ps.choice)) { + throw new ApiError(meta.errors.invalidChoice); + } // if already voted const exist = await Vote.findOne({ @@ -59,7 +88,7 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }); if (exist !== null) { - return rej('already voted'); + throw new ApiError(meta.errors.alreadyVoted); } // Create vote @@ -70,9 +99,6 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { choice: ps.choice }); - // Send response - res(); - const inc: any = {}; inc[`poll.choices.${note.poll.choices.findIndex(c => c.id == ps.choice)}.votes`] = 1; @@ -132,4 +158,6 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { visibleUsers: [ pollOwner ], }); } -})); + + return; +}); diff --git a/src/server/api/endpoints/notes/reactions.ts b/src/server/api/endpoints/notes/reactions.ts index 54cbd2337..3e0656c4f 100644 --- a/src/server/api/endpoints/notes/reactions.ts +++ b/src/server/api/endpoints/notes/reactions.ts @@ -1,8 +1,9 @@ import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id'; -import Note from '../../../../models/note'; import Reaction, { pack } from '../../../../models/note-reaction'; import define from '../../define'; +import { getValiedNote } from '../../common/getters'; +import { ApiError } from '../../error'; export const meta = { desc: { @@ -41,24 +42,23 @@ export const meta = { validator: $.optional.type(ID), transform: transform, }, + }, + + errors: { + noSuchNote: { + message: 'No such note.', + code: 'NO_SUCH_NOTE', + id: '263fff3d-d0e1-4af4-bea7-8408059b451a' + } } }; -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'); - } - - // Lookup note - const note = await Note.findOne({ - _id: ps.noteId +export default define(meta, async (ps, user) => { + const note = await getValiedNote(ps.noteId).catch(e => { + if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw e; }); - if (note === null) { - return rej('note not found'); - } - const query = { noteId: note._id } as any; @@ -78,13 +78,11 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }; } - const reactions = await Reaction - .find(query, { - limit: ps.limit, - skip: ps.offset, - sort: sort - }); + const reactions = await Reaction.find(query, { + limit: ps.limit, + skip: ps.offset, + sort: sort + }); - // Serialize - res(await Promise.all(reactions.map(reaction => pack(reaction, user)))); -})); + return await Promise.all(reactions.map(reaction => pack(reaction, user))); +}); diff --git a/src/server/api/endpoints/notes/reactions/create.ts b/src/server/api/endpoints/notes/reactions/create.ts index 2612b129d..65232e231 100644 --- a/src/server/api/endpoints/notes/reactions/create.ts +++ b/src/server/api/endpoints/notes/reactions/create.ts @@ -1,11 +1,10 @@ -import * as mongo from 'mongodb'; import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id'; import createReaction from '../../../../../services/note/reaction/create'; import { validateReaction } from '../../../../../models/note-reaction'; import define from '../../../define'; -import { IUser } from '../../../../../models/user'; import { getValiedNote } from '../../../common/getters'; +import { ApiError } from '../../../error'; export const meta = { stability: 'stable', @@ -34,15 +33,38 @@ export const meta = { 'ja-JP': 'リアクションの種類' } } + }, + + errors: { + noSuchNote: { + message: 'No such note.', + code: 'NO_SUCH_NOTE', + id: '033d0620-5bfe-4027-965d-980b0c85a3ea' + }, + + isMyNote: { + message: 'You can not react to your own notes.', + code: 'IS_MY_NOTE', + id: '7eeb9714-b047-43b5-b559-7b1b72810f53' + }, + + alreadyReacted: { + message: 'You are already reacting to that note.', + code: 'ALREADY_REACTED', + id: '71efcf98-86d6-4e2b-b2ad-9d032369366b' + } } }; -export default define(meta, (ps, user) => new Promise((res, rej) => { - createReactionById(user, ps.noteId, ps.reaction) - .then(r => res(r)).catch(e => rej(e)); -})); - -async function createReactionById(user: IUser, noteId: mongo.ObjectID, reaction: string) { - const note = await getValiedNote(noteId); - await createReaction(user, note, reaction); -} +export default define(meta, async (ps, user) => { + const note = await getValiedNote(ps.noteId).catch(e => { + if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw e; + }); + await createReaction(user, note, ps.reaction).catch(e => { + if (e.id === '2d8e7297-1873-4c00-8404-792c68d7bef0') throw new ApiError(meta.errors.isMyNote); + if (e.id === '51c42bb4-931a-456b-bff7-e5a8a70dd298') throw new ApiError(meta.errors.alreadyReacted); + throw e; + }); + return; +}); diff --git a/src/server/api/endpoints/notes/reactions/delete.ts b/src/server/api/endpoints/notes/reactions/delete.ts index da3c2e356..47e98f63e 100644 --- a/src/server/api/endpoints/notes/reactions/delete.ts +++ b/src/server/api/endpoints/notes/reactions/delete.ts @@ -1,11 +1,10 @@ -import * as mongo from 'mongodb'; import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id'; import define from '../../../define'; import * as ms from 'ms'; import deleteReaction from '../../../../../services/note/reaction/delete'; -import { IUser } from '../../../../../models/user'; import { getValiedNote } from '../../../common/getters'; +import { ApiError } from '../../../error'; export const meta = { desc: { @@ -32,15 +31,30 @@ export const meta = { 'en-US': 'Target note ID' } }, + }, + + errors: { + noSuchNote: { + message: 'No such note.', + code: 'NO_SUCH_NOTE', + id: '764d9fce-f9f2-4a0e-92b1-6ceac9a7ad37' + }, + + notReacted: { + message: 'You are not reacting to that note.', + code: 'NOT_REACTED', + id: '92f4426d-4196-4125-aa5b-02943e2ec8fc' + }, } }; -export default define(meta, (ps, user) => new Promise((res, rej) => { - deleteReactionById(user, ps.noteId) - .then(r => res(r)).catch(e => rej(e)); -})); - -async function deleteReactionById(user: IUser, noteId: mongo.ObjectID) { - const note = await getValiedNote(noteId); - await deleteReaction(user, note); -} +export default define(meta, async (ps, user) => { + const note = await getValiedNote(ps.noteId).catch(e => { + if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw e; + }); + await deleteReaction(user, note).catch(e => { + if (e.id === '60527ec9-b4cb-4a88-a6bd-32d3ad26817d') throw new ApiError(meta.errors.notReacted); + throw e; + }); +}); diff --git a/src/server/api/endpoints/notes/renotes.ts b/src/server/api/endpoints/notes/renotes.ts index 531953540..9873f1bff 100644 --- a/src/server/api/endpoints/notes/renotes.ts +++ b/src/server/api/endpoints/notes/renotes.ts @@ -2,6 +2,8 @@ import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id'; import Note, { packMany } from '../../../../models/note'; import define from '../../define'; +import { getValiedNote } from '../../common/getters'; +import { ApiError } from '../../error'; export const meta = { desc: { @@ -35,24 +37,23 @@ export const meta = { validator: $.optional.type(ID), transform: transform, } + }, + + errors: { + noSuchNote: { + message: 'No such note.', + code: 'NO_SUCH_NOTE', + id: '12908022-2e21-46cd-ba6a-3edaf6093f46' + } } }; -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'); - } - - // Lookup note - const note = await Note.findOne({ - _id: ps.noteId +export default define(meta, async (ps, user) => { + const note = await getValiedNote(ps.noteId).catch(e => { + if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw e; }); - if (note === null) { - return rej('note not found'); - } - const sort = { _id: -1 }; @@ -72,11 +73,10 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }; } - const renotes = await Note - .find(query, { - limit: ps.limit, - sort: sort - }); + const renotes = await Note.find(query, { + limit: ps.limit, + sort: sort + }); - res(await packMany(renotes, user)); -})); + return await packMany(renotes, user); +}); diff --git a/src/server/api/endpoints/notes/replies.ts b/src/server/api/endpoints/notes/replies.ts index 26e9c6c5c..080917f6f 100644 --- a/src/server/api/endpoints/notes/replies.ts +++ b/src/server/api/endpoints/notes/replies.ts @@ -35,7 +35,7 @@ export const meta = { } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { const [followings, hideUserIds] = await Promise.all([ // フォローを取得 // Fetch following @@ -85,5 +85,5 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { skip: ps.offset }); - res(await packMany(notes, user)); -})); + return await packMany(notes, user); +}); diff --git a/src/server/api/endpoints/notes/search.ts b/src/server/api/endpoints/notes/search.ts index e671e7250..3eec2e318 100644 --- a/src/server/api/endpoints/notes/search.ts +++ b/src/server/api/endpoints/notes/search.ts @@ -4,7 +4,7 @@ import Note from '../../../../models/note'; import { packMany } from '../../../../models/note'; import es from '../../../../db/elasticsearch'; import define from '../../define'; -import { apiLogger } from '../../logger'; +import { ApiError } from '../../error'; export const meta = { desc: { @@ -28,13 +28,21 @@ export const meta = { validator: $.optional.num.min(0), default: 0 } + }, + + errors: { + searchingNotAvailable: { + message: 'Searching not available.', + code: 'SEARCHING_NOT_AVAILABLE', + id: '7ee9c119-16a1-479f-a6fd-6fab00ed946f' + } } }; -export default define(meta, (ps, me) => new Promise(async (res, rej) => { - if (es == null) return rej('searching not available'); +export default define(meta, async (ps, me) => { + if (es == null) throw new ApiError(meta.errors.searchingNotAvailable); - es.search({ + const response = await es.search({ index: 'misskey', type: 'note', body: { @@ -51,29 +59,24 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { { _doc: 'desc' } ] } - }, async (error, response) => { - if (error) { - apiLogger.error(error); - return res(500); - } - - if (response.hits.total === 0) { - return res([]); - } - - const hits = response.hits.hits.map(hit => new mongo.ObjectID(hit._id)); - - // Fetch found notes - const notes = await Note.find({ - _id: { - $in: hits - } - }, { - sort: { - _id: -1 - } - }); - - res(await packMany(notes, me)); }); -})); + + if (response.hits.total === 0) { + return []; + } + + const hits = response.hits.hits.map(hit => new mongo.ObjectID(hit._id)); + + // Fetch found notes + const notes = await Note.find({ + _id: { + $in: hits + } + }, { + sort: { + _id: -1 + } + }); + + return await packMany(notes, me); +}); diff --git a/src/server/api/endpoints/notes/search_by_tag.ts b/src/server/api/endpoints/notes/search_by_tag.ts index 05b22bb9f..a4fd1903e 100644 --- a/src/server/api/endpoints/notes/search_by_tag.ts +++ b/src/server/api/endpoints/notes/search_by_tag.ts @@ -103,7 +103,7 @@ export const meta = { } }; -export default define(meta, (ps, me) => new Promise(async (res, rej) => { +export default define(meta, async (ps, me) => { const visibleQuery = me == null ? [{ visibility: { $in: [ 'public', 'home' ] } }] : [{ @@ -317,15 +317,13 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { } // Search notes - const notes = await Note - .find(q, { - sort: { - _id: -1 - }, - limit: ps.limit, - skip: ps.offset - }); + const notes = await Note.find(q, { + sort: { + _id: -1 + }, + limit: ps.limit, + skip: ps.offset + }); - // Serialize - res(await packMany(notes, me)); -})); + return await packMany(notes, me); +}); diff --git a/src/server/api/endpoints/notes/show.ts b/src/server/api/endpoints/notes/show.ts index 41134a220..2134fad95 100644 --- a/src/server/api/endpoints/notes/show.ts +++ b/src/server/api/endpoints/notes/show.ts @@ -1,7 +1,9 @@ import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id'; -import Note, { pack } from '../../../../models/note'; +import { pack } from '../../../../models/note'; import define from '../../define'; +import { getValiedNote } from '../../common/getters'; +import { ApiError } from '../../error'; export const meta = { stability: 'stable', @@ -22,21 +24,24 @@ export const meta = { 'en-US': 'Target note ID.' } } + }, + + errors: { + noSuchNote: { + message: 'No such note.', + code: 'NO_SUCH_NOTE', + id: '24fcbfc6-2e37-42b6-8388-c29b3861a08d' + } } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { - // Get note - const note = await Note.findOne({ - _id: ps.noteId +export default define(meta, async (ps, user) => { + const note = await getValiedNote(ps.noteId).catch(e => { + if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw e; }); - if (note === null) { - return rej('note not found'); - } - - // Serialize - res(await pack(note, user, { + return await pack(note, user, { detail: true - })); -})); + }); +}); diff --git a/src/server/api/endpoints/notes/state.ts b/src/server/api/endpoints/notes/state.ts index 7519241e0..07e35480f 100644 --- a/src/server/api/endpoints/notes/state.ts +++ b/src/server/api/endpoints/notes/state.ts @@ -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) => { const [favorite, watching] = await Promise.all([ Favorite.count({ userId: user._id, @@ -42,8 +42,8 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }) ]); - res({ + return { isFavorited: favorite !== 0, isWatching: watching !== 0 - }); -})); + }; +}); diff --git a/src/server/api/endpoints/notes/timeline.ts b/src/server/api/endpoints/notes/timeline.ts index aff3ec8cb..cb6900a10 100644 --- a/src/server/api/endpoints/notes/timeline.ts +++ b/src/server/api/endpoints/notes/timeline.ts @@ -4,7 +4,6 @@ import Note from '../../../../models/note'; import { getFriends } from '../../common/get-friends'; import { packMany } from '../../../../models/note'; import define from '../../define'; -import { countIf } from '../../../../prelude/array'; import activeUsersChart from '../../../../services/chart/active-users'; import { getHideUserIds } from '../../common/get-hide-users'; @@ -95,13 +94,7 @@ export const meta = { } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { - // Check if only one of sinceId, untilId, sinceDate, untilDate specified - if (countIf(x => x != null, [ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate]) > 1) { - rej('only one of sinceId, untilId, sinceDate, untilDate can be specified'); - return; - } - +export default define(meta, async (ps, user) => { const [followings, hideUserIds] = await Promise.all([ // フォローを取得 // Fetch following @@ -255,15 +248,12 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { } //#endregion - // Issue query - const timeline = await Note - .find(query, { - limit: ps.limit, - sort: sort - }); - - // Serialize - res(await packMany(timeline, user)); + const timeline = await Note.find(query, { + limit: ps.limit, + sort: sort + }); activeUsersChart.update(user); -})); + + return await packMany(timeline, user); +}); diff --git a/src/server/api/endpoints/notes/user-list-timeline.ts b/src/server/api/endpoints/notes/user-list-timeline.ts index eaaa80c3a..1969b924a 100644 --- a/src/server/api/endpoints/notes/user-list-timeline.ts +++ b/src/server/api/endpoints/notes/user-list-timeline.ts @@ -6,6 +6,7 @@ import UserList from '../../../../models/user-list'; import define from '../../define'; import { getFriends } from '../../common/get-friends'; import { getHideUserIds } from '../../common/get-hide-users'; +import { ApiError } from '../../error'; export const meta = { desc: { @@ -99,10 +100,18 @@ export const meta = { 'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します (このパラメータは廃止予定です。代わりに withFiles を使ってください。)' } }, + }, + + errors: { + noSuchList: { + message: 'No such list.', + code: 'NO_SUCH_LIST', + id: '8fb1fbd5-e476-4c37-9fb0-43d55b63a2ff' + } } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { const [list, followings, hideUserIds] = await Promise.all([ // リストを取得 // Fetch the list @@ -120,13 +129,11 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { ]); if (list == null) { - rej('list not found'); - return; + throw new ApiError(meta.errors.noSuchList); } if (list.userIds.length == 0) { - res([]); - return; + return []; } //#region Construct query @@ -274,13 +281,10 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { } //#endregion - // Issue query - const timeline = await Note - .find(query, { - limit: ps.limit, - sort: sort - }); + const timeline = await Note.find(query, { + limit: ps.limit, + sort: sort + }); - // Serialize - res(await packMany(timeline, user)); -})); + return await packMany(timeline, user); +}); diff --git a/src/server/api/endpoints/notes/watching/create.ts b/src/server/api/endpoints/notes/watching/create.ts index 5efe69a3b..ebba211fc 100644 --- a/src/server/api/endpoints/notes/watching/create.ts +++ b/src/server/api/endpoints/notes/watching/create.ts @@ -1,8 +1,9 @@ import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id'; -import Note from '../../../../../models/note'; import define from '../../../define'; import watch from '../../../../../services/note/watch'; +import { getValiedNote } from '../../../common/getters'; +import { ApiError } from '../../../error'; export const meta = { stability: 'stable', @@ -25,21 +26,22 @@ export const meta = { 'en-US': 'Target note ID.' } } + }, + + errors: { + noSuchNote: { + message: 'No such note.', + code: 'NO_SUCH_NOTE', + id: 'ea0e37a6-90a3-4f58-ba6b-c328ca206fc7' + } } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { - // Get note - const note = await Note.findOne({ - _id: ps.noteId +export default define(meta, async (ps, user) => { + const note = await getValiedNote(ps.noteId).catch(e => { + if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw e; }); - if (note === null) { - return rej('note not found'); - } - await watch(user._id, note); - - // Send response - res(); -})); +}); diff --git a/src/server/api/endpoints/notes/watching/delete.ts b/src/server/api/endpoints/notes/watching/delete.ts index 2d99cc825..63354d4ab 100644 --- a/src/server/api/endpoints/notes/watching/delete.ts +++ b/src/server/api/endpoints/notes/watching/delete.ts @@ -1,8 +1,9 @@ import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id'; -import Note from '../../../../../models/note'; import define from '../../../define'; import unwatch from '../../../../../services/note/unwatch'; +import { getValiedNote } from '../../../common/getters'; +import { ApiError } from '../../../error'; export const meta = { stability: 'stable', @@ -25,21 +26,22 @@ export const meta = { 'en-US': 'Target note ID.' } } + }, + + errors: { + noSuchNote: { + message: 'No such note.', + code: 'NO_SUCH_NOTE', + id: '09b3695c-f72c-4731-a428-7cff825fc82e' + } } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { - // Get note - const note = await Note.findOne({ - _id: ps.noteId +export default define(meta, async (ps, user) => { + const note = await getValiedNote(ps.noteId).catch(e => { + if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw e; }); - if (note === null) { - return rej('note not found'); - } - await unwatch(user._id, note); - - // Send response - res(); -})); +}); diff --git a/src/server/api/endpoints/notifications/mark_all_as_read.ts b/src/server/api/endpoints/notifications/mark_all_as_read.ts index de5a12707..91e10b46e 100644 --- a/src/server/api/endpoints/notifications/mark_all_as_read.ts +++ b/src/server/api/endpoints/notifications/mark_all_as_read.ts @@ -14,21 +14,18 @@ export const meta = { kind: 'notification-write' }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { // Update documents await Notification.update({ notifieeId: user._id, isRead: false }, { - $set: { - isRead: true - } - }, { - multi: true - }); - - // Response - res(); + $set: { + isRead: true + } + }, { + multi: true + }); // Update flag User.update({ _id: user._id }, { @@ -39,4 +36,4 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { // 全ての通知を読みましたよというイベントを発行 publishMainStream(user._id, 'readAllNotifications'); -})); +}); diff --git a/src/server/api/endpoints/stats.ts b/src/server/api/endpoints/stats.ts index bb5b1a6ad..43d7c4495 100644 --- a/src/server/api/endpoints/stats.ts +++ b/src/server/api/endpoints/stats.ts @@ -14,7 +14,7 @@ export const meta = { } }; -export default define(meta, () => new Promise(async (res, rej) => { +export default define(meta, async () => { const instance = await fetchMeta(); const stats: any = instance.stats; @@ -26,5 +26,5 @@ export default define(meta, () => new Promise(async (res, rej) => { const federationStats = await federationChart.getChart('hour', 1); stats.instances = federationStats.instance.total[0]; - res(stats); -})); + return stats; +}); diff --git a/src/server/api/endpoints/sw/register.ts b/src/server/api/endpoints/sw/register.ts index 095c1b765..870c13555 100644 --- a/src/server/api/endpoints/sw/register.ts +++ b/src/server/api/endpoints/sw/register.ts @@ -21,7 +21,7 @@ export const meta = { } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { // if already subscribed const exist = await Subscription.findOne({ userId: user._id, @@ -34,10 +34,10 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { const instance = await fetchMeta(); if (exist != null) { - return res({ + return { state: 'already-subscribed', key: instance.swPublicKey - }); + }; } await Subscription.insert({ @@ -47,8 +47,8 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { publickey: ps.publickey }); - res({ + return { state: 'subscribed', key: instance.swPublicKey - }); -})); + }; +}); diff --git a/src/server/api/endpoints/username/available.ts b/src/server/api/endpoints/username/available.ts index a5f0b5943..a651bc4d5 100644 --- a/src/server/api/endpoints/username/available.ts +++ b/src/server/api/endpoints/username/available.ts @@ -13,18 +13,16 @@ export const meta = { } }; -export default define(meta, (ps) => new Promise(async (res, rej) => { +export default define(meta, async (ps) => { // Get exist - const exist = await User - .count({ - host: null, - usernameLower: ps.username.toLowerCase() - }, { - limit: 1 - }); - - // Reply - res({ - available: exist === 0 + const exist = await User.count({ + host: null, + usernameLower: ps.username.toLowerCase() + }, { + limit: 1 }); -})); + + return { + available: exist === 0 + }; +}); diff --git a/src/server/api/endpoints/users.ts b/src/server/api/endpoints/users.ts index fd8cc6413..f7e7a1001 100644 --- a/src/server/api/endpoints/users.ts +++ b/src/server/api/endpoints/users.ts @@ -86,7 +86,7 @@ const sort: any = { // < https://github.com/Microsoft/TypeScript/issues/1863 [fallback]: { _id: -1 } }; -export default define(meta, (ps, me) => new Promise(async (res, rej) => { +export default define(meta, async (ps, me) => { const hideUserIds = await getHideUserIds(me); const users = await User @@ -102,5 +102,5 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { skip: ps.offset }); - res(await Promise.all(users.map(user => pack(user, me, { detail: true })))); -})); + return await Promise.all(users.map(user => pack(user, me, { detail: true }))); +}); diff --git a/src/server/api/endpoints/users/followers.ts b/src/server/api/endpoints/users/followers.ts index 2f7f1af6a..cb5b04762 100644 --- a/src/server/api/endpoints/users/followers.ts +++ b/src/server/api/endpoints/users/followers.ts @@ -5,6 +5,7 @@ import Following from '../../../../models/following'; import { pack } from '../../../../models/user'; import { getFriendIds } from '../../common/get-friends'; import define from '../../define'; +import { ApiError } from '../../error'; export const meta = { desc: { @@ -47,10 +48,18 @@ export const meta = { validator: $.optional.bool, default: false, } + }, + + errors: { + noSuchUser: { + message: 'No such user.', + code: 'NO_SUCH_USER', + id: '27fa5435-88ab-43de-9360-387de88727cd' + } } }; -export default define(meta, (ps, me) => new Promise(async (res, rej) => { +export default define(meta, async (ps, me) => { const q: any = ps.userId != null ? { _id: ps.userId } : { usernameLower: ps.username.toLowerCase(), host: ps.host }; @@ -58,10 +67,9 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { const user = await User.findOne(q); if (user === null) { - return rej('user not found'); + throw new ApiError(meta.errors.noSuchUser); } - // Construct query const query = { followeeId: user._id } as any; @@ -98,8 +106,8 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { const users = await Promise.all(following.map(f => pack(f.followerId, me, { detail: true }))); - res({ + return { users: users, next: inStock ? following[following.length - 1]._id : null, - }); -})); + }; +}); diff --git a/src/server/api/endpoints/users/following.ts b/src/server/api/endpoints/users/following.ts index 1485a63f2..04abe5f4b 100644 --- a/src/server/api/endpoints/users/following.ts +++ b/src/server/api/endpoints/users/following.ts @@ -5,6 +5,7 @@ import Following from '../../../../models/following'; import { pack } from '../../../../models/user'; import { getFriendIds } from '../../common/get-friends'; import define from '../../define'; +import { ApiError } from '../../error'; export const meta = { desc: { @@ -47,10 +48,18 @@ export const meta = { validator: $.optional.bool, default: false, } + }, + + errors: { + noSuchUser: { + message: 'No such user.', + code: 'NO_SUCH_USER', + id: '63e4aba4-4156-4e53-be25-c9559e42d71b' + } } }; -export default define(meta, (ps, me) => new Promise(async (res, rej) => { +export default define(meta, async (ps, me) => { const q: any = ps.userId != null ? { _id: ps.userId } : { usernameLower: ps.username.toLowerCase(), host: ps.host }; @@ -58,10 +67,9 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { const user = await User.findOne(q); if (user === null) { - return rej('user not found'); + throw new ApiError(meta.errors.noSuchUser); } - // Construct query const query = { followerId: user._id } as any; @@ -98,8 +106,8 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { const users = await Promise.all(following.map(f => pack(f.followeeId, me, { detail: true }))); - res({ + return { users: users, next: inStock ? following[following.length - 1]._id : null, - }); -})); + }; +}); diff --git a/src/server/api/endpoints/users/get_frequently_replied_users.ts b/src/server/api/endpoints/users/get_frequently_replied_users.ts index e897fe8db..b20d1ef69 100644 --- a/src/server/api/endpoints/users/get_frequently_replied_users.ts +++ b/src/server/api/endpoints/users/get_frequently_replied_users.ts @@ -5,6 +5,7 @@ import User, { pack } from '../../../../models/user'; import define from '../../define'; import { maximum } from '../../../../prelude/array'; import { getHideUserIds } from '../../common/get-hide-users'; +import { ApiError } from '../../error'; export const meta = { requireCredential: false, @@ -23,10 +24,18 @@ export const meta = { validator: $.optional.num.range(1, 100), default: 10 }, + }, + + errors: { + noSuchUser: { + message: 'No such user.', + code: 'NO_SUCH_USER', + id: 'e6965129-7b2a-40a4-bae2-cd84cd434822' + } } }; -export default define(meta, (ps, me) => new Promise(async (res, rej) => { +export default define(meta, async (ps, me) => { // Lookup user const user = await User.findOne({ _id: ps.userId @@ -37,7 +46,7 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { }); if (user === null) { - return rej('user not found'); + throw new ApiError(meta.errors.noSuchUser); } // Fetch recent notes @@ -60,7 +69,7 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { // 投稿が少なかったら中断 if (recentNotes.length === 0) { - return res([]); + return []; } const hideUserIds = await getHideUserIds(me); @@ -106,5 +115,5 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { weight: repliedUsers[user] / peak }))); - res(repliesObj); -})); + return repliesObj; +}); diff --git a/src/server/api/endpoints/users/lists/create.ts b/src/server/api/endpoints/users/lists/create.ts index d93cf13d9..a3d91b6c2 100644 --- a/src/server/api/endpoints/users/lists/create.ts +++ b/src/server/api/endpoints/users/lists/create.ts @@ -19,8 +19,7 @@ export const meta = { } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { - // insert +export default define(meta, async (ps, user) => { const userList = await UserList.insert({ createdAt: new Date(), userId: user._id, @@ -28,6 +27,5 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { userIds: [] }); - // Response - res(await pack(userList)); -})); + return await pack(userList); +}); diff --git a/src/server/api/endpoints/users/lists/delete.ts b/src/server/api/endpoints/users/lists/delete.ts index 8d297198c..6aaf4701d 100644 --- a/src/server/api/endpoints/users/lists/delete.ts +++ b/src/server/api/endpoints/users/lists/delete.ts @@ -2,6 +2,7 @@ import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id'; import UserList from '../../../../../models/user-list'; import define from '../../../define'; +import { ApiError } from '../../../error'; export const meta = { desc: { @@ -22,22 +23,28 @@ export const meta = { 'en-US': 'ID of target user list' } } + }, + + errors: { + noSuchList: { + message: 'No such list.', + code: 'NO_SUCH_LIST', + id: '78436795-db79-42f5-b1e2-55ea2cf19166' + } } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { const userList = await UserList.findOne({ _id: ps.listId, userId: user._id }); if (userList == null) { - return rej('list not found'); + throw new ApiError(meta.errors.noSuchList); } await UserList.remove({ _id: userList._id }); - - res(); -})); +}); diff --git a/src/server/api/endpoints/users/lists/list.ts b/src/server/api/endpoints/users/lists/list.ts index 8f9775e19..5bd662878 100644 --- a/src/server/api/endpoints/users/lists/list.ts +++ b/src/server/api/endpoints/users/lists/list.ts @@ -11,11 +11,10 @@ export const meta = { kind: 'account-read' }; -export default define(meta, (ps, me) => new Promise(async (res, rej) => { - // Fetch lists +export default define(meta, async (ps, me) => { const userLists = await UserList.find({ userId: me._id, }); - res(await Promise.all(userLists.map(x => pack(x)))); -})); + return await Promise.all(userLists.map(x => pack(x))); +}); diff --git a/src/server/api/endpoints/users/lists/pull.ts b/src/server/api/endpoints/users/lists/pull.ts index 86eaa1dd8..416c1b4b2 100644 --- a/src/server/api/endpoints/users/lists/pull.ts +++ b/src/server/api/endpoints/users/lists/pull.ts @@ -4,6 +4,7 @@ import UserList from '../../../../../models/user-list'; import User, { pack as packUser } from '../../../../../models/user'; import { publishUserListStream } from '../../../../../services/stream'; import define from '../../../define'; +import { ApiError } from '../../../error'; export const meta = { desc: { @@ -29,10 +30,24 @@ export const meta = { 'en-US': 'Target user ID' } }, + }, + + errors: { + noSuchList: { + message: 'No such list.', + code: 'NO_SUCH_LIST', + id: '7f44670e-ab16-43b8-b4c1-ccd2ee89cc02' + }, + + noSuchUser: { + message: 'No such user.', + code: 'NO_SUCH_USER', + id: '588e7f72-c744-4a61-b180-d354e912bda2' + } } }; -export default define(meta, (ps, me) => new Promise(async (res, rej) => { +export default define(meta, async (ps, me) => { // Fetch the list const userList = await UserList.findOne({ _id: ps.listId, @@ -40,7 +55,7 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { }); if (userList == null) { - return rej('list not found'); + throw new ApiError(meta.errors.noSuchList); } // Fetch the user @@ -49,7 +64,7 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { }); if (user == null) { - return rej('user not found'); + throw new ApiError(meta.errors.noSuchUser); } // Pull the user @@ -59,7 +74,5 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { } }); - res(); - publishUserListStream(userList._id, 'userRemoved', await packUser(user)); -})); +}); diff --git a/src/server/api/endpoints/users/lists/push.ts b/src/server/api/endpoints/users/lists/push.ts index 6e2dbed51..90dd2aa04 100644 --- a/src/server/api/endpoints/users/lists/push.ts +++ b/src/server/api/endpoints/users/lists/push.ts @@ -7,6 +7,7 @@ import { renderActivity } from '../../../../../remote/activitypub/renderer'; import renderFollow from '../../../../../remote/activitypub/renderer/follow'; import { deliver } from '../../../../../queue'; import define from '../../../define'; +import { ApiError } from '../../../error'; export const meta = { desc: { @@ -32,10 +33,30 @@ export const meta = { 'en-US': 'Target user ID' } }, + }, + + errors: { + noSuchList: { + message: 'No such list.', + code: 'NO_SUCH_LIST', + id: '2214501d-ac96-4049-b717-91e42272a711' + }, + + noSuchUser: { + message: 'No such user.', + code: 'NO_SUCH_USER', + id: 'a89abd3d-f0bc-4cce-beb1-2f446f4f1e6a' + }, + + alreadyAdded: { + message: 'That user has already been added to that list.', + code: 'ALREADY_ADDED', + id: '1de7c884-1595-49e9-857e-61f12f4d4fc5' + } } }; -export default define(meta, (ps, me) => new Promise(async (res, rej) => { +export default define(meta, async (ps, me) => { // Fetch the list const userList = await UserList.findOne({ _id: ps.listId, @@ -43,7 +64,7 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { }); if (userList == null) { - return rej('list not found'); + throw new ApiError(meta.errors.noSuchList); } // Fetch the user @@ -52,11 +73,11 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { }); if (user == null) { - return rej('user not found'); + throw new ApiError(meta.errors.noSuchUser); } if (userList.userIds.map(id => id.toHexString()).includes(user._id.toHexString())) { - return rej('the user already added'); + throw new ApiError(meta.errors.alreadyAdded); } // Push the user @@ -66,8 +87,6 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { } }); - res(); - publishUserListStream(userList._id, 'userAdded', await packUser(user)); // このインスタンス内にこのリモートユーザーをフォローしているユーザーがいなくても投稿を受け取るためにダミーのユーザーがフォローしたということにする @@ -76,4 +95,4 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { const content = renderActivity(renderFollow(proxy, user)); deliver(proxy, content, user.inbox); } -})); +}); diff --git a/src/server/api/endpoints/users/lists/show.ts b/src/server/api/endpoints/users/lists/show.ts index c508659a1..3bd8ce657 100644 --- a/src/server/api/endpoints/users/lists/show.ts +++ b/src/server/api/endpoints/users/lists/show.ts @@ -2,6 +2,7 @@ import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id'; import UserList, { pack } from '../../../../../models/user-list'; import define from '../../../define'; +import { ApiError } from '../../../error'; export const meta = { desc: { @@ -18,10 +19,18 @@ export const meta = { validator: $.type(ID), transform: transform, }, + }, + + errors: { + noSuchList: { + message: 'No such list.', + code: 'NO_SUCH_LIST', + id: '7bc05c21-1d7a-41ae-88f1-66820f4dc686' + }, } }; -export default define(meta, (ps, me) => new Promise(async (res, rej) => { +export default define(meta, async (ps, me) => { // Fetch the list const userList = await UserList.findOne({ _id: ps.listId, @@ -29,8 +38,8 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { }); if (userList == null) { - return rej('list not found'); + throw new ApiError(meta.errors.noSuchList); } - res(await pack(userList)); -})); + return await pack(userList); +}); diff --git a/src/server/api/endpoints/users/lists/update.ts b/src/server/api/endpoints/users/lists/update.ts index 79a7da565..842ae189e 100644 --- a/src/server/api/endpoints/users/lists/update.ts +++ b/src/server/api/endpoints/users/lists/update.ts @@ -2,6 +2,7 @@ import $ from 'cafy'; import ID, { transform } from '../../../../../misc/cafy-id'; import UserList, { pack } from '../../../../../models/user-list'; import define from '../../../define'; +import { ApiError } from '../../../error'; export const meta = { desc: { @@ -30,10 +31,18 @@ export const meta = { 'en-US': 'name of this user list' } } + }, + + errors: { + noSuchList: { + message: 'No such list.', + code: 'NO_SUCH_LIST', + id: '796666fe-3dff-4d39-becb-8a5932c1d5b7' + }, } }; -export default define(meta, (ps, user) => new Promise(async (res, rej) => { +export default define(meta, async (ps, user) => { // Fetch the list const userList = await UserList.findOne({ _id: ps.listId, @@ -41,16 +50,14 @@ export default define(meta, (ps, user) => new Promise(async (res, rej) => { }); if (userList == null) { - return rej('list not found'); + throw new ApiError(meta.errors.noSuchList); } - // update await UserList.update({ _id: userList._id }, { $set: { title: ps.title } }); - // Response - res(await pack(userList._id)); -})); + return await pack(userList._id); +}); diff --git a/src/server/api/endpoints/users/notes.ts b/src/server/api/endpoints/users/notes.ts index 5c776e48e..98d67e865 100644 --- a/src/server/api/endpoints/users/notes.ts +++ b/src/server/api/endpoints/users/notes.ts @@ -1,11 +1,10 @@ import $ from 'cafy'; import ID, { transform } from '../../../../misc/cafy-id'; -import getHostLower from '../../common/get-host-lower'; import Note, { packMany } from '../../../../models/note'; import User from '../../../../models/user'; import define from '../../define'; -import { countIf } from '../../../../prelude/array'; import Following from '../../../../models/following'; +import { ApiError } from '../../error'; export const meta = { desc: { @@ -14,7 +13,7 @@ export const meta = { params: { userId: { - validator: $.optional.type(ID), + validator: $.type(ID), transform: transform, desc: { 'ja-JP': '対象のユーザーのID', @@ -22,17 +21,6 @@ export const meta = { } }, - username: { - validator: $.optional.str, - desc: { - 'ja-JP': 'ユーザー名' - } - }, - - host: { - validator: $.optional.nullable.str, - }, - includeReplies: { validator: $.optional.bool, default: true, @@ -134,32 +122,27 @@ export const meta = { 'ja-JP': 'true にすると、NSFW指定されたファイルを除外します(fileTypeが指定されている場合のみ有効)' } }, + }, + + errors: { + noSuchUser: { + message: 'No such user.', + code: 'NO_SUCH_USER', + id: '27e494ba-2ac2-48e8-893b-10d4d8c2387b' + } } }; -export default define(meta, (ps, me) => new Promise(async (res, rej) => { - if (ps.userId === undefined && ps.username === undefined) { - return rej('userId or username is required'); - } - - // Check if only one of sinceId, untilId, sinceDate, untilDate specified - if (countIf(x => x != null, [ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate]) > 1) { - throw 'only one of sinceId, untilId, sinceDate, untilDate can be specified'; - } - - const q = ps.userId != null - ? { _id: ps.userId } - : { usernameLower: ps.username.toLowerCase(), host: getHostLower(ps.host) } ; - +export default define(meta, async (ps, me) => { // Lookup user - const user = await User.findOne(q, { + const user = await User.findOne({ _id: ps.userId }, { fields: { _id: true } }); if (user === null) { - return rej('user not found'); + throw new ApiError(meta.errors.noSuchUser); } const isFollowing = me == null ? false : ((await Following.findOne({ @@ -259,13 +242,10 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { } //#endregion - // Issue query - const notes = await Note - .find(query, { - limit: ps.limit, - sort: sort - }); + const notes = await Note.find(query, { + limit: ps.limit, + sort: sort + }); - // Serialize - res(await packMany(notes, me)); -})); + return await packMany(notes, me); +}); diff --git a/src/server/api/endpoints/users/recommendation.ts b/src/server/api/endpoints/users/recommendation.ts index e9f010771..dc37bc56b 100644 --- a/src/server/api/endpoints/users/recommendation.ts +++ b/src/server/api/endpoints/users/recommendation.ts @@ -32,7 +32,7 @@ export const meta = { } }; -export default define(meta, (ps, me) => new Promise(async (res, rej) => { +export default define(meta, async (ps, me) => { const instance = await fetchMeta(); if (instance.enableExternalUserRecommendation) { @@ -48,7 +48,7 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { .replace('{{limit}}', limit.toString()) .replace('{{offset}}', offset.toString()); - request({ + const users = await request({ url: url, proxy: config.proxy, timeout: timeout, @@ -56,37 +56,36 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { followRedirect: true, followAllRedirects: true }) - .then(body => convertUsers(body, me)) - .then(packed => res(packed)) - .catch(e => rej(e)); + .then(body => convertUsers(body, me)); + + return users; } else { // ID list of the user itself and other users who the user follows const followingIds = await getFriendIds(me._id); - // 隠すユーザーを取得 - const hideUserIds = await getHideUserIds(me); + // 隠すユーザーを取得 + const hideUserIds = await getHideUserIds(me); - const users = await User - .find({ - _id: { - $nin: followingIds.concat(hideUserIds) - }, - isLocked: { $ne: true }, - updatedAt: { - $gte: new Date(Date.now() - ms('7days')) - }, - host: null - }, { - limit: ps.limit, - skip: ps.offset, - sort: { - followersCount: -1 - } - }); + const users = await User.find({ + _id: { + $nin: followingIds.concat(hideUserIds) + }, + isLocked: { $ne: true }, + updatedAt: { + $gte: new Date(Date.now() - ms('7days')) + }, + host: null + }, { + limit: ps.limit, + skip: ps.offset, + sort: { + followersCount: -1 + } + }); - res(await Promise.all(users.map(user => pack(user, me, { detail: true })))); + return await Promise.all(users.map(user => pack(user, me, { detail: true }))); } -})); +}); type IRecommendUser = { name: string; diff --git a/src/server/api/endpoints/users/relation.ts b/src/server/api/endpoints/users/relation.ts index 43d5e7064..5b7b4d8e4 100644 --- a/src/server/api/endpoints/users/relation.ts +++ b/src/server/api/endpoints/users/relation.ts @@ -21,10 +21,10 @@ export const meta = { } }; -export default define(meta, (ps, me) => new Promise(async (res, rej) => { +export default define(meta, async (ps, me) => { const ids = Array.isArray(ps.userId) ? ps.userId : [ps.userId]; const relations = await Promise.all(ids.map(id => getRelation(me._id, id))); - res(Array.isArray(ps.userId) ? relations : relations[0]); -})); + return Array.isArray(ps.userId) ? relations : relations[0]; +}); diff --git a/src/server/api/endpoints/users/report-abuse.ts b/src/server/api/endpoints/users/report-abuse.ts index c592e5cfd..32a239349 100644 --- a/src/server/api/endpoints/users/report-abuse.ts +++ b/src/server/api/endpoints/users/report-abuse.ts @@ -4,6 +4,7 @@ import define from '../../define'; import User from '../../../../models/user'; import AbuseUserReport from '../../../../models/abuse-user-report'; import { publishAdminStream } from '../../../../services/stream'; +import { ApiError } from '../../error'; export const meta = { desc: { @@ -28,25 +29,45 @@ export const meta = { 'ja-JP': '迷惑行為の詳細' } }, + }, + + errors: { + noSuchUser: { + message: 'No such user.', + code: 'NO_SUCH_USER', + id: '1acefcb5-0959-43fd-9685-b48305736cb5' + }, + + cannotReportYourself: { + message: 'Cannot report yourself.', + code: 'CANNOT_REPORT_YOURSELF', + id: '1e13149e-b1e8-43cf-902e-c01dbfcb202f' + }, + + cannotReportAdmin: { + message: 'Cannot report the admin.', + code: 'CANNOT_REPORT_THE_ADMIN', + id: '35e166f5-05fb-4f87-a2d5-adb42676d48f' + } } }; -export default define(meta, (ps, me) => new Promise(async (res, rej) => { +export default define(meta, async (ps, me) => { // Lookup user const user = await User.findOne({ _id: ps.userId }); if (user === null) { - return rej('user not found'); + throw new ApiError(meta.errors.noSuchUser); } if (user._id.equals(me._id)) { - return rej('cannot report yourself'); + throw new ApiError(meta.errors.cannotReportYourself); } if (user.isAdmin) { - return rej('cannot report admin'); + throw new ApiError(meta.errors.cannotReportAdmin); } const report = await AbuseUserReport.insert({ @@ -65,6 +86,7 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { isModerator: true }] }); + for (const moderator of moderators) { publishAdminStream(moderator._id, 'newAbuseUserReport', { id: report._id, @@ -74,6 +96,4 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { }); } }, 1); - - res(); -})); +}); diff --git a/src/server/api/endpoints/users/search.ts b/src/server/api/endpoints/users/search.ts index 5dc1e4aea..6fd390f28 100644 --- a/src/server/api/endpoints/users/search.ts +++ b/src/server/api/endpoints/users/search.ts @@ -52,7 +52,7 @@ export const meta = { }, }; -export default define(meta, (ps, me) => new Promise(async (res, rej) => { +export default define(meta, async (ps, me) => { const isUsername = validateUsername(ps.query.replace('@', ''), !ps.localOnly); let users: IUser[] = []; @@ -80,5 +80,5 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { } } - res(await Promise.all(users.map(user => pack(user, me, { detail: ps.detail })))); -})); + return await Promise.all(users.map(user => pack(user, me, { detail: ps.detail }))); +}); diff --git a/src/server/api/endpoints/users/show.ts b/src/server/api/endpoints/users/show.ts index 025598b8f..4f870b8b9 100644 --- a/src/server/api/endpoints/users/show.ts +++ b/src/server/api/endpoints/users/show.ts @@ -4,6 +4,7 @@ import User, { pack, isRemoteUser } from '../../../../models/user'; import resolveRemoteUser from '../../../../remote/resolve-user'; import define from '../../define'; import { apiLogger } from '../../logger'; +import { ApiError } from '../../error'; const cursorOption = { fields: { data: false } }; @@ -39,10 +40,25 @@ export const meta = { host: { validator: $.optional.nullable.str } + }, + + errors: { + failedToResolveRemoteUser: { + message: 'Failed to resolve remote user.', + code: 'FAILED_TO_RESOLVE_REMOTE_USER', + id: 'ef7b9be4-9cba-4e6f-ab41-90ed171c7d3c', + kind: 'server' as 'server' + }, + + noSuchUser: { + message: 'No such user.', + code: 'NO_SUCH_USER', + id: '4362f8dc-731f-4ad8-a694-be5a88922a24' + }, } }; -export default define(meta, (ps, me) => new Promise(async (res, rej) => { +export default define(meta, async (ps, me) => { let user; if (ps.userIds) { @@ -52,18 +68,16 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { } }); - res(await Promise.all(users.map(u => pack(u, me, { + return await Promise.all(users.map(u => pack(u, me, { detail: true - })))); + }))); } else { // Lookup user if (typeof ps.host === 'string') { - try { - user = await resolveRemoteUser(ps.username, ps.host, cursorOption); - } catch (e) { + user = await resolveRemoteUser(ps.username, ps.host, cursorOption).catch(e => { apiLogger.warn(`failed to resolve remote user: ${e}`); - return rej('failed to resolve remote user'); - } + throw new ApiError(meta.errors.failedToResolveRemoteUser); + }); } else { const q: any = ps.userId != null ? { _id: ps.userId } @@ -73,18 +87,18 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => { } if (user === null) { - return rej('user not found'); + throw new ApiError(meta.errors.noSuchUser); } - // Send response - res(await pack(user, me, { - detail: true - })); - + // ユーザー情報更新 if (isRemoteUser(user)) { if (user.lastFetchedAt == null || Date.now() - user.lastFetchedAt.getTime() > 1000 * 60 * 60 * 24) { resolveRemoteUser(ps.username, ps.host, { }, true); } } + + return await pack(user, me, { + detail: true + }); } -})); +}); diff --git a/src/server/api/error.ts b/src/server/api/error.ts new file mode 100644 index 000000000..6b5c44cac --- /dev/null +++ b/src/server/api/error.ts @@ -0,0 +1,23 @@ +export class ApiError extends Error { + public message: string; + public code: string; + public id: string; + public kind: string; + public info?: any; + + constructor(e?: { message: string, code: string, id: string, kind?: 'client' | 'server' }, info?: any) { + if (e == null) e = { + message: 'Internal error occurred. Please contact us if the error persists.', + code: 'INTERNAL_ERROR', + id: '5d37dbcb-891e-41ca-a3d6-e690c97775ac', + kind: 'server' + }; + + super(e.message); + this.message = e.message; + this.code = e.code; + this.id = e.id; + this.kind = e.kind || 'client'; + this.info = info; + } +} diff --git a/src/services/following/create.ts b/src/services/following/create.ts index 5a92e4103..38f86fbf7 100644 --- a/src/services/following/create.ts +++ b/src/services/following/create.ts @@ -15,6 +15,7 @@ import Instance from '../../models/instance'; import instanceChart from '../../services/chart/instance'; import Logger from '../../misc/logger'; import FollowRequest from '../../models/follow-request'; +import { IdentifiableError } from '../../misc/identifiable-error'; const logger = new Logger('following/create'); @@ -142,8 +143,8 @@ export default async function(follower: IUser, followee: IUser, requestId?: stri }); } else { // それ以外は単純に例外 - if (blocking != null) throw new Error('blocking'); - if (blocked != null) throw new Error('blocked'); + if (blocking != null) throw new IdentifiableError('710e8fb0-b8c3-4922-be49-d5d93d8e6a6e', 'blocking'); + if (blocked != null) throw new IdentifiableError('3338392a-f764-498d-8855-db939dcf8c48', 'blocked'); } // フォロー対象が鍵アカウントである or diff --git a/src/services/following/requests/cancel.ts b/src/services/following/requests/cancel.ts index 375c14d5e..af4cca85f 100644 --- a/src/services/following/requests/cancel.ts +++ b/src/services/following/requests/cancel.ts @@ -5,6 +5,7 @@ import renderFollow from '../../../remote/activitypub/renderer/follow'; import renderUndo from '../../../remote/activitypub/renderer/undo'; import { deliver } from '../../../queue'; import { publishMainStream } from '../../stream'; +import { IdentifiableError } from '../../../misc/identifiable-error'; export default async function(followee: IUser, follower: IUser) { if (isRemoteUser(followee)) { @@ -18,7 +19,7 @@ export default async function(followee: IUser, follower: IUser) { }); if (request == null) { - throw 'request not found'; + throw new IdentifiableError('17447091-ce07-46dd-b331-c1fd4f15b1e7', 'request not found'); } await FollowRequest.remove({ diff --git a/src/services/i/pin.ts b/src/services/i/pin.ts index c48c1838a..4d0ae3c14 100644 --- a/src/services/i/pin.ts +++ b/src/services/i/pin.ts @@ -7,6 +7,7 @@ import renderAdd from '../../remote/activitypub/renderer/add'; import renderRemove from '../../remote/activitypub/renderer/remove'; import { renderActivity } from '../../remote/activitypub/renderer'; import { deliver } from '../../queue'; +import { IdentifiableError } from '../../misc/identifiable-error'; /** * 指定した投稿をピン留めします @@ -21,7 +22,7 @@ export async function addPinned(user: IUser, noteId: mongo.ObjectID) { }); if (note === null) { - throw new Error('note not found'); + throw new IdentifiableError('70c4e51f-5bea-449c-a030-53bee3cce202', 'No such note.'); } let pinnedNoteIds = user.pinnedNoteIds || []; @@ -35,11 +36,11 @@ export async function addPinned(user: IUser, noteId: mongo.ObjectID) { //#endregion if (pinnedNoteIds.length >= 5) { - throw new Error('cannot pin more notes'); + throw new IdentifiableError('15a018eb-58e5-4da1-93be-330fcc5e4e1a', 'You can not pin notes any more.'); } if (pinnedNoteIds.some(id => id.equals(note._id))) { - throw new Error('already exists'); + throw new IdentifiableError('23f0cf4e-59a3-4276-a91d-61a5891c1514', 'That note has already been pinned.'); } pinnedNoteIds.unshift(note._id); @@ -69,7 +70,7 @@ export async function removePinned(user: IUser, noteId: mongo.ObjectID) { }); if (note === null) { - throw new Error('note not found'); + throw new IdentifiableError('b302d4cf-c050-400a-bbb3-be208681f40c', 'No such note.'); } const pinnedNoteIds = (user.pinnedNoteIds || []).filter(id => !id.equals(note._id)); diff --git a/src/services/note/polls/vote.ts b/src/services/note/polls/vote.ts index 115b071b4..e154f33b7 100644 --- a/src/services/note/polls/vote.ts +++ b/src/services/note/polls/vote.ts @@ -27,7 +27,6 @@ export default (user: IUser, note: INote, choice: number) => new Promise(async ( choice: choice }); - // Send response res(); const inc: any = {}; diff --git a/src/services/note/reaction/create.ts b/src/services/note/reaction/create.ts index cf7292764..5897df2c9 100644 --- a/src/services/note/reaction/create.ts +++ b/src/services/note/reaction/create.ts @@ -9,31 +9,28 @@ import renderLike from '../../../remote/activitypub/renderer/like'; import { deliver } from '../../../queue'; import { renderActivity } from '../../../remote/activitypub/renderer'; import perUserReactionsChart from '../../../services/chart/per-user-reactions'; +import { IdentifiableError } from '../../../misc/identifiable-error'; -export default async (user: IUser, note: INote, reaction: string) => new Promise(async (res, rej) => { +export default async (user: IUser, note: INote, reaction: string) => { // Myself if (note.userId.equals(user._id)) { - return rej('cannot react to my note'); + throw new IdentifiableError('2d8e7297-1873-4c00-8404-792c68d7bef0', 'cannot react to my note'); } // Create reaction - try { - await NoteReaction.insert({ - createdAt: new Date(), - noteId: note._id, - userId: user._id, - reaction - }); - } catch (e) { + await NoteReaction.insert({ + createdAt: new Date(), + noteId: note._id, + userId: user._id, + reaction + }).catch(e => { // duplicate key error if (e.code === 11000) { - return rej('already reacted'); + throw new IdentifiableError('51c42bb4-931a-456b-bff7-e5a8a70dd298', 'already reacted'); } - return rej('something happened'); - } - - res(); + throw e; + }); // Increment reactions count await Note.update({ _id: note._id }, { @@ -89,4 +86,6 @@ export default async (user: IUser, note: INote, reaction: string) => new Promise deliver(user, content, note._user.inbox); } //#endregion -}); + + return; +}; diff --git a/src/services/note/reaction/delete.ts b/src/services/note/reaction/delete.ts index ee3a9e13d..9735d4ec0 100644 --- a/src/services/note/reaction/delete.ts +++ b/src/services/note/reaction/delete.ts @@ -6,8 +6,9 @@ import renderLike from '../../../remote/activitypub/renderer/like'; import renderUndo from '../../../remote/activitypub/renderer/undo'; import { renderActivity } from '../../../remote/activitypub/renderer'; import { deliver } from '../../../queue'; +import { IdentifiableError } from '../../../misc/identifiable-error'; -export default async (user: IUser, note: INote) => new Promise(async (res, rej) => { +export default async (user: IUser, note: INote) => { // if already unreacted const exist = await Reaction.findOne({ noteId: note._id, @@ -16,7 +17,7 @@ export default async (user: IUser, note: INote) => new Promise(async (res, rej) }); if (exist === null) { - return rej('never reacted'); + throw new IdentifiableError('60527ec9-b4cb-4a88-a6bd-32d3ad26817d', 'not reacted'); } // Delete reaction @@ -24,8 +25,6 @@ export default async (user: IUser, note: INote) => new Promise(async (res, rej) _id: exist._id }); - res(); - const dec: any = {}; dec[`reactionCounts.${exist.reaction}`] = -1; @@ -46,4 +45,6 @@ export default async (user: IUser, note: INote) => new Promise(async (res, rej) deliver(user, content, note._user.inbox); } //#endregion -}); + + return; +};