update readme
This commit is contained in:
@@ -1,101 +1,59 @@
|
||||
<picture>
|
||||
<source srcset="https://raw.githubusercontent.com/leptos-rs/leptos/main/docs/logos/Leptos_logo_Solid_White.svg" media="(prefers-color-scheme: dark)">
|
||||
<img src="https://raw.githubusercontent.com/leptos-rs/leptos/main/docs/logos/Leptos_logo_RGB.svg" alt="Leptos Logo">
|
||||
</picture>
|
||||
# Code Submission
|
||||
|
||||
# Leptos Axum Starter Template
|
||||
This project is a Leptos + Axum app for a multi-step code submission challenge. It exposes a small web UI where users enter a code, accepts challenge submissions for valid codes, and can toggle the server between offline and live mode for SMS-driven challenge flow testing.
|
||||
|
||||
This is a template for use with the [Leptos](https://github.com/leptos-rs/leptos) web framework and the [cargo-leptos](https://github.com/akesson/cargo-leptos) tool using [Axum](https://github.com/tokio-rs/axum).
|
||||
## Overview
|
||||
|
||||
## Creating your template repo
|
||||
The app supports:
|
||||
|
||||
If you don't have `cargo-leptos` installed you can install it with
|
||||
- A home page for entering challenge codes.
|
||||
- A `/submit/:code` route that validates supported codes.
|
||||
- A `/manage` page to toggle the server between live and offline states.
|
||||
- A `/history` page for successful submission records.
|
||||
- A protected `POST /event/buzz` endpoint for challenge code 1, which validates a secret and timing window.
|
||||
- TextBee SMS notifications when the server is live.
|
||||
|
||||
```bash
|
||||
cargo install cargo-leptos --locked
|
||||
This repository is not a generic template anymore; it is a working challenge server with specific code-handling behavior and operational configuration.
|
||||
|
||||
## Project structure
|
||||
|
||||
```text
|
||||
.
|
||||
├── Cargo.toml
|
||||
├── README.md
|
||||
├── dev.env
|
||||
├── LICENSE
|
||||
├── end2end/
|
||||
│ ├── package.json
|
||||
│ ├── playwright.config.ts
|
||||
│ └── tests/
|
||||
├── public/
|
||||
│ ├── challenge3.txt
|
||||
│ ├── challenge3_annotated.txt
|
||||
│ └── favicon.ico
|
||||
├── src/
|
||||
│ ├── app.rs
|
||||
│ ├── lib.rs
|
||||
│ ├── main.rs
|
||||
│ ├── manage.rs
|
||||
│ ├── secrets.rs
|
||||
│ └── codes/
|
||||
│ ├── code1.rs
|
||||
│ ├── code2.rs
|
||||
│ ├── code5.rs
|
||||
│ ├── history.rs
|
||||
│ └── mod.rs
|
||||
├── style/
|
||||
│ └── main.scss
|
||||
└── target/
|
||||
```
|
||||
|
||||
Then run
|
||||
```bash
|
||||
cargo leptos new --git https://github.com/leptos-rs/start-axum
|
||||
```
|
||||
## Features
|
||||
|
||||
to generate a new project template.
|
||||
### Code 1
|
||||
|
||||
```bash
|
||||
cd code_submission
|
||||
```
|
||||
Code 1 is the timed buzz challenge. It accepts a `POST /event/buzz` request with:
|
||||
|
||||
to go to your newly created project.
|
||||
Feel free to explore the project structure, but the best place to start with your application code is in `src/app.rs`.
|
||||
Additionally, Cargo.toml may need updating as new versions of the dependencies are released, especially if things are not working after a `cargo update`.
|
||||
|
||||
## Running your project
|
||||
|
||||
```bash
|
||||
cargo leptos watch
|
||||
```
|
||||
|
||||
Copy `dev.env` to `.env`, then replace the placeholder with a unique secret
|
||||
before starting the server. The `.env` file is loaded automatically at startup
|
||||
and is ignored by Git. Startup validates the required settings and stops with a
|
||||
clear error if any are missing or malformed. `DEV_SECRET_KEY` authorizes server
|
||||
status changes and submission-history access:
|
||||
|
||||
```powershell
|
||||
Copy-Item dev.env .env
|
||||
```
|
||||
|
||||
You can also provide `DEV_SECRET_KEY` through the process environment. Do not
|
||||
commit secrets or expose them in client-side code.
|
||||
|
||||
Set `PROD=true` to make the server start in the live state. Truthy values are
|
||||
`1`, `true`, `yes`, and `on` (case-insensitive); an absent or other value starts
|
||||
the server offline.
|
||||
|
||||
Code 1 sends an SMS through TextBee when the server is live. Set
|
||||
`TEXTBEE_API_KEY`, `RECIPIENT_SELF`, `RECIPIENT_1`, and `RECIPIENT_2` (each a
|
||||
single E.164 number) in `.env`; `TEXTBEE_DEVICE_ID` is optional and selects a
|
||||
specific paired device. When live, code 1 sends a notification to self, the
|
||||
congratulations message to recipients 1 and 2, then `CHALLENGE_1_FIRST` to
|
||||
recipient 1 and `CHALLENGE_1_SECOND` to recipient 2. The latter two are sent
|
||||
literally as written. When offline, the planned messages are logged instead.
|
||||
TextBee acceptance means a message was queued, not necessarily delivered.
|
||||
|
||||
Code 2 uses the former manual code value (`QJ4EAN`). Code values are case
|
||||
insensitive. On successful submission, code 2 sends a notification to
|
||||
`RECIPIENT_SELF` while the server is live (and logs the notification instead
|
||||
while offline). The result page displays `NEXT_STEP_TEXT` from
|
||||
`src/codes/code2.rs`; replace its TODO text with the next step when ready.
|
||||
|
||||
Code 5 uses the code value defined in `src/codes/code5.rs`. A successful
|
||||
submission sends a notification to `RECIPIENT_SELF` while the server is live
|
||||
(and logs the notification instead while offline), and displays the completion
|
||||
message without providing a download.
|
||||
|
||||
The robot submits code 1 with `POST /event/buzz` and a JSON body containing
|
||||
`delay_ms` (a non-negative integer) and `secret` (the value of `CODE1_SECRET`).
|
||||
The server subtracts the target delay of 6430 ms from `delay_ms` to calculate
|
||||
the signed buzz delta. Deltas outside the inclusive range -500 to +500 ms are
|
||||
logged and ignored; the endpoint returns HTTP `200` and does not record the
|
||||
submission or send/log the submission actions. Accepted submissions return
|
||||
HTTP `202`. Correctly timed buzzes are only accepted between 11:00 AM and
|
||||
1:30 PM, inclusive, in the `America/Chicago` timezone. Requests outside that
|
||||
window are logged and return HTTP `200` without running submission actions.
|
||||
Set `CODE1_SECRET` to a unique random secret of at least 32 bytes in
|
||||
`.env`. For example, with the server running locally:
|
||||
|
||||
```sh
|
||||
curl.exe -X POST http://127.0.0.1:3000/event/buzz -H "Content-Type: application/json" -d '{"delay_ms":6388,"secret":"<your CODE1_SECRET value>"}'
|
||||
```
|
||||
|
||||
The server calculates `6388 - 6430 = -42` ms for the congratulations message.
|
||||
A valid request returns HTTP `202`.
|
||||
|
||||
### Test code1 with Postman
|
||||
|
||||
Create a `POST` request to `http://127.0.0.1:3000/event/buzz`, select **Body >
|
||||
raw > JSON**, and send:
|
||||
```json
|
||||
{
|
||||
"delay_ms": 6388,
|
||||
@@ -103,10 +61,129 @@ raw > JSON**, and send:
|
||||
}
|
||||
```
|
||||
|
||||
### Test code1 with Node.js
|
||||
Behavior:
|
||||
|
||||
With the server running and `CODE1_SECRET` set in your shell, this sends a
|
||||
single test event:
|
||||
- `delay_ms` must be a non-negative integer.
|
||||
- `secret` must match `CODE1_SECRET` exactly.
|
||||
- The request is only accepted in the `America/Chicago` window from 11:00 AM to 1:30 PM inclusive.
|
||||
- The server computes the signed delta from the target of `6430 ms`.
|
||||
- Deltas outside the inclusive range `-500` to `+500` are rejected and ignored.
|
||||
- Valid submissions return HTTP `202`.
|
||||
- When live, the app sends SMS notifications via TextBee to the configured recipients.
|
||||
- When offline, it logs the planned SMS actions instead.
|
||||
|
||||
The challenge text includes the literal strings `CHALLENGE_1_FIRST` and `CHALLENGE_1_SECOND`, which are sent to the configured recipients once the buzz is accepted.
|
||||
|
||||
### Code 2
|
||||
|
||||
The code value is `QJ4EAN` and is case-insensitive.
|
||||
|
||||
On successful submission:
|
||||
|
||||
- It records the success in the submission history.
|
||||
- If the server is live, it sends a TextBee SMS to `RECIPIENT_SELF`.
|
||||
- If the server is offline, it logs the notification instead.
|
||||
|
||||
### Code 5
|
||||
|
||||
The code value is `V7MK2P` and is case-insensitive.
|
||||
|
||||
On successful submission:
|
||||
|
||||
- It records the success in the submission history.
|
||||
- If the server is live, it sends a TextBee SMS to `RECIPIENT_SELF`.
|
||||
- If the server is offline, it logs the notification instead.
|
||||
- It shows a completion message without providing a download.
|
||||
|
||||
## Getting started
|
||||
|
||||
### Prerequisites
|
||||
|
||||
You need Rust, the Leptos toolchain, and the project dependencies.
|
||||
|
||||
If you do not have `cargo-leptos` installed:
|
||||
|
||||
```bash
|
||||
cargo install cargo-leptos --locked
|
||||
```
|
||||
|
||||
If you need the additional tooling required by cargo-leptos:
|
||||
|
||||
```bash
|
||||
rustup toolchain install nightly --allow-downgrade
|
||||
rustup target add wasm32-unknown-unknown
|
||||
cargo install cargo-generate
|
||||
npm install -g sass
|
||||
```
|
||||
|
||||
Then install the end-to-end test dependencies:
|
||||
|
||||
```bash
|
||||
cd end2end
|
||||
npm install
|
||||
```
|
||||
|
||||
### Environment configuration
|
||||
|
||||
Copy the sample environment file to `.env` before starting the app:
|
||||
|
||||
```powershell
|
||||
Copy-Item dev.env .env
|
||||
```
|
||||
|
||||
The app loads `.env` automatically at startup and validates the required values. The expected settings are defined in `src/secrets.rs` and include:
|
||||
|
||||
- `DEV_SECRET_KEY` — required for admin actions such as toggling live/offline mode and accessing submission history.
|
||||
- `TEXTBEE_API_KEY` — required for sending SMS via TextBee.
|
||||
- `TEXTBEE_DEVICE_ID` — optional, selects a specific paired device.
|
||||
- `RECIPIENT_SELF`, `RECIPIENT_1`, `RECIPIENT_2` — each must be a valid E.164 phone number.
|
||||
- `CODE1_SECRET` — required and must be at least 32 bytes long.
|
||||
- `PROD` — set to `true` to start the server in the live state. Truthy values are `1`, `true`, `yes`, and `on` (case-insensitive); any other value starts the server offline.
|
||||
|
||||
Do not commit secrets or expose them in client-side code.
|
||||
|
||||
### Run the app
|
||||
|
||||
```bash
|
||||
cargo leptos watch
|
||||
```
|
||||
|
||||
Then open the local app in your browser as directed by cargo-leptos.
|
||||
|
||||
## Management and admin flow
|
||||
|
||||
The app includes a status management page at `/manage`.
|
||||
|
||||
- It shows whether the server is currently live.
|
||||
- It lets an operator toggle the server status.
|
||||
- It requires the configured `DEV_SECRET_KEY` before changing state.
|
||||
|
||||
This is how the system can be transitioned between testing/offline and live challenge operation without exposing the settings publicly.
|
||||
|
||||
## Challenge testing
|
||||
|
||||
### Test Code 1 with curl
|
||||
|
||||
With the server running locally and `CODE1_SECRET` set in your shell:
|
||||
|
||||
```sh
|
||||
curl.exe -X POST http://127.0.0.1:3000/event/buzz -H "Content-Type: application/json" -d '{"delay_ms":6388,"secret":"<your CODE1_SECRET value>"}'
|
||||
```
|
||||
|
||||
The server calculates `6388 - 6430 = -42` ms, and a valid request returns HTTP `202`.
|
||||
|
||||
### Test Code 1 with Postman
|
||||
|
||||
Create a `POST` request to `http://127.0.0.1:3000/event/buzz`, select Body > raw > JSON, and send:
|
||||
|
||||
```json
|
||||
{
|
||||
"delay_ms": 6388,
|
||||
"secret": "<your CODE1_SECRET value>"
|
||||
}
|
||||
```
|
||||
|
||||
### Test Code 1 with Node.js
|
||||
|
||||
```javascript
|
||||
async function main() {
|
||||
@@ -129,27 +206,20 @@ main().catch((error) => {
|
||||
});
|
||||
```
|
||||
|
||||
When the server is live, a successful test event sends the configured TextBee
|
||||
SMS to the configured recipients. When it is offline, it only logs the message.
|
||||
When the server is live, a successful test event sends the configured TextBee SMS to the configured recipients. When it is offline, it logs the action instead.
|
||||
|
||||
## Installing Additional Tools
|
||||
## Build, test, and release
|
||||
|
||||
By default, `cargo-leptos` uses `nightly` Rust, `cargo-generate`, and `sass`. If you run into any trouble, you may need to install one or more of these tools.
|
||||
### Compile for release
|
||||
|
||||
1. `rustup toolchain install nightly --allow-downgrade` - make sure you have Rust nightly
|
||||
2. `rustup target add wasm32-unknown-unknown` - add the ability to compile Rust to WebAssembly
|
||||
3. `cargo install cargo-generate` - install `cargo-generate` binary (should be installed automatically in future)
|
||||
4. `npm install -g sass` - install `dart-sass` (should be optional in future
|
||||
5. Run `npm install` in end2end subdirectory before test
|
||||
|
||||
## Compiling for Release
|
||||
```bash
|
||||
cargo leptos build --release
|
||||
```
|
||||
|
||||
Will generate your server binary in target/release and your site package in target/site
|
||||
This will generate the server binary in `target/release` and the site bundle in `target/site`.
|
||||
|
||||
### End-to-end tests
|
||||
|
||||
## Testing Your Project
|
||||
```bash
|
||||
cargo leptos end-to-end
|
||||
```
|
||||
@@ -158,21 +228,24 @@ cargo leptos end-to-end
|
||||
cargo leptos end-to-end --release
|
||||
```
|
||||
|
||||
Cargo-leptos uses Playwright as the end-to-end test tool.
|
||||
Tests are located in end2end/tests directory.
|
||||
Cargo-leptos uses Playwright for browser tests and the test files are under `end2end/tests`.
|
||||
|
||||
## Executing a Server on a Remote Machine Without the Toolchain
|
||||
After running a `cargo leptos build --release` the minimum files needed are:
|
||||
## Remote deployment without the Rust toolchain
|
||||
|
||||
1. The server binary located in `target/server/release`
|
||||
2. The `site` directory and all files within located in `target/site`
|
||||
After running `cargo leptos build --release`, the minimum files needed are:
|
||||
|
||||
1. The server binary in `target/server/release`
|
||||
2. The `site` directory and all files inside it in `target/site`
|
||||
|
||||
Copy these files to your remote server. The expected directory structure is:
|
||||
|
||||
Copy these files to your remote server. The directory structure should be:
|
||||
```text
|
||||
code_submission
|
||||
site/
|
||||
```
|
||||
Set the following environment variables (updating for your project as needed):
|
||||
|
||||
Set the appropriate environment variables before starting the binary:
|
||||
|
||||
```sh
|
||||
export LEPTOS_OUTPUT_NAME="code_submission"
|
||||
export LEPTOS_SITE_ROOT="site"
|
||||
@@ -180,8 +253,9 @@ export LEPTOS_SITE_PKG_DIR="pkg"
|
||||
export LEPTOS_SITE_ADDR="0.0.0.0:3000"
|
||||
export LEPTOS_RELOAD_PORT="3001"
|
||||
```
|
||||
Finally, run the server binary.
|
||||
|
||||
Then run the server binary.
|
||||
|
||||
## Licensing
|
||||
|
||||
This template itself is released under the Unlicense. You should replace the LICENSE for your own application with an appropriate license if you plan to release it publicly.
|
||||
This project is released under the repository's existing license. The default template license is present in `LICENSE`, and you should replace it with an appropriate license if you intend to distribute this project publicly.
|
||||
|
||||
Reference in New Issue
Block a user