ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

SQLPad 开发者指南:从源码构建、前后端联调开发到测试与 Docker 镜像发布的完整实战手册

SQLPad 开发者指南:从源码构建、前后端联调开发到测试与 Docker 镜像发布的完整实战手册
  • 后端
  • 数据可视化
  • 数据库
  • 数据分析

【免费下载链接】sqlpad

Web-based SQL editor

项目地址:https://gitcode.com/gh_mirrors/sq/sqlpad
点击查看免费下载

本指南面向希望在本地从源码构建 SQLPad、参与其前后端开发、运行其集成测试,或为其构建本地 Docker 镜像的开发者。文章以仓库根目录的 DEVELOPER-GUIDE.md 为骨架,结合server、client目录下的真实源码、配置与测试用例展开讲解,阅读完成后你将掌握 SQLPad 的工程结构、一键构建脚本原理、双终端开发工作流、测试数据库生成机制、Mocha 集成测试运行方法,以及本地 Docker 镜像的构建与跨架构验证方案。

环境前置要求(Prerequisites)

SQLPad 的开发环境要求并不复杂,但有几个关键点需要先确认:

  • Node.js v14.0.0 及以上。需要说明的是,当前仓库根目录package.json声明packageManager为yarn@1.22.19,而 server/package.json 中engines字段已要求node >= 20,因此在实际开发时建议使用 Node 20 或更高版本,Docker 镜像也基于node:20-slim构建(见 Dockerfile)。
  • Yarn 1(classic yarn)。SQLPad 是一个由根目录、client/、server/三个 Node 子项目组成的 monorepo 结构,统一使用 classic yarn 管理依赖锁文件。
  • 部分依赖需要本地编译:
    • macOS 需要安装 Xcode Command Line Tools;
    • Ubuntu / Debian 系执行apt-get install build-essential即可获得编译所需的 gcc/g++、make 等工具;
    • Windows 需要额外步骤,参考node-gyp的官方安装说明。若你打算在 Docker 中启用 ODBC 支持,构建阶段还需要unixodbc-dev(见 Dockerfile)。

代码风格与静态检查(Style Guide)

SQLPad 使用Prettier作为自动代码格式化工具、ESLint作为 lint 工具,所有代码在提交前都应通过检查:

# 检查格式与 lint yarn run lint # 自动修复部分格式与 lint 问题 yarn run fixlint

从根目录 package.json 可以看到,lint与fixlint分别串联了client与server两个子项目的检查:

"fixlint": "npm run fixlint --prefix client && npm run fixlint --prefix server && prettier --write \"**/*.js\"", "lint": "npm run lint --prefix client && npm run lint --prefix server && prettier --check \"**/*.js\""

后端(server)的 ESLint 配置在 server/.eslintrc,基于airbnb-base+prettier,并针对 Node/ES2022/Mocha 环境做了大量规则放宽(如关闭no-plusplus、no-param-reassign等)。前端(client)的配置在 client/.eslintrc,基于@typescript-eslint+react-hooks+prettier,因为前端已全面 TypeScript 化。

如果你使用 Visual Studio Code,可以安装 Prettier 和 ESLint 扩展,在编写过程中即时发现并修复问题。另外,仓库还通过husky+lint-staged配置了 pre-commit 钩子(见根目录 package.json),会在提交时对暂存的*.{js,json,css}文件自动执行prettier --write。

从源码构建 SQLPad(Getting Started)

一键构建脚本

克隆仓库后,在仓库根目录执行:

scripts/build.sh

该脚本做了目录存在性校验(确保从项目根目录执行),其核心步骤可以从 scripts/build.sh 源码中直接看到:

# 1. 安装根目录依赖(以 yarn.lock 为准) yarn # 2. 安装前端依赖 (cd client && yarn) # 3. 构建前端(TypeScript 编译 + Vite 打包) (cd client && yarn build) # 4. 安装后端依赖 (cd server && yarn) # 5. 将前端构建产物复制到 server/public rm -rf server/public mkdir server/public cp -r client/build/* server/public # 6. 生成测试用的 SQLite 数据库 fixture node server/generate-test-db-fixture.js

注意脚本注释中提到的"package-lock.json"在当前仓库实际对应的是yarn.lock。前端构建命令yarn build对应 client/package.json 中的"build": "tsc && vite build",即先由 TypeScript 编译器做类型检查,再由 Vite 产出client/build目录。

以生产模式运行

构建完成后即可用生产模式启动服务端:

cd server node server.js --config ./config.dev.env

node server.js的启动逻辑在 server/server.js,它通过minimist解析 CLI 参数,支持--help、--version、--migrate和--config四个核心用法(见 server/server.js):

# 打印帮助 node server.js --help # 仅执行数据库迁移后退出 node server.js --migrate --config path/to/file.format # 使用配置文件启动 node server.js --config path/to/file.format

当--config指向.env文件时,server.js会先用dotenv将变量载入process.env,再交给Config类统一解析。服务器启动前会依次完成:校验配置(getValidations(),发现错误直接exit(1))→ 初始化数据库(makeDb)→ 执行迁移(makeMigrator)→ 初始化驱动(initDrivers)→ 确保管理员存在(ensureAdmin)→ 加载种子数据(loadSeedData)→ 创建 Express 应用(makeApp)。若配置了SQLPAD_HTTPS_KEY_PATH与SQLPAD_HTTPS_CERT_PATH,则以 HTTPS 方式监听;否则以 HTTP 方式监听,并且端口被占用时detect-port会自动切换到可用端口(见 server/server.js)。

仓库自带的 server/config.dev.env 是开发配置范本,其中已经预置了一个通过环境变量声明的 SQLite 测试连接:

SQLPAD_PORT = 3010 SQLPAD_BASE_URL = "/sqlpad" SQLPAD_DB_PATH = ../db SQLPAD_APP_LOG_LEVEL = debug SQLPAD_WEB_LOG_LEVEL = debug SQLPAD_SERVICE_TOKEN_SECRET = secr3t SQLPAD_DEFAULT_CONNECTION_ID = devdbdriverid123 SQLPAD_CONNECTIONS__devdbdriverid123__driver = sqlite SQLPAD_CONNECTIONS__devdbdriverid123__name = dev connection from config SQLPAD_CONNECTIONS__devdbdriverid123__filename = "./test/fixtures/sales.sqlite"

其中SQLPAD_CONNECTIONS__<id>__<field>这类"双下划线"环境变量是 SQLPad 通过环境变量声明连接配置的约定,解析逻辑在 server/lib/config/index.js(getConnections())与config-utils.js的parseConnectionsFromEnv()中:运行时从环境变量解析、不会 upsert 进后端数据库,且自动标记editable = false。从配置校验逻辑(server/lib/config/index.js)可以看到,连接必须提供name与受支持的driver,其余字段必须匹配对应驱动fields中声明的 key,否则启动时直接报错。

全局安装为 CLI

如果你希望像npm install sqlpad -g一样在任意目录通过sqlpad命令启动,可以基于本地文件做全局安装(注意需先完成前端构建与复制):

cd server npm install -g # 之后在任意目录运行 cd ~ sqlpad --config ./config.dev.env

全局安装依赖 server/package.json 中的"bin": { "sqlpad": "server.js" }声明。如果此前已通过 npm 全局安装过 sqlpad,需先npm uninstall sqlpad -g避免冲突。

前后端分离开发模式(Developing)

SQLPad 的前端(React + TypeScript + Vite)与后端(Express)在开发期是两套独立的开发服务器,需要在仓库根目录开两个终端:

# 终端 1:后端开发模式(node-dev 自动重启 + pino-pretty 美化日志) cd server yarn start # 终端 2:前端开发服务器 cd client yarn start

对应的 npm script 定义:

  • 后端:server/package.json 中"start": "node-dev server.js -- --config \"./config.dev.env\" | pino-pretty";
  • 前端:client/package.json 中"start": "vite"。

两个服务各自监听不同端口:

  • http://localhost:3000:React 前端的 Vite 开发模式;
  • http://localhost:3010:编译后的前端产物 + SQLPad 服务端。

开发体验上,前端文件变更会触发 Vite 热更新(页面自动刷新),后端文件变更会触发node-dev自动重启。

关于 3000 端口开发模式的一些已知限制,原文的解释很关键:在 3000 端口下,部分功能可能无法正常工作(如 CSV/XLSX 下载、Google 登录)。原因有二:

  1. 端口差异导致的配置问题:例如 Google OAuth 的回调地址只能绑定一个 URL/端口;
  2. 开发代理机制:Vite 开发服务器是一个"次要 Web 服务器",会把自身不理解的请求代理到 3010 端口的 SQLPad 服务端。

代理规则就定义在 client/vite.config.ts 中,/api、/statement-results、/auth/saml、/login/callback、/auth/google、/auth/oidc等路由(以及带/sqlpad前缀的对应路由)都会被转发到http://127.0.0.1:3010:

const PROXY_ROUTES = [ '/api', '/statement-results', '/auth/saml', '/login/callback', '/auth/google', '/auth/oidc', ]; const PROXY_URL = 'http://127.0.0.1:3010';

开发模式下 Vite 的base被固定为/sqlpad/,而生产构建可通过VITE_SPA_BASE_URL_OVERRIDE环境变量覆盖(见 client/vite.config.ts)。

在 Windows 上开发

Windows 下无法直接执行build.sh这类 shell 脚本,可以使用 cmder 之类的终端模拟器,通过sh直接调用:

sh scripts/build.sh

测试/开发用数据库连接(Test/Dev Database Connection)

早期 SQLPad 依赖一个"mock driver"(开启debug标志后,解析 SQL 注释中的变量来生成假数据),而现在被一个更实际的方案取代:server/generate-test-db-fixture.js会生成一个 SQLite 测试数据库,用于测试和本地开发。

这个方案的价值在于:你可以直接编写自己熟悉的 SQL,而不用去记忆那些写在 SQL 注释里的 mock 变量。脚本会在server/test/fixtures/sales.sqlite生成数据库,只需在 SQLPad 中新建一个 SQLite 连接、把 filename 设为./test/fixtures/sales.sqlite即可使用。

从脚本源码可以看到这个数据库的内容设计:

  • regions表:5 个销售区域(Northeast / West / South / Midwest / East),各带一个 revenue 乘数(1.1~1.5);
  • products表:16 条模拟商品记录,覆盖 toy / tech / food 三类,包含名称、类别、颜色、价格;
  • sales表:100 批次 × 100 行 =10,000 条销售记录,cost与revenue由区域乘数、日期/周数/分钟/秒四个时间维度乘数叠加计算而来(见 server/generate-test-db-fixture.js);
  • vw_sales视图:将 sales 关联 regions、products,并拆出order_date、order_time、order_hour等便于做图表的时间维度字段;
  • 500 张z_fake_table_products_*空表:用于模拟大型 schema,方便测试 Schema 侧边栏的加载与搜索场景。

脚本生成的order_timestamp基于moment.utc('2020-01-01')加上固定的分钟/秒偏移(rowId * 43分钟、rowId * 37秒),因此数据是确定性的、可预测的(数据本身语义上有点"无厘头",但这不是随机数据)。如果你需要扩展这个数据库,可以直接修改脚本重新生成。

运行测试(Running Tests)

SQLPad 的测试以 API 服务器为主:绝大多数是集成测试,穿插少量单元测试。测试代码集中在server/test目录下,按 API 路由划分(如api/queries.js、api/batches.js、api/connection-accesses.js、api/query-result-stores.js等),使用mocha作为测试运行器。测试辅助类TestUtils在 server/test/utils.js,它基于内存数据库 + 内存 session 构建出完整的 Express 应用,并封装了get/post/put/del等方法配合supertest发起请求。

前置步骤

