Loading modules/containers/containers.cpp +2 −0 Changes for modules/containers/containers.cpp: 2 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -258,8 +258,10 @@ json_object *apps() { app = json_object_new_object(); json_object_object_add(app, "name", jstr(f.second)); json_object_object_add(app, "tags", json_object_new_array()); json_object_object_add(app, "volumes", json_object_new_array()); json_object_array_add(list, app); } else if (app && f.first == "tag") json_object_array_add(json_object_object_get(app, "tags"), jstr(f.second)); else if (app && f.first == "volume") json_object_array_add(json_object_object_get(app, "volumes"), jstr(f.second)); else if (app) json_object_object_add(app, f.first.c_str(), jstr(f.second)); } } Loading www/modules/containers.js +31 −2 Changes for www/modules/containers.js: 31 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -326,6 +326,20 @@ } // app: installing it, a new container on an image not pulled yet // host directories for an image's volumes: the first below /etc is the // container's directory itself, the others keep their path below it function defaultBinds(container, volumes) { const base = '/srv/container/' + container; let config = false; return volumes.map(v => v.replace(/\/+$/, '')).filter(Boolean).map(v => { if (!config && v.startsWith('/etc/')) { config = true; return base + ':' + v; } return base + v + ':' + v; }); } async function containerDialog(name, app) { const images = data.images || []; if (!name && !app && !images.length) { Loading Loading @@ -408,9 +422,24 @@ net.addEventListener('change', update); update(); const area = (nm, list, rows) => h('textarea', {name: nm, rows: rows || 3, class: 'mono'}, (list || []).join('\n')); const nameInput = h('input', {name: 'name', value: c.name, required: true, pattern: '[A-Za-z0-9_.\\-]{1,64}'}); const binds = area('binds', c.binds); // a new container's binds follow its name and image until edited let bindsEdited = !!name; const proposeBinds = () => { if (bindsEdited) return; const img = images.find(i => i.image === imageSel.value); const volumes = (img && img.volumes) || (app && app.volumes) || []; const container = nameInput.value.trim(); binds.value = container ? defaultBinds(container, volumes).join('\n') : ''; }; binds.addEventListener('input', () => { bindsEdited = binds.value !== ''; proposeBinds(); }); nameInput.addEventListener('input', proposeBinds); imageSel.addEventListener('change', proposeBinds); proposeBinds(); return [ h('div', {class: 'row'}, h('label', {}, t('Name'), h('input', {name: 'name', value: c.name, required: true, pattern: '[A-Za-z0-9_.\\-]{1,64}'})), h('label', {}, t('Name'), nameInput), h('label', {}, t('Image'), imageSel)), h('div', {class: 'row'}, h('label', {class: 'check'}, h('input', {type: 'checkbox', name: 'autostart', checked: c.autostart}), t('Start automatically')), Loading @@ -422,7 +451,7 @@ portsHint, h('label', {}, t('Arguments (replace the image\'s CMD)'), args), h('label', {}, t('Environment (KEY=value, one per line)'), area('env', c.env)), h('label', {}, t('Bind mounts (/host/path:/container/path[:ro], one per line)'), area('binds', c.binds)), h('label', {}, t('Bind mounts (/host/path:/container/path[:ro], one per line)'), binds), volumesHint, h('details', {}, h('summary', {}, t('Advanced')), Loading Loading
modules/containers/containers.cpp +2 −0 Changes for modules/containers/containers.cpp: 2 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -258,8 +258,10 @@ json_object *apps() { app = json_object_new_object(); json_object_object_add(app, "name", jstr(f.second)); json_object_object_add(app, "tags", json_object_new_array()); json_object_object_add(app, "volumes", json_object_new_array()); json_object_array_add(list, app); } else if (app && f.first == "tag") json_object_array_add(json_object_object_get(app, "tags"), jstr(f.second)); else if (app && f.first == "volume") json_object_array_add(json_object_object_get(app, "volumes"), jstr(f.second)); else if (app) json_object_object_add(app, f.first.c_str(), jstr(f.second)); } } Loading
www/modules/containers.js +31 −2 Changes for www/modules/containers.js: 31 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -326,6 +326,20 @@ } // app: installing it, a new container on an image not pulled yet // host directories for an image's volumes: the first below /etc is the // container's directory itself, the others keep their path below it function defaultBinds(container, volumes) { const base = '/srv/container/' + container; let config = false; return volumes.map(v => v.replace(/\/+$/, '')).filter(Boolean).map(v => { if (!config && v.startsWith('/etc/')) { config = true; return base + ':' + v; } return base + v + ':' + v; }); } async function containerDialog(name, app) { const images = data.images || []; if (!name && !app && !images.length) { Loading Loading @@ -408,9 +422,24 @@ net.addEventListener('change', update); update(); const area = (nm, list, rows) => h('textarea', {name: nm, rows: rows || 3, class: 'mono'}, (list || []).join('\n')); const nameInput = h('input', {name: 'name', value: c.name, required: true, pattern: '[A-Za-z0-9_.\\-]{1,64}'}); const binds = area('binds', c.binds); // a new container's binds follow its name and image until edited let bindsEdited = !!name; const proposeBinds = () => { if (bindsEdited) return; const img = images.find(i => i.image === imageSel.value); const volumes = (img && img.volumes) || (app && app.volumes) || []; const container = nameInput.value.trim(); binds.value = container ? defaultBinds(container, volumes).join('\n') : ''; }; binds.addEventListener('input', () => { bindsEdited = binds.value !== ''; proposeBinds(); }); nameInput.addEventListener('input', proposeBinds); imageSel.addEventListener('change', proposeBinds); proposeBinds(); return [ h('div', {class: 'row'}, h('label', {}, t('Name'), h('input', {name: 'name', value: c.name, required: true, pattern: '[A-Za-z0-9_.\\-]{1,64}'})), h('label', {}, t('Name'), nameInput), h('label', {}, t('Image'), imageSel)), h('div', {class: 'row'}, h('label', {class: 'check'}, h('input', {type: 'checkbox', name: 'autostart', checked: c.autostart}), t('Start automatically')), Loading @@ -422,7 +451,7 @@ portsHint, h('label', {}, t('Arguments (replace the image\'s CMD)'), args), h('label', {}, t('Environment (KEY=value, one per line)'), area('env', c.env)), h('label', {}, t('Bind mounts (/host/path:/container/path[:ro], one per line)'), area('binds', c.binds)), h('label', {}, t('Bind mounts (/host/path:/container/path[:ro], one per line)'), binds), volumesHint, h('details', {}, h('summary', {}, t('Advanced')), Loading