@stacksjs/ts-cloud 0.7.32 → 0.7.33

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 (35) hide show
  1. package/dist/bin/cli.js +235 -234
  2. package/dist/{chunk-jgenfdz6.js → chunk-1nfkggdw.js} +4 -2
  3. package/dist/{chunk-dah449r1.js → chunk-m0zayf6n.js} +13 -5
  4. package/dist/deploy/index.js +2 -2
  5. package/dist/drivers/index.js +1 -1
  6. package/dist/index.js +2 -2
  7. package/dist/ui/index.html +3 -3
  8. package/dist/ui/server/actions.html +23 -6
  9. package/dist/ui/server/backups.html +3 -3
  10. package/dist/ui/server/database.html +3 -3
  11. package/dist/ui/server/deployments.html +3 -3
  12. package/dist/ui/server/firewall.html +3 -3
  13. package/dist/ui/server/logs.html +3 -3
  14. package/dist/ui/server/services.html +3 -3
  15. package/dist/ui/server/sites.html +3 -3
  16. package/dist/ui/server/ssh-keys.html +26 -4
  17. package/dist/ui/server/team.html +26 -4
  18. package/dist/ui/server/terminal.html +3 -3
  19. package/dist/ui/server/workers.html +3 -3
  20. package/dist/ui/serverless/alarms.html +3 -3
  21. package/dist/ui/serverless/assets.html +3 -3
  22. package/dist/ui/serverless/data.html +3 -3
  23. package/dist/ui/serverless/deployments.html +3 -3
  24. package/dist/ui/serverless/functions.html +3 -3
  25. package/dist/ui/serverless/logs.html +3 -3
  26. package/dist/ui/serverless/queues.html +3 -3
  27. package/dist/ui/serverless/scheduler.html +3 -3
  28. package/dist/ui/serverless/secrets.html +3 -3
  29. package/dist/ui/serverless/traces.html +3 -3
  30. package/dist/ui/serverless.html +23 -6
  31. package/dist/ui-src/pages/server/actions.stx +27 -4
  32. package/dist/ui-src/pages/server/ssh-keys.stx +22 -1
  33. package/dist/ui-src/pages/server/team.stx +23 -1
  34. package/dist/ui-src/pages/serverless.stx +27 -4
  35. package/package.json +3 -3
@@ -67,6 +67,22 @@ async function revoke(name) {
67
67
  load()
68
68
  }
69
69
 
70
+ // Removal runs through a typed-confirm bar (same pattern as firewall/secrets):
71
+ // type the username to confirm. Revoking pulls someone's access to every site
72
+ // on the box at once and invalidates their password, so it should never be one
73
+ // misclick away.
74
+ const pendingRevoke = state(null)
75
+ const typedRevoke = state('')
76
+ const canRevoke = derived(() => { const u = pendingRevoke(); return u !== null && typedRevoke() === String(u.username) })
77
+ function askRevoke(user) { pendingRevoke.set(user); typedRevoke.set('') }
78
+ function cancelRevoke() { pendingRevoke.set(null); typedRevoke.set('') }
79
+ async function confirmRevoke() {
80
+ const u = pendingRevoke()
81
+ if (!u || typedRevoke() !== String(u.username)) return
82
+ pendingRevoke.set(null); typedRevoke.set('')
83
+ await revoke(u.username)
84
+ }
85
+
70
86
  onMount(() => { load() })
71
87
  </script>
72
88
  <!DOCTYPE html>
@@ -135,12 +151,18 @@ onMount(() => { load() })
135
151
  <td><b>{{ u.name }}</b><span class="mono uname">{{ u.username }}</span></td>
136
152
  <td><span class="tag">{{ u.role === 'admin' ? 'box owner' : 'member' }}</span></td>
137
153
  <td class="mono">{{ u.role === 'admin' ? 'every site' : siteList(u) }}</td>
138
- <td class="table-actions"><button type="button" class="btn danger sm" @click="revoke(u.username)">Remove</button></td>
154
+ <td class="table-actions"><button type="button" class="btn danger sm" @click="askRevoke(u)">Remove</button></td>
139
155
  </tr>
140
156
  </template>
141
157
  </tbody>
142
158
  </table>
143
159
  <div class="compact empty" @show="users().length === 0"><strong>No one invited yet</strong><span>Invite someone above to give them access to a site.</span></div>