cd server # 1. 若依赖尚未安装(build.sh 会自动安装) yarn # 2. 若测试数据库 fixture 尚未生成(build.sh 会自动生成) node generate-test-db-fixture.js # 3. 按需启动后端服务(-d 表示后台运行) docker-compose up -d redis docker-compose up -d ldap

server/docker-compose.yml 除了 redis(6379)与 ldap(10389,用于测试 LDAP 认证流程),还定义了 mssql、postgres、mariadb、mysql 等服务,用于驱动相关的集成测试。

运行全部测试

yarn test

对应 server/package.json 中的:

"test": "mocha test --timeout 20000 --recursive --exit --bail"

--recursive递归收集test目录下的用例,--bail遇到首个失败即停止,--timeout 20000设定了 20 秒超时。部分用例依赖 redis,若本地未启动 redis,对应测试会通过TestUtils.redisAvailable()检测后自动 skip(见 server/test/api/query-result-stores.js)。

运行单个测试用例

npx mocha test/path/to/file.js --exit

例如只运行连接 API 测试:

npx mocha test/api/connections.js --exit

--exit用于测试结束后立即退出进程。此外,仓库还提供了针对不同后端数据库的全套测试命令(server/package.json),可通过SQLPAD_BACKEND_DB_URI将后端元数据库切换到 mssql、mysql、mariadb 或 postgres:

npm run testpostgres npm run testmysql npm run testmariadb npm run testmssql

测试的运行路径示例

以api/query-result-stores.js为例(server/test/api/query-result-stores.js),它验证了queryResultStore的 file / redis / database / memory 四种存储后端都能完成"提交批次 → 轮询状态 → 拉取语句结果 → 删除结果"的完整闭环:

