first fully working version

This commit is contained in:
hexlocation pc 2024-07-12 21:36:35 +02:00
parent 473bd34e4f
commit db01e0494f
6 changed files with 205 additions and 31 deletions

20
Cargo.lock generated
View file

@ -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"

View file

@ -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"

View file

@ -0,0 +1 @@
{"messages":[{"body":"b","date":"2024-0712 | 21:34","name":"a "}]}

37
src/logger.rs Normal file
View 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);
}
}

View file

@ -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())
}

View file

@ -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;">&nbsp;[ post message ]&nbsp;</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()">&nbsp;[ send ]&nbsp;</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;
}