// Services: named ports and ICMP types, built in and yours. import { h, mount, table, modal, errorBox, field, input, confirmDialog, badge } from "../ui.js"; import { get, change, can } from "../api.js"; import { rerender } from "../app.js"; import { listEditor } from "../widgets.js"; export async function render(el) { const services = await get("/services"); const write = can("config", true); mount(el, h("div", { class: "page-head" }, h("div", { class: "grow" }, h("h1", null, "Services"), h("div", { class: "muted" }, "Ports of a protocol and ICMP types, by name. The built-in ones can be changed by adding one of the same name.")), write ? h("div", { class: "actions" }, h("button", { class: "primary", onclick: () => serviceDialog(null) }, "Add service")) : null), h("div", { class: "panel" }, table([ { label: "Name", render: (s) => h("strong", null, s.name) }, { label: "Entries", render: (s) => h("span", { class: "mono small" }, s.entries.join(", ")) }, { label: "", render: (s) => s.builtin ? badge("built in", "") : badge("yours", "accent") }, { label: "Comment", render: (s) => s.comment }, write ? { label: "", tdClass: "actions-cell", render: (s) => s.builtin ? h("button", { class: "small reveal", onclick: () => serviceDialog({ name: s.name, entries: s.entries }) }, "Change") : [h("button", { class: "small reveal", onclick: () => serviceDialog(s) }, "Edit"), " ", h("button", { class: "small reveal", onclick: () => del(s) }, "Delete")] } : null, ].filter(Boolean), services))); } async function del(s) { if (!await confirmDialog("Delete service", "Delete service " + s.name + "?", "Delete", "danger")) return; try { await change("DELETE", "/services/" + s.id); rerender(); } catch (e) { modal("Not deleted", errorBox(e)); } } async function serviceDialog(s) { s = s || { entries: [] }; const name = input({ value: s.name || "", placeholder: "jellyfin" }); const list = listEditor(s.entries, { suggestions: ["tcp/", "udp/", "icmp/echo-request", "icmpv6/echo-request"], placeholder: "tcp/8096, udp/27015-27030, icmp/echo-request", addLabel: "Add entry" }); const comment = input({ value: s.comment || "" }); const err = h("div"); await modal(s.id ? "Edit service " + s.name : "Add service", h("div", null, h("div", { class: "form-grid" }, field("Name", name), field("Entries", list), field("Comment", comment)), err), [ { label: "Cancel", value: null }, { label: s.id ? "Save" : "Add service", kind: "primary", onclick: async () => { mount(err); try { await change(s.id ? "PATCH" : "POST", s.id ? "/services/" + s.id : "/services", { name: name.value.trim(), entries: list.value(), comment: comment.value.trim() }); rerender(); return true; } catch (e) { mount(err, errorBox(e)); return false; } } }, ], { sticky: true }); }