refactor: Standardize error handling for RESTful APIs

This commit is contained in:
Peng-YM
2022-07-05 17:04:58 +08:00
parent 0e089ef8ce
commit 00c28c6cb8
10 changed files with 123 additions and 68 deletions

View File

@@ -1,7 +1,8 @@
import { deleteByName, findByName, updateByName } from '@/utils/database';
import { COLLECTIONS_KEY } from '@/constants';
import { success } from '@/restful/response';
import { failed, success } from '@/restful/response';
import $ from '@/core/app';
import { RequestInvalidError, ResourceNotFoundError } from '@/restful/errors';
export default function register($app) {
if (!$.read(COLLECTIONS_KEY)) $.write({}, COLLECTIONS_KEY);
@@ -22,10 +23,13 @@ function createCollection(req, res) {
$.info(`正在创建组合订阅:${collection.name}`);
const allCols = $.read(COLLECTIONS_KEY);
if (findByName(allCols, collection.name)) {
res.status(500).json({
status: 'failed',
message: `订阅集${collection.name}已存在!`,
});
failed(
res,
new RequestInvalidError(
'DUPLICATE_KEY',
`Collection ${collection.name} already exists.`,
),
);
}
allCols.push(collection);
$.write(allCols, COLLECTIONS_KEY);
@@ -40,10 +44,14 @@ function getCollection(req, res) {
if (collection) {
success(res, collection);
} else {
res.status(404).json({
status: 'failed',
message: `未找到订阅集:${name}!`,
});
failed(
res,
new ResourceNotFoundError(
`SUBSCRIPTION_NOT_FOUND`,
`Collection ${name} does not exist`,
404,
),
);
}
}
@@ -63,10 +71,14 @@ function updateCollection(req, res) {
$.write(allCols, COLLECTIONS_KEY);
success(res, newCol);
} else {
res.status(500).json({
status: 'failed',
message: `订阅集${name}不存在,无法更新!`,
});
failed(
res,
new ResourceNotFoundError(
'RESOURCE_NOT_FOUND',
`Collection ${name} does not exist!`,
),
404,
);
}
}

View File

@@ -4,6 +4,8 @@ import { findByName } from '@/utils/database';
import { getFlowHeaders } from '@/utils/flow';
import { produceArtifact } from './artifacts';
import $ from '@/core/app';
import { failed } from '@/restful/response';
import { InternalServerError, ResourceNotFoundError } from '@/restful/errors';
export default function register($app) {
$app.get('/download/collection/:name', downloadCollection);
@@ -51,16 +53,25 @@ async function downloadSubscription(req, res) {
`🤔 原因:${JSON.stringify(err)}`,
);
$.error(JSON.stringify(err));
res.status(500).json({
status: 'failed',
message: err,
});
failed(
res,
new InternalServerError(
'INTERNAL_SERVER_ERROR',
`Failed to download subscription: ${name}`,
`Reason: ${JSON.stringify(err)}`,
),
);
}
} else {
$.notify(`🌍 『 𝑺𝒖𝒃-𝑺𝒕𝒐𝒓𝒆 』 下载订阅失败`, `❌ 未找到订阅:${name}`);
res.status(404).json({
status: 'failed',
});
failed(
res,
new ResourceNotFoundError(
'RESOURCE_NOT_FOUND',
`Subscription ${name} does not exist!`,
),
404,
);
}
}
@@ -110,18 +121,27 @@ async function downloadCollection(req, res) {
`❌ 下载组合订阅错误:${name}`,
`🤔 原因:${err}`,
);
res.status(500).json({
status: 'failed',
message: err,
});
failed(
res,
new InternalServerError(
'INTERNAL_SERVER_ERROR',
`Failed to download collection: ${name}`,
`Reason: ${JSON.stringify(err)}`,
),
);
}
} else {
$.notify(
`🌍 『 𝑺𝒖𝒃-𝑺𝒕𝒐𝒓𝒆 』 下载组合订阅失败`,
`❌ 未找到组合订阅:${name}`,
);
res.status(404).json({
status: 'failed',
});
failed(
res,
new ResourceNotFoundError(
'RESOURCE_NOT_FOUND',
`Collection ${name} does not exist!`,
),
404,
);
}
}

View File

@@ -12,6 +12,13 @@ export class InternalServerError extends BaseError {
}
}
export class RequestInvalidError extends BaseError {
constructor(code, message, details) {
super(code, message, details);
this.type = 'RequestInvalidError';
}
}
export class ResourceNotFoundError extends BaseError {
constructor(code, message, details) {
super(code, message, details);

View File

@@ -16,7 +16,8 @@ import registerDownloadRoutes from './download';
import registerSettingRoutes from './settings';
import registerPreviewRoutes from './preview';
import registerSortingRoutes from './sort';
import { success } from '@/restful/response';
import { failed, success } from '@/restful/response';
import { InternalServerError, RequestInvalidError } from '@/restful/errors';
export default function serve() {
const $app = express({ substore: $ });
@@ -76,12 +77,9 @@ function getEnv(req, res) {
if (isStash) backend = 'Stash';
if (isShadowRocket) backend = 'ShadowRocket';
res.json({
status: 200,
data: {
backend,
version: substoreVersion,
},
success(res, {
backend,
version: substoreVersion,
});
}
@@ -90,10 +88,13 @@ async function gistBackup(req, res) {
// read token
const { gistToken } = $.read(SETTINGS_KEY);
if (!gistToken) {
res.status(500).json({
status: 'failed',
message: '未找到Gist备份Token!',
});
failed(
res,
new RequestInvalidError(
'GIST_TOKEN_NOT_FOUND',
`GitHub Token is required for backup!`,
),
);
} else {
const gist = new Gist({
token: gistToken,
@@ -127,14 +128,14 @@ async function gistBackup(req, res) {
}
success(res);
} catch (err) {
const msg = `${
action === 'upload' ? '上传' : '下载'
}备份失败!${err}`;
$.error(msg);
res.status(500).json({
status: 'failed',
message: msg,
});
failed(
res,
new InternalServerError(
'BACKUP_FAILED',
`Failed to ${action} data to gist!`,
`Reason: ${JSON.stringify(err)}`,
),
);
}
}
}

View File

@@ -2,6 +2,7 @@ import {
NetworkError,
InternalServerError,
ResourceNotFoundError,
RequestInvalidError,
} from './errors';
import { deleteByName, findByName, updateByName } from '@/utils/database';
import { SUBS_KEY, COLLECTIONS_KEY } from '@/constants';
@@ -40,7 +41,7 @@ async function getFlowInfo(req, res) {
return;
}
if (sub.source === 'local') {
failed(res, new InternalServerError('NO_FLOW_INFO', 'N/A'));
failed(res, new RequestInvalidError('NO_FLOW_INFO', 'N/A'));
return;
}
try {
@@ -76,10 +77,13 @@ function createSubscription(req, res) {
$.info(`正在创建订阅: ${sub.name}`);
const allSubs = $.read(SUBS_KEY);
if (findByName(allSubs, sub.name)) {
res.status(500).json({
status: 'failed',
message: `订阅${sub.name}已存在!`,
});
failed(
res,
new RequestInvalidError(
'DUPLICATE_KEY',
`Subscription ${sub.name} already exists.`,
),
);
}
allSubs.push(sub);
$.write(allSubs, SUBS_KEY);
@@ -98,6 +102,14 @@ function getSubscription(req, res) {
status: 'failed',
message: `未找到订阅:${name}!`,
});
failed(
res,
new ResourceNotFoundError(
`SUBSCRIPTION_NOT_FOUND`,
`Subscription ${name} does not exist`,
404,
),
);
}
}
@@ -128,10 +140,14 @@ function updateSubscription(req, res) {
$.write(allSubs, SUBS_KEY);
success(res, newSub);
} else {
res.status(500).json({
status: 'failed',
message: `订阅${name}不存在,无法更新!`,
});
failed(
res,
new ResourceNotFoundError(
'RESOURCE_NOT_FOUND',
`Subscription ${name} does not exist!`,
),
404,
);
}
}

View File

@@ -75,14 +75,13 @@ function doMigrationV2() {
tfo: 'DEFAULT',
scert: 'DEFAULT',
'vmess aead': 'DEFAULT',
'useless': 'DEFAULT',
useless: 'DEFAULT',
},
};
processes.forEach((p) => {
if (p.type === 'Useless Filter') {
quickSettingOperator.args.useless = 'ENABLED';
}
else if (p.type === 'Set Property Operator') {
} else if (p.type === 'Set Property Operator') {
const { key, value } = p.args;
switch (key) {
case 'udp':