Files
2026-10-20 11:20:55 -05:00

262 lines
7.4 KiB
Markdown

# Code Submission
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.
## Overview
The app supports:
- 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.
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/
```
## Features
### Code 1
Code 1 is the timed buzz challenge. It accepts a `POST /event/buzz` request with:
```json
{
"delay_ms": 6388,
"secret": "<your CODE1_SECRET value>"
}
```
Behavior:
- `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() {
const delayMs = 6388;
const secret = process.env.CODE1_SECRET;
if (!secret) throw new Error("CODE1_SECRET is not set");
const response = await fetch("http://127.0.0.1:3000/event/buzz", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ delay_ms: delayMs, secret }),
});
console.log(response.status, await response.text());
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
```
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.
## Build, test, and release
### Compile for release
```bash
cargo leptos build --release
```
This will generate the server binary in `target/release` and the site bundle in `target/site`.
### End-to-end tests
```bash
cargo leptos end-to-end
```
```bash
cargo leptos end-to-end --release
```
Cargo-leptos uses Playwright for browser tests and the test files are under `end2end/tests`.
## Remote deployment without the Rust toolchain
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:
```text
code_submission
site/
```
Set the appropriate environment variables before starting the binary:
```sh
export LEPTOS_OUTPUT_NAME="code_submission"
export LEPTOS_SITE_ROOT="site"
export LEPTOS_SITE_PKG_DIR="pkg"
export LEPTOS_SITE_ADDR="0.0.0.0:3000"
export LEPTOS_RELOAD_PORT="3001"
```
Then run the server binary.
## Licensing
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.