`APSIS_BOOK` is one character longer than `APSIS_WEB`, which pushes a few lines past the width rustfmt allows. |
||
|---|---|---|
| content | ||
| docs | ||
| frontend | ||
| scripts | ||
| src | ||
| tests | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| LICENSE | ||
| README.md | ||
| README.zh.md | ||
| build.rs | ||
| rust-toolchain.toml | ||
README.md
apsis-book
简体中文 | English
The web service layer of ApsisRS. It presents the ApsisRS computational kernels for commercial spaceflight and satellite internet systems in a browser: the concept documentation, a knowledge graph, the interface documentation extracted from the library source, and an online tool set that runs the kernels on parameters the user supplies.
The service builds to a single binary that embeds every asset it serves. It does not read a content directory or a frontend build at run time, and it does not need either to be present next to the binary.
What it serves
| Section | Content | Route prefix |
|---|---|---|
| Concepts | Documents on the time scales, the reference frames, the element formats, and the propagation models, with the units and the applicable range of each | /api/docs |
| Knowledge graph | The concepts and their relationships, plus the crate structure of the library and the algorithm flow graphs | /api/graph |
| Interface | The public interface of the ApsisRS layers the service builds against, extracted from the source doc comments | /api/api |
| Online tools | TLE parsing, SGP4 propagation, track sampling, time scale conversion, coordinate conversion, frame rotation, Keplerian conversion, and Hohmann transfer planning | /api/tools |
Design constraints
The service follows the constraints of the workspace it belongs to.
- One runtime dependency. The only entry in
[dependencies]isapsis-rs, through a path dependency. The HTTP server, the JSON codec, the router, the Markdown renderer, and the graph model are all written against the standard library. - No
unsafe. The crate does not contain an unsafe block. - No panic in a request path. A malformed request, a bad parameter, or a failing computation produces a response, not a panic. The worker that serves a request catches a panic in the handler as a final guard and answers with a server error.
- Self-contained comments. Every comment explains the code, the algorithm, the interface, or the project. No comment refers to a private document, an instruction, or a plan.
Building
The frontend must be built before the release binary so that its output can be embedded. A release build does this automatically.
Every built asset carries a content hash in its name, so replacing the binary
replaces those names. A browser therefore cannot be holding a stale bundle:
the new bundle is requested under a URL it has never seen. The entry point
is served with no-cache, must-revalidate, and hashed assets with
public, max-age=31536000, immutable. The service registers no service
worker.
# Development: a debug build embeds whatever is in frontend/dist and does
# not run the frontend build.
cargo build
# Release: build.rs builds the frontend and embeds the result.
cargo build --release
The binary is at target/release/apsis-book.
Environment variables:
| Variable | Effect |
|---|---|
APSIS_BOOK_HOST |
Interface to bind, default 127.0.0.1 |
APSIS_BOOK_PORT |
Port to bind, default 8080 |
APSIS_BOOK_ADDRESS |
Bind address as HOST:PORT, overriding the two above |
APSIS_BOOK_WORKERS |
Worker threads, default 4 |
APSIS_BOOK_SKIP_FRONTEND |
Set to 1 to skip the frontend build in a release build |
APSIS_BOOK_FRONTEND_DIR |
Frontend directory, default frontend |
Command line options override the environment:
# Bind every interface on port 8080.
apsis-book --host 0.0.0.0 --port 8080
# The combined form is equivalent.
apsis-book --address 0.0.0.0:8080
--host and --port may be given together. --address cannot be combined
with either of them, because the order in which they would apply is not
defined. A host may be a literal address, an IPv6 literal in brackets, or a
name the operating system resolves.
Language
The interface is bilingual and opens in Chinese. The reader switches to English with the control in the top bar; the choice is stored in the browser and applies to every page.
The backend carries every user facing string in both languages, so the switch
takes effect without a request. A concept document is written once in English
as content/concepts/<id>.md and translated as
content/concepts/<id>.zh.md. A document without a translation is shown in
English to every reader, with a note saying so.
Running
./target/release/apsis-book --address 127.0.0.1:8080
Open http://127.0.0.1:8080. The JSON interfaces are available without the
frontend:
GET /healthGET /api/docs,GET /api/docs/<id>,GET /api/docs/search?q=GET /api/graph,GET /api/graph/crates,GET /api/graph/flows,GET /api/graph/<flow>GET /api/api,GET /api/api/crates/<name>,GET /api/api/items/<name>GET /api/tools,GET /api/tools/<id>,POST /api/tools/<id>/run
A tool call takes a JSON object of parameters and returns a shaped result:
curl -s http://127.0.0.1:8080/api/tools/hohmann-transfer/run \
-H 'content-type: application/json' \
-d '{"from_radius": 6778, "to_radius": 42164}'
Content layout
The content is a superset of what any one library implements. It has two groups.
概念 / Concepts describes the field. The layers follow the ones the library is built in, because that is the order a reader meets the subject:
| Layer | Subject |
|---|---|
| 导论 Introduction | Scope, organisation, and how to read a page |
| 基础层 Foundations | Units, dimensions, numerical method, error |
| 物理层 Physics | Time scales and reference frames, which are two unrelated subjects and are both held here |
| 轨道层 Orbit | Elements, element formats, perturbations, propagators, manoeuvres |
| 射频与传播 Radio and propagation | Doppler, link budgets, the medium, interference |
| 星座与网络 Constellation and network | Constellations, access, topology, handover, routing |
| 运行层 Operations | Telemetry, tracking, command, planning |
| 仿真层 Simulation | Scenarios, events, and what a result means |
实现 / Implementation describes what one particular library implements of the above, including how it is organised and where its algorithms come from. Keeping it separate is what makes the concept set independent: a page about link budgets does not change when a library changes.
A document belongs to the layer named by the directory it is in, so adding a
document means creating it under its layer and listing it in
chapters.json.
Regenerating the interface catalog
The interface documentation is generated from the ApsisRS sources rather than written by hand. Run the extractor from this directory when the sources change, then rebuild:
scripts/extract_api.sh
cargo build --release
The script reads the workspace at ../apsis-rs by default; set
APSIS_RS_WORKSPACE to point at a checkout elsewhere. It requires Python 3.8
or later. The Rust build does not run it and does not depend on Python.
Editing the content
| Path | Contents |
|---|---|
content/concepts/chapters.json |
The groups, their layers, and the reading order, with bilingual titles |
content/concepts/<layer>/*.md |
Documents, one directory per layer; the directory gives the layer, the file name gives the identifier |
content/concepts/<layer>/*.zh.md |
Chinese translations, named for the file they translate |
content/graph/concepts.json |
The concept graph |
content/flows/*.json |
The algorithm flow graphs |
content/api.json |
The generated interface catalog; do not edit by hand |
content/api.zh.json |
Chinese translations of the crate summaries and item doc comments |
A document header declares title, summary, tags, and prerequisites.
The identifier and the layer are not declared: the identifier is the file
name and the layer is the directory, so the two cannot disagree with the
tree. A cross reference is written [[id]] or [[id|label]] and renders as
a link that the frontend routes without a page load.
Layout
apsis-book/
├── build.rs embed the frontend build and the text content
├── Cargo.toml one runtime dependency: apsis-rs
├── content/ authored documents, graphs, and generated catalog
│ └── concepts/<layer>/ documents, one directory per layer
├── frontend/ Svelte and TypeScript, built by Vite
│ ├── src/lib/ API client, router, components, formatting
│ ├── src/pages/ one component per route
│ └── dist/ build output, embedded by build.rs
├── scripts/extract_api.sh interface catalog generator
├── src/
│ ├── api/ the router and the JSON payloads
│ ├── docs/ content, Markdown, documents, interface catalog
│ ├── embedded/ access to the embedded frontend assets
│ ├── graph/ the knowledge graph and the flow graphs
│ ├── http/ request, response, and server
│ ├── tools/ the tool registry, the handlers, the result shape
│ ├── json.rs the JSON codec
│ ├── main.rs the process entry point
│ └── state.rs the parsed content, shared by every worker
└── tests/service.rs end to end tests against the built binary
Testing
cargo test # unit tests and end to end tests
cargo clippy --all-targets
cargo fmt --check
cd frontend && npm run check
The end to end tests start the built binary on an ephemeral port and make plain TCP requests against it.
License
GNU General Public License, version 3 or later, and that alone. See
LICENSE.
The service depends on apsis-rs, which is offered under MIT or
Apache-2.0. Both of those permit redistribution inside a GPL work, so the
two are distributed together under these terms. No other licence is
offered for this service.