A template for rust project consisting of a website backend and update daemon, connected through a PostgreSQL database. Suitable for building services like Repology.
- Async tokio runtime.
- Configuration.
- Logging support.
- tracing based logging.
- Support for logging to files.
- Local timezone for timestamps.
- Daily rotation (though it's not affected by the timezone yet).
- Target (module path) removed from the log due to uselessness.
- Support for shipping logs to Grafana Loki.
- Metrics collection.
- metrics support with Prometheus export.
- Out of box metrics-process collection.
- Out of box tokio metrics collection, including unstable metrics.
- PostgreSQL database support through sqlx.
- indoc is used for nicely formatted multiline queries in the code.
sqlx::query!macros are not used (yet?), so no database connection is required for compilation.- Migrations support. Migration are available from the common module, and can be used from both the daemon, the webapp, and tests.
- All applications set PostgreSQL
application_name.
- Testing.
- Intended to use with cargo llvm-cov.
- Unit tests are excluded from coverage for more correct coverage metrics (
#![feature(coverage_attribute)]). - A hack is used to support coverage for functions instrumented with
tracing::instrument.
- Continuous integration.
- GitHub actions pipeline running build (both at workspace level, and for individual crates through cargo hack) and all kinds of tests, for pushes, PRs and weekly (to avoid bitrot).
- CI uses fresh version of PostgreSQL.
- CI uses pinned nightly rust to avoid unwanted breakages and use cache more effectively.
- Swatinem/rust-cache is used for more effective rust caching. Additionally,
clippyis set up to use different target directory than normal build to avoid cache invalidation. - Dependabot config for keeping dependencies up to date, with 14 day cooldown for major and minor updates.
- No much more that what's listed in the section above.
- A place to add the update logic right away.
- axum based web service.
- askama based templates.
- Template inheritance is used, so all common HTML code resides in the single
_base.htmlfile. - Minimal custom CSS framework is included (responsive, supporting automatic light/dark themes, basic page elements and styling).
- Template inheritance is used, so all common HTML code resides in the single
- Advanced static files handling.
- Files from the
staticdirectory are automatically compiled into binary. - Endpoint for serving these is included (
/static/<filename>). - A file may be accessed by a hashed name (e.g.
<filename>.<hash>.<ext>), which allows infinite caching without invalidation issues. Corresponding headers are set out of box. - Files are compressed on startup, and compressed content is served for clients which accept it.
- Files from the
- Statically enumerated endpoint registry with template helpers.
- Each endpoint is assigned an unique
enumvalue. - When constructing internal links, endpoints are referred by such enum value, which makes broken internal links impossible, and simplifies moving endpoints around.
- Endpoints can also be grouped into sections for use in website navigation.
- Each endpoint is assigned an unique
- Includes middleware for tracking response codes, sizes, and latency for each route.
- Includes middleware adding basic security HTTP headers, tunable from route properties.
- Extensive integration tests support.
- Concise HTTP endpoint tests with axum-test.
- Migrations and fixtures support from sqlx.
- Webapp may be constructed as an object and used in the tests.
- HTML validation in tests with tidier.
- insta based snapshot tests in addition to integration tests.
- (Note:
profile.dev.package.insta.opt-level = 3recommended setting is deliberately omitted, as it doesn't seem to provide any gain). - Filters feature of
instais used to prevent static file hash changes from invalidating snapshots.
- (Note:
- automod used to simplify adding new tests
This code requires latest Rust-nightly.
-
You can rename the project (fixing all occurrences of placeholder name in paths and the code) by calling
./rename.sh <target name> -
Prepare the database
(note that you likely want stronger password for production usage)
sudo -u postgres psql -c "CREATE DATABASE ruwete" sudo -u postgres psql -c "CREATE USER ruwete WITH PASSWORD 'ruwete'" sudo -u postgres psql -c "GRANT ALL ON DATABASE ruwete TO ruwete" -
Run the daemon
(note that you DSN may vary depending on postgresql settings)
cargo run --bin ruwete-daemon -- --dsn postgresql://ruwete:ruwete@localhost/ruwete -
Run the webapp
cargo run --bin ruwete-web -- --dsn postgresql://ruwete:ruwete@localhost/ruwete --listen 127.0.0.1:3000
- MIT OR Apache-2.0