mirror of
https://github.com/remnawave/node.git
synced 2026-08-30 05:47:46 +00:00
refactor: integration modules
This commit is contained in:
parent
8ff4c83873
commit
4e4f5596e9
10 changed files with 73 additions and 13 deletions
4
.github/workflows/build-and-push.yml
vendored
4
.github/workflows/build-and-push.yml
vendored
|
|
@ -192,10 +192,6 @@ jobs:
|
|||
- `remnawave/node:${{github.ref_name}}`
|
||||
- `ghcr.io/remnawave/node:${{github.ref_name}}`
|
||||
|
||||
Docker images with bundled HAProxy:
|
||||
- `remnawave/node:latest-hp` / `remnawave/node:${{github.ref_name}}-hp`
|
||||
- `ghcr.io/remnawave/node:latest-hp` / `ghcr.io/remnawave/node:${{github.ref_name}}-hp`
|
||||
|
||||
send-telegram-message:
|
||||
name: Send Telegram message
|
||||
needs: [merge, create-release]
|
||||
|
|
|
|||
|
|
@ -30,6 +30,9 @@ RUN apk add --no-cache curl \
|
|||
&& rm -f /tmp/asn-prefixes-lmdb.tar.gz
|
||||
|
||||
|
||||
FROM scratch AS integration-bin
|
||||
|
||||
|
||||
FROM node:24.19-trixie-slim
|
||||
|
||||
ARG S6_OVERLAY_VERSION=3.2.3.0
|
||||
|
|
@ -80,6 +83,7 @@ RUN apt-get update \
|
|||
ARG INTEGRATIONS=""
|
||||
|
||||
RUN --mount=type=bind,source=docker/integrations,target=/mnt/integrations \
|
||||
--mount=type=bind,from=integration-bin,target=/mnt/bin \
|
||||
sh /mnt/integrations/install.sh "${INTEGRATIONS}"
|
||||
|
||||
ENV NODE_ENV=production
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
import { z } from 'zod';
|
||||
|
||||
import { NodeSystemSchema } from '../../models';
|
||||
import { REST_API } from '../../api';
|
||||
import { NodeMetadataSchema, NodeSystemSchema } from '../../models';
|
||||
|
||||
export namespace StartXrayCommand {
|
||||
export const url = REST_API.XRAY.START;
|
||||
export const RequestSchema = z.object({
|
||||
internals: z.object({
|
||||
metadata: NodeMetadataSchema.optional(),
|
||||
forceRestart: z.boolean().default(false),
|
||||
hashes: z.object({
|
||||
emptyConfig: z.string(),
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
export * from './node-system.schema';
|
||||
export * from './torrent-blocker.report.schema';
|
||||
export * from './xray-webhook.schema';
|
||||
export * from './node-metadata.schema';
|
||||
|
|
|
|||
11
libs/contract/models/node-metadata.schema.ts
Normal file
11
libs/contract/models/node-metadata.schema.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import { z } from 'zod';
|
||||
|
||||
export const NodeMetadataSchema = z.object({
|
||||
name: z.string(),
|
||||
uuid: z.string(),
|
||||
id: z.number(),
|
||||
tags: z.array(z.string()),
|
||||
countryCode: z.string(),
|
||||
});
|
||||
|
||||
export type TNodeMetadata = z.infer<typeof NodeMetadataSchema>;
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@remnawave/node-contract",
|
||||
"version": "2.9.0",
|
||||
"version": "2.9.1",
|
||||
"description": "A node-contract library for Remnawave Panel",
|
||||
"keywords": [],
|
||||
"homepage": "https://github.com/remnawave",
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
import { Type } from '@nestjs/common';
|
||||
|
||||
import { TNodeMetadata } from '@libs/contracts/models';
|
||||
|
||||
export const NODE_INTEGRATIONS = 'NODE_INTEGRATIONS' as const;
|
||||
|
||||
export interface INodeIntegrationResult {
|
||||
|
|
@ -7,7 +11,16 @@ export interface INodeIntegrationResult {
|
|||
export interface INodeIntegration {
|
||||
readonly name: string;
|
||||
|
||||
sync(coreConfig: unknown): Promise<INodeIntegrationResult>;
|
||||
sync(
|
||||
integrationConfig: Record<string, unknown>,
|
||||
nodeMetadata: TNodeMetadata,
|
||||
): Promise<INodeIntegrationResult>;
|
||||
|
||||
stop(): Promise<void>;
|
||||
}
|
||||
|
||||
export interface INodeIntegrationDescriptor {
|
||||
module: Type;
|
||||
service: Type<INodeIntegration>;
|
||||
isAvailable: () => boolean;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,34 @@
|
|||
import { Global, Module } from '@nestjs/common';
|
||||
import { ConditionalModule } from '@nestjs/config';
|
||||
|
||||
import { INodeIntegration, NODE_INTEGRATIONS } from './integrations.contract';
|
||||
import {
|
||||
INodeIntegration,
|
||||
INodeIntegrationDescriptor,
|
||||
NODE_INTEGRATIONS,
|
||||
} from './integrations.contract';
|
||||
import { IntegrationsService } from './integrations.service';
|
||||
|
||||
const context = require.context('./', true, /\.integration\.ts$/);
|
||||
|
||||
const DESCRIPTORS: INodeIntegrationDescriptor[] = context
|
||||
.keys()
|
||||
.map((key) => (context(key) as { descriptor: INodeIntegrationDescriptor }).descriptor);
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
imports: [],
|
||||
imports: DESCRIPTORS.map((descriptor) =>
|
||||
ConditionalModule.registerWhen(descriptor.module, descriptor.isAvailable, { debug: false }),
|
||||
),
|
||||
providers: [
|
||||
IntegrationsService,
|
||||
{
|
||||
provide: NODE_INTEGRATIONS,
|
||||
useFactory: (...integrations: (INodeIntegration | undefined)[]) =>
|
||||
integrations.filter((integration) => integration !== undefined),
|
||||
inject: [],
|
||||
inject: DESCRIPTORS.map((descriptor) => ({
|
||||
token: descriptor.service,
|
||||
optional: true,
|
||||
})),
|
||||
},
|
||||
],
|
||||
exports: [IntegrationsService],
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { Inject, Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import { TNodeMetadata } from '@libs/contracts/models';
|
||||
|
||||
import {
|
||||
INodeIntegration,
|
||||
INodeIntegrationResult,
|
||||
|
|
@ -18,14 +20,21 @@ export class IntegrationsService {
|
|||
}
|
||||
}
|
||||
|
||||
public async sync(coreConfig: unknown): Promise<INodeIntegrationResult> {
|
||||
public async sync(
|
||||
integrationConfig: unknown | undefined,
|
||||
nodeMetadata: TNodeMetadata | undefined,
|
||||
): Promise<INodeIntegrationResult> {
|
||||
if (this.integrations.length === 0) return { error: null };
|
||||
if (!nodeMetadata) return { error: 'Node metadata is missing.' };
|
||||
|
||||
const errors: string[] = [];
|
||||
|
||||
for (const integration of this.integrations) {
|
||||
try {
|
||||
const { error } = await integration.sync(coreConfig);
|
||||
const { error } = await integration.sync(
|
||||
this.asRecord(integrationConfig),
|
||||
nodeMetadata,
|
||||
);
|
||||
|
||||
if (error) errors.push(`[${integration.name}] ${error}`);
|
||||
} catch (error) {
|
||||
|
|
@ -49,4 +58,10 @@ export class IntegrationsService {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private asRecord(value: unknown): Record<string, unknown> {
|
||||
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
||||
? (value as Record<string, unknown>)
|
||||
: {};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,7 +124,10 @@ export class XrayService implements OnApplicationBootstrap {
|
|||
this.isXrayStartedProccesing = true;
|
||||
|
||||
try {
|
||||
const integrations = await this.integrations.sync(body.xrayConfig);
|
||||
const integrations = await this.integrations.sync(
|
||||
body.xrayConfig.integrations,
|
||||
body.internals.metadata,
|
||||
);
|
||||
|
||||
if (integrations.error) {
|
||||
this.logger.error(`Failed to sync integrations: ${integrations.error}`);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue