@stacksjs/ts-cloud 0.5.35 → 0.6.1

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 (77) hide show
  1. package/dist/aws/cloudwatch.d.ts +2 -0
  2. package/dist/aws/xray.d.ts +45 -0
  3. package/dist/bin/cli.js +718 -701
  4. package/dist/deploy/dashboard-database.d.ts +25 -0
  5. package/dist/deploy/dashboard-operations.d.ts +7 -0
  6. package/dist/deploy/firewall-config-editor.d.ts +15 -0
  7. package/dist/deploy/firewall-config-editor.test.d.ts +1 -0
  8. package/dist/deploy/index.d.ts +1 -0
  9. package/dist/deploy/management-dashboard.d.ts +26 -3
  10. package/dist/deploy/serverless-operations.d.ts +150 -0
  11. package/dist/deploy/serverless-operations.test.d.ts +1 -0
  12. package/dist/deploy/site-config-editor.d.ts +6 -0
  13. package/dist/deploy/ssh-config-editor.d.ts +1 -0
  14. package/dist/deploy/terminal-session.d.ts +23 -0
  15. package/dist/index.d.ts +2 -2
  16. package/dist/index.js +1003 -33
  17. package/dist/ui/index.html +4 -4
  18. package/dist/ui/server/actions.html +34 -5
  19. package/dist/ui/server/activity.html +1 -1
  20. package/dist/ui/server/backups.html +4 -4
  21. package/dist/ui/server/database.html +27 -5
  22. package/dist/ui/server/deployments.html +4 -4
  23. package/dist/ui/server/diagnostics.html +1 -1
  24. package/dist/ui/server/firewall.html +1233 -0
  25. package/dist/ui/server/logs.html +4 -4
  26. package/dist/ui/server/metrics.html +1 -1
  27. package/dist/ui/server/security.html +1 -1
  28. package/dist/ui/server/services.html +4 -4
  29. package/dist/ui/server/sites.html +24 -5
  30. package/dist/ui/server/ssh-keys.html +4 -4
  31. package/dist/ui/server/terminal.html +1191 -0
  32. package/dist/ui/server/workers.html +4 -4
  33. package/dist/ui/serverless/alarms.html +1236 -0
  34. package/dist/ui/serverless/assets.html +53 -1
  35. package/dist/ui/serverless/cost.html +1 -1
  36. package/dist/ui/serverless/data.html +58 -1
  37. package/dist/ui/serverless/deployments.html +53 -1
  38. package/dist/ui/serverless/firewall.html +1 -1
  39. package/dist/ui/serverless/functions.html +63 -1
  40. package/dist/ui/serverless/logs.html +4 -4
  41. package/dist/ui/serverless/metrics.html +1 -1
  42. package/dist/ui/serverless/queues.html +94 -1
  43. package/dist/ui/serverless/scheduler.html +55 -1
  44. package/dist/ui/serverless/secrets.html +99 -1
  45. package/dist/ui/serverless/traces.html +1177 -0
  46. package/dist/ui/serverless.html +87 -1
  47. package/dist/ui-src/pages/index.stx +1 -1
  48. package/dist/ui-src/pages/partials/head.stx +47 -35
  49. package/dist/ui-src/pages/partials/nav.stx +4 -3
  50. package/dist/ui-src/pages/server/actions.stx +34 -1
  51. package/dist/ui-src/pages/server/backups.stx +1 -1
  52. package/dist/ui-src/pages/server/database.stx +32 -9
  53. package/dist/ui-src/pages/server/deployments.stx +2 -2
  54. package/dist/ui-src/pages/server/firewall.stx +134 -0
  55. package/dist/ui-src/pages/server/logs.stx +2 -2
  56. package/dist/ui-src/pages/server/metrics.stx +5 -5
  57. package/dist/ui-src/pages/server/security.stx +1 -1
  58. package/dist/ui-src/pages/server/services.stx +1 -1
  59. package/dist/ui-src/pages/server/sites.stx +24 -3
  60. package/dist/ui-src/pages/server/ssh-keys.stx +2 -2
  61. package/dist/ui-src/pages/server/terminal.stx +65 -0
  62. package/dist/ui-src/pages/server/workers.stx +1 -1
  63. package/dist/ui-src/pages/serverless/alarms.stx +153 -0
  64. package/dist/ui-src/pages/serverless/assets.stx +29 -2
  65. package/dist/ui-src/pages/serverless/cost.stx +4 -4
  66. package/dist/ui-src/pages/serverless/data.stx +56 -3
  67. package/dist/ui-src/pages/serverless/deployments.stx +30 -6
  68. package/dist/ui-src/pages/serverless/firewall.stx +1 -1
  69. package/dist/ui-src/pages/serverless/functions.stx +62 -4
  70. package/dist/ui-src/pages/serverless/logs.stx +2 -2
  71. package/dist/ui-src/pages/serverless/metrics.stx +5 -1
  72. package/dist/ui-src/pages/serverless/queues.stx +90 -26
  73. package/dist/ui-src/pages/serverless/scheduler.stx +31 -2
  74. package/dist/ui-src/pages/serverless/secrets.stx +94 -14
  75. package/dist/ui-src/pages/serverless/traces.stx +88 -0
  76. package/dist/ui-src/pages/serverless.stx +87 -24
  77. package/package.json +3 -3
@@ -1,8 +1,9 @@
1
1
  <script server>
2
2
  // ts-cloud serverless (Vapor-style) dashboard. Real data is injected at build
3
- // time via the TSCLOUD_DASHBOARD_DATA env var (run `cloud dashboard:build`);
4
- // the sample values below render the design when nothing is injected. Each
5
- // section drills into a dedicated page under /serverless/*.
3
+ // time via TSCLOUD_DASHBOARD_DATA (run `cloud dashboard:build`); the sample
4
+ // values below render the design when nothing is injected. Sections drill into
5
+ // dedicated pages under /serverless/*, and the header operations run live
6
+ // against AWS via /api/serverless/*.
6
7
  let __d = null
7
8
  try { __d = JSON.parse(process.env.TSCLOUD_DASHBOARD_DATA || '') }
8
9
  catch { __d = null }
@@ -23,24 +24,26 @@ const queues = __d?.queues ?? [
23
24
  { name: 'emails', visible: 4, inFlight: 1, processed: 9210, dlq: 1 },
24
25
  ]
25
26
  const scheduler = __d?.scheduler ?? { enabled: true, expression: 'rate(1 minute)', lastRun: '36s ago' }
26
- const data = [
27
- { name: 'Aurora Serverless v2', detail: 'acme-production-db · 0.54 ACU', status: 'available' },
27
+ const dataServices = __d?.dataServices ?? [
28
+ { name: 'Aurora Serverless v2', detail: 'acme-production-db · 0.5-4 ACU', status: 'available' },
28
29
  { name: 'RDS Proxy', detail: 'acme-production-proxy', status: 'available' },
29
30
  { name: 'ElastiCache (redis)', detail: 'cache.t4g.micro', status: 'available' },
30
31
  ]
31
- const assets = { bucket: 'acme-production-assets', cdn: 'd2x1.cloudfront.net', assetUrl: 'https://d2x1.cloudfront.net/a4d63d2' }
32
- const waf = { enabled: true, rules: ['rate-limit', 'common', 'sqlInjection'], blocked24h: 318 }
33
- const deployments = [
32
+ const assets = __d?.assetsInfo ?? { bucket: 'acme-production-assets', cdn: 'd2x1.cloudfront.net', assetUrl: 'https://d2x1.cloudfront.net/a4d63d2' }
33
+ const waf = __d?.waf ?? { enabled: true, rules: ['rate-limit', 'common', 'sqlInjection'], blocked24h: 318 }
34
+ const wafRules = __d?.wafRules ?? []
35
+ const deployments = __d?.deploymentsDetail ?? [
34
36
  { sha: 'a4d63d2', status: 'success', when: '12m ago', took: '41s', by: 'chris' },
35
37
  { sha: '9557430', status: 'success', when: '2h ago', took: '38s', by: 'ci' },
36
38
  { sha: 'c38fc09', status: 'success', when: '1d ago', took: '44s', by: 'ci' },
37
39
  { sha: '5a2b284', status: 'rolled-back', when: '1d ago', took: '12s', by: 'chris' },
38
40
  ]
39
- const secrets = [
41
+ const secrets = (__d?.secretsList ?? [
40
42
  { key: 'APP_KEY', updated: '14d ago' },
41
43
  { key: 'DB_PASSWORD', updated: '14d ago' },
42
44
  { key: 'STRIPE_SECRET', updated: '3d ago' },
43
- ]
45
+ ])
46
+ const wafRuleText = waf.rules ? waf.rules.join(', ') : (wafRules.length ? wafRules.map(r => r.name).join(', ') : '-')
44
47
  function statusTone(status) {
45
48
  if (status === 'active' || status === 'available' || status === 'success' || status === 'on') return 'ok'
46
49
  if (status === 'failed' || status === 'stopped' || status === 'error') return 'bad'
@@ -48,11 +51,58 @@ function statusTone(status) {
48
51
  return 'warn'
49
52
  }
50
53
  function fmt(n) { return n >= 1000 ? `${(n / 1000).toFixed(n >= 10000 ? 0 : 1)}k` : String(n) }
54
+ // Bridge the initial maintenance flag into <script client>.
55
+ const initialMaintenance = !!maintenance.enabled
56
+ </script>
57
+ <script client>
58
+ // Live serverless operations: maintenance toggle, redeploy, and rollback run
59
+ // against AWS through /api/serverless/operations/run. Mutating ops stage a
60
+ // typed-confirm via the shared op-confirm bar, then stream their output.
61
+ const maint = state(initialMaintenance)
62
+ const opOutput = state('')
63
+ const opShown = state(false)
64
+ const pending = state(null)
65
+ const typed = state('')
66
+ const confirmTok = derived(() => { const p = pending(); return p ? p.confirm : '' })
67
+ const confirmVerb = derived(() => { const p = pending(); return p ? p.verb : '' })
68
+ const canRun = derived(() => { const p = pending(); return !!p && typed() === p.confirm })
69
+ function askOp(operation, confirm, verb) { pending.set({ operation, confirm, verb }); typed.set('') }
70
+ function cancelOp() { pending.set(null) }
71
+ async function runOp() {
72
+ const p = pending()
73
+ if (!p || typed() !== p.confirm) return
74
+ pending.set(null); typed.set('')
75
+ opShown.set(true); opOutput.set('Running ' + p.operation + '...')
76
+ try {
77
+ const res = await fetch('/api/serverless/operations/run', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ operation: p.operation, confirm: p.confirm }) })
78
+ const body = await res.json()
79
+ opOutput.set((body.ok ? 'OK ' : 'FAILED ') + (body.command || p.operation) + '\n\n' + (body.stdout || '') + (body.error ? '\n' + body.error : ''))
80
+ if (body.ok && p.operation === 'maintenance:on') maint.set(true)
81
+ if (body.ok && p.operation === 'maintenance:off') maint.set(false)
82
+ } catch (e) { opOutput.set('FAILED ' + p.operation + '\n\n' + ((e && e.message) || e)) }
83
+ }
84
+
85
+ // Command console (Vapor-style): invoke an app command through the CLI function.
86
+ const appCmd = state('')
87
+ const cmdOut = state('')
88
+ const cmdShown = state(false)
89
+ const cmdBusy = state(false)
90
+ async function runAppCmd() {
91
+ const c = appCmd().trim()
92
+ if (!c) return
93
+ cmdBusy.set(true); cmdShown.set(true); cmdOut.set('Running ' + c + '...')
94
+ try {
95
+ const res = await fetch('/api/serverless/command', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ command: c, confirm: 'run' }) })
96
+ const b = await res.json()
97
+ cmdOut.set((b.ok ? 'OK ' : 'FAILED ') + (b.command || c) + '\n\n' + (b.stdout || '') + (b.error ? '\n' + b.error : ''))
98
+ } catch (e) { cmdOut.set('FAILED ' + c + '\n\n' + ((e && e.message) || e)) }
99
+ finally { cmdBusy.set(false) }
100
+ }
51
101
  </script>
52
102
  <!DOCTYPE html>
53
103
  <html lang="en" data-theme="dark">
54
104
  <head>
55
- @include('./partials/head', { title: 'ts-cloud Serverless' })
105
+ @include('./partials/head', { title: 'ts-cloud · Serverless' })
56
106
  </head>
57
107
  <body>
58
108
  @include('./partials/nav', { active: 'serverless', name: app.name, env: app.env })
@@ -68,18 +118,19 @@ function fmt(n) { return n >= 1000 ? `${(n / 1000).toFixed(n >= 10000 ? 0 : 1)}k
68
118
  <span>Deployed <b>{{ app.deployedAt }}</b></span>
69
119
  </div>
70
120
  </div>
71
- <div class="cmds header-actions">
72
- @if (maintenance.enabled)
73
- <span class="pill warn">maintenance</span>
74
- <span class="cmd">cloud up --env {{ app.env }}</span>
75
- @else
76
- <span class="ok pill">live</span>
77
- <span class="cmd">cloud down --env {{ app.env }}</span>
78
- @endif
79
- <span class="accent cmd">cloud deploy:serverless --env {{ app.env }}</span>
121
+ <div class="header-actions">
122
+ <span class="pill" :class="maint() ? 'warn' : 'ok'">{{ maint() ? 'maintenance' : 'live' }}</span>
123
+ <button class="btn ghost sm" type="button" @show="!maint()" @click="askOp('maintenance:on', 'maintenance', 'enable maintenance mode')">Maintenance</button>
124
+ <button class="btn ghost sm" type="button" @show="maint()" @click="askOp('maintenance:off', 'live', 'bring the app back live')">Go live</button>
125
+ <button class="btn ghost sm" type="button" @click="askOp('rollback', 'rollback', 'roll back to the previous build')">Roll back</button>
126
+ <button class="btn sm" type="button" @click="askOp('redeploy', 'redeploy', 'redeploy the current build')">Redeploy</button>
80
127
  </div>
81
128
  </header>
82
129
 
130
+ <div class="panel" style="margin-bottom:20px" @show="pending() !== null || opShown()">
131
+ @include('./partials/op-confirm')
132
+ </div>
133
+
83
134
  <div class="grid cards">
84
135
  <a class="panel" href="/serverless/metrics">
85
136
  <h3>Invocations (24h)</h3>
@@ -161,7 +212,7 @@ function fmt(n) { return n >= 1000 ? `${(n / 1000).toFixed(n >= 10000 ? 0 : 1)}k
161
212
  <h2>Firewall (WAF) <a class="more" href="/serverless/firewall">Details →</a></h2>
162
213
  <div class="panel">
163
214
  <div class="kv"><span>state</span> @if (waf.enabled)<span class="ok pill">on</span>@else<span class="pill warn">off</span>@endif</div>
164
- <div class="kv"><span>rules</span> <span class="mono">{{ waf.rules.join(', ') }}</span></div>
215
+ <div class="kv"><span>rules</span> <span class="mono">{{ wafRuleText }}</span></div>
165
216
  <div class="kv"><span>blocked (24h)</span> <span>{{ fmt(waf.blocked24h) }}</span></div>
166
217
  </div>
167
218
  </div>
@@ -170,7 +221,7 @@ function fmt(n) { return n >= 1000 ? `${(n / 1000).toFixed(n >= 10000 ? 0 : 1)}k
170
221
  <div class="section">
171
222
  <h2>Data <a class="more" href="/serverless/data">View all →</a></h2>
172
223
  <div class="panel">
173
- @foreach (data as d)
224
+ @foreach (dataServices as d)
174
225
  <div class="data-item">
175
226
  <b>{{ d.name }}</b>
176
227
  <span class="detail">{{ d.detail }}</span>
@@ -193,7 +244,7 @@ function fmt(n) { return n >= 1000 ? `${(n / 1000).toFixed(n >= 10000 ? 0 : 1)}k
193
244
  </div>
194
245
  @endforeach
195
246
  <div style="margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--panel-br);">
196
- <span class="cmd">cloud serverless:rollback --env {{ app.env }}</span>
247
+ <button class="btn danger sm" type="button" @click="askOp('rollback', 'rollback', 'roll back to the previous build')">Roll back to previous</button>
197
248
  </div>
198
249
  </div>
199
250
  </div>
@@ -210,13 +261,25 @@ function fmt(n) { return n >= 1000 ? `${(n / 1000).toFixed(n >= 10000 ? 0 : 1)}k
210
261
  @foreach (secrets as s)
211
262
  <div class="secret">
212
263
  <b class="mono">{{ s.key }}</b>
213
- <span style="color: var(--txt3); font-size: 12px">updated {{ s.updated }}</span>
264
+ <span style="color: var(--txt3); font-size: 12px">updated {{ s.updated ?? '-' }}</span>
214
265
  <span class="val">••••••••</span>
215
266
  </div>
216
267
  @endforeach
217
268
  </div>
218
269
  </div>
219
270
  </div>
271
+
272
+ <div class="section">
273
+ <h2>Run a command</h2>
274
+ <div class="panel">
275
+ <div class="field">
276
+ <input :value="appCmd()" @input="appCmd.set($event.target.value)" @keydown.enter="runAppCmd()" placeholder="e.g. migrate --force, cache:clear, queue:retry all" autocomplete="off">
277
+ <button class="btn" type="button" :disabled="cmdBusy()" @click="runAppCmd()">Run</button>
278
+ </div>
279
+ <pre class="action-output" @show="cmdShown()">{{ cmdOut() }}</pre>
280
+ <p class="note">Invokes the app command through the CLI Lambda function (RequestResponse), the same path as <span class="mono">cloud command "..."</span>. Output is clamped.</p>
281
+ </div>
282
+ </div>
220
283
  </div>
221
284
  </body>
222
285
  </html>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stacksjs/ts-cloud",
3
3
  "type": "module",
4
- "version": "0.5.35",
4
+ "version": "0.6.1",
5
5
  "description": "A lightweight, performant infrastructure-as-code library and CLI for deploying both server-based (EC2) and serverless applications.",
6
6
  "author": "Chris Breuer <chris@stacksjs.com>",
7
7
  "license": "MIT",
@@ -89,8 +89,8 @@
89
89
  "test": "bun test"
90
90
  },
91
91
  "dependencies": {
92
- "@ts-cloud/aws-types": "0.5.35",
93
- "@ts-cloud/core": "0.5.35",
92
+ "@ts-cloud/aws-types": "0.6.1",
93
+ "@ts-cloud/core": "0.6.1",
94
94
  "@stacksjs/ts-xml": "^0.1.0"
95
95
  },
96
96
  "devDependencies": {