Ajouter des fichiers et des modifications existants
This commit is contained in:
commit
4ea514650d
13 changed files with 1481 additions and 0 deletions
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
/target
|
||||||
|
/node_modules
|
||||||
|
pnpm-lock.yaml
|
||||||
|
/assets
|
1234
Cargo.lock
generated
Normal file
1234
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
17
Cargo.toml
Normal file
17
Cargo.toml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
[package]
|
||||||
|
name = "fastmx"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
askama = { version = "0.12.1", features = ["with-axum"] }
|
||||||
|
askama_axum = "0.4.0"
|
||||||
|
axum = { version = "0.7.4", features = ["macros", "form"] }
|
||||||
|
serde = { version = "1.0.197", features = ["derive"] }
|
||||||
|
tokio = { version = "1.36.0", features = ["full"] }
|
||||||
|
tower = "0.4.13"
|
||||||
|
tower-http = { version = "0.5.2", features = ["fs"] }
|
||||||
|
tracing = "0.1.40"
|
||||||
|
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
11
README.md
Normal file
11
README.md
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
## To generate the stylesheet, run the following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm dlx tailwindcss -i styles/tailwind.css -o assets/main.css
|
||||||
|
```
|
||||||
|
|
||||||
|
## To launch the server, run the following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo run --release
|
||||||
|
```
|
13
package.json
Normal file
13
package.json
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"name": "fastmx",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"license": "ISC",
|
||||||
|
"devDependencies": {
|
||||||
|
"prettier": "^2.8.8",
|
||||||
|
"prettier-plugin-tailwindcss": "^0.3.0",
|
||||||
|
"tailwindcss": "^3.4.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@tailwindcss/forms": "^0.5.7"
|
||||||
|
}
|
||||||
|
}
|
108
src/main.rs
Normal file
108
src/main.rs
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use askama::Template;
|
||||||
|
use axum::{
|
||||||
|
debug_handler,
|
||||||
|
extract::State,
|
||||||
|
response::IntoResponse,
|
||||||
|
routing::{get, post},
|
||||||
|
Form, Router,
|
||||||
|
};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use tokio::{net::TcpListener, sync::Mutex};
|
||||||
|
use tower_http::services::ServeDir;
|
||||||
|
use tracing::info;
|
||||||
|
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
|
||||||
|
|
||||||
|
#[warn(clippy::all)]
|
||||||
|
#[warn(clippy::pedantic)]
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
tracing_subscriber::registry()
|
||||||
|
.with(
|
||||||
|
tracing_subscriber::EnvFilter::try_from_default_env()
|
||||||
|
.unwrap_or_else(|_| "fastmx=info".into()),
|
||||||
|
)
|
||||||
|
.with(tracing_subscriber::fmt::layer())
|
||||||
|
.init();
|
||||||
|
|
||||||
|
let pwd = std::env::current_dir().unwrap();
|
||||||
|
|
||||||
|
let todo_state = Arc::new(TodoState {
|
||||||
|
todos: Mutex::new(Vec::new()),
|
||||||
|
});
|
||||||
|
|
||||||
|
let api_router = Router::new()
|
||||||
|
.route("/hello", get(hello_from_the_server))
|
||||||
|
.route("/todos", post(add_todo))
|
||||||
|
.with_state(todo_state);
|
||||||
|
|
||||||
|
let router = Router::new()
|
||||||
|
.nest("/api", api_router)
|
||||||
|
.route("/", get(hello))
|
||||||
|
.route("/another-page", get(another_page))
|
||||||
|
.nest_service(
|
||||||
|
"/assets",
|
||||||
|
ServeDir::new(format!("{}/assets", pwd.to_str().unwrap())),
|
||||||
|
);
|
||||||
|
|
||||||
|
let listener = get_listener().await;
|
||||||
|
info!("listening on http://{}", listener.local_addr().unwrap());
|
||||||
|
axum::serve(listener, router).await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_listener() -> TcpListener {
|
||||||
|
let mut port = 3000;
|
||||||
|
loop {
|
||||||
|
match TcpListener::bind(format!("0.0.0.0:{port}")).await {
|
||||||
|
Ok(listener) => return listener,
|
||||||
|
Err(_) => port += 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn hello() -> impl IntoResponse {
|
||||||
|
HelloTemplate {}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn another_page() -> impl IntoResponse {
|
||||||
|
AnotherPageTemplate {}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn hello_from_the_server() -> impl IntoResponse {
|
||||||
|
"Hello from the server"
|
||||||
|
}
|
||||||
|
|
||||||
|
#[debug_handler]
|
||||||
|
async fn add_todo(
|
||||||
|
State(todo_state): State<Arc<TodoState>>,
|
||||||
|
Form(todo): Form<TodoRequest>,
|
||||||
|
) -> impl IntoResponse {
|
||||||
|
todo_state.todos.lock().await.push(todo.name);
|
||||||
|
TodoListTemplate {
|
||||||
|
todos: todo_state.todos.lock().await.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
struct TodoRequest {
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Template)]
|
||||||
|
#[template(path = "hello.html")]
|
||||||
|
struct HelloTemplate {}
|
||||||
|
|
||||||
|
#[derive(Template)]
|
||||||
|
#[template(path = "another-page.html")]
|
||||||
|
struct AnotherPageTemplate {}
|
||||||
|
|
||||||
|
#[derive(Template)]
|
||||||
|
#[template(path = "todo-list.html")]
|
||||||
|
struct TodoListTemplate {
|
||||||
|
todos: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TodoState {
|
||||||
|
todos: Mutex<Vec<String>>,
|
||||||
|
}
|
3
styles/tailwind.css
Normal file
3
styles/tailwind.css
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
14
tailwind.config.cjs
Normal file
14
tailwind.config.cjs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
const { fontFamily } = require("tailwindcss/defaultTheme");
|
||||||
|
|
||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
module.exports = {
|
||||||
|
content: ["./templates/*.html"],
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
fontFamily: {
|
||||||
|
sans: ["Inter var", ...fontFamily.sans],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [require("@tailwindcss/forms")],
|
||||||
|
};
|
9
templates/another-page.html
Normal file
9
templates/another-page.html
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<!-- prettier-ignore -->
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block title %}Another page!{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<a href="/"> Main page </a>
|
||||||
|
<h1 class="font-bold text-indigo-500">Another page</h1>
|
||||||
|
{% endblock %}
|
21
templates/base.html
Normal file
21
templates/base.html
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<link href="/assets/main.css" rel="stylesheet" />
|
||||||
|
<link href="https://rsms.me/inter/inter.css" rel="stylesheet" />
|
||||||
|
<!-- Allow any inheriting page to set it's own title -->
|
||||||
|
<title>{% block title %}{{ title }}{% endblock %}</title>
|
||||||
|
|
||||||
|
<!-- htmx from the unpkg CDN - your mileage may vary -->
|
||||||
|
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
|
||||||
|
|
||||||
|
<!-- Allow any inheriting page to extend head with additional assets -->
|
||||||
|
{% block head %}{% endblock %}
|
||||||
|
</head>
|
||||||
|
<body hx-boost="true">
|
||||||
|
<div id="content">
|
||||||
|
<!-- Inheriting pages will have their content rendered here, similar to app root in React, Angular, etc. -->
|
||||||
|
{% block content %}{% endblock %}
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
24
templates/hello.html
Normal file
24
templates/hello.html
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<!-- prettier-ignore -->
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block title %}Hello!{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="inline-flex flex-row space-x-2">
|
||||||
|
<h1 class="text-green-500">Howdy!</h1>
|
||||||
|
<a
|
||||||
|
href="/another-page"
|
||||||
|
class="text-indigo-500 underline hover:text-indigo-300"
|
||||||
|
>Another page</a
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
hx-get="/api/hello"
|
||||||
|
hx-swap="innerHtml"
|
||||||
|
class="rounded-md bg-indigo-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
|
||||||
|
>
|
||||||
|
Say hello
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% include "todo-form.html" %} {% endblock %}
|
20
templates/todo-form.html
Normal file
20
templates/todo-form.html
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<form hx-post="/api/todos" hx-target="#todos" class="max-w-md">
|
||||||
|
<label for="todo" class="block text-sm font-medium leading-6 text-gray-900"
|
||||||
|
>Todo</label
|
||||||
|
>
|
||||||
|
<div class="mt-2 inline-flex flex-row space-x-2">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="name"
|
||||||
|
class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
|
||||||
|
placeholder="Replace frontend with htmx"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="rounded-md bg-indigo-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
|
||||||
|
>
|
||||||
|
Add
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div id="todos"></div>
|
3
templates/todo-list.html
Normal file
3
templates/todo-list.html
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{% for todo in todos %}
|
||||||
|
<p class="text-lg">{{ todo }}</p>
|
||||||
|
{% endfor %}
|
Loading…
Add table
Add a link
Reference in a new issue