move optional config from app info page to deploy/settings page
Users couldn't find metadata provider fields (IGDB, ScreenScraper, etc.) on the app info page. Move them to the deploy page where all other settings (integrations, geo-restriction) already live. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -418,6 +418,23 @@ func (s *Server) deployHandler(w http.ResponseWriter, r *http.Request, name stri
|
|||||||
data["GeoGlobalCountries"] = []string{}
|
data["GeoGlobalCountries"] = []string{}
|
||||||
data["GeoAppOverrideCountries"] = []string{}
|
data["GeoAppOverrideCountries"] = []string{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Optional config (metadata providers, etc.)
|
||||||
|
if meta.HasOptionalConfig() {
|
||||||
|
data["HasOptionalConfig"] = true
|
||||||
|
data["OptionalConfig"] = meta.OptionalConfig
|
||||||
|
optValues := make(map[string]string)
|
||||||
|
if decryptedEnv != nil {
|
||||||
|
for _, group := range meta.OptionalConfig {
|
||||||
|
for _, field := range group.Fields {
|
||||||
|
if val, ok := decryptedEnv[field.EnvVar]; ok {
|
||||||
|
optValues[field.EnvVar] = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data["CurrentValues"] = optValues
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Memory info for deploy page (only for non-deployed apps)
|
// Memory info for deploy page (only for non-deployed apps)
|
||||||
@@ -482,28 +499,19 @@ func (s *Server) appDetailHandler(w http.ResponseWriter, r *http.Request, slug s
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load current optional config values from app.yaml
|
|
||||||
currentValues := make(map[string]string)
|
|
||||||
if appCfg := s.stackMgr.LoadAppConfigByName(found.Name); appCfg != nil {
|
|
||||||
for k, v := range appCfg.Env {
|
|
||||||
currentValues[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine effective subdomain (stored env > metadata fallback)
|
// Determine effective subdomain (stored env > metadata fallback)
|
||||||
effectiveSubdomain := found.Meta.Subdomain
|
effectiveSubdomain := found.Meta.Subdomain
|
||||||
if sd, ok := currentValues["SUBDOMAIN"]; ok && sd != "" {
|
if appCfg := s.stackMgr.LoadAppConfigByName(found.Name); appCfg != nil {
|
||||||
effectiveSubdomain = sd
|
if sd, ok := appCfg.Env["SUBDOMAIN"]; ok && sd != "" {
|
||||||
|
effectiveSubdomain = sd
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data := s.baseData("stacks", found.Meta.DisplayName)
|
data := s.baseData("stacks", found.Meta.DisplayName)
|
||||||
data["Stack"] = found
|
data["Stack"] = found
|
||||||
data["Meta"] = found.Meta
|
data["Meta"] = found.Meta
|
||||||
data["AppInfo"] = found.Meta.AppInfo
|
data["AppInfo"] = found.Meta.AppInfo
|
||||||
data["OptionalConfig"] = found.Meta.OptionalConfig
|
|
||||||
data["CurrentValues"] = currentValues
|
|
||||||
data["HasAppInfo"] = found.Meta.HasAppInfo()
|
data["HasAppInfo"] = found.Meta.HasAppInfo()
|
||||||
data["HasOptionalConfig"] = found.Meta.HasOptionalConfig()
|
|
||||||
data["EffectiveSubdomain"] = effectiveSubdomain
|
data["EffectiveSubdomain"] = effectiveSubdomain
|
||||||
|
|
||||||
s.executeTemplate(w, r, "app_info", data)
|
s.executeTemplate(w, r, "app_info", data)
|
||||||
|
|||||||
@@ -101,84 +101,5 @@
|
|||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{if .HasOptionalConfig}}
|
|
||||||
<div class="app-optional-config">
|
|
||||||
<h3>Opcionális beállítások</h3>
|
|
||||||
{{range .OptionalConfig}}
|
|
||||||
<div class="config-group">
|
|
||||||
<h4>{{.Group}}</h4>
|
|
||||||
{{if .Description}}<p class="config-group-desc">{{.Description}}</p>{{end}}
|
|
||||||
|
|
||||||
<div class="config-fields">
|
|
||||||
{{range .Fields}}
|
|
||||||
<div class="config-field">
|
|
||||||
<label for="opt-{{.EnvVar}}">{{.Label}}</label>
|
|
||||||
{{if .HelpText}}<p class="config-field-help">{{.HelpText}}</p>{{end}}
|
|
||||||
{{if .HelpURL}}<p class="config-field-help"><a href="{{.HelpURL}}" target="_blank">Regisztrációs útmutató ↗</a></p>{{end}}
|
|
||||||
<input type="{{if eq .Type "secret_input"}}password{{else}}text{{end}}"
|
|
||||||
id="opt-{{.EnvVar}}"
|
|
||||||
name="{{.EnvVar}}"
|
|
||||||
class="config-input"
|
|
||||||
value="{{index $.CurrentValues .EnvVar}}"
|
|
||||||
placeholder="{{.Label}}"
|
|
||||||
autocomplete="off">
|
|
||||||
</div>
|
|
||||||
{{end}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
<div class="config-actions">
|
|
||||||
<button class="btn btn-primary" id="save-optional-config" onclick="saveOptionalConfig('{{.Stack.Name}}')">
|
|
||||||
Mentés
|
|
||||||
</button>
|
|
||||||
<span id="config-save-status" class="config-save-status"></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
async function saveOptionalConfig(stackName) {
|
|
||||||
const btn = document.getElementById('save-optional-config');
|
|
||||||
const status = document.getElementById('config-save-status');
|
|
||||||
const inputs = document.querySelectorAll('.config-input');
|
|
||||||
|
|
||||||
const values = {};
|
|
||||||
inputs.forEach(function(input) {
|
|
||||||
values[input.name] = input.value;
|
|
||||||
});
|
|
||||||
|
|
||||||
btn.disabled = true;
|
|
||||||
btn.textContent = 'Mentés...';
|
|
||||||
status.textContent = '';
|
|
||||||
status.className = 'config-save-status';
|
|
||||||
|
|
||||||
try {
|
|
||||||
const resp = await fetch('/api/stacks/' + stackName + '/optional-config', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()),
|
|
||||||
body: JSON.stringify({values: values})
|
|
||||||
});
|
|
||||||
const data = await resp.json();
|
|
||||||
|
|
||||||
if (data.ok) {
|
|
||||||
status.textContent = (data.message || 'Mentve');
|
|
||||||
status.className = 'config-save-status config-save-ok';
|
|
||||||
} else {
|
|
||||||
status.textContent = (data.error || 'Hiba');
|
|
||||||
status.className = 'config-save-status config-save-err';
|
|
||||||
}
|
|
||||||
} catch(err) {
|
|
||||||
status.textContent = 'Hálózati hiba';
|
|
||||||
status.className = 'config-save-status config-save-err';
|
|
||||||
}
|
|
||||||
|
|
||||||
btn.disabled = false;
|
|
||||||
btn.textContent = 'Mentés';
|
|
||||||
|
|
||||||
setTimeout(function() { status.textContent = ''; }, 5000);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{template "layout_end" .}}
|
{{template "layout_end" .}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@@ -402,6 +402,85 @@
|
|||||||
</script>
|
</script>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
{{if .HasOptionalConfig}}
|
||||||
|
<div class="app-optional-config">
|
||||||
|
<h3>Opcionális beállítások</h3>
|
||||||
|
{{range .OptionalConfig}}
|
||||||
|
<div class="config-group">
|
||||||
|
<h4>{{.Group}}</h4>
|
||||||
|
{{if .Description}}<p class="config-group-desc">{{.Description}}</p>{{end}}
|
||||||
|
|
||||||
|
<div class="config-fields">
|
||||||
|
{{range .Fields}}
|
||||||
|
<div class="config-field">
|
||||||
|
<label for="opt-{{.EnvVar}}">{{.Label}}</label>
|
||||||
|
{{if .HelpText}}<p class="config-field-help">{{.HelpText}}</p>{{end}}
|
||||||
|
{{if .HelpURL}}<p class="config-field-help"><a href="{{.HelpURL}}" target="_blank">Regisztrációs útmutató ↗</a></p>{{end}}
|
||||||
|
<input type="{{if eq .Type "secret_input"}}password{{else}}text{{end}}"
|
||||||
|
id="opt-{{.EnvVar}}"
|
||||||
|
name="{{.EnvVar}}"
|
||||||
|
class="config-input"
|
||||||
|
value="{{index $.CurrentValues .EnvVar}}"
|
||||||
|
placeholder="{{.Label}}"
|
||||||
|
autocomplete="off">
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
<div class="config-actions">
|
||||||
|
<button class="btn btn-primary" id="save-optional-config" onclick="saveOptionalConfig('{{.Stack.Name}}')">
|
||||||
|
Mentés
|
||||||
|
</button>
|
||||||
|
<span id="config-save-status" class="config-save-status"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
async function saveOptionalConfig(stackName) {
|
||||||
|
var btn = document.getElementById('save-optional-config');
|
||||||
|
var status = document.getElementById('config-save-status');
|
||||||
|
var inputs = document.querySelectorAll('.config-input');
|
||||||
|
|
||||||
|
var values = {};
|
||||||
|
inputs.forEach(function(input) {
|
||||||
|
values[input.name] = input.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
btn.disabled = true;
|
||||||
|
btn.textContent = 'Mentés...';
|
||||||
|
status.textContent = '';
|
||||||
|
status.className = 'config-save-status';
|
||||||
|
|
||||||
|
try {
|
||||||
|
var resp = await fetch('/api/stacks/' + stackName + '/optional-config', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()),
|
||||||
|
body: JSON.stringify({values: values})
|
||||||
|
});
|
||||||
|
var data = await resp.json();
|
||||||
|
|
||||||
|
if (data.ok) {
|
||||||
|
status.textContent = (data.message || 'Mentve');
|
||||||
|
status.className = 'config-save-status config-save-ok';
|
||||||
|
} else {
|
||||||
|
status.textContent = (data.error || 'Hiba');
|
||||||
|
status.className = 'config-save-status config-save-err';
|
||||||
|
}
|
||||||
|
} catch(err) {
|
||||||
|
status.textContent = 'Hálózati hiba';
|
||||||
|
status.className = 'config-save-status config-save-err';
|
||||||
|
}
|
||||||
|
|
||||||
|
btn.disabled = false;
|
||||||
|
btn.textContent = 'Mentés';
|
||||||
|
|
||||||
|
setTimeout(function() { status.textContent = ''; }, 5000);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
{{if and (not .AlreadyDeployed) .MemoryInfo}}
|
{{if and (not .AlreadyDeployed) .MemoryInfo}}
|
||||||
{{with .MemoryInfo}}
|
{{with .MemoryInfo}}
|
||||||
{{if .Available}}
|
{{if .Available}}
|
||||||
|
|||||||
Reference in New Issue
Block a user