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 = [
|
dependencies = [
|
||||||
"android-tzdata",
|
"android-tzdata",
|
||||||
"iana-time-zone",
|
"iana-time-zone",
|
||||||
|
"js-sys",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
|
"wasm-bindgen",
|
||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -748,6 +750,12 @@ dependencies = [
|
||||||
"windows-sys 0.52.0",
|
"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]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.11"
|
version = "1.0.11"
|
||||||
|
@ -758,10 +766,12 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||||
name = "iwakura-web"
|
name = "iwakura-web"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
"rocket",
|
"rocket",
|
||||||
"rocket_dyn_templates",
|
"rocket_dyn_templates",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"supports-color",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1330,6 +1340,7 @@ dependencies = [
|
||||||
"rocket_codegen",
|
"rocket_codegen",
|
||||||
"rocket_http",
|
"rocket_http",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"state",
|
"state",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"time",
|
"time",
|
||||||
|
@ -1584,6 +1595,15 @@ dependencies = [
|
||||||
"loom",
|
"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]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.70"
|
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
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[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"] }
|
rocket_dyn_templates = { version = "0.2.0", features = ["tera"] }
|
||||||
serde = "1.0.204"
|
serde = "1.0.204"
|
||||||
serde_json = "1.0.120"
|
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;
|
||||||
extern crate rocket_dyn_templates;
|
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::fs::FileServer;
|
||||||
use rocket_dyn_templates::{Template, context};
|
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::path::Path;
|
||||||
use std::fs::File;
|
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 {
|
fn check_file(path: &str) -> bool {
|
||||||
Path::new(path).exists()
|
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("/")]
|
#[get("/")]
|
||||||
fn index() -> Template {
|
fn index() -> Template {
|
||||||
let cx = context! {
|
Template::render("index", context! {})
|
||||||
hi: "no",
|
}
|
||||||
};
|
|
||||||
Template::render("index", &cx)
|
#[post("/post_message", data = "<input>")]
|
||||||
|
fn post_msg(input: Json<GuestMessage<'_>>) {
|
||||||
|
post_guest_message(&input.name, &input.body);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/guestbook")]
|
#[get("/guestbook")]
|
||||||
fn guests() -> Template {
|
fn guests() -> Template {
|
||||||
let cx = context! {
|
let guest_json = match read_guest_messages() {
|
||||||
messages: [
|
Ok(x) => x,
|
||||||
context!{
|
Err(x) => panic!("Error while reading guest messages: {x:?}")
|
||||||
name: "hexlocation",
|
|
||||||
date: "12 Jun.",
|
|
||||||
body: "<h1>xd1</h1>",
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
};
|
||||||
Template::render("guest", &cx)
|
Template::render("guest", &guest_json)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[launch]
|
#[launch]
|
||||||
fn rocket() -> _ {
|
fn rocket() -> _ {
|
||||||
if !check_file(GUEST_MESSAGES) {
|
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()
|
build()
|
||||||
.mount("/", routes![index, guests])
|
.mount("/", routes![index, guests, post_msg])
|
||||||
.mount("/assets", FileServer::from(ASSETS_DIR))
|
.mount("/assets", FileServer::from(ASSETS_DIR))
|
||||||
.attach(Template::fairing())
|
.attach(Template::fairing())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
<div id="page-container">
|
<div id="page-container">
|
||||||
<div id="header-container">
|
<div id="header-container">
|
||||||
<h1 id="title">iwakura.rip</h1>
|
<h1 id="title">iwakura.rip</h1>
|
||||||
|
<a class="link" onclick="show_message_poster()" style="margin-bottom: 10px;"> [ post message ] </a>
|
||||||
</div>
|
</div>
|
||||||
{% for message in messages %}
|
|
||||||
<div id="entries-container">
|
<div id="entries-container">
|
||||||
|
{% for message in messages %}
|
||||||
<div class="guest-entry">
|
<div class="guest-entry">
|
||||||
<div class="entry-header">
|
<div class="entry-header">
|
||||||
<bu class="entry-title">{{ message.name }}</bu>
|
<bu class="entry-title">{{ message.name }}</bu>
|
||||||
|
@ -13,9 +14,33 @@
|
||||||
<p>{{ message.body }}</p>
|
<p>{{ message.body }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
{% endfor %}
|
{% 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>
|
</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>
|
<style>
|
||||||
:root {
|
:root {
|
||||||
--term-color: #4AF626;
|
--term-color: #4AF626;
|
||||||
|
@ -23,11 +48,11 @@
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "Hack Nerd Font";
|
font-family: "Hack Nerd Font";
|
||||||
src: url(../resources/ttf/HNF-Reg.ttf);
|
src: url(/assets/ttf/HNF-Reg.ttf);
|
||||||
}
|
}
|
||||||
#title {
|
#title {
|
||||||
background: url("../resources/img/lain.png");
|
background: url("/assets/img/lain.png");
|
||||||
background-position: -200px 1125px;
|
background-position: -100px 1125px;
|
||||||
-webkit-background-clip: text;
|
-webkit-background-clip: text;
|
||||||
-webkit-text-fill-color: transparent;
|
-webkit-text-fill-color: transparent;
|
||||||
font-size: 35px;
|
font-size: 35px;
|
||||||
|
@ -37,9 +62,49 @@
|
||||||
margin-top: var(--title-gap);
|
margin-top: var(--title-gap);
|
||||||
font-family: var(--title-font);
|
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 {
|
#entries-container {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
margin-top: 10px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue