From 7d0c781e07d3b9cffc16628a65cddad30c7ad325 Mon Sep 17 00:00:00 2001 From: Loki Date: Mon, 25 May 2026 15:02:35 +0800 Subject: [PATCH 1/3] add Web UI build pipeline --- README.md | 2 +- vermeer/.dockerignore | 22 +++- vermeer/.gitignore | 8 ++ vermeer/Dockerfile | 4 +- vermeer/Makefile | 20 ++- vermeer/README.md | 49 ++++++- vermeer/asset/asset.go | 2 +- vermeer/asset/asset_dev_ui.go | 2 +- vermeer/build.sh | 4 + vermeer/config/worker.ini | 21 +-- vermeer/scripts/download_ui_assets.sh | 159 +++++++++++++++++++++++ vermeer/ui/package.json | 10 ++ vermeer/ui/ui/lib/functions.js | 179 ++++++++++++++++++++++++++ vermeer/ui/ui/lib/vermeer.css | 53 ++++++++ 14 files changed, 502 insertions(+), 33 deletions(-) create mode 100644 vermeer/scripts/download_ui_assets.sh create mode 100644 vermeer/ui/package.json create mode 100644 vermeer/ui/ui/lib/functions.js create mode 100644 vermeer/ui/ui/lib/vermeer.css diff --git a/README.md b/README.md index c9fda699c..2f742e162 100644 --- a/README.md +++ b/README.md @@ -146,7 +146,7 @@ For quick start and single-machine deployments, we recommend **Vermeer**: # Pull the image docker pull hugegraph/vermeer:latest -# Change config path in docker-compose.yml +# Change config path in docker-compose.yaml volumes: - ~/:/go/bin/config # Change here to your actual config path, e.g., vermeer/config diff --git a/vermeer/.dockerignore b/vermeer/.dockerignore index 6e8f887b7..697abfb06 100644 --- a/vermeer/.dockerignore +++ b/vermeer/.dockerignore @@ -14,6 +14,22 @@ # See the License for the specific language governing permissions and # limitations under the License. # -*data* -*test* -*.git* \ No newline at end of file +.git/ +.gitignore +.gitattributes +test/ +vermeer_test.go +vermeer_test.sh +vermeer_data/ +asset/assets_vfsdata.go +ui/node_modules/ +ui/package-lock.json +ui/ui/lib/bootstrap-4.3.1-dist/ +ui/ui/lib/bootstrap4-glyphicons/ +ui/ui/lib/jquery-3.5.1.min.js +ui/ui/lib/jquery-license +_tmp_extractor/ +tools/supervisord/*/supervisord +tools/protoc/*/protoc +tools/protoc/*/bin/ +tools/protoc/*/include/ \ No newline at end of file diff --git a/vermeer/.gitignore b/vermeer/.gitignore index 8fb775a2e..9907f62df 100644 --- a/vermeer/.gitignore +++ b/vermeer/.gitignore @@ -99,6 +99,14 @@ tools/protoc/*/include/ ###################### asset/assets_vfsdata.go +# UI dependencies (downloaded via scripts/download_ui_assets.sh) # +###################### +ui/node_modules/ +ui/package-lock.json +ui/ui/lib/* +!ui/ui/lib/functions.js +!ui/ui/lib/vermeer.css + # AI assistant specific files (we only maintain AGENTS.md) # ###################### CLAUDE.md diff --git a/vermeer/Dockerfile b/vermeer/Dockerfile index 75a65bf66..49c36ce0f 100644 --- a/vermeer/Dockerfile +++ b/vermeer/Dockerfile @@ -15,10 +15,12 @@ # limitations under the License. # FROM golang:1.23-alpine AS builder +RUN apk add --no-cache npm bash curl COPY ./ /src/ WORKDIR /src/ ENV CGO_ENABLED="0" -RUN cd asset && go generate +RUN ./scripts/download_ui_assets.sh && ls -la ui/ui/lib/ +RUN cd asset && go generate && wc -l assets_vfsdata.go RUN go build -o /go/bin/app FROM alpine diff --git a/vermeer/Makefile b/vermeer/Makefile index ba9cb73f3..f99b51b03 100644 --- a/vermeer/Makefile +++ b/vermeer/Makefile @@ -15,13 +15,13 @@ # limitations under the License. # -.PHONY: all init download-binaries generate-assets build clean help +.PHONY: all init download-binaries download-ui-assets generate-assets build clean help # Default target all: generate-assets build # Initialize project (first time setup) -init: download-binaries +init: download-binaries download-ui-assets @echo "Installing Go dependencies..." go mod download @echo "Project initialized successfully!" @@ -31,8 +31,13 @@ download-binaries: @echo "Downloading binary dependencies..." @./scripts/download_binaries.sh || (echo "Failed to download binaries" && exit 1) +# Download UI assets (jQuery, Bootstrap, Glyphicons) +download-ui-assets: + @echo "Downloading UI assets..." + @./scripts/download_ui_assets.sh || (echo "Failed to download UI assets" && exit 1) + # Generate assets (vfsdata.go for web UI) -generate-assets: +generate-assets: download-ui-assets @echo "Generating assets..." @cd asset && go generate || (echo "Failed to generate assets" && exit 1) @echo "Assets generated successfully!" @@ -66,6 +71,10 @@ 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 @@ -73,13 +82,14 @@ 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/" @echo " make generate-assets - Generate assets_vfsdata.go from web UI (required before build)" @echo " make build - Build vermeer for current platform (default: local architecture)" @echo " make build-linux-amd64 - Build for Linux AMD64 (for deployment)" @echo " make build-linux-arm64 - Build for Linux ARM64 (for ARM servers)" @echo " make clean - Remove generated files and binaries (keep downloaded tools)" - @echo " make clean-all - Remove everything including downloaded tools" + @echo " make clean-all - Remove everything including downloaded tools and UI assets" @echo " make all - Generate assets and build (default target)" @echo " make help - Show this help message" diff --git a/vermeer/README.md b/vermeer/README.md index 384256375..97cf58d71 100644 --- a/vermeer/README.md +++ b/vermeer/README.md @@ -89,7 +89,12 @@ vermeer/ │ ├── master.ini │ └── worker.ini ├── tools/ # Binary dependencies (supervisord, protoc) -└── ui/ # Web dashboard +└── ui/ # Web dashboard source and dependencies + ├── package.json # Frontend dependencies (jQuery, Bootstrap) + └── ui/ + ├── master.html # Master dashboard page + ├── worker.html # Worker dashboard page + └── lib/ # Downloaded at build time (git-ignored) ``` ## Quick Start @@ -107,8 +112,8 @@ Create a dedicated config directory (e.g., `~/vermeer-config/`) with `master.ini Run with Docker: ```bash -# Master node -docker run -v ~/vermeer-config:/go/bin/config hugegraph/vermeer --env=master +# Master node (with Web UI enabled) +docker run -v ~/vermeer-config:/go/bin/config hugegraph/vermeer --env=master -auth token -auth_token_factor 1234 # Worker node docker run -v ~/vermeer-config:/go/bin/config hugegraph/vermeer --env=worker @@ -158,6 +163,7 @@ Configure parameters in `vermeer.sh`, then: #### Prerequisites - Go 1.23 or later +- Node.js/npm (for downloading UI assets) - `curl` and `unzip` utilities (for downloading dependencies) - Internet connection (for first-time setup) @@ -226,10 +232,41 @@ task_strategy = 1 # Number of parallel tasks task_parallel_num = 1 + +# Authentication mode (none or token) +# Set to "token" to enable Web UI and API authentication +auth = none + +# Token authentication factor (required when auth=token) +auth_token_factor = 1234 ``` **Note**: HugeGraph connection details (`pd_peers`, `server`, `graph`) are provided in the graph load API request, not in the configuration file. See [HugeGraph Integration](#hugegraph-integration) section for details. +### Generating Auth Token + +When `auth=token` is enabled, you need to generate a token for login. Use the built-in token generator: + +```bash +# Run with Go +go run tools/token/token_generator.go -user admin -space default -factor 1234 -i + +# Or run in Docker (no local Go required) +docker run --rm -v $(pwd):/src -v $(pwd)/config:/src/config golang:1.23-alpine sh -c "cd /src && go run tools/token/token_generator.go -user admin -space default -factor 1234 -i" +``` + +Parameters: +- `-user` — Username (default: `foo`) +- `-space` — Graph space name (default: `bar`) +- `-factor` — Must match `auth_token_factor` in `master.ini` +- `-client` — Client identifier (default: `hg`) +- `-i` — Generate immortal token (never expires) + +The generated token can be used in two ways: + +1. **HTTP Header**: `Authorization: Bearer ` +2. **Login API**: `POST /api/v1/login` with body `{"token": ""}` (sets cookie) + ### Worker Configuration (`worker.ini`) ```ini @@ -278,6 +315,8 @@ worker_group = default Vermeer exposes a REST API on port `6688` (configurable in `master.ini`). +> **Note**: The Web UI (`/ui/*`) is only available when `auth=token` is configured. Set `auth_token_factor` in `master.ini` or pass `-auth token -auth_token_factor ` via command line. + ### Key Endpoints | Endpoint | Method | Description | @@ -502,7 +541,9 @@ vermeer/tools/protoc/linux64/protoc vermeer/apps/protos/*.proto --go-grpc_out=ve ## Monitoring -Access the Web UI dashboard at `http://master-ip:6688/ui/` for: +Access the Web UI dashboard at `http://master-ip:6688/ui/master.html` for: + +> **Note**: The Web UI requires `auth=token` in `master.ini` (or `-auth token` via command line). Without token authentication, the UI routes are not registered. - Worker status and resource usage - Active and completed tasks diff --git a/vermeer/asset/asset.go b/vermeer/asset/asset.go index a2f158918..040a4361c 100644 --- a/vermeer/asset/asset.go +++ b/vermeer/asset/asset.go @@ -25,4 +25,4 @@ import ( ) // Assets contains the project's assets. -var Assets http.FileSystem = http.Dir("../ui") +var Assets http.FileSystem = http.Dir("../ui/ui") diff --git a/vermeer/asset/asset_dev_ui.go b/vermeer/asset/asset_dev_ui.go index 1b82cab50..60a0b3a67 100644 --- a/vermeer/asset/asset_dev_ui.go +++ b/vermeer/asset/asset_dev_ui.go @@ -27,5 +27,5 @@ import ( func init() { // for ui development - Assets = http.Dir(common.AppRootPath() + "/ui/") + Assets = http.Dir(common.AppRootPath() + "/ui/ui/") } diff --git a/vermeer/build.sh b/vermeer/build.sh index 47cd42e33..716e02c7e 100644 --- a/vermeer/build.sh +++ b/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 + # Generate assets if not exist if [ ! -f "asset/assets_vfsdata.go" ]; then echo "Generating assets..." diff --git a/vermeer/config/worker.ini b/vermeer/config/worker.ini index c11cbc4a3..bfd855e34 100644 --- a/vermeer/config/worker.ini +++ b/vermeer/config/worker.ini @@ -1,23 +1,10 @@ -; 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. - [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 +master_peer=172.25.0.10:6689 run_mode=worker -worker_group=default \ No newline at end of file +worker_group=$ +auth=none +auth_token_factor=1234 diff --git a/vermeer/scripts/download_ui_assets.sh b/vermeer/scripts/download_ui_assets.sh new file mode 100644 index 000000000..e9fc70867 --- /dev/null +++ b/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" + + # Step 2: Create lib directory structure + log_info "Copying files to ui/ui/lib/..." + mkdir -p "$LIB_DIR" + + # Copy jQuery (npm names it jquery.min.js, rename to match original) + cp "$UI_DIR/node_modules/jquery/dist/jquery.min.js" "$LIB_DIR/jquery-3.5.1.min.js" + + # Copy Bootstrap CSS + mkdir -p "$LIB_DIR/bootstrap-4.3.1-dist/css" + cp "$UI_DIR/node_modules/bootstrap/dist/css/bootstrap.min.css" "$LIB_DIR/bootstrap-4.3.1-dist/css/" + cp "$UI_DIR/node_modules/bootstrap/dist/css/bootstrap.min.css.map" "$LIB_DIR/bootstrap-4.3.1-dist/css/" + cp "$UI_DIR/node_modules/bootstrap/dist/css/bootstrap-grid.min.css" "$LIB_DIR/bootstrap-4.3.1-dist/css/" + cp "$UI_DIR/node_modules/bootstrap/dist/css/bootstrap-grid.min.css.map" "$LIB_DIR/bootstrap-4.3.1-dist/css/" + cp "$UI_DIR/node_modules/bootstrap/dist/css/bootstrap-reboot.min.css" "$LIB_DIR/bootstrap-4.3.1-dist/css/" + cp "$UI_DIR/node_modules/bootstrap/dist/css/bootstrap-reboot.min.css.map" "$LIB_DIR/bootstrap-4.3.1-dist/css/" + + # Copy Bootstrap JS + mkdir -p "$LIB_DIR/bootstrap-4.3.1-dist/js" + cp "$UI_DIR/node_modules/bootstrap/dist/js/bootstrap.min.js" "$LIB_DIR/bootstrap-4.3.1-dist/js/" + cp "$UI_DIR/node_modules/bootstrap/dist/js/bootstrap.min.js.map" "$LIB_DIR/bootstrap-4.3.1-dist/js/" + cp "$UI_DIR/node_modules/bootstrap/dist/js/bootstrap.bundle.min.js" "$LIB_DIR/bootstrap-4.3.1-dist/js/" + cp "$UI_DIR/node_modules/bootstrap/dist/js/bootstrap.bundle.min.js.map" "$LIB_DIR/bootstrap-4.3.1-dist/js/" + + # Copy Bootstrap LICENSE + cp "$UI_DIR/node_modules/bootstrap/LICENSE" "$LIB_DIR/bootstrap-4.3.1-dist/" + + # Create jquery-license (MIT) + cat > "$LIB_DIR/jquery-license" << 'JQLICENSE' +Copyright JS Foundation and other contributors, https://js.foundation/ + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +JQLICENSE + + # Step 3: Download Bootstrap4 Glyphicons from GitHub + log_info "Downloading Bootstrap4 Glyphicons from GitHub..." + download_file "$GLYPHICONS_BASE/css/bootstrap-glyphicons.min.css" \ + "$LIB_DIR/bootstrap4-glyphicons/css/bootstrap-glyphicons.min.css" + + # Download fontawesome fonts + for font in fa-brands-400 fa-regular-400 fa-solid-900; do + for ext in eot svg ttf woff woff2; do + download_file "$GLYPHICONS_BASE/fonts/fontawesome/${font}.${ext}" \ + "$LIB_DIR/bootstrap4-glyphicons/fonts/fontawesome/${font}.${ext}" + done + done + + # Download glyphicons fonts + for ext in eot svg ttf woff woff2; do + download_file "$GLYPHICONS_BASE/fonts/glyphicons/glyphicons-halflings-regular.${ext}" \ + "$LIB_DIR/bootstrap4-glyphicons/fonts/glyphicons/glyphicons-halflings-regular.${ext}" + done + + # Download maps + download_file "$GLYPHICONS_BASE/maps/glyphicons-fontawesome.less" \ + "$LIB_DIR/bootstrap4-glyphicons/maps/glyphicons-fontawesome.less" + download_file "$GLYPHICONS_BASE/maps/glyphicons-fontawesome.min.css" \ + "$LIB_DIR/bootstrap4-glyphicons/maps/glyphicons-fontawesome.min.css" + + log_info "All UI assets downloaded successfully!" + log_info "" + log_info "Downloaded to: $LIB_DIR" + log_info " - jQuery 3.5.1" + log_info " - Bootstrap 4.3.1 (CSS + JS + source maps)" + log_info " - Bootstrap4 Glyphicons (CSS + fonts + maps)" +} + +main "$@" diff --git a/vermeer/ui/package.json b/vermeer/ui/package.json new file mode 100644 index 000000000..978c95d8b --- /dev/null +++ b/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" + } +} diff --git a/vermeer/ui/ui/lib/functions.js b/vermeer/ui/ui/lib/functions.js new file mode 100644 index 000000000..ee3726590 --- /dev/null +++ b/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(''); + $tr = $t.find('thead tr'); + $.each(fields, function (index, field) { + $tr.append($('').text(field)); + }); + + const ok = function (data) { + const $tb = $t.append(''); + 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(''); + const $tr = $t.find('thead tr'); + $.each(fields, function (index, field) { + $tr.append($('').text(field)); + }); + + const ok = function (data) { + const $tb = $t.append(''); + rows = data.tasks; + $.each(rows, function (index, row) { + $tb.append(toTableRow(fields, row)); + }); + }; + get('/tasks', ok); + } + + }; + + function toTableRow(fields, row) { + const $row = $(''); + $.each(fields, function (index, field) { + let value = ''; + + if (field.endsWith('_time')) { + value = formatDate(row[field]); + } else { + value = row[field]; + } + + $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 = $('').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); + } else { + error(err); + } + } + }); + } + + function formatDate(inputDate) { + const date = new Date(inputDate); + const year = date.getFullYear(); + const month = (date.getMonth() + 1).toString().padStart(2, '0'); + const day = date.getDate().toString().padStart(2, '0'); + const hours = date.getHours().toString().padStart(2, '0'); + const minutes = date.getMinutes().toString().padStart(2, '0'); + const seconds = date.getSeconds().toString().padStart(2, '0'); + + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; + } + +})(); + +$(function () { + vermeer.qeuryGraphs(); + vermeer.queryTasks(); +}); diff --git a/vermeer/ui/ui/lib/vermeer.css b/vermeer/ui/ui/lib/vermeer.css new file mode 100644 index 000000000..a35ba7eff --- /dev/null +++ b/vermeer/ui/ui/lib/vermeer.css @@ -0,0 +1,53 @@ +/* + * 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. + */ +/* Move down content because we have a fixed navbar that is 50px tall with 20px padding */ +body { + padding-top: 70px; + padding-bottom: 20px; +} + +.state_indicator { + padding: 0 4px 0 4px; +} + +.literal_output td { + font-family: monospace; +} + +.cursor-pointer { + cursor: pointer; +} + +.tooltip-inner { + max-width: none; + text-align: left; +} + +.label { + white-space: normal; +} + +/* The navbar adds horizontal padding already */ +.navbar .container-fluid { + padding: 0; +} + +/* This class provides style for containers that hold card like (without background) objects within container-fluid and out of accordion */ +.blank-card { + padding-bottom: 50px; + padding-right: 25px; +} \ No newline at end of file From 4224e2ae3d70448e2e3443ad39bd48779fcb98e6 Mon Sep 17 00:00:00 2001 From: Loki Date: Mon, 25 May 2026 15:21:24 +0800 Subject: [PATCH 2/3] add license --- vermeer/config/worker.ini | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/vermeer/config/worker.ini b/vermeer/config/worker.ini index bfd855e34..c1c77b930 100644 --- a/vermeer/config/worker.ini +++ b/vermeer/config/worker.ini @@ -1,9 +1,23 @@ +; 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. [default] log_level=info debug_mode=release http_peer=0.0.0.0:6788 grpc_peer=0.0.0.0:6789 -master_peer=172.25.0.10:6689 +master_peer=127.0.0.1:6689 run_mode=worker worker_group=$ auth=none From 8d5a4923b2f978ef6fde458e7ed67a3916076308 Mon Sep 17 00:00:00 2001 From: Loki Date: Mon, 25 May 2026 16:34:54 +0800 Subject: [PATCH 3/3] fix copilot message --- vermeer/Makefile | 2 +- vermeer/build.sh | 4 +++- vermeer/config/worker.ini | 2 +- vermeer/scripts/download_ui_assets.sh | 7 ++++--- vermeer/ui/package.json | 3 ++- vermeer/ui/ui/lib/functions.js | 22 ++++++++++++---------- 6 files changed, 23 insertions(+), 17 deletions(-) diff --git a/vermeer/Makefile b/vermeer/Makefile index f99b51b03..a621cdb6c 100644 --- a/vermeer/Makefile +++ b/vermeer/Makefile @@ -84,7 +84,7 @@ help: @echo "Usage:" @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/" + @echo " make download-ui-assets - Download jQuery, Bootstrap, Glyphicons to ui/ui/lib/" @echo " make generate-assets - Generate assets_vfsdata.go from web UI (required before build)" @echo " make build - Build vermeer for current platform (default: local architecture)" @echo " make build-linux-amd64 - Build for Linux AMD64 (for deployment)" diff --git a/vermeer/build.sh b/vermeer/build.sh index 716e02c7e..216460fca 100644 --- a/vermeer/build.sh +++ b/vermeer/build.sh @@ -35,7 +35,9 @@ echo "Checking binary dependencies..." # Download UI assets if not exist echo "Checking UI assets..." -./scripts/download_ui_assets.sh + if [ ! -d "ui/ui/lib" ] || [ -z "$(ls -A ui/ui/lib 2>/dev/null)" ]; then + ./scripts/download_ui_assets.sh + fi # Generate assets if not exist if [ ! -f "asset/assets_vfsdata.go" ]; then diff --git a/vermeer/config/worker.ini b/vermeer/config/worker.ini index c1c77b930..5bfd38b44 100644 --- a/vermeer/config/worker.ini +++ b/vermeer/config/worker.ini @@ -19,6 +19,6 @@ 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=$ +worker_group=default auth=none auth_token_factor=1234 diff --git a/vermeer/scripts/download_ui_assets.sh b/vermeer/scripts/download_ui_assets.sh index e9fc70867..dadb855d2 100644 --- a/vermeer/scripts/download_ui_assets.sh +++ b/vermeer/scripts/download_ui_assets.sh @@ -17,15 +17,16 @@ # set -e +set -o pipefail 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" +# Glyphicons source (GitHub raw) - pinned to specific commit for reproducible builds +GLYPHICONS_COMMIT="f7b1a17bbe64308d1d8b2b4bb2ba8a0ea621b377" +GLYPHICONS_BASE="https://raw.githubusercontent.com/Darkseal/bootstrap4-glyphicons/${GLYPHICONS_COMMIT}/bootstrap4-glyphicons" # Colors for output RED='\033[0;31m' diff --git a/vermeer/ui/package.json b/vermeer/ui/package.json index 978c95d8b..df2717387 100644 --- a/vermeer/ui/package.json +++ b/vermeer/ui/package.json @@ -5,6 +5,7 @@ "description": "Frontend dependencies for Vermeer UI (downloaded at build time for ASF compliance)", "dependencies": { "jquery": "3.5.1", - "bootstrap": "4.3.1" + "bootstrap": "4.3.1", + "popper.js": "1.14.7" } } diff --git a/vermeer/ui/ui/lib/functions.js b/vermeer/ui/ui/lib/functions.js index ee3726590..8bbeba155 100644 --- a/vermeer/ui/ui/lib/functions.js +++ b/vermeer/ui/ui/lib/functions.js @@ -32,19 +32,20 @@ } ); }, - qeuryGraphs: function () { + queryGraphs: 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(''); - $tr = $t.find('thead tr'); + const $tr = $t.find('thead tr'); $.each(fields, function (index, field) { $tr.append($('').text(field)); }); const ok = function (data) { - const $tb = $t.append(''); + const $tb = $(''); + $t.append($tb); const rows = data.graphs; $.each(rows, function (index, row) { $tb.append(toTableRow(fields, row)); @@ -65,8 +66,9 @@ }); const ok = function (data) { - const $tb = $t.append(''); - rows = data.tasks; + const $tb = $(''); + $t.append($tb); + const rows = data.tasks; $.each(rows, function (index, row) { $tb.append(toTableRow(fields, row)); }); @@ -87,7 +89,7 @@ value = row[field]; } - $span = $('').text(value); + const $span = $('').text(value); switch (value) { case 'error': @@ -102,7 +104,7 @@ $span.addClass('badge badge-lg badge-success'); } - $td = $('').append($span); + const $td = $('').append($span); $row.append($td); }); return $row; @@ -133,7 +135,7 @@ return; } if (caller) { - ok.appply(caller, response); + ok.apply(caller, [response]); } else { ok(response); } @@ -151,7 +153,7 @@ return; } if (caller) { - error.appley(caller, err); + error.apply(caller, [err]); } else { error(err); } @@ -174,6 +176,6 @@ })(); $(function () { - vermeer.qeuryGraphs(); + vermeer.queryGraphs (); vermeer.queryTasks(); });