aura-node — API
AppModule
Section titled “AppModule”@Module({ imports: [ ConfigModule.forRoot({ isGlobal: true, envFilePath: '.env',
validate: (config) => validateEnvConfig<Env>(configSchema, config), }), XtlsSdkNestjsModule.forRootAsync({ imports: [ConfigModule], inject: [ConfigService], useFactory: (configService: ConfigService) => ({ ip: configService.getOrThrow<string>('XTLS_IP'), port: configService.getOrThrow<string>('XTLS_PORT'), }), }), SupervisordNestjsModule.forRootAsync({ imports: [], inject: [], useFactory: () => ({ host: 'http://127.0.0.1:61002', options: { username: 'aura', password: 'JNBFdjbEDbDBFbJH', }, }), }), AuraNodeModules, InternalModule, JwtModule.registerAsync(getJWTConfig()), ], providers: [JwtStrategy], exports: [],})export class AppModule {}ConfigSchema
Section titled “ConfigSchema”src/common/config/app-config/config.schema.ts
export type ConfigSchema = z.infer<typeof configSchema>;src/common/config/app-config/config.schema.ts
export class Env extends createZodDto(configSchema) {}HttpExceptionWithErrorCodeType
Section titled “HttpExceptionWithErrorCodeType”src/common/exception/http-exeception-with-error-code.type.ts
export class HttpExceptionWithErrorCodeType extends HttpException { errorCode: string;
constructor(message: string, errorCode: string, statusCode: number) { super(message, statusCode); this.errorCode = errorCode; }}HttpExceptionFilter
Section titled “HttpExceptionFilter”src/common/exception/httpException.filter.ts
@Catch(HttpExceptionWithErrorCodeType, ZodValidationException)export class HttpExceptionFilter implements ExceptionFilter { private readonly logger = new Logger(HttpExceptionFilter.name);
catch(exception: HttpExceptionWithErrorCodeType | ZodValidationException, host: ArgumentsHost) { const ctx = host.switchToHttp(); const response = ctx.getResponse<Response>(); const request = ctx.getRequest<Request>(); const status = exception?.getStatus();
let errorMessage: string | string[]; let errorCode = 'E000'; if (status === HttpStatus.FORBIDDEN) { errorMessage = 'Forbidden'; } else { errorMessage = exception.message; if (exception instanceof HttpExceptionWithErrorCodeType) { errorCode = exception.errorCode; } }
if (exception instanceof ZodValidationException) { this.logger.error(exception.getResponse()); response.status(status).json(exception.getResponse()); } else { this.logger.error({ timestamp: new Date().toISOString(), code: errorCode, path: request.url, message: errorMessage, }); response.status(status).json({ timestamp: new Date().toISOString(), path: request.url, message: errorMessage, errorCode, }); } }}NotFoundExceptionFilter
Section titled “NotFoundExceptionFilter”src/common/exception/not-found-exception.filter.ts
@Catch(NotFoundException)export class NotFoundExceptionFilter implements ExceptionFilter { catch(exception: NotFoundException, host: ArgumentsHost) { const ctx = host.switchToHttp(); const response = ctx.getResponse();
response.socket?.destroy(); }}JwtDefaultGuard
Section titled “JwtDefaultGuard”src/common/guards/jwt-guards/def-jwt-guard.ts
export class JwtDefaultGuard extends AuthGuard('registeredUserJWT') { private readonly logger = new Logger(JwtDefaultGuard.name);
handleRequest(err: any, user: any, info: any, context: ExecutionContext) { if (info instanceof Error || err || !user) { const response = context.switchToHttp().getResponse();
this.logger.debug(`${JSON.stringify(context.switchToHttp().getRequest().headers)}`);
this.logger.error( `Неверный SSL_CERT или JWT! Запрос отклонён. URL: ${context.switchToHttp().getRequest().url}, IP: ${context.switchToHttp().getRequest().ip}`, );
response.socket?.destroy(); throw new UnauthorizedException('Не авторизован'); } return user; }}JwtStrategy
Section titled “JwtStrategy”src/common/guards/jwt-guards/strategies/validate-token.ts
@Injectable()export class JwtStrategy extends PassportStrategy(Strategy, 'registeredUserJWT') { constructor(configService: ConfigService) { super({ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), ignoreExpiration: false, secretOrKey: configService.getOrThrow<string>('JWT_PUBLIC_KEY'), algorithms: ['RS256'], }); }
async validate(JWTPrivatePayload: unknown): Promise<unknown> { return JWTPrivatePayload; }}PortGuard
Section titled “PortGuard”src/common/guards/request-port-guard/request-port.guard.ts
@Injectable()export class PortGuard implements CanActivate { constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean { const port = this.reflector.get<number>('port', context.getHandler()) || this.reflector.get<number>('port', context.getClass()); if (!port) return true;
const req = context.switchToHttp().getRequest(); const addressInfo = req.socket.server.address();
const isInternal = addressInfo?.port === port; const isInternalIp = addressInfo?.address === '127.0.0.1';
if (!isInternal || !isInternalIp) { const response = context.switchToHttp().getResponse(); response.socket?.destroy(); throw new UnauthorizedException('Не авторизован'); } return isInternal; }}canParseJSON
Section titled “canParseJSON”src/common/helpers/can-parse-json.ts
export function canParseJSON(jsonString: string): booleanerrorHandlerWithNull
Section titled “errorHandlerWithNull”src/common/helpers/error-handler-with-null.helper.ts
export function errorHandlerWithNull<T>(response: ICommandResponse<T>): null | TerrorHandler
Section titled “errorHandler”src/common/helpers/error-handler.helper.ts
export function errorHandler<T>(response: ICommandResponse<T>): TICommandResponse
Section titled “ICommandResponse”src/common/types/command-response.type.ts
export interface ICommandResponse<T> { code?: string; isOk: boolean; message?: string; response?: T;}IConverter
Section titled “IConverter”src/common/types/converter.interface.ts
export interface IConverter<T, U> { fromEntitiesToPrismaModels(entities: T[]): U[];
fromEntityToPrismaModel(entity: T): U;
fromPrismaModelsToEntities(prismaModels: U[]): T[];
fromPrismaModelToEntity(prismaModel: U): T;}export interface ICrud<ENTITY> { create: (entity: ENTITY) => Promise<ENTITY>; deleteByUUID: (uuid: string) => Promise<boolean>; findByCriteria: (entity: Partial<ENTITY>) => Promise<ENTITY[]>; findByUUID: (uuid: string) => Promise<ENTITY | null>; update: (entity: ENTITY) => Promise<ENTITY | null>;}parseNodePayload
Section titled “parseNodePayload”src/common/utils/decode-node-payload/decode-node-payload.ts
export function parseNodePayload(): INodePayloadparseNodePayloadFromConfigService
Section titled “parseNodePayloadFromConfigService”src/common/utils/decode-node-payload/decode-node-payload.ts
export function parseNodePayloadFromConfigService(sslCert: string): INodePayloadgetStartMessage
Section titled “getStartMessage”src/common/utils/get-start-message.ts
export async function getStartMessage( appPort: number, internalPort: number, app: INestApplication,)ISystemStats
Section titled “ISystemStats”src/common/utils/get-system-stats/get-system-stats.interface.ts
export interface ISystemStats { cpuCores: number; cpuModel: string; memoryTotal: string;}getSystemStats
Section titled “getSystemStats”src/common/utils/get-system-stats/get-system-stats.ts
export async function getSystemStats(): Promise<ISystemStats>isDevelopment
Section titled “isDevelopment”src/common/utils/is-development.ts
export function isDevelopment(): booleanexport function sleep(ms: number): Promise<void>validateEnvConfig
Section titled “validateEnvConfig”src/common/utils/validate-env-config.ts
export function validateEnvConfig<T>(schema: z.ZodType, config: Record<string, unknown>): TAuraNodeModules
Section titled “AuraNodeModules”src/modules/aura-node.modules.ts
@Module({ imports: [StatsModule, XrayModule, HandlerModule, VisionModule], providers: [],})export class AuraNodeModules {}AddUserRequestDto
Section titled “AddUserRequestDto”src/modules/handler/dtos/add-user.dto.ts
export class AddUserRequestDto extends createZodDto(AddUserCommand.RequestSchema) {}AddUserResponseDto
Section titled “AddUserResponseDto”src/modules/handler/dtos/add-user.dto.ts
export class AddUserResponseDto extends createZodDto(AddUserCommand.ResponseSchema) {}GetInboundUsersCountRequestDto
Section titled “GetInboundUsersCountRequestDto”src/modules/handler/dtos/get-inbound-users-count.dto.ts
export class GetInboundUsersCountRequestDto extends createZodDto( GetInboundUsersCountCommand.RequestSchema,) {}GetInboundUsersCountResponseDto
Section titled “GetInboundUsersCountResponseDto”src/modules/handler/dtos/get-inbound-users-count.dto.ts
export class GetInboundUsersCountResponseDto extends createZodDto( GetInboundUsersCountCommand.ResponseSchema,) {}GetInboundUsersRequestDto
Section titled “GetInboundUsersRequestDto”src/modules/handler/dtos/get-inbound-users.dto.ts
export class GetInboundUsersRequestDto extends createZodDto(GetInboundUsersCommand.RequestSchema) {}GetInboundUsersResponseDto
Section titled “GetInboundUsersResponseDto”src/modules/handler/dtos/get-inbound-users.dto.ts
export class GetInboundUsersResponseDto extends createZodDto( GetInboundUsersCommand.ResponseSchema,) {}RemoveUserRequestDto
Section titled “RemoveUserRequestDto”src/modules/handler/dtos/remove-user.dto.ts
export class RemoveUserRequestDto extends createZodDto(RemoveUserCommand.RequestSchema) {}RemoveUserResponseDto
Section titled “RemoveUserResponseDto”src/modules/handler/dtos/remove-user.dto.ts
export class RemoveUserResponseDto extends createZodDto(RemoveUserCommand.ResponseSchema) {}HandlerController
Section titled “HandlerController”src/modules/handler/handler.controller.ts
@UseFilters(HttpExceptionFilter)@UseGuards(JwtDefaultGuard)@Controller(HANDLER_CONTROLLER)export class HandlerController { constructor(private readonly handlerService: HandlerService) {}
@Post(HANDLER_ROUTES.ADD_USER) public async addUser(@Body() body: AddUserRequestDto): Promise<AddUserResponseDto> { const response = await this.handlerService.addUser(body); const data = errorHandler(response);
return { response: data, }; }
@Post(HANDLER_ROUTES.GET_INBOUND_USERS) public async getInboundUsers( @Body() body: GetInboundUsersRequestDto, ): Promise<GetInboundUsersResponseDto> { const response = await this.handlerService.getInboundUsers(body.tag); const data = errorHandler(response);
return { response: data, }; }
@Post(HANDLER_ROUTES.REMOVE_USER) public async removeUser(@Body() body: RemoveUserRequestDto): Promise<RemoveUserResponseDto> { const response = await this.handlerService.removeUser(body); const data = errorHandler(response);
return { response: data, }; }
@Post(HANDLER_ROUTES.GET_INBOUND_USERS_COUNT) public async getInboundUsersCount( @Body() body: GetInboundUsersCountRequestDto, ): Promise<GetInboundUsersCountResponseDto> { const response = await this.handlerService.getInboundUsersCount(body.tag); const data = errorHandler(response);
return { response: data, }; }}HandlerModule
Section titled “HandlerModule”src/modules/handler/handler.module.ts
@Module({ imports: [XrayModule], controllers: [HandlerController], providers: [HandlerService],})export class HandlerModule {}TAddUserRequest
Section titled “TAddUserRequest”src/modules/handler/interfaces/add-user.interface.ts
export interface TAddUserRequest { data: Array< | { cipherType: CipherType; ivCheck: boolean; level: number; password: string; tag: string; type: 'shadowsocks'; username: string; } | { flow: '' | 'xtls-rprx-vision'; level: number; tag: string; type: 'vless'; username: string; uuid: string; } | { http_password: string; http_username: string; level: number; tag: string; type: 'http'; username: string; } | { key: string; level: number; tag: string; type: 'shadowsocks2022'; username: string; } | { level: number; password: string; tag: string; type: 'trojan'; username: string; } | { level: number; socks_password: string; socks_username: string; tag: string; type: 'socks'; username: string; } >;}IRemoveUserRequest
Section titled “IRemoveUserRequest”src/modules/handler/interfaces/remove-user.interface.ts
export interface IRemoveUserRequest { username: string;}AddUserResponseModel
Section titled “AddUserResponseModel”src/modules/handler/models/add-user.response.model.ts
export class AddUserResponseModel { success: boolean; error: null | string;
constructor(success: boolean, error: null | string) { this.success = success; this.error = error; }}GetInboundUsersCountResponseModel
Section titled “GetInboundUsersCountResponseModel”src/modules/handler/models/get-inbound-users-count.response.model.ts
export class GetInboundUsersCountResponseModel { public count: number;
constructor(count: number) { this.count = count; }}GetInboundUsersResponseModel
Section titled “GetInboundUsersResponseModel”src/modules/handler/models/get-inbound-users.response.model.ts
export class GetInboundUsersResponseModel { public users: IInboundUser[];
constructor(users: IInboundUser[]) { this.users = users.map((user) => ({ username: user.username, level: user.level, protocol: user.protocol, })); }}IInboundUser
Section titled “IInboundUser”src/modules/handler/models/interfaces/inbound-user.interface.ts
export interface IInboundUser { level: number; protocol: string; username: string;}RemoveUserResponseModel
Section titled “RemoveUserResponseModel”src/modules/handler/models/remove-user.response.model.ts
export class RemoveUserResponseModel { success: boolean; error: null | string;
constructor(success: boolean, error: null | string) { this.success = success; this.error = error; }}InternalController
Section titled “InternalController”src/modules/internal/internal.controller.ts
@OnPort(XRAY_INTERNAL_API_PORT)@UseFilters(HttpExceptionFilter)@UseGuards(PortGuard)@Controller(XRAY_INTERNAL_API_CONTROLLER)export class InternalController { constructor(private readonly internalService: InternalService) {}
@Get(XRAY_INTERNAL_API_PATH) public async getXrayConfig(): Promise<Record<string, unknown>> { try { const config = await this.internalService.getXrayConfig();
return config; } catch { return {}; } }}InternalModule
Section titled “InternalModule”src/modules/internal/internal.module.ts
@Global()@Module({ imports: [], providers: [InternalService], controllers: [InternalController], exports: [InternalService],})export class InternalModule {}InternalService
Section titled “InternalService”src/modules/internal/internal.service.ts
@Injectable()export class InternalService { private readonly logger = new Logger(InternalService.name); private xrayConfig: null | Record<string, unknown> = null;
constructor() {}
public async getXrayConfig(): Promise<Record<string, unknown>> { if (!this.xrayConfig) { return {}; }
return this.xrayConfig; }
public setXrayConfig(config: Record<string, unknown>): void { this.logger.debug('Установка новой конфигурации Xray'); this.xrayConfig = config; }}