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]
|
||||
name = "ponguito-serv"
|
||||
name = "ponguito-server"
|
||||
version = "0.1.0"
|
||||
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]
|
||||
chrono = "0.4.31"
|
||||
rocket = { version = "0.5.0", features = ["json"] }
|
||||
serde = "1.0.195"
|
||||
axum = "0.7.3"
|
||||
axum-client-ip = "0.5.0"
|
||||
chrono = { version = "0.4.31", features = ["serde"] }
|
||||
serde = { version = "1.0.195", features = ["derive"] }
|
||||
serde_json = "1.0.111"
|
||||
tokio = { version = "1.35.1", features = ["full"] }
|
||||
|
|
|
@ -6,5 +6,5 @@ RUN cargo build --release
|
|||
|
||||
FROM alpine:latest
|
||||
WORKDIR /app
|
||||
COPY --from=builder /app/target/release/ponguito-serv .
|
||||
CMD ["./ponguito-serv"]
|
||||
COPY --from=builder /app/target/release/ponguito-server .
|
||||
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;
|
||||
use rocket::form::Form;
|
||||
use rocket::serde::json::Json;
|
||||
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};
|
||||
//! Un simple serveur http pour enregistrer des scores du jeu Ponguito.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::net::IpAddr;
|
||||
|
||||
#[derive(Deserialize, Serialize, FromForm, Clone, Debug)]
|
||||
struct NewScore {
|
||||
use axum::extract::Path;
|
||||
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,
|
||||
score: u32,
|
||||
}
|
||||
|
||||
#[post("/new_score", data = "<score>")]
|
||||
fn new_score(score: Form<NewScore>) -> Option<()> {
|
||||
let name = score.name.clone();
|
||||
let score = score.score.clone();
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let app = Router::new()
|
||||
.route("/best-scores", get(get_best_scores))
|
||||
.route("/register/:name/:score", get(register_score));
|
||||
|
||||
save_data(name, score, Utc::now().to_string());
|
||||
|
||||
Some(())
|
||||
let listener = tokio::net::TcpListener::bind("0.0.0.0:80").await.unwrap();
|
||||
axum::serve(listener, app).await.unwrap();
|
||||
}
|
||||
|
||||
#[get("/all-data")]
|
||||
fn all_data() -> Json<Vec<(u32, String, String)>> {
|
||||
let best_scores: Vec<_> = load_data().into_iter().map(|(k, v, d)| (v, k, d)).collect();
|
||||
/// Récupération des meilleurs scores pour chaque joueur.
|
||||
async fn get_best_scores() -> impl IntoResponse {
|
||||
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")]
|
||||
fn data() -> Json<Vec<(u32, String, String)>> {
|
||||
let mut best_scores: Vec<_> = load_data().into_iter().map(|(k, v, d)| (v, k, d)).collect();
|
||||
/// Enregistrement d'un nouveau score.
|
||||
async fn register_score(
|
||||
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();
|
||||
best_scores.truncate(nb);
|
||||
|
||||
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])
|
||||
// Renvoi du statut
|
||||
StatusCode::CREATED
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue