@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.
- package/dist/aws/cloudwatch.d.ts +2 -0
- package/dist/aws/xray.d.ts +45 -0
- package/dist/bin/cli.js +718 -701
- package/dist/deploy/dashboard-database.d.ts +25 -0
- package/dist/deploy/dashboard-operations.d.ts +7 -0
- package/dist/deploy/firewall-config-editor.d.ts +15 -0
- package/dist/deploy/firewall-config-editor.test.d.ts +1 -0
- package/dist/deploy/index.d.ts +1 -0
- package/dist/deploy/management-dashboard.d.ts +26 -3
- package/dist/deploy/serverless-operations.d.ts +150 -0
- package/dist/deploy/serverless-operations.test.d.ts +1 -0
- package/dist/deploy/site-config-editor.d.ts +6 -0
- package/dist/deploy/ssh-config-editor.d.ts +1 -0
- package/dist/deploy/terminal-session.d.ts +23 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1003 -33
- package/dist/ui/index.html +4 -4
- package/dist/ui/server/actions.html +34 -5
- package/dist/ui/server/activity.html +1 -1
- package/dist/ui/server/backups.html +4 -4
- package/dist/ui/server/database.html +27 -5
- package/dist/ui/server/deployments.html +4 -4
- package/dist/ui/server/diagnostics.html +1 -1
- package/dist/ui/server/firewall.html +1233 -0
- package/dist/ui/server/logs.html +4 -4
- package/dist/ui/server/metrics.html +1 -1
- package/dist/ui/server/security.html +1 -1
- package/dist/ui/server/services.html +4 -4
- package/dist/ui/server/sites.html +24 -5
- package/dist/ui/server/ssh-keys.html +4 -4
- package/dist/ui/server/terminal.html +1191 -0
- package/dist/ui/server/workers.html +4 -4
- package/dist/ui/serverless/alarms.html +1236 -0
- package/dist/ui/serverless/assets.html +53 -1
- package/dist/ui/serverless/cost.html +1 -1
- package/dist/ui/serverless/data.html +58 -1
- package/dist/ui/serverless/deployments.html +53 -1
- package/dist/ui/serverless/firewall.html +1 -1
- package/dist/ui/serverless/functions.html +63 -1
- package/dist/ui/serverless/logs.html +4 -4
- package/dist/ui/serverless/metrics.html +1 -1
- package/dist/ui/serverless/queues.html +94 -1
- package/dist/ui/serverless/scheduler.html +55 -1
- package/dist/ui/serverless/secrets.html +99 -1
- package/dist/ui/serverless/traces.html +1177 -0
- package/dist/ui/serverless.html +87 -1
- package/dist/ui-src/pages/index.stx +1 -1
- package/dist/ui-src/pages/partials/head.stx +47 -35
- package/dist/ui-src/pages/partials/nav.stx +4 -3
- package/dist/ui-src/pages/server/actions.stx +34 -1
- package/dist/ui-src/pages/server/backups.stx +1 -1
- package/dist/ui-src/pages/server/database.stx +32 -9
- package/dist/ui-src/pages/server/deployments.stx +2 -2
- package/dist/ui-src/pages/server/firewall.stx +134 -0
- package/dist/ui-src/pages/server/logs.stx +2 -2
- package/dist/ui-src/pages/server/metrics.stx +5 -5
- package/dist/ui-src/pages/server/security.stx +1 -1
- package/dist/ui-src/pages/server/services.stx +1 -1
- package/dist/ui-src/pages/server/sites.stx +24 -3
- package/dist/ui-src/pages/server/ssh-keys.stx +2 -2
- package/dist/ui-src/pages/server/terminal.stx +65 -0
- package/dist/ui-src/pages/server/workers.stx +1 -1
- package/dist/ui-src/pages/serverless/alarms.stx +153 -0
- package/dist/ui-src/pages/serverless/assets.stx +29 -2
- package/dist/ui-src/pages/serverless/cost.stx +4 -4
- package/dist/ui-src/pages/serverless/data.stx +56 -3
- package/dist/ui-src/pages/serverless/deployments.stx +30 -6
- package/dist/ui-src/pages/serverless/firewall.stx +1 -1
- package/dist/ui-src/pages/serverless/functions.stx +62 -4
- package/dist/ui-src/pages/serverless/logs.stx +2 -2
- package/dist/ui-src/pages/serverless/metrics.stx +5 -1
- package/dist/ui-src/pages/serverless/queues.stx +90 -26
- package/dist/ui-src/pages/serverless/scheduler.stx +31 -2
- package/dist/ui-src/pages/serverless/secrets.stx +94 -14
- package/dist/ui-src/pages/serverless/traces.stx +88 -0
- package/dist/ui-src/pages/serverless.stx +87 -24
- 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
|
|
4
|
-
//
|
|
5
|
-
//
|
|
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
|
|
27
|
-
{ name: 'Aurora Serverless v2', detail: 'acme-production-db · 0.5
|
|
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
|
|
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
|
|
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="
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
@
|
|
76
|
-
|
|
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">{{
|
|
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 (
|
|
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
|
-
<
|
|
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.
|
|
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.
|
|
93
|
-
"@ts-cloud/core": "0.
|
|
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": {
|