@gokiteam/goki-dev 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. package/README.md +478 -0
  2. package/bin/goki-dev.js +452 -0
  3. package/bin/mcp-server.js +16 -0
  4. package/bin/secrets-cli.js +302 -0
  5. package/cli/ComposeOverrideGenerator.js +226 -0
  6. package/cli/ComposeParser.js +73 -0
  7. package/cli/ConfigGenerator.js +304 -0
  8. package/cli/ConfigManager.js +46 -0
  9. package/cli/DatabaseManager.js +94 -0
  10. package/cli/DevToolsChecker.js +21 -0
  11. package/cli/DevToolsDir.js +66 -0
  12. package/cli/DevToolsManager.js +451 -0
  13. package/cli/DockerManager.js +138 -0
  14. package/cli/FunctionManager.js +95 -0
  15. package/cli/HttpProxyRewriter.js +91 -0
  16. package/cli/Logger.js +10 -0
  17. package/cli/McpConfigManager.js +123 -0
  18. package/cli/NgrokManager.js +431 -0
  19. package/cli/ProjectCLI.js +2322 -0
  20. package/cli/PubSubManager.js +129 -0
  21. package/cli/SnapshotManager.js +88 -0
  22. package/cli/UiFormatter.js +292 -0
  23. package/cli/WebhookUrlRewriter.js +32 -0
  24. package/cli/secrets/BiometricAuth.js +125 -0
  25. package/cli/secrets/SecretInjector.js +47 -0
  26. package/cli/secrets/SecretsConfig.js +141 -0
  27. package/cli/secrets/SecretsDoctor.js +384 -0
  28. package/cli/secrets/SecretsManager.js +255 -0
  29. package/client/dist/client.d.ts +332 -0
  30. package/client/dist/client.js +507 -0
  31. package/client/dist/helpers.d.ts +62 -0
  32. package/client/dist/helpers.js +122 -0
  33. package/client/dist/index.d.ts +59 -0
  34. package/client/dist/index.js +78 -0
  35. package/client/dist/package.json +1 -0
  36. package/client/dist/types.d.ts +280 -0
  37. package/client/dist/types.js +7 -0
  38. package/config.development +46 -0
  39. package/config.test +18 -0
  40. package/guidelines/CodingStyleGuideline.md +148 -0
  41. package/guidelines/CommentingGuideline.md +10 -0
  42. package/guidelines/HttpApiImplementationGuideline.md +137 -0
  43. package/guidelines/NamingGuideline.md +182 -0
  44. package/package.json +138 -0
  45. package/patterns/api/[collectionName]/Controllers.md +62 -0
  46. package/patterns/api/[collectionName]/Logic.md +154 -0
  47. package/patterns/api/[collectionName]/Permissions.md +81 -0
  48. package/patterns/api/[collectionName]/Router.md +83 -0
  49. package/patterns/api/[collectionName]/Schemas.md +197 -0
  50. package/patterns/configs/Patterns.md +7 -0
  51. package/patterns/enums/Patterns.md +24 -0
  52. package/patterns/errorHandling/Patterns.md +185 -0
  53. package/patterns/testing/Patterns.md +232 -0
  54. package/src/Server.js +238 -0
  55. package/src/api/dashboard/Controllers.js +9 -0
  56. package/src/api/dashboard/Logic.js +76 -0
  57. package/src/api/dashboard/Router.js +11 -0
  58. package/src/api/dashboard/Schemas.js +47 -0
  59. package/src/api/data/Controllers.js +26 -0
  60. package/src/api/data/Logic.js +188 -0
  61. package/src/api/data/Router.js +16 -0
  62. package/src/api/docker/Controllers.js +33 -0
  63. package/src/api/docker/Logic.js +268 -0
  64. package/src/api/docker/Router.js +15 -0
  65. package/src/api/docker/Schemas.js +80 -0
  66. package/src/api/docs/Controllers.js +15 -0
  67. package/src/api/docs/Logic.js +85 -0
  68. package/src/api/docs/Router.js +12 -0
  69. package/src/api/export/Controllers.js +30 -0
  70. package/src/api/export/Logic.js +143 -0
  71. package/src/api/export/Router.js +18 -0
  72. package/src/api/export/Schemas.js +104 -0
  73. package/src/api/firestore/Controllers.js +152 -0
  74. package/src/api/firestore/Logic.js +474 -0
  75. package/src/api/firestore/Router.js +23 -0
  76. package/src/api/functions/Controllers.js +261 -0
  77. package/src/api/functions/Logic.js +710 -0
  78. package/src/api/functions/Router.js +50 -0
  79. package/src/api/functions/Schemas.js +193 -0
  80. package/src/api/gateway/Controllers.js +72 -0
  81. package/src/api/gateway/Logic.js +74 -0
  82. package/src/api/gateway/Router.js +10 -0
  83. package/src/api/gateway/Schemas.js +19 -0
  84. package/src/api/health/Controllers.js +14 -0
  85. package/src/api/health/Logic.js +24 -0
  86. package/src/api/health/Router.js +12 -0
  87. package/src/api/httpTraffic/Controllers.js +29 -0
  88. package/src/api/httpTraffic/Logic.js +33 -0
  89. package/src/api/httpTraffic/Router.js +9 -0
  90. package/src/api/httpTraffic/Schemas.js +23 -0
  91. package/src/api/logging/Controllers.js +80 -0
  92. package/src/api/logging/Logic.js +461 -0
  93. package/src/api/logging/Router.js +24 -0
  94. package/src/api/logging/Schemas.js +43 -0
  95. package/src/api/mqtt/Controllers.js +17 -0
  96. package/src/api/mqtt/Logic.js +66 -0
  97. package/src/api/mqtt/Router.js +12 -0
  98. package/src/api/postgres/Controllers.js +97 -0
  99. package/src/api/postgres/Logic.js +221 -0
  100. package/src/api/postgres/Router.js +21 -0
  101. package/src/api/pubsub/Controllers.js +236 -0
  102. package/src/api/pubsub/Logic.js +732 -0
  103. package/src/api/pubsub/Router.js +41 -0
  104. package/src/api/pubsub/Schemas.js +355 -0
  105. package/src/api/redis/Controllers.js +63 -0
  106. package/src/api/redis/Logic.js +239 -0
  107. package/src/api/redis/Router.js +21 -0
  108. package/src/api/scheduler/Controllers.js +27 -0
  109. package/src/api/scheduler/Logic.js +49 -0
  110. package/src/api/scheduler/Router.js +16 -0
  111. package/src/api/services/Controllers.js +26 -0
  112. package/src/api/services/Logic.js +205 -0
  113. package/src/api/services/Router.js +14 -0
  114. package/src/api/services/Schemas.js +66 -0
  115. package/src/api/snapshots/Controllers.js +37 -0
  116. package/src/api/snapshots/Logic.js +797 -0
  117. package/src/api/snapshots/Router.js +15 -0
  118. package/src/api/snapshots/Schemas.js +23 -0
  119. package/src/api/webhooks/Controllers.js +49 -0
  120. package/src/api/webhooks/Logic.js +137 -0
  121. package/src/api/webhooks/Router.js +12 -0
  122. package/src/api/webhooks/Schemas.js +31 -0
  123. package/src/configs/Application.js +147 -0
  124. package/src/configs/Default.js +13 -0
  125. package/src/consumers/BlackboxLogsConsumer.js +235 -0
  126. package/src/consumers/DockerLogsConsumer.js +687 -0
  127. package/src/db/Tables.js +66 -0
  128. package/src/db/schemas/firestore.js +18 -0
  129. package/src/db/schemas/functions.js +65 -0
  130. package/src/db/schemas/httpTraffic.js +43 -0
  131. package/src/db/schemas/logging.js +74 -0
  132. package/src/db/schemas/migrations.js +64 -0
  133. package/src/db/schemas/mqtt.js +56 -0
  134. package/src/db/schemas/pubsub.js +90 -0
  135. package/src/db/schemas/pubsubRegistry.js +22 -0
  136. package/src/db/schemas/webhooks.js +28 -0
  137. package/src/emulation/awsiot/Controllers.js +91 -0
  138. package/src/emulation/awsiot/Logic.js +70 -0
  139. package/src/emulation/awsiot/Router.js +19 -0
  140. package/src/emulation/awsiot/Server.js +100 -0
  141. package/src/emulation/firestore/Server.js +136 -0
  142. package/src/emulation/logging/Controllers.js +212 -0
  143. package/src/emulation/logging/Logic.js +416 -0
  144. package/src/emulation/logging/Router.js +36 -0
  145. package/src/emulation/logging/Schemas.js +82 -0
  146. package/src/emulation/logging/Server.js +108 -0
  147. package/src/emulation/pubsub/Controllers.js +279 -0
  148. package/src/emulation/pubsub/DefaultTopics.js +162 -0
  149. package/src/emulation/pubsub/Logic.js +427 -0
  150. package/src/emulation/pubsub/README.md +309 -0
  151. package/src/emulation/pubsub/Router.js +33 -0
  152. package/src/emulation/pubsub/Server.js +104 -0
  153. package/src/emulation/pubsub/ShadowPoller.js +276 -0
  154. package/src/emulation/pubsub/ShadowSubscriptionManager.js +199 -0
  155. package/src/enums/ContainerNames.js +106 -0
  156. package/src/enums/ErrorReason.js +28 -0
  157. package/src/enums/FunctionStatuses.js +15 -0
  158. package/src/enums/FunctionTriggerTypes.js +15 -0
  159. package/src/enums/GatewayState.js +7 -0
  160. package/src/enums/ServiceNames.js +68 -0
  161. package/src/jobs/DatabaseMaintenance.js +184 -0
  162. package/src/jobs/MessageHistoryCleanup.js +152 -0
  163. package/src/mcp/ApiClient.js +25 -0
  164. package/src/mcp/Server.js +52 -0
  165. package/src/mcp/prompts/debugging.js +104 -0
  166. package/src/mcp/resources/platform.js +118 -0
  167. package/src/mcp/tools/data.js +84 -0
  168. package/src/mcp/tools/docker.js +166 -0
  169. package/src/mcp/tools/firestore.js +162 -0
  170. package/src/mcp/tools/functions.js +380 -0
  171. package/src/mcp/tools/httpTraffic.js +69 -0
  172. package/src/mcp/tools/logging.js +174 -0
  173. package/src/mcp/tools/mqtt.js +37 -0
  174. package/src/mcp/tools/postgres.js +130 -0
  175. package/src/mcp/tools/pubsub.js +316 -0
  176. package/src/mcp/tools/redis.js +146 -0
  177. package/src/mcp/tools/services.js +169 -0
  178. package/src/mcp/tools/snapshots.js +88 -0
  179. package/src/mcp/tools/webhooks.js +115 -0
  180. package/src/middleware/DevProxy.js +67 -0
  181. package/src/middleware/ErrorCatcher.js +35 -0
  182. package/src/middleware/HttpProxy.js +215 -0
  183. package/src/middleware/Reply.js +24 -0
  184. package/src/middleware/TraceId.js +9 -0
  185. package/src/middleware/WebhookProxy.js +234 -0
  186. package/src/protocols/mqtt/Broker.js +92 -0
  187. package/src/protocols/mqtt/Handlers.js +175 -0
  188. package/src/protocols/mqtt/PubSubBridge.js +162 -0
  189. package/src/protocols/mqtt/Server.js +116 -0
  190. package/src/runtime/FunctionRunner.js +179 -0
  191. package/src/services/AppGatewayService.js +582 -0
  192. package/src/singletons/FirestoreBroadcaster.js +367 -0
  193. package/src/singletons/FunctionTriggerDispatcher.js +456 -0
  194. package/src/singletons/FunctionsService.js +418 -0
  195. package/src/singletons/HttpProxy.js +224 -0
  196. package/src/singletons/LogBroadcaster.js +159 -0
  197. package/src/singletons/Logger.js +49 -0
  198. package/src/singletons/MemoryJsonStore.js +175 -0
  199. package/src/singletons/MessageBroadcaster.js +190 -0
  200. package/src/singletons/PostgresBroadcaster.js +367 -0
  201. package/src/singletons/PostgresClient.js +180 -0
  202. package/src/singletons/RedisClient.js +184 -0
  203. package/src/singletons/SqliteStore.js +480 -0
  204. package/src/singletons/TickService.js +151 -0
  205. package/src/singletons/WebhookProxy.js +223 -0
@@ -0,0 +1,50 @@
1
+ import KoaRouter from 'koa-router'
2
+ import { Controllers } from './Controllers.js'
3
+
4
+ export const Router = new KoaRouter({ prefix: '/v1/functions' })
5
+
6
+ // CRUD
7
+ Router.post('/create', Controllers.create)
8
+ Router.post('/list', Controllers.list)
9
+ Router.post('/details', Controllers.details)
10
+ Router.post('/update', Controllers.update)
11
+ Router.post('/delete', Controllers.delete)
12
+
13
+ // Lifecycle
14
+ Router.post('/start', Controllers.start)
15
+ Router.post('/stop', Controllers.stop)
16
+ Router.post('/restart', Controllers.restart)
17
+
18
+ // Source management
19
+ Router.post('/source/read', Controllers.readSource)
20
+ Router.post('/source/update', Controllers.updateSource)
21
+
22
+ // Dependencies management
23
+ Router.post('/dependencies/read', Controllers.readDependencies)
24
+ Router.post('/dependencies/update', Controllers.updateDependencies)
25
+
26
+ // File management
27
+ Router.post('/files/list', Controllers.listFiles)
28
+ Router.post('/files/read', Controllers.readFile)
29
+ Router.post('/files/write', Controllers.writeFile)
30
+ Router.post('/files/delete', Controllers.deleteFile)
31
+
32
+ // Invocation
33
+ Router.post('/invoke', Controllers.invoke)
34
+
35
+ // Invocation history
36
+ Router.post('/invocations/list', Controllers.listInvocations)
37
+ Router.post('/invocations/details', Controllers.invocationDetails)
38
+ Router.post('/invocations/clear', Controllers.clearInvocations)
39
+
40
+ // Stats
41
+ Router.post('/stats', Controllers.stats)
42
+
43
+ // SSE streams
44
+ Router.get('/logs/stream', Controllers.streamLogs)
45
+ Router.get('/invocations/stream', Controllers.streamInvocations)
46
+ Router.get('/files/stream', Controllers.streamFileChanges)
47
+
48
+ // HTTP trigger endpoint
49
+ Router.all('/call/:functionName', Controllers.callHttpFunction)
50
+ Router.all('/call/:functionName/(.*)', Controllers.callHttpFunction)
@@ -0,0 +1,193 @@
1
+ import Joi from 'joi'
2
+
3
+ export const Schemas = {
4
+ create: {
5
+ body: Joi.object({
6
+ name: Joi.string().pattern(/^[a-zA-Z0-9-_]+$/).required()
7
+ .description('Unique function name (alphanumeric, hyphens, underscores)'),
8
+ source: Joi.string().optional()
9
+ .description('Inline function source code (used when sourcePath is not provided)'),
10
+ sourcePath: Joi.string().optional()
11
+ .description('Absolute path to the function source directory'),
12
+ entryPoint: Joi.string().required()
13
+ .description('Name of the exported function to invoke'),
14
+ triggerType: Joi.string().valid('http', 'pubsub', 'firestore', 'scheduler').required()
15
+ .description('How the function is triggered'),
16
+ triggerConfig: Joi.object().optional().default({})
17
+ .description('Trigger-specific configuration (e.g. { topic: "myTopic" } for pubsub)'),
18
+ signatureType: Joi.string().valid('cloudevent', 'http').optional().default('cloudevent')
19
+ .description('Function signature type for the functions-framework'),
20
+ runtime: Joi.string().optional().default('nodejs20')
21
+ .description('Runtime identifier'),
22
+ environmentVariables: Joi.object().optional().default({})
23
+ .description('Environment variables to pass to the function process'),
24
+ timeoutSeconds: Joi.number().integer().min(1).max(540).optional().default(60)
25
+ .description('Maximum execution time in seconds'),
26
+ description: Joi.string().optional().allow('')
27
+ .description('Human-readable description of the function')
28
+ })
29
+ },
30
+
31
+ list: {
32
+ body: Joi.object({
33
+ filter: Joi.object({
34
+ triggerType: Joi.string().valid('http', 'pubsub', 'firestore', 'scheduler').optional(),
35
+ status: Joi.string().valid('stopped', 'starting', 'running', 'error').optional(),
36
+ enabled: Joi.boolean().optional()
37
+ }).optional(),
38
+ limit: Joi.number().integer().min(0).optional(),
39
+ offset: Joi.number().integer().min(0).optional()
40
+ })
41
+ },
42
+
43
+ details: {
44
+ body: Joi.object({
45
+ id: Joi.string().optional(),
46
+ name: Joi.string().optional()
47
+ }).or('id', 'name')
48
+ },
49
+
50
+ update: {
51
+ body: Joi.object({
52
+ id: Joi.string().optional(),
53
+ name: Joi.string().optional(),
54
+ description: Joi.string().optional().allow(''),
55
+ triggerConfig: Joi.object().optional(),
56
+ enabled: Joi.boolean().optional(),
57
+ environmentVariables: Joi.object().optional(),
58
+ timeoutSeconds: Joi.number().integer().min(1).max(540).optional(),
59
+ entryPoint: Joi.string().optional(),
60
+ signatureType: Joi.string().valid('cloudevent', 'http').optional()
61
+ }).or('id', 'name')
62
+ },
63
+
64
+ delete: {
65
+ body: Joi.object({
66
+ id: Joi.string().optional(),
67
+ name: Joi.string().optional()
68
+ }).or('id', 'name')
69
+ },
70
+
71
+ start: {
72
+ body: Joi.object({
73
+ id: Joi.string().optional(),
74
+ name: Joi.string().optional()
75
+ }).or('id', 'name')
76
+ },
77
+
78
+ stop: {
79
+ body: Joi.object({
80
+ id: Joi.string().optional(),
81
+ name: Joi.string().optional()
82
+ }).or('id', 'name')
83
+ },
84
+
85
+ restart: {
86
+ body: Joi.object({
87
+ id: Joi.string().optional(),
88
+ name: Joi.string().optional()
89
+ }).or('id', 'name')
90
+ },
91
+
92
+ readSource: {
93
+ body: Joi.object({
94
+ id: Joi.string().optional(),
95
+ name: Joi.string().optional()
96
+ }).or('id', 'name')
97
+ },
98
+
99
+ updateSource: {
100
+ body: Joi.object({
101
+ id: Joi.string().optional(),
102
+ name: Joi.string().optional(),
103
+ content: Joi.string().required()
104
+ .description('New source code content')
105
+ }).or('id', 'name')
106
+ },
107
+
108
+ readDependencies: {
109
+ body: Joi.object({
110
+ id: Joi.string().optional(),
111
+ name: Joi.string().optional()
112
+ }).or('id', 'name')
113
+ },
114
+
115
+ updateDependencies: {
116
+ body: Joi.object({
117
+ id: Joi.string().optional(),
118
+ name: Joi.string().optional(),
119
+ packageJson: Joi.string().required()
120
+ .description('Full package.json content as a JSON string'),
121
+ install: Joi.boolean().optional().default(true)
122
+ .description('Whether to run npm install after writing')
123
+ }).or('id', 'name')
124
+ },
125
+
126
+ listFiles: {
127
+ body: Joi.object({
128
+ id: Joi.string().optional(),
129
+ name: Joi.string().optional()
130
+ }).or('id', 'name')
131
+ },
132
+
133
+ readFile: {
134
+ body: Joi.object({
135
+ id: Joi.string().optional(),
136
+ name: Joi.string().optional(),
137
+ filePath: Joi.string().required()
138
+ .description('Relative file path within the function source directory')
139
+ }).or('id', 'name')
140
+ },
141
+
142
+ writeFile: {
143
+ body: Joi.object({
144
+ id: Joi.string().optional(),
145
+ name: Joi.string().optional(),
146
+ filePath: Joi.string().required()
147
+ .description('Relative file path within the function source directory'),
148
+ content: Joi.string().required()
149
+ .description('File content to write')
150
+ }).or('id', 'name')
151
+ },
152
+
153
+ deleteFile: {
154
+ body: Joi.object({
155
+ id: Joi.string().optional(),
156
+ name: Joi.string().optional(),
157
+ filePath: Joi.string().required()
158
+ .description('Relative file path within the function source directory')
159
+ }).or('id', 'name')
160
+ },
161
+
162
+ invoke: {
163
+ body: Joi.object({
164
+ id: Joi.string().optional(),
165
+ name: Joi.string().optional(),
166
+ payload: Joi.any().optional()
167
+ .description('Payload to pass to the function as CloudEvent data')
168
+ }).or('id', 'name')
169
+ },
170
+
171
+ listInvocations: {
172
+ body: Joi.object({
173
+ functionId: Joi.string().optional(),
174
+ functionName: Joi.string().optional(),
175
+ triggerType: Joi.string().optional(),
176
+ limit: Joi.number().integer().min(0).optional().default(50),
177
+ offset: Joi.number().integer().min(0).optional().default(0)
178
+ })
179
+ },
180
+
181
+ invocationDetails: {
182
+ body: Joi.object({
183
+ id: Joi.string().required()
184
+ })
185
+ },
186
+
187
+ clearInvocations: {
188
+ body: Joi.object({
189
+ functionId: Joi.string().optional()
190
+ .description('Clear invocations for a specific function, or all if omitted')
191
+ })
192
+ }
193
+ }
@@ -0,0 +1,72 @@
1
+ import { Logic } from './Logic.js'
2
+
3
+ export const Controllers = {
4
+ async start (ctx) {
5
+ const { traceId } = ctx.state
6
+ const result = await Logic.start({ traceId })
7
+ if (!result.success) {
8
+ ctx.status = 400
9
+ ctx.body = {
10
+ success: false,
11
+ status: 400,
12
+ message: result.message,
13
+ traceId: result.traceId
14
+ }
15
+ return
16
+ }
17
+ ctx.reply({
18
+ gateway: result.gateway,
19
+ traceId: result.traceId
20
+ })
21
+ },
22
+
23
+ async stop (ctx) {
24
+ const { traceId } = ctx.state
25
+ const result = await Logic.stop({ traceId })
26
+ if (!result.success) {
27
+ ctx.status = 400
28
+ ctx.body = {
29
+ success: false,
30
+ status: 400,
31
+ message: result.message,
32
+ traceId: result.traceId
33
+ }
34
+ return
35
+ }
36
+ ctx.reply({
37
+ message: result.message,
38
+ traceId: result.traceId
39
+ })
40
+ },
41
+
42
+ async status (ctx) {
43
+ const { traceId } = ctx.state
44
+ const result = Logic.status({ traceId })
45
+ ctx.reply(result)
46
+ },
47
+
48
+ async triggerScan (ctx) {
49
+ const { traceId } = ctx.state
50
+ const result = await Logic.triggerScan({ traceId })
51
+ if (!result.success) {
52
+ ctx.status = 400
53
+ ctx.body = {
54
+ success: false,
55
+ status: 400,
56
+ message: result.message,
57
+ traceId: result.traceId
58
+ }
59
+ return
60
+ }
61
+ ctx.reply({
62
+ message: result.message,
63
+ traceId: result.traceId
64
+ })
65
+ },
66
+
67
+ async healthCheck (ctx) {
68
+ const { traceId } = ctx.state
69
+ const result = await Logic.healthCheck({ traceId })
70
+ ctx.reply(result)
71
+ }
72
+ }
@@ -0,0 +1,74 @@
1
+ import { AppGatewayService } from '../../services/AppGatewayService.js'
2
+
3
+ export const Logic = {
4
+ async start (params) {
5
+ const { traceId } = params
6
+ const result = await AppGatewayService.start()
7
+ // Service returns { success, message, gateway? }
8
+ if (!result.success) {
9
+ return {
10
+ success: false,
11
+ message: result.message,
12
+ traceId
13
+ }
14
+ }
15
+ return {
16
+ success: true,
17
+ gateway: result.gateway,
18
+ traceId
19
+ }
20
+ },
21
+
22
+ async stop (params) {
23
+ const { traceId } = params
24
+ const result = await AppGatewayService.stop()
25
+ if (!result.success) {
26
+ return {
27
+ success: false,
28
+ message: result.message,
29
+ traceId
30
+ }
31
+ }
32
+ return {
33
+ success: true,
34
+ message: result.message,
35
+ traceId
36
+ }
37
+ },
38
+
39
+ status (params) {
40
+ const { traceId } = params
41
+ const status = AppGatewayService.getStatus()
42
+ // Return status directly, no extra wrapping
43
+ return {
44
+ ...status,
45
+ traceId
46
+ }
47
+ },
48
+
49
+ async triggerScan (params) {
50
+ const { traceId } = params
51
+ const result = await AppGatewayService.triggerScan()
52
+ if (!result.success) {
53
+ return {
54
+ success: false,
55
+ message: result.message,
56
+ traceId
57
+ }
58
+ }
59
+ return {
60
+ success: true,
61
+ message: result.message,
62
+ traceId
63
+ }
64
+ },
65
+
66
+ async healthCheck (params) {
67
+ const { traceId } = params
68
+ const health = await AppGatewayService.checkDependencyHealth()
69
+ return {
70
+ ...health,
71
+ traceId
72
+ }
73
+ }
74
+ }
@@ -0,0 +1,10 @@
1
+ import KoaRouter from 'koa-router'
2
+ import { Controllers } from './Controllers.js'
3
+
4
+ export const Router = new KoaRouter({ prefix: '/v1/gateway' })
5
+
6
+ Router.post('/start', Controllers.start)
7
+ Router.post('/stop', Controllers.stop)
8
+ Router.post('/status', Controllers.status)
9
+ Router.post('/triggerScan', Controllers.triggerScan)
10
+ Router.post('/healthCheck', Controllers.healthCheck)
@@ -0,0 +1,19 @@
1
+ import Joi from 'joi'
2
+
3
+ export const Schemas = {
4
+ start: {
5
+ body: Joi.object({}).optional()
6
+ },
7
+ stop: {
8
+ body: Joi.object({}).optional()
9
+ },
10
+ status: {
11
+ body: Joi.object({}).optional()
12
+ },
13
+ triggerScan: {
14
+ body: Joi.object({}).optional()
15
+ },
16
+ healthCheck: {
17
+ body: Joi.object({}).optional()
18
+ }
19
+ }
@@ -0,0 +1,14 @@
1
+ import { Logic } from './Logic.js'
2
+
3
+ export const Controllers = {
4
+ async liveness (ctx) {
5
+ const { traceId } = ctx.state
6
+ const result = await Logic.liveness({ traceId })
7
+ ctx.reply(result)
8
+ },
9
+ async readiness (ctx) {
10
+ const { traceId } = ctx.state
11
+ const result = await Logic.readiness({ traceId })
12
+ ctx.reply(result)
13
+ }
14
+ }
@@ -0,0 +1,24 @@
1
+ import { Application } from '../../configs/Application.js'
2
+ import { SqliteStore } from '../../singletons/SqliteStore.js'
3
+
4
+ export const Logic = {
5
+ async liveness (params) {
6
+ const { traceId } = params
7
+ return {
8
+ status: 'ok',
9
+ timestamp: new Date().toISOString(),
10
+ traceId
11
+ }
12
+ },
13
+ async readiness (params) {
14
+ const { traceId } = params
15
+ const isStoreInitialized = SqliteStore.isInitialized
16
+ return {
17
+ status: isStoreInitialized ? 'ready' : 'not_ready',
18
+ timestamp: new Date().toISOString(),
19
+ environment: Application.environment,
20
+ runId: Application.runId,
21
+ traceId
22
+ }
23
+ }
24
+ }
@@ -0,0 +1,12 @@
1
+ import KoaRouter from 'koa-router'
2
+ import { Controllers } from './Controllers.js'
3
+
4
+ const Router = new KoaRouter()
5
+ const v1 = new KoaRouter({ prefix: '/v1/health' })
6
+
7
+ v1.get('/liveness', Controllers.liveness)
8
+ v1.get('/readiness', Controllers.readiness)
9
+
10
+ Router.use(v1.routes())
11
+
12
+ export { Router }
@@ -0,0 +1,29 @@
1
+ import { Logic } from './Logic.js'
2
+
3
+ export const Controllers = {
4
+ async list (ctx) {
5
+ const { traceId } = ctx.state
6
+ const { filter, limit, offset } = ctx.request.body
7
+ const result = Logic.list({ filter, limit, offset, traceId })
8
+ ctx.reply(result)
9
+ },
10
+
11
+ async details (ctx) {
12
+ const { traceId } = ctx.state
13
+ const { id } = ctx.request.body
14
+ const result = Logic.details({ id, traceId })
15
+ ctx.reply(result)
16
+ },
17
+
18
+ async clear (ctx) {
19
+ const { traceId } = ctx.state
20
+ const result = Logic.clear({ traceId })
21
+ ctx.reply(result)
22
+ },
23
+
24
+ async stats (ctx) {
25
+ const { traceId } = ctx.state
26
+ const result = Logic.stats({ traceId })
27
+ ctx.reply(result)
28
+ }
29
+ }
@@ -0,0 +1,33 @@
1
+ import { HttpProxy } from '../../singletons/HttpProxy.js'
2
+
3
+ export const Logic = {
4
+ list (params) {
5
+ const { filter, limit = 50, offset = 0, traceId } = params
6
+ const { data, total } = HttpProxy.listTraffic({ filter, limit, offset })
7
+ return { entries: data, total, limit, offset, traceId }
8
+ },
9
+
10
+ details (params) {
11
+ const { id, traceId } = params
12
+ if (!id) {
13
+ return { success: false, message: 'id is required', traceId }
14
+ }
15
+ const entry = HttpProxy.getTrafficEntry(id)
16
+ if (!entry) {
17
+ return { success: false, message: 'Traffic entry not found', traceId }
18
+ }
19
+ return { entry, traceId }
20
+ },
21
+
22
+ clear (params) {
23
+ const { traceId } = params
24
+ HttpProxy.clearTraffic()
25
+ return { message: 'All HTTP traffic records cleared', traceId }
26
+ },
27
+
28
+ stats (params) {
29
+ const { traceId } = params
30
+ const stats = HttpProxy.getStats()
31
+ return { ...stats, traceId }
32
+ }
33
+ }
@@ -0,0 +1,9 @@
1
+ import KoaRouter from 'koa-router'
2
+ import { Controllers } from './Controllers.js'
3
+
4
+ export const Router = new KoaRouter({ prefix: '/v1/http-traffic' })
5
+
6
+ Router.post('/list', Controllers.list)
7
+ Router.post('/details', Controllers.details)
8
+ Router.post('/clear', Controllers.clear)
9
+ Router.post('/stats', Controllers.stats)
@@ -0,0 +1,23 @@
1
+ import Joi from 'joi'
2
+
3
+ export const Schemas = {
4
+ list: Joi.object({
5
+ filter: Joi.object({
6
+ method: Joi.string().valid('GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'),
7
+ targetHost: Joi.string(),
8
+ sourceService: Joi.string(),
9
+ statusCode: Joi.number().integer(),
10
+ traceId: Joi.string()
11
+ }).optional(),
12
+ limit: Joi.number().integer().min(1).max(500).default(50),
13
+ offset: Joi.number().integer().min(0).default(0)
14
+ }),
15
+
16
+ details: Joi.object({
17
+ id: Joi.string().required()
18
+ }),
19
+
20
+ clear: Joi.object({}),
21
+
22
+ stats: Joi.object({})
23
+ }
@@ -0,0 +1,80 @@
1
+ import { Logic } from './Logic.js'
2
+ import { LogBroadcaster } from '../../singletons/LogBroadcaster.js'
3
+
4
+ export const Controllers = {
5
+ async listServices (ctx) {
6
+ const { traceId } = ctx.state
7
+ const result = await Logic.listServices({ traceId })
8
+ ctx.reply(result)
9
+ },
10
+ async listEntries (ctx) {
11
+ const { traceId } = ctx.state
12
+ const { filter, page, compact } = ctx.request.body
13
+ const result = await Logic.listEntries({ filter, page, compact, traceId })
14
+ ctx.reply(result)
15
+ },
16
+ async getEntriesById (ctx) {
17
+ const { traceId } = ctx.state
18
+ const { ids } = ctx.request.body
19
+ const result = await Logic.getEntriesById({ ids, traceId })
20
+ ctx.reply(result)
21
+ },
22
+ async clearEntries (ctx) {
23
+ const { traceId } = ctx.state
24
+ const result = await Logic.clearEntries({ traceId })
25
+ ctx.reply(result)
26
+ },
27
+ async exportEntries (ctx) {
28
+ const { traceId } = ctx.state
29
+ const result = await Logic.exportEntries({ traceId })
30
+ ctx.reply(result)
31
+ },
32
+ async getTrace (ctx) {
33
+ const traceId = ctx.params.traceId
34
+ const result = await Logic.getTrace({ traceId })
35
+ ctx.reply(result)
36
+ },
37
+ async waitForLog (ctx) {
38
+ const { traceId } = ctx.state
39
+ const { filter, timeout } = ctx.request.body
40
+ const result = await Logic.waitForLog({ filter, timeout, traceId })
41
+ ctx.reply(result)
42
+ },
43
+ async assertNoErrors (ctx) {
44
+ const { traceId } = ctx.state
45
+ const { traceId: targetTraceId, sinceTimestamp } = ctx.request.body
46
+ const result = await Logic.assertNoErrors({ targetTraceId, sinceTimestamp, traceId })
47
+ ctx.reply(result)
48
+ },
49
+ async streamLogs (ctx) {
50
+ const connectionId = ctx.query.connectionId || `log-${Date.now()}`
51
+ // Parse optional filter from query param (JSON-encoded MongoDB-style filter)
52
+ let filter = null
53
+ if (ctx.query.filter) {
54
+ try {
55
+ filter = JSON.parse(ctx.query.filter)
56
+ if (Object.keys(filter).length === 0) filter = null
57
+ } catch {
58
+ // Ignore malformed filter — stream all entries
59
+ }
60
+ }
61
+ ctx.set({
62
+ 'Content-Type': 'text/event-stream',
63
+ 'Cache-Control': 'no-cache',
64
+ Connection: 'keep-alive',
65
+ 'X-Accel-Buffering': 'no'
66
+ })
67
+ ctx.status = 200
68
+ ctx.respond = false
69
+ ctx.res.write(`event: connected\ndata: ${JSON.stringify({ connected: true, connectionId })}\n\n`)
70
+ LogBroadcaster.addClient(connectionId, ctx, filter)
71
+ ctx.req.on('close', () => {
72
+ LogBroadcaster.removeClient(connectionId)
73
+ })
74
+ },
75
+ async getQueueStats (ctx) {
76
+ const { traceId } = ctx.state
77
+ const result = await Logic.getQueueStats({ traceId })
78
+ ctx.reply(result)
79
+ }
80
+ }