Skip to content

Commit 2efa428

Browse files
committed
update 1
1 parent 877e55c commit 2efa428

288 files changed

Lines changed: 22013 additions & 2802 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.dockerignore‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Keep the build context small and the image reproducible: everything below is either rebuilt inside
2+
# the container, irrelevant to it, or must not travel into an image at all.
3+
**/bin/
4+
**/obj/
5+
node_modules/
6+
test-results/
7+
publish-test/
8+
.git/
9+
.github/
10+
.claude/
11+
.vs/
12+
.env
13+
*.ps1
14+
*.md
15+
!README.md
16+
17+
# Real API keys and database credentials. Never in an image.
18+
dev-secrets.json

‎.github/workflows/ci.yml‎

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Builds, tests, and packages the web-only app on every push. The zip this produces is the app itself:
2+
# a folder of static files that can be dropped on any web host, which is what "no backend" means here.
3+
name: CI
4+
5+
on:
6+
push:
7+
branches: [master, main]
8+
pull_request:
9+
workflow_dispatch:
10+
11+
permissions:
12+
contents: read
13+
14+
jobs:
15+
build:
16+
name: Build, test and package
17+
runs-on: ubuntu-latest
18+
19+
steps:
20+
- uses: actions/checkout@v7
21+
22+
- uses: actions/setup-dotnet@v6
23+
with:
24+
dotnet-version: '10.0.x'
25+
26+
- uses: actions/setup-node@v4
27+
with:
28+
node-version: '22'
29+
30+
- name: Build
31+
run: dotnet build GraphDBViewerWeb.slnx -c Release
32+
33+
# The live-server integration tests dial a real Gremlin endpoint, which no runner has.
34+
- name: C# tests
35+
run: dotnet test GraphDBViewerWeb.slnx -c Release --no-build
36+
env:
37+
SKIP_GREMLIN_TESTS: 'true'
38+
39+
- name: JS tests
40+
run: npm ci && npm test
41+
42+
# WasmBuildNative=false skips the Emscripten native relink, so the runner needs no wasm-tools
43+
# workload. The payload is slightly larger; install the workload and drop the flag to shrink it.
44+
- name: Publish the web-only app
45+
run: dotnet publish GraphDBViewerWeb/GraphDBViewerWeb.csproj -c Release -p:WasmBuildNative=false -o publish
46+
47+
- name: Package
48+
run: |
49+
cd publish/wwwroot
50+
zip -qr "$GITHUB_WORKSPACE/graph-db-viewer-web.zip" .
51+
52+
- uses: actions/upload-artifact@v4
53+
with:
54+
name: graph-db-viewer-web-${{ github.sha }}
55+
path: graph-db-viewer-web.zip
56+
if-no-files-found: error
57+
58+
e2e:
59+
name: End-to-end
60+
runs-on: ubuntu-latest
61+
62+
steps:
63+
- uses: actions/checkout@v7
64+
65+
- uses: actions/setup-dotnet@v6
66+
with:
67+
dotnet-version: '10.0.x'
68+
69+
- uses: actions/setup-node@v4
70+
with:
71+
node-version: '22'
72+
73+
- run: npm ci
74+
75+
- run: npx playwright install --with-deps chromium
76+
77+
# Playwright starts the app itself (see playwright.config.js). Two workers rather than the default:
78+
# each one boots a WebAssembly runtime and 250+ resources, and the suite drops specs under load.
79+
- name: Playwright
80+
run: npx playwright test --workers=2
81+
82+
- uses: actions/upload-artifact@v4
83+
if: failure()
84+
with:
85+
name: playwright-report
86+
path: test-results/
87+
if-no-files-found: ignore
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# Publishes the backend edition to Docker Hub: the same WebAssembly app, served by a host that adds the
2+
# query proxy for databases a browser cannot reach. The web-only build needs none of this — see README.
3+
#
4+
# Set up once, on the repository:
5+
# Settings > Secrets and variables > Actions > Variables: DOCKERHUB_USERNAME
6+
# Settings > Secrets and variables > Actions > Secrets: DOCKERHUB_TOKEN (a Docker Hub access token,
7+
# Read & Write, not the account password)
8+
#
9+
# Without those the job is skipped rather than failed, so a fork does not get a red tick for credentials
10+
# it was never meant to have.
11+
name: Publish Docker image
12+
13+
on:
14+
push:
15+
branches: [master, main]
16+
tags: ['v*']
17+
workflow_dispatch:
18+
inputs:
19+
tag:
20+
description: 'Extra tag to publish under (optional)'
21+
required: false
22+
23+
permissions:
24+
contents: read
25+
26+
env:
27+
IMAGE_NAME: graphdbviewer-server
28+
29+
jobs:
30+
publish:
31+
name: Build and push
32+
runs-on: ubuntu-latest
33+
if: vars.DOCKERHUB_USERNAME != ''
34+
35+
steps:
36+
- uses: actions/checkout@v7
37+
38+
# arm64 is emulated on an amd64 runner; the setup below is what makes that possible.
39+
- uses: docker/setup-qemu-action@v3
40+
41+
- uses: docker/setup-buildx-action@v3
42+
43+
- id: meta
44+
uses: docker/metadata-action@v5
45+
with:
46+
images: ${{ vars.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}
47+
tags: |
48+
type=semver,pattern={{version}}
49+
type=semver,pattern={{major}}.{{minor}}
50+
type=semver,pattern={{major}}
51+
type=sha,format=short
52+
type=raw,value=${{ inputs.tag }},enable=${{ inputs.tag != '' }}
53+
flavor: |
54+
latest=auto
55+
56+
# Built for this runner's architecture first and kept locally, so the image can be run before
57+
# anything is published. A container that starts but serves nothing is exactly the failure a
58+
# registry push would otherwise hide until someone pulled it.
59+
- name: Build for the smoke test
60+
uses: docker/build-push-action@v6
61+
with:
62+
context: .
63+
load: true
64+
tags: ${{ env.IMAGE_NAME }}:smoke-test
65+
cache-from: type=gha
66+
cache-to: type=gha,mode=max
67+
68+
- name: Smoke test the image
69+
run: |
70+
docker run -d --name smoke -p 8080:8080 "$IMAGE_NAME:smoke-test"
71+
72+
# The app is WebAssembly, so a runner cannot execute it — but the host serving it answers for
73+
# itself, and these two answers are what the edition exists to provide.
74+
for i in $(seq 1 30); do
75+
if curl -fsS http://localhost:8080/api/graph/capabilities | grep -q '"serverRoute":true'; then
76+
echo "the host answers as a proxying host"
77+
curl -fsS http://localhost:8080/ | grep -qi '<html' && echo "the app is served"
78+
docker rm -f smoke
79+
exit 0
80+
fi
81+
sleep 2
82+
done
83+
84+
echo "the container never answered"
85+
docker logs smoke
86+
docker rm -f smoke
87+
exit 1
88+
89+
- uses: docker/login-action@v3
90+
with:
91+
username: ${{ vars.DOCKERHUB_USERNAME }}
92+
password: ${{ secrets.DOCKERHUB_TOKEN }}
93+
94+
- name: Build and push
95+
uses: docker/build-push-action@v6
96+
with:
97+
context: .
98+
platforms: linux/amd64,linux/arm64
99+
push: true
100+
tags: ${{ steps.meta.outputs.tags }}
101+
labels: ${{ steps.meta.outputs.labels }}
102+
cache-from: type=gha
103+
cache-to: type=gha,mode=max

‎.github/workflows/release.yml‎

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# Attaches the web-only app to a GitHub Release as a zip, so a released version is a thing you can
2+
# download and serve rather than something you have to build. Tag a commit v1.2.3 and push the tag.
3+
#
4+
# The same tag also triggers publish-docker.yml, which pushes the backend edition to Docker Hub. The two
5+
# run independently -- nothing here waits on that -- but the notes written below name the image, because
6+
# a release that mentions only the zip reads as though the container does not exist.
7+
name: Release
8+
9+
on:
10+
push:
11+
tags: ['v*']
12+
workflow_dispatch:
13+
inputs:
14+
tag:
15+
description: 'Existing tag to build and release (e.g. v1.2.3)'
16+
required: true
17+
18+
permissions:
19+
contents: write
20+
21+
env:
22+
# Must match IMAGE_NAME in publish-docker.yml. Named in both because Actions has no way to share a
23+
# value between workflows short of a repository variable, which would then have to be configured
24+
# before either could run. Renaming the image means renaming it twice.
25+
IMAGE_NAME: graphdbviewer-server
26+
27+
jobs:
28+
release:
29+
name: Package and publish the release
30+
runs-on: ubuntu-latest
31+
32+
steps:
33+
- uses: actions/checkout@v7
34+
with:
35+
ref: ${{ inputs.tag || github.ref }}
36+
37+
- uses: actions/setup-dotnet@v6
38+
with:
39+
dotnet-version: '10.0.x'
40+
41+
# The tag is the version, minus its leading v. Everything downstream is named from this.
42+
- name: Read the version from the tag
43+
run: |
44+
TAG="${{ inputs.tag || github.ref_name }}"
45+
echo "TAG=$TAG" >> "$GITHUB_ENV"
46+
echo "VERSION=${TAG#v}" >> "$GITHUB_ENV"
47+
48+
- name: Publish the web-only app
49+
run: dotnet publish GraphDBViewerWeb/GraphDBViewerWeb.csproj -c Release -p:WasmBuildNative=false -o publish
50+
51+
# Zipped from inside wwwroot, so it unpacks as the site root rather than a nested folder — the
52+
# contents are what a static host serves.
53+
- name: Package
54+
run: |
55+
cd publish/wwwroot
56+
zip -qr "$GITHUB_WORKSPACE/graph-db-viewer-web-$VERSION.zip" .
57+
58+
- name: Create or update the release
59+
env:
60+
GH_TOKEN: ${{ github.token }}
61+
run: |
62+
if gh release view "$TAG" >/dev/null 2>&1; then
63+
gh release upload "$TAG" "graph-db-viewer-web-$VERSION.zip" --clobber
64+
else
65+
gh release create "$TAG" "graph-db-viewer-web-$VERSION.zip" \
66+
--title "$TAG" \
67+
--generate-notes
68+
fi
69+
70+
# Generated notes are commit subjects and nothing else, so the container is invisible to anyone
71+
# reading the release. Skipped when no Docker Hub account is configured, matching publish-docker.yml:
72+
# there is no image to point at then, and half an image name is worse than none.
73+
- name: Name the Docker image in the release notes
74+
if: vars.DOCKERHUB_USERNAME != ''
75+
env:
76+
GH_TOKEN: ${{ github.token }}
77+
IMAGE: ${{ vars.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}
78+
run: |
79+
BODY=$(gh release view "$TAG" --json body -q .body)
80+
81+
# Idempotent: re-running this workflow over an existing release must not stack the section up
82+
# twice, and must not overwrite notes somebody has edited by hand around it.
83+
if printf '%s' "$BODY" | grep -q '^## Docker image'; then
84+
echo "The notes already name the image."
85+
exit 0
86+
fi
87+
88+
{
89+
printf '%s\n\n' "$BODY"
90+
printf '## Docker image\n\n'
91+
printf 'This version as a container — the backend edition, which adds a proxy for databases a\n'
92+
printf 'browser cannot reach (an endpoint with no CORS headers, plain http on an https page, Bolt,\n'
93+
printf 'or a host only the server can route to):\n\n'
94+
printf '```\n'
95+
printf 'docker run --rm -p 8080:8080 %s:%s\n' "$IMAGE" "$VERSION"
96+
printf '```\n\n'
97+
printf 'Then open http://localhost:8080. Published to Docker Hub from this same tag.\n\n'
98+
printf 'The zip attached to this release needs none of that: unpack it onto any static host and\n'
99+
printf 'open it. Everything runs in the browser, and the toggle for the server route hides itself\n'
100+
printf 'when there is no host answering behind it.\n'
101+
} > notes.md
102+
103+
gh release edit "$TAG" --notes-file notes.md

‎Dockerfile‎

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Builds the backend edition of Graph DB Viewer: an ASP.NET Core host that serves the same WebAssembly
2+
# app the static build serves, plus the query proxy. Build from the repo root:
3+
#
4+
# docker build -t graphdbviewer .
5+
# docker run --rm -p 8080:8080 graphdbviewer # then open http://localhost:8080
6+
#
7+
# ---- What the host is for ----
8+
#
9+
# The viewer talks to databases from the browser, and for most of them that is enough. This host exists
10+
# for the ones where it is not: an endpoint that sends no CORS headers, a plain-http endpoint on an https
11+
# page, a database only the server can route to, and Bolt, which needs a raw TCP socket the browser
12+
# cannot open. Connections carry a "Make requests from" toggle, and the Server side of it lands here.
13+
#
14+
# The app detects the host by asking for api/graph/capabilities on startup, so the same build works
15+
# either way -- served as static files it gets a 404, hides the toggle, and stays browser-direct.
16+
#
17+
# ---- What it is not ----
18+
#
19+
# There are no accounts, no database and no server-side storage here: the workspace stays in the
20+
# browser's IndexedDB exactly as it does without a host. The full-stack platform, with accounts and
21+
# saved workspaces that follow a user between machines, is a separate product (Treeality).
22+
#
23+
# ---- Two things to know before exposing it ----
24+
#
25+
# **The proxy endpoint is unauthenticated and dials whatever it is asked to.** That is the feature -- it
26+
# is a developer tool, and pointing it at an arbitrary endpoint is the point -- but it means anything
27+
# that can reach this container can make it open connections on its behalf, including to addresses only
28+
# it can route to. Do not put this on the open internet.
29+
#
30+
# **Credentials travel to the host.** On the Server route a connection's key is sent here rather than
31+
# staying in the browser. TLS is expected to terminate at a reverse proxy in front of the container; the
32+
# app serves plain HTTP on 8080.
33+
34+
# ---- build ----
35+
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
36+
WORKDIR /src
37+
38+
# The host, the client it serves, and the core they share. Tests and tooling stay out.
39+
COPY GraphDBViewer.Core/ GraphDBViewer.Core/
40+
COPY GraphDBViewerWeb/ GraphDBViewerWeb/
41+
COPY GraphDBViewerWeb.Server/ GraphDBViewerWeb.Server/
42+
43+
# WasmBuildNative=false skips the Emscripten native relink of the WASM runtime, so the image needs no
44+
# wasm-tools workload and builds fast and reliably. The client payload is a little larger; if a smaller
45+
# download matters more, install the wasm-tools workload in this stage and drop the flag.
46+
RUN dotnet publish GraphDBViewerWeb.Server/GraphDBViewerWeb.Server.csproj -c Release -p:WasmBuildNative=false -o /app
47+
48+
# ---- runtime ----
49+
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS final
50+
WORKDIR /app
51+
COPY --from=build /app .
52+
53+
# The .NET 8+ aspnet image already defaults Kestrel to http://+:8080.
54+
EXPOSE 8080
55+
ENTRYPOINT ["dotnet", "GraphDBViewerWeb.Server.dll"]

0 commit comments

Comments
 (0)