first fully working version
This commit is contained in:
parent
473bd34e4f
commit
db01e0494f
6 changed files with 205 additions and 31 deletions
20
Cargo.lock
generated
20
Cargo.lock
generated
|
@ -185,7 +185,9 @@ checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
|
|||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"wasm-bindgen",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
|
@ -748,6 +750,12 @@ dependencies = [
|
|||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_ci"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.11"
|
||||
|
@ -758,10 +766,12 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
|||
name = "iwakura-web"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"rocket",
|
||||
"rocket_dyn_templates",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"supports-color",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1330,6 +1340,7 @@ dependencies = [
|
|||
"rocket_codegen",
|
||||
"rocket_http",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"state",
|
||||
"tempfile",
|
||||
"time",
|
||||
|
@ -1584,6 +1595,15 @@ dependencies = [
|
|||
"loom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "supports-color"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9829b314621dfc575df4e409e79f9d6a66a3bd707ab73f23cb4aa3a854ac854f"
|
||||
dependencies = [
|
||||
"is_ci",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.70"
|
||||
|
|
|
@ -6,7 +6,9 @@ edition = "2021"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
rocket = "0.5.1"
|
||||
chrono = "0.4.38"
|
||||
rocket = {version="0.5.1", features=["json"]}
|
||||
rocket_dyn_templates = { version = "0.2.0", features = ["tera"] }
|
||||
serde = "1.0.204"
|
||||
serde_json = "1.0.120"
|
||||
supports-color = "3.0.0"
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
{"messages":[{"body":"b","date":"2024-0712 | 21:34","name":"a "}]}
|
37
src/logger.rs
Normal file
37
src/logger.rs
Normal file
|
@ -0,0 +1,37 @@
|
|||
extern crate supports_color;
|
||||
|
||||
pub mod iwakulog {
|
||||
use supports_color::Stream;
|
||||
|
||||
pub enum LogLevel {
|
||||
Fail,
|
||||
Warn,
|
||||
Info
|
||||
}
|
||||
|
||||
const COLOR_RESET: &str = "\x1b[0m";
|
||||
const COLOR_FAIL: &str = "\x1b[0;31m";
|
||||
const COLOR_WARN: &str = "\x1b[0;33m";
|
||||
const COLOR_INFO: &str = "\x1b[0;34m";
|
||||
|
||||
fn _prefix(p: &str, c1: &str, c2: &str, c3: &str, color_support: bool) -> String {
|
||||
if color_support {
|
||||
return format!("{}[{}{}{}]", c1, c2, p, c3);
|
||||
}
|
||||
return format!("[{}]", p);
|
||||
}
|
||||
|
||||
fn prefix(level: LogLevel) -> String {
|
||||
let color_support = supports_color::on(Stream::Stdout);
|
||||
let prefixa = match level {
|
||||
LogLevel::Fail => _prefix("f", COLOR_RESET, COLOR_FAIL, COLOR_RESET, color_support.is_some()),
|
||||
LogLevel::Warn => _prefix("w", COLOR_RESET, COLOR_WARN, COLOR_RESET, color_support.is_some()),
|
||||
LogLevel::Info => _prefix("i", COLOR_RESET, COLOR_INFO, COLOR_RESET, color_support.is_some()),
|
||||
};
|
||||
return prefixa;
|
||||
}
|
||||
|
||||
pub fn log(level: LogLevel, message: &str) {
|
||||
println!("{}: {}", prefix(level), message);
|
||||
}
|
||||
}
|
85
src/main.rs
85
src/main.rs
|
@ -1,48 +1,97 @@
|
|||
mod logger;
|
||||
|
||||
extern crate rocket;
|
||||
extern crate rocket_dyn_templates;
|
||||
|
||||
use rocket::{get,launch, build, routes};
|
||||
use rocket::response::content::RawJson;
|
||||
use rocket::{get, post, launch, build, routes};
|
||||
use rocket::fs::FileServer;
|
||||
use rocket_dyn_templates::{Template, context};
|
||||
use rocket::serde::{Deserialize, Serialize, json::Json};
|
||||
|
||||
use chrono::Local;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::fs::File;
|
||||
use std::fs::read_to_string;
|
||||
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use crate::logger::iwakulog::{LogLevel, log};
|
||||
|
||||
const ASSETS_DIR: &str = "./assets";
|
||||
const GUEST_MESSAGES: &str = "./guests.json";
|
||||
const DATE_TIME_FORMAT: &str = "%Y-%m%d | %H:%M";
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
struct GuestMessage<'r> {
|
||||
name: Cow<'r, str>,
|
||||
body: Cow<'r, str>,
|
||||
}
|
||||
|
||||
static ASSETS_DIR: &str = "./assets";
|
||||
static GUEST_MESSAGES: &str = "./guests.json";
|
||||
|
||||
fn check_file(path: &str) -> bool {
|
||||
Path::new(path).exists()
|
||||
}
|
||||
|
||||
fn read_guest_messages() -> Result<Value, Box<dyn std::error::Error>> {
|
||||
let file = read_to_string(GUEST_MESSAGES)?;
|
||||
let json: Value = serde_json::from_str(&file)?;
|
||||
Ok(json)
|
||||
}
|
||||
|
||||
fn post_guest_message(name: &str, body: &str) {
|
||||
let mut guest_json = match read_guest_messages() {
|
||||
Ok(x) => x,
|
||||
Err(x) => panic!("Error while reading guest messages: {x:?}")
|
||||
};
|
||||
let messages = guest_json["messages"]
|
||||
.as_array_mut()
|
||||
.expect("Object is not an array?");
|
||||
let date = Local::now().format(DATE_TIME_FORMAT);
|
||||
messages.push(json!({
|
||||
"name": name,
|
||||
"body": body,
|
||||
"date": date.to_string(),
|
||||
}));
|
||||
let mut file = File::create(GUEST_MESSAGES).expect("Couldn't read msgs");
|
||||
file.write_all(guest_json.to_string().as_bytes()).expect("wah");
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[get("/")]
|
||||
fn index() -> Template {
|
||||
let cx = context! {
|
||||
hi: "no",
|
||||
};
|
||||
Template::render("index", &cx)
|
||||
Template::render("index", context! {})
|
||||
}
|
||||
|
||||
#[post("/post_message", data = "<input>")]
|
||||
fn post_msg(input: Json<GuestMessage<'_>>) {
|
||||
post_guest_message(&input.name, &input.body);
|
||||
return;
|
||||
}
|
||||
|
||||
#[get("/guestbook")]
|
||||
fn guests() -> Template {
|
||||
let cx = context! {
|
||||
messages: [
|
||||
context!{
|
||||
name: "hexlocation",
|
||||
date: "12 Jun.",
|
||||
body: "<h1>xd1</h1>",
|
||||
}
|
||||
]
|
||||
let guest_json = match read_guest_messages() {
|
||||
Ok(x) => x,
|
||||
Err(x) => panic!("Error while reading guest messages: {x:?}")
|
||||
};
|
||||
Template::render("guest", &cx)
|
||||
Template::render("guest", &guest_json)
|
||||
}
|
||||
|
||||
#[launch]
|
||||
fn rocket() -> _ {
|
||||
if !check_file(GUEST_MESSAGES) {
|
||||
File::create(GUEST_MESSAGES).unwrap();
|
||||
log(LogLevel::Warn, format!("Guest messages file ({}) has not been found. Creating a new one.", GUEST_MESSAGES).as_str());
|
||||
let mut file = File::create(GUEST_MESSAGES).expect("Failed to create file.");
|
||||
file.write_all(b"{\"messages\":[]}").expect("Failed to write to file.");
|
||||
}
|
||||
build()
|
||||
.mount("/", routes![index, guests])
|
||||
.mount("/", routes![index, guests, post_msg])
|
||||
.mount("/assets", FileServer::from(ASSETS_DIR))
|
||||
.attach(Template::fairing())
|
||||
}
|
||||
|
|
|
@ -1,21 +1,46 @@
|
|||
<div id="page-container">
|
||||
<div id="header-container">
|
||||
<h1 id="title">iwakura.rip</h1>
|
||||
<a class="link" onclick="show_message_poster()" style="margin-bottom: 10px;"> [ post message ] </a>
|
||||
</div>
|
||||
<div id="entries-container">
|
||||
{% for message in messages %}
|
||||
<div id="entries-container">
|
||||
<div class="guest-entry">
|
||||
<div class="entry-header">
|
||||
<bu class="entry-title">{{ message.name }}</bu>
|
||||
<bu class="entry-date">{{ message.date }}</bu>
|
||||
</div>
|
||||
<div class="entry-body">
|
||||
<p>{{ message.body }}</p>
|
||||
</div>
|
||||
<div class="guest-entry">
|
||||
<div class="entry-header">
|
||||
<bu class="entry-title">{{ message.name }}</bu>
|
||||
<bu class="entry-date">{{ message.date }}</bu>
|
||||
</div>
|
||||
<div class="entry-body">
|
||||
<p>{{ message.body }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div id="post-container">
|
||||
<bu id="name-label">name</bu>
|
||||
<input type="text" id="name-box" name="name-box"><br>
|
||||
<div class="break"></div>
|
||||
<bu id="msg-label">message</bu>
|
||||
<textarea id="msg-box"></textarea>
|
||||
<div class="break"></div>
|
||||
<a class="link" onclick="send_msg()"> [ send ] </a>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
let show_message_poster = () => {
|
||||
document.getElementById("entries-container").style.display = "none";
|
||||
document.getElementById("post-container").style.display = "flex";
|
||||
}
|
||||
let send_msg = async () => {
|
||||
fetch("/post_message", {
|
||||
"method": "POST",
|
||||
"body": JSON.stringify({
|
||||
body: document.getElementById("msg-box").value,
|
||||
name: document.getElementById("name-box").value
|
||||
}),
|
||||
})
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
:root {
|
||||
--term-color: #4AF626;
|
||||
|
@ -23,11 +48,11 @@
|
|||
}
|
||||
@font-face {
|
||||
font-family: "Hack Nerd Font";
|
||||
src: url(../resources/ttf/HNF-Reg.ttf);
|
||||
src: url(/assets/ttf/HNF-Reg.ttf);
|
||||
}
|
||||
#title {
|
||||
background: url("../resources/img/lain.png");
|
||||
background-position: -200px 1125px;
|
||||
background: url("/assets/img/lain.png");
|
||||
background-position: -100px 1125px;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
font-size: 35px;
|
||||
|
@ -37,9 +62,49 @@
|
|||
margin-top: var(--title-gap);
|
||||
font-family: var(--title-font);
|
||||
}
|
||||
#post-container {
|
||||
width: 50%;
|
||||
padding: 5px;
|
||||
border: 1px solid var(--term-color);
|
||||
display: none;
|
||||
margin: auto;
|
||||
margin-top: 10px;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
input {
|
||||
margin-left: auto;
|
||||
float: right;
|
||||
}
|
||||
.break {
|
||||
flex-basis: 100%;
|
||||
height: 0;
|
||||
}
|
||||
.link:hover {
|
||||
background-color: var(--term-color);
|
||||
color: black;
|
||||
}
|
||||
label {
|
||||
text-align: left;
|
||||
clear: both;
|
||||
float: left;
|
||||
}
|
||||
.link {
|
||||
align-items: center;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
text-align: center;
|
||||
transition: 0.3s;
|
||||
}
|
||||
#header-container {
|
||||
text-align: center;
|
||||
}
|
||||
#entries-container {
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue