@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,19 @@
1
+ import KoaRouter from 'koa-router'
2
+ import { publish } from './Controllers.js'
3
+
4
+ const router = new KoaRouter()
5
+
6
+ /**
7
+ * AWS IoT Core HTTPS API emulation routes
8
+ *
9
+ * Mimics: https://docs.aws.amazon.com/iot/latest/developerguide/mqtt-publish-over-https.html
10
+ *
11
+ * POST /topics/:topicName?qos=1
12
+ * - Publishes a message to the specified MQTT topic
13
+ * - Binary payload in request body
14
+ * - Returns empty 200 response
15
+ */
16
+
17
+ router.post('/topics/:topicName', publish)
18
+
19
+ export { router as Router }
@@ -0,0 +1,100 @@
1
+ import Koa from 'koa'
2
+ import bodyParser from 'koa-bodyparser'
3
+ import { Application } from '../../configs/Application.js'
4
+ import { Logger } from '../../singletons/Logger.js'
5
+ import { Router } from './Router.js'
6
+
7
+ const { environment, runId, ports } = Application
8
+
9
+ /**
10
+ * Start AWS IoT Core HTTPS API emulation server
11
+ *
12
+ * Mimics AWS IoT Core HTTPS publishing API
13
+ * POST /topics/{topicName}?qos=1
14
+ *
15
+ * This allows backend services (device-simulator, device-native) to publish
16
+ * messages to the MQTT broker using the same HTTPS API they use in production.
17
+ */
18
+ export const startAwsIotServer = async () => {
19
+ try {
20
+ const app = new Koa()
21
+
22
+ // Error handler
23
+ app.use(async (ctx, next) => {
24
+ try {
25
+ await next()
26
+ } catch (error) {
27
+ Logger.log({
28
+ level: 'error',
29
+ message: 'AWS IoT server error',
30
+ data: { error: error.message, stack: error.stack, path: ctx.path }
31
+ })
32
+ ctx.status = error.status || 500
33
+ ctx.body = {
34
+ error: {
35
+ code: ctx.status,
36
+ message: error.message || 'Internal server error',
37
+ status: 'INTERNAL'
38
+ }
39
+ }
40
+ }
41
+ })
42
+
43
+ // Body parser with raw binary support
44
+ app.use(bodyParser({
45
+ enableTypes: ['json', 'form', 'text'],
46
+ extendTypes: {
47
+ text: ['application/octet-stream']
48
+ },
49
+ // Parse binary payloads as Buffer
50
+ onerror: (err, ctx) => {
51
+ Logger.log({
52
+ level: 'error',
53
+ message: 'Body parser error',
54
+ data: { error: err.message, path: ctx.path }
55
+ })
56
+ ctx.throw(422, 'Body parse error')
57
+ }
58
+ }))
59
+
60
+ // Custom middleware to handle raw binary payloads
61
+ app.use(async (ctx, next) => {
62
+ if (ctx.request.is('application/octet-stream') || ctx.path.startsWith('/topics/')) {
63
+ // Read raw body as Buffer
64
+ const chunks = []
65
+ for await (const chunk of ctx.req) {
66
+ chunks.push(chunk)
67
+ }
68
+ ctx.request.body = Buffer.concat(chunks)
69
+ }
70
+ await next()
71
+ })
72
+
73
+ // Register routes
74
+ app.use(Router.routes())
75
+ app.use(Router.allowedMethods())
76
+
77
+ // Start server
78
+ const server = app.listen(ports.awsIot, () => {
79
+ Logger.log({
80
+ level: 'info',
81
+ message: 'AWS IoT Core HTTPS API emulation server started',
82
+ data: {
83
+ port: ports.awsIot,
84
+ environment,
85
+ runId,
86
+ endpoint: `http://localhost:${ports.awsIot}/topics/:topicName?qos=1`
87
+ }
88
+ })
89
+ })
90
+
91
+ return server
92
+ } catch (error) {
93
+ Logger.log({
94
+ level: 'error',
95
+ message: 'Failed to start AWS IoT server',
96
+ data: { error: error.message, stack: error.stack }
97
+ })
98
+ throw error
99
+ }
100
+ }
@@ -0,0 +1,136 @@
1
+ import { spawn } from 'child_process'
2
+ import { Application } from '../../configs/Application.js'
3
+ import { Logger } from '../../singletons/Logger.js'
4
+ import { SqliteStore } from '../../singletons/SqliteStore.js'
5
+
6
+ class FirestoreEmulatorClass {
7
+ constructor () {
8
+ this.process = null
9
+ this.isRunning = false
10
+ this.port = Application.ports.firestore || 8080
11
+ this.projectId = Application.firestore.projectId
12
+ }
13
+
14
+ async start () {
15
+ if (this.isRunning) {
16
+ Logger.log({
17
+ level: 'warn',
18
+ message: 'Firestore emulator already running'
19
+ })
20
+ return
21
+ }
22
+ this.registerStorageCollections()
23
+ return new Promise((resolve, reject) => {
24
+ this.process = spawn('firebase', [
25
+ 'emulators:start',
26
+ '--only', 'firestore',
27
+ '--project', this.projectId
28
+ ], {
29
+ env: {
30
+ ...process.env,
31
+ FIRESTORE_EMULATOR_PORT: this.port
32
+ },
33
+ stdio: ['ignore', 'pipe', 'pipe']
34
+ })
35
+ const startupTimeout = setTimeout(() => {
36
+ if (!this.isRunning) {
37
+ Logger.log({
38
+ level: 'error',
39
+ message: 'Firestore emulator failed to start within 30 seconds'
40
+ })
41
+ reject(new Error('Firestore emulator startup timeout'))
42
+ }
43
+ }, 30000)
44
+ this.process.stdout.on('data', (data) => {
45
+ const message = data.toString()
46
+ if (message.includes('Firestore Emulator running') || message.includes('All emulators ready')) {
47
+ clearTimeout(startupTimeout)
48
+ this.isRunning = true
49
+ Logger.log({
50
+ level: 'info',
51
+ message: `Firestore emulator started on port ${this.port}`,
52
+ data: { port: this.port, projectId: this.projectId }
53
+ })
54
+ resolve()
55
+ }
56
+ })
57
+ this.process.stderr.on('data', (data) => {
58
+ const error = data.toString()
59
+ if (!this.isRunning && error.includes('error')) {
60
+ Logger.log({
61
+ level: 'error',
62
+ message: 'Firestore emulator error during startup',
63
+ data: { error }
64
+ })
65
+ }
66
+ })
67
+ this.process.on('error', (error) => {
68
+ clearTimeout(startupTimeout)
69
+ Logger.log({
70
+ level: 'error',
71
+ message: 'Failed to start Firestore emulator process',
72
+ data: { error: error.message }
73
+ })
74
+ reject(error)
75
+ })
76
+ this.process.on('exit', (code) => {
77
+ this.isRunning = false
78
+ if (code !== 0 && code !== null) {
79
+ Logger.log({
80
+ level: 'error',
81
+ message: 'Firestore emulator process exited unexpectedly',
82
+ data: { exitCode: code }
83
+ })
84
+ }
85
+ })
86
+ })
87
+ }
88
+
89
+ registerStorageCollections () {
90
+ // No-op for SQLite - tables created at DB initialization
91
+ Logger.log({
92
+ level: 'debug',
93
+ message: 'Firestore storage tables ready (SQLite)',
94
+ data: { tables: ['firestore_metadata'] }
95
+ })
96
+ }
97
+
98
+ async stop () {
99
+ if (!this.isRunning || !this.process) {
100
+ Logger.log({
101
+ level: 'warn',
102
+ message: 'Firestore emulator not running'
103
+ })
104
+ return
105
+ }
106
+ return new Promise((resolve) => {
107
+ this.process.on('close', () => {
108
+ this.isRunning = false
109
+ Logger.log({
110
+ level: 'info',
111
+ message: 'Firestore emulator stopped'
112
+ })
113
+ resolve()
114
+ })
115
+ this.process.kill('SIGTERM')
116
+ setTimeout(() => {
117
+ if (this.process && !this.process.killed) {
118
+ this.process.kill('SIGKILL')
119
+ resolve()
120
+ }
121
+ }, 5000)
122
+ })
123
+ }
124
+
125
+ getStatus () {
126
+ return {
127
+ running: this.isRunning,
128
+ port: this.port,
129
+ projectId: this.projectId
130
+ }
131
+ }
132
+ }
133
+
134
+ export const FirestoreEmulator = new FirestoreEmulatorClass()
135
+ export const start = () => FirestoreEmulator.start()
136
+ export const stop = () => FirestoreEmulator.stop()
@@ -0,0 +1,212 @@
1
+ import { Logic } from './Logic.js'
2
+ import {
3
+ WriteEntriesRequestSchema,
4
+ ListEntriesRequestSchema,
5
+ DeleteLogRequestSchema,
6
+ ListLogsRequestSchema
7
+ } from './Schemas.js'
8
+ import { Logger } from '../../singletons/Logger.js'
9
+
10
+ export const Controllers = {
11
+ /**
12
+ * POST /v2/entries:write
13
+ * Write log entries
14
+ */
15
+ async writeEntries (ctx) {
16
+ const { traceId } = ctx.state
17
+
18
+ // Validate request body
19
+ const { error, value } = WriteEntriesRequestSchema.validate(ctx.request.body)
20
+ if (error) {
21
+ ctx.status = 400
22
+ ctx.body = {
23
+ error: {
24
+ code: 400,
25
+ message: 'Invalid request',
26
+ status: 'INVALID_ARGUMENT',
27
+ details: error.details.map(d => d.message)
28
+ }
29
+ }
30
+ return
31
+ }
32
+
33
+ try {
34
+ const result = await Logic.writeEntries({
35
+ traceId,
36
+ ...value
37
+ })
38
+
39
+ ctx.status = 200
40
+ ctx.body = result
41
+ } catch (error) {
42
+ Logger.log({
43
+ level: 'error',
44
+ message: 'Failed to write entries',
45
+ data: { traceId, error: error.message, stack: error.stack }
46
+ })
47
+
48
+ ctx.status = 500
49
+ ctx.body = {
50
+ error: {
51
+ code: 500,
52
+ message: 'Internal server error',
53
+ status: 'INTERNAL'
54
+ }
55
+ }
56
+ }
57
+ },
58
+
59
+ /**
60
+ * POST /v2/entries:list
61
+ * List log entries
62
+ */
63
+ async listEntries (ctx) {
64
+ const { traceId } = ctx.state
65
+
66
+ // Validate request body
67
+ const { error, value } = ListEntriesRequestSchema.validate(ctx.request.body)
68
+ if (error) {
69
+ ctx.status = 400
70
+ ctx.body = {
71
+ error: {
72
+ code: 400,
73
+ message: 'Invalid request',
74
+ status: 'INVALID_ARGUMENT',
75
+ details: error.details.map(d => d.message)
76
+ }
77
+ }
78
+ return
79
+ }
80
+
81
+ try {
82
+ const result = await Logic.listEntries({
83
+ traceId,
84
+ ...value
85
+ })
86
+
87
+ ctx.status = 200
88
+ ctx.body = result
89
+ } catch (error) {
90
+ Logger.log({
91
+ level: 'error',
92
+ message: 'Failed to list entries',
93
+ data: { traceId, error: error.message, stack: error.stack }
94
+ })
95
+
96
+ ctx.status = 500
97
+ ctx.body = {
98
+ error: {
99
+ code: 500,
100
+ message: 'Internal server error',
101
+ status: 'INTERNAL'
102
+ }
103
+ }
104
+ }
105
+ },
106
+
107
+ /**
108
+ * GET /v2/projects/{project}/logs
109
+ * List all logs for a project
110
+ */
111
+ async listLogs (ctx) {
112
+ const { traceId } = ctx.state
113
+ const { project } = ctx.params
114
+ const { pageSize, pageToken } = ctx.query
115
+
116
+ // Validate request
117
+ const { error, value } = ListLogsRequestSchema.validate({
118
+ project,
119
+ pageSize: pageSize ? parseInt(pageSize) : undefined,
120
+ pageToken
121
+ })
122
+ if (error) {
123
+ ctx.status = 400
124
+ ctx.body = {
125
+ error: {
126
+ code: 400,
127
+ message: 'Invalid request',
128
+ status: 'INVALID_ARGUMENT',
129
+ details: error.details.map(d => d.message)
130
+ }
131
+ }
132
+ return
133
+ }
134
+
135
+ try {
136
+ const result = await Logic.listLogs({
137
+ traceId,
138
+ ...value
139
+ })
140
+
141
+ ctx.status = 200
142
+ ctx.body = result
143
+ } catch (error) {
144
+ Logger.log({
145
+ level: 'error',
146
+ message: 'Failed to list logs',
147
+ data: { traceId, error: error.message, stack: error.stack }
148
+ })
149
+
150
+ ctx.status = 500
151
+ ctx.body = {
152
+ error: {
153
+ code: 500,
154
+ message: 'Internal server error',
155
+ status: 'INTERNAL'
156
+ }
157
+ }
158
+ }
159
+ },
160
+
161
+ /**
162
+ * DELETE /v2/projects/{project}/logs/{logId}
163
+ * Delete a log
164
+ */
165
+ async deleteLog (ctx) {
166
+ const { traceId } = ctx.state
167
+ const { project, logId } = ctx.params
168
+
169
+ // Validate request
170
+ const { error, value } = DeleteLogRequestSchema.validate({
171
+ project,
172
+ logId: decodeURIComponent(logId) // Log IDs may be URL-encoded
173
+ })
174
+ if (error) {
175
+ ctx.status = 400
176
+ ctx.body = {
177
+ error: {
178
+ code: 400,
179
+ message: 'Invalid request',
180
+ status: 'INVALID_ARGUMENT',
181
+ details: error.details.map(d => d.message)
182
+ }
183
+ }
184
+ return
185
+ }
186
+
187
+ try {
188
+ const result = await Logic.deleteLog({
189
+ traceId,
190
+ ...value
191
+ })
192
+
193
+ ctx.status = 200
194
+ ctx.body = result
195
+ } catch (error) {
196
+ Logger.log({
197
+ level: 'error',
198
+ message: 'Failed to delete log',
199
+ data: { traceId, error: error.message, stack: error.stack }
200
+ })
201
+
202
+ ctx.status = 500
203
+ ctx.body = {
204
+ error: {
205
+ code: 500,
206
+ message: 'Internal server error',
207
+ status: 'INTERNAL'
208
+ }
209
+ }
210
+ }
211
+ }
212
+ }