@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,151 @@
1
+ // TickService - Publishes one-minute tick messages to Pub/Sub
2
+ // Simple toggle service for simulating Cloud Scheduler behavior
3
+
4
+ import { Application } from '../configs/Application.js'
5
+ import { Logger } from './Logger.js'
6
+
7
+ const { pubsub } = Application
8
+ const PUBSUB_API = `http://${pubsub.emulatorHost}:${pubsub.emulatorPort}`
9
+ const PROJECT_ID = pubsub.projectId
10
+
11
+ class TickServiceClass {
12
+ constructor () {
13
+ this.interval = null
14
+ this.running = false
15
+ this.topic = 'systemOneMinuteTick'
16
+ this.lastTickAt = null
17
+ this.tickCount = 0
18
+ }
19
+
20
+ async start () {
21
+ if (this.running) {
22
+ return { success: false, message: 'Already running' }
23
+ }
24
+ this.running = true
25
+ this.tickCount = 0
26
+ // Ensure topic exists
27
+ await this.ensureTopic()
28
+ // Publish immediately, then every minute
29
+ await this.publishTick()
30
+ this.interval = setInterval(() => this.publishTick(), 60000)
31
+ Logger.log({
32
+ level: 'info',
33
+ message: 'Tick service started',
34
+ data: { topic: this.topic, intervalMs: 60000 }
35
+ })
36
+ return { success: true, message: 'Tick service started' }
37
+ }
38
+
39
+ stop () {
40
+ if (!this.running) {
41
+ return { success: false, message: 'Not running' }
42
+ }
43
+ clearInterval(this.interval)
44
+ this.interval = null
45
+ this.running = false
46
+ Logger.log({
47
+ level: 'info',
48
+ message: 'Tick service stopped',
49
+ data: { topic: this.topic, totalTicks: this.tickCount }
50
+ })
51
+ return { success: true, message: 'Tick service stopped' }
52
+ }
53
+
54
+ async ensureTopic () {
55
+ try {
56
+ // Check if topic exists
57
+ const response = await fetch(`${PUBSUB_API}/v1/projects/${PROJECT_ID}/topics/${this.topic}`)
58
+ if (response.status === 404) {
59
+ // Create topic
60
+ await fetch(`${PUBSUB_API}/v1/projects/${PROJECT_ID}/topics/${this.topic}`, {
61
+ method: 'PUT',
62
+ headers: { 'Content-Type': 'application/json' },
63
+ body: JSON.stringify({})
64
+ })
65
+ // Create shadow subscription for message capture
66
+ await fetch(`${PUBSUB_API}/v1/projects/${PROJECT_ID}/subscriptions/${this.topic}-devtools-shadow`, {
67
+ method: 'PUT',
68
+ headers: { 'Content-Type': 'application/json' },
69
+ body: JSON.stringify({ topic: `projects/${PROJECT_ID}/topics/${this.topic}` })
70
+ })
71
+ Logger.log({
72
+ level: 'info',
73
+ message: 'Created tick topic and shadow subscription',
74
+ data: { topic: this.topic }
75
+ })
76
+ }
77
+ } catch (error) {
78
+ Logger.log({
79
+ level: 'error',
80
+ message: 'Failed to ensure tick topic',
81
+ data: { error: error.message }
82
+ })
83
+ }
84
+ }
85
+
86
+ generateUuid () {
87
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
88
+ const r = Math.random() * 16 | 0
89
+ const v = c === 'x' ? r : (r & 0x3 | 0x8)
90
+ return v.toString(16)
91
+ })
92
+ }
93
+
94
+ async publishTick () {
95
+ try {
96
+ const now = new Date()
97
+ const traceId = this.generateUuid()
98
+ const tickPayload = {
99
+ type: 'systemOneMinuteTick',
100
+ tickNumber: this.tickCount + 1,
101
+ timestamp: now.toISOString()
102
+ }
103
+ const messageData = Buffer.from(JSON.stringify(tickPayload)).toString('base64')
104
+ const response = await fetch(`${PUBSUB_API}/v1/projects/${PROJECT_ID}/topics/${this.topic}:publish`, {
105
+ method: 'POST',
106
+ headers: { 'Content-Type': 'application/json' },
107
+ body: JSON.stringify({
108
+ messages: [{
109
+ data: messageData,
110
+ attributes: {
111
+ traceId
112
+ }
113
+ }]
114
+ })
115
+ })
116
+ if (response.ok) {
117
+ this.lastTickAt = now
118
+ this.tickCount++
119
+ Logger.log({
120
+ level: 'debug',
121
+ message: 'Published tick message',
122
+ data: { topic: this.topic, tickNumber: this.tickCount }
123
+ })
124
+ } else {
125
+ const error = await response.json()
126
+ Logger.log({
127
+ level: 'error',
128
+ message: 'Failed to publish tick',
129
+ data: { error: error.error?.message || response.status }
130
+ })
131
+ }
132
+ } catch (error) {
133
+ Logger.log({
134
+ level: 'error',
135
+ message: 'Failed to publish tick',
136
+ data: { error: error.message }
137
+ })
138
+ }
139
+ }
140
+
141
+ getStatus () {
142
+ return {
143
+ running: this.running,
144
+ topic: this.topic,
145
+ lastTickAt: this.lastTickAt?.toISOString() || null,
146
+ tickCount: this.tickCount
147
+ }
148
+ }
149
+ }
150
+
151
+ export const TickService = new TickServiceClass()
@@ -0,0 +1,223 @@
1
+ import httpProxy from 'http-proxy'
2
+ import { v4 as uuidv4 } from 'uuid'
3
+ import { SqliteStore } from './SqliteStore.js'
4
+ import { Logger } from './Logger.js'
5
+ import { LogBroadcaster } from './LogBroadcaster.js'
6
+ import { WEBHOOK_ROUTES, WEBHOOK_SETTINGS } from '../db/Tables.js'
7
+ import { ServiceNames } from '../enums/ServiceNames.js'
8
+
9
+ class WebhookProxyClass {
10
+ constructor () {
11
+ this.routes = new Map()
12
+ this.proxy = null
13
+ }
14
+
15
+ initialize () {
16
+ this.proxy = httpProxy.createProxyServer({
17
+ changeOrigin: true,
18
+ xfwd: true,
19
+ proxyTimeout: 30000
20
+ })
21
+ this.proxy.on('error', (err, req, res) => {
22
+ Logger.log({
23
+ level: 'error',
24
+ message: 'Webhook proxy error',
25
+ data: { error: err.message, url: req.url, method: req.method }
26
+ })
27
+ })
28
+ this.loadRoutes()
29
+ Logger.log({
30
+ level: 'info',
31
+ message: 'WebhookProxy initialized',
32
+ data: { routeCount: this.routes.size }
33
+ })
34
+ }
35
+
36
+ loadRoutes () {
37
+ const { data } = SqliteStore.list(WEBHOOK_ROUTES)
38
+ for (const route of data) {
39
+ this.routes.set(route.prefix, {
40
+ target: route.target,
41
+ pathRewrite: route.pathRewrite,
42
+ description: route.description,
43
+ enabled: !!route.enabled
44
+ })
45
+ }
46
+ }
47
+
48
+ register (config) {
49
+ const { prefix, target, pathRewrite, description } = config
50
+ const existing = SqliteStore.get(WEBHOOK_ROUTES, prefix, 'prefix')
51
+ if (existing) {
52
+ SqliteStore.update(WEBHOOK_ROUTES, prefix, {
53
+ target,
54
+ pathRewrite: pathRewrite || null,
55
+ description: description || null,
56
+ updatedAt: Math.floor(Date.now() / 1000)
57
+ }, 'prefix')
58
+ } else {
59
+ SqliteStore.create(WEBHOOK_ROUTES, {
60
+ prefix,
61
+ target,
62
+ pathRewrite: pathRewrite || null,
63
+ description: description || null,
64
+ enabled: true
65
+ })
66
+ }
67
+ this.routes.set(prefix, {
68
+ target,
69
+ pathRewrite: pathRewrite || null,
70
+ description: description || null,
71
+ enabled: true
72
+ })
73
+ Logger.log({
74
+ level: 'info',
75
+ message: `Webhook route registered: ${prefix} -> ${target}${pathRewrite || ''}`,
76
+ data: { prefix, target, pathRewrite }
77
+ })
78
+ }
79
+
80
+ remove (prefix) {
81
+ try {
82
+ SqliteStore.delete(WEBHOOK_ROUTES, prefix, 'prefix')
83
+ } catch {
84
+ // Ignore if not found
85
+ }
86
+ this.routes.delete(prefix)
87
+ Logger.log({
88
+ level: 'info',
89
+ message: `Webhook route removed: ${prefix}`,
90
+ data: { prefix }
91
+ })
92
+ }
93
+
94
+ getRoute (prefix) {
95
+ return this.routes.get(prefix)
96
+ }
97
+
98
+ listRoutes () {
99
+ return SqliteStore.list(WEBHOOK_ROUTES, { orderBy: 'created_at DESC' })
100
+ }
101
+
102
+ resolveTarget (path) {
103
+ const proxyMatch = path.match(/^\/v1\/webhooks\/proxy(\/.*)$/)
104
+ if (!proxyMatch) return null
105
+ const pathAfterProxy = proxyMatch[1]
106
+ // Try to find route by matching pathRewrite against the path after /proxy/
107
+ for (const [prefix, route] of this.routes.entries()) {
108
+ if (!route.enabled) continue
109
+ if (route.pathRewrite && pathAfterProxy.startsWith(route.pathRewrite)) {
110
+ const remainingPath = pathAfterProxy.substring(route.pathRewrite.length)
111
+ return {
112
+ prefix,
113
+ target: route.target,
114
+ path: route.pathRewrite + remainingPath,
115
+ fullUrl: route.target + route.pathRewrite + remainingPath
116
+ }
117
+ }
118
+ }
119
+ // Fallback: try prefix-based lookup for backward compatibility
120
+ const prefixMatch = pathAfterProxy.match(/^\/([^/]+)(\/.*)?$/)
121
+ if (prefixMatch) {
122
+ const prefix = prefixMatch[1]
123
+ const remainingPath = prefixMatch[2] || ''
124
+ const route = this.routes.get(prefix)
125
+ if (route && route.enabled) {
126
+ const targetPath = route.pathRewrite
127
+ ? route.pathRewrite + remainingPath
128
+ : remainingPath || '/'
129
+ return {
130
+ prefix,
131
+ target: route.target,
132
+ path: targetPath,
133
+ fullUrl: route.target + targetPath
134
+ }
135
+ }
136
+ }
137
+ return null
138
+ }
139
+
140
+ logRequest (entry) {
141
+ const {
142
+ prefix, method, originalPath, targetUrl,
143
+ statusCode, responseTimeMs, error,
144
+ queryParams, requestBody, responseBody
145
+ } = entry
146
+ const now = new Date().toISOString()
147
+ const severity = error || (statusCode && statusCode >= 400) ? 'ERROR' : 'INFO'
148
+ const statusText = error ? `ERR ${error}` : `${statusCode}`
149
+ const textPayload = `${method} ${originalPath} → ${statusText} (${responseTimeMs}ms)`
150
+ const jsonPayload = {
151
+ prefix,
152
+ method,
153
+ originalPath,
154
+ targetUrl,
155
+ statusCode,
156
+ responseTimeMs,
157
+ error: error || null,
158
+ ...(queryParams && { queryParams }),
159
+ ...(requestBody && { requestBody }),
160
+ ...(responseBody && { responseBody })
161
+ }
162
+ const logEntry = {
163
+ _id: uuidv4(),
164
+ logName: 'projects/dev-tools/logs/webhook',
165
+ source: ServiceNames.WEBHOOK_PROXY,
166
+ serviceName: ServiceNames.WEBHOOK_PROXY,
167
+ severity,
168
+ textPayload,
169
+ jsonPayload,
170
+ timestamp: now,
171
+ receiveTimestamp: now,
172
+ insertId: uuidv4(),
173
+ createdAt: now
174
+ }
175
+ try {
176
+ SqliteStore.create('logging_entries', logEntry)
177
+ } catch (err) {
178
+ Logger.log({ level: 'error', message: 'Failed to store webhook log', data: { error: err.message } })
179
+ }
180
+ LogBroadcaster.broadcast(logEntry)
181
+ // Update request count on route
182
+ try {
183
+ const route = SqliteStore.get(WEBHOOK_ROUTES, prefix, 'prefix')
184
+ if (route) {
185
+ SqliteStore.update(WEBHOOK_ROUTES, prefix, {
186
+ requestCount: (route.requestCount || 0) + 1,
187
+ lastRequestAt: Math.floor(Date.now() / 1000)
188
+ }, 'prefix')
189
+ }
190
+ } catch {
191
+ // Non-critical
192
+ }
193
+ }
194
+
195
+ // Settings helpers for ngrok domain etc.
196
+ getSetting (key) {
197
+ const row = SqliteStore.get(WEBHOOK_SETTINGS, key, 'key')
198
+ return row?.value || null
199
+ }
200
+
201
+ setSetting (key, value) {
202
+ const existing = SqliteStore.get(WEBHOOK_SETTINGS, key, 'key')
203
+ if (existing) {
204
+ SqliteStore.update(WEBHOOK_SETTINGS, key, {
205
+ value,
206
+ updatedAt: Math.floor(Date.now() / 1000)
207
+ }, 'key')
208
+ } else {
209
+ SqliteStore.create(WEBHOOK_SETTINGS, { key, value })
210
+ }
211
+ }
212
+
213
+ getSettings () {
214
+ const { data } = SqliteStore.list(WEBHOOK_SETTINGS)
215
+ const settings = {}
216
+ for (const row of data) {
217
+ settings[row.key] = row.value
218
+ }
219
+ return settings
220
+ }
221
+ }
222
+
223
+ export const WebhookProxy = new WebhookProxyClass()