1
Fork 0
mirror of https://github.com/RGBCube/GDUS synced 2025-07-28 21:47:45 +00:00

Move webserver to server/

This commit is contained in:
RGBCube 2023-12-15 17:07:17 +03:00
parent 1fa76f9a15
commit d525fd353f
No known key found for this signature in database
8 changed files with 11 additions and 4 deletions

20
server/.rustfmt.toml Normal file
View file

@ -0,0 +1,20 @@
condense_wildcard_suffixes = true
control_brace_style = "ClosingNextLine"
enum_discrim_align_threshold = 25
force_explicit_abi = false
force_multiline_blocks = true
format_code_in_doc_comments = true
format_macro_matchers = true
format_strings = true
hex_literal_case = "Upper"
imports_layout = "Vertical"
match_block_trailing_comma = true
imports_granularity = "Crate"
newline_style = "Unix"
normalize_comments = true
normalize_doc_attributes = true
reorder_impl_items = true
group_imports = "StdExternalCrate"
unstable_features = true
use_try_shorthand = true
wrap_comments = true

2285
server/Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

19
server/Cargo.toml Normal file
View file

@ -0,0 +1,19 @@
[package]
name = "proje"
description = "TÜBİTAK Projesi"
authors = [ "RGBCube" ]
repository = "https://github.com/RGBCube/NamelessProje"
license = "GPL"
version = "0.0.1"
edition = "2021"
[dependencies]
actix-web = "4.4.0"
chrono = "0.4.31"
maud = { version = "0.25.0", features = [ "actix-web" ] }
serde = { version = "1.0.192", features = [ "derive" ] }
sqlx = { version = "0.7.3", features = [ "chrono", "sqlite", "runtime-tokio" ] }
tokio = { version = "1.34.0", features = [ "full" ] }
[profile.dev]
incremental = true

55
server/src/index.rs Normal file
View file

@ -0,0 +1,55 @@
use actix_web as web;
use maud::{
html,
Markup,
DOCTYPE,
};
#[web::get("/")]
async fn index() -> web::Result<Markup> {
Ok(html! {
(DOCTYPE)
style {r#"
body {
font-family: sans;
background-color: #f4f4f4;
margin: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
h1 {
text-align: center;
color: #333;
}
.links {
text-align: center;
margin-top: 30px;
}
.links a {
display: inline-block;
margin: 10px;
padding: 8px 16px;
text-decoration: none;
color: #fff;
background-color: #4caf50;
border-radius: 4px;
transition: background-color 0.3s ease;
}
.links a:hover {
background-color: #45a049;
}
"#}
div class="links" {
a href="/submit" { "Hatırlatıcı Koy" }
a href="/view" { "Hatırlatıcıları Görüntüle" }
}
})
}

46
server/src/main.rs Normal file
View file

@ -0,0 +1,46 @@
mod index;
mod submit;
mod view;
use std::io;
use actix_web as web;
use sqlx::{
sqlite::SqliteConnectOptions,
SqlitePool,
};
#[web::main]
async fn main() -> io::Result<()> {
let db = SqlitePool::connect_with(
SqliteConnectOptions::new()
.filename("data.db")
.create_if_missing(true),
)
.await
.expect("Failed to connect to SQLite database.");
sqlx::query(
r"
CREATE TABLE IF NOT EXISTS reminders (
date DATETIME NOT NULL,
message TEXT NOT NULL
)
",
)
.execute(&db)
.await
.expect("Failed to create table reminders.");
web::HttpServer::new(move || {
web::App::new()
.app_data(web::web::Data::new(db.clone()))
.service(index::index)
.service(submit::submit)
.service(submit::submit_form)
.service(view::view)
})
.bind(("127.0.0.1", 80))?
.run()
.await
}

139
server/src/submit.rs Normal file
View file

@ -0,0 +1,139 @@
use actix_web as web;
use actix_web::web::{
Data,
Query,
};
use chrono::NaiveDateTime;
use maud::{
html,
Markup,
PreEscaped,
DOCTYPE,
};
use sqlx::SqlitePool;
#[derive(Debug, serde::Deserialize)]
pub struct Reminder {
date: String,
message: String,
}
#[web::get("/submit-form")]
async fn submit_form(
data: Data<SqlitePool>,
Query(reminder): Query<Reminder>,
) -> web::Result<Markup> {
println!("{reminder:?}");
let date_time = NaiveDateTime::parse_from_str(&reminder.date, "%Y-%m-%dT%H:%M").unwrap();
sqlx::query(
r"
INSERT INTO
reminders (date, message)
VALUES
(?, ?)
",
)
.bind(date_time)
.bind(reminder.message)
.execute(&**data)
.await
.expect("Failed to save reminder.");
Ok(html! {
(DOCTYPE)
style {r#"
body {
font-family: sans;
background-color: #f4f4f4;
margin: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
flex-direction: column;
}
h1 {
color: #333;
margin-bottom: 20px;
}
p {
color: #666;
margin-bottom: 40px;
}
"#}
h1 { "Kaydedildi." }
p { "Ana sayfaya geri yönlendiriliyorsun..." }
script type="text/javascript" {(PreEscaped(r#"
setTimeout(function() { window.location.href = "/"; }, 5000);
"#))}
})
}
#[web::get("/submit")]
async fn submit() -> web::Result<Markup> {
Ok(html! {
(DOCTYPE)
style {r#"
body {
font-family: sans;
background-color: #f4f4f4;
margin: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
form {
background-color: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
label {
display: block;
margin-bottom: 8px;
}
input {
width: 100%;
padding: 8px;
margin-bottom: 16px;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 4px;
}
button {
background-color: #4caf50;
color: #fff;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background-color: #45a049;
}
"#}
form action="/submit-form" {
button type="submit" { "Kaydet" }
br;
label for="date" { "Tarih:" }
input type="datetime-local" id="date" name="date";
br;
label for="message" { "Mesaj:" }
input id="message" name="message";
}
})
}

126
server/src/view.rs Normal file
View file

@ -0,0 +1,126 @@
use actix_web as web;
use actix_web::web::Data;
use chrono::{
Local,
NaiveDateTime,
TimeZone,
};
use maud::{
html,
Markup,
PreEscaped,
DOCTYPE,
};
use sqlx::SqlitePool;
#[web::get("/view")]
async fn view(data: Data<SqlitePool>) -> web::Result<Markup> {
let reminders = sqlx::query_as::<_, (NaiveDateTime, String)>(
r#"
SELECT
date, message
FROM
reminders
WHERE
date > datetime("now")
ORDER BY date ASC
"#,
)
.fetch_all(&**data)
.await
.expect("Failed to fetch reminders.");
println!("{reminders:?}");
let formatted_reminders: Vec<(i64, String, String)> = reminders
.into_iter()
.map(|(date_time, message)| {
let local_time = Local.from_local_datetime(&date_time).unwrap();
(
local_time.timestamp() as i64,
local_time.format("%Y/%m/%d %H:%M").to_string(),
message,
)
})
.collect();
Ok(html! {
(DOCTYPE)
style {r#"
body {
font-family: sans;
background-color: #f4f4f4;
margin: 0;
padding: 20px;
}
ul {
list-style: none;
padding: 0;
}
ul li {
background-color: #fff;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
padding: 20px;
margin-bottom: 20px;
}
ul li h3 {
margin-bottom: 10px;
font-size: 18px;
color: #333;
}
ul li p {
color: #666;
}
"#}
ul id="reminders" {
@for reminder in formatted_reminders {
li {
p class="timestamp" data-timestamp=(reminder.0) style="display: none;" {}
h3 { (reminder.1) }
p { (reminder.2) }
}
}
}
script defer {(PreEscaped(r##"
const alertIfTime = () => {
const reminders = Array.from(
document
.querySelectorAll("#reminders .timestamp")
)
.map(timestampItem => ({
content: timestampItem
.parentNode
.querySelector("p:not(.timestamp)")
.textContent,
timestamp: +timestampItem.getAttribute("data-timestamp"),
}));
const currentTime = Math.floor(Date.now() / 1000);
reminders.forEach(reminder => {
const differenceSeconds = currentTime - reminder.timestamp;
if (differenceSeconds < 1 * 60) {
// new Audio("/beep.mp3").play();
alert("Geldi! " + reminder.content);
}
});
};
alertIfTime();
setInterval(() => {
location.reload();
alertIfTime();
}, 1 * 60 * 1000);
"##))}
})
}