160
+ <div class="op-confirm" @show="pendingRevoke() !== null" style="margin-top:14px">
161
+ <span>Type <b class="mono">{{ pendingRevoke()?.username }}</b> to remove <b>{{ pendingRevoke()?.name }}</b> from every site:</span>
162
+ <input class="op-confirm-input" :value="typedRevoke()" @input="typedRevoke.set($event.target.value)" @keydown.enter="confirmRevoke()" placeholder="confirm" autocomplete="off">
163
+ <button class="btn danger sm" :disabled="!canRevoke()" @click="confirmRevoke()">Remove</button>
164
+ <button class="btn ghost sm" @click="cancelRevoke()">Cancel</button>
165
+ </div>
144
166
  <p class="note">Box owners reach everything on this server. Members reach only the sites listed here, and never the shell, SSH keys, firewall or databases. Access is checked on every request.</p>
145
167
  </div>
146
168
  </div>
@@ -87,12 +87,29 @@ const appCmd = state('')
87
87
  const cmdOut = state('')
88
88
  const cmdShown = state(false)
89
89
  const cmdBusy = state(false)
90
- async function runAppCmd() {
90
+ // The API already requires a typed confirmation here ('Type "run" to execute
91
+ // this command.'), but the UI supplied the token itself, so the gate always
92
+ // passed and Enter ran an arbitrary app command (migrate:fresh, queue:flush)
93
+ // against production. Stage it, make the operator type the word, send what they
94
+ // actually typed.
95
+ const cmdPending = state(null)
96
+ const cmdTyped = state('')
97
+ const cmdCanRun = derived(() => cmdPending() !== null && cmdTyped().trim() === 'run')
98
+ function askAppCmd() {
91
99
  const c = appCmd().trim()
92
100
  if (!c) return
101
+ cmdPending.set(c); cmdTyped.set('')
102
+ }
103
+ function cancelAppCmd() { cmdPending.set(null); cmdTyped.set('') }
104
+
105
+ async function runAppCmd() {
106
+ const c = cmdPending()
107
+ const confirm = cmdTyped().trim()
108
+ if (!c || confirm !== 'run') return
109
+ cmdPending.set(null); cmdTyped.set('')
93
110
  cmdBusy.set(true); cmdShown.set(true); cmdOut.set('Running ' + c + '...')
94
111
  try {
95
- const res = await fetch('/api/serverless/command', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ command: c, confirm: 'run' }) })
112
+ const res = await fetch('/api/serverless/command', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ command: c, confirm }) })
96
113
  const b = await res.json()
97
114
  cmdOut.set((b.ok ? 'OK ' : 'FAILED ') + (b.command || c) + '\n\n' + (b.stdout || '') + (b.error ? '\n' + b.error : ''))
98
115
  } catch (e) { cmdOut.set('FAILED ' + c + '\n\n' + ((e && e.message) || e)) }
@@ -273,8 +290,14 @@ async function runAppCmd() {
273
290
  <h2>Run a command</h2>
274
291
  <div class="panel">
275
292
  <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>
293
+ <input :value="appCmd()" @input="appCmd.set($event.target.value)" @keydown.enter="askAppCmd()" placeholder="e.g. migrate --force, cache:clear, queue:retry all" autocomplete="off" aria-label="App command to run">
294
+ <button class="btn" type="button" :disabled="cmdBusy()" @click="askAppCmd()">Run</button>
295
+ </div>
296
+ <div class="op-confirm" @show="cmdPending() !== null" style="margin-top:14px">
297
+ <span>Type <b class="mono">run</b> to execute <b class="mono">{{ cmdPending() }}</b> against production:</span>
298
+ <input class="op-confirm-input" :value="cmdTyped()" @input="cmdTyped.set($event.target.value)" @keydown.enter="runAppCmd()" placeholder="confirm" autocomplete="off" aria-label="Type run to confirm executing this command">
299
+ <button type="button" class="btn danger sm" :disabled="!cmdCanRun()" @click="runAppCmd()">Run command</button>
300
+ <button type="button" class="btn ghost sm" @click="cancelAppCmd()">Cancel</button>
278
301
  </div>
279
302
  <pre class="action-output" @show="cmdShown()">{{ cmdOut() }}</pre>
280
303
  <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>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stacksjs/ts-cloud",
3
3
  "type": "module",
4
- "version": "0.7.32",
4
+ "version": "0.7.33",
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.7.32",
93
- "@ts-cloud/core": "0.7.32",
92
+ "@ts-cloud/aws-types": "0.7.33",
93
+ "@ts-cloud/core": "0.7.33",
94
94
  "@stacksjs/ts-xml": "^0.1.0"
95
95
  },
96
96
  "devDependencies": {