No description
  • Rust 96.4%
  • Shell 2.8%
  • HTML 0.8%
Find a file
faicel 6179bef607
All checks were successful
Test backend and create tag on main / test_and_tag (push) Successful in 3m48s
Create Forgejo release for TypeScript client / release_ts_client (push) Successful in 2m31s
init
2026-03-06 22:28:48 +01:00
.cargo init 2026-03-06 22:28:48 +01:00
.forgejo/workflows init 2026-03-06 22:28:48 +01:00
build init 2026-03-06 22:28:48 +01:00
scripts init 2026-03-06 22:28:48 +01:00
src init 2026-03-06 22:28:48 +01:00
static init 2026-03-06 22:28:48 +01:00
tests init 2026-03-06 22:28:48 +01:00
.gitignore init 2026-03-06 22:28:48 +01:00
Cargo.lock init 2026-03-06 22:28:48 +01:00
Cargo.toml init 2026-03-06 22:28:48 +01:00
config.example.json init 2026-03-06 22:28:48 +01:00
config_test.json init 2026-03-06 22:28:48 +01:00
Cross.toml init 2026-03-06 22:28:48 +01:00
LICENSE init 2026-03-06 22:28:48 +01:00
README.md init 2026-03-06 22:28:48 +01:00

Home IoT Backend (rest_api_server)

This crate is the HTTP API backend for the Home IoT project. It exposes REST endpoints for:

  • User management (registration, update, deletion, authentication),
  • Sensor management (listing, pagination, filtering),
  • Authentication using JWT tokens stored in HTTP-only cookies,
  • OpenAPI documentation and Swagger UI,
  • TypeScript client generation scripts based on the OpenAPI spec.

The backend relies on a separate crate, db_handler, for all database access and data models.


1. Architecture overview

  • Crates

    • rest_api_server (this crate): HTTP API, routing, auth, OpenAPI.
    • db_handler: primary/history databases, repositories, models, shared error types.
  • Main modules

    • src/lib.rs
      • run_server(...): builds the Axum router, mounts routes, configures CORS, compression, Swagger UI.
      • generate_openapi(): combines OpenAPI fragments from user, sensor, and auth modules.
    • src/main.rs
      • Binary entry point (rest_api_server),
      • Loads configuration from CONFIG_FILE,
      • Initializes database connections via db_handler::db::connection::Connection,
      • Starts the HTTP server on 0.0.0.0:2606.
    • src/auth/*
      • JWT token generation & verification (handlers::token),
      • Login / logout / refresh handlers, cookie management,
      • Swagger/OpenAPI security scheme declaration (cookie-based auth).
    • src/user/*, src/sensor/*
      • REST handlers over db_handler services,
      • Pagination, validation, error handling.
    • src/tools/*
      • error.rs: centralized application error type (AppError),
      • state.rs: shared AppState (database connection + optional restart channel),
      • config.rs, responses.rs: configuration and response helper types.
    • src/bin/generate-openapi.rs
      • Standalone binary to export the OpenAPI spec into a JSON file.

2. Requirements

  • Rust toolchain: Rust stable with Cargo.

  • Databases

    • Primary database: SQLite (handled by db_handler with sqlx).
    • Time-series / history: InfluxDB 2.x (also configured in db_handler).
  • Configuration file

    • A JSON configuration file is required; its path is provided via the CONFIG_FILE environment variable (see below).
  • Registries

    • The db_handler crate is fetched from a custom Cargo registry named forgejo, configured in .cargo/config.toml:

      [registries.forgejo]
      index = "sparse+https://code.bhk-itsolutions.com/api/packages/homeiot/cargo/"
      

      You must ensure that this registry is reachable from your environment (it is public for this project).


3. Configuration

3.1 Application configuration file (CONFIG_FILE)

The backend expects the CONFIG_FILE environment variable to point to a JSON file. A typical pattern:

export CONFIG_FILE=/path/to/config.json

The JSON is deserialized into tools::config::Config. At minimum it should contain:

  • Primary database URL (e.g. SQLite path or in-memory DB),
  • Static assets folder used to serve the frontend (used by run_server),
  • Any additional fields required by db_handler (for example, history DB URL).

Recommendation: add a config.example.json (not provided here by default) with:

  • a non-production SQLite URL (e.g. ":memory:"),
  • a placeholder path for static_folder,
  • dummy values for any additional fields.

3.2 Secret key (SECRET_KEY)

JWT tokens use a shared secret stored in the SECRET_KEY environment variable:

export SECRET_KEY="CHANGE_ME_TO_A_32_CHARS_MINIMUM_SECRET_KEY"

Constraints:

  • SECRET_KEY must be set,
  • It must be at least 32 characters long,
  • It is used for both token encoding and decoding (HS256).

4. Running the server

  1. Ensure db_handler is available and the databases are reachable:
    • Primary SQLite DB (and migrations will be applied by db_handler),
    • InfluxDB 2.x if you use history features.
  2. Prepare your configuration file and export CONFIG_FILE and SECRET_KEY:
export CONFIG_FILE=/path/to/config.json
export SECRET_KEY="CHANGE_ME_TO_A_32_CHARS_MINIMUM_SECRET_KEY"
  1. Run the server:
cargo run --bin rest_api_server

The server will bind to 0.0.0.0:2606 and log basic information via tracing.

Alternatively, you can use the helper script:

./scripts/run.sh

Note: adjust scripts/run.sh to export the correct CONFIG_FILE and SECRET_KEY values for your environment.


5. OpenAPI & Swagger UI

5.1 Generating OpenAPI JSON

Use the dedicated binary:

cargo run --bin generate-openapi -- openapi.json

or the convenience script (using the running server):

./scripts/download-openapi.sh

This will:

  • Build the OpenAPI document from the code (binary), or download it from the running server (/api-doc/openapi.json),
  • Write it to scripts/openapi.json (or another path, depending on the script).

5.2 Swagger UI in the running server

When the server is running, Swagger UI is served at:

  • GET /docs Swagger UI frontend,
  • GET /api-doc/openapi.json OpenAPI JSON served by the backend.

Authentication is configured via the CookieAuth security scheme using HTTP-only cookies (auth_token).


6. TypeScript client generation

The scripts/ directory contains utilities to generate a TypeScript client from the OpenAPI spec:

  • scripts/openapi.json a checked-in OpenAPI JSON snapshot (can be regenerated).
  • scripts/generate-ts-client-from-file.sh generates a TS client from a local OpenAPI JSON file.

Typical flow:

cd scripts

# Option 1: generate OpenAPI from code (binary) and copy it here
cargo run --bin generate-openapi -- openapi.json

# Option 2: or download from a running server
./download-openapi.sh http://localhost:2606/api-doc/openapi.json openapi.json

# Then generate the TS client
./generate-ts-client-from-file.sh openapi.json ../frontend/src/api

The TS client will be generated in the path configured inside the script (for example, the frontend project).

For more details about using the generated client in Vue 3, see the documentation referenced in scripts/README.md.


7. Scripts overview

From the backend/ directory:

  • scripts/tests.sh

    • Exports CONFIG_FILE (pointing to a test configuration file),
    • Exports a SECRET_KEY value (should be dummy for public use),
    • Runs cargo test -- --test-threads=1 --nocapture.
  • scripts/run.sh

    • Helper to run the API server (you can customize environment variables here).
  • scripts/build.sh

    • Helper to build the API server in release mode.
  • scripts/deploy.sh

    • Placeholder for your deployment logic (currently minimal; adapt to your environment).
  • scripts/download-openapi.sh

    • Downloads OpenAPI JSON from a running backend and saves it under scripts/openapi.json.
  • scripts/generate-ts-client-from-file.sh

    • Consumes a local OpenAPI JSON file and generates a TypeScript client.

Security note: do not commit real secrets (keys, passwords, tokens) into these scripts. Use dummy values or rely on CI/CD environment variables.


8. Tests

8.1 Rust tests

The project includes:

  • Unit tests for handlers in src/**/tests.rs,
  • Integration tests in tests/general/*.rs and tests/users/*.rs.

To run all tests from the backend directory:

./scripts/tests.sh

This script:

  • Exports a CONFIG_FILE pointing to a test configuration file,
  • Ensures SECRET_KEY is set for JWT tests,
  • Runs cargo test with a single test thread and --nocapture to show output.

8.2 Integration tests behaviour

Integration tests:

  • Start an Axum HTTP server on a random local port,
  • Use reqwest clients (with or without cookie jar) to:
    • Register a user,
    • Log in and verify HTTP-only cookies,
    • Call authenticated routes (e.g. updating and reading users),
    • Verify error codes (401, 404, 400, etc.).

9. Error handling

  • All API-level errors use the AppError type (src/tools/error.rs), which:
    • Logs errors via tracing,
    • Maps them to appropriate HTTP status codes,
    • Returns generic or user-friendly messages to clients.
  • Database-layer errors are encapsulated in db_handler (e.g. CommonError), and then converted into AppError as needed.

10. Notes on publishing

Before publishing this crate in a public Git repository, you should:

  1. Ensure there are no real secrets:
    • No real SECRET_KEY in scripts/tests.sh or any config file,
    • No .env, config.json, or other sensitive files tracked by Git.
  2. Decide how to distribute db_handler:
    • Either include it in the same repository,
    • Or publish it separately and reference it via git URL or crates.io (if you ever choose to).
  3. Optionally add:
    • config.example.json,
    • .env.example,
    • A README.DOCKER.md if you add Docker-based workflows.