11import { zValidator } from "@hono/zod-validator" ;
22import { Hono } from "hono" ;
33import { logger } from "hono/logger" ;
4- import type { ProxyConfig } from "redis-monorepo/packages/test-utils/lib/redis-proxy.ts" ;
4+ import type { Interceptor , ProxyConfig , ProxyStats , SendResult } from "redis-monorepo/packages/test-utils/lib/redis-proxy.ts" ;
55import { RedisProxy } from "redis-monorepo/packages/test-utils/lib/redis-proxy.ts" ;
66
77import {
@@ -10,21 +10,116 @@ import {
1010 getConfig ,
1111 paramSchema ,
1212 parseBuffer ,
13+ proxyConfigSchema ,
1314} from "./util.ts" ;
15+ import ProxyStore , { makeId } from "./proxy-store.ts" ;
16+
17+ const startNewProxy = ( config : ProxyConfig ) => {
18+ const proxy = new RedisProxy ( config ) ;
19+ proxy . start ( ) . catch ( console . error ) ;
20+ return proxy ;
21+ }
22+
23+ interface Mapping {
24+ from : {
25+ host : string ,
26+ port : number
27+ } ,
28+ to : {
29+ host : string ,
30+ port : number
31+ }
32+ }
33+
34+ const addressMapping = new Map < string , Mapping > ( ) ;
35+
36+ const setTransformers = ( addressMapping : Map < string , Mapping > , proxyStore : ProxyStore ) => {
37+ const interceptors = [ ] ;
38+ for ( const mapping of addressMapping . values ( ) ) {
39+ interceptors . push ( async ( data : Buffer , next : Interceptor ) => {
40+ const response = await next ( data ) ;
41+ // for example $9\r\n127.0.0.1\r\n:3000
42+ const from = `$${ mapping . from . host . length } \r\n${ mapping . from . host } \r\n:${ mapping . from . port } ` ;
43+ if ( response . includes ( from ) ) {
44+ const to = `$${ mapping . to . host . length } \r\n${ mapping . to . host } \r\n:${ mapping . to . port } ` ;
45+ return Buffer . from ( response . toString ( ) . replaceAll ( from , to ) ) ;
46+ }
47+ return response ;
48+ } ) ;
49+ }
50+ for ( const proxy of proxyStore . proxies ) {
51+ proxy . setInterceptors ( interceptors ) ;
52+ }
53+ }
54+
1455
1556export function createApp ( testConfig ?: ProxyConfig & { readonly apiPort ?: number } ) {
1657 const config = testConfig || getConfig ( ) ;
1758 const app = new Hono ( ) ;
1859 app . use ( logger ( ) ) ;
1960
20- const proxy = new RedisProxy ( config ) ;
21- proxy . start ( ) . catch ( console . error ) ;
61+ const proxyStore = new ProxyStore ( ) ;
62+ const nodeId = makeId ( config . targetHost , config . targetPort )
63+ proxyStore . add ( nodeId , startNewProxy ( config ) ) ;
64+ addressMapping . set ( nodeId , {
65+ from : {
66+ host : config . targetHost ,
67+ port : config . targetPort
68+ } ,
69+ to : {
70+ host : config . listenHost ?? '127.0.0.1' ,
71+ port : config . listenPort
72+ }
73+ } ) ;
74+ setTransformers ( addressMapping , proxyStore ) ;
75+
76+ app . post ( "/nodes" ,
77+ zValidator ( "json" , proxyConfigSchema ) ,
78+ async ( c ) => {
79+ const data = await c . req . json ( ) ;
80+ const cfg : ProxyConfig = { ...config , ...data } ;
81+ const nodeId = makeId ( cfg . targetHost , cfg . targetPort ) ;
82+ proxyStore . add ( nodeId , startNewProxy ( cfg ) ) ;
83+ addressMapping . set ( nodeId , {
84+ from : {
85+ host : cfg . targetHost ,
86+ port : cfg . targetPort
87+ } ,
88+ to : {
89+ host : cfg . listenHost ?? '127.0.0.1' ,
90+ port : cfg . listenPort
91+ }
92+ } ) ;
93+ setTransformers ( addressMapping , proxyStore ) ;
94+ return c . json ( { success : true , cfg } ) ;
95+ } ) ;
96+
97+ app . delete ( "/nodes/:id" , async ( c ) => {
98+ const nodeId = c . req . param ( "id" ) ;
99+ const success = await proxyStore . delete ( nodeId ) ;
100+ addressMapping . delete ( nodeId ) ;
101+ setTransformers ( addressMapping , proxyStore ) ;
102+ return c . json ( { success } ) ;
103+ } ) ;
104+
105+ app . get ( "/nodes" , ( c ) => {
106+ return c . json ( { ids : proxyStore . nodeIds } ) ;
107+ } ) ;
108+
22109 app . get ( "/stats" , ( c ) => {
23- return c . json ( proxy . getStats ( ) ) ;
110+ const response = proxyStore . entries . reduce ( ( acc , [ id , proxy ] ) => {
111+ acc [ id ] = proxy . getStats ( ) ;
112+ return acc ;
113+ } , { } as Record < string , ProxyStats > ) ;
114+ return c . json ( response ) ;
24115 } ) ;
25116
26117 app . get ( "/connections" , ( c ) => {
27- return c . json ( { connectionIds : proxy . getActiveConnectionIds ( ) } ) ;
118+ const response = proxyStore . entries . reduce ( ( acc , [ id , proxy ] ) => {
119+ acc [ id ] = proxy . getActiveConnectionIds ( ) ;
120+ return acc ;
121+ } , { } as Record < string , readonly string [ ] > )
122+ return c . json ( response ) ;
28123 } ) ;
29124
30125 app . post (
@@ -37,6 +132,14 @@ export function createApp(testConfig?: ProxyConfig & { readonly apiPort?: number
37132 const data = await c . req . text ( ) ;
38133
39134 const buffer = parseBuffer ( data , encoding ) ;
135+
136+ const proxy = proxyStore . getProxyByConnectionId ( connectionId ) ;
137+ if ( ! proxy ) return c . json ( {
138+ success : false ,
139+ error : 'Connection not found' ,
140+ connectionId
141+ } ) ;
142+
40143 const result = proxy . sendToClient ( connectionId , buffer ) ;
41144 return c . json ( result ) ;
42145 } ,
@@ -47,24 +150,35 @@ export function createApp(testConfig?: ProxyConfig & { readonly apiPort?: number
47150 const data = await c . req . text ( ) ;
48151
49152 const buffer = parseBuffer ( data , encoding ) ;
50- const results = proxy . sendToClients ( connectionIds , buffer ) ;
153+
154+ const results : SendResult [ ] = [ ] ;
155+ for ( const [ proxy , matchingConIds ] of proxyStore . getProxiesByConnectionIds ( connectionIds ) ) {
156+ results . push ( ...proxy . sendToClients ( matchingConIds , buffer ) ) ;
157+ }
51158 return c . json ( { results } ) ;
52159 } ) ;
53160
54161 app . post ( "/send-to-all-clients" , zValidator ( "query" , encodingSchema ) , async ( c ) => {
55162 const { encoding } = c . req . valid ( "query" ) ;
56163 const data = await c . req . text ( ) ;
57-
58164 const buffer = parseBuffer ( data , encoding ) ;
59- const results = proxy . sendToAllClients ( buffer ) ;
165+ const results : SendResult [ ] = [ ] ;
166+ for ( const proxy of proxyStore . proxies ) {
167+ results . push ( ...proxy . sendToAllClients ( buffer ) ) ;
168+ }
60169 return c . json ( { results } ) ;
61170 } ) ;
62171
63- app . delete ( "/connections/:id" , ( c ) => {
64- const connectionId = c . req . param ( "id" ) ;
172+ app . delete ( "/connections/:id" , ( c ) => {
173+ const connectionId = c . req . param ( "id" ) ;
174+ const proxy = proxyStore . getProxyByConnectionId ( connectionId ) ;
175+ if ( ! proxy ) return c . json ( {
176+ success : false ,
177+ connectionId
178+ } ) ;
65179 const success = proxy . closeConnection ( connectionId ) ;
66180 return c . json ( { success, connectionId } ) ;
67181 } ) ;
68182
69- return { app, proxy, config } ;
183+ return { app, proxy : proxyStore . proxies [ 0 ] ! , config } ;
70184}
0 commit comments