mirror of
https://git.mirrors.martin98.com/https://github.com/sub-store-org/Sub-Store.git
synced 2025-06-04 11:13:59 +08:00
refactor: Standardize error handling for RESTful APIs
This commit is contained in:
parent
0e089ef8ce
commit
00c28c6cb8
6
backend/dist/cron-sync-artifacts.min.js
vendored
6
backend/dist/cron-sync-artifacts.min.js
vendored
File diff suppressed because one or more lines are too long
6
backend/dist/sub-store-parser.loon.min.js
vendored
6
backend/dist/sub-store-parser.loon.min.js
vendored
File diff suppressed because one or more lines are too long
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "sub-store",
|
"name": "sub-store",
|
||||||
"version": "2.6.0",
|
"version": "2.6.1",
|
||||||
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and ShadowRocket.",
|
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and ShadowRocket.",
|
||||||
"main": "src/main.js",
|
"main": "src/main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { deleteByName, findByName, updateByName } from '@/utils/database';
|
import { deleteByName, findByName, updateByName } from '@/utils/database';
|
||||||
import { COLLECTIONS_KEY } from '@/constants';
|
import { COLLECTIONS_KEY } from '@/constants';
|
||||||
import { success } from '@/restful/response';
|
import { failed, success } from '@/restful/response';
|
||||||
import $ from '@/core/app';
|
import $ from '@/core/app';
|
||||||
|
import { RequestInvalidError, ResourceNotFoundError } from '@/restful/errors';
|
||||||
|
|
||||||
export default function register($app) {
|
export default function register($app) {
|
||||||
if (!$.read(COLLECTIONS_KEY)) $.write({}, COLLECTIONS_KEY);
|
if (!$.read(COLLECTIONS_KEY)) $.write({}, COLLECTIONS_KEY);
|
||||||
@ -22,10 +23,13 @@ function createCollection(req, res) {
|
|||||||
$.info(`正在创建组合订阅:${collection.name}`);
|
$.info(`正在创建组合订阅:${collection.name}`);
|
||||||
const allCols = $.read(COLLECTIONS_KEY);
|
const allCols = $.read(COLLECTIONS_KEY);
|
||||||
if (findByName(allCols, collection.name)) {
|
if (findByName(allCols, collection.name)) {
|
||||||
res.status(500).json({
|
failed(
|
||||||
status: 'failed',
|
res,
|
||||||
message: `订阅集${collection.name}已存在!`,
|
new RequestInvalidError(
|
||||||
});
|
'DUPLICATE_KEY',
|
||||||
|
`Collection ${collection.name} already exists.`,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
allCols.push(collection);
|
allCols.push(collection);
|
||||||
$.write(allCols, COLLECTIONS_KEY);
|
$.write(allCols, COLLECTIONS_KEY);
|
||||||
@ -40,10 +44,14 @@ function getCollection(req, res) {
|
|||||||
if (collection) {
|
if (collection) {
|
||||||
success(res, collection);
|
success(res, collection);
|
||||||
} else {
|
} else {
|
||||||
res.status(404).json({
|
failed(
|
||||||
status: 'failed',
|
res,
|
||||||
message: `未找到订阅集:${name}!`,
|
new ResourceNotFoundError(
|
||||||
});
|
`SUBSCRIPTION_NOT_FOUND`,
|
||||||
|
`Collection ${name} does not exist`,
|
||||||
|
404,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,10 +71,14 @@ function updateCollection(req, res) {
|
|||||||
$.write(allCols, COLLECTIONS_KEY);
|
$.write(allCols, COLLECTIONS_KEY);
|
||||||
success(res, newCol);
|
success(res, newCol);
|
||||||
} else {
|
} else {
|
||||||
res.status(500).json({
|
failed(
|
||||||
status: 'failed',
|
res,
|
||||||
message: `订阅集${name}不存在,无法更新!`,
|
new ResourceNotFoundError(
|
||||||
});
|
'RESOURCE_NOT_FOUND',
|
||||||
|
`Collection ${name} does not exist!`,
|
||||||
|
),
|
||||||
|
404,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,8 @@ import { findByName } from '@/utils/database';
|
|||||||
import { getFlowHeaders } from '@/utils/flow';
|
import { getFlowHeaders } from '@/utils/flow';
|
||||||
import { produceArtifact } from './artifacts';
|
import { produceArtifact } from './artifacts';
|
||||||
import $ from '@/core/app';
|
import $ from '@/core/app';
|
||||||
|
import { failed } from '@/restful/response';
|
||||||
|
import { InternalServerError, ResourceNotFoundError } from '@/restful/errors';
|
||||||
|
|
||||||
export default function register($app) {
|
export default function register($app) {
|
||||||
$app.get('/download/collection/:name', downloadCollection);
|
$app.get('/download/collection/:name', downloadCollection);
|
||||||
@ -51,16 +53,25 @@ async function downloadSubscription(req, res) {
|
|||||||
`🤔 原因:${JSON.stringify(err)}`,
|
`🤔 原因:${JSON.stringify(err)}`,
|
||||||
);
|
);
|
||||||
$.error(JSON.stringify(err));
|
$.error(JSON.stringify(err));
|
||||||
res.status(500).json({
|
failed(
|
||||||
status: 'failed',
|
res,
|
||||||
message: err,
|
new InternalServerError(
|
||||||
});
|
'INTERNAL_SERVER_ERROR',
|
||||||
|
`Failed to download subscription: ${name}`,
|
||||||
|
`Reason: ${JSON.stringify(err)}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$.notify(`🌍 『 𝑺𝒖𝒃-𝑺𝒕𝒐𝒓𝒆 』 下载订阅失败`, `❌ 未找到订阅:${name}!`);
|
$.notify(`🌍 『 𝑺𝒖𝒃-𝑺𝒕𝒐𝒓𝒆 』 下载订阅失败`, `❌ 未找到订阅:${name}!`);
|
||||||
res.status(404).json({
|
failed(
|
||||||
status: 'failed',
|
res,
|
||||||
});
|
new ResourceNotFoundError(
|
||||||
|
'RESOURCE_NOT_FOUND',
|
||||||
|
`Subscription ${name} does not exist!`,
|
||||||
|
),
|
||||||
|
404,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,18 +121,27 @@ async function downloadCollection(req, res) {
|
|||||||
`❌ 下载组合订阅错误:${name}!`,
|
`❌ 下载组合订阅错误:${name}!`,
|
||||||
`🤔 原因:${err}`,
|
`🤔 原因:${err}`,
|
||||||
);
|
);
|
||||||
res.status(500).json({
|
failed(
|
||||||
status: 'failed',
|
res,
|
||||||
message: err,
|
new InternalServerError(
|
||||||
});
|
'INTERNAL_SERVER_ERROR',
|
||||||
|
`Failed to download collection: ${name}`,
|
||||||
|
`Reason: ${JSON.stringify(err)}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$.notify(
|
$.notify(
|
||||||
`🌍 『 𝑺𝒖𝒃-𝑺𝒕𝒐𝒓𝒆 』 下载组合订阅失败`,
|
`🌍 『 𝑺𝒖𝒃-𝑺𝒕𝒐𝒓𝒆 』 下载组合订阅失败`,
|
||||||
`❌ 未找到组合订阅:${name}!`,
|
`❌ 未找到组合订阅:${name}!`,
|
||||||
);
|
);
|
||||||
res.status(404).json({
|
failed(
|
||||||
status: 'failed',
|
res,
|
||||||
});
|
new ResourceNotFoundError(
|
||||||
|
'RESOURCE_NOT_FOUND',
|
||||||
|
`Collection ${name} does not exist!`,
|
||||||
|
),
|
||||||
|
404,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
export class ResourceNotFoundError extends BaseError {
|
||||||
constructor(code, message, details) {
|
constructor(code, message, details) {
|
||||||
super(code, message, details);
|
super(code, message, details);
|
||||||
|
@ -16,7 +16,8 @@ import registerDownloadRoutes from './download';
|
|||||||
import registerSettingRoutes from './settings';
|
import registerSettingRoutes from './settings';
|
||||||
import registerPreviewRoutes from './preview';
|
import registerPreviewRoutes from './preview';
|
||||||
import registerSortingRoutes from './sort';
|
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() {
|
export default function serve() {
|
||||||
const $app = express({ substore: $ });
|
const $app = express({ substore: $ });
|
||||||
@ -76,12 +77,9 @@ function getEnv(req, res) {
|
|||||||
if (isStash) backend = 'Stash';
|
if (isStash) backend = 'Stash';
|
||||||
if (isShadowRocket) backend = 'ShadowRocket';
|
if (isShadowRocket) backend = 'ShadowRocket';
|
||||||
|
|
||||||
res.json({
|
success(res, {
|
||||||
status: 200,
|
backend,
|
||||||
data: {
|
version: substoreVersion,
|
||||||
backend,
|
|
||||||
version: substoreVersion,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,10 +88,13 @@ async function gistBackup(req, res) {
|
|||||||
// read token
|
// read token
|
||||||
const { gistToken } = $.read(SETTINGS_KEY);
|
const { gistToken } = $.read(SETTINGS_KEY);
|
||||||
if (!gistToken) {
|
if (!gistToken) {
|
||||||
res.status(500).json({
|
failed(
|
||||||
status: 'failed',
|
res,
|
||||||
message: '未找到Gist备份Token!',
|
new RequestInvalidError(
|
||||||
});
|
'GIST_TOKEN_NOT_FOUND',
|
||||||
|
`GitHub Token is required for backup!`,
|
||||||
|
),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
const gist = new Gist({
|
const gist = new Gist({
|
||||||
token: gistToken,
|
token: gistToken,
|
||||||
@ -127,14 +128,14 @@ async function gistBackup(req, res) {
|
|||||||
}
|
}
|
||||||
success(res);
|
success(res);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const msg = `${
|
failed(
|
||||||
action === 'upload' ? '上传' : '下载'
|
res,
|
||||||
}备份失败!${err}`;
|
new InternalServerError(
|
||||||
$.error(msg);
|
'BACKUP_FAILED',
|
||||||
res.status(500).json({
|
`Failed to ${action} data to gist!`,
|
||||||
status: 'failed',
|
`Reason: ${JSON.stringify(err)}`,
|
||||||
message: msg,
|
),
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import {
|
|||||||
NetworkError,
|
NetworkError,
|
||||||
InternalServerError,
|
InternalServerError,
|
||||||
ResourceNotFoundError,
|
ResourceNotFoundError,
|
||||||
|
RequestInvalidError,
|
||||||
} from './errors';
|
} from './errors';
|
||||||
import { deleteByName, findByName, updateByName } from '@/utils/database';
|
import { deleteByName, findByName, updateByName } from '@/utils/database';
|
||||||
import { SUBS_KEY, COLLECTIONS_KEY } from '@/constants';
|
import { SUBS_KEY, COLLECTIONS_KEY } from '@/constants';
|
||||||
@ -40,7 +41,7 @@ async function getFlowInfo(req, res) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (sub.source === 'local') {
|
if (sub.source === 'local') {
|
||||||
failed(res, new InternalServerError('NO_FLOW_INFO', 'N/A'));
|
failed(res, new RequestInvalidError('NO_FLOW_INFO', 'N/A'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@ -76,10 +77,13 @@ function createSubscription(req, res) {
|
|||||||
$.info(`正在创建订阅: ${sub.name}`);
|
$.info(`正在创建订阅: ${sub.name}`);
|
||||||
const allSubs = $.read(SUBS_KEY);
|
const allSubs = $.read(SUBS_KEY);
|
||||||
if (findByName(allSubs, sub.name)) {
|
if (findByName(allSubs, sub.name)) {
|
||||||
res.status(500).json({
|
failed(
|
||||||
status: 'failed',
|
res,
|
||||||
message: `订阅${sub.name}已存在!`,
|
new RequestInvalidError(
|
||||||
});
|
'DUPLICATE_KEY',
|
||||||
|
`Subscription ${sub.name} already exists.`,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
allSubs.push(sub);
|
allSubs.push(sub);
|
||||||
$.write(allSubs, SUBS_KEY);
|
$.write(allSubs, SUBS_KEY);
|
||||||
@ -98,6 +102,14 @@ function getSubscription(req, res) {
|
|||||||
status: 'failed',
|
status: 'failed',
|
||||||
message: `未找到订阅:${name}!`,
|
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);
|
$.write(allSubs, SUBS_KEY);
|
||||||
success(res, newSub);
|
success(res, newSub);
|
||||||
} else {
|
} else {
|
||||||
res.status(500).json({
|
failed(
|
||||||
status: 'failed',
|
res,
|
||||||
message: `订阅${name}不存在,无法更新!`,
|
new ResourceNotFoundError(
|
||||||
});
|
'RESOURCE_NOT_FOUND',
|
||||||
|
`Subscription ${name} does not exist!`,
|
||||||
|
),
|
||||||
|
404,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,14 +75,13 @@ function doMigrationV2() {
|
|||||||
tfo: 'DEFAULT',
|
tfo: 'DEFAULT',
|
||||||
scert: 'DEFAULT',
|
scert: 'DEFAULT',
|
||||||
'vmess aead': 'DEFAULT',
|
'vmess aead': 'DEFAULT',
|
||||||
'useless': 'DEFAULT',
|
useless: 'DEFAULT',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
processes.forEach((p) => {
|
processes.forEach((p) => {
|
||||||
if (p.type === 'Useless Filter') {
|
if (p.type === 'Useless Filter') {
|
||||||
quickSettingOperator.args.useless = 'ENABLED';
|
quickSettingOperator.args.useless = 'ENABLED';
|
||||||
}
|
} else if (p.type === 'Set Property Operator') {
|
||||||
else if (p.type === 'Set Property Operator') {
|
|
||||||
const { key, value } = p.args;
|
const { key, value } = p.args;
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case 'udp':
|
case 'udp':
|
||||||
|
6
backend/sub-store.min.js
vendored
6
backend/sub-store.min.js
vendored
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user