Added the new link form and processing

This commit is contained in:
Awstin 2024-12-19 07:32:10 -05:00
parent c4bffa0387
commit 8341e64dfd
11 changed files with 261 additions and 45 deletions

149
Cargo.lock generated
View file

@ -9,6 +9,7 @@ dependencies = [
"askama",
"axum",
"bb8",
"chrono",
"clap",
"cookie",
"futures-util",
@ -18,7 +19,6 @@ dependencies = [
"rand_core",
"serde",
"sqlx",
"time",
"tokio",
"tower",
"tower-http",
@ -69,6 +69,21 @@ version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]]
name = "anstream"
version = "0.6.14"
@ -310,6 +325,12 @@ dependencies = [
"generic-array",
]
[[package]]
name = "bumpalo"
version = "3.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
[[package]]
name = "byteorder"
version = "1.5.0"
@ -334,6 +355,21 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"serde",
"wasm-bindgen",
"windows-targets 0.52.5",
]
[[package]]
name = "clap"
version = "4.5.13"
@ -396,6 +432,12 @@ dependencies = [
"version_check",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "cpufeatures"
version = "0.2.12"
@ -463,7 +505,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
dependencies = [
"powerfmt",
"serde",
]
[[package]]
@ -821,6 +862,29 @@ dependencies = [
"want",
]
[[package]]
name = "iana-time-zone"
version = "0.1.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"windows-core",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]]
name = "idna"
version = "0.5.0"
@ -862,6 +926,16 @@ version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "js-sys"
version = "0.3.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7"
dependencies = [
"once_cell",
"wasm-bindgen",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -1640,6 +1714,7 @@ dependencies = [
"atoi",
"byteorder",
"bytes",
"chrono",
"crc",
"crossbeam-queue",
"either",
@ -1665,7 +1740,6 @@ dependencies = [
"smallvec",
"sqlformat",
"thiserror",
"time",
"tokio",
"tokio-stream",
"tracing",
@ -1723,6 +1797,7 @@ dependencies = [
"bitflags 2.5.0",
"byteorder",
"bytes",
"chrono",
"crc",
"digest",
"dotenvy",
@ -1750,7 +1825,6 @@ dependencies = [
"sqlx-core",
"stringprep",
"thiserror",
"time",
"tracing",
"whoami",
]
@ -1765,6 +1839,7 @@ dependencies = [
"base64",
"bitflags 2.5.0",
"byteorder",
"chrono",
"crc",
"dotenvy",
"etcetera",
@ -1789,7 +1864,6 @@ dependencies = [
"sqlx-core",
"stringprep",
"thiserror",
"time",
"tracing",
"whoami",
]
@ -1801,6 +1875,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b244ef0a8414da0bed4bb1910426e890b19e5e9bccc27ada6b797d05c55ae0aa"
dependencies = [
"atoi",
"chrono",
"flume",
"futures-channel",
"futures-core",
@ -1812,7 +1887,6 @@ dependencies = [
"percent-encoding",
"serde",
"sqlx-core",
"time",
"tracing",
"url",
"urlencoding",
@ -2248,6 +2322,60 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b"
[[package]]
name = "wasm-bindgen"
version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396"
dependencies = [
"cfg-if",
"once_cell",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79"
dependencies = [
"bumpalo",
"log",
"proc-macro2",
"quote",
"syn 2.0.60",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.60",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6"
[[package]]
name = "webpki-roots"
version = "0.25.4"
@ -2286,6 +2414,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-core"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
"windows-targets 0.52.5",
]
[[package]]
name = "windows-sys"
version = "0.48.0"

View file

@ -9,6 +9,7 @@ edition = "2021"
askama = "0.12.1"
axum = "0.6"
bb8 = "0.8.3"
chrono = { version = "0.4.38", features = ["alloc", "serde"] }
clap = { version = "4.5.13", features = ["derive"] }
cookie = "0.18.1"
futures-util = "0.3.30"
@ -18,12 +19,11 @@ rand_chacha = "0.3.1"
rand_core = "0.6.4"
serde = { version = "1.0.197", features = ["derive"] }
sqlx = { version = "0.7.4", features = [
"runtime-tokio-rustls",
"postgres",
"time",
"chrono",
"macros",
"postgres",
"runtime-tokio-rustls",
] }
time = { version = "0.3.36", features = ["macros", "serde"] }
tokio = { version = "1.35.0", features = ["full"] }
tower = "0.4.13"
tower-http = { version = "0.4.4", features = ["fs"] }

View file

@ -79,10 +79,7 @@ impl AuthState {
.unwrap();
if let Some((id, admin)) = user {
*store = Some(UserInfo {
user_id: id,
admin
});
*store = Some(UserInfo { user_id: id, admin });
}
}
store.as_ref()
@ -141,7 +138,9 @@ pub async fn post_signup(
return Err(error_page(&SignupError::PasswordsDoNotMatch));
}
let user_id = create_user(&signup.username, &signup.password, &pool).await.unwrap();
let user_id = create_user(&signup.username, &signup.password, &pool)
.await
.unwrap();
let session_token = new_session(&pool, random, user_id).await;

View file

@ -7,7 +7,7 @@ use std::{
fs::{self, metadata},
path::PathBuf,
};
use time::{macros::format_description, Date};
use chrono::NaiveDate;
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
pub struct Article {
@ -17,7 +17,7 @@ pub struct Article {
pub content: String,
pub previous: Option<String>,
pub next: Option<String>,
pub date: Date,
pub date: NaiveDate,
pub description: Option<String>,
}
@ -30,7 +30,7 @@ impl Article {
let mut title: Option<String> = None;
let mut previous: Option<String> = None;
let mut next: Option<String> = None;
let mut date: Option<Date> = None;
let mut date: Option<NaiveDate> = None;
let mut description: Option<String> = None;
let mut lines: Vec<String> = super::read_lines(file_name);
lines.reverse();
@ -45,9 +45,8 @@ impl Article {
} else if line.contains("next: ") {
next = Some(line.clone().replace("next: ", ""));
} else if line.contains("date: ") {
let format = format_description!("[year]-[month]-[day]");
date = Some(
Date::parse(&line.clone().replace("date: ", ""), format).expect("not a date"),
NaiveDate::parse_from_str(&line.clone().replace("date: ", ""), "%Y-%m-%d").expect("not a date"),
);
} else if line.contains("description: ") {
description = Some(line.clone().replace("description: ", ""));

View file

@ -1,9 +1,9 @@
use crate::database::PsqlData;
use chrono::NaiveDate;
use futures_util::TryStreamExt;
use serde::{Deserialize, Serialize};
use sqlx::{self, postgres::PgPool, Pool, Postgres};
use std::{error::Error, path::Path};
use time::{macros::format_description, Date};
#[derive(Debug, Serialize, Deserialize, PartialEq, PartialOrd, Clone, sqlx::Type)]
#[sqlx(type_name = "link_type", rename_all = "lowercase")]
@ -20,7 +20,7 @@ pub struct Link {
pub title: String,
pub author: String,
pub link_type: LinkType,
pub date_added: Date,
pub date_added: NaiveDate,
}
impl Link {
@ -30,7 +30,7 @@ impl Link {
let mut title: Option<String> = None;
let mut author: Option<String> = None;
let mut link_type: Option<LinkType> = None;
let mut date_added: Option<Date> = None;
let mut date_added: Option<NaiveDate> = None;
while lines.len() > 0 {
let line: String = lines.pop().expect("Something went terribly wrong here");
if line.contains("url: ") {
@ -45,10 +45,8 @@ impl Link {
_ => LinkType::ARTICLE,
});
} else if line.contains("date_added: ") {
let format = format_description!("[year]-[month]-[day]");
date_added = Some(
Date::parse(&line.clone().replace("date_added: ", ""), format)
.expect("not a date"),
NaiveDate::parse_from_str(&line.clone().replace("date: ", ""), "%Y-%m-%d").expect("not a date"),
);
} else if line.contains("description: ") {
description = Some(line.clone().replace("description: ", ""));

View file

@ -1,20 +1,33 @@
use core::panic;
use axum::{
response::{IntoResponse, Redirect, Response},
routing::{get, Router},
Extension,
routing::{get, post, Router},
Extension, Form,
};
use chrono::Local;
use sqlx::PgPool;
use crate::{
auth::{AuthState, UserInfo},
database::{
link::{Link, LinkType},
PsqlData,
},
};
use crate::auth::{AuthState, UserInfo};
use super::templates::{AdminTemplate, HtmlTemplate};
use super::{
templates::{AdminTemplate, HtmlTemplate},
NewLink,
};
pub fn get_router() -> Router {
Router::new().route("/", get(get_admin))
Router::new()
.route("/", get(get_admin))
.route("/new_link", post(new_link))
}
pub async fn get_admin(
Extension(mut current_user): Extension<AuthState>,
) -> Response {
pub async fn get_admin(Extension(mut current_user): Extension<AuthState>) -> Response {
let user_info: UserInfo = current_user.get_user().await.unwrap().clone();
if !user_info.admin {
return Redirect::to("/").into_response();
@ -22,6 +35,35 @@ pub async fn get_admin(
HtmlTemplate(AdminTemplate {}).into_response()
}
pub async fn new_link(
Extension(mut current_user): Extension<AuthState>,
Extension(pool): Extension<PgPool>,
Form(new_item): Form<NewLink>,
) -> Response {
let user_info: UserInfo = current_user.get_user().await.unwrap().clone();
if !user_info.admin {
return Redirect::to("/").into_response();
}
let new_link: Link = Link {
id: 0,
url: new_item.url,
title: new_item.title,
author: new_item.author,
link_type: match &*new_item.link_type {
"article" => LinkType::ARTICLE,
"blog" => LinkType::BLOG,
_ => panic!("Not a proper link type"),
},
description: get_trimmed_string(new_item.description),
date_added: Local::now().date_naive(),
};
let _ = new_link.insert(&pool).await;
Redirect::to("/admin").into_response()
}
fn get_trimmed_string(input: String) -> Option<String> {
if input.trim().is_empty() {
None

View file

@ -4,7 +4,8 @@ use axum::{
};
use super::templates::{
ArchServerTemplate, BooksTemplate, CookingTemplate, CreationTemplate, EdgeDetectionTemplate, GardenTemplate, HtmlTemplate, TakTemplate, TechnologyTemplate, TimeTemplate
ArchServerTemplate, BooksTemplate, CookingTemplate, CreationTemplate, EdgeDetectionTemplate,
GardenTemplate, HtmlTemplate, TakTemplate, TechnologyTemplate, TimeTemplate,
};
pub fn get_router() -> Router {

View file

@ -18,3 +18,12 @@ pub struct Signup {
pub password: String,
pub confirm_password: String,
}
#[derive(Deserialize)]
pub struct NewLink {
pub url: String,
pub description: String,
pub title: String,
pub author: String,
pub link_type: String,
}

View file

@ -22,9 +22,14 @@ use crate::{
};
use super::{
admin, blog::{self, get_articles_date_sorted}, garden, templates::{
AboutTemplate, AiTemplate, BlogrollTemplate, ContactTemplate, GiftsTemplate, HomeTemplate, HtmlTemplate, InterestsTemplate, LinksPageTemplate, LoginTemplate, NowTemplate, ResumeTemplate, SignupTemplate, UsesTemplate, WorkTemplate
}
admin,
blog::{self, get_articles_date_sorted},
garden,
templates::{
AboutTemplate, AiTemplate, BlogrollTemplate, ContactTemplate, GiftsTemplate, HomeTemplate,
HtmlTemplate, InterestsTemplate, LinksPageTemplate, LoginTemplate, NowTemplate,
ResumeTemplate, SignupTemplate, UsesTemplate, WorkTemplate,
},
};
pub fn get_router(pool: PgPool) -> Router {

View file

@ -4,9 +4,35 @@
{% block content %}
<h2>Admin</h2>
<form action="/logout" method="post">
<input type="submit" value="logout">
<input type="submit" value="Logout">
</form>
<h3>New Link</h3>
<form action="/admin/new_link" method="post">
<p>
My admin page
<label for="title">Title:</label>
<input type="text" name="title" id="title" required>
</p>
<p>
<label for="author">Author:</label>
<input type="text" name="author" id="author" required>
</p>
<p>
<label for="url">URL:</label>
<input type="text" name="url" id="url" required>
</p>
<p>
<label for="link_type">Type:</label>
<select name="link_type" id="link_type">
<option value="blog">Blog</option>
<option value="article">Article</option>
</select>
</p>
<p>
<label for="description">Description:</label>
<br>
<textarea name="description" id="description" rows="5" columns="50">
</textarea>
</p>
<input type="submit" value="Submit">
</form>
{% endblock %}

View file

@ -23,7 +23,7 @@
<label for="password">Confirm Password: </label>
<input type="password" name="confirm_password" id="confirm_password" required>
</p>
<input type="submit" value="Login">
<input type="submit" value="Signup">
</form>
</main>
</body>