feat: restore table UI with monolithic env var submission
All checks were successful
Check / check (pull_request) Successful in 3m18s
All checks were successful
Check / check (pull_request) Successful in 3m18s
Keep the original table-based UI with individual key/value rows, edit/delete buttons, and add form. Use Alpine.js to manage the env var list client-side. On form submit, all env vars are collected into a hidden textarea field and POSTed as a single bulk request. The server-side handler (HandleEnvVarSave) atomically replaces all env vars: DELETE all existing + INSERT the full submitted set. This combines the fix for issue #156 (env var 404) with the monolithic list approach from issue #163. closes #156 closes #163
This commit is contained in:
@@ -101,14 +101,89 @@
|
||||
</div>
|
||||
|
||||
<!-- Environment Variables -->
|
||||
<div class="card p-6 mb-6">
|
||||
<div class="card p-6 mb-6" x-data="{
|
||||
vars: [],
|
||||
newKey: '',
|
||||
newValue: '',
|
||||
init() {
|
||||
const text = this.$refs.envVarsField.value;
|
||||
if (!text.trim()) return;
|
||||
this.vars = text.split('\n')
|
||||
.map(l => l.trim())
|
||||
.filter(l => l !== '')
|
||||
.map(l => {
|
||||
const idx = l.indexOf('=');
|
||||
if (idx === -1) return null;
|
||||
return { key: l.substring(0, idx), value: l.substring(idx + 1), editing: false };
|
||||
})
|
||||
.filter(v => v !== null);
|
||||
},
|
||||
addVar() {
|
||||
if (this.newKey.trim() === '') return;
|
||||
this.vars.push({ key: this.newKey.trim(), value: this.newValue, editing: false });
|
||||
this.newKey = '';
|
||||
this.newValue = '';
|
||||
},
|
||||
removeVar(index) {
|
||||
this.vars.splice(index, 1);
|
||||
},
|
||||
prepareSubmit() {
|
||||
this.$refs.envVarsField.value = this.vars.map(v => v.key + '=' + v.value).join('\n');
|
||||
}
|
||||
}">
|
||||
<h2 class="section-title mb-4">Environment Variables</h2>
|
||||
<form method="POST" action="/apps/{{.App.ID}}/env">
|
||||
<form method="POST" action="/apps/{{.App.ID}}/env" @submit="prepareSubmit()">
|
||||
{{ .CSRFField }}
|
||||
<p class="text-sm text-gray-500 mb-2">One <code>KEY=value</code> per line. Lines starting with <code>#</code> are ignored.</p>
|
||||
<textarea name="env_vars" rows="8" class="input font-mono text-sm w-full mb-2" placeholder="DATABASE_URL=postgres://localhost/mydb SECRET_KEY=changeme">{{range .EnvVars}}{{.Key}}={{.Value}} {{end}}</textarea>
|
||||
<template x-if="vars.length > 0">
|
||||
<div class="overflow-x-auto mb-4">
|
||||
<table class="table">
|
||||
<thead class="table-header">
|
||||
<tr>
|
||||
<th>Key</th>
|
||||
<th>Value</th>
|
||||
<th class="text-right">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="table-body">
|
||||
<template x-for="(v, index) in vars" :key="index">
|
||||
<tr>
|
||||
<template x-if="!v.editing">
|
||||
<td class="font-mono font-medium" x-text="v.key"></td>
|
||||
</template>
|
||||
<template x-if="!v.editing">
|
||||
<td class="font-mono text-gray-500" x-text="v.value"></td>
|
||||
</template>
|
||||
<template x-if="!v.editing">
|
||||
<td class="text-right">
|
||||
<button type="button" @click="v.editing = true" class="text-primary-600 hover:text-primary-800 text-sm mr-2">Edit</button>
|
||||
<button type="button" @click="removeVar(index)" class="text-error-500 hover:text-error-700 text-sm">Delete</button>
|
||||
</td>
|
||||
</template>
|
||||
<template x-if="v.editing">
|
||||
<td colspan="3">
|
||||
<div class="flex gap-2 items-center">
|
||||
<input type="text" x-model="v.key" required class="input flex-1 font-mono text-sm">
|
||||
<input type="text" x-model="v.value" required class="input flex-1 font-mono text-sm">
|
||||
<button type="button" @click="v.editing = false" class="btn-primary text-sm">Done</button>
|
||||
</div>
|
||||
<p class="text-xs text-amber-600 mt-1">⚠ Container restart needed after env var changes.</p>
|
||||
</td>
|
||||
</template>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
<div class="flex flex-col sm:flex-row gap-2 mb-3">
|
||||
<input type="text" x-model="newKey" placeholder="KEY" class="input flex-1 font-mono text-sm">
|
||||
<input type="text" x-model="newValue" placeholder="value" class="input flex-1 font-mono text-sm">
|
||||
<button type="button" @click="addVar()" class="btn-secondary">Add</button>
|
||||
</div>
|
||||
<textarea name="env_vars" x-ref="envVarsField" class="hidden">{{range .EnvVars}}{{.Key}}={{.Value}}
|
||||
{{end}}</textarea>
|
||||
<div class="flex items-center gap-3">
|
||||
<button type="submit" class="btn-primary">Save Environment Variables</button>
|
||||
<button type="submit" class="btn-primary">Save All</button>
|
||||
<p class="text-xs text-amber-600">⚠ Container restart needed after env var changes.</p>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
Reference in New Issue
Block a user