let batch = await utils.post('admin', `/api/batches`, { connectionId: connection.id, batchText: query1, // "SELECT 1 AS id, 'blue' AS color" }); while (batch.status !== 'finished' && batch.status !== 'error') { await wait(25); batch = await utils.get('admin', `/api/batches/${batch.id}`); }

这也从侧面说明:SQL 的执行被建模为batch(批次)→ statements(语句)→ results(结果)的三级资源结构,对应 server/routes/batches.js、server/routes/statements.js 与 server/routes/statement-results.js 三组路由。

构建本地 Docker 镜像(Building local docker images)

仓库根目录的 Dockerfile 采用多阶段构建:第一阶段基于node:20-slim完成依赖安装与前端构建,第二阶段用全新的node:20-slim只拷贝server目录(其中已含构建好的前端产物),并将NODE_ENV设为 production、默认端口设为 3000。

构建与运行

# 在仓库根目录执行(需先安装并启动 Docker) docker build -t localsqlpad . # 构建其他架构时追加 --platform,例如 amd64 docker build -t localsqlpad . --no-cache=true --platform=linux/amd64 # 查看镜像列表 docker images # 运行本地镜像 docker run -p 3000:3000 -e "SQLPAD_ADMIN=admin@sqlpad.com" -e "SQLPAD_ADMIN_PASSWORD=admin" localsqlpad # 浏览器访问 http://localhost:3000

容器启动入口是 docker-entrypoint:它会执行/etc/docker-entrypoint.d目录下所有.sh文件(source方式加载,允许脚本通过环境变量覆盖默认行为),最后exec node /usr/app/server.js $@启动服务端。镜像默认设置SQLPAD_DB_PATH=/var/lib/sqlpad,用于持久化数据卷。

支持 ODBC 的镜像

如果使用 ODBC 驱动(如连接 SAP HANA、Oracle 等),需要启用 ODBC 构建参数:

docker build -t sqlpad/sqlpad-odbc --build-arg ODBC_ENABLED=true .

构建阶段会安装unixodbc-dev,运行阶段会安装unixodbc libaio1 odbcinst libodbc1并创建/etc/odbcinst.ini(见 Dockerfile)。之后有两种方式补充具体驱动:

  1. 在 fork 中扩展这个 Dockerfile;
  2. 基于sqlpad/sqlpad-odbc编写自己的 Dockerfile,FROM sqlpad/sqlpad-odbc后安装所需驱动。注意:目前该 ODBC 镜像并未发布到 Docker Hub,需要先在本地用上面的构建命令产出。

跨架构镜像验证

当需要测试特定架构(例如在 arm64 机器上跑一个 amd64 镜像)时,可以直接拉取基于特定 sha 的 Docker Hub 镜像:

docker run -p 3000:3000 -e "SQLPAD_ADMIN=admin@sqlpad.com" -e "SQLPAD_ADMIN_PASSWORD=admin" \ sqlpad/sqlpad@sha256:b80b4ab2af32ed07479d9233327bf3e5352fc71b41245d8e73b6403361be266a

这也正是docker build --platform=linux/amd64本地构建的对照验证方式。

进阶:配置项速查与开发调试要点

虽然 DEVELOPER-GUIDE.md 本身不逐项罗列配置,但从 server/lib/config/config-items.js(共 445 行、约 80 项配置)中可以看到 SQLPad 完整的配置体系,开发调试时最常用的几项:

配置 key环境变量默认值说明
portSQLPAD_PORT80监听端口
baseUrlSQLPAD_BASE_URL空应用挂载路径前缀
dbPathSQLPAD_DB_PATH空数据/会话文件目录(生产环境强烈建议设置)
sessionStoreSQLPAD_SESSION_STOREfile会话存储:file/database/redis/memory
queryResultStoreSQLPAD_QUERY_RESULT_STOREfile查询结果存储:file/database/redis/memory
queryResultMaxRowsSQLPAD_QUERY_RESULT_MAX_ROWS10000单个查询结果最大行数
appLogLevelSQLPAD_APP_LOG_LEVELinfo应用日志级别
webLogLevelSQLPAD_WEB_LOG_LEVELinfoWeb 请求日志级别
backendDatabaseUriSQLPAD_BACKEND_DB_URI空后端元数据库连接串(默认 SQLite)
redisUriSQLPAD_REDIS_URI空Redis 连接串(session / 结果存储用)
dbAutomigrateSQLPAD_DB_AUTOMIGRATEtrue启动时自动执行数据库迁移

配置优先级为CLI 参数 > 环境变量 > 默认值(见 server/lib/config/index.js),字符串形式的"true"/"false"会被自动转换为布尔值。Config.getValidations()会在启动时报错:session/结果存储必须属于允许列表、使用 redis 存储时必须设置SQLPAD_REDIS_URI、SQLPAD_前缀的未知环境变量会被警告、已移除的旧变量(如SQLPAD_SMTP_HOST、SQLPAD_DEBUG、DISABLE_AUTH等,完整清单见 server/lib/config/index.js)会直接报错,这能有效帮助你排查配置拼写问题。

小结

SQLPad 的开发者工作流可以概括为一条清晰的链路:scripts/build.sh一键完成三个子项目的依赖安装与前端构建 → 双终端分别启动前后端开发服务器进行热开发 → 用generate-test-db-fixture.js生成确定性测试数据库 →yarn test/npx mocha <file> --exit运行集成测试 → 多阶段 Dockerfile 构建镜像并跨架构验证。配合yarn run lint/yarn run fixlint保持代码风格,你就可以在本地完整地编译、运行、测试和打包 SQLPad 了。更深入的产品功能(连接管理、查询执行、图表、认证与 Webhook 等)可继续阅读 README.md 与 docs 目录下的官方文档。

  • 后端
  • 数据可视化
  • 数据库
  • 数据分析

【免费下载链接】sqlpad

Web-based SQL editor

项目地址:https://gitcode.com/gh_mirrors/sq/sqlpad
点击查看免费下载

相关推荐

上一篇:蔚蓝档案鼠标指针:3分钟让Windows光标变身动漫游戏角色
下一篇:从零读懂 React 源码:Under-the-hood-ReactJS 的 Stack Reconciler 全景流程导览

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进