aura-node — API
AppModule
Заголовок раздела «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
Заголовок раздела «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
Заголовок раздела «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
Заголовок раздела «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
Заголовок раздела «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
Заголовок раздела «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
Заголовок раздела «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
Заголовок раздела «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
Заголовок раздела «canParseJSON»src/common/helpers/can-parse-json.ts
export function canParseJSON(jsonString: string): booleanerrorHandlerWithNull
Заголовок раздела «errorHandlerWithNull»src/common/helpers/error-handler-with-null.helper.ts
export function errorHandlerWithNull<T>(response: ICommandResponse<T>): null | TerrorHandler
Заголовок раздела «errorHandler»src/common/helpers/error-handler.helper.ts
export function errorHandler<T>(response: ICommandResponse<T>): TICommandResponse
Заголовок раздела «ICommandResponse»src/common/types/command-response.type.ts
export interface ICommandResponse<T> { code?: string; isOk: boolean; message?: string; response?: T;}IConverter
Заголовок раздела «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
Заголовок раздела «parseNodePayload»src/common/utils/decode-node-payload/decode-node-payload.ts
export function parseNodePayload(): INodePayloadparseNodePayloadFromConfigService
Заголовок раздела «parseNodePayloadFromConfigService»src/common/utils/decode-node-payload/decode-node-payload.ts
export function parseNodePayloadFromConfigService(sslCert: string): INodePayloadgetStartMessage
Заголовок раздела «getStartMessage»src/common/utils/get-start-message.ts
export async function getStartMessage( appPort: number, internalPort: number, app: INestApplication,)ISystemStats
Заголовок раздела «ISystemStats»src/common/utils/get-system-stats/get-system-stats.interface.ts
export interface ISystemStats { cpuCores: number; cpuModel: string; memoryTotal: string;}getSystemStats
Заголовок раздела «getSystemStats»src/common/utils/get-system-stats/get-system-stats.ts
export async function getSystemStats(): Promise<ISystemStats>isDevelopment
Заголовок раздела «isDevelopment»src/common/utils/is-development.ts
export function isDevelopment(): booleanexport function sleep(ms: number): Promise<void>validateEnvConfig
Заголовок раздела «validateEnvConfig»src/common/utils/validate-env-config.ts
export function validateEnvConfig<T>(schema: z.ZodType, config: Record<string, unknown>): TAuraNodeModules
Заголовок раздела «AuraNodeModules»src/modules/aura-node.modules.ts
@Module({ imports: [StatsModule, XrayModule, HandlerModule, VisionModule], providers: [],})export class AuraNodeModules {}AddUserRequestDto
Заголовок раздела «AddUserRequestDto»src/modules/handler/dtos/add-user.dto.ts
export class AddUserRequestDto extends createZodDto(AddUserCommand.RequestSchema) {}AddUserResponseDto
Заголовок раздела «AddUserResponseDto»src/modules/handler/dtos/add-user.dto.ts
export class AddUserResponseDto extends createZodDto(AddUserCommand.ResponseSchema) {}GetInboundUsersCountRequestDto
Заголовок раздела «GetInboundUsersCountRequestDto»src/modules/handler/dtos/get-inbound-users-count.dto.ts
export class GetInboundUsersCountRequestDto extends createZodDto( GetInboundUsersCountCommand.RequestSchema,) {}GetInboundUsersCountResponseDto
Заголовок раздела «GetInboundUsersCountResponseDto»src/modules/handler/dtos/get-inbound-users-count.dto.ts
export class GetInboundUsersCountResponseDto extends createZodDto( GetInboundUsersCountCommand.ResponseSchema,) {}GetInboundUsersRequestDto
Заголовок раздела «GetInboundUsersRequestDto»src/modules/handler/dtos/get-inbound-users.dto.ts
export class GetInboundUsersRequestDto extends createZodDto(GetInboundUsersCommand.RequestSchema) {}GetInboundUsersResponseDto
Заголовок раздела «GetInboundUsersResponseDto»src/modules/handler/dtos/get-inbound-users.dto.ts
export class GetInboundUsersResponseDto extends createZodDto( GetInboundUsersCommand.ResponseSchema,) {}RemoveUserRequestDto
Заголовок раздела «RemoveUserRequestDto»src/modules/handler/dtos/remove-user.dto.ts
export class RemoveUserRequestDto extends createZodDto(RemoveUserCommand.RequestSchema) {}RemoveUserResponseDto
Заголовок раздела «RemoveUserResponseDto»src/modules/handler/dtos/remove-user.dto.ts
export class RemoveUserResponseDto extends createZodDto(RemoveUserCommand.ResponseSchema) {}HandlerController
Заголовок раздела «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
Заголовок раздела «HandlerModule»src/modules/handler/handler.module.ts
@Module({ imports: [XrayModule], controllers: [HandlerController], providers: [HandlerService],})export class HandlerModule {}TAddUserRequest
Заголовок раздела «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
Заголовок раздела «IRemoveUserRequest»src/modules/handler/interfaces/remove-user.interface.ts
export interface IRemoveUserRequest { username: string;}AddUserResponseModel
Заголовок раздела «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
Заголовок раздела «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
Заголовок раздела «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
Заголовок раздела «IInboundUser»src/modules/handler/models/interfaces/inbound-user.interface.ts
export interface IInboundUser { level: number; protocol: string; username: string;}RemoveUserResponseModel
Заголовок раздела «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
Заголовок раздела «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
Заголовок раздела «InternalModule»src/modules/internal/internal.module.ts
@Global()@Module({ imports: [], providers: [InternalService], controllers: [InternalController], exports: [InternalService],})export class InternalModule {}InternalService
Заголовок раздела «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; }}