Copilot commented on code in PR #348:
URL: 
https://github.com/apache/hugegraph-computer/pull/348#discussion_r3296790956


##########
vermeer/ui/ui/lib/functions.js:
##########
@@ -0,0 +1,179 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+; (function () {
+    const API_PREFIX = '/api/v1';
+    window.vermeer = {
+        login: function () {
+            const token = $('#admin_token').val();
+            if (!token) {
+                showDelModal('empty token');
+                return;
+            }
+
+            const req = { token: token };
+            postJson('/login', req,
+                function (data) {
+                    console.log(data.message);
+                    location.reload(true);
+                }
+            );
+        },
+        qeuryGraphs: function () {
+            const $t = $('#graphs_table');
+            $t.empty();
+            const fields = ['space_name', 'name', 'status', 'state', 
'create_time',
+                'update_time', 'use_out_edges', 'use_out_degree'];
+            $t.append('<thead><tr/></thead>');
+            $tr = $t.find('thead tr');
+            $.each(fields, function (index, field) {
+                $tr.append($('<th/>').text(field));
+            });
+
+            const ok = function (data) {
+                const $tb = $t.append('<tbody/>');
+                const rows = data.graphs;
+                $.each(rows, function (index, row) {
+                    $tb.append(toTableRow(fields, row));
+                });

Review Comment:
   `const $tb = $t.append('<tbody/>')` does not return the newly-created 
`<tbody>`; jQuery `append()` returns the original set (`$t`). As a result, rows 
may be appended directly to the table instead of the tbody, producing invalid 
markup and inconsistent rendering. Create the `<tbody>` as an element and 
append it, keeping a reference to the tbody for subsequent row appends.



##########
vermeer/ui/ui/lib/functions.js:
##########
@@ -0,0 +1,179 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+; (function () {
+    const API_PREFIX = '/api/v1';
+    window.vermeer = {
+        login: function () {
+            const token = $('#admin_token').val();
+            if (!token) {
+                showDelModal('empty token');
+                return;
+            }
+
+            const req = { token: token };
+            postJson('/login', req,
+                function (data) {
+                    console.log(data.message);
+                    location.reload(true);
+                }
+            );
+        },
+        qeuryGraphs: function () {
+            const $t = $('#graphs_table');

Review Comment:
   The method name `qeuryGraphs` is misspelled (and referenced as 
`vermeer.qeuryGraphs()` later). This makes the public UI API harder to 
read/maintain and is easy to propagate into future callers. Consider renaming 
it to `queryGraphs` (and updating all references).



##########
vermeer/ui/ui/lib/functions.js:
##########
@@ -0,0 +1,179 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+; (function () {
+    const API_PREFIX = '/api/v1';
+    window.vermeer = {
+        login: function () {
+            const token = $('#admin_token').val();
+            if (!token) {
+                showDelModal('empty token');
+                return;
+            }
+
+            const req = { token: token };
+            postJson('/login', req,
+                function (data) {
+                    console.log(data.message);
+                    location.reload(true);
+                }
+            );
+        },
+        qeuryGraphs: function () {
+            const $t = $('#graphs_table');
+            $t.empty();
+            const fields = ['space_name', 'name', 'status', 'state', 
'create_time',
+                'update_time', 'use_out_edges', 'use_out_degree'];
+            $t.append('<thead><tr/></thead>');
+            $tr = $t.find('thead tr');

Review Comment:
   `$tr` is assigned without `const`/`let`, which implicitly creates a global 
variable in non-strict mode and can cause hard-to-debug collisions. Declare it 
locally (e.g., `const $tr = ...`) to keep scope correct.
   



##########
vermeer/ui/ui/lib/functions.js:
##########
@@ -0,0 +1,179 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+; (function () {
+    const API_PREFIX = '/api/v1';
+    window.vermeer = {
+        login: function () {
+            const token = $('#admin_token').val();
+            if (!token) {
+                showDelModal('empty token');
+                return;
+            }
+
+            const req = { token: token };
+            postJson('/login', req,
+                function (data) {
+                    console.log(data.message);
+                    location.reload(true);
+                }
+            );
+        },
+        qeuryGraphs: function () {
+            const $t = $('#graphs_table');
+            $t.empty();
+            const fields = ['space_name', 'name', 'status', 'state', 
'create_time',
+                'update_time', 'use_out_edges', 'use_out_degree'];
+            $t.append('<thead><tr/></thead>');
+            $tr = $t.find('thead tr');
+            $.each(fields, function (index, field) {
+                $tr.append($('<th/>').text(field));
+            });
+
+            const ok = function (data) {
+                const $tb = $t.append('<tbody/>');
+                const rows = data.graphs;
+                $.each(rows, function (index, row) {
+                    $tb.append(toTableRow(fields, row));
+                });
+            };
+
+            get('/graphs', ok);
+        },
+        queryTasks: function () {
+            const $t = $('#tasks_table');
+            $t.empty();
+            const fields = ['id', 'space_name', 'graph_name', 'create_user', 
'task_type',
+                'status', 'state', 'create_time', 'start_time', 'update_time'];
+            $t.append('<thead><tr/></thead>');
+            const $tr = $t.find('thead tr');
+            $.each(fields, function (index, field) {
+                $tr.append($('<th/>').text(field));
+            });
+
+            const ok = function (data) {
+                const $tb = $t.append('<tbody/>');
+                rows = data.tasks;

Review Comment:
   In the tasks table callback, `rows = data.tasks;` is missing a `const`/`let` 
declaration, which creates/overwrites a global variable. Also, 
`$t.append('<tbody/>')` returns `$t` rather than the `<tbody>` element, so 
appending rows via `$tb.append(...)` is likely targeting the wrong node. 
Declare `rows` locally and keep a reference to the actual `<tbody>` element.
   



##########
vermeer/scripts/download_ui_assets.sh:
##########
@@ -0,0 +1,159 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+set -e
+
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
+UI_DIR="$PROJECT_ROOT/ui"
+LIB_DIR="$PROJECT_ROOT/ui/ui/lib"
+
+# Glyphicons source (GitHub raw)
+GLYPHICONS_BASE="https://raw.githubusercontent.com/Darkseal/bootstrap4-glyphicons/master/bootstrap4-glyphicons";
+GLYPHICONS_COMMIT="master"
+
+# Colors for output
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+NC='\033[0m'
+
+log_info() {
+    echo -e "${GREEN}[INFO]${NC} $1"
+}
+
+log_warn() {
+    echo -e "${YELLOW}[WARN]${NC} $1"
+}
+
+log_error() {
+    echo -e "${RED}[ERROR]${NC} $1"
+}
+
+# Download a single file from URL to target path
+download_file() {
+    local url=$1
+    local target=$2
+
+    mkdir -p "$(dirname "$target")"
+    if ! curl -sL -f "$url" -o "$target"; then
+        log_error "Failed to download: $url"
+        return 1
+    fi
+}
+
+# Main function
+main() {
+    log_info "Downloading UI assets..."
+
+    # Check npm
+    if ! command -v npm &> /dev/null; then
+        log_error "npm is required but not installed. Please install 
Node.js/npm first."
+        exit 1
+    fi
+
+    # Step 1: npm install (jQuery + Bootstrap)
+    log_info "Installing npm dependencies (jQuery, Bootstrap)..."
+    cd "$UI_DIR"
+    npm install --no-audit --no-fund 2>&1 | tail -3
+    cd "$PROJECT_ROOT"

Review Comment:
   `npm install ... | tail -3` will mask `npm install` failures because, 
without `set -o pipefail`, the pipeline exit code comes from `tail`. With `set 
-e`, the script may continue even when dependencies fail to install, leading to 
confusing downstream copy errors. Consider removing the pipe or enabling 
`pipefail` (and/or explicitly checking `npm install`'s exit status).



##########
vermeer/ui/package.json:
##########
@@ -0,0 +1,10 @@
+{
+  "name": "vermeer-ui",
+  "version": "1.0.0",
+  "private": true,
+  "description": "Frontend dependencies for Vermeer UI (downloaded at build 
time for ASF compliance)",
+  "dependencies": {
+    "jquery": "3.5.1",
+    "bootstrap": "4.3.1"

Review Comment:
   Without a committed lockfile, `npm install` will resolve transitive 
dependencies (e.g., Bootstrap’s dependencies) differently over time, making 
Docker/CI builds non-reproducible. Consider committing `package-lock.json` 
(it’s dependency metadata, not vendored code) and using `npm ci`, or otherwise 
pin transitive versions to ensure consistent builds.
   



##########
vermeer/ui/ui/lib/functions.js:
##########
@@ -0,0 +1,179 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+; (function () {
+    const API_PREFIX = '/api/v1';
+    window.vermeer = {
+        login: function () {
+            const token = $('#admin_token').val();
+            if (!token) {
+                showDelModal('empty token');
+                return;
+            }
+
+            const req = { token: token };
+            postJson('/login', req,
+                function (data) {
+                    console.log(data.message);
+                    location.reload(true);
+                }
+            );
+        },
+        qeuryGraphs: function () {
+            const $t = $('#graphs_table');
+            $t.empty();
+            const fields = ['space_name', 'name', 'status', 'state', 
'create_time',
+                'update_time', 'use_out_edges', 'use_out_degree'];
+            $t.append('<thead><tr/></thead>');
+            $tr = $t.find('thead tr');
+            $.each(fields, function (index, field) {
+                $tr.append($('<th/>').text(field));
+            });
+
+            const ok = function (data) {
+                const $tb = $t.append('<tbody/>');
+                const rows = data.graphs;
+                $.each(rows, function (index, row) {
+                    $tb.append(toTableRow(fields, row));
+                });
+            };
+
+            get('/graphs', ok);
+        },
+        queryTasks: function () {
+            const $t = $('#tasks_table');
+            $t.empty();
+            const fields = ['id', 'space_name', 'graph_name', 'create_user', 
'task_type',
+                'status', 'state', 'create_time', 'start_time', 'update_time'];
+            $t.append('<thead><tr/></thead>');
+            const $tr = $t.find('thead tr');
+            $.each(fields, function (index, field) {
+                $tr.append($('<th/>').text(field));
+            });
+
+            const ok = function (data) {
+                const $tb = $t.append('<tbody/>');
+                rows = data.tasks;
+                $.each(rows, function (index, row) {
+                    $tb.append(toTableRow(fields, row));
+                });
+            };
+            get('/tasks', ok);
+        }
+
+    };
+
+    function toTableRow(fields, row) {
+        const $row = $('<tr>');
+        $.each(fields, function (index, field) {
+            let value = '';
+
+            if (field.endsWith('_time')) {
+                value = formatDate(row[field]);
+            } else {
+                value = row[field];
+            }
+
+            $span = $('<span/>').text(value);
+
+            switch (value) {
+                case 'error':
+                    $span.addClass('badge badge-lg badge-danger');
+                    break;
+                case 'incomplete':
+                    $span.addClass('badge badge-lg badge-warning');
+                    break;
+                case 'complete':
+                case 'loaded':
+                case 'disk':
+                    $span.addClass('badge badge-lg badge-success');
+            }
+
+            $td = $('<td>').append($span);
+            $row.append($td);
+        });
+        return $row;
+    }
+
+    function showDelModal(text) {
+        $('#msg-modal-msg').text(text);
+        $('#msg-modal').modal('show');
+    }
+
+    function get(url, ok, error, caller) {
+        ajax('GET', url, '', ok, error, caller);
+    }
+
+    function postJson(url, data, ok, error, caller) {
+        ajax('POST', url, JSON.stringify(data), ok, error, caller);
+    }
+
+    function ajax(method, url, data, ok, error, caller) {
+        $.ajax({
+            url: API_PREFIX + url,
+            type: method,
+            data: data,
+            contentType: 'application/json',
+            success: function (response) {
+                if (!ok) {
+                    console.log('ajax request successful:', response);
+                    return;
+                }
+                if (caller) {
+                    ok.appply(caller, response);
+                } else {
+                    ok(response);
+                }

Review Comment:
   `ok.appply` is a typo and will throw at runtime when `caller` is provided. 
Additionally, `Function.prototype.apply` expects the second argument to be an 
array-like of parameters, so passing `response` directly is incorrect. Use 
`ok.apply(caller, [response])` (or just `ok.call(caller, response)`) to invoke 
the callback correctly.



##########
vermeer/ui/ui/lib/functions.js:
##########
@@ -0,0 +1,179 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+; (function () {
+    const API_PREFIX = '/api/v1';
+    window.vermeer = {
+        login: function () {
+            const token = $('#admin_token').val();
+            if (!token) {
+                showDelModal('empty token');
+                return;
+            }
+
+            const req = { token: token };
+            postJson('/login', req,
+                function (data) {
+                    console.log(data.message);
+                    location.reload(true);
+                }
+            );
+        },
+        qeuryGraphs: function () {
+            const $t = $('#graphs_table');
+            $t.empty();
+            const fields = ['space_name', 'name', 'status', 'state', 
'create_time',
+                'update_time', 'use_out_edges', 'use_out_degree'];
+            $t.append('<thead><tr/></thead>');
+            $tr = $t.find('thead tr');
+            $.each(fields, function (index, field) {
+                $tr.append($('<th/>').text(field));
+            });
+
+            const ok = function (data) {
+                const $tb = $t.append('<tbody/>');
+                const rows = data.graphs;
+                $.each(rows, function (index, row) {
+                    $tb.append(toTableRow(fields, row));
+                });
+            };
+
+            get('/graphs', ok);
+        },
+        queryTasks: function () {
+            const $t = $('#tasks_table');
+            $t.empty();
+            const fields = ['id', 'space_name', 'graph_name', 'create_user', 
'task_type',
+                'status', 'state', 'create_time', 'start_time', 'update_time'];
+            $t.append('<thead><tr/></thead>');
+            const $tr = $t.find('thead tr');
+            $.each(fields, function (index, field) {
+                $tr.append($('<th/>').text(field));
+            });
+
+            const ok = function (data) {
+                const $tb = $t.append('<tbody/>');
+                rows = data.tasks;
+                $.each(rows, function (index, row) {
+                    $tb.append(toTableRow(fields, row));
+                });
+            };
+            get('/tasks', ok);
+        }
+
+    };
+
+    function toTableRow(fields, row) {
+        const $row = $('<tr>');
+        $.each(fields, function (index, field) {
+            let value = '';
+
+            if (field.endsWith('_time')) {
+                value = formatDate(row[field]);
+            } else {
+                value = row[field];
+            }
+
+            $span = $('<span/>').text(value);
+
+            switch (value) {
+                case 'error':
+                    $span.addClass('badge badge-lg badge-danger');
+                    break;
+                case 'incomplete':
+                    $span.addClass('badge badge-lg badge-warning');
+                    break;
+                case 'complete':
+                case 'loaded':
+                case 'disk':
+                    $span.addClass('badge badge-lg badge-success');
+            }
+
+            $td = $('<td>').append($span);
+            $row.append($td);

Review Comment:
   `$span` and `$td` are assigned without `const`/`let`, which implicitly 
creates globals and can lead to cross-call state leakage in this module. 
Declare them as block-scoped variables inside the loop.



##########
vermeer/ui/ui/lib/functions.js:
##########
@@ -0,0 +1,179 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+; (function () {
+    const API_PREFIX = '/api/v1';
+    window.vermeer = {
+        login: function () {
+            const token = $('#admin_token').val();
+            if (!token) {
+                showDelModal('empty token');
+                return;
+            }
+
+            const req = { token: token };
+            postJson('/login', req,
+                function (data) {
+                    console.log(data.message);
+                    location.reload(true);
+                }
+            );
+        },
+        qeuryGraphs: function () {
+            const $t = $('#graphs_table');
+            $t.empty();
+            const fields = ['space_name', 'name', 'status', 'state', 
'create_time',
+                'update_time', 'use_out_edges', 'use_out_degree'];
+            $t.append('<thead><tr/></thead>');
+            $tr = $t.find('thead tr');
+            $.each(fields, function (index, field) {
+                $tr.append($('<th/>').text(field));
+            });
+
+            const ok = function (data) {
+                const $tb = $t.append('<tbody/>');
+                const rows = data.graphs;
+                $.each(rows, function (index, row) {
+                    $tb.append(toTableRow(fields, row));
+                });
+            };
+
+            get('/graphs', ok);
+        },
+        queryTasks: function () {
+            const $t = $('#tasks_table');
+            $t.empty();
+            const fields = ['id', 'space_name', 'graph_name', 'create_user', 
'task_type',
+                'status', 'state', 'create_time', 'start_time', 'update_time'];
+            $t.append('<thead><tr/></thead>');
+            const $tr = $t.find('thead tr');
+            $.each(fields, function (index, field) {
+                $tr.append($('<th/>').text(field));
+            });
+
+            const ok = function (data) {
+                const $tb = $t.append('<tbody/>');
+                rows = data.tasks;
+                $.each(rows, function (index, row) {
+                    $tb.append(toTableRow(fields, row));
+                });
+            };
+            get('/tasks', ok);
+        }
+
+    };
+
+    function toTableRow(fields, row) {
+        const $row = $('<tr>');
+        $.each(fields, function (index, field) {
+            let value = '';
+
+            if (field.endsWith('_time')) {
+                value = formatDate(row[field]);
+            } else {
+                value = row[field];
+            }
+
+            $span = $('<span/>').text(value);
+
+            switch (value) {
+                case 'error':
+                    $span.addClass('badge badge-lg badge-danger');
+                    break;
+                case 'incomplete':
+                    $span.addClass('badge badge-lg badge-warning');
+                    break;
+                case 'complete':
+                case 'loaded':
+                case 'disk':
+                    $span.addClass('badge badge-lg badge-success');
+            }
+
+            $td = $('<td>').append($span);
+            $row.append($td);
+        });
+        return $row;
+    }
+
+    function showDelModal(text) {
+        $('#msg-modal-msg').text(text);
+        $('#msg-modal').modal('show');
+    }
+
+    function get(url, ok, error, caller) {
+        ajax('GET', url, '', ok, error, caller);
+    }
+
+    function postJson(url, data, ok, error, caller) {
+        ajax('POST', url, JSON.stringify(data), ok, error, caller);
+    }
+
+    function ajax(method, url, data, ok, error, caller) {
+        $.ajax({
+            url: API_PREFIX + url,
+            type: method,
+            data: data,
+            contentType: 'application/json',
+            success: function (response) {
+                if (!ok) {
+                    console.log('ajax request successful:', response);
+                    return;
+                }
+                if (caller) {
+                    ok.appply(caller, response);
+                } else {
+                    ok(response);
+                }
+            },
+            error: function (err) {
+                if (err.status === 401) {
+                    showDelModal('Login First!');
+                    return;
+                } else {
+                    console.log('ajax request failed:', err);
+                }
+                if (!error) {
+                    let data = JSON.parse(err.responseText);
+                    showDelModal(data.message);
+                    return;
+                }
+                if (caller) {
+                    error.appley(caller, err);

Review Comment:
   `error.appley` is a typo and will throw at runtime when `caller` is 
provided. Similar to the success path, use `error.apply(caller, [err])` (or 
`error.call(caller, err)`) so the error callback is invoked correctly.
   



##########
vermeer/Makefile:
##########
@@ -66,20 +71,25 @@ clean-all: clean
        @rm -rf tools/protoc/*/protoc
        @rm -rf tools/protoc/*/bin
        @rm -rf tools/protoc/*/include
+       @echo "Cleaning downloaded UI assets..."
+       @rm -rf ui/node_modules ui/package-lock.json
+       @rm -rf ui/ui/lib/bootstrap-4.3.1-dist ui/ui/lib/bootstrap4-glyphicons
+       @rm -f ui/ui/lib/jquery-3.5.1.min.js ui/ui/lib/jquery-license
        @echo "All clean completed!"
 
 # Help
 help:
        @echo "Vermeer Build System"
        @echo ""
        @echo "Usage:"
-       @echo "  make init              - First time setup (download binaries + 
go mod download)"
+       @echo "  make init              - First time setup (download binaries + 
UI assets + go mod download)"
        @echo "  make download-binaries - Download supervisord and protoc 
binaries for your platform"
+       @echo "  make download-ui-assets- Download jQuery, Bootstrap, 
Glyphicons to ui/ui/lib/"

Review Comment:
   The help output has a formatting typo: `make download-ui-assets- Download 
...` is missing a space after the target name, which makes the usage text 
misleading. Update the string to include the missing space (and align spacing 
with the other help lines).
   



##########
vermeer/build.sh:
##########
@@ -33,6 +33,10 @@ go mod download
 echo "Checking binary dependencies..."
 ./scripts/download_binaries.sh
 
+# Download UI assets if not exist
+echo "Checking UI assets..."
+./scripts/download_ui_assets.sh

Review Comment:
   The comment says “Download UI assets if not exist”, but the script 
unconditionally runs `./scripts/download_ui_assets.sh` on every build. This 
will re-run `npm install` each time, increasing build time and making builds 
more fragile/offline-unfriendly. Consider adding an existence check (e.g., for 
the expected files under `ui/ui/lib/`) before invoking the download script, or 
adjust the comment to match the behavior.
   



##########
vermeer/scripts/download_ui_assets.sh:
##########
@@ -0,0 +1,159 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+set -e
+
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
+UI_DIR="$PROJECT_ROOT/ui"
+LIB_DIR="$PROJECT_ROOT/ui/ui/lib"
+
+# Glyphicons source (GitHub raw)
+GLYPHICONS_BASE="https://raw.githubusercontent.com/Darkseal/bootstrap4-glyphicons/master/bootstrap4-glyphicons";
+GLYPHICONS_COMMIT="master"
+

Review Comment:
   `GLYPHICONS_COMMIT` is defined but never used, and the download URLs are 
pinned to the moving `master` branch. This makes builds non-reproducible and 
increases supply-chain risk (remote content can change without a version bump). 
Consider pinning to a specific commit/tag and either using `GLYPHICONS_COMMIT` 
in the URL or removing it if not needed.



##########
vermeer/config/worker.ini:
##########
@@ -12,12 +12,13 @@
 ; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 ; See the License for the specific language governing permissions and
 ; limitations under the License.
-  
 [default]
 log_level=info
 debug_mode=release
 http_peer=0.0.0.0:6788
 grpc_peer=0.0.0.0:6789
 master_peer=127.0.0.1:6689
 run_mode=worker
-worker_group=default
\ No newline at end of file
+worker_group=$

Review Comment:
   `worker_group` is set to `$`, which looks like an accidental 
placeholder/typo and will change worker grouping behavior compared to the 
previous `default`. This will likely break deployments expecting the default 
group name. Set it back to a valid concrete value (e.g., `default`) or 
document/parameterize it properly.
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to