圭表 is a Nuxt 3 full-stack application for data visualization, analysis, and data processing workflows.
view/edit/manage resource permissions for analyses, dashboards, datasets, and data sources, plus access applications, approval notifications, and public read-only fallback for content resources./dev/*, guarded by ENABLE_DEMO_PAGES.packageManager pins [email protected]; corepack enable installs it)Install with pnpm only: package.json carries pnpm.overrides that pin @nuxt/cli and @nuxt/schema, so npm or yarn resolve a different, untested dependency tree.
git clone [repository-url]
cd guibiao
pnpm install
The application reads configuration through two chains, but the repository keeps only three files:
env/.env.daily, env/.env.pre: daily and pre-release application runtime configuration, loaded by pnpm dev / pnpm dev:pre / pnpm build:pre through --dotenv.env/.env.prod: production configuration. One file serves both chains — Docker Compose deployment (docker compose --env-file env/.env.prod) and bare-metal PM2 / direct connection (pnpm build:prod, start:prod through --dotenv). Compose uses it to interpolate images, container names, host ports and bind-mount source paths, then injects NUXT_* variables so the app reaches the metadata MySQL, Redis, and the knowledge base embedding (Infinity) and qdrant services; postgres-data, clickhouse-data, sqlserver-data, oracle-data and mongodb-data host the optional PostgreSQL, ClickHouse, SQL Server, Oracle and MongoDB demo business databases. Data source connections are registered at runtime on the Data Sources page instead of via environment variables. Within the same chain one setting can have two key names (for example MYSQL_ROOT_PASSWORD and SERVICE_DB_PASSWORD, APP_CONTAINER_NAME and MONITOR_NUXT_CONTAINER_NAME); both must carry the same value. Database addresses use the host LAN IP plus the published port, with MONITOR_PROMETHEUS_URL as the only exception — it stays a container name because Prometheus publishes no host port.This repository is private and these environment files (with real values) are committed directly. When adding a new key, update the key set in the files below and keep each environment's value consistent with the actual deployment:
env/.env.daily, env/.env.pre, env/.env.prodExample env/.env.daily:
NODE_ENV=daily
PORT=12581
# Application title
APP_NAME=圭表
APP_VERSION=daily-local
API_BASE=/api
ENABLE_DEMO_PAGES=false
# Database
SERVICE_DB_HOST=127.0.0.1
SERVICE_DB_PORT=3310
SERVICE_DB_USER=root
SERVICE_DB_PASSWORD=change_me
SERVICE_DB_NAME=guibiao
SERVICE_DB_TIMEZONE=+08:00
SERVICE_DB_DATE_STRINGS=true
SERVICE_DB_DECIMAL_NUMBERS=true
SERVICE_DB_CONNECTION_LIMIT=20
SERVICE_DB_CONNECT_TIMEOUT=10000
SERVICE_DB_IDLE_TIMEOUT=60000
# Encryption key for data source passwords at rest; 32-byte hex string (64 chars)
# Generate: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
DATA_SOURCE_SECRET_KEY=change_me
# Redis
SERVICE_REDIS_HOST=127.0.0.1
SERVICE_REDIS_PORT=6383
SERVICE_REDIS_USERNAME=default
SERVICE_REDIS_PASSWORD=change_me
SERVICE_REDIS_DB=0
SERVICE_REDIS_BASE=guibiao-redis
# Logs
LOG_PATH=./logs
LOG_TIME_FORMAT=YYYY-MM-DD HH:mm:ss
LOG_LEVEL=info
# Auth and email
JWT_SECRET_KEY=replace_with_a_long_random_secret
JWT_EXPIRES_IN=2h
JWT_REFRESH_EXPIRES_IN=30d
AUTH_SESSION_FAIL_MODE=strict
SM2_PUBLIC_KEY=replace_with_sm2_public_key
SM2_PRIVATE_KEY=replace_with_sm2_private_key
# Session cookie SameSite policy: lax (default) / strict / none.
# Cross-origin development needs none; browsers then require Secure (set automatically),
# so cookies only work over HTTPS — plain-http cross-origin cannot carry cookies.
AUTH_COOKIE_SAMESITE=lax
# Comma-separated origins allowed to call the API cross-origin
CORS_ALLOWED_ORIGINS=
ENABLE_SOCKET_DEMO=false
SOCKET_ALLOWED_ORIGINS=
SMTP_HOST=smtp.example.com
SMTP_PORT=465
SMTP_SECURE=true
SMTP_REJECT_UNAUTHORIZED=true
[email protected]
SMTP_PASS=your_smtp_password
[email protected]
# LLM AI
LLM_PROVIDER=openai-compatible
LLM_API_KEY=
LLM_BASE_URL=
LLM_MODEL=
# Knowledge index
# Index provider: qdrant (native Qdrant) or mock (local stub); leave empty to disable
KNOWLEDGE_INDEX_PROVIDER=
# Qdrant HTTP base URL; use http://qdrant:6333 from the Compose app container
KNOWLEDGE_INDEX_BASE_URL=
# Qdrant API key
KNOWLEDGE_INDEX_API_KEY=
# Knowledge index request timeout in milliseconds
KNOWLEDGE_INDEX_TIMEOUT_MS=30000
# OpenAI-compatible Embedding base URL; /embeddings is appended automatically
KNOWLEDGE_EMBEDDING_BASE_URL=
# Embedding API key, independent from LLM_API_KEY
KNOWLEDGE_EMBEDDING_API_KEY=
# Embedding model and its native vector dimension
KNOWLEDGE_EMBEDDING_MODEL=
KNOWLEDGE_EMBEDDING_DIMENSIONS=
KNOWLEDGE_EMBEDDING_BATCH_SIZE=16
KNOWLEDGE_EMBEDDING_TIMEOUT_MS=30000
# Retrieval similarity floor (cosine): hits below it are dropped, 0 disables the filter.
# Calibrated for bge-m3; re-calibrate whenever the embedding model changes.
KNOWLEDGE_RETRIEVAL_MIN_SCORE=0.54
# Optional per-path recall candidate cap (vector and keyword recall each fetch up to this many).
# Unset or blank falls back to the built-in default of 20; non-integer or out-of-range values (1-50) fail retrieval.
KNOWLEDGE_CANDIDATE_TOP_K=
# Character budget of one chunk, and the overlap used when splitting plain text.
# The overlap must stay smaller than the budget, and the budget well below the embedding context window.
KNOWLEDGE_CHUNK_MAX_CHARACTERS=800
KNOWLEDGE_CHUNK_OVERLAP=100
# Index sync lock TTL in milliseconds; also how long before a stale running task is reclaimed.
KNOWLEDGE_INDEX_LOCK_TTL_MS=600000
# Tasks claimed per worker tick, and the worker polling interval in milliseconds.
KNOWLEDGE_INDEX_WORKER_BATCH_SIZE=10
KNOWLEDGE_INDEX_WORKER_INTERVAL_MS=2000
# Retry back-off for failed index tasks, in minutes: min(base * 2^(n-1), max).
KNOWLEDGE_INDEX_RETRY_BASE_MINUTES=5
KNOWLEDGE_INDEX_RETRY_MAX_MINUTES=60
# Attempts allowed before a non-delete index task reaches a terminal state.
KNOWLEDGE_INDEX_MAX_ATTEMPTS=3
# Delay in minutes before retrying a task while no index provider is configured.
KNOWLEDGE_INDEX_PROVIDER_MISSING_DELAY_MINUTES=30
# Optional cross-encoder rerank service speaking the /rerank protocol (Jina, bge-reranker, ...).
# Leave the base URL empty to keep the plain vector score order.
KNOWLEDGE_RERANK_BASE_URL=
KNOWLEDGE_RERANK_API_KEY=
KNOWLEDGE_RERANK_MODEL=BAAI/bge-reranker-v2-m3
KNOWLEDGE_RERANK_TIMEOUT_MS=10000
# Monitor
MONITOR_PROMETHEUS_URL=http://localhost:9090
MONITOR_METRICS_TOKEN=replace_with_a_long_random_monitor_token
MONITOR_DISK_MOUNTPOINT=/
MONITOR_LOG_DISK_MOUNTPOINT=/
MONITOR_NUXT_CONTAINER_NAME=guibiao-app-container
MONITOR_CONTAINER_METRICS_ENABLED=false
MONITOR_REFRESH_INTERVAL_SECONDS=5
MONITOR_RETENTION_DAYS=30
MONITOR_MAX_HISTORY_POINTS=2000
# Log cleanup
SCHEDULED_EMAIL_LOG_RETENTION_DAYS=90
ANALYSIS_ALARM_LOG_RETENTION_DAYS=90
NOTIFICATION_READ_RETENTION_DAYS=90
SYSTEM_BROADCAST_ENDED_RETENTION_DAYS=365
ACCESS_APPLY_RESOLVED_RETENTION_DAYS=365
KNOWLEDGE_RETRIEVAL_LOG_RETENTION_DAYS=90
USER_LOGIN_LOG_RETENTION_DAYS=365
# Scheduled email recovery
SCHEDULED_EMAIL_RUNNING_TIMEOUT_MINUTES=10
SCHEDULED_EMAIL_RECOVERY_INTERVAL_MINUTES=5
SCHEDULED_EMAIL_ORPHANED_EXECUTIONS_THRESHOLD_MINUTES=30
# Distributed execution history cleanup
SCHEDULED_JOB_EXECUTION_COMPLETED_RETENTION_DAYS=30
SCHEDULED_JOB_EXECUTION_FAILED_RETENTION_DAYS=15
SCHEDULED_JOB_EXECUTION_CLEANUP_CRON=15 3 * * *
The schema and demo data are plain dumps under sql/, applied by hand — neither Compose nor the app imports them, and the app never migrates its own metadata schema:
sql/guibiao.sql: platform metadata schema (resources, permissions, datasets, schedules, knowledge bases, logs). Import it into the database named by SERVICE_DB_NAME; the dump has no CREATE DATABASE.sql/mysql_center.sql: optional MySQL demo business database (one database, two themes), creating mysql_center itself with the Yunqi Zhilian SaaS subscription theme (saas_* tables) and the Huayao manufacturing theme (dim_*/fact_* tables); must not be imported into the metadata database.sql/postgresql_center.sql: optional PostgreSQL demo business database (one database, two themes), matching the Compose postgres-data service. It targets postgresql_center (Xianda retail and Qiyue hotel themes; hotel member tables are named dim_member etc. to avoid clashing with retail) and the public schema, which is the only schema the PostgreSQL data source driver reads. The file contains no CREATE DATABASE — create the database first, then run it against it.sql/clickhouse_center.sql: optional ClickHouse demo business database (one database, two themes), matching the Compose clickhouse-data service. It targets clickhouse_center (app behavior events and power generation operations themes) with ClickHouse native DDL (MergeTree tables, no transaction block), so it must be imported with clickhouse-client rather than a MySQL/PostgreSQL client.sql/sqlserver_center.sql: optional SQL Server demo business database (one database, two themes), matching the Compose sqlserver-data service (host port 3316). It targets sqlserver_center (Hefeng retail finance and Hengyue HR themes; HR org tables are named dim_hr_region/dim_hr_department to avoid clashing with finance) with T-SQL DDL; the official image only ships linux/amd64 (Apple Silicon needs Rosetta emulation) and its container runs as UID 10001, so the host data directory must be chown -R 10001:0 before first start.sql/oracle_center.sql: optional Oracle demo business database matching the Compose oracle-data service (host port 3317). Connect as the business user root to the service name FREEPDB1 and run the whole file in any client such as Navicat, DBeaver or sqlplus (it contains plain SQL only, no SQLPlus-specific commands). Table and column names are double-quoted lowercase identifiers, because Oracle folds unquoted identifiers to uppercase while the analysis queries quote lowercase column names. The image needs about 2 GB of memory and its container runs as UID 54321, so the host data directory must be chown -R 54321:54321 before first start. Use the slim tag of gvenzl/oracle-free, not *-faststart: the faststart database files are already expanded at /opt/oracle/oradata inside the image, and mounting an empty data directory over that path hides them.sql/mongodb_center.js: optional MongoDB demo business database matching the Compose mongodb-data service (host port 3318). Select mongodb_center before running the script in Navicat or mongosh, using admin as the authentication database. The script drops and recreates its demo collections in the selected database. Dataset queries use {collection, pipeline} JSON format, e.g., {"collection": "fact_enrollment", "pipeline": [{"$match": {"status": "已支付"}}]}.Register the business databases you imported as data sources on the Data Sources page after the app is running.
For an existing deployment moving from the previous metadata database to guibiao, stop every application instance connected to the old database, including local development, schedulers and knowledge indexing workers. Back up the live database, create guibiao with the same character set and collation, and restore that backup. Compare per-table row counts and data checksums, review account grants and stored SQL schema references, then switch configuration and resume services. Never overwrite live data with the repository initialization dump.
Set SERVICE_DB_NAME=guibiao in all three environments and MYSQL_MAIN_DATABASE=guibiao for production Compose; the application container receives NUXT_SERVICE_DB_NAME. Deploy code using the new connection pool identifier at the same time. Changing MYSQL_DATABASE does not migrate an existing MySQL volume. For DPanel, also update the environment settings of guibiao-compose in ims_compose and the actual deployment env file so panel redeployments preserve the change. Retain the old database and backup for rollback; once the new database accepts writes, reconcile those writes before switching back.
Start the development server:
# Default environment, reads env/.env.daily
pnpm dev
# Against the pre or prod env file
pnpm dev:pre
pnpm dev:prod
The server port comes from PORT in the selected env file.
Build the production version:
pnpm build # env/.env.daily
pnpm build:pre # env/.env.pre
pnpm build:prod # env/.env.prod
To deploy with Docker Compose:
# First deployment or full-stack reconciliation
docker compose --env-file env/.env.prod -p guibiao-compose -f docker-compose.yml \
up -d --pull always
# Update only the application after a new image is published
docker compose --env-file env/.env.prod -p guibiao-compose -f docker-compose.yml \
up -d --pull always --force-recreate guibiao
GUIBIAO_IMAGE may remain xcy960815/guibiao:latest. Nuxt exposes a single built-in build identifier to both the server and client; GitHub Actions injects a unique CI run identifier for each image publication so Docker cannot reuse an older Nuxt build layer.
The deployment command explicitly pulls the remote latest digest and recreates the Nuxt container. After the replacement succeeds, already-open pages detect the changed build identifier and prompt the user to refresh.
Restarting the same image keeps its original build identifier and does not produce a false update notification. Image publication is currently triggered by v* Git tags, while build identifier generation itself does not depend on the tag value.
Double-check the target host's real values in env/.env.prod before deployment. One up -d reconciles the whole stack: the application plus redis, mysql-main (platform metadata), mysql-data, postgres-data, clickhouse-data, sqlserver-data, oracle-data and mongodb-data (demo business databases), embedding and qdrant (knowledge base), and prometheus, node-exporter and cadvisor (host monitoring). The Compose file exposes configurable host ports for the web service, Redis, the MySQL instances, PostgreSQL, ClickHouse, SQL Server, Oracle and MongoDB; restrict the Redis/MySQL/PostgreSQL/ClickHouse/SQL Server/Oracle/MongoDB ports with the host firewall when remote access is not required. Prometheus, node-exporter, and cAdvisor remain internal to guibiao-monitor-network. The rerank service is not part of the stack and is reached over the LAN when KNOWLEDGE_RERANK_BASE_URL is set.
The host monitoring deployment adds Prometheus, node-exporter, and cAdvisor on the internal guibiao-monitor-network.
Prometheus data is persisted under PROMETHEUS_DATA_DIR_PATH, and the Nuxt metrics endpoint is protected by MONITOR_METRICS_TOKEN.
The Prometheus image runs as nobody (65534:65534), so the directory configured by PROMETHEUS_DATA_DIR_PATH must be writable by that user when it is pre-created, for example chown -R 65534:65534 /your/prometheus/data/path.
cAdvisor runs with privileged host access and is only reachable from the internal monitor network. Set DOCKER_DATA_ROOT to the target host's actual Docker data root, and validate its /dev/kmsg device plus /var/run, /sys, and /dev/disk mounts.
MONITOR_DISK_MOUNTPOINT selects the host filesystem card, while MONITOR_LOG_DISK_MOUNTPOINT selects the filesystem containing the Nuxt log bind mount.
Docker Compose is the deployment path for the NAS; PM2 runs the same build artifact directly on one machine for local or Windows hosts. The entry is ecosystem.config.js, which starts .output/server/index.mjs in fork mode, so build first:
pnpm build:prod && pnpm start:prod # daily and pre have matching start:* scripts
pnpm restart:prod # or restart / restart:pre
pnpm stop && pnpm delete
start:* trigger a prestart* lifecycle that rebuilds .output/; keep pnpm dev stopped when you run them.
The project uses the following tools:
Run the checks that match the change:
pnpm lint # ESLint over the repository
pnpm lint:fix
pnpm format:check # Prettier; run pnpm format to rewrite
pnpm test # Vitest, tests live in test/
pnpm test:watch
pnpm exec nuxt typecheck # vue-tsc over pages, stores and server types
guibiao/
├── assets/ # Styles and compiled static assets
├── components/ # Shared Vue components
├── composables/ # Frontend composables
├── layouts/ # Layout components
├── middleware/ # Route middleware (auth.global.ts)
├── pages/ # File-based routes; pages/dev/ is the guarded demo area
├── plugins/ # Nuxt client plugins
├── public/ # Unprocessed public assets
├── shared/ # Contracts and domain rules consumed by both client and server
├── stores/ # Pinia stores
├── types/ # TypeScript declarations
├── utils/ # Frontend utilities
├── server/ # Nitro server: api -> service -> mapper, plus middleware, plugins, validation
├── sql/ # Metadata schema and demo business database dumps
├── storage/ # Uploaded knowledge base documents (runtime data)
├── test/ # Vitest suites
├── env/ # Application and Docker Compose env files (committed in this private repo)
├── docs/ # Temporary plans and archived material
└── .agents/ # Long-lived collaboration rules and per-module knowledge
The main request path is pages/components -> stores/composables -> server/api -> service -> mapper -> SQL.
AGENTS.md - collaboration entry point and repository-wide constraints.agents/core/project-overview.md - architecture map and real call chains.agents/core/commands.md - canonical commands, env layers and deployment entry points.agents/modules/analysis.md - analysis/query/access boundaries.agents/modules/ai-assistant.md - analysis and platform AI streaming rules.agents/modules/permission.md - resource permissions, access applications, and notifications.agents/modules/monitoring.md - host monitoring and monitor AI boundaries.agents/modules/email-schedule.md - email, scheduling, recovery, and cleanup semantics.agents/modules/auth.md - JWT, Redis sessions and revocation rules.agents/modules/dataset.md, .agents/modules/data-source.md, .agents/modules/dashboard.md - dataset, data source and dashboard boundaries.agents/standards/ - mapper, API route, frontend page, module fix, server review and Git contribution standardsdocs/roadmap.md - planned product capabilities, not current implementation rulesA release is a vX.Y.Z Git tag. Pushing one runs .github/workflows/docker-publish.yml, which publishes xcy960815/guibiao:latest plus the matching version label; package.json version is kept aligned with the newest tag. Commit messages follow Conventional Commits and are linted by Commitlint, which is what makes the changelog generatable.
CHANGELOG.md is generated from the tags, not written by hand:
pnpm changelog # append the newest release range
pnpm changelog:version # rebuild the whole file from every tag
After a full rebuild, fix up the top of the file by hand: the generator writes commits that are not covered by a tag into invented 0.0.N+1 headings, repeats the current version as an empty heading, and drops the # Changelog preamble. Fold those commits into one ## 未发布 (YYYY-MM-DD) block and restore the preamble. CHANGELOG.md is in .prettierignore, so formatting is not rewritten on commit.
git checkout -b feature/AmazingFeature).git commit -m 'feat: add some amazing feature').git push origin feature/AmazingFeature).MIT License. See the LICENSE file in the repository root.
Please submit questions or suggestions through GitHub Issues.
Content type
Image
Digest
sha256:a73b2b98f…
Size
87.6 MB
Last updated
21 minutes ago
docker pull xcy960815/guibiao