Unify Harbor error handling across server and client
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import crypto from 'node:crypto';
|
||||
import fs from 'node:fs';
|
||||
import { settings } from './config.js';
|
||||
import { HarborError } from '../shared/errors.js';
|
||||
|
||||
const PROXY_TYPES = new Set(['vless', 'vmess', 'trojan', 'shadowsocks', 'hysteria2']);
|
||||
|
||||
@@ -40,10 +41,15 @@ export function parseUserInfo(headerValue) {
|
||||
|
||||
export function parseVlessUrl(rawUrl) {
|
||||
if (!rawUrl.startsWith('vless://')) {
|
||||
throw new Error('VLESS URL must start with vless://');
|
||||
throw new HarborError('SUBSCRIPTION_INVALID');
|
||||
}
|
||||
|
||||
const parsed = new URL(rawUrl);
|
||||
let parsed;
|
||||
try {
|
||||
parsed = new URL(rawUrl);
|
||||
} catch (cause) {
|
||||
throw new HarborError('SUBSCRIPTION_INVALID', { cause });
|
||||
}
|
||||
const tag = decodeURIComponent(parsed.hash ? parsed.hash.slice(1) : 'vless-out');
|
||||
const uuid = decodeURIComponent(parsed.username || '');
|
||||
const server = parsed.hostname;
|
||||
@@ -55,11 +61,11 @@ export function parseVlessUrl(rawUrl) {
|
||||
const flow = parsed.searchParams.get('flow') || '';
|
||||
|
||||
if (!uuid || !server || !serverPort) {
|
||||
throw new Error('VLESS URL misses uuid, host or port');
|
||||
throw new HarborError('SUBSCRIPTION_INVALID');
|
||||
}
|
||||
|
||||
if (!publicKey || !shortId) {
|
||||
throw new Error('VLESS REALITY parameters pbk and sid are required');
|
||||
throw new HarborError('SUBSCRIPTION_INVALID');
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -111,7 +117,7 @@ export function parseSubscriptionBody(body) {
|
||||
.filter((line) => line.startsWith('vless://'));
|
||||
|
||||
if (!links.length) {
|
||||
throw new Error('Subscription does not contain JSON config or VLESS links');
|
||||
throw new HarborError('SUBSCRIPTION_INVALID');
|
||||
}
|
||||
|
||||
parsedConfig = {
|
||||
@@ -130,7 +136,7 @@ export function parseSubscriptionBody(body) {
|
||||
}));
|
||||
|
||||
if (!servers.length) {
|
||||
throw new Error('No supported proxy outbounds found in subscription');
|
||||
throw new HarborError('SUBSCRIPTION_INVALID');
|
||||
}
|
||||
|
||||
return { config: parsedConfig, servers };
|
||||
@@ -140,21 +146,26 @@ async function requestSubscription(url) {
|
||||
let parsedUrl;
|
||||
try {
|
||||
parsedUrl = new URL(url);
|
||||
} catch {
|
||||
throw new Error('Invalid subscription URL');
|
||||
} catch (cause) {
|
||||
throw new HarborError('SUBSCRIPTION_INVALID', { cause });
|
||||
}
|
||||
|
||||
if (!['http:', 'https:'].includes(parsedUrl.protocol)) {
|
||||
throw new Error('Subscription URL must use http or https');
|
||||
throw new HarborError('SUBSCRIPTION_INVALID');
|
||||
}
|
||||
|
||||
const response = await fetch(parsedUrl, {
|
||||
headers: subscriptionHeaders(),
|
||||
redirect: 'follow',
|
||||
});
|
||||
let response;
|
||||
try {
|
||||
response = await fetch(parsedUrl, {
|
||||
headers: subscriptionHeaders(),
|
||||
redirect: 'follow',
|
||||
});
|
||||
} catch (cause) {
|
||||
throw new HarborError('PROVIDER_UNAVAILABLE', { cause });
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Subscription request failed: HTTP ${response.status}`);
|
||||
throw new HarborError('PROVIDER_UNAVAILABLE', { details: `HTTP ${response.status}` });
|
||||
}
|
||||
|
||||
return response;
|
||||
|
||||
Reference in New Issue
Block a user