]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
[WebUI] Add fuzzy flag selectors to Scan/Learn tab 5524/head
authorAlexander Moisseev <moiseev@mezonplus.ru>
Wed, 25 Jun 2025 14:20:19 +0000 (17:20 +0300)
committerAlexander Moisseev <moiseev@mezonplus.ru>
Wed, 25 Jun 2025 18:36:48 +0000 (21:36 +0300)
- Populate selectors with symbols and flags from writable fuzzy storages.
- Cache each server’s config_id to avoid redundant `/plugins/fuzzy/storages` calls.
- Dynamically show, hide, and disable controls based on fuzzy_check module availability and storage writability.

eslint.config.mjs
interface/css/rspamd.css
interface/index.html
interface/js/app/rspamd.js
interface/js/app/upload.js

index 2f256afcbe3d7e8df81fcc5700bcddfd18943f17..bdd6ede4814f3eb37fcb86eef63d8a17934373c4 100644 (file)
@@ -13,6 +13,9 @@ export default [
                 ...globals.browser,
                 define: false,
             },
+            parserOptions: {
+                ecmaVersion: 2020,
+            },
             sourceType: "script",
         },
         plugins: {
index a79fed28b0fa3315522086fbd3381398b48da109..a4335c07b9ea156535522c26184e03f06e717b77 100644 (file)
@@ -95,6 +95,15 @@ fieldset[disabled] .btn {
     pointer-events: auto;
     cursor: not-allowed;
 }
+.card.disabled,
+.input-group.disabled {
+    cursor: not-allowed;
+    opacity: 0.65;
+}
+.card.disabled *,
+.input-group.disabled * {
+    pointer-events: none;
+}
 .w-1 {
     width: 1%;
 }
index 61487ce2951eda4102a1dd852f5d62b3f8b0b5c9..30181e788645598831b27beb811986387c877941 100644 (file)
                                                </div>
                                                <div class="input-group d-inline-flex w-auto my-1">
                                                        <label for="fuzzy-flag" class="input-group-text">Flag</label>
-                                                       <input id="fuzzy-flag" class="form-control" value="1" min="1" type="number">
+                                                       <select id="fuzzy-flag-picker" class="form-select"></select>
+                                                       <input id="fuzzy-flag" class="form-control flex-grow-0" value="1" min="1" type="number">
                                                        <button class="btn btn-warning d-flex align-items-center" data-upload="compute-fuzzy"><i class="fas fa-hashtag me-2"></i>Compute fuzzy hashes</button>
                                                </div>
                                                <div class="float-end my-1">
                                                                        <div class="row g-2 align-items-center">
                                                                                <div class="col-auto d-flex align-items-center me-1">
                                                                                        <label for="fuzzyFlagText" class="me-1">Flag:</label>
+                                                                                       <select id="fuzzyFlagText-picker" class="form-select"></select>
                                                                                        <input id="fuzzyFlagText" class="form-control" type="number" value="1"/>
                                                                                </div>
                                                                                <div class="col-auto d-flex align-items-center me-2">
index da4e9bccfeaf4221bc61093ad25409b765949ad6..ceba6864bf20e759093510b1922b27d91c7edb15 100644 (file)
@@ -176,7 +176,7 @@ define(["jquery", "app/common", "stickytabs", "visibility",
                 require(["app/symbols"], (module) => module.getSymbols());
                 break;
             case "#scan_nav":
-                require(["app/upload"]);
+                require(["app/upload"], (module) => module.getFuzzyStorages());
                 break;
             case "#selectors_nav":
                 require(["app/selectors"], (module) => module.displayUI());
index 0960ebf25bb509094b1c2a2746c0ef88380ada66..c82bb23069680bf84eb0d22833d9b278c13b61a8 100644 (file)
@@ -312,5 +312,99 @@ define(["jquery", "app/common", "app/libft"],
         };
         ui.getClassifiers();
 
+
+        const fuzzyWidgets = [
+            {
+                picker: "#fuzzy-flag-picker",
+                input: "#fuzzy-flag",
+                container: ($picker) => $picker.parent()
+            },
+            {
+                picker: "#fuzzyFlagText-picker",
+                input: "#fuzzyFlagText",
+                container: ($picker) => $picker.closest("div.card")
+            }
+        ];
+
+        function toggleWidgets(showPicker, showInput) {
+            fuzzyWidgets.forEach(({picker, input}) => {
+                $(picker)[showPicker ? "show" : "hide"]();
+                $(input)[showInput ? "show" : "hide"]();
+            });
+        }
+
+        function setWidgetsDisabled(disable) {
+            fuzzyWidgets.forEach(({picker, container}) => {
+                container($(picker))[disable ? "addClass" : "removeClass"]("disabled");
+            });
+        }
+
+        let lastFuzzyStoragesReq = {config_id: null, server: null};
+
+        ui.getFuzzyStorages = function () {
+            const server = common.getServer();
+
+            const servers = JSON.parse(sessionStorage.getItem("Credentials") || "{}");
+            const config_id = servers[server]?.data?.config_id;
+
+            if ((config_id && config_id === lastFuzzyStoragesReq.config_id) ||
+                (!config_id && server === lastFuzzyStoragesReq.server)) {
+                return;
+            }
+            lastFuzzyStoragesReq = {config_id: config_id, server: server};
+
+            fuzzyWidgets.forEach(({picker, container}) => container($(picker)).removeAttr("title"));
+
+            common.query("plugins/fuzzy/storages", {
+                success: function (data) {
+                    const storages = data[0].data.storages || {};
+                    const hasWritableStorages = Object.keys(storages).some((name) => !storages[name].read_only);
+
+                    toggleWidgets(true, false);
+                    setWidgetsDisabled(!hasWritableStorages);
+
+                    fuzzyWidgets.forEach(({picker, input}) => {
+                        const $sel = $(picker);
+
+                        $sel.empty();
+
+                        if (hasWritableStorages) {
+                            Object.entries(storages).forEach(([name, info]) => {
+                                if (!info.read_only) {
+                                    Object.entries(info.flags).forEach(([symbol, val]) => {
+                                        $sel.append($("<option>", {value: val, text: `${name}:${symbol} (${val})`}));
+                                    });
+                                }
+                            });
+                            $(input).val($sel.val());
+                            $sel.off("change").on("change", () => $(input).val($sel.val()));
+                        } else {
+                            $sel.append($("<option>", {value: "", text: "No writable storages"}));
+                        }
+                    });
+                },
+                error: function (_result, _jqXHR, _textStatus, errorThrown) {
+                    if (errorThrown === "fuzzy_check is not enabled") {
+                        toggleWidgets(true, false);
+                        setWidgetsDisabled(true);
+
+                        fuzzyWidgets.forEach(({picker, container}) => {
+                            const $picker = $(picker);
+                            $picker
+                                .empty()
+                                .append($("<option>", {value: "", text: "fuzzy_check disabled"}))
+                                .show();
+                            container($picker)
+                                .attr("title", "fuzzy_check module is not enabled in server configuration.");
+                        });
+                    } else {
+                        toggleWidgets(false, true);
+                        setWidgetsDisabled(false);
+                    }
+                },
+                server: server
+            });
+        };
+
         return ui;
     });