Je vais dodo fleme de finir maintenant
This commit is contained in:
parent
3ec6d37dfa
commit
2c1756e704
2
.gitea/template
Normal file
2
.gitea/template
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
Cargo.toml
|
||||||
|
Cargo.lock
|
19
.gitea/workflows/rust.yml
Normal file
19
.gitea/workflows/rust.yml
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
on: [push, pull_request]
|
||||||
|
name: Rust Checks
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
checks:
|
||||||
|
runs-on: main
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Use cache
|
||||||
|
run: mkdir -p /cache/${{ gitea.repository }} && ln -s /cache/${{ gitea.repository }} target
|
||||||
|
- name: Cargo fmt
|
||||||
|
run: cargo fmt --check
|
||||||
|
- name: Cargo build
|
||||||
|
run: cargo build
|
||||||
|
- name: Cargo test
|
||||||
|
run: cargo test
|
||||||
|
- name: Cargo clippy
|
||||||
|
run: cargo clippy -- -D warnings
|
1015
Cargo.lock
generated
1015
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
19
Cargo.toml
19
Cargo.toml
|
@ -1,12 +1,21 @@
|
||||||
[package]
|
[package]
|
||||||
name = "ponguito-serv"
|
name = "ponguito-server"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# [lints.rust]
|
||||||
|
# missing_docs = "warn"
|
||||||
|
|
||||||
|
# [lints.clippy]
|
||||||
|
# missing_docs_in_private_items = "warn"
|
||||||
|
# unwrap_in_result = "warn"
|
||||||
|
# unwrap_used = "warn"
|
||||||
|
# nursery = "warn"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = "0.4.31"
|
axum = "0.7.3"
|
||||||
rocket = { version = "0.5.0", features = ["json"] }
|
axum-client-ip = "0.5.0"
|
||||||
serde = "1.0.195"
|
chrono = { version = "0.4.31", features = ["serde"] }
|
||||||
|
serde = { version = "1.0.195", features = ["derive"] }
|
||||||
serde_json = "1.0.111"
|
serde_json = "1.0.111"
|
||||||
|
tokio = { version = "1.35.1", features = ["full"] }
|
||||||
|
|
|
@ -6,5 +6,5 @@ RUN cargo build --release
|
||||||
|
|
||||||
FROM alpine:latest
|
FROM alpine:latest
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY --from=builder /app/target/release/ponguito-serv .
|
COPY --from=builder /app/target/release/ponguito-server .
|
||||||
CMD ["./ponguito-serv"]
|
CMD ["./ponguito-server"]
|
10
exemple.py
10
exemple.py
|
@ -1,10 +0,0 @@
|
||||||
import requests as rq
|
|
||||||
|
|
||||||
IP = "127.0.0.1"
|
|
||||||
|
|
||||||
post = {"name": "coco_sol", "score": 10}
|
|
||||||
|
|
||||||
rq.post(f"http://{IP}/new_score", post)
|
|
||||||
rq.post(f"http://{IP}/new_score", post)
|
|
||||||
|
|
||||||
print(rq.get(f"http://{IP}/data").json())
|
|
18
rustfmt.toml
Normal file
18
rustfmt.toml
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
use_try_shorthand = true
|
||||||
|
use_field_init_shorthand = true
|
||||||
|
|
||||||
|
version = "Two"
|
||||||
|
error_on_line_overflow = true
|
||||||
|
error_on_unformatted = true
|
||||||
|
|
||||||
|
format_code_in_doc_comments = true
|
||||||
|
format_macro_bodies = true
|
||||||
|
format_macro_matchers = true
|
||||||
|
format_strings = true
|
||||||
|
|
||||||
|
imports_granularity = "Module"
|
||||||
|
group_imports = "StdExternalCrate"
|
||||||
|
|
||||||
|
normalize_doc_attributes = true
|
||||||
|
normalize_comments = true
|
||||||
|
wrap_comments = true
|
8
scores.log
Normal file
8
scores.log
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{"name": "Tipragot", "ip": "86.206.50.244"}
|
||||||
|
sqdsq
|
||||||
|
|
||||||
|
sqdsqqs
|
||||||
|
d
|
||||||
|
q
|
||||||
|
sqdsqd
|
||||||
|
|
138
src/main.rs
138
src/main.rs
|
@ -1,80 +1,88 @@
|
||||||
use chrono::Utc;
|
//! Un simple serveur http pour enregistrer des scores du jeu Ponguito.
|
||||||
use rocket::form::Form;
|
|
||||||
use rocket::serde::json::Json;
|
use std::collections::HashMap;
|
||||||
use rocket::{get, launch, Config, FromForm};
|
|
||||||
use rocket::{post, routes};
|
|
||||||
use serde::Deserialize;
|
|
||||||
use serde::Serialize;
|
|
||||||
use std::fs::File;
|
|
||||||
use std::io::{Read, Write};
|
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, FromForm, Clone, Debug)]
|
use axum::extract::Path;
|
||||||
struct NewScore {
|
use axum::http::StatusCode;
|
||||||
|
use axum::response::IntoResponse;
|
||||||
|
use axum::routing::get;
|
||||||
|
use axum::{Json, Router};
|
||||||
|
use axum_client_ip::LeftmostXForwardedFor;
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use tokio::fs::{File, OpenOptions};
|
||||||
|
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
|
||||||
|
|
||||||
|
/// Structure du score.
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
struct Score {
|
||||||
|
ip: IpAddr,
|
||||||
|
time: DateTime<Utc>,
|
||||||
name: String,
|
name: String,
|
||||||
score: u32,
|
score: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/new_score", data = "<score>")]
|
#[tokio::main]
|
||||||
fn new_score(score: Form<NewScore>) -> Option<()> {
|
async fn main() {
|
||||||
let name = score.name.clone();
|
let app = Router::new()
|
||||||
let score = score.score.clone();
|
.route("/best-scores", get(get_best_scores))
|
||||||
|
.route("/register/:name/:score", get(register_score));
|
||||||
|
|
||||||
save_data(name, score, Utc::now().to_string());
|
let listener = tokio::net::TcpListener::bind("0.0.0.0:80").await.unwrap();
|
||||||
|
axum::serve(listener, app).await.unwrap();
|
||||||
Some(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/all-data")]
|
/// Récupération des meilleurs scores pour chaque joueur.
|
||||||
fn all_data() -> Json<Vec<(u32, String, String)>> {
|
async fn get_best_scores() -> impl IntoResponse {
|
||||||
let best_scores: Vec<_> = load_data().into_iter().map(|(k, v, d)| (v, k, d)).collect();
|
let mut lines = BufReader::new(
|
||||||
|
File::open("scores.log")
|
||||||
|
.await
|
||||||
|
.expect("failed to load scores file"),
|
||||||
|
)
|
||||||
|
.lines();
|
||||||
|
|
||||||
Json(best_scores)
|
let mut players: HashMap<String, u32> = HashMap::new();
|
||||||
|
while let Ok(Some(line)) = lines.next_line().await {
|
||||||
|
let score: Score = serde_json::from_str(&line).expect("failed to deserialize score");
|
||||||
|
players
|
||||||
|
.entry(score.name)
|
||||||
|
.and_modify(|last_score| {
|
||||||
|
if score.score > *last_score {
|
||||||
|
*last_score = score.score
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.or_insert(score.score);
|
||||||
|
}
|
||||||
|
Json(players)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/data")]
|
/// Enregistrement d'un nouveau score.
|
||||||
fn data() -> Json<Vec<(u32, String, String)>> {
|
async fn register_score(
|
||||||
let mut best_scores: Vec<_> = load_data().into_iter().map(|(k, v, d)| (v, k, d)).collect();
|
LeftmostXForwardedFor(ip): LeftmostXForwardedFor,
|
||||||
|
Path((name, score)): Path<(String, u32)>,
|
||||||
|
) -> impl IntoResponse {
|
||||||
|
// Création du fichier de logs des scores si il n'existe pas
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.create(true)
|
||||||
|
.append(true)
|
||||||
|
.open("scores.log")
|
||||||
|
.await
|
||||||
|
.expect("failed to load scores file");
|
||||||
|
|
||||||
let nb = 5.min(best_scores.len());
|
// Enregistrement du score
|
||||||
|
let score = Score {
|
||||||
|
ip,
|
||||||
|
time: Utc::now(),
|
||||||
|
name,
|
||||||
|
score,
|
||||||
|
};
|
||||||
|
let mut data = serde_json::to_vec(&score).expect("failed to serialize score");
|
||||||
|
data.push(b'\n');
|
||||||
|
file.write_all(&data)
|
||||||
|
.await
|
||||||
|
.expect("failed to write to scores file");
|
||||||
|
|
||||||
best_scores.sort_by(|a, b| b.0.cmp(&a.0)); // sort by score();
|
// Renvoi du statut
|
||||||
best_scores.truncate(nb);
|
StatusCode::CREATED
|
||||||
|
|
||||||
Json(best_scores)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn save_data(name: String, score: u32, date: String) {
|
|
||||||
let mut data = load_data();
|
|
||||||
data.push((name, score, date));
|
|
||||||
let json = serde_json::to_string(&data).unwrap();
|
|
||||||
let mut file = File::create("data.json").unwrap();
|
|
||||||
file.write_all(json.as_bytes()).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_data() -> Vec<(String, u32, String)> {
|
|
||||||
let mut file = File::open("data.json").unwrap();
|
|
||||||
let mut contents = String::new();
|
|
||||||
file.read_to_string(&mut contents).unwrap();
|
|
||||||
serde_json::from_str(&contents).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
// The main function of the website.
|
|
||||||
#[launch]
|
|
||||||
async fn rocket() -> _ {
|
|
||||||
// generate a secret key and figment the rocket server
|
|
||||||
let figment = Config::figment()
|
|
||||||
.merge(("port", 80))
|
|
||||||
.merge(("worker_count", 4))
|
|
||||||
.merge(("log_level", rocket::config::LogLevel::Critical))
|
|
||||||
.merge(("address", IpAddr::from([0, 0, 0, 0])));
|
|
||||||
|
|
||||||
// create a config
|
|
||||||
let config = Config::from(figment);
|
|
||||||
|
|
||||||
// launch the server with different routes
|
|
||||||
rocket::build()
|
|
||||||
// apply the config
|
|
||||||
.configure(config)
|
|
||||||
.mount("/", routes![new_score, data, all_data])
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue