diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..820af48 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,76 @@ +name: CI + +on: + push: + branches: [stage] + pull_request: + branches: [stage] + +env: + API_ENDPOINT_SERVER: "http://localhost:1443" + API_ENDPOINT_TRUSTAUTH_TO_SERVER: "https://localhost:1444" + API_ENDPOINT_TRUSTAUTH: "http://localhost:2443" + API_ENDPOINT_SERVER_TO_TRUSTAUTH: "https://localhost:2444" + +jobs: + frontend: + name: Frontend tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: dtolnay/rust-toolchain@stable + + - uses: Swatinem/rust-cache@v2 + + - name: Generate mTLS certificates + working-directory: mtls + run: bash mkcerts.sh dev + + - name: Build backend binaries + run: cargo build --bin rustsystem-server --bin rustsystem-trustauth + + - name: Start backend services + run: | + ./target/debug/rustsystem-server & + ./target/debug/rustsystem-trustauth & + + - name: Wait for services to be ready + run: | + until nc -z localhost 1443 2>/dev/null; do sleep 1; done + until nc -z localhost 2443 2>/dev/null; do sleep 1; done + + - uses: pnpm/action-setup@v4 + with: + version: latest + + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: pnpm + cache-dependency-path: frontend/pnpm-lock.yaml + + - name: Install dependencies + working-directory: frontend + run: pnpm install --frozen-lockfile + + - name: Run tests + working-directory: frontend + run: pnpm test + + backend: + name: Backend tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Generate mTLS certificates + working-directory: mtls + run: bash mkcerts.sh dev + + - uses: dtolnay/rust-toolchain@stable + + - uses: Swatinem/rust-cache@v2 + + - name: Run tests + run: cargo test diff --git a/CONTRIBUTE.md b/CONTRIBUTE.md new file mode 100644 index 0000000..5c77e41 --- /dev/null +++ b/CONTRIBUTE.md @@ -0,0 +1,204 @@ +# Contributing Guide + +This document explains how to contribute to Rustsystem: the branch structure, development workflow, and how releases are versioned and documented. + +--- + +## Branch Structure + +Development flows through two long-lived branches (`stage` and `main`) and short-lived feature branches: + +```mermaid +gitGraph + commit id: "main (stable)" + branch stage + checkout stage + commit id: "stage (testing)" + branch add-deletion-endpoint + checkout add-deletion-endpoint + commit id: "feature work" + commit id: "more work" + checkout stage + merge add-deletion-endpoint id: "PR: feature → stage" + commit id: "2.1.0-beta tag" + commit id: "bug fix" + commit id: "2.1.1-beta tag" + checkout main + merge stage id: "PR: stage → main (2.1.1 full release)" +``` + +| Branch | Purpose | +| -------------- | ------------------------------------------------------------------------------------------------ | +| `main` | Stable, production-ready code. Only updated when a beta is promoted to a full release. | +| `stage` | Integration and testing branch. Receives feature PRs; beta tags are created here. | +| `` | Short-lived branch named after the issue being worked on. Branched from and merged into `stage`. | + +### Workflow + +1. **Create a branch off `stage`** named after the issue or feature (e.g. `add-deletion-endpoint`, `fix-sse-reconnect`). +2. **Develop and commit** on your feature branch. +3. **Open a PR from `` → `stage`** when the work is ready for testing. +4. **Test on `stage`**. Beta tags (`x.y.z-beta`) are created here. Fix bugs on `stage` directly or via follow-up PRs. +5. **Open a PR from `stage` → `main`** once a beta passes testing. `main` only ever receives full releases — beta tags remain on `stage`. + +```mermaid +flowchart LR + A["<issue-name>\n(feature branch)"] -->|PR| B["stage\n(testing, beta tags)"] + B -->|bugs found| B + B -->|beta passes PR| C["main\n(full releases only)"] +``` + +--- + +## Versioning + +Versions follow **MAJOR.MINOR.PATCH**, optionally suffixed with `-beta` for pre-releases. + +MAJOR and MINOR versions are **milestone-based**: each number corresponds to a defined project milestone. PATCH versions are used for incremental development updates made while working towards the next milestone. + +| Component | When to increment | +| --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **MAJOR** | Reaching a top-level milestone that represents a new generation of the project — e.g. a complete rewrite, a fundamental protocol change, or a shift in overall project direction so significant that it warrants a new major milestone series. | +| **MINOR** | Reaching a planned minor milestone — a defined set of features, goals, or deliverables that has been completed. What constitutes a milestone should be agreed upon before development begins (tracked on github milestones). | +| **PATCH** | Development updates made while working towards the next milestone — bug fixes, partial feature progress, documentation changes, small visual tweaks, or any change that does not yet complete a milestone. | + +### Beta Pre-releases + +Every release that increments **MAJOR or MINOR must first be released as a `-beta` pre-release** and pass testing before it is promoted to a full release. + +The promotion process works as follows: + +```mermaid +flowchart LR + A["x.y.0-beta\n(pre-release)"] --> B{Testing} + B -- bugs found --> C["x.y.1-beta\n(fix)"] + C --> D{Testing} + D -- bugs found --> E["x.y.2-beta\n(fix) ..."] + D -- passed --> F["x.y.1\n(full release)"] + E --> G{Testing} + G -- passed --> H["x.y.2\n(full release)"] +``` + +**The full release tag is placed at the same commit as the accepted pre-release on `stage`, and that commit is then merged into `main` via PR.** No code changes are made between the last `-beta` tag and the full release tag. Beta tags only ever exist on `stage` — `main` only receives full releases. + +> **Example:** `10.11.0-beta` is released on `stage`. Bugs are found and fixed in `10.11.1-beta`, `10.11.2-beta`, `10.11.3-beta`. Once `10.11.3-beta` passes testing, it is tagged `10.11.3` on `stage` and merged into `main`. + +PATCH releases do not require a `-beta` pre-release and may be released directly as a full version. + +--- + +## Release Notes + +All release notes **must** follow this structure. + +### 1. Title + +``` +# Release Notes — v +``` + +### 2. Preamble + +A short block (blockquote) immediately below the title containing: + +- **Date** of release. +- **Built upon** — which version this release is built on top of. +- **Status** — either `pre-release` (with a note on what remains before promotion) or `stable`. + +Example: + +```markdown +> Release date: 2026-04-01 +> Built on top of v2.0.3-beta. +> **Status: stable.** +``` + +```markdown +> Pre-release date: 2026-04-01 +> Built on top of v2.0.0-beta. +> **Status: pre-release — requires stress testing before production use.** +``` + +--- + +### 3. Overview + +A prose summary of the release followed by a **bullet point list** of every problem addressed or change made. The summary should communicate: + +- How large the release is (patch-level tweak, significant feature addition, major overhaul, etc.). +- The motivation — what prompted the changes. + +The bullet list gives a reader a complete picture at a glance before they read the detail sections. + +Example: + +```markdown +## Overview + +v2.1.0-beta is a significant feature release ... + +- New meeting deletion endpoint +- Vote state endpoints added +- Lock ordering corrected in README +``` + +--- + +### 4. Changes + +One subsection per item from the overview bullet list. Each subsection: + +- Explains the change in depth — what was changed, why, and what impact it has. +- Uses **tables** to compare before/after states, list endpoints, or summarise options where that aids clarity. +- Uses **Mermaid diagrams** to illustrate flows, architecture, or sequences where a diagram communicates more than prose. +- Is separated from the next section by a horizontal rule (`---`). + +Example structure: + +```markdown +## Changes + +### New Meeting Deletion Endpoint + +...explanation... + +--- + +### Vote State Endpoints + +...explanation, possibly with a table of endpoints... + +--- + +### README: Lock Ordering Correction + +...explanation... +``` + +#### When to use tables + +Use a table when comparing multiple items across consistent attributes — e.g. a list of endpoints and their roles, a before/after comparison, or a summary of test coverage areas. + +#### When to use Mermaid diagrams + +Use a Mermaid diagram when the relationship between steps, services, or states is easier to understand visually than as prose. Preferred diagram types: + +| Situation | Diagram type | +| -------------------------------- | ----------------- | +| Multi-step flows or processes | `flowchart` | +| Service-to-service communication | `sequenceDiagram` | +| State machines | `stateDiagram-v2` | +| Version/branching timelines | `gitGraph` | + +--- + +## Checklist Before Publishing a Release + +- [ ] Version number follows MAJOR.MINOR.PATCH rules above (MAJOR/MINOR = milestone reached, PATCH = development update). +- [ ] If MINOR or MAJOR changed, this is a `-beta` pre-release. +- [ ] The PR from `stage` → `main` has been reviewed and testing passed. +- [ ] Preamble includes date, base version, and status. +- [ ] Overview contains a prose summary and a bullet list of all changes. +- [ ] Each bullet point has a corresponding Changes subsection. +- [ ] Tables or Mermaid diagrams are used where they add clarity. +- [ ] Each Changes subsection is separated by a horizontal rule. diff --git a/Cargo.lock b/Cargo.lock index 0939cd5..88ba989 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,6 +39,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "android_system_properties" version = "0.1.5" @@ -636,6 +642,20 @@ dependencies = [ "syn", ] +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + [[package]] name = "data-url" version = "0.3.2" @@ -867,6 +887,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "fontconfig-parser" version = "0.5.8" @@ -899,6 +925,16 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "forwarded-header-value" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835f84f38484cc86f110a805655697908257fb9a7af005234060891557198e9" +dependencies = [ + "nonempty", + "thiserror 1.0.69", +] + [[package]] name = "fs-err" version = "3.3.0" @@ -992,6 +1028,12 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" +[[package]] +name = "futures-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" + [[package]] name = "futures-util" version = "0.3.32" @@ -1098,6 +1140,29 @@ dependencies = [ "weezl", ] +[[package]] +name = "governor" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9efcab3c1958580ff1f25a2a41be1668f7603d849bb63af523b208a3cc1223b8" +dependencies = [ + "cfg-if", + "dashmap", + "futures-sink", + "futures-timer", + "futures-util", + "getrandom 0.3.4", + "hashbrown 0.16.1", + "nonzero_ext", + "parking_lot", + "portable-atomic", + "quanta", + "rand 0.9.2", + "smallvec", + "spinning_top", + "web-time", +] + [[package]] name = "group" version = "0.13.0" @@ -1148,13 +1213,19 @@ dependencies = [ "ahash", ] +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + [[package]] name = "hashbrown" version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ - "foldhash", + "foldhash 0.1.5", ] [[package]] @@ -1162,6 +1233,11 @@ name = "hashbrown" version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", +] [[package]] name = "heck" @@ -1283,6 +1359,19 @@ dependencies = [ "tower-service", ] +[[package]] +name = "hyper-timeout" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" +dependencies = [ + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + [[package]] name = "hyper-util" version = "0.1.20" @@ -1764,6 +1853,18 @@ dependencies = [ "pxfm", ] +[[package]] +name = "nonempty" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9e591e719385e6ebaeb5ce5d3887f7d5676fceca6411d1925ccc95745f3d6f7" + +[[package]] +name = "nonzero_ext" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21" + [[package]] name = "nu-ansi-term" version = "0.50.3" @@ -1879,6 +1980,26 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" +[[package]] +name = "pin-project" +version = "1.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "pin-project-lite" version = "0.2.16" @@ -1925,6 +2046,12 @@ dependencies = [ "universal-hash", ] +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + [[package]] name = "potential_utf" version = "0.1.4" @@ -2011,6 +2138,21 @@ dependencies = [ "image 0.25.9", ] +[[package]] +name = "quanta" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3ab5a9d756f0d97bdc89019bd2e4ea098cf9cde50ee7564dde6b81ccc8f06c7" +dependencies = [ + "crossbeam-utils", + "libc", + "once_cell", + "raw-cpuid", + "wasi", + "web-sys", + "winapi", +] + [[package]] name = "quick-error" version = "2.0.1" @@ -2153,6 +2295,15 @@ dependencies = [ "getrandom 0.3.4", ] +[[package]] +name = "raw-cpuid" +version = "11.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" +dependencies = [ + "bitflags 2.11.0", +] + [[package]] name = "rayon" version = "1.11.0" @@ -2459,6 +2610,7 @@ dependencies = [ "tokio-stream", "tower", "tower-http", + "tower_governor", "tracing", "tracing-appender", "tracing-subscriber", @@ -2490,6 +2642,7 @@ dependencies = [ "tokio", "tower", "tower-http", + "tower_governor", "tracing", "tracing-subscriber", "uuid", @@ -2764,6 +2917,15 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "spinning_top" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96d2d1d716fb500937168cc09353ffdc7a012be8475ac7308e1bdf0e3923300" +dependencies = [ + "lock_api", +] + [[package]] name = "spki" version = "0.7.3" @@ -3068,6 +3230,35 @@ dependencies = [ "tokio", ] +[[package]] +name = "tonic" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fec7c61a0695dc1887c1b53952990f3ad2e3a31453e1f49f10e75424943a93ec" +dependencies = [ + "async-trait", + "axum", + "base64", + "bytes", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-timeout", + "hyper-util", + "percent-encoding", + "pin-project", + "socket2", + "sync_wrapper", + "tokio", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "tower" version = "0.5.3" @@ -3076,9 +3267,12 @@ checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" dependencies = [ "futures-core", "futures-util", + "indexmap", "pin-project-lite", + "slab", "sync_wrapper", "tokio", + "tokio-util", "tower-layer", "tower-service", "tracing", @@ -3124,6 +3318,23 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" +[[package]] +name = "tower_governor" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44de9b94d849d3c46e06a883d72d408c2de6403367b39df2b1c9d9e7b6736fe6" +dependencies = [ + "axum", + "forwarded-header-value", + "governor", + "http", + "pin-project", + "thiserror 2.0.18", + "tonic", + "tower", + "tracing", +] + [[package]] name = "tracing" version = "0.1.44" @@ -3529,6 +3740,22 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + [[package]] name = "winapi-util" version = "0.1.11" @@ -3538,6 +3765,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-core" version = "0.62.2" diff --git a/TODO.md b/TODO.md deleted file mode 100644 index 6cc021f..0000000 --- a/TODO.md +++ /dev/null @@ -1,22 +0,0 @@ -## Needs to be done by November 16th - -### Server Side (and wasm) - -- [x] member list -- [x] delete members -- [ ] Fix warnings... -- [ ] Doc tests for proof -- [x] Fix UUID -- [x] Host Universal Auth Token -- [x] Meeting agenda update as we go - -### Client Side - -- [x] Host/Voter Page -- [x] Start Page -- [x] Invite Voter/Host -- [x] Agenda Viewer -- [ ] Click to enlarge qr code -- [ ] Allow voters to refresh page during voting -- [ ] Format links and nested lists in agenda viewer -- [ ] Tabs in textarea diff --git a/frontend/src/components/Navbar/Navbar.tsx b/frontend/src/components/Navbar/Navbar.tsx index 7dc4ffa..fae5192 100644 --- a/frontend/src/components/Navbar/Navbar.tsx +++ b/frontend/src/components/Navbar/Navbar.tsx @@ -25,7 +25,7 @@ export function Navbar() { Rustsystem -
+
{NAV_LINKS.map(({ to, label }) => (
- - Waiting for {result.voterName} to scan the QR code… - - {/* QR code — white background ensures readability regardless of theme */}
Promise; onRemoveAll: () => Promise; onReload: () => void; @@ -369,7 +369,7 @@ function VoterListPanel({ )} - {!v.is_host && ( + {v.uuid !== selfUuuid && (