mirror of
https://github.com/remnawave/node.git
synced 2026-08-27 04:05:33 +00:00
chore: update @nestjs packages and other dependencies; refactor torrent blocker state management and API config generation
This commit is contained in:
parent
739fe84c6e
commit
9d3319216c
8 changed files with 120 additions and 52 deletions
40
package-lock.json
generated
40
package-lock.json
generated
|
|
@ -12,17 +12,17 @@
|
|||
"@henrygd/semaphore": "0.1.0",
|
||||
"@kastov/body-parser-with-zstd": "2.2.2",
|
||||
"@kastov/node-supervisord": ">=2.0.3",
|
||||
"@nestjs/common": "11.1.15",
|
||||
"@nestjs/common": "11.1.16",
|
||||
"@nestjs/config": "4.0.3",
|
||||
"@nestjs/core": "11.1.15",
|
||||
"@nestjs/core": "11.1.16",
|
||||
"@nestjs/cqrs": "11.0.3",
|
||||
"@nestjs/jwt": "11.0.2",
|
||||
"@nestjs/passport": "11.0.5",
|
||||
"@nestjs/platform-express": "11.1.15",
|
||||
"@nestjs/platform-express": "11.1.16",
|
||||
"@peculiar/webcrypto": "^1.5.0",
|
||||
"@peculiar/x509": "^1.14.3",
|
||||
"@remnawave/hashed-set": "^0.0.4",
|
||||
"@remnawave/node-plugins": "0.1.1",
|
||||
"@remnawave/node-plugins": "0.1.3",
|
||||
"@remnawave/supervisord-nestjs": "0.3.1",
|
||||
"@remnawave/xtls-sdk": "0.11.1",
|
||||
"@remnawave/xtls-sdk-nestjs": "0.6.1",
|
||||
|
|
@ -1207,9 +1207,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@nestjs/common": {
|
||||
"version": "11.1.15",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.15.tgz",
|
||||
"integrity": "sha512-51sqIj83xEhmxi3D+QA+bo+hs+YTnZ5eJvOxa34VFiSlilT8DEF83kZc4t1t3arO8PjfDFI+LaTQQkx0gm1QGA==",
|
||||
"version": "11.1.16",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.16.tgz",
|
||||
"integrity": "sha512-JSIeW+USuMJkkcNbiOdcPkVCeI3TSnXstIVEPpp3HiaKnPRuSbUUKm9TY9o/XpIcPHWUOQItAtC5BiAwFdVITQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"file-type": "21.3.0",
|
||||
|
|
@ -1253,9 +1253,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@nestjs/core": {
|
||||
"version": "11.1.15",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.15.tgz",
|
||||
"integrity": "sha512-s+SQxA8yAGu8RapbI5Y3GFuNyvF1ocva2Va+d4K5T7G0eC5kAvYfiHr8iKvZg+rue5NjV7fYQQmEvA5jCTG+CA==",
|
||||
"version": "11.1.16",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.16.tgz",
|
||||
"integrity": "sha512-tXWXyCiqWthelJjrE0KLFjf0O98VEt+WPVx5CrqCf+059kIxJ8y1Vw7Cy7N4fwQafWNrmFL2AfN87DDMbVAY0w==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
|
@ -1329,14 +1329,14 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@nestjs/platform-express": {
|
||||
"version": "11.1.15",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.15.tgz",
|
||||
"integrity": "sha512-sR42dQ5fPy4NGbnhT4mRIJLL0h2eNY4KpLkcfd27vg5XdTjQQ07wkQORrw6rAkWkBvMRr0dmhAcUBnXpe7ro5Q==",
|
||||
"version": "11.1.16",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.16.tgz",
|
||||
"integrity": "sha512-IOegr5+ZfUiMKgk+garsSU4MOkPRhm46e6w8Bp1GcO4vCdl9Piz6FlWAzKVfa/U3Hn/DdzSVJOW3TWcQQFdBDw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cors": "2.8.6",
|
||||
"express": "5.2.1",
|
||||
"multer": "2.1.0",
|
||||
"multer": "2.1.1",
|
||||
"path-to-regexp": "8.3.0",
|
||||
"tslib": "2.8.1"
|
||||
},
|
||||
|
|
@ -2068,9 +2068,9 @@
|
|||
"license": "AGPL-3.0-only"
|
||||
},
|
||||
"node_modules/@remnawave/node-plugins": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@remnawave/node-plugins/-/node-plugins-0.1.1.tgz",
|
||||
"integrity": "sha512-UxiEG6+hjjCDRjXlAieq4vx9KIJNwI36hdup3vl6fTXYUy4csnPR6JsPC5AhGj1n4B539ifkEr2MuWtwXLfx5w==",
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@remnawave/node-plugins/-/node-plugins-0.1.3.tgz",
|
||||
"integrity": "sha512-n5gZGHpC3pJVB99+BeVX9ZadKIyAajHDlMBviyJOepb2R9gB7eICPxIt9YroLS/5/GdkcLNd6ojveE2fvDF4Jw==",
|
||||
"license": "AGPL-3.0-only",
|
||||
"dependencies": {
|
||||
"zod": "3.25.76"
|
||||
|
|
@ -6075,9 +6075,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/multer": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/multer/-/multer-2.1.0.tgz",
|
||||
"integrity": "sha512-TBm6j41rxNohqawsxlsWsNNh/VdV4QFXcBvRcPhXaA05EZ79z0qJ2bQFpync6JBoHTeNY5Q1JpG7AlTjdlfAEA==",
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/multer/-/multer-2.1.1.tgz",
|
||||
"integrity": "sha512-mo+QTzKlx8R7E5ylSXxWzGoXoZbOsRMpyitcht8By2KHvMbf3tjwosZ/Mu/XYU6UuJ3VZnODIrak5ZrPiPyB6A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"append-field": "^1.0.0",
|
||||
|
|
|
|||
|
|
@ -35,17 +35,17 @@
|
|||
"@henrygd/semaphore": "0.1.0",
|
||||
"@kastov/body-parser-with-zstd": "2.2.2",
|
||||
"@kastov/node-supervisord": ">=2.0.3",
|
||||
"@nestjs/common": "11.1.15",
|
||||
"@nestjs/common": "11.1.16",
|
||||
"@nestjs/config": "4.0.3",
|
||||
"@nestjs/core": "11.1.15",
|
||||
"@nestjs/core": "11.1.16",
|
||||
"@nestjs/jwt": "11.0.2",
|
||||
"@nestjs/passport": "11.0.5",
|
||||
"@nestjs/platform-express": "11.1.15",
|
||||
"@nestjs/platform-express": "11.1.16",
|
||||
"@peculiar/webcrypto": "^1.5.0",
|
||||
"@peculiar/x509": "^1.14.3",
|
||||
"@remnawave/hashed-set": "^0.0.4",
|
||||
"@nestjs/cqrs": "11.0.3",
|
||||
"@remnawave/node-plugins": "0.1.1",
|
||||
"@remnawave/node-plugins": "0.1.3",
|
||||
"@remnawave/supervisord-nestjs": "0.3.1",
|
||||
"@remnawave/xtls-sdk": "0.11.1",
|
||||
"@remnawave/xtls-sdk-nestjs": "0.6.1",
|
||||
|
|
|
|||
|
|
@ -15,14 +15,32 @@ import { getServerCerts } from './generate-mtls-certs';
|
|||
import { getXtlsApiPort } from './get-initial-ports';
|
||||
import { IPolicyConfig } from './interfaces';
|
||||
|
||||
export const generateApiConfig = (
|
||||
config: Record<string, unknown>,
|
||||
isTorrentBlockerEnabled: boolean,
|
||||
interface IRoutingXrayConfig {
|
||||
rules: {
|
||||
ruleTag?: string;
|
||||
webhook?: {
|
||||
url: string;
|
||||
deduplication: number;
|
||||
};
|
||||
[key: string]: unknown;
|
||||
}[];
|
||||
}
|
||||
|
||||
interface IGenerateApiConfigParams {
|
||||
config: Record<string, unknown>;
|
||||
torrentBlockerState: {
|
||||
enabled: boolean;
|
||||
includeRuleTags: Set<string>;
|
||||
};
|
||||
internal: {
|
||||
socketPath: string;
|
||||
token: string;
|
||||
},
|
||||
): Record<string, unknown> => {
|
||||
};
|
||||
}
|
||||
|
||||
export const generateApiConfig = (args: IGenerateApiConfigParams): Record<string, unknown> => {
|
||||
const { config, torrentBlockerState, internal } = args;
|
||||
|
||||
const policyConfig = config.policy as undefined | IPolicyConfig;
|
||||
const serverCerts = getServerCerts();
|
||||
const hasCapNetAdminResult = hasCapNetAdmin();
|
||||
|
|
@ -39,7 +57,7 @@ export const generateApiConfig = (
|
|||
system: XRAY_DEFAULT_POLICY_MODEL.policy.system,
|
||||
};
|
||||
|
||||
return {
|
||||
const result = {
|
||||
...config,
|
||||
...XRAY_DEFAULT_STATS_MODEL,
|
||||
...XRAY_DEFAULT_API_MODEL,
|
||||
|
|
@ -52,24 +70,44 @@ export const generateApiConfig = (
|
|||
}),
|
||||
...(Array.isArray(config.inbounds) ? config.inbounds : []),
|
||||
],
|
||||
outbounds: [
|
||||
...(Array.isArray(config.outbounds) ? config.outbounds : []),
|
||||
...(isTorrentBlockerEnabled ? [XRAY_TORRENT_BLOCKER_OUTBOUND_MODEL] : []),
|
||||
],
|
||||
outbounds: [...(Array.isArray(config.outbounds) ? config.outbounds : [])],
|
||||
policy: builtPolicy,
|
||||
routing: {
|
||||
...(config.routing || {}),
|
||||
rules: [
|
||||
XRAY_ROUTING_RULES_MODEL,
|
||||
...(isTorrentBlockerEnabled
|
||||
? [
|
||||
XRAY_TORRENT_BLOCKER_ROUTING_RULES_MODEL({
|
||||
webhookUrl: `/${internal.socketPath}:${XRAY_INTERNAL_FULL_WEBHOOK_PATH}?token=${internal.token}`,
|
||||
}),
|
||||
]
|
||||
: []),
|
||||
...((config.routing as { rules?: unknown[] })?.rules || []),
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
if (torrentBlockerState.enabled) {
|
||||
const webhookUrl = buildWebhookUrl(internal);
|
||||
const routing = result.routing as IRoutingXrayConfig;
|
||||
|
||||
result.outbounds.push(XRAY_TORRENT_BLOCKER_OUTBOUND_MODEL);
|
||||
|
||||
routing.rules.splice(1, 0, XRAY_TORRENT_BLOCKER_ROUTING_RULES_MODEL({ webhookUrl }));
|
||||
|
||||
if (torrentBlockerState.includeRuleTags.size > 0) {
|
||||
for (const rule of routing.rules) {
|
||||
if (
|
||||
rule.ruleTag &&
|
||||
typeof rule.ruleTag === 'string' &&
|
||||
torrentBlockerState.includeRuleTags.has(rule.ruleTag)
|
||||
) {
|
||||
rule.webhook = {
|
||||
url: webhookUrl,
|
||||
deduplication: 30,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
const buildWebhookUrl = (internal: { socketPath: string; token: string }): string => {
|
||||
return `/${internal.socketPath}:${XRAY_INTERNAL_FULL_WEBHOOK_PATH}?token=${internal.token}`;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -95,9 +95,18 @@ export class PluginService {
|
|||
this.logger.log('[PLUGIN] Plugins changed...');
|
||||
|
||||
if (currentTorrentBlocker && !pluginData.torrentBlocker?.enabled) {
|
||||
await this.commandBus.execute(
|
||||
new RemoveOutboundCommand(XRAY_TORRENT_BLOCKER_OUTBOUND_TAG),
|
||||
);
|
||||
if (!pluginData.torrentBlocker?.includeRuleTags) {
|
||||
await this.commandBus.execute(
|
||||
new RemoveOutboundCommand(XRAY_TORRENT_BLOCKER_OUTBOUND_TAG),
|
||||
);
|
||||
} else {
|
||||
await this.commandBus.execute(
|
||||
new StopXrayCommand({
|
||||
withOnlineCheck: true,
|
||||
withPluginCleanup: false,
|
||||
}),
|
||||
);
|
||||
}
|
||||
} else if (!currentTorrentBlocker && pluginData.torrentBlocker?.enabled) {
|
||||
await this.commandBus.execute(
|
||||
new StopXrayCommand({
|
||||
|
|
@ -158,6 +167,7 @@ export class PluginService {
|
|||
this.state.torrentBlocker.setIgnoredIps(ips);
|
||||
this.state.torrentBlocker.setIgnoredUsers(users);
|
||||
this.state.torrentBlocker.configure(blockDuration);
|
||||
this.state.torrentBlocker.setIncludeRuleTags(pluginData.torrentBlocker.includeRuleTags);
|
||||
|
||||
this.logger.log(
|
||||
`[PLUGIN] Torrent-Blocker: blockDuration=${blockDuration}s, ${ips.length} ignored IPs, ${users.length} ignored users`,
|
||||
|
|
|
|||
|
|
@ -13,10 +13,16 @@ export class GetTorrentBlockerStateHandler implements IQueryHandler<GetTorrentBl
|
|||
try {
|
||||
const isEnabled = this.pluginState.torrentBlocker.isEnabled;
|
||||
|
||||
return isEnabled;
|
||||
return {
|
||||
enabled: isEnabled,
|
||||
includeRuleTags: this.pluginState.torrentBlocker.includeRuleTagsSet,
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.error(error);
|
||||
return false;
|
||||
return {
|
||||
enabled: false,
|
||||
includeRuleTags: new Set<string>(),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
import { Query } from '@nestjs/cqrs';
|
||||
|
||||
export class GetTorrentBlockerStateQuery extends Query<boolean> {
|
||||
export class GetTorrentBlockerStateQuery extends Query<{
|
||||
enabled: boolean;
|
||||
includeRuleTags: Set<string>;
|
||||
}> {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ export class TorrentBlockerState {
|
|||
private blockDuration: number | null = null;
|
||||
private ignoredIps = new Set<string>();
|
||||
private ignoredUsers = new Set<string>();
|
||||
private includeRuleTags = new Set<string>();
|
||||
private reports: TorrentBlockerReportModel[] = [];
|
||||
|
||||
get isEnabled(): boolean {
|
||||
|
|
@ -55,5 +56,15 @@ export class TorrentBlockerState {
|
|||
this.blockDuration = null;
|
||||
this.ignoredIps.clear();
|
||||
this.ignoredUsers.clear();
|
||||
this.includeRuleTags.clear();
|
||||
}
|
||||
|
||||
setIncludeRuleTags(tags: string[] | undefined): void {
|
||||
if (!tags) return;
|
||||
this.includeRuleTags = new Set(tags);
|
||||
}
|
||||
|
||||
get includeRuleTagsSet(): Set<string> {
|
||||
return this.includeRuleTags;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -160,11 +160,11 @@ export class XrayService implements OnApplicationBootstrap {
|
|||
new GetTorrentBlockerStateQuery(),
|
||||
);
|
||||
|
||||
const fullConfig = generateApiConfig(
|
||||
body.xrayConfig,
|
||||
isTorrentBlockerEnabled,
|
||||
this.internal,
|
||||
);
|
||||
const fullConfig = generateApiConfig({
|
||||
config: body.xrayConfig,
|
||||
torrentBlockerState: isTorrentBlockerEnabled,
|
||||
internal: this.internal,
|
||||
});
|
||||
|
||||
await this.internalService.extractUsersFromConfig(body.internals.hashes, fullConfig);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue