ZOYKUHot Board

alexichepura/lapaTài liệu kỹ thuật

Leptos Axum starter with Admin dashboard and SSR/SPA website

Thông báo ngôn ngữ:Chưa có tài liệu tiếng Việt cho kho lưu trữ này. Đang hiển thị bản gốc tiếng Anh chính thức.
Cài đặt nhanh
cargo add lapa
Lapa Logo

Lapa - Leptos Axum starter with Admin dashboard and SSR/SPA website

Status

This project is under active development.
Recently migrated from Prisma ORM to SQL first approach with Cornucopia.
Also added rich text editing with leptoslate, a Slate-compatible editor written in Rust for Leptos, so the whole stack stays Rust and the stored Slate JSON keeps working.\


Intro: https://youtu.be/6eMWAI1D-XA
Demo site: https://lapa.chepura.space


Screenshots below are outdated and to be updated.

Site Home Site Home Admin Dashboard Admin Posts Admin Post Admin Post Admin Settings Admin Mobile

Motivation

I want to have practical full-stack setup to build websites and services.
Utilising type safety and performance of Rust opens the door for new era of web dev, that is taking off.
Ecosystem and standardized approach is helpful to develop scalable and future-proof apps.
Some benefits:

  • strict types
  • enforced error and value management (Result and Option)
  • predictable performance (no garbage collector)
  • native performance
  • single bundler (cargo)
  • straight path to WebAssembly

Leptos

https://leptos.dev
leptos-rs/leptos
A cutting-edge, high-performance frontend framework SSR+SPA. Using reactive signals.

Axum

tokio-rs/axum
Backend framework built with Tokio, Tower, and Hyper. Focuses on ergonomics and modularity.

Features

  • project
    • SEO site
    • admin dashboard
    • CLI with clap: migrate, settings-init, user-add, user-disable, user-enable, user-set-role
    • ops scripts: build/deploy, systemd units, ferron config, migrations, backups
  • site
    • SSR + SPA hydrated
    • open graph meta tags
  • prod features
  • admin auth and session with
  • rich text content editor integration with alexichepura/leptoslate
  • post
    • CRUDL
    • publish_at
  • product
    • CRUDL
    • publish_at
    • product images
      • preview and upload
      • resize and convert on backend
      • order
      • is_hero flag
      • delete and alt update in "dialog"
  • settings
    • robots.txt, site_url
    • home_text
  • css
    • based on https://open-props.style
    • dark and light themes
    • mobile first
    • CSS modules with stylance, bundled by Lightning CSS (style-bundle) in cargo-leptos, see notes on css below
  • components
    • forms, inputs and response messages
    • input datetime-local usage with chrono library
    • RoutingProgress
    • Favicons

Run

Generate db client

https://cornucopia-rs.github.io/cornucopia/introduction/installation.html
Note the need of docker or podman.

cargo install cornucopia
cornucopia schema ./schema.sql

Build rich text editor

The editor is a Rust dependency (leptoslate), so there is no separate JavaScript build step.

Local DB

Simplified with postgres user.

psql postgres
# create database lapa;
# .env
PG__URL="postgresql://postgres:postgres@localhost:5432/lapa"

Environment

Runtime environment variables (all optional):

  • PG__URL — Postgres connection string (see above).
  • PG_TLS — set to 1/true to connect to Postgres over TLS (rustls with Mozilla roots). Leave unset for a local socket or trusted-network plaintext connection.
  • SESSION_SECURE — force the admin session cookie Secure flag (1/0). Defaults to on for release builds, off for debug.
  • AUTH_RATELIMIT_BURST / AUTH_RATELIMIT_PERIOD_SECS — admin /auth/* rate limit (defaults: burst 10, then one token per second per client).
  • IMAGE_UPLOAD_PATH / IMAGE_CONVERT_PATH — image storage roots.

Migrations

The schema is managed with refinery migrations in db/migrations/, embedded in the cli binary. db/migrations/V1__init.sql mirrors schema.sql (kept for Cornucopia codegen, see above).

cargo cli migrate

Add a migration by creating the next file (e.g. db/migrations/V2__add_thing.sql) and running cargo cli migrate again.

Init

RUST_LOG="debug" cargo cli settings-init
RUST_LOG="debug" cargo cli user-add <username> <password>

User management

Users carry a nullable deactivated_at timestamp (added in migration V2__user_deactivated_at.sql): NULL means active, a timestamp records when the account was disabled. A disabled account cannot log in, and any session it already holds is rejected on the next request (the axum-login backend returns no user for a disabled account), so disabling takes effect immediately without deleting the account.

cargo cli user-disable <username>   # revoke access (also purges their sessions)
cargo cli user-enable <username>    # restore access

Both commands exit non-zero when no such user exists.

Users also have a single role (migration V4__user_role.sql):

role can
admin everything
editor product/post/category/content CRUD, but not site settings
viewer read-only (dashboard and lists)

The same permissions are enforced by the server functions; the admin UI hides nav/buttons and renders a "Forbidden" page for gated routes. Set a role with:

cargo cli user-set-role <username> admin|editor|viewer

Dev

cargo install stylance-cli   # one-time
stylance --watch .           # site CSS modules (run in a second terminal)
stylance --watch admin       # admin CSS modules (run in a second terminal)
RUST_LOG="info,admin=debug" cargo leptos watch -p admin
RUST_LOG="info,site=debug" cargo leptos watch -p site

Tip to enable tower_http debug

RUST_LOG="info,tower_http=debug,admin=debug" cargo leptos watch -p admin
RUST_LOG="info,tower_http=debug,site=debug" cargo leptos watch -p site

Prod

See the tutorial/demo video. https://www.youtube.com/watch?v=KLg8Hcd3K_U

cargo leptos build --release

Production with compress and ratelimit

cargo leptos build --release --features="compression,ratelimit"
# or
cargo leptos build --release --features="prod"

Ops

The ops/ folder contains example scripts to build, deploy and operate the server. Copy .env.example to .env and adjust the host, paths and domains. Requires https://github.com/ryanfowler/precompress, stylance and cargo-leptos.

Server layout assumed by the scripts and systemd units:

  • /home/lapa-server — SERVER_DIR, holds .env
  • /home/lapa-server/{web,admin}/public — LEPTOS_SITE_ROOT
  • /home/lapa-server/image_upload, /home/lapa-server/image_convert
  • /usr/bin/lapa_web, /usr/bin/lapa_admin — binaries
  • services lapa-web (8110) and lapa-admin (8111), fronted by ferron 3

One-time setup: Postgres running, /home/lapa-server/.env with PG__URL (read by dotenvy; the units set WorkingDirectory), service users with access to SERVER_DIR and write access to the image dirs, then ./ops/services-deploy.sh and ./ops/ferron-deploy.sh.

./ops/build-upload.sh web    # build + upload site, restart lapa-web
./ops/build-upload.sh admin  # build + upload admin, restart lapa-admin
./ops/build-upload.sh cli    # upload the CLI binary
./ops/cli-migrate.sh         # run migrations on the server
./ops/restart.sh web|admin
./ops/services-deploy.sh     # install/restart the systemd units
./ops/ferron-deploy.sh       # validate + install ferron.conf and reload

Backups (Postgres + image_upload/, 30-day retention by default):

./ops/backup.sh                  # DB + originals into ./backups
./ops/backup.sh --with-derived   # also image_convert/
./ops/backup-upload.sh backups/lapa-<timestamp>.dump  # restore (destructive)

E2E tests

Rust-native end-to-end harness (playwright-rs + testcontainers) that boots an ephemeral Postgres, applies schema.sql, seeds fixtures, starts the site and drives Chromium. See e2e/README.md.

cargo install cargo-leptos
cargo run -p lapa-e2e -- install          # download Chromium (one-time)
cargo run -p lapa-e2e -- test             # public site suite
cargo run -p lapa-e2e -- test --app admin  # admin suite (auth + CRUD)
cargo run -p lapa-e2e -- test --app integration  # cross-app: admin write -> site read
cargo run -p lapa-e2e -- test --filter home
cargo run -p lapa-e2e -- update           # regenerate screenshot baselines
cargo run -p lapa-e2e -- serve            # keep the stack up for manual checks
cargo run -p lapa-e2e -- test --features prod  # build with compression + ratelimit

The suites also run in CI (.github/workflows/e2e.yml); CI sets LAPA_E2E_SKIP_SNAPSHOTS=1 since only macOS baselines are committed.

Notes on CSS

Styling is plain CSS organised as CSS modules with stylance plus Lightning CSS for bundling (cargo-leptos style-bundle = true).

  • Component-scoped rules live next to the components as *.module.css (e.g. src/product_list.module.css). Add the stylance::import_crate_style!(style, "src/product_list.module.css"); import and reference style::ProductList; the macro resolves the same deterministic hash the CLI writes into the bundle.
  • Shared/global element rules (e.g. body, dialog, @container) live in style/main.css, which also @imports the vendor stylesheets (open-props, normalize, buttons) and the generated style/stylance.css.
  • Regenerate the bundle with stylance . (site) / stylance admin (admin), or keep stylance --watch <crate> running while editing. The generated style/stylance.css is committed so a plain cargo leptos build works without the CLI; ops/build-upload.sh and the e2e harness refresh it for you.

Native CSS nesting, @media, @container and @layer are all supported. The browserquery in both crates targets browsers with native nesting support (Chrome/Edge 120+, Firefox 117+, Safari/iOS 17.2+, Opera 106+, Samsung 25+), so Lightning CSS keeps the nested source in the bundle instead of flattening it. Adding an older target there re-enables downleveling for it.

@custom-media is not available: cargo-leptos always parses with ParserOptions::default() and gives no way to set Lightning CSS' ParserFlags::CUSTOM_MEDIA.

Earlier experiments: Sass PR #24, Lightning CSS bundle with CLI proof of concept #23.

License

This project is licensed under the terms of the MIT license.

Bản sao tài liệu lưu trữ từ kho lưu trữ GitHub chính thức · Xem trên GitHub
Ngày đồng bộ: 2026-11-10