queryview 0.0.5__tar.gz → 0.0.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {queryview-0.0.5 → queryview-0.0.7}/.github/actions/start-git-daemon/action.yaml +3 -3
- {queryview-0.0.5 → queryview-0.0.7}/.github/workflows/publish.yaml +1 -1
- {queryview-0.0.5 → queryview-0.0.7}/.gitignore +1 -0
- {queryview-0.0.5 → queryview-0.0.7}/CLAUDE.md +12 -0
- queryview-0.0.7/CONTRIBUTING.md +124 -0
- queryview-0.0.7/PKG-INFO +218 -0
- queryview-0.0.7/README.md +194 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/conftest.py +16 -12
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/connect.py +16 -15
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/gitsync.py +85 -23
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/migrations/env.py +1 -1
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/migrations/versions/c7d8e9f0a1b2_workspaces.py +7 -13
- queryview-0.0.7/backend/queryview/static/assets/index-Cysz_FnM.js +46 -0
- queryview-0.0.7/backend/queryview/static/assets/index-h_3ZdSNd.css +2 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/static/index.html +2 -2
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/test_api_gitsync.py +1 -1
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/test_connect_store.py +2 -3
- queryview-0.0.7/backend/queryview/test_data_dir.py +51 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/test_gitsync.py +109 -9
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/test_mcp_gitsync.py +2 -4
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/test_migrations.py +5 -7
- {queryview-0.0.5 → queryview-0.0.7}/docker/Dockerfile +13 -6
- {queryview-0.0.5 → queryview-0.0.7}/docs/connect.md +22 -10
- {queryview-0.0.5 → queryview-0.0.7}/docs/explorer.md +22 -9
- {queryview-0.0.5 → queryview-0.0.7}/docs/future.md +3 -2
- {queryview-0.0.5 → queryview-0.0.7}/docs/gitsync.md +19 -9
- {queryview-0.0.5 → queryview-0.0.7}/docs/query.md +31 -4
- {queryview-0.0.5 → queryview-0.0.7}/docs/queryview.md +17 -1
- {queryview-0.0.5 → queryview-0.0.7}/docs/workspace.md +3 -4
- {queryview-0.0.5 → queryview-0.0.7}/e2e/conftest.py +67 -7
- queryview-0.0.7/e2e/test_app.py +98 -0
- {queryview-0.0.5 → queryview-0.0.7}/e2e/test_dashboard.py +3 -10
- {queryview-0.0.5 → queryview-0.0.7}/e2e/test_drivers.py +5 -10
- queryview-0.0.7/e2e/test_explorer.py +202 -0
- {queryview-0.0.5 → queryview-0.0.7}/e2e/test_gitsync.py +5 -4
- queryview-0.0.7/e2e/test_popovers.py +49 -0
- {queryview-0.0.5 → queryview-0.0.7}/e2e/test_query.py +69 -11
- {queryview-0.0.5 → queryview-0.0.7}/e2e/test_remote.py +3 -11
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/app/App.tsx +120 -33
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/app/ExplorerView.tsx +186 -42
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/app/QueryView.tsx +62 -94
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/app/controls/GitSyncControls.tsx +0 -0
- queryview-0.0.7/frontend/src/app/controls/Spinner.tsx +34 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/app/controls/WorkspaceSwitcher.tsx +5 -1
- queryview-0.0.7/frontend/src/app/controls/useDismiss.ts +28 -0
- queryview-0.0.7/frontend/src/app/databaseFilter.test.ts +28 -0
- queryview-0.0.7/frontend/src/app/databaseFilter.ts +7 -0
- queryview-0.0.7/frontend/src/app/explorerSettings.test.ts +61 -0
- queryview-0.0.7/frontend/src/app/explorerSettings.ts +27 -0
- queryview-0.0.7/frontend/src/app/testStorage.ts +25 -0
- queryview-0.0.7/frontend/src/app/viewSettings.test.ts +65 -0
- queryview-0.0.7/frontend/src/app/viewSettings.ts +31 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/app/workspace.test.ts +1 -8
- queryview-0.0.7/frontend/src/core/cells/CellDataModal.tsx +125 -0
- queryview-0.0.7/frontend/src/core/cells/structured.test.ts +63 -0
- queryview-0.0.7/frontend/src/core/cells/structured.ts +51 -0
- queryview-0.0.7/frontend/src/core/cells/structuredTree.test.ts +85 -0
- queryview-0.0.7/frontend/src/core/cells/structuredTree.ts +58 -0
- queryview-0.0.7/frontend/src/core/results/ResultsTable.tsx +131 -0
- queryview-0.0.7/frontend/src/core/results/cellWidth.test.ts +21 -0
- queryview-0.0.7/frontend/src/core/results/cellWidth.ts +21 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/index.css +20 -0
- {queryview-0.0.5 → queryview-0.0.7}/pyproject.toml +1 -2
- {queryview-0.0.5 → queryview-0.0.7}/scripts/setup_browser.sh +1 -1
- {queryview-0.0.5 → queryview-0.0.7}/uv.lock +0 -2
- queryview-0.0.5/PKG-INFO +0 -210
- queryview-0.0.5/README.md +0 -185
- queryview-0.0.5/backend/queryview/static/assets/index-B3W6A5bg.css +0 -2
- queryview-0.0.5/backend/queryview/static/assets/index-BE8_u4Np.js +0 -44
- queryview-0.0.5/backend/queryview/test_db_path.py +0 -58
- queryview-0.0.5/e2e/test_app.py +0 -47
- queryview-0.0.5/e2e/test_explorer.py +0 -66
- queryview-0.0.5/frontend/src/core/results/ResultsTable.tsx +0 -56
- {queryview-0.0.5 → queryview-0.0.7}/.claude/settings.json +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/.github/workflows/ci.yml +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/.mcp.json +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/.pre-commit-config.yaml +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/LICENSE +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/alembic.ini +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/__init__.py +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/dashboard_queries.py +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/dashboards.py +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/drivers/__init__.py +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/drivers/base.py +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/drivers/clickhouse.py +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/drivers/duckdb.py +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/drivers/postgres.py +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/drivers/test_base.py +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/drivers/test_clickhouse.py +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/drivers/test_contract.py +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/drivers/test_duckdb.py +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/drivers/test_postgres.py +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/main.py +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/mcp_server.py +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/migrations/script.py.mako +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/migrations/versions/9a536b7c0328_initial_schema.py +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/migrations/versions/a1b2c3d4e5f6_connection_config_blob.py +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/migrations/versions/b2c3d4e5f6a7_predefined_presentation.py +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/queries.py +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/remote.py +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/static/favicon.svg +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/test_api_db.py +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/test_api_export_import.py +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/test_api_workspaces.py +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/test_connect_flow.py +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/test_dashboard_queries.py +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/test_dashboards.py +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/test_main.py +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/test_mcp_mount.py +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/test_queries.py +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/test_remote.py +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/test_validation.py +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/test_workspaces.py +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/test_yamlio.py +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/validation.py +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/workspaces.py +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/backend/queryview/yamlio.py +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/docs/api.md +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/docs/dashboard.md +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/docs/export-import.md +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/docs/remote.md +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/e2e/test_export_import.py +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/e2e/test_workspaces.py +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/.gitignore +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/README.md +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/eslint.config.js +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/index.html +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/package.json +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/public/favicon.svg +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/app/DashboardView.tsx +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/app/compactNumber.test.ts +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/app/compactNumber.ts +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/app/connection.ts +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/app/controls/ExportImportControls.tsx +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/app/controls/Toast.tsx +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/app/drivers.ts +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/app/gitsync.test.ts +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/app/gitsync.ts +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/app/promptSuggestions.test.ts +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/app/promptSuggestions.ts +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/app/sessionLock.test.ts +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/app/sessionLock.ts +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/app/workspace.ts +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/app/yamlio.test.ts +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/app/yamlio.ts +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/core/cells/CellViewModal.tsx +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/core/cells/ComplexCell.tsx +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/core/cells/cellView.test.ts +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/core/cells/cellView.ts +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/core/cells/cellViewYaml.test.ts +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/core/cells/cellViewYaml.tsx +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/core/cells/complexCells.test.ts +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/core/cells/complexCells.ts +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/core/dashboard/DashboardFrame.tsx +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/core/dashboard/srcDoc.test.ts +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/core/dashboard/srcDoc.ts +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/core/index.ts +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/core/params/queryParams.test.ts +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/core/params/queryParams.ts +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/core/presentation/FieldPickers.tsx +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/core/presentation/presentation.test.ts +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/core/presentation/presentation.ts +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/core/results/rows.test.ts +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/core/results/rows.ts +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/src/main.tsx +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/tsconfig.app.json +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/tsconfig.json +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/tsconfig.node.json +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/frontend/vite.config.ts +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/package-lock.json +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/package.json +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/pyrightconfig.json +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/scripts/dev.sh +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/scripts/setup.sh +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/scripts/setup_clickhouse.sh +0 -0
- {queryview-0.0.5 → queryview-0.0.7}/scripts/setup_postgres.sh +0 -0
|
@@ -3,8 +3,8 @@ description: >-
|
|
|
3
3
|
Start a loopback `git daemon` that serves — and accepts pushes into — bare
|
|
4
4
|
repos under a base path, so e2e tests exercise git sync against a real
|
|
5
5
|
git:// remote with no auth. Creates an empty bare repo and exports
|
|
6
|
-
|
|
7
|
-
steps.
|
|
6
|
+
E2E_GIT_REMOTE pointing at it, which the e2e suite applies to the default
|
|
7
|
+
workspace; `--detach` keeps the daemon alive across steps.
|
|
8
8
|
|
|
9
9
|
runs:
|
|
10
10
|
using: composite
|
|
@@ -15,7 +15,7 @@ runs:
|
|
|
15
15
|
GIT_DAEMON_BASE="$RUNNER_TEMP/gitsrv"
|
|
16
16
|
mkdir -p "$GIT_DAEMON_BASE"
|
|
17
17
|
git init --bare -b main "$GIT_DAEMON_BASE/queryview.git"
|
|
18
|
-
echo "
|
|
18
|
+
echo "E2E_GIT_REMOTE=git://127.0.0.1:9418/queryview.git" >> "$GITHUB_ENV"
|
|
19
19
|
git daemon --reuseaddr --listen=127.0.0.1 --port=9418 \
|
|
20
20
|
--base-path="$GIT_DAEMON_BASE" --export-all \
|
|
21
21
|
--enable=receive-pack --detach
|
|
@@ -221,7 +221,7 @@ jobs:
|
|
|
221
221
|
- name: Smoke-test installed package
|
|
222
222
|
working-directory: ${{ runner.temp }}
|
|
223
223
|
run: |
|
|
224
|
-
|
|
224
|
+
DATA_DIR="$PWD/qv-data" "$GITHUB_WORKSPACE/.venv/bin/queryview" > server.log 2>&1 &
|
|
225
225
|
pid=$!
|
|
226
226
|
for i in $(seq 1 30); do
|
|
227
227
|
curl -sf http://localhost:8000/api/health > /dev/null && break
|
|
@@ -3,6 +3,18 @@
|
|
|
3
3
|
1. Avoid `Co-Authored-By: Claude` (and similar Claude/AI attribution) trailers in
|
|
4
4
|
commit messages and PR bodies.
|
|
5
5
|
|
|
6
|
+
# Data Privacy
|
|
7
|
+
|
|
8
|
+
1. **Never commit tenant data.** Real customer, tenant, or environment
|
|
9
|
+
identifiers (database and table names, hostnames, account or user names,
|
|
10
|
+
query results, screenshots of live data) must not appear in code, tests,
|
|
11
|
+
fixtures, docs, or commit messages.
|
|
12
|
+
2. **Always anonymize.** When an example needs realistic-looking data, invent
|
|
13
|
+
generic names (e.g. `sales_reporting`, `example.internal`). Rewrite any
|
|
14
|
+
pasted samples before using them.
|
|
15
|
+
3. If tenant data slips into history, rewrite the commits to remove it and
|
|
16
|
+
force-push the branch rather than adding a follow-up commit.
|
|
17
|
+
|
|
6
18
|
# Operational Guidelines
|
|
7
19
|
|
|
8
20
|
1. Use `uv run` (e.g. `uv run pytest`, `uv run python ...`), not `python -m` or a
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# Contributing to QueryView
|
|
2
|
+
|
|
3
|
+
QueryView is a **Python** backend (**FastAPI + SQLModel**) serving `/api/*`, a
|
|
4
|
+
**Vite + React + TypeScript** SPA with **Tailwind CSS**, and a
|
|
5
|
+
**[Playwright](https://playwright.dev)** end-to-end suite. This page covers
|
|
6
|
+
working on it; [README.md](README.md) covers running it.
|
|
7
|
+
|
|
8
|
+
## Layout
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
.
|
|
12
|
+
├── backend/ # Python FastAPI + SQLModel app exposing /api/* (queryview package)
|
|
13
|
+
├── frontend/ # Vite + React + TS + Tailwind v4 SPA (npm workspace)
|
|
14
|
+
├── e2e/ # Playwright (pytest) browser tests
|
|
15
|
+
├── docs/ # Feature and API documentation
|
|
16
|
+
├── pyproject.toml # Backend deps + console script + e2e `test` group (uv)
|
|
17
|
+
└── package.json # npm workspace root: dev orchestration + frontend build
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Prerequisites
|
|
21
|
+
|
|
22
|
+
- [uv](https://docs.astral.sh/uv/) — runs the Python backend and the Playwright
|
|
23
|
+
(pytest) e2e suite (it manages the Python toolchain and dependencies for you).
|
|
24
|
+
- [Node.js](https://nodejs.org) 20+ (with npm) — runs the root tasks and the
|
|
25
|
+
Vite frontend.
|
|
26
|
+
|
|
27
|
+
npm runs the frontend and the root task scripts; uv handles the backend's and
|
|
28
|
+
e2e suite's Python virtualenv and dependencies.
|
|
29
|
+
|
|
30
|
+
## Install
|
|
31
|
+
|
|
32
|
+
Install the backend's Python dependencies (uv reads the root `pyproject.toml`;
|
|
33
|
+
the package lives in `backend/queryview`):
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
uv sync
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Install the JavaScript dependencies for the frontend workspace:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npm install
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Install the e2e tooling (the `test` dependency group) and fetch the Playwright
|
|
46
|
+
browser:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
uv sync --group test
|
|
50
|
+
uv run --group test playwright install chromium
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Run dev servers
|
|
54
|
+
|
|
55
|
+
Run backend and frontend together:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
npm run dev
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Or individually:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npm run backend # uvicorn --reload on http://localhost:8000
|
|
65
|
+
npm run frontend # http://localhost:5173
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
The Vite dev server proxies `/api/*` to the FastAPI backend, so the SPA can call the API on the same origin.
|
|
69
|
+
|
|
70
|
+
## Build & preview production
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
npm run build # produces frontend/dist/
|
|
74
|
+
npm run start # SERVE_STATIC=1, FastAPI serves dist/ + /api on :8000
|
|
75
|
+
npm run preview # build && start in one shot
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
In production there is no Vite — the FastAPI backend serves the bundled SPA from `frontend/dist/` and falls back to `index.html` for any unknown non-`/api` path so client-side routing works. Override the dist location with `STATIC_ROOT=/path/to/dist`.
|
|
79
|
+
|
|
80
|
+
## Tests
|
|
81
|
+
|
|
82
|
+
Backend unit tests live beside the code they test inside `backend/queryview`,
|
|
83
|
+
and ship in the wheel:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
uv run --group test pytest backend/queryview
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The e2e suite is [pytest-playwright](https://playwright.dev/python/docs/test-runners),
|
|
90
|
+
installed via the `test` dependency group and run through `uv`. Start the dev
|
|
91
|
+
servers (`npm run dev`) in one terminal, then in another:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
uv run --group test pytest
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Override the target URL with `BASE_URL=http://localhost:4173 uv run --group test pytest` (e.g. to test a built preview). To run the full suite against a real ClickHouse the way CI does, use `scripts/setup.sh`.
|
|
98
|
+
|
|
99
|
+
## Lint & type-check
|
|
100
|
+
|
|
101
|
+
[pre-commit](https://pre-commit.com) runs ruff (lint + format) and pyright, the
|
|
102
|
+
same checks CI's `lint` job runs. Pyright resolves imports from the project's
|
|
103
|
+
`.venv`, so sync first:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
uv sync --group dev
|
|
107
|
+
uv run pre-commit install # once, to run on every commit
|
|
108
|
+
uv run pre-commit run --all-files
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Release to PyPI
|
|
112
|
+
|
|
113
|
+
The **Publish to PyPI** workflow (`.github/workflows/publish.yaml`, manual
|
|
114
|
+
dispatch with a `vX.Y.Z` tag input) builds the SPA into the wheel
|
|
115
|
+
(`queryview/static/`), then gates the release on the installed wheel: an HTTP
|
|
116
|
+
smoke test, the packaged backend test suite (`pytest --pyargs queryview`), and
|
|
117
|
+
the Playwright e2e suite driving the packaged server (skippable via the
|
|
118
|
+
`skip-e2e` input for emergencies). It then publishes
|
|
119
|
+
[`queryview`](https://pypi.org/project/queryview/) via PyPI trusted publishing,
|
|
120
|
+
pushes the tag, and creates the GitHub release. The package version comes
|
|
121
|
+
from the tag (no version bump in `pyproject.toml`).
|
|
122
|
+
|
|
123
|
+
An installed wheel serves the bundled UI by default — see the
|
|
124
|
+
[quick start](README.md#quick-start).
|
queryview-0.0.7/PKG-INFO
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: queryview
|
|
3
|
+
Version: 0.0.7
|
|
4
|
+
Summary: Local SQL workbench for ClickHouse, Postgres and DuckDB, with a built-in MCP server for AI agents
|
|
5
|
+
Project-URL: Homepage, https://github.com/kolodkin/queryview
|
|
6
|
+
Project-URL: Repository, https://github.com/kolodkin/queryview
|
|
7
|
+
Project-URL: Issues, https://github.com/kolodkin/queryview/issues
|
|
8
|
+
License: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Python: >=3.11
|
|
11
|
+
Requires-Dist: aiosqlite>=0.22.1
|
|
12
|
+
Requires-Dist: alembic>=1.13
|
|
13
|
+
Requires-Dist: asyncpg>=0.29
|
|
14
|
+
Requires-Dist: cryptography>=42
|
|
15
|
+
Requires-Dist: duckdb>=1.0
|
|
16
|
+
Requires-Dist: fastapi>=0.110
|
|
17
|
+
Requires-Dist: httpx>=0.27
|
|
18
|
+
Requires-Dist: mcp<2,>=1.9
|
|
19
|
+
Requires-Dist: pyyaml>=6
|
|
20
|
+
Requires-Dist: sqlalchemy[asyncio]>=2.0
|
|
21
|
+
Requires-Dist: sqlmodel>=0.0.16
|
|
22
|
+
Requires-Dist: uvicorn>=0.29
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# QueryView
|
|
26
|
+
|
|
27
|
+
A local SQL workbench for **ClickHouse**, **Postgres** and **DuckDB** — and a
|
|
28
|
+
place to let an AI agent do the querying for you.
|
|
29
|
+
|
|
30
|
+
Everything happens at one prompt: type `connect prod` to open a database,
|
|
31
|
+
`query` to run SQL, `explorer` to click through tables, `dashboard` to open a
|
|
32
|
+
saved dashboard. Saved queries and dashboards live in workspaces that can back
|
|
33
|
+
themselves up to a git remote. A built-in MCP server lets an agent run
|
|
34
|
+
read-only queries, push a query or a whole dashboard into your open browser tab,
|
|
35
|
+
and snapshot your work to git.
|
|
36
|
+
|
|
37
|
+
QueryView is a **single-user tool that runs on your own machine**. It has no
|
|
38
|
+
login and assumes it is reachable only from localhost — don't expose its port.
|
|
39
|
+
|
|
40
|
+
## Quick start
|
|
41
|
+
|
|
42
|
+
Run the released package — API + bundled SPA on http://localhost:8000:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
uvx queryview
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
`--port` (or the `PORT` env var) picks the listen port, default 8000:
|
|
49
|
+
`uvx queryview --port 9000`.
|
|
50
|
+
|
|
51
|
+
Or run the container image — every release publishes
|
|
52
|
+
`ghcr.io/kolodkin/queryview` to GHCR for `linux/amd64` and `linux/arm64`, tagged
|
|
53
|
+
`vX.Y.Z` and (for non-pre-releases) `latest`:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
docker run -p 8000:8000 ghcr.io/kolodkin/queryview:latest
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
To serve on a different host port, remap it (the container keeps listening on
|
|
60
|
+
8000, which its healthcheck probes): `docker run -p 9000:8000 ...`. QueryView
|
|
61
|
+
expects to be reached from localhost only, so prefer binding the published port
|
|
62
|
+
to loopback: `docker run -p 127.0.0.1:8000:8000 ...`.
|
|
63
|
+
|
|
64
|
+
The command above keeps its state inside the container, so connections and
|
|
65
|
+
workspaces are lost when the container is removed. See
|
|
66
|
+
[Run with Docker](#run-with-docker) for a persistent setup.
|
|
67
|
+
|
|
68
|
+
## What you can do
|
|
69
|
+
|
|
70
|
+
Open http://localhost:8000 and type into the prompt:
|
|
71
|
+
|
|
72
|
+
| Command | What happens |
|
|
73
|
+
|---|---|
|
|
74
|
+
| `new clickhouse` / `new postgres` / `new duckdb` | Create a connection — host, port and credentials, or a file path for DuckDB. Passwords are encrypted at rest. |
|
|
75
|
+
| `connect <name>` | Open a saved connection and pick a database — that opens the explorer. The last one reconnects automatically next time. |
|
|
76
|
+
| `query` | Run SQL: paginated results, column picker, save/load reusable queries, download the page as CSV. |
|
|
77
|
+
| `explorer` | Browse tables without typing SQL — a sidebar of tables with row/size estimates, click to page through rows. |
|
|
78
|
+
| `dashboard [name]` | Open a saved dashboard: an HTML layout that re-runs its queries against live data every time you open it. |
|
|
79
|
+
|
|
80
|
+
Beyond the prompt:
|
|
81
|
+
|
|
82
|
+
- **Workspaces** group your saved queries and dashboards, and each can sync to
|
|
83
|
+
its own git remote for backup, history and restore —
|
|
84
|
+
[workspace.md](docs/workspace.md), [gitsync.md](docs/gitsync.md).
|
|
85
|
+
- **YAML export/import** moves a single query, a dashboard, or a whole
|
|
86
|
+
workspace between instances with no git involved —
|
|
87
|
+
[export-import.md](docs/export-import.md).
|
|
88
|
+
- **MCP** lets an agent query your databases and author dashboards — see
|
|
89
|
+
[below](#mcp-server).
|
|
90
|
+
|
|
91
|
+
## Run with Docker
|
|
92
|
+
|
|
93
|
+
The image runs as the non-root user `queryview` (UID 1000) and sets
|
|
94
|
+
`DATA_DIR=/var/lib/queryview`, so every piece of state lives directly under that
|
|
95
|
+
one directory:
|
|
96
|
+
|
|
97
|
+
| Path | Contents |
|
|
98
|
+
|---|---|
|
|
99
|
+
| `/var/lib/queryview/db.sqlite` | SQLite store: connections, workspaces, queries, dashboards |
|
|
100
|
+
| `/var/lib/queryview/encryption.key` | Key that encrypts stored connection passwords |
|
|
101
|
+
| `/var/lib/queryview/gitsync/` | Local clones used by workspace git sync |
|
|
102
|
+
|
|
103
|
+
Mount a volume at `/var/lib/queryview` and all three persist across container
|
|
104
|
+
restarts, removals and upgrades.
|
|
105
|
+
|
|
106
|
+
### Persisting it
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
mkdir -p ~/.queryview
|
|
110
|
+
docker run -d --name queryview \
|
|
111
|
+
-p 127.0.0.1:8000:8000 \
|
|
112
|
+
-v ~/.queryview:/var/lib/queryview \
|
|
113
|
+
ghcr.io/kolodkin/queryview:latest
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Create the directory first so Docker doesn't create it owned by root. The
|
|
117
|
+
container runs as UID 1000, the first user on most Linux distributions; if
|
|
118
|
+
`id -u` prints something else, add `--user "$(id -u):$(id -g)"` so the
|
|
119
|
+
container writes the files as you.
|
|
120
|
+
|
|
121
|
+
`~/.queryview` is also where a local `uvx queryview` keeps its state, so the
|
|
122
|
+
container and a local run share one database, key and set of clones with no
|
|
123
|
+
extra setup. Don't run both at once: each would serve the same SQLite file.
|
|
124
|
+
|
|
125
|
+
To let Docker own the location instead, swap the path for a named volume,
|
|
126
|
+
`-v queryview-data:/var/lib/queryview`. Docker seeds it from the image, so
|
|
127
|
+
ownership is already right and there is nothing to create up front, but the
|
|
128
|
+
state no longer lines up with a local run.
|
|
129
|
+
|
|
130
|
+
### Git sync
|
|
131
|
+
|
|
132
|
+
The image ships `git`, so workspace git sync works in the container, and its
|
|
133
|
+
clones live under `/var/lib/queryview/gitsync/`, which the mount above already
|
|
134
|
+
covers. Give the workspace an HTTPS remote carrying a scoped, expiring token —
|
|
135
|
+
[docs/gitsync.md](docs/gitsync.md) has the URL form and what becomes of the
|
|
136
|
+
credential.
|
|
137
|
+
|
|
138
|
+
SSH also works, but nothing in the container can answer a prompt: the key must
|
|
139
|
+
be passphrase-free and `known_hosts` must already list the host. Mount one
|
|
140
|
+
dedicated deploy key, not your whole `.ssh`, readable by UID 1000:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
docker run -d --name queryview \
|
|
144
|
+
-p 127.0.0.1:8000:8000 \
|
|
145
|
+
-v ~/.queryview:/var/lib/queryview \
|
|
146
|
+
-v ~/.ssh/queryview_deploy:/home/queryview/.ssh/id_ed25519:ro \
|
|
147
|
+
-v ~/.ssh/known_hosts:/home/queryview/.ssh/known_hosts:ro \
|
|
148
|
+
ghcr.io/kolodkin/queryview:latest
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## MCP server
|
|
152
|
+
|
|
153
|
+
The backend mounts a FastMCP server (Streamable HTTP) at
|
|
154
|
+
`http://localhost:8000/mcp/`. There is nothing extra to start — it runs inside
|
|
155
|
+
the server process (`uvx queryview`, `npm run dev`, ...). Registering the client
|
|
156
|
+
is a separate, one-time step on the machine running the agent: an HTTP MCP
|
|
157
|
+
server can't install itself into someone else's client.
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
claude mcp add --transport http queryview http://localhost:8000/mcp/
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Three things to get right:
|
|
164
|
+
|
|
165
|
+
- **Prefer the trailing slash.** The mount serves `/mcp/`. The slashless
|
|
166
|
+
`/mcp` also works — it 307-redirects — but registering the canonical path
|
|
167
|
+
skips a round trip on every call.
|
|
168
|
+
- **Match the port.** The URL must point at the port QueryView actually
|
|
169
|
+
listens on — `--port 9000` means `http://localhost:9000/mcp/`, and
|
|
170
|
+
`docker run -p 9000:8000` means the *host* port, `9000`, not the container's
|
|
171
|
+
`8000`.
|
|
172
|
+
- **Start QueryView first.** The client dials this URL when it starts; if
|
|
173
|
+
nothing is listening it reports a connection error and stays failed until you
|
|
174
|
+
reconnect it.
|
|
175
|
+
|
|
176
|
+
QueryView is a **local, single-user tool**: it assumes it is reachable only from
|
|
177
|
+
localhost. `/mcp/` is unauthenticated, and its tools can query every configured
|
|
178
|
+
connection and rewrite workspace git state, so don't publish the port. Bind the
|
|
179
|
+
container to loopback — `docker run -p 127.0.0.1:8000:8000 ...` — since a plain
|
|
180
|
+
`-p 8000:8000` listens on all interfaces.
|
|
181
|
+
|
|
182
|
+
Tools: `run_query` (read-only SQL, rows returned to the agent), `push_query`
|
|
183
|
+
and `push_dashboard` (fill a live browser session), `list_queries` /
|
|
184
|
+
`list_dashboards`, and `git_store` / `git_history` / `git_restore` (workspace
|
|
185
|
+
git backups). The push tools target an **armed** browser session: enable
|
|
186
|
+
"Allow remote control" from the agent icon next to the connection pill and use
|
|
187
|
+
the session id it shows. See [docs/remote.md](docs/remote.md) for the full
|
|
188
|
+
protocol.
|
|
189
|
+
|
|
190
|
+
## Where your data lives
|
|
191
|
+
|
|
192
|
+
All state lives in one data directory, `~/.queryview` on every OS, relocated
|
|
193
|
+
with `DATA_DIR`. Inside are the SQLite store `db.sqlite`, the local
|
|
194
|
+
password-encryption key `encryption.key`, and the workspace git-sync clones
|
|
195
|
+
under `gitsync/`. Back that directory up — or give each workspace a git remote
|
|
196
|
+
and let [git sync](docs/gitsync.md) do it.
|
|
197
|
+
|
|
198
|
+
## Documentation
|
|
199
|
+
|
|
200
|
+
- [queryview.md](docs/queryview.md) — the single-prompt page.
|
|
201
|
+
- [connect.md](docs/connect.md) — connections, drivers, sessions, storage.
|
|
202
|
+
- [query.md](docs/query.md) — the query panel: pagination, predefined queries, CSV.
|
|
203
|
+
- [explorer.md](docs/explorer.md) — the table navigator.
|
|
204
|
+
- [dashboard.md](docs/dashboard.md) — dashboards and how agents author them.
|
|
205
|
+
- [workspace.md](docs/workspace.md) — workspaces.
|
|
206
|
+
- [gitsync.md](docs/gitsync.md) — git backup, history and restore.
|
|
207
|
+
- [export-import.md](docs/export-import.md) — YAML export and import.
|
|
208
|
+
- [remote.md](docs/remote.md) — the remote-control protocol behind the MCP push tools.
|
|
209
|
+
- [api.md](docs/api.md) — the full HTTP endpoint reference.
|
|
210
|
+
|
|
211
|
+
## Contributing
|
|
212
|
+
|
|
213
|
+
Development setup, tests and the release process are in
|
|
214
|
+
[CONTRIBUTING.md](CONTRIBUTING.md).
|
|
215
|
+
|
|
216
|
+
## License
|
|
217
|
+
|
|
218
|
+
[MIT](LICENSE).
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# QueryView
|
|
2
|
+
|
|
3
|
+
A local SQL workbench for **ClickHouse**, **Postgres** and **DuckDB** — and a
|
|
4
|
+
place to let an AI agent do the querying for you.
|
|
5
|
+
|
|
6
|
+
Everything happens at one prompt: type `connect prod` to open a database,
|
|
7
|
+
`query` to run SQL, `explorer` to click through tables, `dashboard` to open a
|
|
8
|
+
saved dashboard. Saved queries and dashboards live in workspaces that can back
|
|
9
|
+
themselves up to a git remote. A built-in MCP server lets an agent run
|
|
10
|
+
read-only queries, push a query or a whole dashboard into your open browser tab,
|
|
11
|
+
and snapshot your work to git.
|
|
12
|
+
|
|
13
|
+
QueryView is a **single-user tool that runs on your own machine**. It has no
|
|
14
|
+
login and assumes it is reachable only from localhost — don't expose its port.
|
|
15
|
+
|
|
16
|
+
## Quick start
|
|
17
|
+
|
|
18
|
+
Run the released package — API + bundled SPA on http://localhost:8000:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
uvx queryview
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`--port` (or the `PORT` env var) picks the listen port, default 8000:
|
|
25
|
+
`uvx queryview --port 9000`.
|
|
26
|
+
|
|
27
|
+
Or run the container image — every release publishes
|
|
28
|
+
`ghcr.io/kolodkin/queryview` to GHCR for `linux/amd64` and `linux/arm64`, tagged
|
|
29
|
+
`vX.Y.Z` and (for non-pre-releases) `latest`:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
docker run -p 8000:8000 ghcr.io/kolodkin/queryview:latest
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
To serve on a different host port, remap it (the container keeps listening on
|
|
36
|
+
8000, which its healthcheck probes): `docker run -p 9000:8000 ...`. QueryView
|
|
37
|
+
expects to be reached from localhost only, so prefer binding the published port
|
|
38
|
+
to loopback: `docker run -p 127.0.0.1:8000:8000 ...`.
|
|
39
|
+
|
|
40
|
+
The command above keeps its state inside the container, so connections and
|
|
41
|
+
workspaces are lost when the container is removed. See
|
|
42
|
+
[Run with Docker](#run-with-docker) for a persistent setup.
|
|
43
|
+
|
|
44
|
+
## What you can do
|
|
45
|
+
|
|
46
|
+
Open http://localhost:8000 and type into the prompt:
|
|
47
|
+
|
|
48
|
+
| Command | What happens |
|
|
49
|
+
|---|---|
|
|
50
|
+
| `new clickhouse` / `new postgres` / `new duckdb` | Create a connection — host, port and credentials, or a file path for DuckDB. Passwords are encrypted at rest. |
|
|
51
|
+
| `connect <name>` | Open a saved connection and pick a database — that opens the explorer. The last one reconnects automatically next time. |
|
|
52
|
+
| `query` | Run SQL: paginated results, column picker, save/load reusable queries, download the page as CSV. |
|
|
53
|
+
| `explorer` | Browse tables without typing SQL — a sidebar of tables with row/size estimates, click to page through rows. |
|
|
54
|
+
| `dashboard [name]` | Open a saved dashboard: an HTML layout that re-runs its queries against live data every time you open it. |
|
|
55
|
+
|
|
56
|
+
Beyond the prompt:
|
|
57
|
+
|
|
58
|
+
- **Workspaces** group your saved queries and dashboards, and each can sync to
|
|
59
|
+
its own git remote for backup, history and restore —
|
|
60
|
+
[workspace.md](docs/workspace.md), [gitsync.md](docs/gitsync.md).
|
|
61
|
+
- **YAML export/import** moves a single query, a dashboard, or a whole
|
|
62
|
+
workspace between instances with no git involved —
|
|
63
|
+
[export-import.md](docs/export-import.md).
|
|
64
|
+
- **MCP** lets an agent query your databases and author dashboards — see
|
|
65
|
+
[below](#mcp-server).
|
|
66
|
+
|
|
67
|
+
## Run with Docker
|
|
68
|
+
|
|
69
|
+
The image runs as the non-root user `queryview` (UID 1000) and sets
|
|
70
|
+
`DATA_DIR=/var/lib/queryview`, so every piece of state lives directly under that
|
|
71
|
+
one directory:
|
|
72
|
+
|
|
73
|
+
| Path | Contents |
|
|
74
|
+
|---|---|
|
|
75
|
+
| `/var/lib/queryview/db.sqlite` | SQLite store: connections, workspaces, queries, dashboards |
|
|
76
|
+
| `/var/lib/queryview/encryption.key` | Key that encrypts stored connection passwords |
|
|
77
|
+
| `/var/lib/queryview/gitsync/` | Local clones used by workspace git sync |
|
|
78
|
+
|
|
79
|
+
Mount a volume at `/var/lib/queryview` and all three persist across container
|
|
80
|
+
restarts, removals and upgrades.
|
|
81
|
+
|
|
82
|
+
### Persisting it
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
mkdir -p ~/.queryview
|
|
86
|
+
docker run -d --name queryview \
|
|
87
|
+
-p 127.0.0.1:8000:8000 \
|
|
88
|
+
-v ~/.queryview:/var/lib/queryview \
|
|
89
|
+
ghcr.io/kolodkin/queryview:latest
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Create the directory first so Docker doesn't create it owned by root. The
|
|
93
|
+
container runs as UID 1000, the first user on most Linux distributions; if
|
|
94
|
+
`id -u` prints something else, add `--user "$(id -u):$(id -g)"` so the
|
|
95
|
+
container writes the files as you.
|
|
96
|
+
|
|
97
|
+
`~/.queryview` is also where a local `uvx queryview` keeps its state, so the
|
|
98
|
+
container and a local run share one database, key and set of clones with no
|
|
99
|
+
extra setup. Don't run both at once: each would serve the same SQLite file.
|
|
100
|
+
|
|
101
|
+
To let Docker own the location instead, swap the path for a named volume,
|
|
102
|
+
`-v queryview-data:/var/lib/queryview`. Docker seeds it from the image, so
|
|
103
|
+
ownership is already right and there is nothing to create up front, but the
|
|
104
|
+
state no longer lines up with a local run.
|
|
105
|
+
|
|
106
|
+
### Git sync
|
|
107
|
+
|
|
108
|
+
The image ships `git`, so workspace git sync works in the container, and its
|
|
109
|
+
clones live under `/var/lib/queryview/gitsync/`, which the mount above already
|
|
110
|
+
covers. Give the workspace an HTTPS remote carrying a scoped, expiring token —
|
|
111
|
+
[docs/gitsync.md](docs/gitsync.md) has the URL form and what becomes of the
|
|
112
|
+
credential.
|
|
113
|
+
|
|
114
|
+
SSH also works, but nothing in the container can answer a prompt: the key must
|
|
115
|
+
be passphrase-free and `known_hosts` must already list the host. Mount one
|
|
116
|
+
dedicated deploy key, not your whole `.ssh`, readable by UID 1000:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
docker run -d --name queryview \
|
|
120
|
+
-p 127.0.0.1:8000:8000 \
|
|
121
|
+
-v ~/.queryview:/var/lib/queryview \
|
|
122
|
+
-v ~/.ssh/queryview_deploy:/home/queryview/.ssh/id_ed25519:ro \
|
|
123
|
+
-v ~/.ssh/known_hosts:/home/queryview/.ssh/known_hosts:ro \
|
|
124
|
+
ghcr.io/kolodkin/queryview:latest
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## MCP server
|
|
128
|
+
|
|
129
|
+
The backend mounts a FastMCP server (Streamable HTTP) at
|
|
130
|
+
`http://localhost:8000/mcp/`. There is nothing extra to start — it runs inside
|
|
131
|
+
the server process (`uvx queryview`, `npm run dev`, ...). Registering the client
|
|
132
|
+
is a separate, one-time step on the machine running the agent: an HTTP MCP
|
|
133
|
+
server can't install itself into someone else's client.
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
claude mcp add --transport http queryview http://localhost:8000/mcp/
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Three things to get right:
|
|
140
|
+
|
|
141
|
+
- **Prefer the trailing slash.** The mount serves `/mcp/`. The slashless
|
|
142
|
+
`/mcp` also works — it 307-redirects — but registering the canonical path
|
|
143
|
+
skips a round trip on every call.
|
|
144
|
+
- **Match the port.** The URL must point at the port QueryView actually
|
|
145
|
+
listens on — `--port 9000` means `http://localhost:9000/mcp/`, and
|
|
146
|
+
`docker run -p 9000:8000` means the *host* port, `9000`, not the container's
|
|
147
|
+
`8000`.
|
|
148
|
+
- **Start QueryView first.** The client dials this URL when it starts; if
|
|
149
|
+
nothing is listening it reports a connection error and stays failed until you
|
|
150
|
+
reconnect it.
|
|
151
|
+
|
|
152
|
+
QueryView is a **local, single-user tool**: it assumes it is reachable only from
|
|
153
|
+
localhost. `/mcp/` is unauthenticated, and its tools can query every configured
|
|
154
|
+
connection and rewrite workspace git state, so don't publish the port. Bind the
|
|
155
|
+
container to loopback — `docker run -p 127.0.0.1:8000:8000 ...` — since a plain
|
|
156
|
+
`-p 8000:8000` listens on all interfaces.
|
|
157
|
+
|
|
158
|
+
Tools: `run_query` (read-only SQL, rows returned to the agent), `push_query`
|
|
159
|
+
and `push_dashboard` (fill a live browser session), `list_queries` /
|
|
160
|
+
`list_dashboards`, and `git_store` / `git_history` / `git_restore` (workspace
|
|
161
|
+
git backups). The push tools target an **armed** browser session: enable
|
|
162
|
+
"Allow remote control" from the agent icon next to the connection pill and use
|
|
163
|
+
the session id it shows. See [docs/remote.md](docs/remote.md) for the full
|
|
164
|
+
protocol.
|
|
165
|
+
|
|
166
|
+
## Where your data lives
|
|
167
|
+
|
|
168
|
+
All state lives in one data directory, `~/.queryview` on every OS, relocated
|
|
169
|
+
with `DATA_DIR`. Inside are the SQLite store `db.sqlite`, the local
|
|
170
|
+
password-encryption key `encryption.key`, and the workspace git-sync clones
|
|
171
|
+
under `gitsync/`. Back that directory up — or give each workspace a git remote
|
|
172
|
+
and let [git sync](docs/gitsync.md) do it.
|
|
173
|
+
|
|
174
|
+
## Documentation
|
|
175
|
+
|
|
176
|
+
- [queryview.md](docs/queryview.md) — the single-prompt page.
|
|
177
|
+
- [connect.md](docs/connect.md) — connections, drivers, sessions, storage.
|
|
178
|
+
- [query.md](docs/query.md) — the query panel: pagination, predefined queries, CSV.
|
|
179
|
+
- [explorer.md](docs/explorer.md) — the table navigator.
|
|
180
|
+
- [dashboard.md](docs/dashboard.md) — dashboards and how agents author them.
|
|
181
|
+
- [workspace.md](docs/workspace.md) — workspaces.
|
|
182
|
+
- [gitsync.md](docs/gitsync.md) — git backup, history and restore.
|
|
183
|
+
- [export-import.md](docs/export-import.md) — YAML export and import.
|
|
184
|
+
- [remote.md](docs/remote.md) — the remote-control protocol behind the MCP push tools.
|
|
185
|
+
- [api.md](docs/api.md) — the full HTTP endpoint reference.
|
|
186
|
+
|
|
187
|
+
## Contributing
|
|
188
|
+
|
|
189
|
+
Development setup, tests and the release process are in
|
|
190
|
+
[CONTRIBUTING.md](CONTRIBUTING.md).
|
|
191
|
+
|
|
192
|
+
## License
|
|
193
|
+
|
|
194
|
+
[MIT](LICENSE).
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"""Shared fixtures for backend (non-e2e) tests.
|
|
2
2
|
|
|
3
|
-
Redirects the
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Redirects the data directory to a per-session tempdir so tests don't touch the
|
|
4
|
+
real one, and resets the lazy module-level engine/schema state in
|
|
5
|
+
`queryview.connect` before tests run."""
|
|
6
6
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
@@ -14,7 +14,18 @@ import pytest
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
@pytest.fixture
|
|
17
|
-
def
|
|
17
|
+
def clone_base(tmp_path, monkeypatch) -> Path:
|
|
18
|
+
"""Redirect every workspace's sync clone into this test's tmpdir, so clones
|
|
19
|
+
never land in the session data dir and can't leak between tests."""
|
|
20
|
+
from queryview import gitsync
|
|
21
|
+
|
|
22
|
+
base = tmp_path / "clones"
|
|
23
|
+
monkeypatch.setattr(gitsync, "_clone_base", lambda: base)
|
|
24
|
+
return base
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@pytest.fixture
|
|
28
|
+
def git_env(tmp_path, clone_base):
|
|
18
29
|
"""A local bare repo as the default workspace's git-sync remote + a fresh
|
|
19
30
|
per-workspace clone base dir. Resets the default workspace to 'no remote'
|
|
20
31
|
on teardown so unconfigured-state tests stay valid."""
|
|
@@ -28,7 +39,6 @@ def git_env(tmp_path, monkeypatch):
|
|
|
28
39
|
check=True,
|
|
29
40
|
capture_output=True,
|
|
30
41
|
)
|
|
31
|
-
monkeypatch.setenv("GIT_SYNC_DIR", str(tmp_path / "clones"))
|
|
32
42
|
asyncio.run(update_workspace(DEFAULT_WORKSPACE, remote=str(remote)))
|
|
33
43
|
yield remote
|
|
34
44
|
asyncio.run(update_workspace(DEFAULT_WORKSPACE, remote=None))
|
|
@@ -69,13 +79,7 @@ def default_ws_id() -> int:
|
|
|
69
79
|
@pytest.fixture(scope="session", autouse=True)
|
|
70
80
|
def _isolated_db(tmp_path_factory: pytest.TempPathFactory):
|
|
71
81
|
tmp: Path = tmp_path_factory.mktemp("qv_backend_tests")
|
|
72
|
-
os.environ["
|
|
73
|
-
os.environ["DB_KEY_PATH"] = str(tmp / "test.db.key")
|
|
74
|
-
|
|
75
|
-
# The workspaces migration seeds the default workspace from GIT_SYNC_*;
|
|
76
|
-
# tests control that per-test (monkeypatch), never from ambient env.
|
|
77
|
-
os.environ.pop("GIT_SYNC_REMOTE", None)
|
|
78
|
-
os.environ.pop("GIT_SYNC_BRANCH", None)
|
|
82
|
+
os.environ["DATA_DIR"] = str(tmp)
|
|
79
83
|
|
|
80
84
|
# Reset the lazy globals so the next DB touch picks up the new paths.
|
|
81
85
|
import queryview.connect as _c
|